diff --git a/api/v1beta1/awscluster_conversion.go b/api/v1beta1/awscluster_conversion.go index 92f0e96a9f..a48ae427b0 100644 --- a/api/v1beta1/awscluster_conversion.go +++ b/api/v1beta1/awscluster_conversion.go @@ -54,6 +54,14 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error { dst.Status.Network.SecurityGroups[role] = sg } + if restored.Spec.NetworkSpec.VPC.IPAMPool != nil { + if dst.Spec.NetworkSpec.VPC.IPAMPool == nil { + dst.Spec.NetworkSpec.VPC.IPAMPool = &infrav2.IPAMPool{} + } + + restoreIPAMPool(restored.Spec.NetworkSpec.VPC.IPAMPool, dst.Spec.NetworkSpec.VPC.IPAMPool) + } + return nil } @@ -66,6 +74,14 @@ func restoreControlPlaneLoadBalancerStatus(restored, dst *infrav2.LoadBalancer) dst.ELBListeners = restored.ELBListeners } +// restoreIPAMPool manually restores the ipam pool data. +// Assumes restored and dst are non-nil. +func restoreIPAMPool(restored, dst *infrav2.IPAMPool) { + dst.ID = restored.ID + dst.Name = restored.Name + dst.NetmaskLength = restored.NetmaskLength +} + // restoreControlPlaneLoadBalancer manually restores the control plane loadbalancer data. // Assumes restored and dst are non-nil. func restoreControlPlaneLoadBalancer(restored, dst *infrav2.AWSLoadBalancerSpec) { diff --git a/api/v1beta1/conversion.go b/api/v1beta1/conversion.go index 2f7c76b006..88cd03aa47 100644 --- a/api/v1beta1/conversion.go +++ b/api/v1beta1/conversion.go @@ -82,3 +82,7 @@ func Convert_v1beta2_LoadBalancer_To_v1beta1_ClassicELB(in *v1beta2.LoadBalancer func Convert_v1beta2_IngressRule_To_v1beta1_IngressRule(in *v1beta2.IngressRule, out *IngressRule, s conversion.Scope) error { return autoConvert_v1beta2_IngressRule_To_v1beta1_IngressRule(in, out, s) } + +func Convert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(in *v1beta2.VPCSpec, out *VPCSpec, s conversion.Scope) error { + return autoConvert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(in, out, s) +} diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go index 6a5e3effa0..9735c4b375 100644 --- a/api/v1beta1/zz_generated.conversion.go +++ b/api/v1beta1/zz_generated.conversion.go @@ -535,11 +535,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.VPCSpec)(nil), (*VPCSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(a.(*v1beta2.VPCSpec), b.(*VPCSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*Volume)(nil), (*v1beta2.Volume)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_Volume_To_v1beta2_Volume(a.(*Volume), b.(*v1beta2.Volume), scope) }); err != nil { @@ -600,6 +595,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta2.VPCSpec)(nil), (*VPCSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(a.(*v1beta2.VPCSpec), b.(*VPCSpec), scope) + }); err != nil { + return err + } return nil } @@ -2240,6 +2240,7 @@ func Convert_v1beta1_VPCSpec_To_v1beta2_VPCSpec(in *VPCSpec, out *v1beta2.VPCSpe func autoConvert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(in *v1beta2.VPCSpec, out *VPCSpec, s conversion.Scope) error { out.ID = in.ID out.CidrBlock = in.CidrBlock + // WARNING: in.IPAMPool requires manual conversion: does not exist in peer-type out.IPv6 = (*IPv6)(unsafe.Pointer(in.IPv6)) out.InternetGatewayID = (*string)(unsafe.Pointer(in.InternetGatewayID)) out.Tags = *(*Tags)(unsafe.Pointer(&in.Tags)) @@ -2248,11 +2249,6 @@ func autoConvert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(in *v1beta2.VPCSpec, out *VP return nil } -// Convert_v1beta2_VPCSpec_To_v1beta1_VPCSpec is an autogenerated conversion function. -func Convert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(in *v1beta2.VPCSpec, out *VPCSpec, s conversion.Scope) error { - return autoConvert_v1beta2_VPCSpec_To_v1beta1_VPCSpec(in, out, s) -} - func autoConvert_v1beta1_Volume_To_v1beta2_Volume(in *Volume, out *v1beta2.Volume, s conversion.Scope) error { out.DeviceName = in.DeviceName out.Size = in.Size diff --git a/api/v1beta2/awscluster_webhook.go b/api/v1beta2/awscluster_webhook.go index 51d35f18ba..6d3826db9e 100644 --- a/api/v1beta2/awscluster_webhook.go +++ b/api/v1beta2/awscluster_webhook.go @@ -189,6 +189,15 @@ func (r *AWSCluster) validateNetwork() field.ErrorList { allErrs = append(allErrs, field.Invalid(field.NewPath("subnets"), r.Spec.NetworkSpec.Subnets, "IPv6 cannot be used with unmanaged clusters at this time.")) } } + + if r.Spec.NetworkSpec.VPC.CidrBlock != "" && r.Spec.NetworkSpec.VPC.IPAMPool != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("cidrBlock"), r.Spec.NetworkSpec.VPC.CidrBlock, "cidrBlock and ipamPool cannot be used together")) + } + + if r.Spec.NetworkSpec.VPC.IPAMPool != nil && r.Spec.NetworkSpec.VPC.IPAMPool.ID == "" && r.Spec.NetworkSpec.VPC.IPAMPool.Name == "" { + allErrs = append(allErrs, field.Invalid(field.NewPath("ipamPool"), r.Spec.NetworkSpec.VPC.IPAMPool, "ipamPool must have either id or name")) + } + return allErrs } diff --git a/api/v1beta2/awscluster_webhook_test.go b/api/v1beta2/awscluster_webhook_test.go index dfce2b2c7c..977f0f4944 100644 --- a/api/v1beta2/awscluster_webhook_test.go +++ b/api/v1beta2/awscluster_webhook_test.go @@ -335,6 +335,33 @@ func TestAWSClusterValidateCreate(t *testing.T) { }, wantErr: false, }, + { + name: "rejects cidrBlock and ipamPool if set together", + cluster: &AWSCluster{ + Spec: AWSClusterSpec{ + NetworkSpec: NetworkSpec{ + VPC: VPCSpec{ + CidrBlock: "10.0.0.0/16", + IPAMPool: &IPAMPool{}, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "rejects ipamPool if id or name not set", + cluster: &AWSCluster{ + Spec: AWSClusterSpec{ + NetworkSpec: NetworkSpec{ + VPC: VPCSpec{ + IPAMPool: &IPAMPool{}, + }, + }, + }, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/api/v1beta2/network_types.go b/api/v1beta2/network_types.go index 48e1dc6b55..fd929459af 100644 --- a/api/v1beta2/network_types.go +++ b/api/v1beta2/network_types.go @@ -245,6 +245,18 @@ type IPv6 struct { EgressOnlyInternetGatewayID *string `json:"egressOnlyInternetGatewayId,omitempty"` } +// IPAMPool defines the IPAM pool to be used for VPC. +type IPAMPool struct { + // ID is the ID of the IPAM pool this provider should use to create VPC. + ID string `json:"id,omitempty"` + // Name is the name of the IPAM pool this provider should use to create VPC. + Name string `json:"name,omitempty"` + // The netmask length of the IPv4 CIDR you want to allocate to VPC from + // an Amazon VPC IP Address Manager (IPAM) pool. + // Defaults to /16 for IPv4 if not specified. + NetmaskLength int64 `json:"netmaskLength,omitempty"` +} + // VPCSpec configures an AWS VPC. type VPCSpec struct { // ID is the vpc-id of the VPC this provider should use to create resources. @@ -254,6 +266,9 @@ type VPCSpec struct { // Defaults to 10.0.0.0/16. CidrBlock string `json:"cidrBlock,omitempty"` + // IPAMPool defines the IPAM pool to be used for VPC. + IPAMPool *IPAMPool `json:"ipamPool,omitempty"` + // IPv6 contains ipv6 specific settings for the network. Supported only in managed clusters. // This field cannot be set on AWSCluster object. // +optional diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index 80cb1064fa..a25277e9ef 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -1252,6 +1252,21 @@ func (in *Filter) DeepCopy() *Filter { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IPAMPool) DeepCopyInto(out *IPAMPool) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPAMPool. +func (in *IPAMPool) DeepCopy() *IPAMPool { + if in == nil { + return nil + } + out := new(IPAMPool) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPv6) DeepCopyInto(out *IPv6) { *out = *in @@ -1822,6 +1837,11 @@ func (in *TargetGroupSpec) DeepCopy() *TargetGroupSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCSpec) DeepCopyInto(out *VPCSpec) { *out = *in + if in.IPAMPool != nil { + in, out := &in.IPAMPool, &out.IPAMPool + *out = new(IPAMPool) + **out = **in + } if in.IPv6 != nil { in, out := &in.IPv6, &out.IPv6 *out = new(IPv6) diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/cluster_api_controller.go b/cmd/clusterawsadm/cloudformation/bootstrap/cluster_api_controller.go index 06e0b01b22..306e42b6fa 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/cluster_api_controller.go +++ b/cmd/clusterawsadm/cloudformation/bootstrap/cluster_api_controller.go @@ -81,6 +81,8 @@ func (t Template) ControllersPolicy() *iamv1.PolicyDocument { Effect: iamv1.EffectAllow, Resource: iamv1.Resources{iamv1.Any}, Action: iamv1.Actions{ + "ec2:DescribeIpamPools", + "ec2:AllocateIpamPoolCidr", "ec2:AttachNetworkInterface", "ec2:DetachNetworkInterface", "ec2:AllocateAddress", diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/customsuffix.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/customsuffix.yaml index 199b71db89..4e8363613a 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/customsuffix.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/customsuffix.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/default.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/default.yaml index 2517f95f56..a80690a96b 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/default.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/default.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_all_secret_backends.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_all_secret_backends.yaml index a98163dd89..ee66bea15e 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_all_secret_backends.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_all_secret_backends.yaml @@ -146,6 +146,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_allow_assume_role.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_allow_assume_role.yaml index 70d64f00d5..434fddba7a 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_allow_assume_role.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_allow_assume_role.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_bootstrap_user.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_bootstrap_user.yaml index 4ce0f4051b..b720969774 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_bootstrap_user.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_bootstrap_user.yaml @@ -146,6 +146,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_custom_bootstrap_user.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_custom_bootstrap_user.yaml index 90c3dcd1aa..24e4cec098 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_custom_bootstrap_user.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_custom_bootstrap_user.yaml @@ -146,6 +146,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_different_instance_profiles.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_different_instance_profiles.yaml index b96943557b..8ee3b29977 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_different_instance_profiles.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_different_instance_profiles.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_console.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_console.yaml index d45b795fbb..8cd2240b08 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_console.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_console.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_default_roles.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_default_roles.yaml index 004bfb8b55..499780ff40 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_default_roles.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_default_roles.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_disable.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_disable.yaml index 9fca898137..2831264078 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_disable.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_disable.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_kms_prefix.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_kms_prefix.yaml index 8f6e5af704..f2c51d9578 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_kms_prefix.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_eks_kms_prefix.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_extra_statements.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_extra_statements.yaml index c64b9eb595..d0c9d30c39 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_extra_statements.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_extra_statements.yaml @@ -146,6 +146,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_s3_bucket.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_s3_bucket.yaml index 1f165c4b68..978b4aebf3 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_s3_bucket.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_s3_bucket.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_ssm_secret_backend.yaml b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_ssm_secret_backend.yaml index 56f1a2eb69..a3e1bf5afc 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_ssm_secret_backend.yaml +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fixtures/with_ssm_secret_backend.yaml @@ -140,6 +140,8 @@ Resources: PolicyDocument: Statement: - Action: + - ec2:DescribeIpamPools + - ec2:AllocateIpamPoolCidr - ec2:AttachNetworkInterface - ec2:DetachNetworkInterface - ec2:AllocateAddress diff --git a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml index 52f3aaa4e7..f562c1bf8d 100644 --- a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml +++ b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml @@ -492,6 +492,25 @@ spec: description: InternetGatewayID is the id of the internet gateway associated with the VPC. type: string + ipamPool: + description: IPAMPool defines the IPAM pool to be used for + VPC. + properties: + id: + description: ID is the ID of the IPAM pool this provider + should use to create VPC. + type: string + name: + description: Name is the name of the IPAM pool this provider + should use to create VPC. + type: string + netmaskLength: + description: The netmask length of the IPv4 CIDR you want + to allocate to VPC from an Amazon VPC IP Address Manager + (IPAM) pool. Defaults to /16 for IPv4 if not specified. + format: int64 + type: integer + type: object ipv6: description: IPv6 contains ipv6 specific settings for the network. Supported only in managed clusters. This field @@ -1940,6 +1959,25 @@ spec: description: InternetGatewayID is the id of the internet gateway associated with the VPC. type: string + ipamPool: + description: IPAMPool defines the IPAM pool to be used for + VPC. + properties: + id: + description: ID is the ID of the IPAM pool this provider + should use to create VPC. + type: string + name: + description: Name is the name of the IPAM pool this provider + should use to create VPC. + type: string + netmaskLength: + description: The netmask length of the IPv4 CIDR you want + to allocate to VPC from an Amazon VPC IP Address Manager + (IPAM) pool. Defaults to /16 for IPv4 if not specified. + format: int64 + type: integer + type: object ipv6: description: IPv6 contains ipv6 specific settings for the network. Supported only in managed clusters. This field diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml index 609fad7339..98c38222c9 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml @@ -1295,6 +1295,25 @@ spec: description: InternetGatewayID is the id of the internet gateway associated with the VPC. type: string + ipamPool: + description: IPAMPool defines the IPAM pool to be used for + VPC. + properties: + id: + description: ID is the ID of the IPAM pool this provider + should use to create VPC. + type: string + name: + description: Name is the name of the IPAM pool this provider + should use to create VPC. + type: string + netmaskLength: + description: The netmask length of the IPv4 CIDR you want + to allocate to VPC from an Amazon VPC IP Address Manager + (IPAM) pool. Defaults to /16 for IPv4 if not specified. + format: int64 + type: integer + type: object ipv6: description: IPv6 contains ipv6 specific settings for the network. Supported only in managed clusters. This field diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml index 66f3927be0..13e2d0c2e8 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml @@ -902,6 +902,26 @@ spec: description: InternetGatewayID is the id of the internet gateway associated with the VPC. type: string + ipamPool: + description: IPAMPool defines the IPAM pool to be + used for VPC. + properties: + id: + description: ID is the ID of the IPAM pool this + provider should use to create VPC. + type: string + name: + description: Name is the name of the IPAM pool + this provider should use to create VPC. + type: string + netmaskLength: + description: The netmask length of the IPv4 CIDR + you want to allocate to VPC from an Amazon VPC + IP Address Manager (IPAM) pool. Defaults to + /16 for IPv4 if not specified. + format: int64 + type: integer + type: object ipv6: description: IPv6 contains ipv6 specific settings for the network. Supported only in managed clusters. diff --git a/pkg/cloud/filter/ec2.go b/pkg/cloud/filter/ec2.go index b1e3e65f1f..d1d886f73b 100644 --- a/pkg/cloud/filter/ec2.go +++ b/pkg/cloud/filter/ec2.go @@ -31,6 +31,7 @@ const ( filterNameState = "state" filterNameVpcAttachment = "attachment.vpc-id" filterAvailabilityZone = "availability-zone" + filterNameIPAMPoolID = "ipam-pool-id" ) // EC2 exposes the ec2 sdk related filters. @@ -88,6 +89,14 @@ func (ec2Filters) ProviderOwned(clusterName string) *ec2.Filter { } } +// IPAM returns a filter based on the id of the IPAM Pool. +func (ec2Filters) IPAM(ipamPoolID string) *ec2.Filter { + return &ec2.Filter{ + Name: aws.String(filterNameIPAMPoolID), + Values: aws.StringSlice([]string{ipamPoolID}), + } +} + // VPC returns a filter based on the id of the VPC. func (ec2Filters) VPC(vpcID string) *ec2.Filter { return &ec2.Filter{ diff --git a/pkg/cloud/services/network/vpc.go b/pkg/cloud/services/network/vpc.go index 04a720c85a..79975f4fff 100644 --- a/pkg/cloud/services/network/vpc.go +++ b/pkg/cloud/services/network/vpc.go @@ -37,7 +37,8 @@ import ( ) const ( - defaultVPCCidr = "10.0.0.0/16" + defaultVPCCidr = "10.0.0.0/16" + defaultIpamV4NetmaskLength = 16 ) func (s *Service) reconcileVPC() error { @@ -195,6 +196,35 @@ func (s *Service) ensureManagedVPCAttributes(vpc *infrav1.VPCSpec) error { return nil } +func (s *Service) getIPAMPoolID() (*string, error) { + input := &ec2.DescribeIpamPoolsInput{} + + if s.scope.VPC().IPAMPool.ID != "" { + input.Filters = append(input.Filters, filter.EC2.IPAM(s.scope.VPC().IPAMPool.ID)) + } + + if s.scope.VPC().IPAMPool.Name != "" { + input.Filters = append(input.Filters, filter.EC2.Name(s.scope.VPC().IPAMPool.Name)) + } + + output, err := s.EC2Client.DescribeIpamPools(input) + if err != nil { + record.Warnf(s.scope.InfraCluster(), "FailedCreateVPC", "Failed to describe IPAM Pools: %v", err) + return nil, errors.Wrap(err, "failed to describe IPAM Pools") + } + + switch len(output.IpamPools) { + case 0: + record.Warnf(s.scope.InfraCluster(), "FailedCreateVPC", "IPAM not found") + return nil, fmt.Errorf("IPAM not found") + case 1: + return output.IpamPools[0].IpamPoolId, nil + default: + record.Warnf(s.scope.InfraCluster(), "FailedCreateVPC", "multiple IPAMs found") + return nil, fmt.Errorf("multiple IPAMs found") + } +} + func (s *Service) createVPC() (*infrav1.VPCSpec, error) { input := &ec2.CreateVpcInput{ TagSpecifications: []*ec2.TagSpecification{ @@ -211,10 +241,25 @@ func (s *Service) createVPC() (*infrav1.VPCSpec, error) { input.AmazonProvidedIpv6CidrBlock = aws.Bool(s.scope.VPC().IsIPv6Enabled()) } - if s.scope.VPC().CidrBlock == "" { - s.scope.VPC().CidrBlock = defaultVPCCidr + if s.scope.VPC().IPAMPool != nil { + ipamPoolID, err := s.getIPAMPoolID() + if err != nil { + return nil, errors.Wrap(err, "failed to get IPAM Pool ID") + } + + if s.scope.VPC().IPAMPool.NetmaskLength == 0 { + s.scope.VPC().IPAMPool.NetmaskLength = defaultIpamV4NetmaskLength + } + + input.Ipv4IpamPoolId = ipamPoolID + input.Ipv4NetmaskLength = aws.Int64(s.scope.VPC().IPAMPool.NetmaskLength) + } else { + if s.scope.VPC().CidrBlock == "" { + s.scope.VPC().CidrBlock = defaultVPCCidr + } + + input.CidrBlock = &s.scope.VPC().CidrBlock } - input.CidrBlock = &s.scope.VPC().CidrBlock out, err := s.EC2Client.CreateVpc(input) if err != nil {