From eade74fc7f1649a70b95080a006cfa7181481023 Mon Sep 17 00:00:00 2001 From: Nacho Fuertes Date: Fri, 23 May 2025 09:59:17 +0200 Subject: [PATCH 1/3] Fixes #4714. Added output option to namespace list command to specify json or yaml Signed-off-by: Nacho Fuertes --- cmd/namespace/list.go | 101 ++++++++++++++-- cmd/namespace/list_test.go | 228 +++++++++++++++++++++++++++---------- 2 files changed, 256 insertions(+), 73 deletions(-) diff --git a/cmd/namespace/list.go b/cmd/namespace/list.go index 5735d697932d..132e0dfb29c0 100644 --- a/cmd/namespace/list.go +++ b/cmd/namespace/list.go @@ -15,6 +15,8 @@ package namespace import ( "context" + "encoding/json" + "errors" "fmt" "os" "text/tabwriter" @@ -23,12 +25,29 @@ import ( "github.com/okteto/okteto/cmd/utils" oktetoErrors "github.com/okteto/okteto/pkg/errors" "github.com/okteto/okteto/pkg/okteto" + "github.com/okteto/okteto/pkg/types" "github.com/spf13/cobra" + "gopkg.in/yaml.v2" ) +var ( + errInvalidOutput = errors.New("output format is not accepted. Value must be one of: ['json', 'yaml']") +) + +type listFlags struct { + output string +} + +type namespaceOutput struct { + Namespace string `json:"namespace" yaml:"namespace"` + Status string `json:"status" yaml:"status"` + Current bool `json:"current" yaml:"current"` +} + // List all namespace in current context func List(ctx context.Context) *cobra.Command { - return &cobra.Command{ + flags := &listFlags{} + cmd := &cobra.Command{ Use: "list", Short: "List your Okteto Namespaces", Aliases: []string{"ls"}, @@ -46,27 +65,87 @@ func List(ctx context.Context) *cobra.Command { if err != nil { return err } - err = nsCmd.executeListNamespaces(ctx) + err = nsCmd.executeListNamespaces(ctx, flags.output) return err }, Args: utils.NoArgsAccepted(""), } + + cmd.Flags().StringVarP(&flags.output, "output", "o", "", "output format. One of: ['json', 'yaml']") + return cmd } -func (nc *Command) executeListNamespaces(ctx context.Context) error { +func (nc *Command) executeListNamespaces(ctx context.Context, output string) error { + if err := validateNamespaceListOutput(output); err != nil { + return err + } + spaces, err := nc.okClient.Namespaces().List(ctx) if err != nil { return fmt.Errorf("failed to get namespaces: %w", err) } - w := tabwriter.NewWriter(os.Stdout, 1, 1, 2, ' ', 0) - fmt.Fprintf(w, "Namespace\tStatus\n") - for _, space := range spaces { - if space.ID == okteto.GetContext().Namespace { - space.ID += " *" + + namespaces := getNamespaceOutput(spaces) + return displayListNamespaces(namespaces, output) +} + +func displayListNamespaces(namespaces []namespaceOutput, output string) error { + switch output { + case "json": + if len(namespaces) == 0 { + fmt.Println("[]") + return nil + } + b, err := json.MarshalIndent(namespaces, "", " ") + if err != nil { + return err } - fmt.Fprintf(w, "%s\t%v\n", space.ID, space.Status) + fmt.Println(string(b)) + case "yaml": + b, err := yaml.Marshal(namespaces) + if err != nil { + return err + } + fmt.Print(string(b)) + default: + if len(namespaces) == 0 { + fmt.Println("There are no namespaces") + return nil + } + w := tabwriter.NewWriter(os.Stdout, 1, 1, 2, ' ', 0) + fmt.Fprintf(w, "Namespace\tStatus\n") + for _, space := range namespaces { + id := space.Namespace + if id == okteto.GetContext().Namespace { + id += " *" + } + fmt.Fprintf(w, "%s\t%v\n", id, space.Status) + } + w.Flush() } - - w.Flush() return nil } + +func validateNamespaceListOutput(output string) error { + switch output { + case "", "json", "yaml": + return nil + default: + return errInvalidOutput + } +} + +// getNamespaceOutput transforms type.Namespace into namespaceOutput type +func getNamespaceOutput(namespaces []types.Namespace) []namespaceOutput { + var namespaceSlice []namespaceOutput + currentNamespace := okteto.GetContext().Namespace + for _, ns := range namespaces { + previewOutput := namespaceOutput{ + Namespace: ns.ID, + Status: ns.Status, + Current: ns.ID == currentNamespace, + } + namespaceSlice = append(namespaceSlice, previewOutput) + } + return namespaceSlice +} diff --git a/cmd/namespace/list_test.go b/cmd/namespace/list_test.go index d6de6472e4b7..bff6b12774a8 100644 --- a/cmd/namespace/list_test.go +++ b/cmd/namespace/list_test.go @@ -14,72 +14,176 @@ package namespace import ( - "context" - "fmt" - "testing" + "context" + "fmt" + "io" + "os" + "testing" - "github.com/okteto/okteto/internal/test/client" - "github.com/okteto/okteto/pkg/okteto" - "github.com/okteto/okteto/pkg/types" - "github.com/stretchr/testify/assert" + "github.com/okteto/okteto/internal/test/client" + "github.com/okteto/okteto/pkg/okteto" + "github.com/okteto/okteto/pkg/types" + "github.com/stretchr/testify/assert" ) func Test_listNamespace(t *testing.T) { - ctx := context.Background() - var tests = []struct { - err error - name string - currentNamespaces []types.Namespace - }{ - { - name: "List all ns", - currentNamespaces: []types.Namespace{ - { - ID: "test", - }, - { - ID: "test-1", - }, - }, - }, - { - name: "error retrieving ns", - currentNamespaces: nil, - err: fmt.Errorf("error retrieving ns"), - }, - } + ctx := context.Background() + var tests = []struct { + err error + name string + currentNamespaces []types.Namespace + }{ + { + name: "List all ns", + currentNamespaces: []types.Namespace{ + { + ID: "test", + }, + { + ID: "test-1", + }, + }, + }, + { + name: "error retrieving ns", + currentNamespaces: nil, + err: fmt.Errorf("error retrieving ns"), + }, + } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - okteto.CurrentStore = &okteto.ContextStore{ - Contexts: map[string]*okteto.Context{ - "test": { - Name: "test", - Token: "test", - IsOkteto: true, - UserID: "1", - }, - }, - CurrentContext: "test", - } - usr := &types.User{ - Token: "test", - } - fakeOktetoClient := &client.FakeOktetoClient{ - Namespace: client.NewFakeNamespaceClient(tt.currentNamespaces, tt.err), - Users: client.NewFakeUsersClient(usr), - } - nsCmd := &Command{ - okClient: fakeOktetoClient, - ctxCmd: newFakeContextCommand(fakeOktetoClient, usr), - } - err := nsCmd.executeListNamespaces(ctx) - if tt.err != nil { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + okteto.CurrentStore = &okteto.ContextStore{ + Contexts: map[string]*okteto.Context{ + "test": { + Name: "test", + Token: "test", + IsOkteto: true, + UserID: "1", + }, + }, + CurrentContext: "test", + } + usr := &types.User{ + Token: "test", + } + fakeOktetoClient := &client.FakeOktetoClient{ + Namespace: client.NewFakeNamespaceClient(tt.currentNamespaces, tt.err), + Users: client.NewFakeUsersClient(usr), + } + nsCmd := &Command{ + okClient: fakeOktetoClient, + ctxCmd: newFakeContextCommand(fakeOktetoClient, usr), + } + err := nsCmd.executeListNamespaces(ctx, "") + if tt.err != nil { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } - }) - } + }) + } +} + +func Test_validateNamespaceListOutput(t *testing.T) { + tests := []struct { + name string + output string + expectedErr error + }{ + { + name: "yaml output", + output: "yaml", + }, + { + name: "json output", + output: "json", + }, + { + name: "invalid output", + output: "xml", + expectedErr: errInvalidOutput, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateNamespaceListOutput(tt.output) + assert.Equal(t, tt.expectedErr, err) + }) + } +} + +func Test_displayListNamespaces(t *testing.T) { + tests := []struct { + name string + format string + input []namespaceOutput + expectedOutput string + }{ + { + name: "empty default", + format: "", + expectedOutput: "There are no namespaces\n", + }, + { + name: "empty json", + format: "json", + expectedOutput: "[]\n", + }, + { + name: "default format", + format: "", + input: []namespaceOutput{ + {Namespace: "test", Status: "Active", Current: true}, + {Namespace: "test2", Status: "Sleeping", Current: false}, + }, + expectedOutput: "Namespace Status\ntest * Active\ntest2 Sleeping\n", + }, + { + name: "json format", + format: "json", + input: []namespaceOutput{ + {Namespace: "test", Status: "Active", Current: true}, + {Namespace: "test2", Status: "Sleeping", Current: false}, + }, + expectedOutput: "[\n {\n \"namespace\": \"test\",\n \"status\": \"Active\",\n \"current\": true\n },\n {\n \"namespace\": \"test2\",\n \"status\": \"Sleeping\",\n \"current\": false\n }\n]\n", + }, + { + name: "yaml format", + format: "yaml", + input: []namespaceOutput{ + {Namespace: "test", Status: "Active", Current: true}, + {Namespace: "test2", Status: "Sleeping", Current: false}, + }, + expectedOutput: "- namespace: test\n status: Active\n current: true\n- namespace: test2\n status: Sleeping\n current: false\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + okteto.CurrentStore = &okteto.ContextStore{ + Contexts: map[string]*okteto.Context{ + "test": { + Namespace: "test", + IsOkteto: true, + }, + }, + CurrentContext: "test", + } + r, w, _ := os.Pipe() + initialStdout := os.Stdout + os.Stdout = w + + err := displayListNamespaces(tt.input, tt.format) + assert.NoError(t, err) + + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = initialStdout + + assert.Equal(t, tt.expectedOutput, string(out)) + }) + } } From 6307d5cc83458532564714ad5f0acbcca11a6300 Mon Sep 17 00:00:00 2001 From: Nacho Fuertes Date: Fri, 23 May 2025 11:13:52 +0200 Subject: [PATCH 2/3] Fixes #4714. Fixed indentation in test file Signed-off-by: Nacho Fuertes --- cmd/namespace/list_test.go | 310 ++++++++++++++++++------------------- 1 file changed, 155 insertions(+), 155 deletions(-) diff --git a/cmd/namespace/list_test.go b/cmd/namespace/list_test.go index bff6b12774a8..412cca7f7b32 100644 --- a/cmd/namespace/list_test.go +++ b/cmd/namespace/list_test.go @@ -14,176 +14,176 @@ package namespace import ( - "context" - "fmt" - "io" - "os" - "testing" + "context" + "fmt" + "io" + "os" + "testing" - "github.com/okteto/okteto/internal/test/client" - "github.com/okteto/okteto/pkg/okteto" - "github.com/okteto/okteto/pkg/types" - "github.com/stretchr/testify/assert" + "github.com/okteto/okteto/internal/test/client" + "github.com/okteto/okteto/pkg/okteto" + "github.com/okteto/okteto/pkg/types" + "github.com/stretchr/testify/assert" ) func Test_listNamespace(t *testing.T) { - ctx := context.Background() - var tests = []struct { - err error - name string - currentNamespaces []types.Namespace - }{ - { - name: "List all ns", - currentNamespaces: []types.Namespace{ - { - ID: "test", - }, - { - ID: "test-1", - }, - }, - }, - { - name: "error retrieving ns", - currentNamespaces: nil, - err: fmt.Errorf("error retrieving ns"), - }, - } + ctx := context.Background() + var tests = []struct { + err error + name string + currentNamespaces []types.Namespace + }{ + { + name: "List all ns", + currentNamespaces: []types.Namespace{ + { + ID: "test", + }, + { + ID: "test-1", + }, + }, + }, + { + name: "error retrieving ns", + currentNamespaces: nil, + err: fmt.Errorf("error retrieving ns"), + }, + } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - okteto.CurrentStore = &okteto.ContextStore{ - Contexts: map[string]*okteto.Context{ - "test": { - Name: "test", - Token: "test", - IsOkteto: true, - UserID: "1", - }, - }, - CurrentContext: "test", - } - usr := &types.User{ - Token: "test", - } - fakeOktetoClient := &client.FakeOktetoClient{ - Namespace: client.NewFakeNamespaceClient(tt.currentNamespaces, tt.err), - Users: client.NewFakeUsersClient(usr), - } - nsCmd := &Command{ - okClient: fakeOktetoClient, - ctxCmd: newFakeContextCommand(fakeOktetoClient, usr), - } - err := nsCmd.executeListNamespaces(ctx, "") - if tt.err != nil { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + okteto.CurrentStore = &okteto.ContextStore{ + Contexts: map[string]*okteto.Context{ + "test": { + Name: "test", + Token: "test", + IsOkteto: true, + UserID: "1", + }, + }, + CurrentContext: "test", + } + usr := &types.User{ + Token: "test", + } + fakeOktetoClient := &client.FakeOktetoClient{ + Namespace: client.NewFakeNamespaceClient(tt.currentNamespaces, tt.err), + Users: client.NewFakeUsersClient(usr), + } + nsCmd := &Command{ + okClient: fakeOktetoClient, + ctxCmd: newFakeContextCommand(fakeOktetoClient, usr), + } + err := nsCmd.executeListNamespaces(ctx, "") + if tt.err != nil { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } - }) - } + }) + } } func Test_validateNamespaceListOutput(t *testing.T) { - tests := []struct { - name string - output string - expectedErr error - }{ - { - name: "yaml output", - output: "yaml", - }, - { - name: "json output", - output: "json", - }, - { - name: "invalid output", - output: "xml", - expectedErr: errInvalidOutput, - }, - } + tests := []struct { + name string + output string + expectedErr error + }{ + { + name: "yaml output", + output: "yaml", + }, + { + name: "json output", + output: "json", + }, + { + name: "invalid output", + output: "xml", + expectedErr: errInvalidOutput, + }, + } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := validateNamespaceListOutput(tt.output) - assert.Equal(t, tt.expectedErr, err) - }) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateNamespaceListOutput(tt.output) + assert.Equal(t, tt.expectedErr, err) + }) + } } func Test_displayListNamespaces(t *testing.T) { - tests := []struct { - name string - format string - input []namespaceOutput - expectedOutput string - }{ - { - name: "empty default", - format: "", - expectedOutput: "There are no namespaces\n", - }, - { - name: "empty json", - format: "json", - expectedOutput: "[]\n", - }, - { - name: "default format", - format: "", - input: []namespaceOutput{ - {Namespace: "test", Status: "Active", Current: true}, - {Namespace: "test2", Status: "Sleeping", Current: false}, - }, - expectedOutput: "Namespace Status\ntest * Active\ntest2 Sleeping\n", - }, - { - name: "json format", - format: "json", - input: []namespaceOutput{ - {Namespace: "test", Status: "Active", Current: true}, - {Namespace: "test2", Status: "Sleeping", Current: false}, - }, - expectedOutput: "[\n {\n \"namespace\": \"test\",\n \"status\": \"Active\",\n \"current\": true\n },\n {\n \"namespace\": \"test2\",\n \"status\": \"Sleeping\",\n \"current\": false\n }\n]\n", - }, - { - name: "yaml format", - format: "yaml", - input: []namespaceOutput{ - {Namespace: "test", Status: "Active", Current: true}, - {Namespace: "test2", Status: "Sleeping", Current: false}, - }, - expectedOutput: "- namespace: test\n status: Active\n current: true\n- namespace: test2\n status: Sleeping\n current: false\n", - }, - } + tests := []struct { + name string + format string + input []namespaceOutput + expectedOutput string + }{ + { + name: "empty default", + format: "", + expectedOutput: "There are no namespaces\n", + }, + { + name: "empty json", + format: "json", + expectedOutput: "[]\n", + }, + { + name: "default format", + format: "", + input: []namespaceOutput{ + {Namespace: "test", Status: "Active", Current: true}, + {Namespace: "test2", Status: "Sleeping", Current: false}, + }, + expectedOutput: "Namespace Status\ntest * Active\ntest2 Sleeping\n", + }, + { + name: "json format", + format: "json", + input: []namespaceOutput{ + {Namespace: "test", Status: "Active", Current: true}, + {Namespace: "test2", Status: "Sleeping", Current: false}, + }, + expectedOutput: "[\n {\n \"namespace\": \"test\",\n \"status\": \"Active\",\n \"current\": true\n },\n {\n \"namespace\": \"test2\",\n \"status\": \"Sleeping\",\n \"current\": false\n }\n]\n", + }, + { + name: "yaml format", + format: "yaml", + input: []namespaceOutput{ + {Namespace: "test", Status: "Active", Current: true}, + {Namespace: "test2", Status: "Sleeping", Current: false}, + }, + expectedOutput: "- namespace: test\n status: Active\n current: true\n- namespace: test2\n status: Sleeping\n current: false\n", + }, + } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - okteto.CurrentStore = &okteto.ContextStore{ - Contexts: map[string]*okteto.Context{ - "test": { - Namespace: "test", - IsOkteto: true, - }, - }, - CurrentContext: "test", - } - r, w, _ := os.Pipe() - initialStdout := os.Stdout - os.Stdout = w + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + okteto.CurrentStore = &okteto.ContextStore{ + Contexts: map[string]*okteto.Context{ + "test": { + Namespace: "test", + IsOkteto: true, + }, + }, + CurrentContext: "test", + } + r, w, _ := os.Pipe() + initialStdout := os.Stdout + os.Stdout = w - err := displayListNamespaces(tt.input, tt.format) - assert.NoError(t, err) + err := displayListNamespaces(tt.input, tt.format) + assert.NoError(t, err) - w.Close() - out, _ := io.ReadAll(r) - os.Stdout = initialStdout + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = initialStdout - assert.Equal(t, tt.expectedOutput, string(out)) - }) - } + assert.Equal(t, tt.expectedOutput, string(out)) + }) + } } From 60016a1050b386870dc42a0c53eb7a6131970505 Mon Sep 17 00:00:00 2001 From: Nacho Fuertes Date: Mon, 26 May 2025 16:27:18 +0200 Subject: [PATCH 3/3] Fixes #4714. Refactor to use io.Controller to show the output Signed-off-by: Nacho Fuertes --- cmd/build/command.go | 6 +++--- cmd/deploy/deploy.go | 2 +- cmd/namespace/create.go | 5 +++-- cmd/namespace/delete.go | 4 ++-- cmd/namespace/list.go | 17 +++++++++-------- cmd/namespace/list_test.go | 6 +++++- cmd/namespace/namespace.go | 23 +++++++++++++---------- cmd/namespace/sleep.go | 5 +++-- cmd/namespace/use.go | 5 +++-- cmd/namespace/wake.go | 5 +++-- cmd/test/cmd.go | 2 +- cmd/up/up.go | 2 +- main.go | 2 +- 13 files changed, 48 insertions(+), 36 deletions(-) diff --git a/cmd/build/command.go b/cmd/build/command.go index 6ea188453279..d1c9ec969207 100644 --- a/cmd/build/command.go +++ b/cmd/build/command.go @@ -106,7 +106,7 @@ func Build(ctx context.Context, ioCtrl *io.Controller, at, insights buildTracker // The context must be loaded before reading manifest. Otherwise, // secrets will not be resolved when GetManifest is called and // the manifest will load empty values. - oktetoContext, err := getOktetoContext(ctx, options) + oktetoContext, err := getOktetoContext(ctx, options, ioCtrl) if err != nil { return err } @@ -224,7 +224,7 @@ func validateDockerfile(file string) error { return err } -func getOktetoContext(ctx context.Context, options *types.BuildOptions) (*okteto.ContextStateless, error) { +func getOktetoContext(ctx context.Context, options *types.BuildOptions, ioCtrl *io.Controller) (*okteto.ContextStateless, error) { ctxOpts := &contextCMD.Options{ Context: options.K8sContext, Namespace: options.Namespace, @@ -248,7 +248,7 @@ func getOktetoContext(ctx context.Context, options *types.BuildOptions) (*okteto return nil, err } if create { - if err := namespace.NewCommandStateless(c).Create(ctx, &namespace.CreateOptions{Namespace: ctxOpts.Namespace}); err != nil { + if err := namespace.NewCommandStateless(c, ioCtrl).Create(ctx, &namespace.CreateOptions{Namespace: ctxOpts.Namespace}); err != nil { return nil, err } } diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index e9fa449ad263..2c17fd263c6b 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -219,7 +219,7 @@ $ okteto deploy --no-build=true`, return err } if create { - nsCmd, err := namespace.NewCommand() + nsCmd, err := namespace.NewCommand(ioCtrl) if err != nil { return err } diff --git a/cmd/namespace/create.go b/cmd/namespace/create.go index 08cb6b52ba0e..7b02d3c32490 100644 --- a/cmd/namespace/create.go +++ b/cmd/namespace/create.go @@ -23,6 +23,7 @@ import ( "github.com/okteto/okteto/pkg/analytics" oktetoErrors "github.com/okteto/okteto/pkg/errors" oktetoLog "github.com/okteto/okteto/pkg/log" + "github.com/okteto/okteto/pkg/log/io" "github.com/okteto/okteto/pkg/okteto" "github.com/spf13/cobra" ) @@ -36,7 +37,7 @@ type CreateOptions struct { } // Create creates a namespace -func Create(ctx context.Context) *cobra.Command { +func Create(ctx context.Context, ioCtrl *io.Controller) *cobra.Command { options := &CreateOptions{ Show: false, } @@ -53,7 +54,7 @@ func Create(ctx context.Context) *cobra.Command { return oktetoErrors.ErrContextIsNotOktetoCluster } - nsCmd, err := NewCommand() + nsCmd, err := NewCommand(ioCtrl) if err != nil { return err } diff --git a/cmd/namespace/delete.go b/cmd/namespace/delete.go index d5fc7771aa26..6d1f2620fb31 100644 --- a/cmd/namespace/delete.go +++ b/cmd/namespace/delete.go @@ -35,7 +35,7 @@ import ( ) // Delete deletes a namespace -func Delete(ctx context.Context, k8sLogger *io.K8sLogger) *cobra.Command { +func Delete(ctx context.Context, k8sLogger *io.K8sLogger, ioCtrl *io.Controller) *cobra.Command { cmd := &cobra.Command{ Use: "delete ", Short: "Delete an Okteto Namespace", @@ -54,7 +54,7 @@ func Delete(ctx context.Context, k8sLogger *io.K8sLogger) *cobra.Command { return oktetoErrors.ErrContextIsNotOktetoCluster } - nsCmd, err := NewCommand() + nsCmd, err := NewCommand(ioCtrl) if err != nil { return err } diff --git a/cmd/namespace/list.go b/cmd/namespace/list.go index 132e0dfb29c0..3001b5a5a6f8 100644 --- a/cmd/namespace/list.go +++ b/cmd/namespace/list.go @@ -24,6 +24,7 @@ import ( contextCMD "github.com/okteto/okteto/cmd/context" "github.com/okteto/okteto/cmd/utils" oktetoErrors "github.com/okteto/okteto/pkg/errors" + "github.com/okteto/okteto/pkg/log/io" "github.com/okteto/okteto/pkg/okteto" "github.com/okteto/okteto/pkg/types" "github.com/spf13/cobra" @@ -45,7 +46,7 @@ type namespaceOutput struct { } // List all namespace in current context -func List(ctx context.Context) *cobra.Command { +func List(ctx context.Context, ioCtrl *io.Controller) *cobra.Command { flags := &listFlags{} cmd := &cobra.Command{ Use: "list", @@ -61,7 +62,7 @@ func List(ctx context.Context) *cobra.Command { return oktetoErrors.ErrContextIsNotOktetoCluster } - nsCmd, err := NewCommand() + nsCmd, err := NewCommand(ioCtrl) if err != nil { return err } @@ -86,30 +87,30 @@ func (nc *Command) executeListNamespaces(ctx context.Context, output string) err } namespaces := getNamespaceOutput(spaces) - return displayListNamespaces(namespaces, output) + return nc.displayListNamespaces(namespaces, output) } -func displayListNamespaces(namespaces []namespaceOutput, output string) error { +func (nc *Command) displayListNamespaces(namespaces []namespaceOutput, output string) error { switch output { case "json": if len(namespaces) == 0 { - fmt.Println("[]") + nc.ioCtrl.Out().Println("[]") return nil } b, err := json.MarshalIndent(namespaces, "", " ") if err != nil { return err } - fmt.Println(string(b)) + nc.ioCtrl.Out().Println(string(b)) case "yaml": b, err := yaml.Marshal(namespaces) if err != nil { return err } - fmt.Print(string(b)) + nc.ioCtrl.Out().Print(string(b)) default: if len(namespaces) == 0 { - fmt.Println("There are no namespaces") + nc.ioCtrl.Out().Println("There are no namespaces") return nil } w := tabwriter.NewWriter(os.Stdout, 1, 1, 2, ' ', 0) diff --git a/cmd/namespace/list_test.go b/cmd/namespace/list_test.go index 412cca7f7b32..3665db5b17bd 100644 --- a/cmd/namespace/list_test.go +++ b/cmd/namespace/list_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/okteto/okteto/internal/test/client" + oktetoio "github.com/okteto/okteto/pkg/log/io" "github.com/okteto/okteto/pkg/okteto" "github.com/okteto/okteto/pkg/types" "github.com/stretchr/testify/assert" @@ -175,8 +176,11 @@ func Test_displayListNamespaces(t *testing.T) { r, w, _ := os.Pipe() initialStdout := os.Stdout os.Stdout = w + nsCmd := &Command{ + ioCtrl: oktetoio.NewIOController(), + } - err := displayListNamespaces(tt.input, tt.format) + err := nsCmd.displayListNamespaces(tt.input, tt.format) assert.NoError(t, err) w.Close() diff --git a/cmd/namespace/namespace.go b/cmd/namespace/namespace.go index 9cf508b95a34..5e18eeecef6a 100644 --- a/cmd/namespace/namespace.go +++ b/cmd/namespace/namespace.go @@ -29,10 +29,11 @@ type Command struct { ctxCmd *contextCMD.Command okClient types.OktetoInterface k8sClientProvider okteto.K8sClientProviderWithLogger + ioCtrl *io.Controller } // NewCommand creates a namespace command for use in further operations -func NewCommand() (*Command, error) { +func NewCommand(ioCtrl *io.Controller) (*Command, error) { c, err := okteto.NewOktetoClient() if err != nil { return nil, err @@ -42,35 +43,37 @@ func NewCommand() (*Command, error) { ctxCmd: contextCMD.NewContextCommand(), okClient: c, k8sClientProvider: okteto.NewK8sClientProviderWithLogger(nil), + ioCtrl: ioCtrl, }, nil } // NewCommandStateless creates a namespace command for use in further operations -func NewCommandStateless(c *okteto.Client) *Command { +func NewCommandStateless(c *okteto.Client, ioCtrl *io.Controller) *Command { return &Command{ ctxCmd: contextCMD.NewContextCommand(), okClient: c, k8sClientProvider: okteto.NewK8sClientProviderWithLogger(nil), + ioCtrl: ioCtrl, } } // Namespace fetch credentials for a cluster namespace -func Namespace(ctx context.Context, k8sLogger *io.K8sLogger) *cobra.Command { +func Namespace(ctx context.Context, k8sLogger *io.K8sLogger, ioCtrl *io.Controller) *cobra.Command { options := &UseOptions{} cmd := &cobra.Command{ Use: "namespace", Short: "Configure the default namespace of the Okteto Context", Aliases: []string{"ns"}, Args: utils.MaximumNArgsAccepted(1, "https://okteto.com/docs/reference/okteto-cli/#namespace"), - RunE: Use(ctx).RunE, + RunE: Use(ctx, ioCtrl).RunE, } cmd.Flags().BoolVarP(&options.personal, "personal", "", false, "Load personal namespace") - cmd.AddCommand(Use(ctx)) - cmd.AddCommand(List(ctx)) - cmd.AddCommand(Create(ctx)) - cmd.AddCommand(Delete(ctx, k8sLogger)) - cmd.AddCommand(Sleep(ctx)) - cmd.AddCommand(Wake(ctx)) + cmd.AddCommand(Use(ctx, ioCtrl)) + cmd.AddCommand(List(ctx, ioCtrl)) + cmd.AddCommand(Create(ctx, ioCtrl)) + cmd.AddCommand(Delete(ctx, k8sLogger, ioCtrl)) + cmd.AddCommand(Sleep(ctx, ioCtrl)) + cmd.AddCommand(Wake(ctx, ioCtrl)) return cmd } diff --git a/cmd/namespace/sleep.go b/cmd/namespace/sleep.go index 91369a27ad0b..783fb9276e98 100644 --- a/cmd/namespace/sleep.go +++ b/cmd/namespace/sleep.go @@ -21,12 +21,13 @@ import ( "github.com/okteto/okteto/cmd/utils" oktetoErrors "github.com/okteto/okteto/pkg/errors" oktetoLog "github.com/okteto/okteto/pkg/log" + "github.com/okteto/okteto/pkg/log/io" "github.com/okteto/okteto/pkg/okteto" "github.com/spf13/cobra" ) // Sleep sleeps a namespace -func Sleep(ctx context.Context) *cobra.Command { +func Sleep(ctx context.Context, ioCtrl *io.Controller) *cobra.Command { cmd := &cobra.Command{ Use: "sleep ", Short: "Sleeps a namespace", @@ -45,7 +46,7 @@ func Sleep(ctx context.Context) *cobra.Command { return oktetoErrors.ErrContextIsNotOktetoCluster } - nsCmd, err := NewCommand() + nsCmd, err := NewCommand(ioCtrl) if err != nil { return err } diff --git a/cmd/namespace/use.go b/cmd/namespace/use.go index ff91db1ef59c..5f48b4d9cc9c 100644 --- a/cmd/namespace/use.go +++ b/cmd/namespace/use.go @@ -22,6 +22,7 @@ import ( "github.com/okteto/okteto/pkg/analytics" oktetoErrors "github.com/okteto/okteto/pkg/errors" oktetoLog "github.com/okteto/okteto/pkg/log" + "github.com/okteto/okteto/pkg/log/io" "github.com/okteto/okteto/pkg/okteto" "github.com/spf13/cobra" ) @@ -36,7 +37,7 @@ type UseOptions struct { } // Use sets the namespace of current context -func Use(ctx context.Context) *cobra.Command { +func Use(ctx context.Context, ioCtrl *io.Controller) *cobra.Command { options := &UseOptions{} cmd := &cobra.Command{ Use: "use [namespace]", @@ -57,7 +58,7 @@ func Use(ctx context.Context) *cobra.Command { namespace = okteto.GetContext().PersonalNamespace } - nsCmd, err := NewCommand() + nsCmd, err := NewCommand(ioCtrl) if err != nil { return err } diff --git a/cmd/namespace/wake.go b/cmd/namespace/wake.go index 63d3e1d159d1..5f0f715d82c7 100644 --- a/cmd/namespace/wake.go +++ b/cmd/namespace/wake.go @@ -21,11 +21,12 @@ import ( "github.com/okteto/okteto/cmd/utils" oktetoErrors "github.com/okteto/okteto/pkg/errors" oktetoLog "github.com/okteto/okteto/pkg/log" + "github.com/okteto/okteto/pkg/log/io" "github.com/okteto/okteto/pkg/okteto" "github.com/spf13/cobra" ) -func Wake(ctx context.Context) *cobra.Command { +func Wake(ctx context.Context, ioCtrl *io.Controller) *cobra.Command { cmd := &cobra.Command{ Use: "wake ", Short: "Wakes an Okteto Namespace. By default, it wakes the default namespace in the Okteto Context", @@ -43,7 +44,7 @@ func Wake(ctx context.Context) *cobra.Command { return oktetoErrors.ErrContextIsNotOktetoCluster } - nsCmd, err := NewCommand() + nsCmd, err := NewCommand(ioCtrl) if err != nil { return err } diff --git a/cmd/test/cmd.go b/cmd/test/cmd.go index 74359dd03ce2..545135afc131 100644 --- a/cmd/test/cmd.go +++ b/cmd/test/cmd.go @@ -154,7 +154,7 @@ func doRun(ctx context.Context, servicesToTest []string, options *Options, ioCtr return analytics.TestMetadata{}, err } if create { - nsCmd, err := namespace.NewCommand() + nsCmd, err := namespace.NewCommand(ioCtrl) if err != nil { return analytics.TestMetadata{}, err } diff --git a/cmd/up/up.go b/cmd/up/up.go index 6c823fc03b7a..72919d41c553 100644 --- a/cmd/up/up.go +++ b/cmd/up/up.go @@ -184,7 +184,7 @@ okteto up api -- echo this is a test return err } if create { - nsCmd, err := namespace.NewCommand() + nsCmd, err := namespace.NewCommand(ioCtrl) if err != nil { return err } diff --git a/main.go b/main.go index dfeaccfb1e85..d440826954da 100644 --- a/main.go +++ b/main.go @@ -158,7 +158,7 @@ func main() { root.AddCommand(build.Build(ctx, ioController, at, insights, k8sLogger)) - root.AddCommand(namespace.Namespace(ctx, k8sLogger)) + root.AddCommand(namespace.Namespace(ctx, k8sLogger, ioController)) root.AddCommand(up.Up(at, insights, ioController, k8sLogger, fs)) root.AddCommand(cmd.Down(at, k8sLogger, fs)) root.AddCommand(cmd.Status(fs))