Skip to content

Commit e8c01ba

Browse files
committed
implement delete command
1 parent 5ea93f7 commit e8c01ba

File tree

11 files changed

+303
-59
lines changed

11 files changed

+303
-59
lines changed

internal/cmd/alpha/alpha.go

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func NewAlphaCMD() (*cobra.Command, clierror.Error) {
4141
// list of template commands deffinitions
4242
Explain: templates.BuildExplainCommand,
4343
Create: templates.BuildCreateCommand,
44+
Delete: templates.BuildDeleteCommand,
4445
}, cmdcommon.CoreCommandsMap{
4546
// map of available core commands
4647
"registry_config": config.NewConfigCMD,

internal/cmd/alpha/templates/create.go

+4-52
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"io"
77
"os"
8-
"strings"
98

109
"github.com/kyma-project/cli.v3/internal/clierror"
1110
"github.com/kyma-project/cli.v3/internal/cmd/alpha/templates/parameters"
@@ -46,7 +45,7 @@ func buildCreateCommand(out io.Writer, clientGetter KubeClientGetter, createOpti
4645
},
4746
}
4847

49-
flags := append(createOptions.CustomFlags, buildDefaultFlags(createOptions.ResourceInfo.Scope)...)
48+
flags := append(createOptions.CustomFlags, commonResourceFlags(createOptions.ResourceInfo.Scope)...)
5049
for _, flag := range flags {
5150
value := parameters.NewTyped(flag.Type, flag.Path, flag.DefaultValue)
5251
cmd.Flags().VarP(value, flag.Name, flag.Shorthand, flag.Description)
@@ -80,25 +79,9 @@ func createResource(args *createArgs) clierror.Error {
8079
return clierr
8180
}
8281

83-
for _, extraValue := range args.extraValues {
84-
value := extraValue.GetValue()
85-
if value == nil {
86-
// value is not set and has no default value
87-
continue
88-
}
89-
90-
fields := strings.Split(
91-
// remove optional dot at the beginning of the path
92-
strings.TrimPrefix(extraValue.GetPath(), "."),
93-
".",
94-
)
95-
96-
err := unstructured.SetNestedField(u.Object, value, fields...)
97-
if err != nil {
98-
return clierror.Wrap(err, clierror.New(
99-
fmt.Sprintf("failed to set value %v for path %s", value, extraValue.GetPath()),
100-
))
101-
}
82+
clierr = setExtraValues(u, args.extraValues)
83+
if clierr != nil {
84+
return clierr
10285
}
10386

10487
err := client.RootlessDynamic().Apply(args.ctx, u)
@@ -109,34 +92,3 @@ func createResource(args *createArgs) clierror.Error {
10992
fmt.Fprintf(args.out, "resource %s applied\n", getResourceName(args.createOptions.ResourceInfo.Scope, u))
11093
return nil
11194
}
112-
113-
func buildDefaultFlags(resourceScope types.Scope) []types.CreateCustomFlag {
114-
params := []types.CreateCustomFlag{
115-
{
116-
Name: "name",
117-
Type: types.StringCustomFlagType,
118-
Description: "name of the resource",
119-
Path: ".metadata.name",
120-
Required: true,
121-
},
122-
}
123-
if resourceScope == types.NamespaceScope {
124-
params = append(params, types.CreateCustomFlag{
125-
Name: "namespace",
126-
Type: types.StringCustomFlagType,
127-
Description: "resource namespace",
128-
Path: ".metadata.namespace",
129-
DefaultValue: "default",
130-
})
131-
}
132-
133-
return params
134-
}
135-
136-
func getResourceName(scope types.Scope, u *unstructured.Unstructured) string {
137-
if scope == types.NamespaceScope {
138-
return fmt.Sprintf("%s/%s", u.GetNamespace(), u.GetName())
139-
}
140-
141-
return u.GetName()
142-
}

internal/cmd/alpha/templates/create_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func fixCreateOptions() *CreateOptions {
117117
CreateCommand: types.CreateCommand{
118118
Description: "create test deploy",
119119
DescriptionLong: "use this to create test deploy",
120-
CustomFlags: []types.CreateCustomFlag{
120+
CustomFlags: []types.CustomFlag{
121121
{
122122
Type: types.IntCustomFlagType,
123123
Name: "replicas",
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package templates
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"os"
8+
9+
"github.com/kyma-project/cli.v3/internal/clierror"
10+
"github.com/kyma-project/cli.v3/internal/cmd/alpha/templates/parameters"
11+
"github.com/kyma-project/cli.v3/internal/cmd/alpha/templates/types"
12+
"github.com/spf13/cobra"
13+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
"k8s.io/apimachinery/pkg/runtime/schema"
15+
)
16+
17+
type DeleteOptions struct {
18+
types.DeleteCommand
19+
ResourceInfo types.ResourceInfo
20+
}
21+
22+
func BuildDeleteCommand(clientGetter KubeClientGetter, options *DeleteOptions) *cobra.Command {
23+
return buildDeleteCommand(os.Stdout, clientGetter, options)
24+
}
25+
26+
func buildDeleteCommand(out io.Writer, clientGetter KubeClientGetter, options *DeleteOptions) *cobra.Command {
27+
extraValues := []parameters.Value{}
28+
cmd := &cobra.Command{
29+
Use: "delete",
30+
Short: options.Description,
31+
Long: options.DescriptionLong,
32+
Run: func(cmd *cobra.Command, args []string) {
33+
clierror.Check(deleteResource(&deleteArgs{
34+
out: out,
35+
ctx: cmd.Context(),
36+
deleteOptions: options,
37+
clientGetter: clientGetter,
38+
extraValues: extraValues,
39+
}))
40+
},
41+
}
42+
43+
for _, flag := range commonResourceFlags(options.ResourceInfo.Scope) {
44+
value := parameters.NewTyped(flag.Type, flag.Path, flag.DefaultValue)
45+
cmd.Flags().VarP(value, flag.Name, flag.Shorthand, flag.Description)
46+
if flag.Required {
47+
_ = cmd.MarkFlagRequired(flag.Name)
48+
}
49+
extraValues = append(extraValues, value)
50+
}
51+
52+
return cmd
53+
}
54+
55+
type deleteArgs struct {
56+
out io.Writer
57+
ctx context.Context
58+
deleteOptions *DeleteOptions
59+
clientGetter KubeClientGetter
60+
extraValues []parameters.Value
61+
}
62+
63+
func deleteResource(args *deleteArgs) clierror.Error {
64+
u := &unstructured.Unstructured{}
65+
u.SetGroupVersionKind(schema.GroupVersionKind{
66+
Group: args.deleteOptions.ResourceInfo.Group,
67+
Version: args.deleteOptions.ResourceInfo.Version,
68+
Kind: args.deleteOptions.ResourceInfo.Kind,
69+
})
70+
71+
client, clierr := args.clientGetter.GetKubeClientWithClierr()
72+
if clierr != nil {
73+
return clierr
74+
}
75+
76+
clierr = setExtraValues(u, args.extraValues)
77+
if clierr != nil {
78+
return clierr
79+
}
80+
81+
err := client.RootlessDynamic().Remove(args.ctx, u)
82+
if err != nil {
83+
return clierror.Wrap(err, clierror.New("failed to delete resource"))
84+
}
85+
86+
fmt.Fprintf(args.out, "resource %s deleted\n", getResourceName(args.deleteOptions.ResourceInfo.Scope, u))
87+
88+
return nil
89+
}
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package templates
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"errors"
7+
"io"
8+
"testing"
9+
10+
"github.com/kyma-project/cli.v3/internal/clierror"
11+
"github.com/kyma-project/cli.v3/internal/cmd/alpha/templates/types"
12+
"github.com/kyma-project/cli.v3/internal/kube/fake"
13+
"github.com/spf13/cobra"
14+
"github.com/stretchr/testify/require"
15+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
16+
)
17+
18+
func Test_remove(t *testing.T) {
19+
t.Run("build proper command", func(t *testing.T) {
20+
cmd := fixDeleteCommand(bytes.NewBuffer([]byte{}), &mockGetter{})
21+
22+
require.Equal(t, "delete", cmd.Use)
23+
require.Equal(t, "delete test deploy", cmd.Short)
24+
require.Equal(t, "use this to delete test deploy", cmd.Long)
25+
26+
require.NotNil(t, cmd.Flag("name"))
27+
require.NotNil(t, cmd.Flag("namespace"))
28+
})
29+
30+
t.Run("delete resource", func(t *testing.T) {
31+
buf := bytes.NewBuffer([]byte{})
32+
fakeClient := &fake.RootlessDynamicClient{}
33+
mock := mockGetter{
34+
client: &fake.KubeClient{
35+
TestRootlessDynamicInterface: fakeClient,
36+
},
37+
}
38+
39+
cmd := fixDeleteCommand(buf, &mock)
40+
41+
cmd.SetArgs([]string{"--name", "test-deploy", "--namespace", "test-namespace"})
42+
43+
err := cmd.Execute()
44+
require.NoError(t, err)
45+
46+
require.Len(t, fakeClient.RemovedObjs, 1)
47+
require.Equal(t, fixDeletedUnstructuredDeployment(), fakeClient.RemovedObjs[0])
48+
})
49+
50+
t.Run("failed to get client", func(t *testing.T) {
51+
buf := bytes.NewBuffer([]byte{})
52+
mock := mockGetter{
53+
clierror: clierror.New("test error"),
54+
client: nil,
55+
}
56+
57+
err := deleteResource(&deleteArgs{
58+
out: buf,
59+
ctx: context.Background(),
60+
clientGetter: &mock,
61+
deleteOptions: fixDeleteOptions(),
62+
})
63+
require.Equal(t, clierror.New("test error"), err)
64+
})
65+
66+
t.Run("failed to delete object", func(t *testing.T) {
67+
buf := bytes.NewBuffer([]byte{})
68+
fakeClient := &fake.RootlessDynamicClient{
69+
ReturnRemoveErr: errors.New("test error"),
70+
}
71+
mock := mockGetter{
72+
client: &fake.KubeClient{
73+
TestRootlessDynamicInterface: fakeClient,
74+
},
75+
}
76+
77+
err := deleteResource(&deleteArgs{
78+
out: buf,
79+
ctx: context.Background(),
80+
clientGetter: &mock,
81+
deleteOptions: fixDeleteOptions(),
82+
})
83+
require.Equal(t, clierror.Wrap(errors.New("test error"), clierror.New("failed to delete resource")), err)
84+
})
85+
}
86+
87+
func fixDeleteCommand(writer io.Writer, getter KubeClientGetter) *cobra.Command {
88+
return buildDeleteCommand(writer, getter, fixDeleteOptions())
89+
}
90+
91+
func fixDeleteOptions() *DeleteOptions {
92+
return &DeleteOptions{
93+
DeleteCommand: types.DeleteCommand{
94+
Description: "delete test deploy",
95+
DescriptionLong: "use this to delete test deploy",
96+
},
97+
ResourceInfo: types.ResourceInfo{
98+
Scope: types.NamespaceScope,
99+
Kind: "Deployment",
100+
Group: "apps",
101+
Version: "v1",
102+
},
103+
}
104+
}
105+
106+
func fixDeletedUnstructuredDeployment() unstructured.Unstructured {
107+
return unstructured.Unstructured{
108+
Object: map[string]interface{}{
109+
"apiVersion": "apps/v1",
110+
"kind": "Deployment",
111+
"metadata": map[string]interface{}{
112+
"name": "test-deploy",
113+
"namespace": "test-namespace",
114+
},
115+
},
116+
}
117+
}
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package templates
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/kyma-project/cli.v3/internal/clierror"
8+
"github.com/kyma-project/cli.v3/internal/cmd/alpha/templates/parameters"
9+
"github.com/kyma-project/cli.v3/internal/cmd/alpha/templates/types"
10+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
11+
)
12+
13+
func setExtraValues(u *unstructured.Unstructured, extraValues []parameters.Value) clierror.Error {
14+
for _, extraValue := range extraValues {
15+
value := extraValue.GetValue()
16+
if value == nil {
17+
// value is not set and has no default value
18+
continue
19+
}
20+
21+
fields := strings.Split(
22+
// remove optional dot at the beginning of the path
23+
strings.TrimPrefix(extraValue.GetPath(), "."),
24+
".",
25+
)
26+
27+
err := unstructured.SetNestedField(u.Object, value, fields...)
28+
if err != nil {
29+
return clierror.Wrap(err, clierror.New(
30+
fmt.Sprintf("failed to set value %v for path %s", value, extraValue.GetPath()),
31+
))
32+
}
33+
}
34+
35+
return nil
36+
}
37+
38+
func commonResourceFlags(resourceScope types.Scope) []types.CustomFlag {
39+
params := []types.CustomFlag{
40+
{
41+
Name: "name",
42+
Type: types.StringCustomFlagType,
43+
Description: "name of the resource",
44+
Path: ".metadata.name",
45+
Required: true,
46+
},
47+
}
48+
if resourceScope == types.NamespaceScope {
49+
params = append(params, types.CustomFlag{
50+
Name: "namespace",
51+
Type: types.StringCustomFlagType,
52+
Description: "resource namespace",
53+
Path: ".metadata.namespace",
54+
DefaultValue: "default",
55+
})
56+
}
57+
58+
return params
59+
}
60+
61+
func getResourceName(scope types.Scope, u *unstructured.Unstructured) string {
62+
if scope == types.NamespaceScope {
63+
return fmt.Sprintf("%s/%s", u.GetNamespace(), u.GetName())
64+
}
65+
66+
return u.GetName()
67+
}

internal/cmd/alpha/templates/types/create.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type CreateCommand struct {
66
// long description of the command group
77
DescriptionLong string `yaml:"descriptionLong"`
88
// custom flags used to build command and set values for specific fields
9-
CustomFlags []CreateCustomFlag `yaml:"customFlags"`
9+
CustomFlags []CustomFlag `yaml:"customFlags"`
1010
}
1111

1212
type CreateCustomFlagType string
@@ -17,7 +17,7 @@ var (
1717
IntCustomFlagType CreateCustomFlagType = "int64"
1818
)
1919

20-
type CreateCustomFlag struct {
20+
type CustomFlag struct {
2121
// type of the custom flag
2222
Type CreateCustomFlagType `yaml:"type"`
2323
// name of the custom flag

0 commit comments

Comments
 (0)