Skip to content

Commit 2bf3644

Browse files
authored
Merge pull request #2253 from sedefsavas/multitenancy-cont
✨ Support AWS multitenancy
2 parents 0626b9b + b2d95c7 commit 2bf3644

File tree

76 files changed

+5133
-452
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+5133
-452
lines changed

api/v1alpha2/awscluster_conversion.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
100100
// Manually convert conditions
101101
dst.SetConditions(restored.GetConditions())
102102

103+
dst.Spec.IdentityRef = restored.Spec.IdentityRef
103104
return nil
104105
}
105106

api/v1alpha2/zz_generated.conversion.go

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

api/v1alpha3/awscluster_types.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ const (
2525
// ClusterFinalizer allows ReconcileAWSCluster to clean up AWS resources associated with AWSCluster before
2626
// removing it from the apiserver.
2727
ClusterFinalizer = "awscluster.infrastructure.cluster.x-k8s.io"
28+
29+
// AWSClusterControllerIdentityName is the name of the AWSClusterControllerIdentity singleton
30+
AWSClusterControllerIdentityName = "default"
2831
)
2932

3033
// AWSClusterSpec defines the desired state of AWSCluster
@@ -82,6 +85,34 @@ type AWSClusterSpec struct {
8285
// Bastion contains options to configure the bastion host.
8386
// +optional
8487
Bastion Bastion `json:"bastion"`
88+
89+
// IdentityRef is a reference to a identity to be used when reconciling this cluster
90+
// +optional
91+
IdentityRef *AWSIdentityReference `json:"identityRef,omitempty"`
92+
}
93+
94+
type AWSIdentityKind string
95+
96+
var (
97+
// ControllerIdentityKind defines identity reference kind as AWSClusterControllerIdentity
98+
ControllerIdentityKind = AWSIdentityKind("AWSClusterControllerIdentity")
99+
100+
// ClusterRoleIdentityKind defines identity reference kind as AWSClusterRoleIdentity
101+
ClusterRoleIdentityKind = AWSIdentityKind("AWSClusterRoleIdentity")
102+
103+
// ClusterStaticIdentityKind defines identity reference kind as AWSClusterStaticIdentity
104+
ClusterStaticIdentityKind = AWSIdentityKind("AWSClusterStaticIdentity")
105+
)
106+
107+
// AWSIdentityReference specifies a identity.
108+
type AWSIdentityReference struct {
109+
// Name of the identity.
110+
// +kubebuilder:validation:MinLength=1
111+
Name string `json:"name"`
112+
113+
// Kind of the identity.
114+
// +kubebuilder:validation:Enum=AWSClusterControllerIdentity;AWSClusterRoleIdentity;AWSClusterStaticIdentity
115+
Kind AWSIdentityKind `json:"kind"`
85116
}
86117

87118
type Bastion struct {

api/v1alpha3/awscluster_webhook.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ func (r *AWSCluster) ValidateUpdate(old runtime.Object) error {
9696
)
9797
}
9898

99+
// If a identityRef is already set, do not allow removal of it.
100+
if oldC.Spec.IdentityRef != nil && r.Spec.IdentityRef == nil {
101+
allErrs = append(allErrs,
102+
field.Invalid(field.NewPath("spec", "identityRef"),
103+
r.Spec.IdentityRef, "field cannot be set to nil"),
104+
)
105+
}
106+
99107
allErrs = append(allErrs, r.Spec.Bastion.Validate()...)
100108

101109
return aggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, allErrs)
@@ -104,6 +112,13 @@ func (r *AWSCluster) ValidateUpdate(old runtime.Object) error {
104112
func (r *AWSCluster) Default() {
105113
SetDefaults_Bastion(&r.Spec.Bastion)
106114
SetDefaults_NetworkSpec(&r.Spec.NetworkSpec)
115+
116+
if r.Spec.IdentityRef == nil {
117+
r.Spec.IdentityRef = &AWSIdentityReference{
118+
Kind: ControllerIdentityKind,
119+
Name: AWSClusterControllerIdentityName,
120+
}
121+
}
107122
}
108123

109124
func (r *AWSCluster) validateSSHKeyName() field.ErrorList {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha3
18+
19+
import (
20+
"fmt"
21+
"reflect"
22+
23+
"github.com/pkg/errors"
24+
apierrors "k8s.io/apimachinery/pkg/api/errors"
25+
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/apimachinery/pkg/util/validation/field"
27+
ctrl "sigs.k8s.io/controller-runtime"
28+
logf "sigs.k8s.io/controller-runtime/pkg/log"
29+
"sigs.k8s.io/controller-runtime/pkg/webhook"
30+
)
31+
32+
// log is for logging in this package.
33+
var _ = logf.Log.WithName("awsclustercontrolleridentity-resource")
34+
35+
func (r *AWSClusterControllerIdentity) SetupWebhookWithManager(mgr ctrl.Manager) error {
36+
return ctrl.NewWebhookManagedBy(mgr).
37+
For(r).
38+
Complete()
39+
}
40+
41+
// +kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1alpha3-awsclustercontrolleridentity,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=awsclustercontrolleridentities,versions=v1alpha3,name=validation.awsclustercontrolleridentity.infrastructure.cluster.x-k8s.io,sideEffects=None
42+
// +kubebuilder:webhook:verbs=create;update,path=/mutate-infrastructure-cluster-x-k8s-io-v1alpha3-awsclustercontrolleridentity,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=infrastructure.cluster.x-k8s.io,resources=awsclustercontrolleridentities,versions=v1alpha3,name=default.awsclustercontrolleridentity.infrastructure.cluster.x-k8s.io,sideEffects=None
43+
44+
var (
45+
_ webhook.Validator = &AWSClusterControllerIdentity{}
46+
_ webhook.Defaulter = &AWSClusterControllerIdentity{}
47+
)
48+
49+
func (r *AWSClusterControllerIdentity) ValidateCreate() error {
50+
// Ensures AWSClusterControllerIdentity being singleton by only allowing "default" as name
51+
if r.Name != AWSClusterControllerIdentityName {
52+
return field.Invalid(field.NewPath("name"),
53+
r.Name, "AWSClusterControllerIdentity is a singleton and only acceptable name is default")
54+
}
55+
56+
return nil
57+
}
58+
59+
func (r *AWSClusterControllerIdentity) ValidateDelete() error {
60+
return nil
61+
}
62+
63+
func (r *AWSClusterControllerIdentity) ValidateUpdate(old runtime.Object) error {
64+
oldP, ok := old.(*AWSClusterControllerIdentity)
65+
if !ok {
66+
return apierrors.NewBadRequest(fmt.Sprintf("expected an AWSClusterControllerIdentity but got a %T", old))
67+
}
68+
69+
if !reflect.DeepEqual(r.Spec, oldP.Spec) {
70+
return errors.New("AWSClusterControllerIdentity is immutable")
71+
}
72+
73+
if r.Name != oldP.Name {
74+
return field.Invalid(field.NewPath("name"),
75+
r.Name, "AWSClusterControllerIdentity is a singleton and only acceptable name is default")
76+
}
77+
return nil
78+
}
79+
80+
func (r *AWSClusterControllerIdentity) Default() {
81+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha3
18+
19+
import (
20+
"context"
21+
"testing"
22+
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
)
25+
26+
func TestAWSClusterControllerValidateCreate(t *testing.T) {
27+
tests := []struct {
28+
name string
29+
identity *AWSClusterControllerIdentity
30+
wantError bool
31+
}{
32+
{
33+
name: "only allow AWSClusterControllerIdentity creation with name default",
34+
identity: &AWSClusterControllerIdentity{
35+
ObjectMeta: metav1.ObjectMeta{
36+
Name: "default",
37+
},
38+
},
39+
wantError: false,
40+
},
41+
{
42+
name: "do not allow AWSClusterControllerIdentity creation with name other than default",
43+
identity: &AWSClusterControllerIdentity{
44+
ObjectMeta: metav1.ObjectMeta{
45+
Name: "test",
46+
},
47+
},
48+
wantError: true,
49+
},
50+
}
51+
for _, tt := range tests {
52+
t.Run(tt.name, func(t *testing.T) {
53+
identity := tt.identity.DeepCopy()
54+
identity.TypeMeta = metav1.TypeMeta{
55+
APIVersion: GroupVersion.String(),
56+
Kind: "AWSClusterControllerIdentity",
57+
}
58+
ctx := context.TODO()
59+
if err := testEnv.Create(ctx, identity); (err != nil) != tt.wantError {
60+
t.Errorf("ValidateCreate() error = %v, wantErr %v", err, tt.wantError)
61+
}
62+
testEnv.Delete(ctx, identity)
63+
})
64+
}
65+
}
66+
67+
func TestAWSClusterControllerValidateUpdate(t *testing.T) {
68+
controllerIdentity := &AWSClusterControllerIdentity{
69+
TypeMeta: metav1.TypeMeta{
70+
APIVersion: GroupVersion.String(),
71+
Kind: "AWSClusterControllerIdentity",
72+
},
73+
ObjectMeta: metav1.ObjectMeta{
74+
Name: AWSClusterControllerIdentityName,
75+
},
76+
Spec: AWSClusterControllerIdentitySpec{
77+
AWSClusterIdentitySpec: AWSClusterIdentitySpec{
78+
AllowedNamespaces: &AllowedNamespaces{},
79+
},
80+
},
81+
}
82+
83+
ctx := context.TODO()
84+
defer testEnv.Delete(ctx, controllerIdentity)
85+
86+
if err := testEnv.Create(ctx, controllerIdentity); err != nil {
87+
t.Errorf("controllerIdentity creation failed %v", err)
88+
}
89+
90+
tests := []struct {
91+
name string
92+
identity *AWSClusterControllerIdentity
93+
wantError bool
94+
}{
95+
{
96+
name: "do not allow any spec changes",
97+
identity: &AWSClusterControllerIdentity{
98+
ObjectMeta: metav1.ObjectMeta{
99+
Name: "default",
100+
},
101+
},
102+
wantError: true,
103+
},
104+
{
105+
name: "do not allow name change",
106+
identity: &AWSClusterControllerIdentity{
107+
ObjectMeta: metav1.ObjectMeta{
108+
Name: "test",
109+
},
110+
},
111+
wantError: true,
112+
},
113+
{
114+
name: "no error when updating with same object",
115+
identity: controllerIdentity,
116+
wantError: false,
117+
},
118+
}
119+
for _, tt := range tests {
120+
t.Run(tt.name, func(t *testing.T) {
121+
identity := tt.identity.DeepCopy()
122+
identity.TypeMeta = metav1.TypeMeta{
123+
APIVersion: GroupVersion.String(),
124+
Kind: "AWSClusterControllerIdentity",
125+
}
126+
ctx := context.TODO()
127+
if err := testEnv.Update(ctx, identity); (err != nil) != tt.wantError {
128+
t.Errorf("ValidateUpdate() error = %v, wantErr %v", err, tt.wantError)
129+
}
130+
})
131+
}
132+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha3
18+
19+
import (
20+
ctrl "sigs.k8s.io/controller-runtime"
21+
logf "sigs.k8s.io/controller-runtime/pkg/log"
22+
)
23+
24+
// log is for logging in this package.
25+
var _ = logf.Log.WithName("awsclustercontrolleridentitylist-resource")
26+
27+
func (r *AWSClusterControllerIdentityList) SetupWebhookWithManager(mgr ctrl.Manager) error {
28+
return ctrl.NewWebhookManagedBy(mgr).
29+
For(r).
30+
Complete()
31+
}

0 commit comments

Comments
 (0)