Skip to content

Commit

Permalink
add --wait flag to instance power commands (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
0x4c6565 committed Jun 30, 2021
1 parent 3934dc9 commit 2cca756
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 36 deletions.
54 changes: 48 additions & 6 deletions cmd/ecloud/ecloud_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ func ecloudInstanceUnlock(service ecloud.ECloudService, cmd *cobra.Command, args
}

func ecloudInstanceStartCmd(f factory.ClientFactory) *cobra.Command {
return &cobra.Command{
cmd := &cobra.Command{
Use: "start <instance: id>...",
Short: "Starts an instance",
Long: "This command powers on one or more instances",
Expand All @@ -370,13 +370,27 @@ func ecloudInstanceStartCmd(f factory.ClientFactory) *cobra.Command {
},
RunE: ecloudCobraRunEFunc(f, ecloudInstanceStart),
}

cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the instance power on task has been completed")

return cmd
}

func ecloudInstanceStart(service ecloud.ECloudService, cmd *cobra.Command, args []string) error {
for _, arg := range args {
err := service.PowerOnInstance(arg)
taskID, err := service.PowerOnInstance(arg)
if err != nil {
output.OutputWithErrorLevelf("Error starting instance [%s]: %s", arg, err)
continue
}

waitFlag, _ := cmd.Flags().GetBool("wait")
if waitFlag {
err := helper.WaitForCommand(TaskStatusWaitFunc(service, taskID, ecloud.TaskStatusComplete))
if err != nil {
output.OutputWithErrorLevelf("Error waiting for task to complete for instance [%s]: %s", arg, err)
continue
}
}
}
return nil
Expand All @@ -399,6 +413,7 @@ func ecloudInstanceStopCmd(f factory.ClientFactory) *cobra.Command {
}

cmd.Flags().Bool("force", false, "Specifies that instance should be forcefully powered off")
cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the instance power off task has been completed")

return cmd
}
Expand All @@ -407,15 +422,28 @@ func ecloudInstanceStop(service ecloud.ECloudService, cmd *cobra.Command, args [
force, _ := cmd.Flags().GetBool("force")

for _, arg := range args {
var taskID string
var err error
if force {
err := service.PowerOffInstance(arg)
taskID, err = service.PowerOffInstance(arg)
if err != nil {
output.OutputWithErrorLevelf("Error stopping instance [%s] (forced): %s", arg, err)
continue
}
} else {
err := service.PowerShutdownInstance(arg)
taskID, err = service.PowerShutdownInstance(arg)
if err != nil {
output.OutputWithErrorLevelf("Error stopping instance [%s]: %s", arg, err)
continue
}
}

waitFlag, _ := cmd.Flags().GetBool("wait")
if waitFlag {
err := helper.WaitForCommand(TaskStatusWaitFunc(service, taskID, ecloud.TaskStatusComplete))
if err != nil {
output.OutputWithErrorLevelf("Error waiting for task to complete for instance [%s]: %s", arg, err)
continue
}
}
}
Expand All @@ -439,6 +467,7 @@ func ecloudInstanceRestartCmd(f factory.ClientFactory) *cobra.Command {
}

cmd.Flags().Bool("force", false, "Specifies that instance should be forcefully reset")
cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the instance restart task has been completed")

return cmd
}
Expand All @@ -447,15 +476,28 @@ func ecloudInstanceRestart(service ecloud.ECloudService, cmd *cobra.Command, arg
force, _ := cmd.Flags().GetBool("force")

for _, arg := range args {
var taskID string
var err error
if force {
err := service.PowerResetInstance(arg)
taskID, err = service.PowerResetInstance(arg)
if err != nil {
output.OutputWithErrorLevelf("Error restarting instance [%s] (forced): %s", arg, err)
continue
}
} else {
err := service.PowerRestartInstance(arg)
taskID, err = service.PowerRestartInstance(arg)
if err != nil {
output.OutputWithErrorLevelf("Error restarting instance [%s]: %s", arg, err)
continue
}
}

waitFlag, _ := cmd.Flags().GetBool("wait")
if waitFlag {
err := helper.WaitForCommand(TaskStatusWaitFunc(service, taskID, ecloud.TaskStatusComplete))
if err != nil {
output.OutputWithErrorLevelf("Error waiting for task to complete for instance [%s]: %s", arg, err)
continue
}
}
}
Expand Down
114 changes: 102 additions & 12 deletions cmd/ecloud/ecloud_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ func Test_ecloudInstanceStart(t *testing.T) {
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerOnInstance("i-abcdef12").Return(nil)
service.EXPECT().PowerOnInstance("i-abcdef12").Return("task-abcdef12", nil)

ecloudInstanceStart(service, &cobra.Command{}, []string{"i-abcdef12"})
})
Expand All @@ -479,19 +479,49 @@ func Test_ecloudInstanceStart(t *testing.T) {

service := mocks.NewMockECloudService(mockCtrl)
gomock.InOrder(
service.EXPECT().PowerOnInstance("i-abcdef12").Return(nil),
service.EXPECT().PowerOnInstance("i-12abcdef").Return(nil),
service.EXPECT().PowerOnInstance("i-abcdef12").Return("task-abcdef12", nil),
service.EXPECT().PowerOnInstance("i-12abcdef").Return("task-abcdef23", nil),
)

ecloudInstanceStart(service, &cobra.Command{}, []string{"i-abcdef12", "i-12abcdef"})
})

t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

cmd := ecloudInstanceStartCmd(nil)
cmd.ParseFlags([]string{"--wait"})

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerOnInstance("i-abcdef12").Return("task-abcdef12", nil)
service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil)

ecloudInstanceStart(service, cmd, []string{"i-abcdef12"})
})

t.Run("WithWaitFlag_GetTaskError_ReturnsError", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

cmd := ecloudInstanceStartCmd(nil)
cmd.ParseFlags([]string{"--wait"})

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerOnInstance("i-abcdef12").Return("task-abcdef12", nil)
service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{}, errors.New("test error"))

test_output.AssertErrorOutput(t, "Error waiting for task to complete for instance [i-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() {
ecloudInstanceStart(service, cmd, []string{"i-abcdef12"})
})
})

t.Run("PowerOnInstanceError_OutputsError", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerOnInstance("i-abcdef12").Return(errors.New("test error"))
service.EXPECT().PowerOnInstance("i-abcdef12").Return("", errors.New("test error"))

test_output.AssertErrorOutput(t, "Error starting instance [i-abcdef12]: test error\n", func() {
ecloudInstanceStart(service, &cobra.Command{}, []string{"i-abcdef12"})
Expand Down Expand Up @@ -520,7 +550,7 @@ func Test_ecloudInstanceStop(t *testing.T) {
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerShutdownInstance("i-abcdef12").Return(nil)
service.EXPECT().PowerShutdownInstance("i-abcdef12").Return("task-abcdef12", nil)

ecloudInstanceStop(service, &cobra.Command{}, []string{"i-abcdef12"})
})
Expand All @@ -530,20 +560,50 @@ func Test_ecloudInstanceStop(t *testing.T) {
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerOffInstance("i-abcdef12").Return(nil)
service.EXPECT().PowerOffInstance("i-abcdef12").Return("task-abcdef12", nil)

cmd := ecloudInstanceStopCmd(nil)
cmd.ParseFlags([]string{"--force"})

ecloudInstanceStop(service, cmd, []string{"i-abcdef12"})
})

t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

cmd := ecloudInstanceStopCmd(nil)
cmd.ParseFlags([]string{"--wait"})

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerShutdownInstance("i-abcdef12").Return("task-abcdef12", nil)
service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil)

ecloudInstanceStop(service, cmd, []string{"i-abcdef12"})
})

t.Run("WithWaitFlag_GetTaskError_ReturnsError", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

cmd := ecloudInstanceStopCmd(nil)
cmd.ParseFlags([]string{"--wait"})

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerShutdownInstance("i-abcdef12").Return("task-abcdef12", nil)
service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{}, errors.New("test error"))

test_output.AssertErrorOutput(t, "Error waiting for task to complete for instance [i-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() {
ecloudInstanceStop(service, cmd, []string{"i-abcdef12"})
})
})

t.Run("PowerShutdownInstanceError_OutputsError", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerShutdownInstance("i-abcdef12").Return(errors.New("test error")).Times(1)
service.EXPECT().PowerShutdownInstance("i-abcdef12").Return("", errors.New("test error")).Times(1)

test_output.AssertErrorOutput(t, "Error stopping instance [i-abcdef12]: test error\n", func() {
ecloudInstanceStop(service, &cobra.Command{}, []string{"i-abcdef12"})
Expand All @@ -555,7 +615,7 @@ func Test_ecloudInstanceStop(t *testing.T) {
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerOffInstance("i-abcdef12").Return(errors.New("test error"))
service.EXPECT().PowerOffInstance("i-abcdef12").Return("", errors.New("test error"))

cmd := ecloudInstanceStopCmd(nil)
cmd.ParseFlags([]string{"--force"})
Expand Down Expand Up @@ -587,7 +647,7 @@ func Test_ecloudInstanceRestart(t *testing.T) {
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerRestartInstance("i-abcdef12").Return(nil)
service.EXPECT().PowerRestartInstance("i-abcdef12").Return("task-abcdef12", nil)

ecloudInstanceRestart(service, &cobra.Command{}, []string{"i-abcdef12"})
})
Expand All @@ -597,20 +657,50 @@ func Test_ecloudInstanceRestart(t *testing.T) {
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerResetInstance("i-abcdef12").Return(nil)
service.EXPECT().PowerResetInstance("i-abcdef12").Return("task-abcdef12", nil)

cmd := ecloudInstanceRestartCmd(nil)
cmd.ParseFlags([]string{"--force"})

ecloudInstanceRestart(service, cmd, []string{"i-abcdef12"})
})

t.Run("WithWaitFlag_NoError_Succeeds", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

cmd := ecloudInstanceRestartCmd(nil)
cmd.ParseFlags([]string{"--wait"})

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerRestartInstance("i-abcdef12").Return("task-abcdef12", nil)
service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{Status: ecloud.TaskStatusComplete}, nil)

ecloudInstanceRestart(service, cmd, []string{"i-abcdef12"})
})

t.Run("WithWaitFlag_GetTaskError_ReturnsError", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

cmd := ecloudInstanceRestartCmd(nil)
cmd.ParseFlags([]string{"--wait"})

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerRestartInstance("i-abcdef12").Return("task-abcdef12", nil)
service.EXPECT().GetTask("task-abcdef12").Return(ecloud.Task{}, errors.New("test error"))

test_output.AssertErrorOutput(t, "Error waiting for task to complete for instance [i-abcdef12]: Error waiting for command: Failed to retrieve task status: test error\n", func() {
ecloudInstanceRestart(service, cmd, []string{"i-abcdef12"})
})
})

t.Run("PowerRestartInstanceError_OutputsError", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerRestartInstance("i-abcdef12").Return(errors.New("test error")).Times(1)
service.EXPECT().PowerRestartInstance("i-abcdef12").Return("", errors.New("test error")).Times(1)

test_output.AssertErrorOutput(t, "Error restarting instance [i-abcdef12]: test error\n", func() {
ecloudInstanceRestart(service, &cobra.Command{}, []string{"i-abcdef12"})
Expand All @@ -622,7 +712,7 @@ func Test_ecloudInstanceRestart(t *testing.T) {
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)
service.EXPECT().PowerResetInstance("i-abcdef12").Return(errors.New("test error"))
service.EXPECT().PowerResetInstance("i-abcdef12").Return("", errors.New("test error"))

cmd := ecloudInstanceRestartCmd(nil)
cmd.ParseFlags([]string{"--force"})
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ require (
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.3.2
github.com/stretchr/testify v1.6.1
github.com/ukfast/sdk-go v1.4.8
github.com/ukfast/sdk-go v1.4.9
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b // indirect
gopkg.in/go-playground/assert.v1 v1.2.1
k8s.io/client-go v11.0.0+incompatible
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4U
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ukfast/go-durationstring v1.0.0 h1:kgPuA7XjLjgLDfkG8j0MpolxcZh/eMdiVoOIFD/uc5I=
github.com/ukfast/go-durationstring v1.0.0/go.mod h1:Ci81n51kfxlKUIaLY9cINIKRO94VTqV+iCGbOMTb0V8=
github.com/ukfast/sdk-go v1.4.8 h1:ROAh2r3G5XKb9uoBHvoHOaOpTr8xspAOt68TAz0b7ms=
github.com/ukfast/sdk-go v1.4.8/go.mod h1:tspweEP77MHhVEYgEEieKAKGITFgwkYl1q5fLh4HZAo=
github.com/ukfast/sdk-go v1.4.9 h1:MwxQE76YkBVWeAaM5TSKpyAQgJfm0dGYHky9HgTVzh0=
github.com/ukfast/sdk-go v1.4.9/go.mod h1:tspweEP77MHhVEYgEEieKAKGITFgwkYl1q5fLh4HZAo=
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
Expand Down
Loading

0 comments on commit 2cca756

Please sign in to comment.