Skip to content

Commit

Permalink
eCloud command - wait flags (#104)
Browse files Browse the repository at this point in the history
* wip

* add task commands, pull in sdk, fix breaking changes

* move task waiting to /tasks/{id} method

* expose instance tasks

* update argument help wording

* update commands to use task responses

* update sdk

* remove unused method
  • Loading branch information
0x4c6565 committed Jun 8, 2021
1 parent a5a3534 commit 8231a90
Show file tree
Hide file tree
Showing 40 changed files with 2,183 additions and 365 deletions.
45 changes: 5 additions & 40 deletions cmd/ecloud/ecloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/spf13/cobra"
"github.com/ukfast/cli/internal/pkg/factory"
"github.com/ukfast/cli/internal/pkg/helper"
"github.com/ukfast/sdk-go/pkg/connection"
"github.com/ukfast/sdk-go/pkg/service/ecloud"
)

Expand Down Expand Up @@ -51,6 +50,7 @@ func ECloudRootCmd(f factory.ClientFactory, fs afero.Fs) *cobra.Command {
cmd.AddCommand(ecloudRouterRootCmd(f))
cmd.AddCommand(ecloudRouterThroughputRootCmd(f))
cmd.AddCommand(ecloudSSHKeyPairRootCmd(f, fs))
cmd.AddCommand(ecloudTaskRootCmd(f))
cmd.AddCommand(ecloudVolumeRootCmd(f))
cmd.AddCommand(ecloudVPCRootCmd(f))
}
Expand Down Expand Up @@ -159,58 +159,23 @@ func ResourceSyncStatusWaitFunc(fn GetResourceSyncStatusFunc, expectedStatus ecl
}
}

type GetResourceTaskStatusFunc func() (ecloud.TaskStatus, error)

func ResourceTaskStatusWaitFunc(fn GetResourceTaskStatusFunc, expectedStatus ecloud.TaskStatus) helper.WaitFunc {
func TaskStatusWaitFunc(service ecloud.ECloudService, taskID string, expectedStatus ecloud.TaskStatus) helper.WaitFunc {
return func() (finished bool, err error) {
status, err := fn()
task, err := service.GetTask(taskID)
if err != nil {
return false, fmt.Errorf("Failed to retrieve task status: %s", err)
}
if status == ecloud.TaskStatusFailed {
if task.Status == ecloud.TaskStatusFailed {
return false, fmt.Errorf("Task in [%s] state", ecloud.TaskStatusFailed.String())
}
if status == expectedStatus {
if task.Status == expectedStatus {
return true, nil
}

return false, nil
}
}

type TaskStatusFromResourceTaskListRetrieveFunc func(params connection.APIRequestParameters) ([]ecloud.Task, error)

func TaskStatusFromResourceTaskListWaitFunc(service ecloud.ECloudService, taskID string, fn TaskFromResourceTaskListFunc, status ecloud.TaskStatus) helper.WaitFunc {
return ResourceTaskStatusWaitFunc(func() (ecloud.TaskStatus, error) {
task, err := GetTaskFromResourceTaskList(fn, taskID)
if err != nil {
return "", err
}

return task.Status, nil
}, status)
}

type TaskFromResourceTaskListFunc func(params connection.APIRequestParameters) ([]ecloud.Task, error)

func GetTaskFromResourceTaskList(fn TaskFromResourceTaskListFunc, taskID string) (ecloud.Task, error) {
tasks, err := fn(*connection.NewAPIRequestParameters().
WithFilter(connection.APIRequestFiltering{
Property: "id",
Operator: connection.EQOperator,
Value: []string{taskID},
}))
if err != nil {
return ecloud.Task{}, err
}

if len(tasks) != 1 {
return ecloud.Task{}, fmt.Errorf("Expected 1 task, got %d", len(tasks))
}

return tasks[0], nil
}

type ecloudServiceCobraRunEFunc func(service ecloud.ECloudService, cmd *cobra.Command, args []string) error

func ecloudCobraRunEFunc(f factory.ClientFactory, rf ecloudServiceCobraRunEFunc) func(cmd *cobra.Command, args []string) error {
Expand Down
53 changes: 34 additions & 19 deletions cmd/ecloud/ecloud_firewallpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func ecloudFirewallPolicyRootCmd(f factory.ClientFactory) *cobra.Command {

// Child root commands
cmd.AddCommand(ecloudFirewallPolicyFirewallRuleRootCmd(f))
cmd.AddCommand(ecloudFirewallPolicyTaskRootCmd(f))

return cmd
}
Expand Down Expand Up @@ -109,7 +110,7 @@ func ecloudFirewallPolicyCreateCmd(f factory.ClientFactory) *cobra.Command {
cmd.Flags().Int("sequence", 0, "Sequence for policy")
cmd.MarkFlagRequired("sequence")
cmd.Flags().String("name", "", "Name of policy")
cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the firewall policy has been completely created before continuing on")
cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the firewall policy has been completely created")

return cmd
}
Expand All @@ -124,20 +125,20 @@ func ecloudFirewallPolicyCreate(service ecloud.ECloudService, cmd *cobra.Command
createRequest.Sequence, _ = cmd.Flags().GetInt("sequence")
}

policyID, err := service.CreateFirewallPolicy(createRequest)
taskRef, err := service.CreateFirewallPolicy(createRequest)
if err != nil {
return fmt.Errorf("Error creating firewall policy: %s", err)
}

waitFlag, _ := cmd.Flags().GetBool("wait")
if waitFlag {
err := helper.WaitForCommand(FirewallPolicyResourceSyncStatusWaitFunc(service, policyID, ecloud.SyncStatusComplete))
err := helper.WaitForCommand(TaskStatusWaitFunc(service, taskRef.TaskID, ecloud.TaskStatusComplete))
if err != nil {
return fmt.Errorf("Error waiting for firewall policy sync: %s", err)
return fmt.Errorf("Error waiting for firewall policy task to complete: %s", err)
}
}

policy, err := service.GetFirewallPolicy(policyID)
policy, err := service.GetFirewallPolicy(taskRef.ResourceID)
if err != nil {
return fmt.Errorf("Error retrieving new firewall policy: %s", err)
}
Expand All @@ -162,6 +163,7 @@ func ecloudFirewallPolicyUpdateCmd(f factory.ClientFactory) *cobra.Command {
}

cmd.Flags().String("name", "", "Name of policy")
cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the firewall policy has been completely updated")

return cmd
}
Expand All @@ -175,12 +177,21 @@ func ecloudFirewallPolicyUpdate(service ecloud.ECloudService, cmd *cobra.Command

var policies []ecloud.FirewallPolicy
for _, arg := range args {
err := service.PatchFirewallPolicy(arg, patchRequest)
task, err := service.PatchFirewallPolicy(arg, patchRequest)
if err != nil {
output.OutputWithErrorLevelf("Error updating firewall policy [%s]: %s", arg, err)
continue
}

waitFlag, _ := cmd.Flags().GetBool("wait")
if waitFlag {
err := helper.WaitForCommand(TaskStatusWaitFunc(service, task.TaskID, ecloud.TaskStatusComplete))
if err != nil {
output.OutputWithErrorLevelf("Error waiting for task to complete for firewall policy [%s]: %s", arg, err)
continue
}
}

policy, err := service.GetFirewallPolicy(arg)
if err != nil {
output.OutputWithErrorLevelf("Error retrieving updated firewall policy [%s]: %s", arg, err)
Expand All @@ -194,8 +205,8 @@ func ecloudFirewallPolicyUpdate(service ecloud.ECloudService, cmd *cobra.Command
}

func ecloudFirewallPolicyDeleteCmd(f factory.ClientFactory) *cobra.Command {
return &cobra.Command{
Use: "delete <policy: id...>",
cmd := &cobra.Command{
Use: "delete <policy: id>...",
Short: "Removes a firewall policy",
Long: "This command removes one or more firewall policies",
Example: "ukfast ecloud firewallpolicy delete fwp-abcdef12",
Expand All @@ -208,24 +219,28 @@ func ecloudFirewallPolicyDeleteCmd(f factory.ClientFactory) *cobra.Command {
},
RunE: ecloudCobraRunEFunc(f, ecloudFirewallPolicyDelete),
}

cmd.Flags().Bool("wait", false, "Specifies that the command should wait until the firewall policy has been completely removed")

return cmd
}

func ecloudFirewallPolicyDelete(service ecloud.ECloudService, cmd *cobra.Command, args []string) error {
for _, arg := range args {
err := service.DeleteFirewallPolicy(arg)
taskID, err := service.DeleteFirewallPolicy(arg)
if err != nil {
output.OutputWithErrorLevelf("Error removing firewall policy [%s]: %s", arg, err)
continue
}
}
return nil
}

func FirewallPolicyResourceSyncStatusWaitFunc(service ecloud.ECloudService, policyID string, status ecloud.SyncStatus) helper.WaitFunc {
return ResourceSyncStatusWaitFunc(func() (ecloud.SyncStatus, error) {
policy, err := service.GetFirewallPolicy(policyID)
if err != nil {
return "", err
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 firewall policy [%s]: %s", arg, err)
continue
}
}
return policy.Sync.Status, nil
}, status)
}
return nil
}
63 changes: 63 additions & 0 deletions cmd/ecloud/ecloud_firewallpolicy_task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package ecloud

import (
"errors"
"fmt"

"github.com/spf13/cobra"
"github.com/ukfast/cli/internal/pkg/factory"
"github.com/ukfast/cli/internal/pkg/helper"
"github.com/ukfast/cli/internal/pkg/output"
"github.com/ukfast/sdk-go/pkg/service/ecloud"
)

func ecloudFirewallPolicyTaskRootCmd(f factory.ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "task",
Short: "sub-commands relating to firewall policy tasks",
}

// Child commands
cmd.AddCommand(ecloudFirewallPolicyTaskListCmd(f))

return cmd
}

func ecloudFirewallPolicyTaskListCmd(f factory.ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "list <policy: id>",
Short: "Lists firewall policy tasks",
Long: "This command lists firewall policy tasks",
Example: "ukfast ecloud firewall policy task list i-abcdef12",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("Missing firewall policy")
}

return nil
},
RunE: ecloudCobraRunEFunc(f, ecloudFirewallPolicyTaskList),
}

cmd.Flags().String("id", "", "Task ID for filtering")
cmd.Flags().String("name", "", "Task name for filtering")

return cmd
}

func ecloudFirewallPolicyTaskList(service ecloud.ECloudService, cmd *cobra.Command, args []string) error {
params, err := helper.GetAPIRequestParametersFromFlags(cmd,
helper.NewStringFilterFlagOption("id", "id"),
helper.NewStringFilterFlagOption("name", "name"),
)
if err != nil {
return err
}

tasks, err := service.GetFirewallPolicyTasks(args[0], params)
if err != nil {
return fmt.Errorf("Error retrieving firewall policy tasks: %s", err)
}

return output.CommandOutput(cmd, OutputECloudTasksProvider(tasks))
}
68 changes: 68 additions & 0 deletions cmd/ecloud/ecloud_firewallpolicy_task_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ecloud

import (
"errors"
"testing"

gomock "github.com/golang/mock/gomock"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/ukfast/cli/internal/pkg/clierrors"
"github.com/ukfast/cli/test/mocks"
"github.com/ukfast/sdk-go/pkg/service/ecloud"
)

func Test_ecloudFirewallPolicyTaskListCmd_Args(t *testing.T) {
t.Run("ValidArgs_NoError", func(t *testing.T) {
err := ecloudFirewallPolicyTaskListCmd(nil).Args(nil, []string{"i-abcdef12"})

assert.Nil(t, err)
})

t.Run("InvalidArgs_Error", func(t *testing.T) {
err := ecloudFirewallPolicyTaskListCmd(nil).Args(nil, []string{})

assert.NotNil(t, err)
assert.Equal(t, "Missing firewall policy", err.Error())
})
}

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

service := mocks.NewMockECloudService(mockCtrl)

service.EXPECT().GetFirewallPolicyTasks("i-abcdef12", gomock.Any()).Return([]ecloud.Task{}, nil).Times(1)

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

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

service := mocks.NewMockECloudService(mockCtrl)
cmd := &cobra.Command{}
cmd.Flags().StringArray("filter", []string{"invalidfilter"}, "")

err := ecloudFirewallPolicyTaskList(service, cmd, []string{})

assert.IsType(t, &clierrors.ErrInvalidFlagValue{}, err)
})

t.Run("GetFirewallPolicysError_ReturnsError", func(t *testing.T) {

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

service := mocks.NewMockECloudService(mockCtrl)

service.EXPECT().GetFirewallPolicyTasks("i-abcdef12", gomock.Any()).Return([]ecloud.Task{}, errors.New("test error")).Times(1)

err := ecloudFirewallPolicyTaskList(service, &cobra.Command{}, []string{"i-abcdef12"})

assert.Equal(t, "Error retrieving firewall policy tasks: test error", err.Error())
})
}
Loading

0 comments on commit 8231a90

Please sign in to comment.