Skip to content

Commit 37d74d0

Browse files
authored
feat: add finch vm status command (#83)
Issue #, if available: #23 *Description of changes:* Adding the `finch vm status` command. *Testing done:* `make && make lint && make test-unit` since I did not yet updated the e2e tests where I need help to understand how to test the command - [x] I've reviewed the guidance in CONTRIBUTING.md #### License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. Signed-off-by: Niklas Metje <[email protected]>
1 parent 79df04e commit 37d74d0

5 files changed

+265
-1
lines changed

Diff for: cmd/finch/virtual_machine.go

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package main
55

66
import (
77
"fmt"
8+
"os"
89
"strings"
910

1011
"github.com/runfinch/finch/pkg/disk"
@@ -43,6 +44,7 @@ func newVirtualMachineCommand(
4344
newStartVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fs, fp.LimaSSHPrivateKeyPath(), diskManager),
4445
newStopVMCommand(limaCmdCreator, logger),
4546
newRemoveVMCommand(limaCmdCreator, logger),
47+
newStatusVMCommand(limaCmdCreator, logger, os.Stdout),
4648
newInitVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fp.BaseYamlFilePath(), fs,
4749
fp.LimaSSHPrivateKeyPath(), diskManager),
4850
)

Diff for: cmd/finch/virtual_machine_status.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"fmt"
8+
"io"
9+
10+
"github.com/runfinch/finch/pkg/command"
11+
"github.com/runfinch/finch/pkg/flog"
12+
"github.com/runfinch/finch/pkg/lima"
13+
14+
"github.com/spf13/cobra"
15+
)
16+
17+
func newStatusVMCommand(limaCmdCreator command.LimaCmdCreator, logger flog.Logger, stdout io.Writer) *cobra.Command {
18+
statusVMCommand := &cobra.Command{
19+
Use: "status",
20+
Short: "Status of the virtual machine",
21+
RunE: newStatusVMAction(limaCmdCreator, logger, stdout).runAdapter,
22+
}
23+
24+
return statusVMCommand
25+
}
26+
27+
type statusVMAction struct {
28+
creator command.LimaCmdCreator
29+
logger flog.Logger
30+
stdout io.Writer
31+
}
32+
33+
func newStatusVMAction(creator command.LimaCmdCreator, logger flog.Logger, stdout io.Writer) *statusVMAction {
34+
return &statusVMAction{creator: creator, logger: logger, stdout: stdout}
35+
}
36+
37+
func (sva *statusVMAction) runAdapter(cmd *cobra.Command, args []string) error {
38+
return sva.run()
39+
}
40+
41+
func (sva *statusVMAction) run() error {
42+
status, err := lima.GetVMStatus(sva.creator, sva.logger, limaInstanceName)
43+
if err != nil {
44+
return err
45+
}
46+
switch status {
47+
case lima.Running:
48+
fmt.Fprintln(sva.stdout, "Running")
49+
return nil
50+
case lima.Nonexistent:
51+
fmt.Fprintln(sva.stdout, "Nonexistent")
52+
return nil
53+
case lima.Stopped:
54+
fmt.Fprintln(sva.stdout, "Stopped")
55+
return nil
56+
default:
57+
return fmt.Errorf("instance state of %q is unknown", limaInstanceName)
58+
}
59+
}

Diff for: cmd/finch/virtual_machine_status_test.go

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"bytes"
8+
"errors"
9+
"testing"
10+
11+
"github.com/golang/mock/gomock"
12+
"github.com/spf13/cobra"
13+
"github.com/stretchr/testify/assert"
14+
15+
"github.com/runfinch/finch/pkg/mocks"
16+
)
17+
18+
func TestNewStatusVMCommand(t *testing.T) {
19+
t.Parallel()
20+
21+
cmd := newStatusVMCommand(nil, nil, nil)
22+
assert.Equal(t, cmd.Name(), "status")
23+
}
24+
25+
func TestStatusVMAction_runAdapter(t *testing.T) {
26+
t.Parallel()
27+
28+
testCases := []struct {
29+
name string
30+
command *cobra.Command
31+
args []string
32+
mockSvc func(
33+
*mocks.LimaCmdCreator,
34+
*mocks.Logger,
35+
*mocks.LimaConfigApplier,
36+
*gomock.Controller,
37+
)
38+
}{
39+
{
40+
name: "should get nonexistent vm status",
41+
command: &cobra.Command{
42+
Use: "status",
43+
},
44+
args: []string{},
45+
mockSvc: func(
46+
lcc *mocks.LimaCmdCreator,
47+
logger *mocks.Logger,
48+
lca *mocks.LimaConfigApplier,
49+
ctrl *gomock.Controller,
50+
) {
51+
getVMStatusC := mocks.NewCommand(ctrl)
52+
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
53+
getVMStatusC.EXPECT().Output().Return([]byte(""), nil)
54+
logger.EXPECT().Debugf("Status of virtual machine: %s", "")
55+
},
56+
},
57+
}
58+
59+
for _, tc := range testCases {
60+
tc := tc
61+
t.Run(tc.name, func(t *testing.T) {
62+
t.Parallel()
63+
64+
ctrl := gomock.NewController(t)
65+
logger := mocks.NewLogger(ctrl)
66+
stdout := bytes.Buffer{}
67+
lcc := mocks.NewLimaCmdCreator(ctrl)
68+
lca := mocks.NewLimaConfigApplier(ctrl)
69+
70+
tc.mockSvc(lcc, logger, lca, ctrl)
71+
72+
assert.NoError(t, newStatusVMAction(lcc, logger, &stdout).runAdapter(tc.command, tc.args))
73+
})
74+
}
75+
}
76+
77+
func TestStatusVMAction_run(t *testing.T) {
78+
t.Parallel()
79+
80+
testCases := []struct {
81+
name string
82+
wantErr error
83+
wantStatusOutput string
84+
mockSvc func(
85+
*mocks.LimaCmdCreator,
86+
*mocks.Logger,
87+
*mocks.LimaConfigApplier,
88+
*gomock.Controller,
89+
)
90+
}{
91+
{
92+
name: "running VM",
93+
wantErr: nil,
94+
wantStatusOutput: "Running\n",
95+
mockSvc: func(
96+
lcc *mocks.LimaCmdCreator,
97+
logger *mocks.Logger,
98+
lca *mocks.LimaConfigApplier,
99+
ctrl *gomock.Controller,
100+
) {
101+
getVMStatusC := mocks.NewCommand(ctrl)
102+
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
103+
getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil)
104+
logger.EXPECT().Debugf("Status of virtual machine: %s", "Running")
105+
},
106+
},
107+
{
108+
name: "stopped VM",
109+
wantErr: nil,
110+
wantStatusOutput: "Stopped\n",
111+
mockSvc: func(
112+
lcc *mocks.LimaCmdCreator,
113+
logger *mocks.Logger,
114+
lca *mocks.LimaConfigApplier,
115+
ctrl *gomock.Controller,
116+
) {
117+
getVMStatusC := mocks.NewCommand(ctrl)
118+
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
119+
getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil)
120+
logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped")
121+
},
122+
},
123+
{
124+
name: "nonExistent VM",
125+
wantErr: nil,
126+
wantStatusOutput: "Nonexistent\n",
127+
mockSvc: func(
128+
lcc *mocks.LimaCmdCreator,
129+
logger *mocks.Logger,
130+
lca *mocks.LimaConfigApplier,
131+
ctrl *gomock.Controller,
132+
) {
133+
getVMStatusC := mocks.NewCommand(ctrl)
134+
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
135+
getVMStatusC.EXPECT().Output().Return([]byte(""), nil)
136+
logger.EXPECT().Debugf("Status of virtual machine: %s", "")
137+
},
138+
},
139+
{
140+
name: "unknown VM status",
141+
wantErr: errors.New("unrecognized system status"),
142+
wantStatusOutput: "",
143+
mockSvc: func(
144+
lcc *mocks.LimaCmdCreator,
145+
logger *mocks.Logger,
146+
lca *mocks.LimaConfigApplier,
147+
ctrl *gomock.Controller,
148+
) {
149+
getVMStatusC := mocks.NewCommand(ctrl)
150+
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
151+
getVMStatusC.EXPECT().Output().Return([]byte("Broken"), nil)
152+
logger.EXPECT().Debugf("Status of virtual machine: %s", "Broken")
153+
},
154+
},
155+
{
156+
name: "status command returns an error",
157+
wantErr: errors.New("get status error"),
158+
wantStatusOutput: "",
159+
mockSvc: func(
160+
lcc *mocks.LimaCmdCreator,
161+
logger *mocks.Logger,
162+
lca *mocks.LimaConfigApplier,
163+
ctrl *gomock.Controller,
164+
) {
165+
getVMStatusC := mocks.NewCommand(ctrl)
166+
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
167+
getVMStatusC.EXPECT().Output().Return([]byte("Broken"), errors.New("get status error"))
168+
},
169+
},
170+
}
171+
172+
for _, tc := range testCases {
173+
tc := tc
174+
t.Run(tc.name, func(t *testing.T) {
175+
t.Parallel()
176+
177+
ctrl := gomock.NewController(t)
178+
logger := mocks.NewLogger(ctrl)
179+
stdout := bytes.Buffer{}
180+
lcc := mocks.NewLimaCmdCreator(ctrl)
181+
lca := mocks.NewLimaConfigApplier(ctrl)
182+
183+
tc.mockSvc(lcc, logger, lca, ctrl)
184+
185+
err := newStatusVMAction(lcc, logger, &stdout).run()
186+
assert.Equal(t, err, tc.wantErr)
187+
assert.Equal(t, tc.wantStatusOutput, stdout.String())
188+
})
189+
}
190+
}

Diff for: cmd/finch/virtual_machine_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestVirtualMachineCommand(t *testing.T) {
2121
assert.Equal(t, cmd.Use, virtualMachineRootCmd)
2222

2323
// check the number of subcommand for vm
24-
assert.Equal(t, len(cmd.Commands()), 4)
24+
assert.Equal(t, len(cmd.Commands()), 5)
2525
}
2626

2727
func TestPostVMStartInitAction_runAdapter(t *testing.T) {

Diff for: e2e/vm/lifecycle_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package vm
55

66
import (
77
"github.com/onsi/ginkgo/v2"
8+
"github.com/onsi/gomega"
89
"github.com/runfinch/common-tests/command"
910
"github.com/runfinch/common-tests/option"
1011
)
@@ -18,6 +19,10 @@ var testVMLifecycle = func(o *option.Option) {
1819
command.New(o, virtualMachineRootCmd, "remove").WithoutSuccessfulExit().Run()
1920
})
2021

22+
ginkgo.It("should get running status", func() {
23+
gomega.Expect(command.StdoutStr(o, virtualMachineRootCmd, "status")).To(gomega.Equal("Running"))
24+
})
25+
2126
ginkgo.It("should be able to stop the virtual machine", func() {
2227
command.Run(o, "images")
2328
command.New(o, virtualMachineRootCmd, "stop").WithTimeoutInSeconds(60).Run()
@@ -31,6 +36,10 @@ var testVMLifecycle = func(o *option.Option) {
3136
command.New(o, virtualMachineRootCmd, "init").WithoutSuccessfulExit().Run()
3237
})
3338

39+
ginkgo.It("should get stopped status", func() {
40+
gomega.Expect(command.StdoutStr(o, virtualMachineRootCmd, "status")).To(gomega.Equal("Stopped"))
41+
})
42+
3443
ginkgo.It("should be able to start the virtual machine", func() {
3544
command.New(o, virtualMachineRootCmd, "start").WithTimeoutInSeconds(120).Run()
3645
command.Run(o, "images")
@@ -49,6 +58,10 @@ var testVMLifecycle = func(o *option.Option) {
4958
command.New(o, virtualMachineRootCmd, "stop").WithoutSuccessfulExit().Run()
5059
})
5160

61+
ginkgo.It("should get nonexistent status", func() {
62+
gomega.Expect(command.StdoutStr(o, virtualMachineRootCmd, "status")).To(gomega.Equal("Nonexistent"))
63+
})
64+
5265
ginkgo.It("should be able to init", func() {
5366
command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(600).Run()
5467
command.Run(o, "images")

0 commit comments

Comments
 (0)