client

package
v0.5.8 Latest Latest
Warning

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

Go to latest
Published: Jan 27, 2025 License: Apache-2.0 Imports: 35 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AttributesCmd = &cobra.Command{
	Use:       "attributes " + strings.Join(validArgs, "|"),
	Args:      validateAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
	ValidArgs: validArgs,
	Short:     "List Hermes attributes",
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if err := viper.BindPFlags(cmd.Flags()); err != nil {
			return err
		}

		return verifyGlobalFlags(nil)
	},
	RunE: func(cmd *cobra.Command, args []string) error {

		client, err := NewHermesV1Client(cmd.Context())
		if err != nil {
			return fmt.Errorf("failed to create Hermes client: %w", err)
		}

		format := viper.GetString("format")

		projectID := viper.GetString("project-id")
		if viper.GetBool("all-projects") {
			projectID = "*"
		}

		listOpts := attributes.ListOpts{
			Limit:     viper.GetInt("limit"),
			MaxDepth:  viper.GetInt("max-depth"),
			ProjectID: projectID,
		}

		var allAttributes []string

		for _, name := range args {
			err = attributes.List(client, name, listOpts).EachPage(cmd.Context(), func(ctx context.Context, page pagination.Page) (bool, error) {
				attrs, err := attributes.ExtractAttributes(page)
				if err != nil {
					return false, fmt.Errorf("failed to extract attributes: %w", err)
				}

				allAttributes = append(allAttributes, attrs...)

				return true, nil
			})
			if err != nil {
				if gophercloud.ResponseCodeIs(err, http.StatusInternalServerError) {
					return fmt.Errorf(`failed to list attributes: %w: please try to decrease the amount of the attributes in output, e.g. set "--limit 100"`, err)
				}
				return fmt.Errorf("failed to list attributes: %w", err)
			}
		}

		switch format {
		case "json":
			jsonAttrs, err := json.MarshalIndent(allAttributes, "", "  ")
			if err != nil {
				return err
			}
			fmt.Printf("%s\n", jsonAttrs)
		case "yaml":
			yamlAttrs, err := yaml.Marshal(allAttributes)
			if err != nil {
				return err
			}
			fmt.Printf("%s", yamlAttrs)
		case "csv", "value", "table":
			fmt.Printf("%s\n", strings.Join(allAttributes, "\n"))
		default:
			return fmt.Errorf("unsupported format: %s", format)
		}

		return nil
	},
}

AttributesCmd represents the list command

View Source
var ExportCmd = &cobra.Command{
	Use:   "export",
	Args:  cobra.ExactArgs(0),
	Short: "Export Hermes events to Swift",
	Long: `Export Hermes events to Swift storage container.
Exports can be saved in different formats (json, csv, yaml) for further processing or archival.`,
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if err := viper.BindPFlags(cmd.Flags()); err != nil {
			return fmt.Errorf("failed to bind flags: %w", err)
		}

		if viper.GetString("container") == "" {
			return errors.New("container name is required")
		}

		_, err := parseExportFormat(viper.GetString("format"))
		if err != nil {
			return err
		}

		return verifyGlobalFlags(defaultListKeyOrder)
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		ctx := cmd.Context()

		if viper.GetInt("limit") > maxOffset {
			fmt.Fprintf(os.Stderr, "Warning: Exporting more than %d events may take a long time.\n\n", maxOffset)
		}

		client, err := NewHermesV1Client(ctx)
		if err != nil {
			return fmt.Errorf("failed to create Hermes client: %w", err)
		}

		fmt.Fprintf(os.Stderr, "Fetching events...\n")

		var allEvents []events.Event
		var bar *pb.ProgressBar

		logg.Debug("fetching events matching specified criteria")

		listOpts := buildListOpts()
		if err = getEvents(ctx, client, &allEvents, listOpts, viper.GetInt("limit"), true, &bar); err != nil {
			if bar != nil {
				bar.Finish()
			}
			return fmt.Errorf("failed to list events: %w", err)
		}
		if bar != nil {
			bar.Finish()
		}

		if len(allEvents) == 0 {
			return errors.New("no events found matching the specified criteria")
		}

		fmt.Fprintf(os.Stderr, "\nFound %d events to export\n", len(allEvents))

		fmt.Fprintf(os.Stderr, "Converting to %s format...\n", viper.GetString("format"))
		var buf bytes.Buffer
		if err = convertToRequestedFormat(&buf, allEvents, viper.GetString("format")); err != nil {
			return fmt.Errorf("failed to convert events: %w", err)
		}

		dataSize := float64(buf.Len()) / 1024 / 1024
		fmt.Fprintf(os.Stderr, "Uploading %.1fMB to Swift...\n", dataSize)

		uploadBar := pb.Full.Start64(int64(buf.Len()))
		uploadBar.Set(pb.Bytes, true)
		uploadBar.Set(pb.Terminal, true)
		uploadBar.SetWidth(0)
		defer uploadBar.Finish()

		progressReader := &progressReader{
			Reader: &buf,
			Bar:    uploadBar,
		}

		container, err := InitializeSwiftContainer(
			ctx,
			client.ProviderClient,
			viper.GetString("container"),
		)
		if err != nil {
			return fmt.Errorf("failed to initialize Swift container: %w", err)
		}

		// Use hyphenated timestamp format for safe and sortable filenames
		const timeFormat = "2006-01-02-150405"

		filename := viper.GetString("filename")
		if filename == "" {
			filename = "hermes-export-" + time.Now().Format(timeFormat)
		}

		format, err := parseExportFormat(viper.GetString("format"))
		if err != nil {
			return fmt.Errorf("invalid format: %w", err)
		}
		exportFile := ExportFile{
			Format:      format,
			FileName:    filename,
			SegmentSize: uint64(viper.GetInt("segment-size")) * 1024 * 1024,
			Contents:    progressReader,
		}

		if err := exportFile.UploadTo(ctx, container); err != nil {
			return fmt.Errorf("failed to upload to Swift: %w", err)
		}

		fmt.Fprintf(os.Stderr, "\nSuccessfully exported %d events\n", len(allEvents))
		return nil
	},
}

ExportCmd represents the export command

View Source
var ListCmd = &cobra.Command{
	Use:   "list",
	Args:  cobra.ExactArgs(0),
	Short: "List Hermes events",
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if err := viper.BindPFlags(cmd.Flags()); err != nil {
			return err
		}

		teq := viper.GetString("time")
		tgt := viper.GetString("time-start")
		tlt := viper.GetString("time-end")
		if teq != "" && !(tgt == "" && tlt == "") {
			return errors.New("cannot combine time flag with time-start or time-end flags")
		}

		return verifyGlobalFlags(defaultListKeyOrder)
	},
	RunE: func(cmd *cobra.Command, args []string) error {

		userLimit := viper.GetInt("limit")
		keyOrder := viper.GetStringSlice("column")
		if len(keyOrder) == 0 {
			keyOrder = defaultListKeyOrder
		}
		format := viper.GetString("format")

		projectID := viper.GetString("project-id")
		if viper.GetBool("all-projects") {
			projectID = "*"
		}

		listOpts := events.ListOpts{
			Limit:         maxOffset,
			TargetType:    viper.GetString("target-type"),
			TargetID:      viper.GetString("target-id"),
			InitiatorID:   viper.GetString("initiator-id"),
			InitiatorName: viper.GetString("initiator-name"),
			Action:        viper.GetString("action"),
			Outcome:       viper.GetString("outcome"),
			RequestPath:   viper.GetString("request-path"),
			ObserverType:  viper.GetString("source"),
			Search:        viper.GetString("search"),
			ProjectID:     projectID,
			Sort:          strings.Join(viper.GetStringSlice("sort"), ","),
		}

		if userLimit > 0 && userLimit <= maxOffset {

			listOpts.Limit = userLimit
		}

		if t := viper.GetString("time"); t != "" {
			rt, err := parseTime(t)
			if err != nil {
				return fmt.Errorf("failed to parse time: %w", err)
			}
			listOpts.Time = []events.DateQuery{
				{
					Date: rt,
				},
			}
		}
		if t := viper.GetString("time-start"); t != "" {
			rt, err := parseTime(t)
			if err != nil {
				return fmt.Errorf("failed to parse time-start: %w", err)
			}
			listOpts.Time = append(listOpts.Time, events.DateQuery{
				Date:   rt,
				Filter: events.DateFilterGTE,
			})
		}
		if t := viper.GetString("time-end"); t != "" {
			rt, err := parseTime(t)
			if err != nil {
				return fmt.Errorf("failed to parse time-end: %w", err)
			}
			listOpts.Time = append(listOpts.Time, events.DateQuery{
				Date:   rt,
				Filter: events.DateFilterLTE,
			})
		}

		client, err := NewHermesV1Client(cmd.Context())
		if err != nil {
			return fmt.Errorf("failed to create Hermes client: %w", err)
		}

		var allEvents []events.Event
		var bar *pb.ProgressBar

		if err = getEvents(cmd.Context(), client, &allEvents, listOpts, userLimit, viper.GetBool("over-10k-fix"), &bar); err != nil {
			if bar != nil {
				bar.Finish()
			}
			return fmt.Errorf("failed to list the events: %w", err)
		}
		if bar != nil {
			bar.Finish()
		}

		if format == "table" {
			var buf bytes.Buffer
			table := tablewriter.NewWriter(&buf)
			table.SetColWidth(20)
			table.SetAlignment(3)
			table.SetHeader(keyOrder)

			for _, v := range allEvents {
				kv := eventToKV(v)
				tableRow := []string{}
				for _, k := range keyOrder {
					v := kv[k]
					tableRow = append(tableRow, v)
				}
				table.Append(tableRow)
			}

			table.Render()

			fmt.Print(buf.String())
		} else {
			return printEvent(allEvents, format, keyOrder)
		}

		return nil
	},
}

ListCmd represents the list command

View Source
var RootCmd = &cobra.Command{
	Use:          "hermescli",
	Short:        "Hermes CLI tool",
	SilenceUsage: true,
}

RootCmd represents the base command when called without any subcommands

View Source
var ShowCmd = &cobra.Command{
	Use:   "show <event-id> [<event-id>...]",
	Args:  cobra.MinimumNArgs(1),
	Short: "Show Hermes event",
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if err := viper.BindPFlags(cmd.Flags()); err != nil {
			return err
		}

		return verifyGlobalFlags(defaultShowKeyOrder)
	},
	RunE: func(cmd *cobra.Command, args []string) error {

		client, err := NewHermesV1Client(cmd.Context())
		if err != nil {
			return fmt.Errorf("failed to create Hermes client: %w", err)
		}

		keyOrder := viper.GetStringSlice("column")
		if len(keyOrder) == 0 {
			keyOrder = defaultShowKeyOrder
		}
		format := viper.GetString("format")

		// initialize the progress bar, when multiple events are requested
		var bar *pb.ProgressBar
		if len(args) > 1 {
			bar = pb.New(len(args))
			bar.SetWriter(os.Stderr)
			bar.Start()
		}

		projectID := viper.GetString("project-id")
		if viper.GetBool("all-projects") {
			projectID = "*"
		}

		getOpts := events.GetOpts{
			ProjectID: projectID,
		}

		var allEvents []events.Event
		for i, id := range args {
			if bar != nil {
				bar.SetCurrent(int64(i + 1))
			}
			event, err := events.Get(cmd.Context(), client, id, getOpts).Extract()
			if err != nil {
				log.Printf("[WARNING] Failed to get %s event: %s", id, err)
				continue
			}
			allEvents = append(allEvents, *event)
		}

		if bar != nil {
			bar.Finish()
		}

		if format == "table" {
			for _, event := range allEvents {
				kv := eventToKV(event)

				// create table
				var buf bytes.Buffer
				table := tablewriter.NewWriter(&buf)
				table.SetColWidth(20)
				table.SetAlignment(3)
				table.SetHeader([]string{"Key", "Value"})

				for _, k := range keyOrder {
					if v, ok := kv[k]; ok {
						table.Append([]string{k, v})
					}
				}

				table.Render()

				fmt.Print(buf.String())
			}
		} else {
			return printEvent(allEvents, format, keyOrder)
		}

		return nil
	},
}

ShowCmd represents the show command

View Source
var Version = "dev"
View Source
var VersionCmd = &cobra.Command{
	Use:               "version",
	Short:             "Print version information",
	DisableAutoGenTag: true,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("hermescli %s compiled with %v on %v/%v\n",
			Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
	},
}

Functions

func Execute

func Execute()

Execute adds all child commands to the root command sets flags appropriately. This is called by main.main(). It only needs to happen once to the rootCmd.

func InitializeSwiftContainer added in v0.5.8

func InitializeSwiftContainer(ctx context.Context, provider *gophercloud.ProviderClient, containerName string) (*schwift.Container, error)

InitializeSwiftContainer creates and initializes a Swift container

func NewHermesV1Client

func NewHermesV1Client(ctx context.Context) (*gophercloud.ServiceClient, error)

NewHermesV1Client returns a *ServiceClient for making calls to the OpenStack Lyra v1 API. An error will be returned if authentication or client creation was not possible.

Types

type ExportFile added in v0.5.8

type ExportFile struct {
	Format      ExportFormat
	FileName    string
	SegmentSize uint64
	Contents    io.Reader
}

ExportFile represents a file to be exported to Swift storage

func (ExportFile) UploadTo added in v0.5.8

func (f ExportFile) UploadTo(ctx context.Context, container *schwift.Container) error

type ExportFormat added in v0.5.8

type ExportFormat string
const (
	ExportFormatJSON ExportFormat = "json"
	ExportFormatYAML ExportFormat = "yaml"
	ExportFormatCSV  ExportFormat = "csv"
)

Jump to

Keyboard shortcuts

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