Skip to content

Commit 676a7e3

Browse files
committed
rm: Add --config flag
Adds --config flag in rm command to accept VM removal using the same config file that was used to create the VM. VM name or UID is required when passing a config file. Adds tests to verify the changes.
1 parent edfdea7 commit 676a7e3

File tree

7 files changed

+213
-9
lines changed

7 files changed

+213
-9
lines changed

cmd/ignite/cmd/vmcmd/rm.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ func NewCmdRm(out io.Writer) *cobra.Command {
2323
separated by spaces. The force flag (-f, --force) kills running
2424
VMs before removal instead of throwing an error.
2525
`),
26-
Args: cobra.MinimumNArgs(1),
2726
Run: func(cmd *cobra.Command, args []string) {
2827
cmdutil.CheckErr(func() error {
2928
ro, err := rf.NewRmOptions(args)
@@ -42,4 +41,5 @@ func NewCmdRm(out io.Writer) *cobra.Command {
4241

4342
func addRmFlags(fs *pflag.FlagSet, rf *run.RmFlags) {
4443
cmdutil.AddForceFlag(fs, &rf.Force)
44+
cmdutil.AddConfigFlag(fs, &rf.ConfigFile)
4545
}

cmd/ignite/run/rm.go

+34-4
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,55 @@ import (
44
"fmt"
55

66
api "github.com/weaveworks/ignite/pkg/apis/ignite"
7+
"github.com/weaveworks/ignite/pkg/apis/ignite/scheme"
78
"github.com/weaveworks/ignite/pkg/operations"
89
"github.com/weaveworks/ignite/pkg/providers"
910
)
1011

12+
// RmFlags contains the flags supported by the remove command.
1113
type RmFlags struct {
12-
Force bool
14+
Force bool
15+
ConfigFile string
1316
}
1417

1518
type rmOptions struct {
1619
*RmFlags
1720
vms []*api.VM
1821
}
1922

20-
func (rf *RmFlags) NewRmOptions(vmMatches []string) (ro *rmOptions, err error) {
21-
ro = &rmOptions{RmFlags: rf}
23+
// NewRmOptions creates and returns rmOptions with all the flags and VMs to be
24+
// removed.
25+
func (rf *RmFlags) NewRmOptions(vmMatches []string) (*rmOptions, error) {
26+
ro := &rmOptions{RmFlags: rf}
27+
28+
// If config file is provided, use it to find the VM to be removed.
29+
if len(rf.ConfigFile) != 0 {
30+
if len(vmMatches) > 0 {
31+
return ro, fmt.Errorf("cannot use both config flag and vm argument")
32+
}
33+
34+
vm := &api.VM{}
35+
if err := scheme.Serializer.DecodeFileInto(rf.ConfigFile, vm); err != nil {
36+
return ro, err
37+
}
38+
// Name or UID must be provided in the config file.
39+
if len(vm.Name) == 0 && len(vm.UID) == 0 {
40+
return ro, fmt.Errorf("API resource config must have Name or UID")
41+
}
42+
ro.vms = []*api.VM{vm}
43+
return ro, nil
44+
}
45+
46+
// Use vm args to find the VMs to be removed.
47+
if len(vmMatches) < 1 {
48+
return ro, fmt.Errorf("need at least one vm identifier as argument")
49+
}
50+
var err error
2251
ro.vms, err = getVMsForMatches(vmMatches)
23-
return
52+
return ro, err
2453
}
2554

55+
// Rm removes VMs based on rmOptions.
2656
func Rm(ro *rmOptions) error {
2757
for _, vm := range ro.vms {
2858
// If the VM is running, but we haven't enabled force-mode, return an error

cmd/ignite/run/rm_test.go

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package run
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/weaveworks/gitops-toolkit/pkg/runtime"
10+
"github.com/weaveworks/gitops-toolkit/pkg/storage"
11+
"github.com/weaveworks/gitops-toolkit/pkg/storage/cache"
12+
13+
api "github.com/weaveworks/ignite/pkg/apis/ignite"
14+
"github.com/weaveworks/ignite/pkg/apis/ignite/scheme"
15+
meta "github.com/weaveworks/ignite/pkg/apis/meta/v1alpha1"
16+
"github.com/weaveworks/ignite/pkg/client"
17+
"github.com/weaveworks/ignite/pkg/providers"
18+
"github.com/weaveworks/ignite/pkg/util"
19+
)
20+
21+
func TestNewRmOptions(t *testing.T) {
22+
testdataDir := "testdata"
23+
24+
cases := []struct {
25+
name string
26+
existingVMs []string
27+
rmFlags *RmFlags
28+
vmMatches []string // argument of NewRmOptions()
29+
wantMatches []string
30+
err bool
31+
}{
32+
{
33+
name: "rm with vm arg",
34+
existingVMs: []string{"myvm1", "myvm2", "myvm3"},
35+
rmFlags: &RmFlags{},
36+
vmMatches: []string{"myvm2"},
37+
wantMatches: []string{"myvm2"},
38+
},
39+
{
40+
name: "rm with multiple vm args",
41+
existingVMs: []string{"myvm1", "myvm2", "myvm3"},
42+
rmFlags: &RmFlags{},
43+
vmMatches: []string{"myvm2", "myvm3"},
44+
wantMatches: []string{"myvm2", "myvm3"},
45+
},
46+
{
47+
name: "error rm non-existing vm",
48+
existingVMs: []string{"myvm1", "myvm2", "myvm3"},
49+
rmFlags: &RmFlags{},
50+
vmMatches: []string{"myvm4"},
51+
err: true,
52+
},
53+
{
54+
name: "error rm without any args or config flag",
55+
existingVMs: []string{"myvm1", "myvm2", "myvm3"},
56+
rmFlags: &RmFlags{},
57+
err: true,
58+
},
59+
{
60+
name: "error rm with vm arg and config flag",
61+
existingVMs: []string{"myvm1"},
62+
rmFlags: &RmFlags{ConfigFile: "foo.yaml"},
63+
vmMatches: []string{"myvm1"},
64+
err: true,
65+
},
66+
{
67+
name: "rm with config file",
68+
existingVMs: []string{"myvm1", "myvm2", "myvm3"},
69+
rmFlags: &RmFlags{ConfigFile: filepath.Join(testdataDir, "input/rm-vm1.yaml")},
70+
wantMatches: []string{"myvm2"},
71+
},
72+
{
73+
name: "error rm config name and uid missing",
74+
existingVMs: []string{"myvm1"},
75+
rmFlags: &RmFlags{ConfigFile: filepath.Join(testdataDir, "input/rm-no-name-uid.yaml")},
76+
err: true,
77+
},
78+
}
79+
80+
for _, rt := range cases {
81+
t.Run(rt.name, func(t *testing.T) {
82+
// Create storage.
83+
dir, err := ioutil.TempDir("", "ignite")
84+
if err != nil {
85+
t.Fatalf("failed to create storage for ignite: %v", err)
86+
}
87+
defer os.RemoveAll(dir)
88+
89+
storage := cache.NewCache(
90+
storage.NewGenericStorage(
91+
storage.NewGenericRawStorage(dir), scheme.Serializer))
92+
93+
// Create ignite client with the created storage.
94+
ic := client.NewClient(storage)
95+
96+
// Create the existing VMs.
97+
for _, objectName := range rt.existingVMs {
98+
vm := &api.VM{}
99+
vm.SetName(objectName)
100+
101+
// Set UID.
102+
uid, err := util.NewUID()
103+
if err != nil {
104+
t.Errorf("failed to generate new UID: %v", err)
105+
}
106+
vm.SetUID(runtime.UID(uid))
107+
108+
// Set VM image.
109+
ociRef, err := meta.NewOCIImageRef("foo/bar:latest")
110+
if err != nil {
111+
t.Errorf("failed to create new image reference: %v", err)
112+
}
113+
img := &api.Image{
114+
Spec: api.ImageSpec{
115+
OCI: ociRef,
116+
},
117+
}
118+
vm.SetImage(img)
119+
120+
// Set Kernel image.
121+
ociRefKernel, err := meta.NewOCIImageRef("foo/bar:latest")
122+
if err != nil {
123+
t.Errorf("failed to create new image reference: %v", err)
124+
}
125+
kernel := &api.Kernel{
126+
Spec: api.KernelSpec{
127+
OCI: ociRefKernel,
128+
},
129+
}
130+
vm.SetKernel(kernel)
131+
132+
// Save object.
133+
if err := ic.VMs().Set(vm); err != nil {
134+
t.Errorf("failed to store VM object: %v", err)
135+
}
136+
}
137+
138+
// Set provider client used in remove to find VM matches.
139+
providers.Client = ic
140+
141+
// Create new rm options using the rmFlags and vmMatches.
142+
ro, err := rt.rmFlags.NewRmOptions(rt.vmMatches)
143+
if (err != nil) != rt.err {
144+
t.Fatalf("expected error %t, actual: %v", rt.err, err)
145+
}
146+
147+
// Check if the wanted VMs are in the matched VMs list.
148+
for _, wantVM := range rt.wantMatches {
149+
found := false
150+
for _, vm := range ro.vms {
151+
if vm.Name == wantVM {
152+
found = true
153+
break
154+
}
155+
}
156+
if !found {
157+
t.Errorf("expected vm %q to be in remove vm list", wantVM)
158+
}
159+
}
160+
})
161+
}
162+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: ignite.weave.works/v1alpha2
2+
kind: VM
3+
metadata:
4+
name: ""
5+
uid: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: ignite.weave.works/v1alpha2
2+
kind: VM
3+
metadata:
4+
name: myvm2
5+
uid: 599615df99804ae8

docs/cli/ignite/ignite_rm.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ ignite rm <vm>... [flags]
1818
### Options
1919

2020
```
21-
-f, --force Force this operation. Warning, use of this mode may have unintended consequences.
22-
-h, --help help for rm
21+
--config string Specify a path to a file with the API resources you want to pass
22+
-f, --force Force this operation. Warning, use of this mode may have unintended consequences.
23+
-h, --help help for rm
2324
```
2425

2526
### Options inherited from parent commands

docs/cli/ignite/ignite_vm_rm.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ ignite vm rm <vm>... [flags]
1818
### Options
1919

2020
```
21-
-f, --force Force this operation. Warning, use of this mode may have unintended consequences.
22-
-h, --help help for rm
21+
--config string Specify a path to a file with the API resources you want to pass
22+
-f, --force Force this operation. Warning, use of this mode may have unintended consequences.
23+
-h, --help help for rm
2324
```
2425

2526
### Options inherited from parent commands

0 commit comments

Comments
 (0)