Skip to content

Commit d4e07ba

Browse files
authored
feat: disconnect cluster action (#14)
1 parent 79c0a65 commit d4e07ba

File tree

7 files changed

+166
-19
lines changed

7 files changed

+166
-19
lines changed

actions/actions.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import (
77
"reflect"
88
"time"
99

10-
"github.com/castai/cluster-controller/helm"
1110
"github.com/cenkalti/backoff/v4"
1211
"github.com/sirupsen/logrus"
1312
"k8s.io/client-go/kubernetes"
1413

1514
"github.com/castai/cluster-controller/castai"
15+
"github.com/castai/cluster-controller/helm"
1616
)
1717

1818
type Config struct {
@@ -44,12 +44,13 @@ func NewService(
4444
cfg: cfg,
4545
castaiClient: castaiClient,
4646
actionHandlers: map[reflect.Type]ActionHandler{
47-
reflect.TypeOf(&castai.ActionDeleteNode{}): newDeleteNodeHandler(log, clientset),
48-
reflect.TypeOf(&castai.ActionDrainNode{}): newDrainNodeHandler(log, clientset),
49-
reflect.TypeOf(&castai.ActionPatchNode{}): newPatchNodeHandler(log, clientset),
50-
reflect.TypeOf(&castai.ActionCreateEvent{}): newCreateEventHandler(log, clientset),
51-
reflect.TypeOf(&castai.ActionApproveCSR{}): newApproveCSRHandler(log, clientset),
52-
reflect.TypeOf(&castai.ActionChartUpsert{}): newChartUpsertHandler(log, helmClient),
47+
reflect.TypeOf(&castai.ActionDeleteNode{}): newDeleteNodeHandler(log, clientset),
48+
reflect.TypeOf(&castai.ActionDrainNode{}): newDrainNodeHandler(log, clientset),
49+
reflect.TypeOf(&castai.ActionPatchNode{}): newPatchNodeHandler(log, clientset),
50+
reflect.TypeOf(&castai.ActionCreateEvent{}): newCreateEventHandler(log, clientset),
51+
reflect.TypeOf(&castai.ActionApproveCSR{}): newApproveCSRHandler(log, clientset),
52+
reflect.TypeOf(&castai.ActionChartUpsert{}): newChartUpsertHandler(log, helmClient),
53+
reflect.TypeOf(&castai.ActionDisconnectCluster{}): newDisconnectClusterHandler(log, clientset),
5354
},
5455
}
5556
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package actions
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/sirupsen/logrus"
8+
apierrors "k8s.io/apimachinery/pkg/api/errors"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/client-go/kubernetes"
11+
)
12+
13+
func newDisconnectClusterHandler(log logrus.FieldLogger, client kubernetes.Interface) ActionHandler {
14+
return &disconnectClusterHandler{
15+
log: log,
16+
client: client,
17+
}
18+
}
19+
20+
type disconnectClusterHandler struct {
21+
log logrus.FieldLogger
22+
client kubernetes.Interface
23+
}
24+
25+
func (c *disconnectClusterHandler) Handle(ctx context.Context, data interface{}) error {
26+
ns := "castai-agent"
27+
_, err := c.client.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{})
28+
if err != nil {
29+
if apierrors.IsNotFound(err) {
30+
return nil
31+
}
32+
33+
// Skip if unauthorized. We either deleted access in previous reconcile loop or we never had it.
34+
if apierrors.IsUnauthorized(err) {
35+
return nil
36+
}
37+
38+
return err
39+
}
40+
41+
c.log.Infof("deleting namespace %q", ns)
42+
if err := c.client.CoreV1().Namespaces().Delete(ctx, ns, metav1.DeleteOptions{}); err != nil {
43+
return fmt.Errorf("deleting namespace %q: %v", ns, err)
44+
}
45+
46+
return nil
47+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package actions
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/sirupsen/logrus"
8+
"github.com/stretchr/testify/require"
9+
v1 "k8s.io/api/core/v1"
10+
apierrors "k8s.io/apimachinery/pkg/api/errors"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/client-go/kubernetes/fake"
13+
14+
"github.com/castai/cluster-controller/castai"
15+
)
16+
17+
func TestDisconnectClusterHandler(t *testing.T) {
18+
r := require.New(t)
19+
ctx := context.Background()
20+
21+
ns := "castai-agent"
22+
node := &v1.Namespace{
23+
ObjectMeta: metav1.ObjectMeta{
24+
Name: ns,
25+
},
26+
}
27+
clientset := fake.NewSimpleClientset(node)
28+
29+
handler := newDisconnectClusterHandler(logrus.New(), clientset)
30+
31+
err := handler.Handle(ctx, &castai.ActionDisconnectCluster{})
32+
r.NoError(err)
33+
34+
_, err = clientset.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{})
35+
r.Error(err)
36+
r.True(apierrors.IsNotFound(err))
37+
}

castai/types.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ type AckClusterActionRequest struct {
1717
}
1818

1919
type ClusterAction struct {
20-
ID string `json:"id"`
21-
ActionDeleteNode *ActionDeleteNode `json:"actionDeleteNode,omitempty"`
22-
ActionDrainNode *ActionDrainNode `json:"actionDrainNode,omitempty"`
23-
ActionPatchNode *ActionPatchNode `json:"actionPatchNode,omitempty"`
24-
ActionCreateEvent *ActionCreateEvent `json:"actionCreateEvent,omitempty"`
25-
ActionApproveCSR *ActionApproveCSR `json:"actionApproveCsr,omitempty"`
26-
ActionChartUpsert *ActionChartUpsert `json:"actionChartUpsert,omitempty"`
27-
CreatedAt time.Time `json:"createdAt"`
28-
DoneAt *time.Time `json:"doneAt,omitempty"`
29-
Error *string `json:"error,omitempty"`
20+
ID string `json:"id"`
21+
ActionDeleteNode *ActionDeleteNode `json:"actionDeleteNode,omitempty"`
22+
ActionDrainNode *ActionDrainNode `json:"actionDrainNode,omitempty"`
23+
ActionPatchNode *ActionPatchNode `json:"actionPatchNode,omitempty"`
24+
ActionCreateEvent *ActionCreateEvent `json:"actionCreateEvent,omitempty"`
25+
ActionApproveCSR *ActionApproveCSR `json:"actionApproveCsr,omitempty"`
26+
ActionChartUpsert *ActionChartUpsert `json:"actionChartUpsert,omitempty"`
27+
ActionDisconnectCluster *ActionDisconnectCluster `json:"actionDisconnectCluster,omitempty"`
28+
CreatedAt time.Time `json:"createdAt"`
29+
DoneAt *time.Time `json:"doneAt,omitempty"`
30+
Error *string `json:"error,omitempty"`
3031
}
3132

3233
func (c *ClusterAction) Data() interface{} {
@@ -48,7 +49,9 @@ func (c *ClusterAction) Data() interface{} {
4849
if c.ActionChartUpsert != nil {
4950
return c.ActionChartUpsert
5051
}
51-
52+
if c.ActionDisconnectCluster != nil {
53+
return c.ActionDisconnectCluster
54+
}
5255
return nil
5356
}
5457

@@ -95,6 +98,9 @@ type ActionCreateEvent struct {
9598
Message string `json:"message"`
9699
}
97100

101+
type ActionDisconnectCluster struct {
102+
}
103+
98104
type ActionChartUpsert struct {
99105
Namespace string `json:"namespace"`
100106
ReleaseName string `json:"releaseName"`

helm/client.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ type InstallOptions struct {
3030
ValuesOverrides map[string]string
3131
}
3232

33+
type UninstallOptions struct {
34+
Namespace string
35+
ReleaseName string
36+
}
37+
3338
type UpgradeOptions struct {
3439
ChartSource *castai.ChartSource
3540
Release *release.Release
@@ -62,6 +67,7 @@ func NewClient(log logrus.FieldLogger, loader ChartLoader, restConfig *rest.Conf
6267

6368
type Client interface {
6469
Install(ctx context.Context, opts InstallOptions) (*release.Release, error)
70+
Uninstall(opts UninstallOptions) (*release.UninstallReleaseResponse, error)
6571
Upgrade(ctx context.Context, opts UpgradeOptions) (*release.Release, error)
6672
Rollback(opts RollbackOptions) error
6773
GetRelease(opts GetReleaseOptions) (*release.Release, error)
@@ -111,6 +117,20 @@ func (c *client) Install(ctx context.Context, opts InstallOptions) (*release.Rel
111117
return res, err
112118
}
113119

120+
func (c *client) Uninstall(opts UninstallOptions) (*release.UninstallReleaseResponse, error) {
121+
cfg, err := c.configurationGetter.Get(opts.Namespace)
122+
if err != nil {
123+
return nil, err
124+
}
125+
126+
uninstall := action.NewUninstall(cfg)
127+
res, err := uninstall.Run(opts.ReleaseName)
128+
if err != nil {
129+
return nil, fmt.Errorf("chart uninstall failed, name=%s, namespace=%s: %w", opts.ReleaseName, opts.Namespace, err)
130+
}
131+
return res, nil
132+
}
133+
114134
func (c *client) Upgrade(ctx context.Context, opts UpgradeOptions) (*release.Release, error) {
115135
ch, err := c.chartLoader.Load(ctx, opts.ChartSource)
116136
if err != nil {

helm/client_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"io/ioutil"
66
"testing"
77

8-
"github.com/castai/cluster-controller/castai"
98
"github.com/sirupsen/logrus"
109
"github.com/stretchr/testify/require"
1110
"helm.sh/helm/v3/pkg/action"
@@ -16,6 +15,8 @@ import (
1615
"helm.sh/helm/v3/pkg/storage"
1716
"helm.sh/helm/v3/pkg/storage/driver"
1817
"helm.sh/helm/v3/pkg/time"
18+
19+
"github.com/castai/cluster-controller/castai"
1920
)
2021

2122
func TestClientInstall(t *testing.T) {
@@ -74,6 +75,26 @@ func TestClientUpdate(t *testing.T) {
7475
r.Equal("noop", rel.Config["random"])
7576
}
7677

78+
func TestClientUninstall(t *testing.T) {
79+
r := require.New(t)
80+
81+
currentRelease := buildNginxIngressRelease(release.StatusDeployed)
82+
client := &client{
83+
log: logrus.New(),
84+
chartLoader: &testChartLoader{chart: buildNginxIngressChart()},
85+
configurationGetter: &testConfigurationGetter{
86+
t: t,
87+
currentRelease: currentRelease,
88+
},
89+
}
90+
91+
_, err := client.Uninstall(UninstallOptions{
92+
ReleaseName: currentRelease.Name,
93+
Namespace: currentRelease.Namespace,
94+
})
95+
r.NoError(err)
96+
}
97+
7798
type testConfigurationGetter struct {
7899
t *testing.T
79100
currentRelease *release.Release

helm/mock/client.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)