Skip to content

Commit

Permalink
feat: add finch vm status command (runfinch#83)
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
niklasmtj authored Jan 17, 2023
1 parent 79df04e commit 37d74d0
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 1 deletion.
2 changes: 2 additions & 0 deletions cmd/finch/virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package main

import (
"fmt"
"os"
"strings"

"github.com/runfinch/finch/pkg/disk"
Expand Down Expand Up @@ -43,6 +44,7 @@ func newVirtualMachineCommand(
newStartVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fs, fp.LimaSSHPrivateKeyPath(), diskManager),
newStopVMCommand(limaCmdCreator, logger),
newRemoveVMCommand(limaCmdCreator, logger),
newStatusVMCommand(limaCmdCreator, logger, os.Stdout),
newInitVMCommand(limaCmdCreator, logger, optionalDepGroups, lca, nca, fp.BaseYamlFilePath(), fs,
fp.LimaSSHPrivateKeyPath(), diskManager),
)
Expand Down
59 changes: 59 additions & 0 deletions cmd/finch/virtual_machine_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package main

import (
"fmt"
"io"

"github.com/runfinch/finch/pkg/command"
"github.com/runfinch/finch/pkg/flog"
"github.com/runfinch/finch/pkg/lima"

"github.com/spf13/cobra"
)

func newStatusVMCommand(limaCmdCreator command.LimaCmdCreator, logger flog.Logger, stdout io.Writer) *cobra.Command {
statusVMCommand := &cobra.Command{
Use: "status",
Short: "Status of the virtual machine",
RunE: newStatusVMAction(limaCmdCreator, logger, stdout).runAdapter,
}

return statusVMCommand
}

type statusVMAction struct {
creator command.LimaCmdCreator
logger flog.Logger
stdout io.Writer
}

func newStatusVMAction(creator command.LimaCmdCreator, logger flog.Logger, stdout io.Writer) *statusVMAction {
return &statusVMAction{creator: creator, logger: logger, stdout: stdout}
}

func (sva *statusVMAction) runAdapter(cmd *cobra.Command, args []string) error {
return sva.run()
}

func (sva *statusVMAction) run() error {
status, err := lima.GetVMStatus(sva.creator, sva.logger, limaInstanceName)
if err != nil {
return err
}
switch status {
case lima.Running:
fmt.Fprintln(sva.stdout, "Running")
return nil
case lima.Nonexistent:
fmt.Fprintln(sva.stdout, "Nonexistent")
return nil
case lima.Stopped:
fmt.Fprintln(sva.stdout, "Stopped")
return nil
default:
return fmt.Errorf("instance state of %q is unknown", limaInstanceName)
}
}
190 changes: 190 additions & 0 deletions cmd/finch/virtual_machine_status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package main

import (
"bytes"
"errors"
"testing"

"github.com/golang/mock/gomock"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"

"github.com/runfinch/finch/pkg/mocks"
)

func TestNewStatusVMCommand(t *testing.T) {
t.Parallel()

cmd := newStatusVMCommand(nil, nil, nil)
assert.Equal(t, cmd.Name(), "status")
}

func TestStatusVMAction_runAdapter(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
command *cobra.Command
args []string
mockSvc func(
*mocks.LimaCmdCreator,
*mocks.Logger,
*mocks.LimaConfigApplier,
*gomock.Controller,
)
}{
{
name: "should get nonexistent vm status",
command: &cobra.Command{
Use: "status",
},
args: []string{},
mockSvc: func(
lcc *mocks.LimaCmdCreator,
logger *mocks.Logger,
lca *mocks.LimaConfigApplier,
ctrl *gomock.Controller,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte(""), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "")
},
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

ctrl := gomock.NewController(t)
logger := mocks.NewLogger(ctrl)
stdout := bytes.Buffer{}
lcc := mocks.NewLimaCmdCreator(ctrl)
lca := mocks.NewLimaConfigApplier(ctrl)

tc.mockSvc(lcc, logger, lca, ctrl)

assert.NoError(t, newStatusVMAction(lcc, logger, &stdout).runAdapter(tc.command, tc.args))
})
}
}

func TestStatusVMAction_run(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
wantErr error
wantStatusOutput string
mockSvc func(
*mocks.LimaCmdCreator,
*mocks.Logger,
*mocks.LimaConfigApplier,
*gomock.Controller,
)
}{
{
name: "running VM",
wantErr: nil,
wantStatusOutput: "Running\n",
mockSvc: func(
lcc *mocks.LimaCmdCreator,
logger *mocks.Logger,
lca *mocks.LimaConfigApplier,
ctrl *gomock.Controller,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "Running")
},
},
{
name: "stopped VM",
wantErr: nil,
wantStatusOutput: "Stopped\n",
mockSvc: func(
lcc *mocks.LimaCmdCreator,
logger *mocks.Logger,
lca *mocks.LimaConfigApplier,
ctrl *gomock.Controller,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Stopped"), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "Stopped")
},
},
{
name: "nonExistent VM",
wantErr: nil,
wantStatusOutput: "Nonexistent\n",
mockSvc: func(
lcc *mocks.LimaCmdCreator,
logger *mocks.Logger,
lca *mocks.LimaConfigApplier,
ctrl *gomock.Controller,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte(""), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "")
},
},
{
name: "unknown VM status",
wantErr: errors.New("unrecognized system status"),
wantStatusOutput: "",
mockSvc: func(
lcc *mocks.LimaCmdCreator,
logger *mocks.Logger,
lca *mocks.LimaConfigApplier,
ctrl *gomock.Controller,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Broken"), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "Broken")
},
},
{
name: "status command returns an error",
wantErr: errors.New("get status error"),
wantStatusOutput: "",
mockSvc: func(
lcc *mocks.LimaCmdCreator,
logger *mocks.Logger,
lca *mocks.LimaConfigApplier,
ctrl *gomock.Controller,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Broken"), errors.New("get status error"))
},
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

ctrl := gomock.NewController(t)
logger := mocks.NewLogger(ctrl)
stdout := bytes.Buffer{}
lcc := mocks.NewLimaCmdCreator(ctrl)
lca := mocks.NewLimaConfigApplier(ctrl)

tc.mockSvc(lcc, logger, lca, ctrl)

err := newStatusVMAction(lcc, logger, &stdout).run()
assert.Equal(t, err, tc.wantErr)
assert.Equal(t, tc.wantStatusOutput, stdout.String())
})
}
}
2 changes: 1 addition & 1 deletion cmd/finch/virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestVirtualMachineCommand(t *testing.T) {
assert.Equal(t, cmd.Use, virtualMachineRootCmd)

// check the number of subcommand for vm
assert.Equal(t, len(cmd.Commands()), 4)
assert.Equal(t, len(cmd.Commands()), 5)
}

func TestPostVMStartInitAction_runAdapter(t *testing.T) {
Expand Down
13 changes: 13 additions & 0 deletions e2e/vm/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package vm

import (
"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"github.com/runfinch/common-tests/command"
"github.com/runfinch/common-tests/option"
)
Expand All @@ -18,6 +19,10 @@ var testVMLifecycle = func(o *option.Option) {
command.New(o, virtualMachineRootCmd, "remove").WithoutSuccessfulExit().Run()
})

ginkgo.It("should get running status", func() {
gomega.Expect(command.StdoutStr(o, virtualMachineRootCmd, "status")).To(gomega.Equal("Running"))
})

ginkgo.It("should be able to stop the virtual machine", func() {
command.Run(o, "images")
command.New(o, virtualMachineRootCmd, "stop").WithTimeoutInSeconds(60).Run()
Expand All @@ -31,6 +36,10 @@ var testVMLifecycle = func(o *option.Option) {
command.New(o, virtualMachineRootCmd, "init").WithoutSuccessfulExit().Run()
})

ginkgo.It("should get stopped status", func() {
gomega.Expect(command.StdoutStr(o, virtualMachineRootCmd, "status")).To(gomega.Equal("Stopped"))
})

ginkgo.It("should be able to start the virtual machine", func() {
command.New(o, virtualMachineRootCmd, "start").WithTimeoutInSeconds(120).Run()
command.Run(o, "images")
Expand All @@ -49,6 +58,10 @@ var testVMLifecycle = func(o *option.Option) {
command.New(o, virtualMachineRootCmd, "stop").WithoutSuccessfulExit().Run()
})

ginkgo.It("should get nonexistent status", func() {
gomega.Expect(command.StdoutStr(o, virtualMachineRootCmd, "status")).To(gomega.Equal("Nonexistent"))
})

ginkgo.It("should be able to init", func() {
command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(600).Run()
command.Run(o, "images")
Expand Down

0 comments on commit 37d74d0

Please sign in to comment.