From 012f0da3bd2f1a962f9b832522dd0d12f3495ae1 Mon Sep 17 00:00:00 2001
From: "Kistner, Dominic"
-
NatGatewayConfig contains configuration for the nat gateway and the attached resources.
+NatGatewayConfig contains configuration for the NAT gateway and the attached resources.
-(Optional)
IdleConnectionTimeoutMinutes specifies the idle connection timeout limit for NAT gateway in minutes. |
+|
+zone
+
+int32
+
+ |
+
+ Zone specifies the zone in which the NAT gateway should be deployed to. + |
+
+ipAddresses
+
+
+[]PublicIPReference
+
+
+ |
+
+ IPAddresses is a list of ip addresses which should be assigned to the NAT gateway. + |
+
+(Appears on: +NatGatewayConfig) +
++
PublicIPReference contains information about a public ip.
+ +Field | +Description | +
---|---|
+name
+
+string
+
+ |
+
+ Name is the name of the public ip. + |
+
+resourceGroup
+
+string
+
+ |
+
+ ResourceGroup is the name of the resource group where the public ip is assigned to. + |
+
+zone
+
+int32
+
+ |
+
+ Zone is the zone in which the public ip is deployed to. + |
+
string
alias)diff --git a/pkg/apis/azure/types_infrastructure.go b/pkg/apis/azure/types_infrastructure.go index 4eecd2fc0..cedf0c277 100644 --- a/pkg/apis/azure/types_infrastructure.go +++ b/pkg/apis/azure/types_infrastructure.go @@ -51,6 +51,28 @@ type NetworkConfig struct { ServiceEndpoints []string } +// NatGatewayConfig contains configuration for the NAT gateway and the attached resources. +type NatGatewayConfig struct { + // Enabled is an indicator if NAT gateway should be deployed. + Enabled bool + // IdleConnectionTimeoutMinutes specifies the idle connection timeout limit for NAT gateway in minutes. + IdleConnectionTimeoutMinutes *int32 + // Zone specifies the zone in which the NAT gateway should be deployed to. + Zone *int32 + // IPAddresses is a list of ip addresses which should be assigned to the NAT gateway. + IPAddresses []PublicIPReference +} + +// PublicIPReference contains information about a public ip. +type PublicIPReference struct { + // Name is the name of the public ip. + Name string + // ResourceGroup is the name of the resource group where the public ip is assigned to. + ResourceGroup string + // Zone is the zone in which the public ip is deployed to. + Zone int32 +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // InfrastructureStatus contains information about created infrastructure resources. @@ -149,14 +171,6 @@ type VNetStatus struct { ResourceGroup *string } -// NatGatewayConfig contains configuration for the nat gateway and the attached resources. -type NatGatewayConfig struct { - // Enabled is an indicator if NAT gateway should be deployed. - Enabled bool - // IdleConnectionTimeoutMinutes specifies the idle connection timeout limit for NAT gateway in minutes. - IdleConnectionTimeoutMinutes *int32 -} - // IdentityConfig contains configuration for the managed identity. type IdentityConfig struct { // Name is the name of the identity. diff --git a/pkg/apis/azure/v1alpha1/types_infrastructure.go b/pkg/apis/azure/v1alpha1/types_infrastructure.go index 069dce087..d593e18d9 100644 --- a/pkg/apis/azure/v1alpha1/types_infrastructure.go +++ b/pkg/apis/azure/v1alpha1/types_infrastructure.go @@ -57,6 +57,28 @@ type NetworkConfig struct { ServiceEndpoints []string `json:"serviceEndpoints,omitempty"` } +// NatGatewayConfig contains configuration for the NAT gateway and the attached resources. +type NatGatewayConfig struct { + // Enabled is an indicator if NAT gateway should be deployed. + Enabled bool `json:"enabled"` + // IdleConnectionTimeoutMinutes specifies the idle connection timeout limit for NAT gateway in minutes. + IdleConnectionTimeoutMinutes *int32 `json:"idleConnectionTimeoutMinutes,omitempty"` + // Zone specifies the zone in which the NAT gateway should be deployed to. + Zone *int32 `json:"zone,omitempty"` + // IPAddresses is a list of ip addresses which should be assigned to the NAT gateway. + IPAddresses []PublicIPReference `json:"ipAddresses,omitempty"` +} + +// PublicIPReference contains information about a public ip. +type PublicIPReference struct { + // Name is the name of the public ip. + Name string `json:"name"` + // ResourceGroup is the name of the resource group where the public ip is assigned to. + ResourceGroup string `json:"resourceGroup"` + // Zone is the zone in which the public ip is deployed to. + Zone int32 `json:"zone"` +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // InfrastructureStatus contains information about created infrastructure resources. @@ -165,15 +187,6 @@ type VNetStatus struct { ResourceGroup *string `json:"resourceGroup,omitempty"` } -// NatGatewayConfig contains configuration for the nat gateway and the attached resources. -type NatGatewayConfig struct { - // Enabled is an indicator if NAT gateway should be deployed. - Enabled bool `json:"enabled"` - // IdleConnectionTimeoutMinutes specifies the idle connection timeout limit for NAT gateway in minutes. - // +optional - IdleConnectionTimeoutMinutes *int32 `json:"idleConnectionTimeoutMinutes,omitempty"` -} - // IdentityConfig contains configuration for the managed identity. type IdentityConfig struct { // Name is the name of the identity. diff --git a/pkg/apis/azure/v1alpha1/zz_generated.conversion.go b/pkg/apis/azure/v1alpha1/zz_generated.conversion.go index 43bea77a2..c26c39a9c 100644 --- a/pkg/apis/azure/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/azure/v1alpha1/zz_generated.conversion.go @@ -195,6 +195,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*PublicIPReference)(nil), (*azure.PublicIPReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_PublicIPReference_To_azure_PublicIPReference(a.(*PublicIPReference), b.(*azure.PublicIPReference), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*azure.PublicIPReference)(nil), (*PublicIPReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_azure_PublicIPReference_To_v1alpha1_PublicIPReference(a.(*azure.PublicIPReference), b.(*PublicIPReference), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*ResourceGroup)(nil), (*azure.ResourceGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_ResourceGroup_To_azure_ResourceGroup(a.(*ResourceGroup), b.(*azure.ResourceGroup), scope) }); err != nil { @@ -615,6 +625,8 @@ func Convert_azure_MachineType_To_v1alpha1_MachineType(in *azure.MachineType, ou func autoConvert_v1alpha1_NatGatewayConfig_To_azure_NatGatewayConfig(in *NatGatewayConfig, out *azure.NatGatewayConfig, s conversion.Scope) error { out.Enabled = in.Enabled out.IdleConnectionTimeoutMinutes = (*int32)(unsafe.Pointer(in.IdleConnectionTimeoutMinutes)) + out.Zone = (*int32)(unsafe.Pointer(in.Zone)) + out.IPAddresses = *(*[]azure.PublicIPReference)(unsafe.Pointer(&in.IPAddresses)) return nil } @@ -626,6 +638,8 @@ func Convert_v1alpha1_NatGatewayConfig_To_azure_NatGatewayConfig(in *NatGatewayC func autoConvert_azure_NatGatewayConfig_To_v1alpha1_NatGatewayConfig(in *azure.NatGatewayConfig, out *NatGatewayConfig, s conversion.Scope) error { out.Enabled = in.Enabled out.IdleConnectionTimeoutMinutes = (*int32)(unsafe.Pointer(in.IdleConnectionTimeoutMinutes)) + out.Zone = (*int32)(unsafe.Pointer(in.Zone)) + out.IPAddresses = *(*[]PublicIPReference)(unsafe.Pointer(&in.IPAddresses)) return nil } @@ -690,6 +704,30 @@ func Convert_azure_NetworkStatus_To_v1alpha1_NetworkStatus(in *azure.NetworkStat return autoConvert_azure_NetworkStatus_To_v1alpha1_NetworkStatus(in, out, s) } +func autoConvert_v1alpha1_PublicIPReference_To_azure_PublicIPReference(in *PublicIPReference, out *azure.PublicIPReference, s conversion.Scope) error { + out.Name = in.Name + out.ResourceGroup = in.ResourceGroup + out.Zone = in.Zone + return nil +} + +// Convert_v1alpha1_PublicIPReference_To_azure_PublicIPReference is an autogenerated conversion function. +func Convert_v1alpha1_PublicIPReference_To_azure_PublicIPReference(in *PublicIPReference, out *azure.PublicIPReference, s conversion.Scope) error { + return autoConvert_v1alpha1_PublicIPReference_To_azure_PublicIPReference(in, out, s) +} + +func autoConvert_azure_PublicIPReference_To_v1alpha1_PublicIPReference(in *azure.PublicIPReference, out *PublicIPReference, s conversion.Scope) error { + out.Name = in.Name + out.ResourceGroup = in.ResourceGroup + out.Zone = in.Zone + return nil +} + +// Convert_azure_PublicIPReference_To_v1alpha1_PublicIPReference is an autogenerated conversion function. +func Convert_azure_PublicIPReference_To_v1alpha1_PublicIPReference(in *azure.PublicIPReference, out *PublicIPReference, s conversion.Scope) error { + return autoConvert_azure_PublicIPReference_To_v1alpha1_PublicIPReference(in, out, s) +} + func autoConvert_v1alpha1_ResourceGroup_To_azure_ResourceGroup(in *ResourceGroup, out *azure.ResourceGroup, s conversion.Scope) error { out.Name = in.Name return nil diff --git a/pkg/apis/azure/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/azure/v1alpha1/zz_generated.deepcopy.go index 9a65da906..d1ce7769d 100644 --- a/pkg/apis/azure/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/azure/v1alpha1/zz_generated.deepcopy.go @@ -404,6 +404,16 @@ func (in *NatGatewayConfig) DeepCopyInto(out *NatGatewayConfig) { *out = new(int32) **out = **in } + if in.Zone != nil { + in, out := &in.Zone, &out.Zone + *out = new(int32) + **out = **in + } + if in.IPAddresses != nil { + in, out := &in.IPAddresses, &out.IPAddresses + *out = make([]PublicIPReference, len(*in)) + copy(*out, *in) + } return } @@ -466,6 +476,22 @@ func (in *NetworkStatus) DeepCopy() *NetworkStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PublicIPReference) DeepCopyInto(out *PublicIPReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PublicIPReference. +func (in *PublicIPReference) DeepCopy() *PublicIPReference { + if in == nil { + return nil + } + out := new(PublicIPReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceGroup) DeepCopyInto(out *ResourceGroup) { *out = *in diff --git a/pkg/apis/azure/validation/infrastructure.go b/pkg/apis/azure/validation/infrastructure.go index d23760042..992fcdb48 100644 --- a/pkg/apis/azure/validation/infrastructure.go +++ b/pkg/apis/azure/validation/infrastructure.go @@ -68,23 +68,72 @@ func ValidateInfrastructureConfig(infra *apisazure.InfrastructureConfig, nodesCI workerCIDR := cidrvalidation.NewCIDR(infra.Networks.Workers, networksPath.Child("workers")) allErrs = append(allErrs, cidrvalidation.ValidateCIDRParse(workerCIDR)...) allErrs = append(allErrs, cidrvalidation.ValidateCIDRIsCanonical(networksPath.Child("workers"), infra.Networks.Workers)...) + if nodes != nil { + allErrs = append(allErrs, nodes.ValidateSubset(workerCIDR)...) + } - // Validate vnet config allErrs = append(allErrs, validateVnetConfig(infra.Networks.VNet, infra.ResourceGroup, workerCIDR, nodes, pods, services, networksPath.Child("vnet"))...) - - allErrs = append(allErrs, validateNatGatewayConfig(infra, hasVmoAlphaAnnotation, fldPath.Child("networks", "natGateway"))...) + allErrs = append(allErrs, validateNatGatewayConfig(infra.Networks.NatGateway, infra.Zoned, hasVmoAlphaAnnotation, networksPath.Child("natGateway"))...) if infra.Identity != nil && (infra.Identity.Name == "" || infra.Identity.ResourceGroup == "") { allErrs = append(allErrs, field.Invalid(fldPath.Child("identity"), infra.Identity, "specifying an identity requires the name of the identity and the resource group which hosts the identity")) } - if nodes != nil { - allErrs = append(allErrs, nodes.ValidateSubset(workerCIDR)...) + return allErrs +} + +func validateNatGatewayConfig(natGatewayConfig *apisazure.NatGatewayConfig, zoned bool, hasVmoAlphaAnnotation bool, natGatewayPath *field.Path) field.ErrorList { + var allErrs = field.ErrorList{} + + if natGatewayConfig == nil { + return nil + } + + if !natGatewayConfig.Enabled { + if natGatewayConfig.Zone != nil || natGatewayConfig.IdleConnectionTimeoutMinutes != nil || natGatewayConfig.IPAddresses != nil { + return append(allErrs, field.Invalid(natGatewayPath, natGatewayConfig, "NatGateway is disabled but additional NatGateway config is passed")) + } + return nil + } + + // NatGateway cannot be offered for Shoot clusters with a primary AvailabilitySet. + // The NatGateway is not compatible with the Basic SKU Loadbalancers which are + // required to use for *Shoot clusters with AvailabilitySet. + if !zoned && !hasVmoAlphaAnnotation { + return append(allErrs, field.Forbidden(natGatewayPath, "NatGateway is currently only supported for zonal and VMO clusters")) + } + + if natGatewayConfig.IdleConnectionTimeoutMinutes != nil && (*natGatewayConfig.IdleConnectionTimeoutMinutes < natGatewayMinTimeoutInMinutes || *natGatewayConfig.IdleConnectionTimeoutMinutes > natGatewayMaxTimeoutInMinutes) { + allErrs = append(allErrs, field.Invalid(natGatewayPath.Child("idleConnectionTimeoutMinutes"), *natGatewayConfig.IdleConnectionTimeoutMinutes, "idleConnectionTimeoutMinutes values must range between 4 and 120")) + } + + if natGatewayConfig.Zone == nil { + if len(natGatewayConfig.IPAddresses) > 0 { + allErrs = append(allErrs, field.Invalid(natGatewayPath.Child("zone"), *natGatewayConfig, "Public IPs can only be selected for zonal NatGateways")) + } + return allErrs } + allErrs = append(allErrs, validateNatGatewayIPReference(natGatewayConfig.IPAddresses, *natGatewayConfig.Zone, natGatewayPath.Child("ipAddresses"))...) return allErrs } +func validateNatGatewayIPReference(references []apisazure.PublicIPReference, zone int32, fldPath *field.Path) field.ErrorList { + var allErrs = field.ErrorList{} + for i, ref := range references { + if ref.Zone != zone { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("zone"), ref.Zone, fmt.Sprintf("Public IP can't be used as it is not the same zone as the NatGateway (zone %d)", zone))) + } + if ref.Name == "" { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), ref.Name, "Empty name for NatGateway public IP is invalid")) + } + if ref.ResourceGroup == "" { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("resourceGroup"), ref.ResourceGroup, "Empty resourceGroup for NatGateway public IP is invalid")) + } + } + return allErrs +} + func validateVnetConfig(vnetConfig apisazure.VNet, resourceGroupConfig *apisazure.ResourceGroup, workers, nodes, pods, services cidrvalidation.CIDR, vnetConfigPath *field.Path) field.ErrorList { var allErrs = field.ErrorList{} @@ -121,27 +170,6 @@ func validateVnetConfig(vnetConfig apisazure.VNet, resourceGroupConfig *apisazur return allErrs } -func validateNatGatewayConfig(infra *apisazure.InfrastructureConfig, hasVmoAlphaAnnotation bool, natGatewayConfigPath *field.Path) field.ErrorList { - var allErrs = field.ErrorList{} - - if infra.Networks.NatGateway == nil { - return allErrs - } - - // NatGateway can't be offered for Shoot clusters with a primary AvailabilitySet. - // The NatGateway is not compatible with the Basic SKU Loadbalancers which are - // required for Shoot clusters with AvailabilitySet. - if !infra.Zoned && !hasVmoAlphaAnnotation { - return append(allErrs, field.Invalid(natGatewayConfigPath, infra.Networks.NatGateway, "NatGateway is currently only supported for zoned cluster")) - } - - if infra.Networks.NatGateway.IdleConnectionTimeoutMinutes != nil && (*infra.Networks.NatGateway.IdleConnectionTimeoutMinutes < natGatewayMinTimeoutInMinutes || *infra.Networks.NatGateway.IdleConnectionTimeoutMinutes > natGatewayMaxTimeoutInMinutes) { - allErrs = append(allErrs, field.Invalid(natGatewayConfigPath.Child("idleConnectionTimeoutMinutes"), *infra.Networks.NatGateway.IdleConnectionTimeoutMinutes, "idleConnectionTimeoutMinutes values must range between 4 and 120")) - } - - return allErrs -} - // ValidateInfrastructureConfigUpdate validates a InfrastructureConfig object. func ValidateInfrastructureConfigUpdate(oldConfig, newConfig *apisazure.InfrastructureConfig, providerPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} diff --git a/pkg/apis/azure/validation/infrastructure_test.go b/pkg/apis/azure/validation/infrastructure_test.go index 9b1533bdc..6249df157 100644 --- a/pkg/apis/azure/validation/infrastructure_test.go +++ b/pkg/apis/azure/validation/infrastructure_test.go @@ -293,24 +293,101 @@ var _ = Describe("InfrastructureConfig validation", func() { }) Context("NatGateway", func() { - It("should return no errors using a NatGateway for a zoned cluster", func() { + BeforeEach(func() { infrastructureConfig.Zoned = true infrastructureConfig.Networks.NatGateway = &apisazure.NatGatewayConfig{Enabled: true} + }) + + It("should pass as there is no NatGateway config is provided", func() { + infrastructureConfig.Networks.NatGateway = nil Expect(ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath)).To(BeEmpty()) }) - It("should return an error using a NatGateway for a non zoned cluster", func() { - infrastructureConfig.Zoned = false - infrastructureConfig.Networks.NatGateway = &apisazure.NatGatewayConfig{} + It("should pass as the NatGateway is disabled", func() { + infrastructureConfig.Networks.NatGateway.Enabled = false + Expect(ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath)).To(BeEmpty()) + }) + + It("should fail as NatGatway is disabled but additional config for the NatGateway is supplied", func() { + infrastructureConfig.Networks.NatGateway.Enabled = false + infrastructureConfig.Networks.NatGateway.Zone = pointer.Int32Ptr(2) + errorList := ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath) - Expect(errorList).To(HaveLen(1)) Expect(errorList).To(ConsistOfFields(Fields{ "Type": Equal(field.ErrorTypeInvalid), "Field": Equal("networks.natGateway"), - "Detail": Equal("NatGateway is currently only supported for zoned cluster"), + "Detail": Equal("NatGateway is disabled but additional NatGateway config is passed"), })) }) + It("should fail as NatGatway is enabled but the cluster is not zonal", func() { + infrastructureConfig.Zoned = false + infrastructureConfig.Networks.NatGateway.Enabled = true + + errorList := ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath) + Expect(errorList).To(ConsistOfFields(Fields{ + "Type": Equal(field.ErrorTypeForbidden), + "Field": Equal("networks.natGateway"), + "Detail": Equal("NatGateway is currently only supported for zonal and VMO clusters"), + })) + }) + + It("should pass as the NatGateway has a zone", func() { + infrastructureConfig.Networks.NatGateway.Zone = pointer.Int32Ptr(2) + Expect(ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath)).To(BeEmpty()) + }) + + Context("User provided public IP", func() { + BeforeEach(func() { + infrastructureConfig.Networks.NatGateway.Zone = pointer.Int32Ptr(1) + infrastructureConfig.Networks.NatGateway.IPAddresses = []apisazure.PublicIPReference{{ + Name: "public-ip-name", + ResourceGroup: "public-ip-resource-group", + Zone: 1, + }} + }) + + It("should fail as NatGateway has no zone but an external public ip", func() { + infrastructureConfig.Networks.NatGateway.Zone = nil + errorList := ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath) + Expect(errorList).To(ConsistOfFields(Fields{ + "Type": Equal(field.ErrorTypeInvalid), + "Field": Equal("networks.natGateway.zone"), + "Detail": Equal("Public IPs can only be selected for zonal NatGateways"), + })) + }) + + It("should fail as resource is in a different zone as the NatGateway", func() { + infrastructureConfig.Networks.NatGateway.IPAddresses[0].Zone = 2 + errorList := ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath) + Expect(errorList).To(ConsistOfFields(Fields{ + "Type": Equal(field.ErrorTypeInvalid), + "Field": Equal("networks.natGateway.ipAddresses[0].zone"), + "Detail": Equal("Public IP can't be used as it is not the same zone as the NatGateway (zone 1)"), + })) + }) + + It("should fail as name is empty", func() { + infrastructureConfig.Networks.NatGateway.IPAddresses[0].Name = "" + errorList := ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath) + Expect(errorList).To(ConsistOfFields(Fields{ + "Type": Equal(field.ErrorTypeInvalid), + "Field": Equal("networks.natGateway.ipAddresses[0].name"), + "Detail": Equal("Empty name for NatGateway public IP is invalid"), + })) + }) + + It("should fail as resource group is empty", func() { + infrastructureConfig.Networks.NatGateway.IPAddresses[0].ResourceGroup = "" + errorList := ValidateInfrastructureConfig(infrastructureConfig, &nodes, &pods, &services, hasVmoAlphaAnnotation, providerPath) + Expect(errorList).To(ConsistOfFields(Fields{ + "Type": Equal(field.ErrorTypeInvalid), + "Field": Equal("networks.natGateway.ipAddresses[0].resourceGroup"), + "Detail": Equal("Empty resourceGroup for NatGateway public IP is invalid"), + })) + }) + }) + Context("IdleConnectionTimeoutMinutes", func() { It("should return an error when specifying lower than minimum values", func() { var timeoutValue int32 = 0 diff --git a/pkg/apis/azure/zz_generated.deepcopy.go b/pkg/apis/azure/zz_generated.deepcopy.go index 3ff7ef7fb..92aca501b 100644 --- a/pkg/apis/azure/zz_generated.deepcopy.go +++ b/pkg/apis/azure/zz_generated.deepcopy.go @@ -404,6 +404,16 @@ func (in *NatGatewayConfig) DeepCopyInto(out *NatGatewayConfig) { *out = new(int32) **out = **in } + if in.Zone != nil { + in, out := &in.Zone, &out.Zone + *out = new(int32) + **out = **in + } + if in.IPAddresses != nil { + in, out := &in.IPAddresses, &out.IPAddresses + *out = make([]PublicIPReference, len(*in)) + copy(*out, *in) + } return } @@ -466,6 +476,22 @@ func (in *NetworkStatus) DeepCopy() *NetworkStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PublicIPReference) DeepCopyInto(out *PublicIPReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PublicIPReference. +func (in *PublicIPReference) DeepCopy() *PublicIPReference { + if in == nil { + return nil + } + out := new(PublicIPReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceGroup) DeepCopyInto(out *ResourceGroup) { *out = *in diff --git a/pkg/internal/infrastructure/terraform.go b/pkg/internal/infrastructure/terraform.go index 025298a1f..2324cd194 100644 --- a/pkg/internal/infrastructure/terraform.go +++ b/pkg/internal/infrastructure/terraform.go @@ -71,6 +71,26 @@ var StatusTypeMeta = metav1.TypeMeta{ Kind: "InfrastructureStatus", } +// RenderTerraformerChart renders the azure-infra chart with the given values. +func RenderTerraformerChart(renderer chartrenderer.Interface, infra *extensionsv1alpha1.Infrastructure, clientAuth *internal.ClientAuth, + config *api.InfrastructureConfig, cluster *controller.Cluster) (*TerraformFiles, error) { + values, err := ComputeTerraformerChartValues(infra, clientAuth, config, cluster) + if err != nil { + return nil, err + } + + release, err := renderer.Render(filepath.Join(azure.InternalChartsPath, "azure-infra"), "azure-infra", infra.Namespace, values) + if err != nil { + return nil, err + } + + return &TerraformFiles{ + Main: release.FileContent("main.tf"), + Variables: release.FileContent("variables.tf"), + TFVars: []byte(release.FileContent("terraform.tfvars")), + }, nil +} + // ComputeTerraformerChartValues computes the values for the Azure Terraformer chart. func ComputeTerraformerChartValues(infra *extensionsv1alpha1.Infrastructure, clientAuth *internal.ClientAuth, config *api.InfrastructureConfig, cluster *controller.Cluster) (map[string]interface{}, error) { @@ -78,7 +98,6 @@ func ComputeTerraformerChartValues(infra *extensionsv1alpha1.Infrastructure, cli createResourceGroup = true createVNet = true createAvailabilitySet = false - createNatGateway = false resourceGroupName = infra.Namespace identityConfig map[string]interface{} @@ -97,7 +116,6 @@ func ComputeTerraformerChartValues(infra *extensionsv1alpha1.Infrastructure, cli "routeTableName": TerraformerOutputKeyRouteTableName, "securityGroupName": TerraformerOutputKeySecurityGroupName, } - natGatewayConfig = map[string]interface{}{} ) primaryAvSetRequired, err := isPrimaryAvailabilitySetRequired(infra, config, cluster) @@ -140,12 +158,7 @@ func ComputeTerraformerChartValues(infra *extensionsv1alpha1.Infrastructure, cli azureConfig["countUpdateDomains"] = count.updateDomains } - if config.Networks.NatGateway != nil && config.Networks.NatGateway.Enabled { - createNatGateway = true - if config.Networks.NatGateway.IdleConnectionTimeoutMinutes != nil { - natGatewayConfig["idleConnectionTimeoutMinutes"] = *config.Networks.NatGateway.IdleConnectionTimeoutMinutes - } - } + natGatewayConfig, createNatGateway := generateNatGatewayValues(config) // Checks if the Gardener managed NatGateway public ip needs to be migrated. // TODO(natipmigration) This can be removed in future versions when the ip migration has been completed. @@ -189,24 +202,32 @@ func ComputeTerraformerChartValues(infra *extensionsv1alpha1.Infrastructure, cli }, nil } -// RenderTerraformerChart renders the azure-infra chart with the given values. -func RenderTerraformerChart(renderer chartrenderer.Interface, infra *extensionsv1alpha1.Infrastructure, clientAuth *internal.ClientAuth, - config *api.InfrastructureConfig, cluster *controller.Cluster) (*TerraformFiles, error) { - values, err := ComputeTerraformerChartValues(infra, clientAuth, config, cluster) - if err != nil { - return nil, err +func generateNatGatewayValues(config *api.InfrastructureConfig) (map[string]interface{}, bool) { + var natGatewayConfig = make(map[string]interface{}) + if config.Networks.NatGateway == nil || !config.Networks.NatGateway.Enabled { + return natGatewayConfig, false } - release, err := renderer.Render(filepath.Join(azure.InternalChartsPath, "azure-infra"), "azure-infra", infra.Namespace, values) - if err != nil { - return nil, err + if config.Networks.NatGateway.IdleConnectionTimeoutMinutes != nil { + natGatewayConfig["idleConnectionTimeoutMinutes"] = *config.Networks.NatGateway.IdleConnectionTimeoutMinutes } - return &TerraformFiles{ - Main: release.FileContent("main.tf"), - Variables: release.FileContent("variables.tf"), - TFVars: []byte(release.FileContent("terraform.tfvars")), - }, nil + if config.Networks.NatGateway.Zone != nil { + natGatewayConfig["zone"] = *config.Networks.NatGateway.Zone + } + + if len(config.Networks.NatGateway.IPAddresses) > 0 { + var ipAddresses = make([]map[string]interface{}, len(config.Networks.NatGateway.IPAddresses)) + for i, ip := range config.Networks.NatGateway.IPAddresses { + ipAddresses[i] = map[string]interface{}{ + "name": ip.Name, + "resourceGroup": ip.ResourceGroup, + } + } + natGatewayConfig["ipAddresses"] = ipAddresses + } + + return natGatewayConfig, true } // TerraformFiles are the files that have been rendered from the infrastructure chart. diff --git a/pkg/internal/infrastructure/terraform_test.go b/pkg/internal/infrastructure/terraform_test.go index e74d69a55..4790a4ef7 100644 --- a/pkg/internal/infrastructure/terraform_test.go +++ b/pkg/internal/infrastructure/terraform_test.go @@ -387,7 +387,7 @@ var _ = Describe("Terraform", func() { } expectedCreateValues["natGateway"] = true values, err := ComputeTerraformerChartValues(infra, clientAuth, config, cluster) - Expect(err).To(Not(HaveOccurred())) + Expect(err).NotTo(HaveOccurred()) Expect(values).To(BeEquivalentTo(expectedValues)) }) @@ -400,7 +400,46 @@ var _ = Describe("Terraform", func() { expectedCreateValues["natGateway"] = true expectedNatGatewayValues["idleConnectionTimeoutMinutes"] = timeout values, err := ComputeTerraformerChartValues(infra, clientAuth, config, cluster) - Expect(err).To(Not(HaveOccurred())) + Expect(err).NotTo(HaveOccurred()) + Expect(values).To(BeEquivalentTo(expectedValues)) + }) + + It("should correctly compute terraform chart values with zonal NatGateway", func() { + config.Networks.NatGateway = &api.NatGatewayConfig{ + Enabled: true, + Zone: pointer.Int32Ptr(1), + } + expectedCreateValues["natGateway"] = true + expectedNatGatewayValues["zone"] = int32(1) + values, err := ComputeTerraformerChartValues(infra, clientAuth, config, cluster) + Expect(err).NotTo(HaveOccurred()) + Expect(values).To(BeEquivalentTo(expectedValues)) + }) + + It("should correctly compute terraform chart values with zonal public ip addresses", func() { + var ( + ipName = "public-ip-1-name" + ipResourceGroup = "public-ip-1-resource-group" + ) + + config.Networks.NatGateway = &api.NatGatewayConfig{ + Enabled: true, + Zone: pointer.Int32Ptr(1), + IPAddresses: []api.PublicIPReference{{ + Name: ipName, + ResourceGroup: ipResourceGroup, + Zone: int32(1), + }}, + } + expectedCreateValues["natGateway"] = true + expectedNatGatewayValues["zone"] = int32(1) + expectedNatGatewayValues["ipAddresses"] = []map[string]interface{}{{ + "name": ipName, + "resourceGroup": ipResourceGroup, + }} + + values, err := ComputeTerraformerChartValues(infra, clientAuth, config, cluster) + Expect(err).NotTo(HaveOccurred()) Expect(values).To(BeEquivalentTo(expectedValues)) })