Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve flags handling [PC-13534] #177

Merged
merged 7 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/e2e-tests-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ on:
description: Makefile test target to run
required: false
type: string
default: test/e2e
sloctlImage:
description: >
Sloctl docker image to use for e2e docker image tests.
Expand Down
13 changes: 8 additions & 5 deletions internal/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type ApplyCmd struct {
autoConfirm bool
replay bool
replayFrom TimeValue
project string
}

//go:embed apply_example.sh
Expand All @@ -37,19 +38,21 @@ func (r *RootCmd) NewApplyCmd() *cobra.Command {
Args: positionalArgsCondition,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
apply.client = r.GetClient()
if r.Flags.Project != "" {
if apply.project != "" {
apply.projectFlagWasSet = true
apply.client.Config.Project = apply.project
}
if apply.dryRun {
NotifyDryRunFlag()
notifyDryRunFlag()
}
},
RunE: func(cmd *cobra.Command, args []string) error { return apply.Run(cmd) },
}

RegisterFileFlag(cmd, true, &apply.definitionPaths)
RegisterDryRunFlag(cmd, &apply.dryRun)
RegisterAutoConfirmationFlag(cmd, &apply.autoConfirm)
registerFileFlag(cmd, true, &apply.definitionPaths)
registerDryRunFlag(cmd, &apply.dryRun)
registerAutoConfirmationFlag(cmd, &apply.autoConfirm)
registerProjectFlag(cmd, &apply.project)

const (
replayFlagName = "replay"
Expand Down
2 changes: 1 addition & 1 deletion internal/aws_iam_ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (r *RootCmd) NewAwsIamIds() *cobra.Command {
PersistentPreRun: func(iamIdsCmd *cobra.Command, args []string) { awsIamIds.client = r.GetClient() },
RunE: func(iamIdsCmd *cobra.Command, args []string) error { return awsIamIds.Direct(iamIdsCmd) },
}
RegisterOutputFormatFlags(direct, &awsIamIds.outputFormat, &awsIamIds.fieldSeparator, &awsIamIds.recordSeparator)
registerOutputFormatFlags(direct, &awsIamIds.outputFormat, &awsIamIds.fieldSeparator, &awsIamIds.recordSeparator)
cobraCmd.AddCommand(direct)

dataExport := &cobra.Command{
Expand Down
4 changes: 2 additions & 2 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (c *ConfigCmd) CurrentContextCommand() *cobra.Command {
},
}

RegisterVerboseFlag(currentCtxCmd, &c.verbose)
registerVerboseFlag(currentCtxCmd, &c.verbose)

return currentCtxCmd
}
Expand Down Expand Up @@ -331,7 +331,7 @@ func (c *ConfigCmd) GetContextsCommand() *cobra.Command {
},
}

RegisterVerboseFlag(getContextsCmd, &c.verbose)
registerVerboseFlag(getContextsCmd, &c.verbose)

return getContextsCmd
}
Expand Down
18 changes: 12 additions & 6 deletions internal/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type DeleteCmd struct {
definitionPaths []string
dryRun bool
autoConfirm bool
project string
}

//go:embed delete_example.sh
Expand All @@ -36,19 +37,21 @@ func (r *RootCmd) NewDeleteCmd() *cobra.Command {
Args: positionalArgsCondition,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
deleteCmd.client = r.GetClient()
if r.Flags.Project != "" {
if deleteCmd.project != "" {
deleteCmd.projectFlagWasSet = true
deleteCmd.client.Config.Project = deleteCmd.project
}
if deleteCmd.dryRun {
NotifyDryRunFlag()
notifyDryRunFlag()
}
},
RunE: func(cmd *cobra.Command, args []string) error { return deleteCmd.Run(cmd) },
}

RegisterFileFlag(cmd, false, &deleteCmd.definitionPaths)
RegisterDryRunFlag(cmd, &deleteCmd.dryRun)
RegisterAutoConfirmationFlag(cmd, &deleteCmd.autoConfirm)
registerFileFlag(cmd, false, &deleteCmd.definitionPaths)
registerDryRunFlag(cmd, &deleteCmd.dryRun)
registerAutoConfirmationFlag(cmd, &deleteCmd.autoConfirm)
registerProjectFlag(cmd, &deleteCmd.project)

// register all subcommands for delete
for _, def := range []struct {
Expand Down Expand Up @@ -124,7 +127,10 @@ func newSubcommand(
return runSubcommand(cmd.Context(), deleteCmd, kind, args)
},
}
RegisterDryRunFlag(sc, &deleteCmd.dryRun)
if objectKindSupportsProjectFlag(kind) {
registerProjectFlag(sc, &deleteCmd.project)
}
registerDryRunFlag(sc, &deleteCmd.dryRun)
return sc
}

Expand Down
59 changes: 53 additions & 6 deletions internal/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

"github.com/spf13/cobra"

"github.com/nobl9/nobl9-go/manifest"

"github.com/nobl9/sloctl/internal/csv"
)

Expand All @@ -14,11 +16,11 @@ const (
flagDryRun = "dry-run"
)

func NotifyDryRunFlag() {
func notifyDryRunFlag() {
_, _ = fmt.Fprintln(os.Stderr, "Running in dry run mode, changes will not be applied.")
}

func RegisterFileFlag(cmd *cobra.Command, required bool, storeIn *[]string) {
func registerFileFlag(cmd *cobra.Command, required bool, storeIn *[]string) {
cmd.Flags().StringArrayVarP(storeIn, flagFile, "f", []string{},
"File path, glob pattern or a URL to the configuration in YAML or JSON format."+
" This option can be used multiple times.")
Expand All @@ -27,23 +29,23 @@ func RegisterFileFlag(cmd *cobra.Command, required bool, storeIn *[]string) {
}
}

func RegisterDryRunFlag(cmd *cobra.Command, storeIn *bool) {
func registerDryRunFlag(cmd *cobra.Command, storeIn *bool) {
cmd.Flags().BoolVarP(storeIn, flagDryRun, "", false,
"Submit server-side request without persisting the configured resources.")
}

func RegisterVerboseFlag(cmd *cobra.Command, storeIn *bool) {
func registerVerboseFlag(cmd *cobra.Command, storeIn *bool) {
cmd.Flags().BoolVarP(storeIn, "verbose", "v", false,
"Display verbose information about configuration")
}

func RegisterAutoConfirmationFlag(cmd *cobra.Command, storeIn *bool) {
func registerAutoConfirmationFlag(cmd *cobra.Command, storeIn *bool) {
cmd.Flags().BoolVarP(storeIn, "yes", "y", false,
"Auto confirm files threshold prompt."+
" Threshold can be changed or disabled in config.toml or via env variables.")
}

func RegisterOutputFormatFlags(cmd *cobra.Command, outputFormat, fieldSeparator, recordSeparator *string) {
func registerOutputFormatFlags(cmd *cobra.Command, outputFormat, fieldSeparator, recordSeparator *string) {
cmd.PersistentFlags().StringVarP(outputFormat, "output", "o", "yaml",
`Output format: one of yaml|json|csv.`)

Expand All @@ -53,3 +55,48 @@ func RegisterOutputFormatFlags(cmd *cobra.Command, outputFormat, fieldSeparator,
cmd.PersistentFlags().StringVarP(recordSeparator, csv.RecordSeparatorFlag, "",
csv.DefaultRecordSeparator, "Record Separator for CSV.")
}

var projectFlagSupportingKinds = map[manifest.Kind]struct{}{
manifest.KindSLO: {},
manifest.KindService: {},
manifest.KindAgent: {},
manifest.KindAlertPolicy: {},
manifest.KindAlertSilence: {},
manifest.KindAlertMethod: {},
manifest.KindDirect: {},
manifest.KindDataExport: {},
manifest.KindRoleBinding: {},
manifest.KindAnnotation: {},
}

func objectKindSupportsProjectFlag(kind manifest.Kind) bool {
_, ok := projectFlagSupportingKinds[kind]
return ok
}

func registerProjectFlag(cmd *cobra.Command, storeIn *string) {
cmd.Flags().StringVarP(storeIn, "project", "p", "",
`List the requested object(s) which belong to the specified Project (name).`)
}

func registerAllProjectsFlag(cmd *cobra.Command, storeIn *bool) {
cmd.Flags().BoolVarP(storeIn, "all-projects", "A", false,
`List the requested object(s) across all projects.`)
}

var labelSupportingKinds = map[manifest.Kind]struct{}{
manifest.KindProject: {},
manifest.KindService: {},
manifest.KindSLO: {},
manifest.KindAlertPolicy: {},
}

func objectKindSupportsLabelsFlag(kind manifest.Kind) bool {
_, ok := labelSupportingKinds[kind]
return ok
}

func registerLabelsFlag(cmd *cobra.Command, storeIn *[]string) {
cmd.Flags().StringArrayVarP(storeIn, "label", "l", []string{},
`Filter resource by label. Example: key=value,key2=value2,key2=value3.`)
}
41 changes: 25 additions & 16 deletions internal/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"strings"
"sync"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"

Expand All @@ -35,6 +34,8 @@ type GetCmd struct {
labels []string
fieldSeparator string
recordSeparator string
project string
allProjects bool
out io.Writer
}

Expand All @@ -47,18 +48,26 @@ func (r *RootCmd) NewGetCmd() *cobra.Command {
Short: "Display one or more than one resource",
Long: `Prints a table of the most important information about the specified resources.
To get more details in output use one of the available flags.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) { get.client = r.GetClient() },
PersistentPreRun: func(cmd *cobra.Command, args []string) {
get.client = r.GetClient()
if get.allProjects {
get.client.Config.Project = "*"
} else if get.project != "" {
get.client.Config.Project = get.project
}
},
}

// All flags for 'get' and its subcommands.
// All shared flags for 'get' and its subcommands.
get.RegisterFlags(cmd)

// All subcommands for get.
for _, subCmd := range []struct {
Kind manifest.Kind
Aliases []string
Plural string
Extender func(cmd *cobra.Command) *cobra.Command
Kind manifest.Kind
Aliases []string
Plural string
Extender func(cmd *cobra.Command) *cobra.Command
IsProjectScoped bool
}{
{Kind: manifest.KindAgent, Aliases: []string{"agent", "Agents", "Agent"}, Extender: get.newGetAgentCommand},
{Kind: manifest.KindAlertMethod},
Expand Down Expand Up @@ -87,24 +96,27 @@ To get more details in output use one of the available flags.`,
if subCmd.Extender != nil {
subCmd.Extender(sc)
}
if objectKindSupportsProjectFlag(subCmd.Kind) {
registerProjectFlag(sc, &get.project)
registerAllProjectsFlag(sc, &get.allProjects)
}
if objectKindSupportsLabelsFlag(subCmd.Kind) {
registerLabelsFlag(sc, &get.labels)
}
cmd.AddCommand(sc)
}

return cmd
}

func (g *GetCmd) RegisterFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringArrayVarP(&g.labels, "label", "l", []string{},
`Filter resource by label. Example: key=value,key2=value2,key2=value3.`)

// Hidden variables.
mustHide := func(f string) {
if err := cmd.PersistentFlags().MarkHidden(f); err != nil {
panic(err)
}
}

RegisterOutputFormatFlags(cmd, &g.outputFormat, &g.fieldSeparator, &g.recordSeparator)
registerOutputFormatFlags(cmd, &g.outputFormat, &g.fieldSeparator, &g.recordSeparator)
mustHide(csv.RecordSeparatorFlag)
}

Expand Down Expand Up @@ -237,6 +249,7 @@ func (g *GetCmd) newGetAlertCommand(cmd *cobra.Command) *cobra.Command {
return err
}
if len(objects) == 0 {
fmt.Printf("No resources found.\n")
return nil
}
if err = g.printObjects(objects); err != nil {
Expand Down Expand Up @@ -355,10 +368,6 @@ func (g *GetCmd) enrichAgentWithSecrets(
}

func (g *GetCmd) getObjects(ctx context.Context, args []string, kind manifest.Kind) ([]manifest.Object, error) {
if kind == manifest.KindAlert && g.client.Config.Project != sdk.ProjectsWildcard {
return nil, errors.New("'sloctl get alerts' does not support Project filtering," +
" explicitly pass '-A' flag to fetch all Alerts.")
}
query := url.Values{objectsV1.QueryKeyName: args}
if len(g.labels) > 0 {
query.Set(objectsV1.QueryKeyLabels, parseFilterLabel(g.labels))
Expand Down
2 changes: 1 addition & 1 deletion internal/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (r *RootCmd) NewReplayCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { return replay.Run(cmd) },
}

RegisterFileFlag(cmd, false, &replay.configPaths)
registerFileFlag(cmd, false, &replay.configPaths)
cmd.Flags().Var(&replay.from, "from", "Sets the start of Replay time window.")

return cmd
Expand Down
11 changes: 0 additions & 11 deletions internal/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ func Execute() {
type globalFlags struct {
ConfigFile string
Context string
Project string
AllProjects bool
NoConfigFile bool
}

Expand All @@ -46,10 +44,6 @@ For every command more detailed help is available.`,
rootCmd.PersistentFlags().StringVar(&root.Flags.ConfigFile, "config", "", "Config file path.")
rootCmd.PersistentFlags().StringVarP(&root.Flags.Context, "context", "c", "",
`Overrides the default context for the duration of the selected command.`)
rootCmd.PersistentFlags().StringVarP(&root.Flags.Project, "project", "p", "",
`Overrides the default project from active Delete for the duration of the selected command.`)
rootCmd.PersistentFlags().BoolVarP(&root.Flags.AllProjects, "all-projects", "A", false,
`Displays the objects from all of the projects.`)
rootCmd.PersistentFlags().BoolVarP(&root.Flags.NoConfigFile, "no-config-file", "", false,
`Don't create config.toml, operate only on env variables.`)

Expand Down Expand Up @@ -92,11 +86,6 @@ func (r *RootCmd) setupClient() error {
if err != nil {
return err
}
if r.Flags.AllProjects {
conf.Project = "*"
} else if r.Flags.Project != "" {
conf.Project = r.Flags.Project
}
r.Client, err = sdk.NewClient(conf)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion test/delete-by-name.bats
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ test_delete_by_name() {

# Delete the object by name.
args=(delete "$kind" "$object_name")
if [[ $kind != "Project" ]]; then
if [[ $kind != "Project" ]] && [[ $kind != "BudgetAdjustment" ]]; then
args+=("-p" "death-star")
fi
run_sloctl "${args[@]}"
Expand Down
Loading
Loading