Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infrastructure command on bastion of Automate HA cluster for node delete #7944

Merged
merged 13 commits into from Jun 13, 2023
72 changes: 52 additions & 20 deletions components/automate-cli/cmd/chef-automate/infrastructure.go
Expand Up @@ -2,14 +2,16 @@ package main

import (
"context"

"github.com/gofrs/uuid"
"github.com/spf13/cobra"
"os"

api "github.com/chef/automate/api/interservice/deployment"
"github.com/chef/automate/components/automate-cli/pkg/docs"
"github.com/chef/automate/components/automate-cli/pkg/status"
"github.com/chef/automate/components/automate-deployment/pkg/cli"
"github.com/chef/automate/components/automate-deployment/pkg/client"
"github.com/gofrs/uuid"
"github.com/spf13/cobra"
"google.golang.org/grpc"
)

func init() {
Expand All @@ -19,11 +21,12 @@ func init() {
}

var infrastructureCmd = &cobra.Command{
Use: "infrastructure COMMAND",
Short: "Chef Automate infrastructure",
Long: "Commands for automation infrastructure management, for data related to chef-client runs and chef-server actions.",
Use: "infrastructure COMMAND",
Short: "Chef Automate infrastructure",
Long: "Commands for automation infrastructure management, for data related to chef-client runs and chef-server actions.",
PersistentPreRunE: preInfrastructureCmd,
Annotations: map[string]string{
docs.Tag: docs.Automate,
docs.Tag: docs.BastionHost,
},
}

Expand All @@ -34,37 +37,66 @@ var nodeDeleteCmd = &cobra.Command{
RunE: runDeleteNodeCmd,
Args: cobra.ExactArgs(1),
Annotations: map[string]string{
docs.Tag: docs.Automate,
docs.Tag: docs.BastionHost,
},
}

type DSClient interface {
InfrastructureNodeDelete(ctx context.Context, in *api.InfrastructureNodeDeleteRequest, opts ...grpc.CallOption) (*api.InfrastructureNodeDeleteResponse, error)
Close() error
}

type InfraFlow struct {
DsClient DSClient
Writer *cli.Writer
}

func runDeleteNodeCmd(cmd *cobra.Command, args []string) error {
ifw, err := NewDeleteNode(writer)
if err != nil {
return err
}
return ifw.RunDeleteNode(args[0])
}

func preInfrastructureCmd(cmd *cobra.Command, args []string) error {
err := commandPrePersistent(cmd)
if err != nil {
return status.Wrap(err, status.CommandExecutionError, "unable to set command parent settings")
}
if isA2HARBFileExist() {
if err = RunCmdOnSingleAutomateNode(cmd, args); err != nil {
return err
}
os.Exit(1)
}
return nil
}

func NewDeleteNode(writer *cli.Writer) (*InfraFlow, error) {
connection, err := client.Connection(client.DefaultClientTimeout)

if err != nil {
return err
return nil, err
}
return &InfraFlow{DsClient: connection, Writer: writer}, nil
}

func (ifw *InfraFlow) RunDeleteNode(nodeID string) error {

defer func() {
_ = connection.Close()
}()
defer ifw.DsClient.Close()

nodeID := args[0]
if !isValidUUID(nodeID) {
return status.New(status.InvalidCommandArgsError, "argument in not a valid node UUID")
}
deleteReq := &api.InfrastructureNodeDeleteRequest{NodeId: nodeID}

_, err = connection.InfrastructureNodeDelete(context.Background(), deleteReq)
_, err := ifw.DsClient.InfrastructureNodeDelete(context.Background(), deleteReq)
if err != nil {
return status.Wrap(
err,
status.DeploymentServiceCallError,
"Request to delete node failed",
)
return status.Wrap(err, status.DeploymentServiceCallError, "Request to delete node failed")
}

writer.Println("Node successfully deleted")
ifw.Writer.Println("Node successfully deleted")
return nil
}

Expand Down
127 changes: 127 additions & 0 deletions components/automate-cli/cmd/chef-automate/infrastructure_test.go
@@ -0,0 +1,127 @@
package main

import (
"context"
"testing"

api "github.com/chef/automate/api/interservice/deployment"
"github.com/chef/automate/lib/majorupgrade_utils"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
)

var nodeDelCmd = &cobra.Command{
Use: "node-delete [uuid]",
Short: "Delete node by node uuid",
Long: "",
RunE: runDeleteNodeCmd,
Args: cobra.ExactArgs(1),
}

var infrastructureCmdTest = &cobra.Command{
Use: "infrastructure COMMAND",
Short: "Chef Automate infrastructure",
Long: "Test for infra cmd",
PersistentPreRunE: preInfrastructureCmd,
}

func Test_runpreInfrastructureCmd(t *testing.T) {
tests := []struct {
testName string
cmd *cobra.Command
args []string
}{
{"Test node delete", infrastructureCmdTest, []string{"uuid of node"}},
}
for _, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
err := preInfrastructureCmd(tt.cmd, tt.args)
if err == nil {
assert.NoError(t, err)
}
assert.Error(t, err)
})
}
}

func Test_runDeleteNodeCmd(t *testing.T) {
tests := []struct {
testName string
cmd *cobra.Command
args []string
}{
{"Test node delete", nodeDelCmd, []string{"uuid of node"}},
}
for _, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
err := runDeleteNodeCmd(tt.cmd, tt.args)
if err == nil {
assert.NoError(t, err)
}
assert.Error(t, err)
})
}
}

type MockDSClient struct {
InfrastructureNodeDeleteFunc func(ctx context.Context, in *api.InfrastructureNodeDeleteRequest, opts ...grpc.CallOption) (*api.InfrastructureNodeDeleteResponse, error)
CloseFunc func() error
}

func (mds *MockDSClient) InfrastructureNodeDelete(ctx context.Context, in *api.InfrastructureNodeDeleteRequest, opts ...grpc.CallOption) (*api.InfrastructureNodeDeleteResponse, error) {
return mds.InfrastructureNodeDeleteFunc(ctx, in, opts...)
}

func (mds *MockDSClient) Close() error {
return mds.CloseFunc()
}

func TestRunDeleteNode(t *testing.T) {
customWriter := majorupgrade_utils.NewCustomWriter()
i := &InfraFlow{
DsClient: &MockDSClient{InfrastructureNodeDeleteFunc: func(ctx context.Context, in *api.InfrastructureNodeDeleteRequest, opts ...grpc.CallOption) (*api.InfrastructureNodeDeleteResponse, error) {
return &api.InfrastructureNodeDeleteResponse{}, nil
}, CloseFunc: func() error {
return nil
}},
Writer: customWriter.CliWriter,
}
nodeId := "3d8ffe06-6281-494a-9957-34c6f3f50154"
err := i.RunDeleteNode(nodeId)
assert.Equal(t, err, nil)

}

func TestRunDeleteNodeFailed(t *testing.T) {
customWriter := majorupgrade_utils.NewCustomWriter()
i := &InfraFlow{
DsClient: &MockDSClient{InfrastructureNodeDeleteFunc: func(ctx context.Context, in *api.InfrastructureNodeDeleteRequest, opts ...grpc.CallOption) (*api.InfrastructureNodeDeleteResponse, error) {
return nil, errors.New("DeploymentServiceCallError")
}, CloseFunc: func() error {
return nil
}},
Writer: customWriter.CliWriter,
}
nodeId := "3d8ffe06-6281-494a-9957-34c6f3f50154"
err := i.RunDeleteNode(nodeId)
assert.Error(t, err)
assert.Contains(t, err.Error(), "Request to delete node failed: DeploymentServiceCallError")
}

func TestRunDeleteNodeFailedForInvaliUUID(t *testing.T) {
customWriter := majorupgrade_utils.NewCustomWriter()
i := &InfraFlow{
DsClient: &MockDSClient{InfrastructureNodeDeleteFunc: func(ctx context.Context, in *api.InfrastructureNodeDeleteRequest, opts ...grpc.CallOption) (*api.InfrastructureNodeDeleteResponse, error) {
return &api.InfrastructureNodeDeleteResponse{}, nil
}, CloseFunc: func() error {
return nil
}},
Writer: customWriter.CliWriter,
}
nodeId := "not-a-valid-uuid"
err := i.RunDeleteNode(nodeId)
assert.Error(t, err)
assert.Contains(t, err.Error(), "argument in not a valid node UUID")
}