From 5a3d385432c2f1d668428006a58903361c5a6ff9 Mon Sep 17 00:00:00 2001 From: Matej Vasek Date: Mon, 10 May 2021 20:33:34 +0200 Subject: [PATCH] add tests for delete cmd Signed-off-by: Matej Vasek --- cmd/delete.go | 120 ++++++++++++++++++++++++--------------------- cmd/delete_test.go | 119 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 56 deletions(-) create mode 100644 cmd/delete_test.go diff --git a/cmd/delete.go b/cmd/delete.go index 7d292d64f..a4db216f3 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "github.com/ory/viper" "github.com/spf13/cobra" @@ -13,15 +12,13 @@ import ( func init() { root.AddCommand(deleteCmd) - deleteCmd.Flags().BoolP("confirm", "c", false, "Prompt to confirm all configuration options (Env: $FUNC_CONFIRM)") - deleteCmd.Flags().StringP("path", "p", cwd(), "Path to the function project that should be undeployed (Env: $FUNC_PATH)") - deleteCmd.Flags().StringP("namespace", "n", "", "Namespace of the function to undeploy. By default, the namespace in func.yaml is used or the actual active namespace if not set in the configuration. (Env: $FUNC_NAMESPACE)") } -var deleteCmd = &cobra.Command{ - Use: "delete [NAME]", - Short: "Undeploy a function", - Long: `Undeploy a function +func NewDeleteCmd(newRemover func(ns string, verbose bool) (fn.Remover, error)) *cobra.Command { + delCmd := &cobra.Command{ + Use: "delete [NAME]", + Short: "Undeploy a function", + Long: `Undeploy a function This command undeploys a function from the cluster. By default the function from the project in the current directory is undeployed. Alternatively either the name @@ -29,64 +26,75 @@ of the function can be given as argument or the project path provided with --pat No local files are deleted. `, - Example: ` + Example: ` # Undeploy the function defined in the local directory kn func delete # Undeploy the function 'myfunc' in namespace 'apps' kn func delete -n apps myfunc `, - SuggestFor: []string{"remove", "rm", "del"}, - ValidArgsFunction: CompleteFunctionList, - PreRunE: bindEnv("path", "confirm", "namespace"), - RunE: runDelete, + SuggestFor: []string{"remove", "rm", "del"}, + ValidArgsFunction: CompleteFunctionList, + PreRunE: bindEnv("path", "confirm", "namespace"), + RunE: func(cmd *cobra.Command, args []string) (err error) { + config := newDeleteConfig(args).Prompt() + + var function fn.Function + + // Initialize func with explicit name (when provided) + if len(args) > 0 && args[0] != "" { + pathChanged := cmd.Flags().Changed("path") + if pathChanged { + return fmt.Errorf("Only one of --path and [NAME] should be provided") + } + function = fn.Function{ + Name: args[0], + } + } else { + function, err = fn.NewFunction(config.Path) + if err != nil { + return + } + + // Check if the Function has been initialized + if !function.Initialized() { + return fmt.Errorf("the given path '%v' does not contain an initialized function", config.Path) + } + } + + ns := config.Namespace + if ns == "" { + ns = function.Namespace + } + + remover, err := newRemover(ns, config.Verbose) + if err != nil { + return + } + + client := fn.New( + fn.WithVerbose(config.Verbose), + fn.WithRemover(remover)) + + return client.Remove(cmd.Context(), function) + }, + } + + delCmd.Flags().BoolP("confirm", "c", false, "Prompt to confirm all configuration options (Env: $FUNC_CONFIRM)") + delCmd.Flags().StringP("path", "p", cwd(), "Path to the function project that should be undeployed (Env: $FUNC_PATH)") + delCmd.Flags().StringP("namespace", "n", "", "Namespace of the function to undeploy. By default, the namespace in func.yaml is used or the actual active namespace if not set in the configuration. (Env: $FUNC_NAMESPACE)") + + return delCmd } - -func runDelete(cmd *cobra.Command, args []string) (err error) { - config := newDeleteConfig(args).Prompt() - - var function fn.Function - - // Initialize func with explicit name (when provided) - if len(args) > 0 && args[0] != "" { - pathChanged := cmd.Flags().Changed("path") - if pathChanged { - return fmt.Errorf("Only one of --path and [NAME] should be provided") - } - function = fn.Function{ - Name: args[0], - } - } else { - function, err = fn.NewFunction(config.Path) - if err != nil { - return - } - - // Check if the Function has been initialized - if !function.Initialized() { - return fmt.Errorf("the given path '%v' does not contain an initialized function", config.Path) - } - } - - ns := config.Namespace - if ns == "" { - ns = function.Namespace - } - - remover, err := knative.NewRemover(ns) +var deleteCmd = NewDeleteCmd(func(ns string, verbose bool) (fn.Remover, error) { + r, err := knative.NewRemover(ns) if err != nil { - return + return nil, err } - - remover.Verbose = config.Verbose - - client := fn.New( - fn.WithVerbose(config.Verbose), - fn.WithRemover(remover)) - - return client.Remove(cmd.Context(), function) -} + r.Verbose = verbose + return r, nil +}) type deleteConfig struct { Name string diff --git a/cmd/delete_test.go b/cmd/delete_test.go new file mode 100644 index 000000000..ca3ca40ec --- /dev/null +++ b/cmd/delete_test.go @@ -0,0 +1,119 @@ +package cmd + +import ( + "context" + fn "github.com/boson-project/func" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +type testRemover struct { + invokedWith *string +} + +func (t *testRemover) Remove(ctx context.Context, name string) error { + t.invokedWith = &name + return nil +} + +// test delete outside project just using function name +func TestDeleteCmdWithoutProject(t *testing.T) { + tr := &testRemover{} + cmd := NewDeleteCmd(func(ns string, verbose bool) (fn.Remover, error) { + return tr, nil + }) + + cmd.SetArgs([]string{"foo"}) + err := cmd.Execute() + if err != nil { + t.Fatal(err) + } + + if tr.invokedWith == nil { + t.Fatal("fn.Remover has not been invoked") + } + + if *tr.invokedWith != "foo" { + t.Fatalf("expected fn.Remover to be called with 'foo', but was called with '%s'", *tr.invokedWith) + } +} + +// test delete from inside project directory (reading func name from func.yaml) +func TestDeleteCmdWithProject(t *testing.T) { + funcYaml := `name: bar +namespace: "" +runtime: go +image: "" +imageDigest: "" +trigger: http +builder: quay.io/boson/faas-go-builder +builderMap: + default: quay.io/boson/faas-go-builder +env: {} +annotations: {} +` + tmpDir, err := ioutil.TempDir("", "bar") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + f, err := os.Create(filepath.Join(tmpDir, "func.yaml")) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + f.WriteString(funcYaml) + f.Close() + + + oldWD, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + defer os.Chdir(oldWD) + err = os.Chdir(tmpDir) + if err != nil { + t.Fatal(err) + } + + tr := &testRemover{} + cmd := NewDeleteCmd(func(ns string, verbose bool) (fn.Remover, error) { + return tr, nil + }) + + cmd.SetArgs([]string{"-p", "."}) + err = cmd.Execute() + if err != nil { + t.Fatal(err) + } + + if tr.invokedWith == nil { + t.Fatal("fn.Remover has not been invoked") + } + + if *tr.invokedWith != "bar" { + t.Fatalf("expected fn.Remover to be called with 'bar', but was called with '%s'", *tr.invokedWith) + } +} + +// test where both name and path are provided +func TestDeleteCmdWithBothPathAndName(t *testing.T) { + tr := &testRemover{} + cmd := NewDeleteCmd(func(ns string, verbose bool) (fn.Remover, error) { + return tr, nil + }) + + cmd.SetArgs([]string{"foo", "-p", "/adir/"}) + err := cmd.Execute() + if err == nil { + t.Fatal("error was expected as both name an path cannot be used together") + } + + if tr.invokedWith != nil { + t.Fatal("fn.Remove was call when it shouldn't have been") + } +}