Skip to content

Commit a352f20

Browse files
authored
Allow to update subnets and security group IDs (#119)
Issue #, if available: aws-controllers-k8s/community#1855 Description of changes: Subnets or security group IDs can (now) be updated with the api but not at the same time as the public or private accesses. This patch split the api calls for theses changes into two separate methods. This also fix aws-controllers-k8s/community#1855 as the changes in subnets or security group IDs triggers an update but these field are removed before the call to the api. The api then response with an error because there is no changes to apply. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 78062e2 commit a352f20

File tree

3 files changed

+89
-12
lines changed

3 files changed

+89
-12
lines changed

pkg/resource/cluster/hook.go

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,27 @@ func (rm *resourceManager) customUpdate(
220220
}
221221
return returnClusterUpdating(updatedRes)
222222
}
223-
if delta.DifferentAt("Spec.ResourcesVPCConfig") {
224-
if err := rm.updateConfigResourcesVPCConfig(ctx, desired); err != nil {
223+
if delta.DifferentAt("Spec.ResourcesVPCConfig.EndpointPrivateAccess") ||
224+
delta.DifferentAt("Spec.ResourcesVPCConfig.EndpointPublicAccess") ||
225+
delta.DifferentAt("Spec.ResourcesVPCConfig.PublicAccessCIDRs") {
226+
if err := rm.updateConfigResourcesVPCConfigPublicAndPrivateAccess(ctx, desired); err != nil {
227+
awserr, ok := ackerr.AWSError(err)
228+
229+
// Check to see if we've raced an async update call and need to
230+
// requeue
231+
if ok && awserr.Code() == "ResourceInUseException" {
232+
return nil, requeueAfterAsyncUpdate()
233+
}
234+
235+
return nil, err
236+
}
237+
return returnClusterUpdating(updatedRes)
238+
}
239+
if delta.DifferentAt("Spec.ResourcesVPCConfig.SecurityGroupIDs") ||
240+
delta.DifferentAt("Spec.ResourcesVPCConfig.SecurityGroupRefs") ||
241+
delta.DifferentAt("Spec.ResourcesVPCConfig.SubnetIDs") ||
242+
delta.DifferentAt("Spec.ResourcesVPCConfig.SubnetRefs") {
243+
if err := rm.updateConfigResourcesVPCConfigSubnetsAndSecurityGroups(ctx, desired); err != nil {
225244
awserr, ok := ackerr.AWSError(err)
226245

227246
// Check to see if we've raced an async update call and need to
@@ -406,21 +425,20 @@ func (rm *resourceManager) updateAccessConfig(
406425
return nil
407426
}
408427

409-
func (rm *resourceManager) updateConfigResourcesVPCConfig(
428+
func (rm *resourceManager) updateConfigResourcesVPCConfigPublicAndPrivateAccess(
410429
ctx context.Context,
411430
r *resource,
412431
) (err error) {
413432
rlog := ackrtlog.FromContext(ctx)
414-
exit := rlog.Trace("rm.updateConfigResourcesVPCConfig")
433+
exit := rlog.Trace("rm.updateConfigResourcesVPCConfigPublicAndPrivateAccess")
415434
defer exit(err)
416435
input := &svcsdk.UpdateClusterConfigInput{
417436
Name: r.ko.Spec.Name,
418437
ResourcesVpcConfig: rm.newVpcConfigRequest(r),
419438
}
420439

421-
// From the EKS documentation:
422-
// "You can't update the subnets or security group IDs for an existing
423-
// cluster."
440+
// We only want to update endpointPrivateAccess, endpointPublicAccess and
441+
// publicAccessCidrs
424442
input.ResourcesVpcConfig.SetSubnetIds(nil)
425443
input.ResourcesVpcConfig.SetSecurityGroupIds(nil)
426444

@@ -433,6 +451,32 @@ func (rm *resourceManager) updateConfigResourcesVPCConfig(
433451
return nil
434452
}
435453

454+
func (rm *resourceManager) updateConfigResourcesVPCConfigSubnetsAndSecurityGroups(
455+
ctx context.Context,
456+
r *resource,
457+
) (err error) {
458+
rlog := ackrtlog.FromContext(ctx)
459+
exit := rlog.Trace("rm.updateConfigResourcesVPCConfigSubnetsAndSecurityGroups")
460+
defer exit(err)
461+
input := &svcsdk.UpdateClusterConfigInput{
462+
Name: r.ko.Spec.Name,
463+
ResourcesVpcConfig: rm.newVpcConfigRequest(r),
464+
}
465+
466+
// We only want to update securityGroupIds and subnetIds
467+
input.ResourcesVpcConfig.EndpointPublicAccess = nil
468+
input.ResourcesVpcConfig.EndpointPrivateAccess = nil
469+
input.ResourcesVpcConfig.SetPublicAccessCidrs(nil)
470+
471+
_, err = rm.sdkapi.UpdateClusterConfigWithContext(ctx, input)
472+
rm.metrics.RecordAPICall("UPDATE", "UpdateClusterConfig", err)
473+
if err != nil {
474+
return err
475+
}
476+
477+
return nil
478+
}
479+
436480
func (rm *resourceManager) listNodegroups(
437481
ctx context.Context,
438482
r *resource,

test/e2e/service_bootstrap.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,26 @@
1414
"""
1515
import logging
1616

17+
import boto3
18+
from acktest.aws.identity import get_region
1719
from acktest.bootstrapping import Resources, BootstrapFailureException
1820
from acktest.bootstrapping.iam import Role
1921
from acktest.bootstrapping.vpc import VPC
2022
from e2e import bootstrap_directory
2123
from e2e.bootstrap_resources import BootstrapResources
2224

25+
def get_availability_zone_names():
26+
ec2_client = boto3.client("ec2", region_name=get_region())
27+
zones = ec2_client.describe_availability_zones()
28+
return list(map(lambda x: x['ZoneName'], zones['AvailabilityZones']))
29+
2330
def service_bootstrap() -> Resources:
2431
logging.getLogger().setLevel(logging.INFO)
2532

33+
zones = get_availability_zone_names()
34+
# We create one subnet more than the number of AZs in order to have the last subnet in the same AZ as the first one
35+
num_public_subnet=len(zones) + 1
36+
2637
resources = BootstrapResources(
2738
ClusterRole=Role("cluster-role", "eks.amazonaws.com", managed_policies=["arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"]),
2839
FargatePodRole=Role("fargate-pod-role", "eks-fargate-pods.amazonaws.com", managed_policies=["arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy"]),
@@ -42,7 +53,7 @@ def service_bootstrap() -> Resources:
4253
"ack-access-entry-principal-role",
4354
"eks.amazonaws.com",
4455
),
45-
ClusterVPC=VPC(name_prefix="cluster-vpc", num_public_subnet=2, num_private_subnet=2)
56+
ClusterVPC=VPC(name_prefix="cluster-vpc", num_public_subnet=num_public_subnet, num_private_subnet=2),
4657
)
4758

4859
try:
@@ -55,4 +66,4 @@ def service_bootstrap() -> Resources:
5566
if __name__ == "__main__":
5667
config = service_bootstrap()
5768
# Write config to current directory by default
58-
config.serialize(bootstrap_directory)
69+
config.serialize(bootstrap_directory)

test/e2e/tests/test_cluster.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
import boto3
1818
import logging
1919
import time
20-
from typing import Dict
2120

2221
import pytest
2322

2423
from acktest.k8s import resource as k8s
2524
from acktest.k8s import condition
2625
from acktest.resources import random_suffix_name
2726
from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_eks_resource
27+
from e2e.bootstrap_resources import get_bootstrap_resources
2828
from e2e.common.types import CLUSTER_RESOURCE_PLURAL
2929
from e2e.common.waiter import wait_until_deleted
3030
from e2e.replacement_values import REPLACEMENT_VALUES
@@ -156,7 +156,7 @@ def test_create_update_delete_cluster(self, eks_client, simple_cluster):
156156

157157
wait_for_cluster_active(eks_client, cluster_name)
158158

159-
# Update the logging and VPC config fields
159+
# Update VPC endpoint public access config field
160160
updates = {
161161
"spec": {
162162
"resourcesVPCConfig": {
@@ -180,6 +180,28 @@ def test_create_update_delete_cluster(self, eks_client, simple_cluster):
180180
aws_res = eks_client.describe_cluster(name=cluster_name)
181181
assert aws_res["cluster"]["resourcesVpcConfig"]["endpointPublicAccess"] == False
182182

183+
# Update the VPC subnets config field
184+
vpc_subnets_ids = get_bootstrap_resources().ClusterVPC.public_subnets.subnet_ids
185+
# We substitute the first subnet with the last one which is in the same AZ
186+
subnets_ids = [vpc_subnets_ids[len(vpc_subnets_ids)-1], vpc_subnets_ids[1]]
187+
188+
updates = {
189+
"spec": {
190+
"resourcesVPCConfig": {
191+
"subnetIDs": subnets_ids,
192+
}
193+
}
194+
}
195+
196+
k8s.patch_custom_resource(ref, updates)
197+
time.sleep(MODIFY_WAIT_AFTER_SECONDS)
198+
199+
wait_for_cluster_active(eks_client, cluster_name)
200+
201+
aws_res = eks_client.describe_cluster(name=cluster_name)
202+
assert sorted(aws_res["cluster"]["resourcesVpcConfig"]["subnetIds"]) == sorted(subnets_ids)
203+
204+
# Update the logging fields
183205
updates = {
184206
"spec": {
185207
"logging": {
@@ -245,7 +267,7 @@ def test_update_cluster_version(self, eks_client, simple_cluster_version_minus_2
245267

246268
wait_for_cluster_active(eks_client, cluster_name)
247269

248-
# Bump two minor versions 1.27 -> 1.29
270+
# Bump two minor versions 1.27 -> 1.29
249271
updates = {
250272
"spec": {
251273
"version": "1.29"

0 commit comments

Comments
 (0)