cmd

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: AGPL-3.0 Imports: 47 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// FocusPrimary / FocusSecondary used to be yellow (ANSI 226/220). Now
	// brand indigo — same role (selected / active item highlight) but
	// matches the rest of the design system.
	FocusPrimary   = ui.Adaptive(func(p ui.Palette) lipgloss.Color { return p.Accent })
	FocusSecondary = ui.Adaptive(func(p ui.Palette) lipgloss.Color { return p.Accent2 })

	StandardPrimary   = ui.Adaptive(func(p ui.Palette) lipgloss.Color { return p.Body })
	StandardSecondary = ui.Adaptive(func(p ui.Palette) lipgloss.Color { return p.Mute })

	StandardStyle     = lipgloss.NewStyle().Foreground(StandardPrimary)
	StandardStyleBold = lipgloss.NewStyle().Foreground(StandardPrimary).Bold(true)
	StandardStyleAlt  = lipgloss.NewStyle().Foreground(StandardSecondary)
	StandardStyleRule = lipgloss.NewStyle().Foreground(ui.Adaptive(func(p ui.Palette) lipgloss.Color { return p.Border }))
	SelectedStyle     = lipgloss.NewStyle().Foreground(FocusPrimary).Bold(true)
	SelectedStyleAlt  = lipgloss.NewStyle().Foreground(FocusSecondary)
	SelectedItemOuter = lipgloss.NewStyle().
						BorderStyle(lipgloss.NormalBorder()).
						BorderLeft(true).
						PaddingLeft(1).
						BorderForeground(FocusPrimary)
	ItemOuter = lipgloss.NewStyle().PaddingLeft(1)

	StyleBold = lipgloss.NewStyle().Bold(true)
)

Styles for the cobra command CLI outputs (prompts, error messages, list items rendered outside the bubbletea TUI). Sourced from the shared ui.Palette so any palette change auto-propagates here.

Names kept stable for backwards compatibility with existing call sites across cmd/ and pkg/model/.

View Source
var AddDatasetCmd = &cobra.Command{
	Use:          "add dataset-name",
	Example:      "  pb dataset add backend_logs\n  pb dataset add frontend_metrics --type metrics\n  pb dataset add checkout_traces --type traces",
	Short:        "Create a new dataset",
	Args:         cobra.ExactArgs(1),
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		name := args[0]
		datasetType, err := cmd.Flags().GetString(datasetTypeFlag)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}
		datasetType, err = resolveDatasetType(datasetType)
		if err != nil {
			if errors.Is(err, errDatasetTypeSelectionCanceled) {
				fmt.Println("Dataset creation canceled")
				return nil
			}
			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		client := internalHTTP.DefaultClient(&DefaultProfile)
		req, err := client.NewRequest("PUT", "logstream/"+name, nil)
		if err != nil {

			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}
		req.Header.Set("X-P-Telemetry-Type", datasetType)
		if datasetType == datasets.TypeMetrics || datasetType == datasets.TypeTraces {
			req.Header.Set("X-P-Log-Source", "otel-"+datasetType)
		}

		resp, err := client.Client.Do(req)
		if err != nil {

			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		cmd.Annotations["executionTime"] = time.Since(startTime).String()

		if resp.StatusCode == 200 {
			fmt.Printf("Created %s dataset %s\n", SelectedStyle.Render(datasetType), StyleBold.Render(name))
		} else {
			bytes, err := io.ReadAll(resp.Body)
			if err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
				return err
			}
			body := string(bytes)
			defer resp.Body.Close()
			fmt.Printf("Request Failed\nStatus Code: %s\nResponse: %s\n", resp.Status, body)
		}

		return nil
	},
}

AddDatasetCmd is the parent command for dataset

View Source
var AddProfileCmd = &cobra.Command{
	Use:     "add profile-name url <username?> <password?>",
	Example: "  pb profile add local_parseable http://0.0.0.0:8000 admin admin",
	Short:   "Add a new profile",
	Long:    "Add a new profile to the config file",
	Args: func(cmd *cobra.Command, args []string) error {
		if err := cobra.MinimumNArgs(2)(cmd, args); err != nil {
			return err
		}
		return cobra.MaximumNArgs(4)(cmd, args)
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		if cmd.Annotations == nil {
			cmd.Annotations = make(map[string]string)
		}
		startTime := time.Now()
		var commandError error

		name := args[0]
		url, err := url.Parse(args[1])
		if err != nil {
			commandError = fmt.Errorf("error parsing URL: %s", err)
			cmd.Annotations["error"] = commandError.Error()
			return commandError
		}

		var username, password string
		if len(args) < 4 {
			_m, err := tea.NewProgram(credential.New()).Run()
			if err != nil {
				commandError = fmt.Errorf("error reading credentials: %s", err)
				cmd.Annotations["error"] = commandError.Error()
				return commandError
			}
			m := _m.(credential.Model)
			username, password = m.Values()
		} else {
			username = args[2]
			password = args[3]
		}

		profile := config.Profile{URL: url.String(), Username: username, Password: password}
		fileConfig, err := config.ReadConfigFromFile()
		if err != nil {
			newConfig := config.Config{
				Profiles:       map[string]config.Profile{name: profile},
				DefaultProfile: name,
			}
			err = config.WriteConfigToFile(&newConfig)
			commandError = err
		} else {
			if fileConfig.Profiles == nil {
				fileConfig.Profiles = make(map[string]config.Profile)
			}
			fileConfig.Profiles[name] = profile
			if fileConfig.DefaultProfile == "" {
				fileConfig.DefaultProfile = name
			}
			commandError = config.WriteConfigToFile(fileConfig)
		}

		cmd.Annotations["executionTime"] = time.Since(startTime).String()
		if commandError != nil {
			cmd.Annotations["error"] = commandError.Error()
			return commandError
		}

		if outputFormat == "json" {
			return outputResult(profile)
		}
		fmt.Printf("Profile %s added successfully\n", name)
		return nil
	},
}
View Source
var AddRoleCmd = &cobra.Command{
	Use:     "add role-name",
	Example: "  pb role add ingestors",
	Short:   "Add a new role",
	Args:    cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		name := args[0]

		var roles []string
		client := internalHTTP.DefaultClient(&DefaultProfile)
		if err := fetchRoles(&client, &roles); err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error fetching roles: %s", err.Error())
			return err
		}

		if strings.Contains(strings.Join(roles, " "), name) {
			fmt.Println("role already exists, please use a different name")
			return nil
		}

		_m, err := tea.NewProgram(role.New()).Run()
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error initializing program: %s", err.Error())
			return err
		}

		m := _m.(role.Model)
		privilege := m.Selection.Value()
		stream := m.Stream.Value()

		if !m.Success {
			fmt.Println("aborted by user")
			return nil
		}

		var putBody io.Reader
		if privilege != "none" {
			roleData := RoleData{Privilege: privilege}
			switch privilege {
			case "writer", "ingestor":
				roleData.Resource = &RoleResource{Stream: stream}
			case "reader":
				roleData.Resource = &RoleResource{Stream: stream}
			}
			roleDataJSON, _ := json.Marshal([]RoleData{roleData})
			putBody = bytes.NewBuffer(roleDataJSON)
		}

		req, err := client.NewRequest("PUT", "role/"+name, putBody)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error creating request: %s", err.Error())
			return err
		}

		resp, err := client.Client.Do(req)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error performing request: %s", err.Error())
			return err
		}
		defer resp.Body.Close()

		bodyBytes, err := io.ReadAll(resp.Body)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error reading response: %s", err.Error())
			return err
		}
		body := string(bodyBytes)

		if resp.StatusCode == 200 {
			fmt.Printf("Added role %s", name)
		} else {
			cmd.Annotations["errors"] = fmt.Sprintf("Request failed - Status: %s, Response: %s", resp.Status, body)
			fmt.Printf("Request Failed\nStatus Code: %s\nResponse: %s\n", resp.Status, body)
		}

		return nil
	},
}
View Source
var AddUserCmd = func() *cobra.Command {
	addUser.Flags().StringP(roleFlag, roleFlagShort, "", "specify the role(s) to be assigned to the user. Use comma separated values for multiple roles. Example: --role admin,developer")
	return addUser
}()
View Source
var AutocompleteCmd = &cobra.Command{
	Use:   "autocomplete [bash|zsh|powershell]",
	Short: "Generate autocomplete script",
	Long:  `Generate autocomplete script for bash, zsh, or powershell`,
	Args:  cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		var err error
		switch args[0] {
		case "bash":
			err = cmd.Root().GenBashCompletion(os.Stdout)
		case "zsh":
			err = cmd.Root().GenZshCompletion(os.Stdout)
		case "powershell":
			err = cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
		default:
			err = fmt.Errorf("unsupported shell type: %s. Only bash, zsh, and powershell are supported", args[0])
		}

		if err != nil {
			return fmt.Errorf("error generating autocomplete script: %w", err)
		}

		return nil
	},
}

AutocompleteCmd represents the autocomplete command

View Source
var CreateSchemaCmd = &cobra.Command{
	Use:     "create",
	Short:   "Create Schema for a Parseable dataset",
	Example: "pb schema create --dataset=my_dataset --file=schema.json",
	RunE: func(cmd *cobra.Command, _ []string) error {

		streamName, err := cmd.Flags().GetString("dataset")
		if err != nil {
			return fmt.Errorf(common.Red+"failed to read dataset flag: %w"+common.Reset, err)
		}

		if streamName == "" {
			return fmt.Errorf(common.Red + "dataset flag is required" + common.Reset)
		}

		filePath, err := cmd.Flags().GetString("file")
		if err != nil {
			return fmt.Errorf(common.Red+"failed to read config flag: %w"+common.Reset, err)
		}

		if filePath == "" {
			return fmt.Errorf(common.Red + "file path flag is required" + common.Reset)
		}

		schemaContent, err := os.ReadFile(filePath)
		if err != nil {
			return fmt.Errorf(common.Red+"failed to read schema file %s: %w"+common.Reset, filePath, err)
		}

		client := internalHTTP.DefaultClient(&DefaultProfile)

		apiPath := fmt.Sprintf("/logstream/%s", streamName)

		req, err := client.NewRequest(http.MethodPut, apiPath, bytes.NewBuffer(schemaContent))
		if err != nil {
			return fmt.Errorf(common.Red+"failed to create new request: %w"+common.Reset, err)
		}

		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("X-P-Static-Schema-Flag", "true")

		resp, err := client.Client.Do(req)
		if err != nil {
			return fmt.Errorf(common.Red+"request execution failed: %w"+common.Reset, err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			body, _ := io.ReadAll(resp.Body)
			fmt.Printf(common.Red+"Error response: %s\n"+common.Reset, string(body))
			return fmt.Errorf(common.Red+"non-200 status code received: %s"+common.Reset, resp.Status)
		}

		respBody, err := io.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf(common.Red+"failed to read response body: %w"+common.Reset, err)
		}

		fmt.Println(common.Green + string(respBody) + common.Reset)
		return nil
	},
}
View Source
var DefaultProfile config.Profile
View Source
var DefaultProfileCmd = &cobra.Command{
	Use:     "default profile-name",
	Args:    cobra.MaximumNArgs(1),
	Short:   "Set default profile to use with all commands",
	Example: "  pb profile default local_parseable",
	RunE: func(cmd *cobra.Command, args []string) error {
		if cmd.Annotations == nil {
			cmd.Annotations = make(map[string]string)
		}
		startTime := time.Now()

		fileConfig, err := config.ReadConfigFromFile()
		if err != nil {
			cmd.Annotations["error"] = fmt.Sprintf("error reading config: %s", err)
			return err
		}

		var name string
		if len(args) > 0 {
			name = args[0]
		} else {
			model := defaultprofile.New(fileConfig.Profiles)
			_m, err := tea.NewProgram(model).Run()
			if err != nil {
				cmd.Annotations["error"] = fmt.Sprintf("error selecting default profile: %s", err)
				return err
			}
			m := _m.(defaultprofile.Model)
			if !m.Success {
				return nil
			}
			name = m.Choice
		}

		_, exists := fileConfig.Profiles[name]
		if !exists {
			commandError := fmt.Sprintf("profile %s does not exist", name)
			cmd.Annotations["error"] = commandError
			return errors.New(commandError)
		}

		fileConfig.DefaultProfile = name
		commandError := config.WriteConfigToFile(fileConfig)
		cmd.Annotations["executionTime"] = time.Since(startTime).String()
		if commandError != nil {
			cmd.Annotations["error"] = commandError.Error()
			return commandError
		}

		if outputFormat == "json" {
			return outputResult(fmt.Sprintf("%s is now set as default profile", name))
		}
		fmt.Printf("%s is now set as default profile\n", name)
		return nil
	},
}
View Source
var GenerateSchemaCmd = &cobra.Command{
	Use:     "generate",
	Short:   "Generate Schema for JSON",
	Example: "pb schema generate --file=test.json",
	RunE: func(cmd *cobra.Command, _ []string) error {

		filePath, err := cmd.Flags().GetString("file")
		if err != nil {
			return fmt.Errorf(common.Red+"failed to read file flag: %w"+common.Reset, err)
		}

		if filePath == "" {
			return fmt.Errorf(common.Red + "file flag is required" + common.Reset)
		}

		fileContent, err := os.ReadFile(filePath)
		if err != nil {
			return fmt.Errorf(common.Red+"failed to read file %s: %w"+common.Reset, filePath, err)
		}

		client := internalHTTP.DefaultClient(&DefaultProfile)

		req, err := client.NewRequest(http.MethodPost, generateStaticSchemaPath, bytes.NewBuffer(fileContent))
		if err != nil {
			return fmt.Errorf(common.Red+"failed to create new request: %w"+common.Reset, err)
		}

		req.Header.Set("Content-Type", "application/json")

		resp, err := client.Client.Do(req)
		if err != nil {
			return fmt.Errorf(common.Red+"request execution failed: %w"+common.Reset, err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			body, _ := io.ReadAll(resp.Body)
			fmt.Printf(common.Red+"Error response: %s\n"+common.Reset, string(body))
			return fmt.Errorf(common.Red+"non-200 status code received: %s"+common.Reset, resp.Status)
		}

		respBody, err := io.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf(common.Red+"failed to read response body: %w"+common.Reset, err)
		}

		var prettyJSON bytes.Buffer
		if err := json.Indent(&prettyJSON, respBody, "", "  "); err != nil {
			return fmt.Errorf(common.Red+"failed to format response as JSON: %w"+common.Reset, err)
		}

		fmt.Println(common.Green + prettyJSON.String() + common.Reset)
		return nil
	},
}
View Source
var InstallOssCmd = &cobra.Command{
	Use:     "install",
	Short:   "Deploy Parseable",
	Example: "pb cluster install",
	Run: func(cmd *cobra.Command, _ []string) {

		cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
		installer.Installer(verbose)
	},
}
View Source
var ListDatasetCmd = &cobra.Command{
	Use:          "list",
	Aliases:      []string{"ls"},
	Example:      "  pb dataset list",
	Short:        "List all datasets",
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, _ []string) error {

		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		items, err := datasets.FetchHomeDatasets(DefaultProfile)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		output, _ := cmd.Flags().GetString("output")
		if output == "json" {
			jsonData, err := json.MarshalIndent(items, "", "  ")
			if err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
				return err
			}
			fmt.Println(string(jsonData))
			return nil
		}

		for _, dataset := range items {
			fmt.Println((&DatasetListItem{Name: dataset.Title, Type: dataset.DatasetType}).Render())
		}
		return nil
	},
}

ListDatasetCmd is the list command for datasets

View Source
var ListOssCmd = &cobra.Command{
	Use:     "list",
	Short:   "List available Parseable servers",
	Example: "pb list",
	Run: func(_ *cobra.Command, _ []string) {
		_, err := common.PromptK8sContext()
		if err != nil {
			log.Fatalf("Failed to prompt for kubernetes context: %v", err)
		}

		entries, err := common.ReadInstallerConfigMap()
		if err != nil {
			log.Fatalf("Failed to list servers: %v", err)
		}

		if len(entries) == 0 {
			fmt.Println("No clusters found.")
			return
		}

		table := tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{"Name", "Namespace", "Version", "Status"})

		for _, entry := range entries {
			table.Append([]string{entry.Name, entry.Namespace, entry.Version, entry.Status})
		}

		table.Render()
	},
}

ListOssCmd lists the Parseable OSS servers

View Source
var ListProfileCmd = &cobra.Command{
	Use:     "list profiles",
	Aliases: []string{"ls"},
	Short:   "List all added profiles",
	Example: "  pb profile list",
	RunE: func(cmd *cobra.Command, _ []string) error {
		if cmd.Annotations == nil {
			cmd.Annotations = make(map[string]string)
		}
		startTime := time.Now()

		fileConfig, err := config.ReadConfigFromFile()
		if err != nil {
			cmd.Annotations["error"] = fmt.Sprintf("error reading config: %s", err)
			return err
		}

		if outputFormat == "json" {
			commandError := outputResult(fileConfig.Profiles)
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
			if commandError != nil {
				cmd.Annotations["error"] = commandError.Error()
				return commandError
			}
			return nil
		}

		for key, value := range fileConfig.Profiles {
			item := ProfileListItem{key, value.URL, value.Username}
			fmt.Println(item.Render(fileConfig.DefaultProfile == key))
			fmt.Println()
		}
		cmd.Annotations["executionTime"] = time.Since(startTime).String()
		return nil
	},
}
View Source
var ListRoleCmd = &cobra.Command{
	Use:          "list",
	Aliases:      []string{"ls"},
	Short:        "List all roles",
	Example:      "  pb role list",
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, _ []string) error {
		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		var roles []string
		client := internalHTTP.DefaultClient(&DefaultProfile)
		err := fetchRoles(&client, &roles)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error fetching roles: %s", err.Error())
			return err
		}

		outputFormat, err := cmd.Flags().GetString("output")
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error retrieving output flag: %s", err.Error())
			return err
		}

		roleResponses := make([]struct {
			data []RoleData
			err  error
		}, len(roles))

		var wg sync.WaitGroup
		for idx, role := range roles {
			wg.Add(1)
			go func(idx int, role string) {
				defer wg.Done()
				roleResponses[idx].data, roleResponses[idx].err = fetchSpecificRole(&client, role)
			}(idx, role)
		}
		wg.Wait()

		if outputFormat == "json" {
			allRoles := map[string][]RoleData{}
			for idx, roleName := range roles {
				if roleResponses[idx].err == nil {
					allRoles[roleName] = roleResponses[idx].data
				}
			}
			jsonOutput, err := json.MarshalIndent(allRoles, "", "  ")
			if err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error marshaling JSON output: %s", err.Error())
				return fmt.Errorf("failed to marshal JSON output: %w", err)
			}
			fmt.Println(string(jsonOutput))
			return nil
		}

		printRoleTable(roles, roleResponses)
		var fetchErrors []string
		for idx, roleName := range roles {
			if roleResponses[idx].err != nil {
				errMsg := fmt.Sprintf("Error fetching role data for %s: %v", roleName, roleResponses[idx].err)
				fetchErrors = append(fetchErrors, errMsg)
				cmd.Annotations["errors"] += errMsg + "\n"
			}
		}
		if len(fetchErrors) > 0 {
			return fmt.Errorf("failed to fetch details for %d role(s): %s", len(fetchErrors), strings.Join(fetchErrors, "; "))
		}

		return nil
	},
}
View Source
var ListUserCmd = &cobra.Command{
	Use:          "list",
	Aliases:      []string{"ls"},
	Short:        "List all users",
	Example:      "  pb user list",
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, _ []string) error {
		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		client := internalHTTP.DefaultClient(&DefaultProfile)
		users, err := fetchUsers(&client)
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}

		roleResponses := make([]struct {
			data []string
			err  error
		}, len(users))

		wsg := sync.WaitGroup{}
		for idx, user := range users {
			wsg.Add(1)
			out := &roleResponses[idx]
			userID := user.ID
			client := &client
			go func() {
				var rolesResp UserRolesResponse
				rolesResp, out.err = fetchUserRoles(client, userID)
				if out.err == nil {
					for roleName := range rolesResp.DirectRoles {
						out.data = append(out.data, roleName)
					}
					for _, groupRoles := range rolesResp.GroupRoles {
						for roleName := range groupRoles {
							out.data = append(out.data, roleName)
						}
					}
				}
				wsg.Done()
			}()
		}

		wsg.Wait()

		outputFormat, err := cmd.Flags().GetString("output")
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}

		if outputFormat == "json" {
			usersWithRoles := make([]map[string]interface{}, len(users))
			for idx, user := range users {
				usersWithRoles[idx] = map[string]interface{}{
					"id":    user.ID,
					"roles": roleResponses[idx].data,
				}
			}
			jsonOutput, err := json.MarshalIndent(usersWithRoles, "", "  ")
			if err != nil {
				cmd.Annotations["error"] = err.Error()
				return fmt.Errorf("failed to marshal JSON output: %w", err)
			}
			fmt.Println(string(jsonOutput))
			return userRoleFetchError(cmd, users, roleResponses)
		}

		if outputFormat == "text" {
			printUserRoleTable(users, roleResponses)
			return userRoleFetchError(cmd, users, roleResponses)
		}

		printUserRoleTable(users, roleResponses)

		return userRoleFetchError(cmd, users, roleResponses)
	},
}
View Source
var LoginCmd = &cobra.Command{
	Use:   "login",
	Short: "Login to Parseable",
	Long: `Interactive login wizard for Parseable.

Select self-hosted and enter your server URL, credentials, and a
profile name. All settings are saved to ~/.config/pb/config.toml.`,
	RunE: func(_ *cobra.Command, _ []string) error {
		_m, err := tea.NewProgram(login.New()).Run()
		if err != nil {
			return err
		}

		m, ok := _m.(login.Model)
		if !ok || !m.Done {
			return nil
		}

		if err := writeProfile(m.Profile, m.Name); err != nil {
			return fmt.Errorf("failed to save profile: %w", err)
		}
		return nil
	},
}
View Source
var LogoutCmd = &cobra.Command{
	Use:     "logout",
	Short:   "Logout from the current Parseable profile",
	Long:    "Removes the active profile (URL and credentials) from config.",
	Example: "  pb logout",
	RunE: func(_ *cobra.Command, _ []string) error {
		fileConfig, err := config.ReadConfigFromFile()
		if err != nil {
			return fmt.Errorf("no config found — nothing to logout from")
		}

		profileName := fileConfig.DefaultProfile
		activeProfile, exists := fileConfig.Profiles[profileName]
		if !exists || profileName == "" {
			if len(fileConfig.Profiles) == 0 {
				return fmt.Errorf("no active profile found")
			}
			selectedProfile, err := selectLogoutProfile(fileConfig.Profiles)
			if err != nil {
				return err
			}
			if selectedProfile == "" {
				fmt.Println("Logout canceled")
				return nil
			}
			profileName = selectedProfile
			activeProfile = fileConfig.Profiles[profileName]
		}

		if !confirmLogout(profileName, activeProfile.URL) {
			fmt.Println("Logout canceled")
			return nil
		}

		delete(fileConfig.Profiles, profileName)
		newDefaultProfile := ""
		switch len(fileConfig.Profiles) {
		case 0:
			fileConfig.DefaultProfile = ""
		case 1:
			for name := range fileConfig.Profiles {
				fileConfig.DefaultProfile = name
				newDefaultProfile = name
			}
		default:
			fmt.Println("Select a new default profile:")
			_m, err := tea.NewProgram(defaultprofile.New(fileConfig.Profiles)).Run()
			if err != nil {
				return fmt.Errorf("error selecting new default profile: %w", err)
			}
			m := _m.(defaultprofile.Model)
			if m.Success {
				fileConfig.DefaultProfile = m.Choice
				newDefaultProfile = m.Choice
			} else {
				fileConfig.DefaultProfile = ""
			}
		}

		if err := config.WriteConfigToFile(fileConfig); err != nil {
			return fmt.Errorf("failed to update config: %w", err)
		}

		fmt.Printf("Logged out and removed profile '%s'\n", profileName)
		if newDefaultProfile != "" {
			fmt.Printf("'%s' is now set as the default profile\n", newDefaultProfile)
		}
		return nil
	},
}
View Source
var PromqlCmd = &cobra.Command{
	Use:     "promql",
	Short:   "PromQL queries and metrics exploration",
	Long:    "\nRun PromQL queries and explore metrics stored in a Parseable metrics stream.",
	Example: "  pb promql run -i\n  pb promql run \"http_requests_total\" --dataset otel_metrics --from=1h -i",
}

PromqlCmd is the parent command for all PromQL operations.

View Source
var QueryCmd = query
View Source
var RemoveDatasetCmd = &cobra.Command{
	Use:          "remove dataset-name",
	Aliases:      []string{"rm"},
	Example:      " pb dataset remove backend_logs\n pb dataset remove backend_logs --type logs",
	Short:        "Delete a dataset",
	Args:         cobra.ExactArgs(1),
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		name := args[0]
		client := internalHTTP.DefaultClient(&DefaultProfile)
		expectedType, err := cmd.Flags().GetString(datasetTypeFlag)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}
		expectedType = strings.ToLower(strings.TrimSpace(expectedType))
		if expectedType != "" {
			expectedType, err = validateDatasetType(expectedType)
			if err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
				return err
			}

			actualType, err := fetchInfo(&client, name)
			if err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
				return err
			}
			if err := ensureDatasetType(name, actualType, expectedType); err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
				return err
			}
		}

		req, err := client.NewRequest("DELETE", "logstream/"+name, nil)
		if err != nil {

			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		resp, err := client.Client.Do(req)
		if err != nil {

			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		cmd.Annotations["executionTime"] = time.Since(startTime).String()

		if resp.StatusCode == 200 {
			fmt.Printf("Successfully deleted dataset %s\n", StyleBold.Render(name))
		} else {
			bytes, err := io.ReadAll(resp.Body)
			if err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
				return err
			}
			body := string(bytes)
			defer resp.Body.Close()
			fmt.Printf("Request Failed\nStatus Code: %s\nResponse: %s\n", resp.Status, body)
		}

		return nil
	},
}
View Source
var RemoveProfileCmd = &cobra.Command{
	Use:     "remove profile-name",
	Aliases: []string{"rm"},
	Example: "  pb profile remove local_parseable",
	Args:    cobra.ExactArgs(1),
	Short:   "Delete a profile",
	RunE: func(cmd *cobra.Command, args []string) error {
		if cmd.Annotations == nil {
			cmd.Annotations = make(map[string]string)
		}
		startTime := time.Now()

		name := args[0]
		fileConfig, err := config.ReadConfigFromFile()
		if err != nil {
			cmd.Annotations["error"] = fmt.Sprintf("error reading config: %s", err)
			return err
		}

		_, exists := fileConfig.Profiles[name]
		if !exists {
			msg := fmt.Sprintf("No profile found with the name: %s", name)
			cmd.Annotations["error"] = msg
			fmt.Println(msg)
			return nil
		}

		wasDefault := fileConfig.DefaultProfile == name
		delete(fileConfig.Profiles, name)

		if wasDefault {
			switch len(fileConfig.Profiles) {
			case 0:
				fileConfig.DefaultProfile = ""
			case 1:
				for k := range fileConfig.Profiles {
					fileConfig.DefaultProfile = k
					fmt.Printf("'%s' is now set as the default profile\n", k)
				}
			default:
				fmt.Println("Select a new default profile:")
				_m, err := tea.NewProgram(defaultprofile.New(fileConfig.Profiles)).Run()
				if err != nil {
					return fmt.Errorf("error selecting new default profile: %w", err)
				}
				m := _m.(defaultprofile.Model)
				if m.Success {
					fileConfig.DefaultProfile = m.Choice
					fmt.Printf("'%s' is now set as the default profile\n", m.Choice)
				} else {
					fileConfig.DefaultProfile = ""
				}
			}
		}

		commandError := config.WriteConfigToFile(fileConfig)
		cmd.Annotations["executionTime"] = time.Since(startTime).String()
		if commandError != nil {
			cmd.Annotations["error"] = commandError.Error()
			return commandError
		}

		if outputFormat == "json" {
			return outputResult(fmt.Sprintf("Deleted profile %s", name))
		}
		fmt.Printf("Deleted profile %s\n", name)
		return nil
	},
}
View Source
var RemoveRoleCmd = &cobra.Command{
	Use:     "remove role-name",
	Aliases: []string{"rm"},
	Example: "  pb role remove ingestor",
	Short:   "Delete a role",
	Args:    cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		name := args[0]
		client := internalHTTP.DefaultClient(&DefaultProfile)
		req, err := client.NewRequest("DELETE", "role/"+name, nil)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error creating delete request: %s", err.Error())
			return err
		}

		resp, err := client.Client.Do(req)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error performing delete request: %s", err.Error())
			return err
		}
		defer resp.Body.Close()

		if resp.StatusCode == 200 {
			fmt.Printf("Removed role %s\n", StyleBold.Render(name))
		} else {
			bodyBytes, err := io.ReadAll(resp.Body)
			if err != nil {
				cmd.Annotations["errors"] = fmt.Sprintf("Error reading response: %s", err.Error())
				return err
			}
			body := string(bodyBytes)
			cmd.Annotations["errors"] = fmt.Sprintf("Request failed - Status: %s, Response: %s", resp.Status, body)
			fmt.Printf("Request Failed\nStatus Code: %s\nResponse: %s\n", resp.Status, body)
		}

		return nil
	},
}
View Source
var RemoveUserCmd = &cobra.Command{
	Use:     "remove user-name",
	Aliases: []string{"rm"},
	Example: "  pb user remove bob",
	Short:   "Delete a user",
	Args:    cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		name := args[0]
		client := internalHTTP.DefaultClient(&DefaultProfile)
		req, err := client.NewRequest("DELETE", "user/"+name, nil)
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}

		resp, err := client.Client.Do(req)
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}

		if resp.StatusCode == 200 {
			fmt.Printf("Removed user %s\n", StyleBold.Render(name))
			cmd.Annotations["error"] = "none"
		} else {
			body, _ := io.ReadAll(resp.Body)
			fmt.Printf("Request Failed\nStatus Code: %s\nResponse: %s\n", resp.Status, string(body))
			cmd.Annotations["error"] = fmt.Sprintf("request failed with status code %s", resp.Status)
		}

		return nil
	},
}
View Source
var SaveSQLCmd = &cobra.Command{
	Use:          "save [query]",
	Example:      "  pb sql save 'select * from frontend'\n  pb sql save \"select * from frontend\" --name frontend-errors --from=1h --to=now",
	Short:        "Save SQL query",
	Long:         "\nSave a SQL query without running it.",
	Args:         cobra.ExactArgs(1),
	SilenceUsage: true,
	PreRunE:      PreRunDefaultProfile,
	RunE: func(command *cobra.Command, args []string) error {
		startTime := time.Now()
		command.Annotations = map[string]string{
			"startTime": startTime.Format(time.RFC3339),
		}
		defer func() {
			command.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		sqlQuery := strings.TrimSpace(args[0])
		if sqlQuery == "" {
			fmt.Println("Please enter your query")
			fmt.Printf("Example:\n  pb sql save \"select * from frontend\"\n")
			return nil
		}

		start, err := command.Flags().GetString(startFlag)
		if err != nil {
			command.Annotations["error"] = err.Error()
			return err
		}
		if start == "" {
			start = defaultStart
		}

		end, err := command.Flags().GetString(endFlag)
		if err != nil {
			command.Annotations["error"] = err.Error()
			return err
		}
		if end == "" {
			end = defaultEnd
		}

		sqlQuery = quoteStreamNames(sqlQuery)
		sqlQuery = quoteFieldsWithDots(sqlQuery)
		sqlQuery = ensureDefaultLimit(sqlQuery)

		name := strings.TrimSpace(sqlSaveName)
		if name == "" {
			name = defaultSavedQueryName(sqlQuery)
		}

		client := internalHTTP.DefaultClient(&DefaultProfile)
		if err := saveFilter(&client, sqlQuery, name, start, end); err != nil {
			command.Annotations["error"] = err.Error()
			return err
		}

		fmt.Printf("Query saved as '%s'\n", name)
		command.Annotations["error"] = "none"
		return nil
	},
}
View Source
var SavedQueryList = &cobra.Command{
	Use:          "list",
	Aliases:      []string{"ls"},
	Example:      "pb sql list [-o | --output]",
	Short:        "List of saved queries",
	Long:         "\nShow the list of saved queries for active user",
	SilenceUsage: true,
	PreRunE:      PreRunDefaultProfile,
	RunE: func(_ *cobra.Command, _ []string) error {
		client := internalHTTP.DefaultClient(&DefaultProfile)

		if outputFlag != "" {

			userConfig, err := config.ReadConfigFromFile()
			if err != nil {
				fmt.Println("Error reading Default Profile")
			}
			var userProfile config.Profile
			if profile, ok := userConfig.Profiles[userConfig.DefaultProfile]; ok {
				userProfile = profile
			}

			client := &http.Client{
				Timeout: time.Second * 60,
			}
			userSavedQueries := fetchFilters(client, &userProfile)
			// Collect all filter titles in a slice and join with commas
			var filterDetails []string

			if outputFlag == "json" {

				jsonOutput, err := json.MarshalIndent(userSavedQueries, "", "  ")
				if err != nil {
					fmt.Println("Error converting saved queries to JSON:", err)
					return err
				}
				if string(jsonOutput) == "null" {
					fmt.Println("[]")
					return nil
				}
				fmt.Println(string(jsonOutput))
			} else {
				for _, query := range userSavedQueries {
					// Build the line conditionally
					var parts []string
					if query.Title != "" {
						parts = append(parts, query.Title)
					}
					if query.Stream != "" {
						parts = append(parts, query.Stream)
					}
					if query.Desc != "" {
						parts = append(parts, query.Desc)
					}
					if query.From != "" {
						parts = append(parts, query.From)
					}
					if query.To != "" {
						parts = append(parts, query.To)
					}

					fmt.Println(strings.Join(parts, ", "))
				}
			}

			fmt.Println(strings.Join(filterDetails, " "))
			return nil

		}

		p := model.SavedQueriesMenu()
		if _, err := p.Run(); err != nil {
			return err
		}

		a := model.QueryToApply()
		d := model.QueryToDelete()
		if a.SavedQueryID() != "" || strings.TrimSpace(a.Stream()) != "" {
			if err := savedQueryToPbQuery(a.Stream(), a.StartTime(), a.EndTime()); err != nil {
				return err
			}
		}
		if d.SavedQueryID() != "" {
			deleteSavedQuery(&client, d.SavedQueryID(), d.Title())
		}
		return nil
	},
}
View Source
var SetUserRoleCmd = &cobra.Command{
	Use:     "set-role user-name roles",
	Short:   "Set roles for a user",
	Example: "  pb user set-role bob admin,developer",
	PreRunE: func(_ *cobra.Command, args []string) error {
		if len(args) < 2 {
			return fmt.Errorf("requires at least 2 arguments")
		}
		return nil
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		name := args[0]
		client := internalHTTP.DefaultClient(&DefaultProfile)
		users, err := fetchUsers(&client)
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}

		if !slices.ContainsFunc(users, func(user UserData) bool {
			return user.ID == name
		}) {
			fmt.Printf("user doesn't exist. Please create the user with `pb user add %s`\n", name)
			cmd.Annotations["error"] = "user does not exist"
			return nil
		}

		rolesToSet := args[1]
		rolesToSetArr := strings.Split(rolesToSet, ",")
		var rolesOnServer []string
		if err := fetchRoles(&client, &rolesOnServer); err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}
		rolesOnServerArr := strings.Join(rolesOnServer, " ")

		for idx, role := range rolesToSetArr {
			rolesToSetArr[idx] = strings.TrimSpace(role)
			if !strings.Contains(rolesOnServerArr, rolesToSetArr[idx]) {
				fmt.Printf("role %s doesn't exist, please create a role using `pb role add %s`\n", rolesToSetArr[idx], rolesToSetArr[idx])
				cmd.Annotations["error"] = fmt.Sprintf("role %s doesn't exist", rolesToSetArr[idx])
				return nil
			}
		}

		var putBody io.Reader
		putBodyJSON, _ := json.Marshal(rolesToSetArr)
		putBody = bytes.NewBuffer([]byte(putBodyJSON))
		req, err := client.NewRequest("PUT", "user/"+name+"/role", putBody)
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}

		resp, err := client.Client.Do(req)
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}

		bytes, err := io.ReadAll(resp.Body)
		if err != nil {
			cmd.Annotations["error"] = err.Error()
			return err
		}
		body := string(bytes)
		defer resp.Body.Close()

		if resp.StatusCode == 200 {
			fmt.Printf("Added role(s) %s to user %s\n", rolesToSet, name)
			cmd.Annotations["error"] = "none"
		} else {
			fmt.Printf("Request Failed\nStatus Code: %s\nResponse: %s\n", resp.Status, body)
			cmd.Annotations["error"] = fmt.Sprintf("request failed with status code %s", resp.Status)
		}

		return nil
	},
}
View Source
var ShowValuesCmd = &cobra.Command{
	Use:     "show values",
	Short:   "Show values available in Parseable servers",
	Example: "pb show values",
	Run: func(_ *cobra.Command, _ []string) {
		_, err := common.PromptK8sContext()
		if err != nil {
			log.Fatalf("Failed to prompt for Kubernetes context: %v", err)
		}

		entries, err := common.ReadInstallerConfigMap()
		if err != nil {
			log.Fatalf("Failed to list OSS servers: %v", err)
		}

		if len(entries) == 0 {
			fmt.Println("No OSS servers found.")
			return
		}

		selectedCluster, err := common.PromptClusterSelection(entries)
		if err != nil {
			log.Fatalf("Failed to select a cluster: %v", err)
		}

		values, err := helm.GetReleaseValues(selectedCluster.Name, selectedCluster.Namespace)
		if err != nil {
			log.Fatalf("Failed to get values for release: %v", err)
		}

		yamlOutput, err := yaml.Marshal(values)
		if err != nil {
			log.Fatalf("Failed to marshal values to YAML: %v", err)
		}

		fmt.Println(string(yamlOutput))

		fmt.Printf("\nTo get secret values of the Parseable cluster, run the following command:\n")
		fmt.Printf("kubectl get secret -n %s parseable-env-secret -o jsonpath='{.data}' | jq -r 'to_entries[] | \"\\(.key): \\(.value | @base64d)\"'\n", selectedCluster.Namespace)
	},
}

ShowValuesCmd lists the Parseable OSS servers

View Source
var StatDatasetCmd = &cobra.Command{
	Use:          "info dataset-name",
	Aliases:      []string{"stat"},
	Example:      "  pb dataset info backend_logs",
	Short:        "Get statistics for a dataset",
	Args:         cobra.ExactArgs(1),
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		startTime := time.Now()
		cmd.Annotations = make(map[string]string)
		defer func() {
			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		name := args[0]
		client := internalHTTP.DefaultClient(&DefaultProfile)

		datasetType, err := fetchInfo(&client, name)
		if err != nil {
			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		stats, err := fetchStats(&client, name)
		if err != nil {

			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		ingestionCount := stats.Ingestion.Count
		ingestionSize := stats.Ingestion.Size
		storageSize := stats.Storage.Size
		var compressionRatio float64
		if ingestionSize > 0 {
			compressionRatio = 100 - (float64(storageSize) / float64(ingestionSize) * 100)
		}

		retention, err := fetchRetention(&client, name)
		if err != nil {

			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		alertsData, err := fetchAlerts(&client, name)
		if err != nil {

			cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
			return err
		}

		output, _ := cmd.Flags().GetString("output")
		if output == "json" {

			data := map[string]interface{}{
				"info": map[string]interface{}{
					"event_count":       ingestionCount,
					"ingestion_size":    humanize.Bytes(uint64(ingestionSize)),
					"storage_size":      humanize.Bytes(uint64(storageSize)),
					"compression_ratio": fmt.Sprintf("%.2f%%", compressionRatio),
				},
				"retention":    retention,
				"alerts":       alertsData.Alerts,
				"dataset_type": datasetType,
			}

			jsonData, err := json.MarshalIndent(data, "", "  ")
			if err != nil {

				cmd.Annotations["errors"] = fmt.Sprintf("Error: %s", err.Error())
				return err
			}
			fmt.Println(string(jsonData))
		} else {

			isRetentionSet := len(retention) > 0
			isAlertsSet := len(alertsData.Alerts) > 0

			fmt.Println(SelectedStyle.Render("\nInfo:"))
			fmt.Printf("  %-18s %d\n", "Event Count:", ingestionCount)
			fmt.Printf("  %-18s %s\n", "Ingestion Size:", humanize.Bytes(uint64(ingestionSize)))
			fmt.Printf("  %-18s %s\n", "Storage Size:", humanize.Bytes(uint64(storageSize)))
			fmt.Printf("  %-18s %.2f%s\n", "Compression Ratio:", compressionRatio, "%")
			fmt.Printf("  %-18s %s\n", "Dataset Type:", SelectedStyle.Render(datasetType))
			fmt.Println()

			if isRetentionSet {
				fmt.Println(SelectedStyle.Render("Retention:"))
				for _, item := range retention {
					fmt.Printf("  Action:    %s\n", StyleBold.Render(item.Action))
					fmt.Printf("  Duration:  %s\n", StyleBold.Render(item.Duration))
					fmt.Println()
				}
			} else {
				fmt.Println(SelectedStyle.Render("No retention period set on dataset\n"))
			}

			if isAlertsSet {
				fmt.Println(SelectedStyle.Render("Alerts:"))
				for _, alert := range alertsData.Alerts {
					fmt.Printf("  Alert:   %s\n", StyleBold.Render(alert.Name))
					ruleFmt := fmt.Sprintf(
						"%s %s %s repeated %d times",
						alert.Rule.Config.Column,
						alert.Rule.Config.Operator,
						fmt.Sprint(alert.Rule.Config.Value),
						alert.Rule.Config.Repeats,
					)
					fmt.Printf("  Rule:    %s\n", ruleFmt)
					fmt.Printf("  Targets: ")
					for _, target := range alert.Targets {
						fmt.Printf("%s, ", target.Type)
					}
					fmt.Print("\n\n")
				}
			} else {
				fmt.Println(SelectedStyle.Render("No alerts set on dataset\n"))
			}
		}

		return nil
	},
}

StatDatasetCmd is the stat command for dataset

View Source
var StatusCmd = &cobra.Command{
	Use:     "status",
	Short:   "Check connection status for the active profile",
	Example: "  pb status",
	RunE: func(_ *cobra.Command, _ []string) error {
		fileConfig, err := config.ReadConfigFromFile()
		if err != nil {
			return fmt.Errorf("no profile configured. run: pb login")
		}

		profileName := fileConfig.DefaultProfile
		profile, exists := fileConfig.Profiles[profileName]
		if !exists || profileName == "" {
			return fmt.Errorf("no active profile. run: pb login")
		}

		fmt.Printf("Profile : %s\n", profileName)
		fmt.Printf("URL     : %s\n", profile.URL)

		client := internalHTTP.DefaultClient(&profile)
		about, err := analytics.FetchAbout(&client)
		if err != nil {
			statusMessage := statusErrorMessage(err)
			errStyle := lipgloss.NewStyle().Foreground(ui.Active.Err).Bold(true)
			fmt.Printf("Status  : %s\n", errStyle.Render("✗ Not connected"))
			fmt.Printf("Error   : %s\n", statusMessage)
			return fmt.Errorf("status check failed: %s", statusMessage)
		}

		okStyle := lipgloss.NewStyle().Foreground(ui.Active.Ok).Bold(true)
		fmt.Printf("Status  : %s\n", okStyle.Render("✓ Connected"))
		fmt.Printf("Version : %s\n", about.Version)
		return nil
	},
}
View Source
var TailCmd = &cobra.Command{
	Use:     "tail dataset-name",
	Example: " pb tail backend_logs",
	Short:   "Stream live events from a dataset",
	Args:    cobra.ExactArgs(1),
	PreRunE: PreRunDefaultProfile,
	RunE: func(_ *cobra.Command, args []string) error {
		name := args[0]
		profile := DefaultProfile
		return tail(profile, name)
	},
}
View Source
var UninstallOssCmd = &cobra.Command{
	Use:     "uninstall",
	Short:   "Uninstall Parseable servers",
	Example: "pb uninstall",
	Run: func(_ *cobra.Command, _ []string) {
		_, err := common.PromptK8sContext()
		if err != nil {
			log.Fatalf("Failed to prompt for Kubernetes context: %v", err)
		}

		entries, err := common.ReadInstallerConfigMap()
		if err != nil {
			log.Fatalf("Failed to fetch OSS servers: %v", err)
		}

		if len(entries) == 0 {
			fmt.Println(common.Yellow + "\nNo Parseable OSS servers found to uninstall.")
			return
		}

		selectedCluster, err := common.PromptClusterSelection(entries)
		if err != nil {
			log.Fatalf("Failed to select a cluster: %v", err)
		}

		fmt.Println("\n────────────────────────────────────────────────────────────────────────────")
		fmt.Println("⚠️  Deleting this cluster will not delete any data on object storage.")
		fmt.Println("   This operation will clean up the Parseable deployment on Kubernetes.")
		fmt.Println("────────────────────────────────────────────────────────────────────────────")

		fmt.Printf("\nYou have selected to uninstall the cluster '%s' in namespace '%s'.\n", selectedCluster.Name, selectedCluster.Namespace)
		if !common.PromptConfirmation(fmt.Sprintf("Do you want to proceed with uninstalling '%s'?", selectedCluster.Name)) {
			fmt.Println(common.Yellow + "Uninstall operation canceled.")
			return
		}

		if err := uninstallCluster(selectedCluster); err != nil {
			log.Fatalf("Failed to uninstall cluster: %v", err)
		}

		if err := common.RemoveInstallerEntry(selectedCluster.Name); err != nil {
			log.Fatalf("Failed to remove entry from ConfigMap: %v", err)
		}

		if err := deleteSecret(selectedCluster.Namespace, "parseable-env-secret"); err != nil {
			log.Printf("Warning: Failed to delete secret 'parseable-env-secret': %v", err)
		} else {
			fmt.Println(common.Green + "Secret 'parseable-env-secret' deleted successfully." + common.Reset)
		}

		fmt.Println(common.Green + "Uninstallation completed successfully." + common.Reset)
	},
}

UninstallOssCmd removes Parseable OSS servers

View Source
var UpdateProfileCmd = &cobra.Command{
	Use:     "update profile-name new-url",
	Aliases: []string{"set-url"},
	Example: "  pb profile update local http://localhost:9000",
	Short:   "Update the URL of an existing profile",
	Args:    cobra.ExactArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		if cmd.Annotations == nil {
			cmd.Annotations = make(map[string]string)
		}
		startTime := time.Now()

		name := args[0]
		rawURL := args[1]

		if _, err := url.Parse(rawURL); err != nil {
			return fmt.Errorf("invalid URL: %w", err)
		}

		fileConfig, err := config.ReadConfigFromFile()
		if err != nil {
			return fmt.Errorf("error reading config: %w", err)
		}

		profile, exists := fileConfig.Profiles[name]
		if !exists {
			return fmt.Errorf("no profile found with the name: %s", name)
		}

		profile.URL = rawURL
		fileConfig.Profiles[name] = profile

		commandError := config.WriteConfigToFile(fileConfig)
		cmd.Annotations["executionTime"] = time.Since(startTime).String()
		if commandError != nil {
			cmd.Annotations["error"] = commandError.Error()
			return commandError
		}

		if outputFormat == "json" {
			return outputResult(profile)
		}
		fmt.Printf("Profile '%s' URL updated to %s\n", name, rawURL)
		return nil
	},
}
View Source
var VersionCmd = &cobra.Command{
	Use:     "version",
	Short:   "Print version",
	Long:    "Print version and commit information",
	Example: "  pb version",
	Run: func(cmd *cobra.Command, _ []string) {
		if cmd.Annotations == nil {
			cmd.Annotations = make(map[string]string)
		}

		startTime := time.Now()
		defer func() {

			cmd.Annotations["executionTime"] = time.Since(startTime).String()
		}()

		err := PrintVersion("1.0.0", "abc123")
		if err != nil {
			cmd.Annotations["error"] = err.Error()
		}
	},
}

VersionCmd is the command for printing version information

Functions

func Max

func Max(a int, b int) int

func PreRun

func PreRun() error

func PreRunDefaultProfile

func PreRunDefaultProfile(_ *cobra.Command, _ []string) error

PreRunDefaultProfile if a profile exists. This is required by mostly all commands except profile

func PrintVersion

func PrintVersion(version, commit string) error

PrintVersion prints version information

Types

type Alert

type Alert struct {
	Targets []Target `json:"targets"`
	Name    string   `json:"name"`
	Message string   `json:"message"`
	Rule    Rule     `json:"rule"`
}

Alert structure

type AlertConfig

type AlertConfig struct {
	Version string  `json:"version"`
	Alerts  []Alert `json:"alerts"`
}

AlertConfig structure

type DatasetListItem

type DatasetListItem struct {
	Name         string
	Type         string
	LastIngested time.Time
}

func (*DatasetListItem) Render

func (item *DatasetListItem) Render() string

type DatasetRetentionData

type DatasetRetentionData []struct {
	Description string `json:"description"`
	Action      string `json:"action"`
	Duration    string `json:"duration"`
}

DatasetRetentionData is the data structure for dataset retention

type DatasetStatsData

type DatasetStatsData struct {
	Ingestion struct {
		Count  int    `json:"count"`
		Format string `json:"format"`
		Size   uint64 `json:"size"`
	} `json:"ingestion"`
	Storage struct {
		Format string `json:"format"`
		Size   uint64 `json:"size"`
	} `json:"storage"`
	Stream string    `json:"stream"`
	Time   time.Time `json:"time"`
}

DatasetStatsData is the data structure for dataset stats

type Item

type Item struct {
	ID     string `json:"id"`
	Title  string `json:"title"`
	Stream string `json:"stream"`
	Desc   string `json:"desc"`
	From   string `json:"from,omitempty"`
	To     string `json:"to,omitempty"`
}

type ProfileListItem

type ProfileListItem struct {
	// contains filtered or unexported fields
}

ProfileListItem is a struct to hold the profile list items

func (*ProfileListItem) Render

func (item *ProfileListItem) Render(highlight bool) string

type Repeat

type Repeat struct {
	Interval string `json:"interval"`
	Times    int    `json:"times"`
}

Repeat structure

type RoleData

type RoleData struct {
	Privilege string        `json:"privilege"`
	Resource  *RoleResource `json:"resource,omitempty"`
}

func (*RoleData) Render

func (user *RoleData) Render() string

type RoleResource

type RoleResource struct {
	Stream string `json:"stream,omitempty"`
}

type Rule

type Rule struct {
	Type   string     `json:"type"`
	Config RuleConfig `json:"config"`
}

Rule structure

type RuleConfig

type RuleConfig struct {
	Column     string      `json:"column"`
	Operator   string      `json:"operator"`
	IgnoreCase bool        `json:"ignoreCase"`
	Value      interface{} `json:"value"`
	Repeats    int         `json:"repeats"`
}

RuleConfig structure

type Target

type Target struct {
	Type         string            `json:"type"`
	Endpoint     string            `json:"endpoint"`
	Headers      map[string]string `json:"headers"`
	SkipTLSCheck bool              `json:"skip_tls_check"`
	Repeat       Repeat            `json:"repeat"`
}

Target structure

type UserData

type UserData struct {
	ID     string `json:"id"`
	Method string `json:"method"`
}

type UserRoleAction

type UserRoleAction struct {
	Privilege string        `json:"privilege"`
	Resource  *RoleResource `json:"resource,omitempty"`
}

UserRoleAction is a single privilege entry within a named role

type UserRolesResponse

type UserRolesResponse struct {
	DirectRoles map[string]UserServerRole            `json:"roles"`
	GroupRoles  map[string]map[string]UserServerRole `json:"group_roles"`
}

UserRolesResponse is the response from GET /user/{name}/role

type UserServerRole

type UserServerRole struct {
	Actions []UserRoleAction `json:"actions"`
}

UserServerRole is a named role definition returned by the server

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL