Skip to content

Commit a08bc07

Browse files
invidianDongsu Park
andcommitted
api/v1beta1: add S3Bucket field to AWSClusterSpec
As a preparation for using it as an alternative backend to Secret Manager for OS-es, which do not support Secret Manager, like Flatcar Container Linux. Co-authored-by: Dongsu Park <[email protected]> Signed-off-by: Mateusz Gozdek <[email protected]>
1 parent 31f02eb commit a08bc07

13 files changed

+370
-31
lines changed

api/v1alpha3/awscluster_conversion.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ func (r *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
5454
restoreControlPlaneLoadBalancer(restored.Spec.ControlPlaneLoadBalancer, dst.Spec.ControlPlaneLoadBalancer)
5555
}
5656

57+
dst.Spec.S3Bucket = restored.Spec.S3Bucket
58+
5759
return nil
5860
}
5961

@@ -125,3 +127,7 @@ func Convert_v1beta1_NetworkStatus_To_v1alpha3_Network(in *infrav1.NetworkStatus
125127
func Convert_v1beta1_AWSLoadBalancerSpec_To_v1alpha3_AWSLoadBalancerSpec(in *infrav1.AWSLoadBalancerSpec, out *AWSLoadBalancerSpec, s apiconversion.Scope) error {
126128
return autoConvert_v1beta1_AWSLoadBalancerSpec_To_v1alpha3_AWSLoadBalancerSpec(in, out, s)
127129
}
130+
131+
func Convert_v1beta1_AWSClusterSpec_To_v1alpha3_AWSClusterSpec(in *infrav1.AWSClusterSpec, out *AWSClusterSpec, s apiconversion.Scope) error {
132+
return autoConvert_v1beta1_AWSClusterSpec_To_v1alpha3_AWSClusterSpec(in, out, s)
133+
}

api/v1alpha3/zz_generated.conversion.go

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

api/v1alpha4/awscluster_conversion.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
4545
restoreControlPlaneLoadBalancer(restored.Spec.ControlPlaneLoadBalancer, dst.Spec.ControlPlaneLoadBalancer)
4646
}
4747

48+
dst.Spec.S3Bucket = restored.Spec.S3Bucket
49+
4850
return nil
4951
}
5052

api/v1alpha4/conversion.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package v1alpha4
1818

1919
import (
2020
apiconversion "k8s.io/apimachinery/pkg/conversion"
21+
conversion "k8s.io/apimachinery/pkg/conversion"
22+
v1beta1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
2123
clusterv1alpha4 "sigs.k8s.io/cluster-api/api/v1alpha4"
2224
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2325
)
@@ -31,3 +33,7 @@ func Convert_v1alpha4_ObjectMeta_To_v1beta1_ObjectMeta(in *clusterv1alpha4.Objec
3133
func Convert_v1beta1_ObjectMeta_To_v1alpha4_ObjectMeta(in *clusterv1.ObjectMeta, out *clusterv1alpha4.ObjectMeta, s apiconversion.Scope) error {
3234
return clusterv1alpha4.Convert_v1beta1_ObjectMeta_To_v1alpha4_ObjectMeta(in, out, s)
3335
}
36+
37+
func Convert_v1beta1_AWSClusterSpec_To_v1alpha4_AWSClusterSpec(in *v1beta1.AWSClusterSpec, out *AWSClusterSpec, s conversion.Scope) error {
38+
return autoConvert_v1beta1_AWSClusterSpec_To_v1alpha4_AWSClusterSpec(in, out, s)
39+
}

api/v1alpha4/zz_generated.conversion.go

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

api/v1beta1/awscluster_types.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ type AWSClusterSpec struct {
9090
// IdentityRef is a reference to a identity to be used when reconciling this cluster
9191
// +optional
9292
IdentityRef *AWSIdentityReference `json:"identityRef,omitempty"`
93+
94+
// S3Bucket contains options to configure a supporting S3 bucket for this
95+
// cluster - currently used for nodes requiring Ignition
96+
// (https://coreos.github.io/ignition/) for bootstrapping (requires
97+
// BootstrapFormatIgnition feature flag to be enabled).
98+
// +optional
99+
S3Bucket *S3Bucket `json:"s3Bucket,omitempty"`
93100
}
94101

95102
// AWSIdentityKind defines allowed AWS identity types.
@@ -198,6 +205,22 @@ type AWSClusterStatus struct {
198205
Conditions clusterv1.Conditions `json:"conditions,omitempty"`
199206
}
200207

208+
type S3Bucket struct {
209+
// ControlPlaneIAMInstanceProfile is a name of the IAMInstanceProfile, which will be allowed
210+
// to read control-plane node bootstrap data from S3 Bucket.
211+
ControlPlaneIAMInstanceProfile string `json:"controlPlaneIAMInstanceProfile"`
212+
213+
// NodesIAMInstanceProfiles is a list of IAM instance profiles, which will be allowed to read
214+
// worker nodes bootstrap data from S3 Bucket.
215+
NodesIAMInstanceProfiles []string `json:"nodesIAMInstanceProfiles"`
216+
217+
// Name defines name of S3 Bucket to be created.
218+
// +kubebuilder:validation:MinLength:=3
219+
// +kubebuilder:validation:MaxLength:=63
220+
// +kubebuilder:validation:Pattern=`^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$`
221+
Name string `json:"name"`
222+
}
223+
201224
// +kubebuilder:object:root=true
202225
// +kubebuilder:resource:path=awsclusters,scope=Namespaced,categories=cluster-api,shortName=awsc
203226
// +kubebuilder:storageversion

api/v1beta1/awscluster_webhook.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func (r *AWSCluster) ValidateCreate() error {
5555
allErrs = append(allErrs, r.Spec.Bastion.Validate()...)
5656
allErrs = append(allErrs, r.validateSSHKeyName()...)
5757
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
58+
allErrs = append(allErrs, r.Spec.S3Bucket.Validate()...)
5859

5960
return aggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, allErrs)
6061
}
@@ -164,6 +165,7 @@ func (r *AWSCluster) ValidateUpdate(old runtime.Object) error {
164165

165166
allErrs = append(allErrs, r.Spec.Bastion.Validate()...)
166167
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
168+
allErrs = append(allErrs, r.Spec.S3Bucket.Validate()...)
167169

168170
return aggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, allErrs)
169171
}

api/v1beta1/awscluster_webhook_test.go

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,132 @@ func TestAWSCluster_ValidateCreate(t *testing.T) {
9494
},
9595
wantErr: true,
9696
},
97+
{
98+
name: "accepts bucket name with acceptable characters",
99+
cluster: &AWSCluster{
100+
Spec: AWSClusterSpec{
101+
S3Bucket: &S3Bucket{
102+
Name: "abcdefghijklmnoprstuwxyz-0123456789",
103+
ControlPlaneIAMInstanceProfile: "control-plane.cluster-api-provider-aws.sigs.k8s.io",
104+
NodesIAMInstanceProfiles: []string{"nodes.cluster-api-provider-aws.sigs.k8s.io"},
105+
},
106+
},
107+
},
108+
},
109+
{
110+
name: "rejects empty bucket name",
111+
cluster: &AWSCluster{
112+
Spec: AWSClusterSpec{
113+
S3Bucket: &S3Bucket{},
114+
},
115+
},
116+
wantErr: true,
117+
},
118+
{
119+
name: "rejects bucket name shorter than 3 characters",
120+
cluster: &AWSCluster{
121+
Spec: AWSClusterSpec{
122+
S3Bucket: &S3Bucket{
123+
Name: "fo",
124+
},
125+
},
126+
},
127+
wantErr: true,
128+
},
129+
{
130+
name: "rejects bucket name longer than 63 characters",
131+
cluster: &AWSCluster{
132+
Spec: AWSClusterSpec{
133+
S3Bucket: &S3Bucket{
134+
Name: strings.Repeat("a", 64),
135+
},
136+
},
137+
},
138+
wantErr: true,
139+
},
140+
{
141+
name: "rejects bucket name starting with not letter or number",
142+
cluster: &AWSCluster{
143+
Spec: AWSClusterSpec{
144+
S3Bucket: &S3Bucket{
145+
Name: "-foo",
146+
},
147+
},
148+
},
149+
wantErr: true,
150+
},
151+
{
152+
name: "rejects bucket name ending with not letter or number",
153+
cluster: &AWSCluster{
154+
Spec: AWSClusterSpec{
155+
S3Bucket: &S3Bucket{
156+
Name: "foo-",
157+
},
158+
},
159+
},
160+
wantErr: true,
161+
},
162+
{
163+
name: "rejects bucket name formatted as IP address",
164+
cluster: &AWSCluster{
165+
Spec: AWSClusterSpec{
166+
S3Bucket: &S3Bucket{
167+
Name: "8.8.8.8",
168+
},
169+
},
170+
},
171+
wantErr: true,
172+
},
173+
{
174+
name: "requires bucket control plane IAM instance profile to be not empty",
175+
cluster: &AWSCluster{
176+
Spec: AWSClusterSpec{
177+
S3Bucket: &S3Bucket{
178+
Name: "foo",
179+
ControlPlaneIAMInstanceProfile: "",
180+
},
181+
},
182+
},
183+
wantErr: true,
184+
},
185+
{
186+
name: "requires at least one bucket node IAM instance profile",
187+
cluster: &AWSCluster{
188+
Spec: AWSClusterSpec{
189+
S3Bucket: &S3Bucket{
190+
Name: "foo",
191+
ControlPlaneIAMInstanceProfile: "foo",
192+
},
193+
},
194+
},
195+
wantErr: true,
196+
},
197+
{
198+
name: "requires all bucket node IAM instance profiles to be not empty",
199+
cluster: &AWSCluster{
200+
Spec: AWSClusterSpec{
201+
S3Bucket: &S3Bucket{
202+
Name: "foo",
203+
ControlPlaneIAMInstanceProfile: "foo",
204+
NodesIAMInstanceProfiles: []string{""},
205+
},
206+
},
207+
},
208+
wantErr: true,
209+
},
210+
{
211+
name: "does not return error when all IAM instance profiles are populated",
212+
cluster: &AWSCluster{
213+
Spec: AWSClusterSpec{
214+
S3Bucket: &S3Bucket{
215+
Name: "foo",
216+
ControlPlaneIAMInstanceProfile: "foo",
217+
NodesIAMInstanceProfiles: []string{"bar"},
218+
},
219+
},
220+
},
221+
wantErr: false,
222+
},
97223
}
98224
for _, tt := range tests {
99225
t.Run(tt.name, func(t *testing.T) {
@@ -123,7 +249,9 @@ func TestAWSCluster_ValidateCreate(t *testing.T) {
123249
return err == nil
124250
}, 10*time.Second).Should(Equal(true))
125251

126-
tt.expect(g, c.Spec.ControlPlaneLoadBalancer)
252+
if tt.expect != nil {
253+
tt.expect(g, c.Spec.ControlPlaneLoadBalancer)
254+
}
127255
})
128256
}
129257
}

api/v1beta1/conditions_consts.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,11 @@ const (
157157
// ELBDetachFailedReason used when a control plane node fails to detach from an ELB.
158158
ELBDetachFailedReason = "ELBDetachFailed"
159159
)
160+
161+
const (
162+
// S3BucketReadyCondition indicates an S3 bucket has been created successfully.
163+
S3BucketReadyCondition clusterv1.ConditionType = "S3BucketCreated"
164+
165+
// S3BucketFailedReason is used when any errors occur during reconciliation of an S3 bucket.
166+
S3BucketFailedReason = "S3BucketCreationFailed"
167+
)

0 commit comments

Comments
 (0)