Skip to content

Commit 5a95fed

Browse files
authored
Add option to delete tenant's pvcs on tenant deletion (#251)
1 parent f880e39 commit 5a95fed

File tree

12 files changed

+320
-3724
lines changed

12 files changed

+320
-3724
lines changed

k8s/console/base/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ resources:
88
- console-configmap.yaml
99
- console-service.yaml
1010
- console-deployment.yaml
11-
- minio-operator.yaml
11+
- https://github.com/minio/operator/?ref=v3.0.10

k8s/console/base/minio-operator.yaml

Lines changed: 0 additions & 1852 deletions
This file was deleted.

k8s/operator-console/base/console-cluster-role.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ rules:
2828
- create
2929
- list
3030
- patch
31+
- apiGroups:
32+
- ""
33+
resources:
34+
- persistentvolumeclaims
35+
verbs:
36+
- deletecollection
3137
- apiGroups:
3238
- "storage.k8s.io"
3339
resources:

k8s/operator-console/base/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ resources:
88
- console-configmap.yaml
99
- console-service.yaml
1010
- console-deployment.yaml
11-
- minio-operator.yaml
11+
- https://github.com/minio/operator/?ref=v3.0.10

k8s/operator-console/base/minio-operator.yaml

Lines changed: 0 additions & 1852 deletions
This file was deleted.

models/delete_tenant_request.go

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

restapi/admin_tenants.go

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
110110
err := getDeleteTenantResponse(session, params)
111111
if err != nil {
112112
log.Println(err)
113-
return admin_api.NewTenantInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to delete tenant")})
113+
return admin_api.NewTenantInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
114114
}
115115
return admin_api.NewTenantInfoOK()
116116

@@ -145,25 +145,53 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
145145
})
146146
}
147147

148-
// deleteTenantAction performs the actions of deleting a tenant
149-
func deleteTenantAction(ctx context.Context, operatorClient OperatorClient, nameSpace, instanceName string) error {
150-
err := operatorClient.TenantDelete(ctx, nameSpace, instanceName, metav1.DeleteOptions{})
151-
if err != nil {
152-
return err
153-
}
154-
return nil
155-
}
156-
157148
// getDeleteTenantResponse gets the output of deleting a minio instance
158149
func getDeleteTenantResponse(session *models.Principal, params admin_api.DeleteTenantParams) error {
159150
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
160151
if err != nil {
161152
return err
162153
}
154+
// get Kubernetes Client
155+
clientset, err := cluster.K8sClient(session.SessionToken)
156+
if err != nil {
157+
return err
158+
}
163159
opClient := &operatorClient{
164160
client: opClientClientSet,
165161
}
166-
return deleteTenantAction(context.Background(), opClient, params.Namespace, params.Tenant)
162+
deleteTenantPVCs := false
163+
if params.Body != nil {
164+
deleteTenantPVCs = params.Body.DeletePvcs
165+
}
166+
return deleteTenantAction(context.Background(), opClient, clientset.CoreV1(), params.Namespace, params.Tenant, deleteTenantPVCs)
167+
}
168+
169+
// deleteTenantAction performs the actions of deleting a tenant
170+
//
171+
// It also adds the option of deleting the tenant's underlying pvcs if deletePvcs set
172+
func deleteTenantAction(
173+
ctx context.Context,
174+
operatorClient OperatorClient,
175+
clientset v1.CoreV1Interface,
176+
namespace, tenantName string,
177+
deletePvcs bool) error {
178+
179+
err := operatorClient.TenantDelete(ctx, namespace, tenantName, metav1.DeleteOptions{})
180+
if err != nil {
181+
// try to delete pvc even if the tenant doesn't exist anymore but only if deletePvcs is set to true,
182+
// else, we return the error
183+
if (deletePvcs && !k8sErrors.IsNotFound(err)) || !deletePvcs {
184+
return err
185+
}
186+
}
187+
188+
if deletePvcs {
189+
opts := metav1.ListOptions{
190+
LabelSelector: fmt.Sprintf("%s=%s", operator.TenantLabel, tenantName),
191+
}
192+
return clientset.PersistentVolumeClaims(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, opts)
193+
}
194+
return nil
167195
}
168196

169197
func getTenantScheme(mi *operator.Tenant) string {

restapi/admin_tenants_test.go

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ import (
3333
operator "github.com/minio/operator/pkg/apis/minio.min.io/v1"
3434
v1 "github.com/minio/operator/pkg/apis/minio.min.io/v1"
3535
corev1 "k8s.io/api/core/v1"
36+
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
3637
"k8s.io/apimachinery/pkg/api/resource"
3738
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3839
"k8s.io/apimachinery/pkg/runtime"
40+
"k8s.io/apimachinery/pkg/runtime/schema"
3941
types "k8s.io/apimachinery/pkg/types"
4042
"k8s.io/client-go/kubernetes/fake"
4143
)
@@ -335,12 +337,13 @@ func Test_TenantInfo(t *testing.T) {
335337

336338
func Test_deleteTenantAction(t *testing.T) {
337339
opClient := opClientMock{}
338-
339340
type args struct {
340341
ctx context.Context
341342
operatorClient OperatorClient
342343
nameSpace string
343344
tenantName string
345+
deletePvcs bool
346+
objs []runtime.Object
344347
mockTenantDelete func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error
345348
}
346349
tests := []struct {
@@ -355,6 +358,7 @@ func Test_deleteTenantAction(t *testing.T) {
355358
operatorClient: opClient,
356359
nameSpace: "default",
357360
tenantName: "minio-tenant",
361+
deletePvcs: false,
358362
mockTenantDelete: func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error {
359363
return nil
360364
},
@@ -368,17 +372,155 @@ func Test_deleteTenantAction(t *testing.T) {
368372
operatorClient: opClient,
369373
nameSpace: "default",
370374
tenantName: "minio-tenant",
375+
deletePvcs: false,
371376
mockTenantDelete: func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error {
372377
return errors.New("something happened")
373378
},
374379
},
375380
wantErr: true,
376381
},
382+
{
383+
// Delete only PVCs of the defined tenant on the specific namespace
384+
name: "Delete PVCs on Tenant Deletion",
385+
args: args{
386+
ctx: context.Background(),
387+
operatorClient: opClient,
388+
nameSpace: "minio-tenant",
389+
tenantName: "tenant1",
390+
deletePvcs: true,
391+
objs: []runtime.Object{
392+
&corev1.PersistentVolumeClaim{
393+
ObjectMeta: metav1.ObjectMeta{
394+
Name: "PVC1",
395+
Namespace: "minio-tenant",
396+
Labels: map[string]string{
397+
operator.TenantLabel: "tenant1",
398+
operator.ZoneLabel: "zone-1",
399+
},
400+
},
401+
},
402+
},
403+
mockTenantDelete: func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error {
404+
return nil
405+
},
406+
},
407+
wantErr: false,
408+
},
409+
{
410+
// Do not delete underlying pvcs
411+
name: "Don't Delete PVCs on Tenant Deletion",
412+
args: args{
413+
ctx: context.Background(),
414+
operatorClient: opClient,
415+
nameSpace: "minio-tenant",
416+
tenantName: "tenant1",
417+
deletePvcs: false,
418+
objs: []runtime.Object{
419+
&corev1.PersistentVolumeClaim{
420+
ObjectMeta: metav1.ObjectMeta{
421+
Name: "PVC1",
422+
Namespace: "minio-tenant",
423+
Labels: map[string]string{
424+
operator.TenantLabel: "tenant1",
425+
operator.ZoneLabel: "zone-1",
426+
},
427+
},
428+
},
429+
},
430+
mockTenantDelete: func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error {
431+
return nil
432+
},
433+
},
434+
wantErr: false,
435+
},
436+
{
437+
// If error is different than NotFound, PVC deletion should not continue
438+
name: "Don't delete pvcs if error Deleting Tenant, return",
439+
args: args{
440+
ctx: context.Background(),
441+
operatorClient: opClient,
442+
nameSpace: "minio-tenant",
443+
tenantName: "tenant1",
444+
deletePvcs: true,
445+
objs: []runtime.Object{
446+
&corev1.PersistentVolumeClaim{
447+
ObjectMeta: metav1.ObjectMeta{
448+
Name: "PVC1",
449+
Namespace: "minio-tenant",
450+
Labels: map[string]string{
451+
operator.TenantLabel: "tenant1",
452+
operator.ZoneLabel: "zone-1",
453+
},
454+
},
455+
},
456+
},
457+
mockTenantDelete: func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error {
458+
return errors.New("error returned")
459+
},
460+
},
461+
wantErr: true,
462+
},
463+
{
464+
// If error is NotFound while trying to Delete Tenant, PVC deletion should continue
465+
name: "Delete pvcs if tenant not found",
466+
args: args{
467+
ctx: context.Background(),
468+
operatorClient: opClient,
469+
nameSpace: "minio-tenant",
470+
tenantName: "tenant1",
471+
deletePvcs: true,
472+
objs: []runtime.Object{
473+
&corev1.PersistentVolumeClaim{
474+
ObjectMeta: metav1.ObjectMeta{
475+
Name: "PVC1",
476+
Namespace: "minio-tenant",
477+
Labels: map[string]string{
478+
operator.TenantLabel: "tenant1",
479+
operator.ZoneLabel: "zone-1",
480+
},
481+
},
482+
},
483+
},
484+
mockTenantDelete: func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error {
485+
return k8sErrors.NewNotFound(schema.GroupResource{}, "tenant1")
486+
},
487+
},
488+
wantErr: false,
489+
},
490+
{
491+
// If error is NotFound while trying to Delete Tenant and pvcdeletion=false,
492+
// error should be returned
493+
name: "Don't delete pvcs and return error if tenant not found",
494+
args: args{
495+
ctx: context.Background(),
496+
operatorClient: opClient,
497+
nameSpace: "minio-tenant",
498+
tenantName: "tenant1",
499+
deletePvcs: false,
500+
objs: []runtime.Object{
501+
&corev1.PersistentVolumeClaim{
502+
ObjectMeta: metav1.ObjectMeta{
503+
Name: "PVC1",
504+
Namespace: "minio-tenant",
505+
Labels: map[string]string{
506+
operator.TenantLabel: "tenant1",
507+
operator.ZoneLabel: "zone-1",
508+
},
509+
},
510+
},
511+
},
512+
mockTenantDelete: func(ctx context.Context, namespace string, tenantName string, options metav1.DeleteOptions) error {
513+
return k8sErrors.NewNotFound(schema.GroupResource{}, "tenant1")
514+
},
515+
},
516+
wantErr: true,
517+
},
377518
}
378519
for _, tt := range tests {
379520
opClientTenantDeleteMock = tt.args.mockTenantDelete
521+
kubeClient := fake.NewSimpleClientset(tt.args.objs...)
380522
t.Run(tt.name, func(t *testing.T) {
381-
if err := deleteTenantAction(tt.args.ctx, tt.args.operatorClient, tt.args.nameSpace, tt.args.tenantName); (err != nil) != tt.wantErr {
523+
if err := deleteTenantAction(tt.args.ctx, tt.args.operatorClient, kubeClient.CoreV1(), tt.args.nameSpace, tt.args.tenantName, tt.args.deletePvcs); (err != nil) != tt.wantErr {
382524
t.Errorf("deleteTenantAction() error = %v, wantErr %v", err, tt.wantErr)
383525
}
384526
})
@@ -768,7 +910,7 @@ func Test_UpdateTenantAction(t *testing.T) {
768910
cnsClient := fake.NewSimpleClientset(tt.objs...)
769911
t.Run(tt.name, func(t *testing.T) {
770912
if err := updateTenantAction(tt.args.ctx, tt.args.operatorClient, cnsClient.CoreV1(), tt.args.httpCl, tt.args.nameSpace, tt.args.params); (err != nil) != tt.wantErr {
771-
t.Errorf("deleteTenantAction() error = %v, wantErr %v", err, tt.wantErr)
913+
t.Errorf("updateTenantAction() error = %v, wantErr %v", err, tt.wantErr)
772914
}
773915
})
774916
}

0 commit comments

Comments
 (0)