diff --git a/actions/actions.go b/actions/actions.go index d98da5b3..73347801 100644 --- a/actions/actions.go +++ b/actions/actions.go @@ -56,6 +56,7 @@ func NewService( reflect.TypeOf(&castai.ActionApproveCSR{}): newApproveCSRHandler(log, clientset), reflect.TypeOf(&castai.ActionChartUpsert{}): newChartUpsertHandler(log, helmClient), reflect.TypeOf(&castai.ActionChartUninstall{}): newChartUninstallHandler(log, helmClient), + reflect.TypeOf(&castai.ActionChartRollback{}): newChartRollbackHandler(log, helmClient), reflect.TypeOf(&castai.ActionDisconnectCluster{}): newDisconnectClusterHandler(log, clientset), reflect.TypeOf(&castai.ActionSendAKSInitData{}): newSendAKSInitDataHandler(log, castaiClient), reflect.TypeOf(&castai.ActionCheckNodeDeleted{}): newCheckNodeDeletedHandler(log, clientset), diff --git a/actions/chart_rollback_handler.go b/actions/chart_rollback_handler.go new file mode 100644 index 00000000..5715ea4e --- /dev/null +++ b/actions/chart_rollback_handler.go @@ -0,0 +1,50 @@ +package actions + +import ( + "context" + "errors" + "fmt" + + "github.com/sirupsen/logrus" + + "github.com/castai/cluster-controller/castai" + "github.com/castai/cluster-controller/helm" +) + +func newChartRollbackHandler(log logrus.FieldLogger, helm helm.Client) ActionHandler { + return &chartRollbackHandler{ + log: log, + helm: helm, + } +} + +type chartRollbackHandler struct { + log logrus.FieldLogger + helm helm.Client +} + +func (c *chartRollbackHandler) Handle(_ context.Context, data interface{}) error { + req, ok := data.(*castai.ActionChartRollback) + if !ok { + return fmt.Errorf("unexpected type %T for chart rollback handler", data) + } + + if err := c.validateRequest(req); err != nil { + return err + } + + return c.helm.Rollback(helm.RollbackOptions{ + ReleaseName: req.ReleaseName, + Namespace: req.Namespace, + }) +} + +func (c *chartRollbackHandler) validateRequest(req *castai.ActionChartRollback) error { + if req.ReleaseName == "" { + return errors.New("bad request: releaseName not provided") + } + if req.Namespace == "" { + return errors.New("bad request: namespace not provided") + } + return nil +} diff --git a/actions/chart_rollback_handler_test.go b/actions/chart_rollback_handler_test.go new file mode 100644 index 00000000..2a179752 --- /dev/null +++ b/actions/chart_rollback_handler_test.go @@ -0,0 +1,68 @@ +package actions + +import ( + "context" + "fmt" + "testing" + + "github.com/golang/mock/gomock" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + + "github.com/castai/cluster-controller/castai" + "github.com/castai/cluster-controller/helm" + mock_helm "github.com/castai/cluster-controller/helm/mock" +) + +func TestChartRollbackHandler(t *testing.T) { + r := require.New(t) + ctrl := gomock.NewController(t) + helmMock := mock_helm.NewMockClient(ctrl) + ctx := context.Background() + + handler := newChartRollbackHandler(logrus.New(), helmMock) + + t.Run("successfully rollback chart", func(t *testing.T) { + action := newRollbackAction() + + helmMock.EXPECT().Rollback(helm.RollbackOptions{ + Namespace: action.Namespace, + ReleaseName: action.ReleaseName, + }).Return(nil) + + r.NoError(handler.Handle(ctx, action)) + }) + + t.Run("error when rolling back chart", func(t *testing.T) { + action := newRollbackAction() + someError := fmt.Errorf("some error") + + helmMock.EXPECT().Rollback(helm.RollbackOptions{ + Namespace: action.Namespace, + ReleaseName: action.ReleaseName, + }).Return(someError) + + r.Error(handler.Handle(ctx, action), someError) + }) + + t.Run("namespace is missing in rollback action", func(t *testing.T) { + action := newRollbackAction() + action.Namespace = "" + + r.Error(handler.Handle(ctx, action)) + }) + + t.Run("helm release is missing in rollback action", func(t *testing.T) { + action := newRollbackAction() + action.ReleaseName = "" + + r.Error(handler.Handle(ctx, action)) + }) +} + +func newRollbackAction() *castai.ActionChartRollback { + return &castai.ActionChartRollback{ + Namespace: "test", + ReleaseName: "new-release", + } +} diff --git a/castai/types.go b/castai/types.go index 68ef1778..b4742618 100644 --- a/castai/types.go +++ b/castai/types.go @@ -25,6 +25,7 @@ type ClusterAction struct { ActionApproveCSR *ActionApproveCSR `json:"actionApproveCsr,omitempty"` ActionChartUpsert *ActionChartUpsert `json:"actionChartUpsert,omitempty"` ActionChartUninstall *ActionChartUninstall `json:"actionChartUninstall,omitempty"` + ActionChartRollback *ActionChartRollback `json:"actionChartRollback,omitempty"` ActionDisconnectCluster *ActionDisconnectCluster `json:"actionDisconnectCluster,omitempty"` ActionSendAKSInitData *ActionSendAKSInitData `json:"actionSendAksInitData,omitempty"` ActionCheckNodeDeleted *ActionCheckNodeDeleted `json:"actionCheckNodeDeleted,omitempty"` @@ -55,6 +56,9 @@ func (c *ClusterAction) Data() interface{} { if c.ActionChartUninstall != nil { return c.ActionChartUninstall } + if c.ActionChartRollback != nil { + return c.ActionChartRollback + } if c.ActionDisconnectCluster != nil { return c.ActionDisconnectCluster } @@ -134,6 +138,11 @@ type ActionChartUninstall struct { ReleaseName string `json:"releaseName"` } +type ActionChartRollback struct { + Namespace string `json:"namespace"` + ReleaseName string `json:"releaseName"` +} + type ChartSource struct { RepoURL string `json:"repoUrl"` Name string `json:"name"`