diff --git a/cmd/list.go b/cmd/list.go index 45ea685c..1c8d92cd 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -14,6 +14,7 @@ import ( "gopkg.in/yaml.v2" fn "knative.dev/func" + "knative.dev/func/config" ) func NewListCmd(newClient ClientFactory) *cobra.Command { @@ -35,12 +36,18 @@ Lists all deployed functions in a given namespace. {{.Name}} list --all-namespaces --output json `, SuggestFor: []string{"ls", "lsit"}, - PreRunE: bindEnv("all-namespaces", "output"), + PreRunE: bindEnv("all-namespaces", "output", "namespace"), + } + + // Config + cfg, err := config.NewDefault() + if err != nil { + fmt.Fprintf(cmd.OutOrStdout(), "error loading config at '%v'. %v\n", config.File(), err) } // Flags cmd.Flags().BoolP("all-namespaces", "A", false, "List functions in all namespaces. If set, the --namespace flag is ignored.") - cmd.Flags().StringP("namespace", "n", "", "The namespace for which to list functions. (Env: $FUNC_NAMESPACE)") + cmd.Flags().StringP("namespace", "n", cfg.Namespace, "The namespace for which to list functions. (Env: $FUNC_NAMESPACE)") cmd.Flags().StringP("output", "o", "human", "Output format (human|plain|json|xml|yaml) (Env: $FUNC_OUTPUT)") if err := cmd.RegisterFlagCompletionFunc("output", CompleteOutputFormatList); err != nil { @@ -57,13 +64,13 @@ Lists all deployed functions in a given namespace. } func runList(cmd *cobra.Command, _ []string, newClient ClientFactory) (err error) { - config := newListConfig() + cfg := newListConfig() - if err := config.Validate(); err != nil { + if err := cfg.Validate(cmd); err != nil { return err } - client, done := newClient(ClientConfig{Namespace: config.Namespace, Verbose: config.Verbose}) + client, done := newClient(ClientConfig{Namespace: cfg.Namespace, Verbose: cfg.Verbose}) defer done() items, err := client.List(cmd.Context()) @@ -72,18 +79,15 @@ func runList(cmd *cobra.Command, _ []string, newClient ClientFactory) (err error } if len(items) == 0 { - // TODO(lkingland): this isn't particularly script friendly. Suggest this - // prints bo only on --verbose. Possible future tweak, as I don't want to - // make functional changes during a refactor. - if config.Namespace != "" && !config.AllNamespaces { - fmt.Printf("no functions found in namespace '%v'\n", config.Namespace) + if cfg.Namespace != "" { + fmt.Printf("no functions found in namespace '%v'\n", cfg.Namespace) } else { fmt.Println("no functions found") } return } - write(os.Stdout, listItems(items), config.Output) + write(os.Stdout, listItems(items), cfg.Output) return } @@ -92,23 +96,28 @@ func runList(cmd *cobra.Command, _ []string, newClient ClientFactory) (err error // ------------------------------ type listConfig struct { - Namespace string - Output string - AllNamespaces bool - Verbose bool + Namespace string + Output string + Verbose bool } func newListConfig() listConfig { - return listConfig{ - Namespace: viper.GetString("namespace"), - Output: viper.GetString("output"), - AllNamespaces: viper.GetBool("all-namespaces"), - Verbose: viper.GetBool("verbose"), + c := listConfig{ + Namespace: viper.GetString("namespace"), + Output: viper.GetString("output"), + Verbose: viper.GetBool("verbose"), } + // Lister instantiated by newClient explicitly expects "" namespace to + // inidicate it should list from all namespaces, so remove default "default" + // when -A. + if viper.GetBool("all-namespaces") { + c.Namespace = "" + } + return c } -func (c listConfig) Validate() error { - if c.Namespace != "" && c.AllNamespaces { +func (c listConfig) Validate(cmd *cobra.Command) error { + if cmd.Flags().Changed("namespace") && viper.GetBool("all-namespaces") { return errors.New("Both --namespace and --all-namespaces specified.") } return nil diff --git a/cmd/list_test.go b/cmd/list_test.go new file mode 100644 index 00000000..50cf1efa --- /dev/null +++ b/cmd/list_test.go @@ -0,0 +1,75 @@ +package cmd + +import ( + "testing" + + fn "knative.dev/func" + "knative.dev/func/mock" +) + +// TestList_Namespace ensures that list command options for specifying a +// namespace (--namespace) or all namespaces (--all-namespacs) are properly +// evaluated. +func TestList_Namespace(t *testing.T) { + _ = fromTempDirectory(t) + + tests := []struct { + name string + all bool // --all-namespaces + namespace string // use specific namespace + expected string // expected + err bool // expected error + }{ + { + name: "default", + expected: "func", // see ./testdata/default_kubeconfig + }, + { + name: "namespace provided", + namespace: "ns", + expected: "ns", + }, + { + name: "all namespaces", + all: true, + expected: "", // blank is implemented by lister as meaning all + }, + { + name: "both flags error", + namespace: "ns", + all: true, + err: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var ( + lister = mock.NewLister() + client = fn.New(fn.WithLister(lister)) + ) + cmd := NewListCmd(func(cc ClientConfig, options ...fn.Option) (*fn.Client, func()) { + if cc.Namespace != test.expected { + t.Fatalf("expected '%v', got '%v'", test.expected, cc.Namespace) + } + return client, func() {} + }) + args := []string{} + if test.namespace != "" { + args = append(args, "--namespace", test.namespace) + } + if test.all { + args = append(args, "-A") + } + cmd.SetArgs(args) + + err := cmd.Execute() + if err != nil && !test.err { + // TODO: typed error for --namespace with -A. Perhaps ErrFlagConflict? + t.Fatalf("unexpected error: %v", err) + } + if err == nil && test.err { + t.Fatalf("did not receive expected error ") + } + }) + } +} diff --git a/docs/reference/func_list.md b/docs/reference/func_list.md index abd4a37a..5bfac318 100644 --- a/docs/reference/func_list.md +++ b/docs/reference/func_list.md @@ -33,7 +33,7 @@ func list --all-namespaces --output json ``` -A, --all-namespaces List functions in all namespaces. If set, the --namespace flag is ignored. -h, --help help for list - -n, --namespace string The namespace for which to list functions. (Env: $FUNC_NAMESPACE) + -n, --namespace string The namespace for which to list functions. (Env: $FUNC_NAMESPACE) (default "default") -o, --output string Output format (human|plain|json|xml|yaml) (Env: $FUNC_OUTPUT) (default "human") ```