Skip to content

Commit

Permalink
feat: chart upsert action (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
r0kas authored Dec 6, 2021
1 parent 737f15f commit bde6d7f
Show file tree
Hide file tree
Showing 14 changed files with 1,710 additions and 23 deletions.
3 changes: 3 additions & 0 deletions actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"reflect"
"time"

"github.com/castai/cluster-controller/helm"
"github.com/cenkalti/backoff/v4"
"github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes"
Expand Down Expand Up @@ -36,6 +37,7 @@ func NewService(
cfg Config,
clientset *kubernetes.Clientset,
castaiClient castai.Client,
helmClient helm.Client,
) Service {
return &service{
log: log,
Expand All @@ -47,6 +49,7 @@ func NewService(
reflect.TypeOf(&castai.ActionPatchNode{}): newPatchNodeHandler(log, clientset),
reflect.TypeOf(&castai.ActionCreateEvent{}): newCreateEventHandler(log, clientset),
reflect.TypeOf(&castai.ActionApproveCSR{}): newApproveCSRHandler(log, clientset),
reflect.TypeOf(&castai.ActionChartUpsert{}): newChartUpsertHandler(log, helmClient),
},
}
}
Expand Down
2 changes: 1 addition & 1 deletion actions/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestActions(t *testing.T) {
}

newTestService := func(handler ActionHandler, client castai.Client) Service {
svc := NewService(log, cfg, nil, client)
svc := NewService(log, cfg, nil, client, nil)
handlers := svc.(*service).actionHandlers
// Patch handlers with a mock one.
for k := range handlers {
Expand Down
73 changes: 73 additions & 0 deletions actions/chart_upsert_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package actions

import (
"context"
"errors"
"fmt"

"github.com/sirupsen/logrus"
helmdriver "helm.sh/helm/v3/pkg/storage/driver"

"github.com/castai/cluster-controller/castai"
"github.com/castai/cluster-controller/helm"
)

func newChartUpsertHandler(log logrus.FieldLogger, helm helm.Client) ActionHandler {
return &chartUpsertHandler{
log: log,
helm: helm,
}
}

type chartUpsertHandler struct {
log logrus.FieldLogger
helm helm.Client
}

func (c *chartUpsertHandler) Handle(ctx context.Context, data interface{}) error {
req, ok := data.(*castai.ActionChartUpsert)
if !ok {
return fmt.Errorf("unexpected type %T for upsert chart handler", data)
}

if err := c.validateRequest(req); err != nil {
return err
}

release, err := c.helm.GetRelease(helm.GetReleaseOptions{
Namespace: req.Namespace,
ReleaseName: req.ReleaseName,
})
if err != nil {
if !errors.Is(err, helmdriver.ErrReleaseNotFound) {
return fmt.Errorf("getting helm release %q in namespace %q: %w", req.ReleaseName, req.Namespace, err)
}
_, err := c.helm.Install(ctx, helm.InstallOptions{
ChartSource: &req.ChartSource,
Namespace: req.Namespace,
ReleaseName: req.ReleaseName,
ValuesOverrides: req.ValuesOverrides,
})
return err
}

_, err = c.helm.Upgrade(ctx, helm.UpgradeOptions{
ChartSource: &req.ChartSource,
Release: release,
ValuesOverrides: req.ValuesOverrides,
})
return err
}

func (c *chartUpsertHandler) validateRequest(req *castai.ActionChartUpsert) error {
if req.ReleaseName == "" {
return errors.New("bad request: releaseName not provided")
}
if req.Namespace == "" {
return errors.New("bad request: namespace not provided")
}
if err := req.ChartSource.Validate(); err != nil {
return fmt.Errorf("validating chart source: %w", err)
}
return nil
}
80 changes: 80 additions & 0 deletions actions/chart_upsert_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package actions

import (
"context"
"testing"

"github.com/golang/mock/gomock"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"helm.sh/helm/v3/pkg/release"
helmdriver "helm.sh/helm/v3/pkg/storage/driver"

"github.com/castai/cluster-controller/castai"
"github.com/castai/cluster-controller/helm"
mock_helm "github.com/castai/cluster-controller/helm/mock"
)

func TestChartUpsertHandler(t *testing.T) {
r := require.New(t)
ctrl := gomock.NewController(t)
helmMock := mock_helm.NewMockClient(ctrl)
ctx := context.Background()

handler := newChartUpsertHandler(logrus.New(), helmMock)

t.Run("install chart given release is not found", func(t *testing.T) {
action := chartUpsertAction()

helmMock.EXPECT().GetRelease(helm.GetReleaseOptions{
Namespace: action.Namespace,
ReleaseName: action.ReleaseName,
}).Return(nil, helmdriver.ErrReleaseNotFound)

helmMock.EXPECT().Install(ctx, helm.InstallOptions{
ChartSource: &action.ChartSource,
Namespace: action.Namespace,
ReleaseName: action.ReleaseName,
ValuesOverrides: action.ValuesOverrides,
}).Return(nil, nil)

r.NoError(handler.Handle(ctx, action))
})

t.Run("upgrade chart given release is found", func(t *testing.T) {
action := chartUpsertAction()

rel := &release.Release{
Name: "new-release",
Version: 1,
Namespace: "test",
}

helmMock.EXPECT().GetRelease(helm.GetReleaseOptions{
Namespace: action.Namespace,
ReleaseName: action.ReleaseName,
}).Return(rel, nil)

helmMock.EXPECT().Upgrade(ctx, helm.UpgradeOptions{
ChartSource: &action.ChartSource,
Release: rel,
ValuesOverrides: action.ValuesOverrides,
}).Return(nil, nil)

r.NoError(handler.Handle(ctx, action))
})

}

func chartUpsertAction() *castai.ActionChartUpsert {
return &castai.ActionChartUpsert{
Namespace: "test",
ReleaseName: "new-release",
ValuesOverrides: map[string]string{"image.tag": "1.0.0"},
ChartSource: castai.ChartSource{
RepoURL: "https://my-charts.repo",
Name: "super-chart",
Version: "1.5.0",
},
}
}
31 changes: 31 additions & 0 deletions castai/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package castai

import (
"errors"
"time"

"github.com/sirupsen/logrus"
Expand All @@ -22,6 +23,7 @@ type ClusterAction struct {
ActionPatchNode *ActionPatchNode `json:"actionPatchNode,omitempty"`
ActionCreateEvent *ActionCreateEvent `json:"actionCreateEvent,omitempty"`
ActionApproveCSR *ActionApproveCSR `json:"actionApproveCsr,omitempty"`
ActionChartUpsert *ActionChartUpsert `json:"actionChartUpsert,omitempty"`
CreatedAt time.Time `json:"createdAt"`
DoneAt *time.Time `json:"doneAt,omitempty"`
Error *string `json:"error,omitempty"`
Expand All @@ -43,6 +45,9 @@ func (c *ClusterAction) Data() interface{} {
if c.ActionApproveCSR != nil {
return c.ActionApproveCSR
}
if c.ActionChartUpsert != nil {
return c.ActionChartUpsert
}

return nil
}
Expand Down Expand Up @@ -89,3 +94,29 @@ type ActionCreateEvent struct {
Action string `json:"action"`
Message string `json:"message"`
}

type ActionChartUpsert struct {
Namespace string `json:"namespace"`
ReleaseName string `json:"releaseName"`
ValuesOverrides map[string]string `json:"valuesOverrides,omitempty"`
ChartSource ChartSource `json:"chartSource"`
}

type ChartSource struct {
RepoURL string `json:"repoUrl"`
Name string `json:"name"`
Version string `json:"version"`
}

func (c *ChartSource) Validate() error {
if c.Name == "" {
return errors.New("chart name is not set")
}
if c.RepoURL == "" {
return errors.New("chart repoURL is not set")
}
if c.Version == "" {
return errors.New("chart version is not set")
}
return nil
}
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ require (
github.com/cenkalti/backoff/v4 v4.1.1
github.com/go-resty/resty/v2 v2.5.0
github.com/golang/mock v1.6.0
github.com/google/uuid v1.1.2
github.com/sirupsen/logrus v1.7.0
github.com/spf13/viper v1.7.1
github.com/google/uuid v1.2.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
go.uber.org/goleak v1.1.12
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
helm.sh/helm/v3 v3.7.1
k8s.io/api v0.22.1
k8s.io/apimachinery v0.22.1
k8s.io/client-go v0.22.1
Expand Down
Loading

0 comments on commit bde6d7f

Please sign in to comment.