Skip to content

Commit 6e62f2c

Browse files
committed
Support for Azure Private DNS Zone to be present in any resource group
1 parent 5d04aaa commit 6e62f2c

9 files changed

+231
-4
lines changed

api/v1beta1/azurecluster_validation.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func validateNetworkSpec(controlPlaneEnabled bool, networkSpec NetworkSpec, old
203203
lbType = networkSpec.APIServerLB.Type
204204
}
205205
allErrs = append(allErrs, validatePrivateDNSZoneName(networkSpec.PrivateDNSZoneName, controlPlaneEnabled, lbType, fldPath.Child("privateDNSZoneName"))...)
206+
allErrs = append(allErrs, validatePrivateDNSZoneResourceGroup(networkSpec.PrivateDNSZoneName, networkSpec.PrivateDNSZoneResourceGroup, fldPath.Child("privateDNSZoneResourceGroup"))...)
206207

207208
if len(allErrs) == 0 {
208209
return nil
@@ -556,6 +557,24 @@ func validatePrivateDNSZoneName(privateDNSZoneName string, controlPlaneEnabled b
556557
return allErrs
557558
}
558559

560+
// validatePrivateDNSZoneResourceGroup validates the PrivateDNSZoneResourceGroup.
561+
// A private DNS Zone's resource group is valid as long as privateDNSZoneName is provided with the private dns resource group name
562+
func validatePrivateDNSZoneResourceGroup(privateDNSZoneName string, privateDNSZoneResourceGroup string, fldPath *field.Path) field.ErrorList {
563+
var allErrs field.ErrorList
564+
565+
if privateDNSZoneResourceGroup != "" {
566+
if privateDNSZoneName == "" {
567+
allErrs = append(allErrs, field.Invalid(fldPath, privateDNSZoneName,
568+
"PrivateDNSZoneResourceGroup can only be used when PrivateDNSZoneName is provided"))
569+
}
570+
if err := validateResourceGroup(privateDNSZoneResourceGroup, fldPath); err != nil {
571+
allErrs = append(allErrs, err)
572+
}
573+
}
574+
575+
return allErrs
576+
}
577+
559578
// validateCloudProviderConfigOverrides validates CloudProviderConfigOverrides.
560579
func validateCloudProviderConfigOverrides(oldConfig, newConfig *CloudProviderConfigOverrides, fldPath *field.Path) field.ErrorList {
561580
var allErrs field.ErrorList

api/v1beta1/azurecluster_validation_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,81 @@ func TestPrivateDNSZoneName(t *testing.T) {
13711371
}
13721372
}
13731373

1374+
func TestPrivateDNSZoneResourceGroup(t *testing.T) {
1375+
testcases := []struct {
1376+
name string
1377+
network NetworkSpec
1378+
wantErr bool
1379+
expectedErr field.Error
1380+
}{
1381+
{
1382+
name: "testEmptyPrivateDNSZoneNameAndResourceGroup",
1383+
network: NetworkSpec{
1384+
NetworkClassSpec: NetworkClassSpec{
1385+
PrivateDNSZoneName: "",
1386+
PrivateDNSZoneResourceGroup: "",
1387+
},
1388+
},
1389+
wantErr: false,
1390+
},
1391+
{
1392+
name: "testValidPrivateDNSZoneNameAndResourceGroup",
1393+
network: NetworkSpec{
1394+
NetworkClassSpec: NetworkClassSpec{
1395+
PrivateDNSZoneName: "good.dns.io",
1396+
PrivateDNSZoneResourceGroup: "test-rg",
1397+
},
1398+
},
1399+
wantErr: false,
1400+
},
1401+
{
1402+
name: "testInvalidPrivateDNSZoneResourceGroup",
1403+
network: NetworkSpec{
1404+
NetworkClassSpec: NetworkClassSpec{
1405+
PrivateDNSZoneName: "good.dns.io",
1406+
PrivateDNSZoneResourceGroup: "inv@lid-rg",
1407+
},
1408+
},
1409+
expectedErr: field.Error{
1410+
Type: "FieldValueInvalid",
1411+
Field: "spec.networkSpec.privateDNSZoneResourceGroup",
1412+
BadValue: "inv@lid-rg",
1413+
Detail: "resourceGroup doesn't match regex ^[-\\w\\._\\(\\)]+$",
1414+
},
1415+
wantErr: true,
1416+
},
1417+
{
1418+
name: "testEmptyPrivateDNSZoneNameWithValidResourceGroup",
1419+
network: NetworkSpec{
1420+
NetworkClassSpec: NetworkClassSpec{
1421+
PrivateDNSZoneName: "",
1422+
PrivateDNSZoneResourceGroup: "test-rg",
1423+
},
1424+
},
1425+
expectedErr: field.Error{
1426+
Type: "FieldValueInvalid",
1427+
Field: "spec.networkSpec.privateDNSZoneResourceGroup",
1428+
BadValue: "",
1429+
Detail: "PrivateDNSZoneResourceGroup can only be used when PrivateDNSZoneName is provided",
1430+
},
1431+
wantErr: true,
1432+
},
1433+
}
1434+
1435+
for _, test := range testcases {
1436+
t.Run(test.name, func(t *testing.T) {
1437+
t.Parallel()
1438+
g := NewWithT(t)
1439+
err := validatePrivateDNSZoneResourceGroup(test.network.PrivateDNSZoneName, test.network.PrivateDNSZoneResourceGroup, field.NewPath("spec", "networkSpec", "privateDNSZoneResourceGroup"))
1440+
if test.wantErr {
1441+
g.Expect(err).To(ContainElement(MatchError(test.expectedErr.Error())))
1442+
} else {
1443+
g.Expect(err).To(BeEmpty())
1444+
}
1445+
})
1446+
}
1447+
}
1448+
13741449
func TestValidateNodeOutboundLB(t *testing.T) {
13751450
testcases := []struct {
13761451
name string

api/v1beta1/azurecluster_webhook.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ func (c *AzureCluster) ValidateUpdate(oldRaw runtime.Object) (admission.Warnings
116116
allErrs = append(allErrs, err)
117117
}
118118

119+
if err := webhookutils.ValidateImmutable(
120+
field.NewPath("spec", "networkSpec", "privateDNSZoneResourceGroup"),
121+
old.Spec.NetworkSpec.PrivateDNSZoneResourceGroup,
122+
c.Spec.NetworkSpec.PrivateDNSZoneResourceGroup); err != nil {
123+
allErrs = append(allErrs, err)
124+
}
125+
119126
// Allow enabling azure bastion but avoid disabling it.
120127
if old.Spec.BastionSpec.AzureBastion != nil && !reflect.DeepEqual(old.Spec.BastionSpec.AzureBastion, c.Spec.BastionSpec.AzureBastion) {
121128
allErrs = append(allErrs,

api/v1beta1/azureclustertemplate_validation.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ func (c *AzureClusterTemplate) validateClusterTemplateSpec() field.ErrorList {
6262

6363
allErrs = append(allErrs, c.validatePrivateDNSZoneName()...)
6464

65+
allErrs = append(allErrs, c.validatePrivateDNSZoneResourceGroup()...)
66+
6567
return allErrs
6668
}
6769

@@ -169,3 +171,18 @@ func (c *AzureClusterTemplate) validatePrivateDNSZoneName() field.ErrorList {
169171

170172
return allErrs
171173
}
174+
175+
func (c *AzureClusterTemplate) validatePrivateDNSZoneResourceGroup() field.ErrorList {
176+
var allErrs field.ErrorList
177+
178+
fldPath := field.NewPath("spec").Child("template").Child("spec").Child("networkSpec").Child("privateDNSZoneResourceGroup")
179+
networkSpec := c.Spec.Template.Spec.NetworkSpec
180+
181+
allErrs = append(allErrs, validatePrivateDNSZoneResourceGroup(
182+
networkSpec.PrivateDNSZoneName,
183+
networkSpec.PrivateDNSZoneResourceGroup,
184+
fldPath,
185+
)...)
186+
187+
return allErrs
188+
}

api/v1beta1/azureclustertemplate_validation_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,96 @@ func TestValidatePrivateDNSZoneName(t *testing.T) {
822822
})
823823
}
824824
}
825+
826+
func TestValidatePrivateDNSZoneResourceGroup(t *testing.T) {
827+
cases := []struct {
828+
name string
829+
clusterTemplate *AzureClusterTemplate
830+
expectValid bool
831+
expectedErr field.Error
832+
}{
833+
{
834+
name: "not set",
835+
clusterTemplate: &AzureClusterTemplate{
836+
ObjectMeta: metav1.ObjectMeta{
837+
Name: "test-cluster-template",
838+
},
839+
Spec: AzureClusterTemplateSpec{
840+
Template: AzureClusterTemplateResource{
841+
Spec: AzureClusterTemplateResourceSpec{
842+
NetworkSpec: NetworkTemplateSpec{},
843+
},
844+
},
845+
},
846+
},
847+
expectValid: true,
848+
},
849+
{
850+
name: "should be set if PrivateDNSZoneName is given",
851+
clusterTemplate: &AzureClusterTemplate{
852+
ObjectMeta: metav1.ObjectMeta{
853+
Name: "test-cluster-template",
854+
},
855+
Spec: AzureClusterTemplateSpec{
856+
Template: AzureClusterTemplateResource{
857+
Spec: AzureClusterTemplateResourceSpec{
858+
NetworkSpec: NetworkTemplateSpec{
859+
NetworkClassSpec: NetworkClassSpec{
860+
PrivateDNSZoneName: "e.f.g",
861+
PrivateDNSZoneResourceGroup: "a.b.c",
862+
},
863+
},
864+
},
865+
},
866+
},
867+
},
868+
expectValid: true,
869+
},
870+
{
871+
name: "should not be set if PrivateDNSZoneName is not given",
872+
clusterTemplate: &AzureClusterTemplate{
873+
ObjectMeta: metav1.ObjectMeta{
874+
Name: "test-cluster-template",
875+
},
876+
Spec: AzureClusterTemplateSpec{
877+
Template: AzureClusterTemplateResource{
878+
Spec: AzureClusterTemplateResourceSpec{
879+
NetworkSpec: NetworkTemplateSpec{
880+
NetworkClassSpec: NetworkClassSpec{
881+
PrivateDNSZoneResourceGroup: "a.b.c",
882+
},
883+
},
884+
},
885+
},
886+
},
887+
},
888+
expectValid: false,
889+
expectedErr: field.Error{
890+
Type: "FieldValueInvalid",
891+
Field: "spec.template.spec.networkSpec.privateDNSZoneResourceGroup",
892+
BadValue: "",
893+
Detail: "PrivateDNSZoneResourceGroup can only be used when PrivateDNSZoneName is provided",
894+
},
895+
},
896+
}
897+
898+
for _, c := range cases {
899+
tc := c
900+
t.Run(tc.name, func(t *testing.T) {
901+
t.Parallel()
902+
g := NewWithT(t)
903+
res := tc.clusterTemplate.validatePrivateDNSZoneResourceGroup()
904+
905+
if tc.expectValid {
906+
g.Expect(res).To(BeNil())
907+
} else {
908+
g.Expect(res).NotTo(BeNil())
909+
g.Expect(res).To(ContainElement(MatchError(tc.expectedErr.Error())))
910+
}
911+
})
912+
}
913+
}
914+
825915
func TestValidateNetworkSpec(t *testing.T) {
826916
cases := []struct {
827917
name string

api/v1beta1/types_class.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,11 @@ type NetworkClassSpec struct {
458458
// PrivateDNSZoneName defines the zone name for the Azure Private DNS.
459459
// +optional
460460
PrivateDNSZoneName string `json:"privateDNSZoneName,omitempty"`
461+
462+
// PrivateDNSZoneResourceGroup defines the resource group to be used for Azure Private DNS Zone.
463+
// If not specified, the resource group of the cluster will be used to create the Azure Private DNS Zone.
464+
// +optional
465+
PrivateDNSZoneResourceGroup string `json:"privateDNSZoneResourceGroup,omitempty"`
461466
}
462467

463468
// VnetClassSpec defines the VnetSpec properties that may be shared across several Azure clusters.

azure/scope/cluster.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -558,9 +558,13 @@ func (s *ClusterScope) VNetSpec() azure.ASOResourceSpecGetter[*asonetworkv1api20
558558
// PrivateDNSSpec returns the private dns zone spec.
559559
func (s *ClusterScope) PrivateDNSSpec() (zoneSpec azure.ResourceSpecGetter, linkSpec, recordSpec []azure.ResourceSpecGetter) {
560560
if s.IsAPIServerPrivate() {
561+
resourceGroup := s.ResourceGroup()
562+
if s.AzureCluster.Spec.NetworkSpec.PrivateDNSZoneResourceGroup != "" {
563+
resourceGroup = s.AzureCluster.Spec.NetworkSpec.PrivateDNSZoneResourceGroup
564+
}
561565
zone := privatedns.ZoneSpec{
562566
Name: s.GetPrivateDNSZoneName(),
563-
ResourceGroup: s.ResourceGroup(),
567+
ResourceGroup: resourceGroup,
564568
ClusterName: s.ClusterName(),
565569
AdditionalTags: s.AdditionalTags(),
566570
}
@@ -572,7 +576,7 @@ func (s *ClusterScope) PrivateDNSSpec() (zoneSpec azure.ResourceSpecGetter, link
572576
SubscriptionID: s.SubscriptionID(),
573577
VNetResourceGroup: s.Vnet().ResourceGroup,
574578
VNetName: s.Vnet().Name,
575-
ResourceGroup: s.ResourceGroup(),
579+
ResourceGroup: resourceGroup,
576580
ClusterName: s.ClusterName(),
577581
AdditionalTags: s.AdditionalTags(),
578582
}
@@ -583,7 +587,7 @@ func (s *ClusterScope) PrivateDNSSpec() (zoneSpec azure.ResourceSpecGetter, link
583587
SubscriptionID: s.SubscriptionID(),
584588
VNetResourceGroup: peering.ResourceGroup,
585589
VNetName: peering.RemoteVnetName,
586-
ResourceGroup: s.ResourceGroup(),
590+
ResourceGroup: resourceGroup,
587591
ClusterName: s.ClusterName(),
588592
AdditionalTags: s.AdditionalTags(),
589593
}
@@ -596,7 +600,7 @@ func (s *ClusterScope) PrivateDNSSpec() (zoneSpec azure.ResourceSpecGetter, link
596600
IP: s.APIServerPrivateIP(),
597601
},
598602
ZoneName: s.GetPrivateDNSZoneName(),
599-
ResourceGroup: s.ResourceGroup(),
603+
ResourceGroup: resourceGroup,
600604
}
601605

602606
return zone, links, records

config/crd/bases/infrastructure.cluster.x-k8s.io_azureclusters.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,11 @@ spec:
909909
description: PrivateDNSZoneName defines the zone name for the
910910
Azure Private DNS.
911911
type: string
912+
privateDNSZoneResourceGroup:
913+
description: |-
914+
PrivateDNSZoneResourceGroup defines the resource group to be used for Azure Private DNS Zone.
915+
If not specified, the resource group of the cluster will be used to create the Azure Private DNS Zone.
916+
type: string
912917
subnets:
913918
description: Subnets is the configuration for the control-plane
914919
subnet and the node subnet.

config/crd/bases/infrastructure.cluster.x-k8s.io_azureclustertemplates.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,11 @@ spec:
576576
description: PrivateDNSZoneName defines the zone name
577577
for the Azure Private DNS.
578578
type: string
579+
privateDNSZoneResourceGroup:
580+
description: |-
581+
PrivateDNSZoneResourceGroup defines the resource group to be used for Azure Private DNS Zone.
582+
If not specified, the resource group of the cluster will be used to create the Azure Private DNS Zone.
583+
type: string
579584
subnets:
580585
description: Subnets is the configuration for the control-plane
581586
subnet and the node subnet.

0 commit comments

Comments
 (0)