diff --git a/pkg/deploy/assets/cluster-development-predeploy.json b/pkg/deploy/assets/cluster-development-predeploy.json index 71c04a049ea..7cbc97a6bfb 100644 --- a/pkg/deploy/assets/cluster-development-predeploy.json +++ b/pkg/deploy/assets/cluster-development-predeploy.json @@ -58,7 +58,9 @@ }, { "properties": { - "addressPrefix": "[parameters('masterAddressPrefix')]", + "addressPrefixes": [ + "[parameters('masterAddressPrefix')]" + ], "routeTable": { "id": "[resourceid('Microsoft.Network/routeTables', concat(parameters('clusterName'), '-rt'))]", "tags": null diff --git a/pkg/deploy/generator/resources_cluster.go b/pkg/deploy/generator/resources_cluster.go index 3297fc92bc6..c60b5acc922 100644 --- a/pkg/deploy/generator/resources_cluster.go +++ b/pkg/deploy/generator/resources_cluster.go @@ -44,7 +44,9 @@ func (g *generator) clusterMasterSubnet() *arm.Resource { return &arm.Resource{ Resource: &mgmtnetwork.Subnet{ SubnetPropertiesFormat: &mgmtnetwork.SubnetPropertiesFormat{ - AddressPrefix: to.StringPtr("[parameters('masterAddressPrefix')]"), + AddressPrefixes: &[]string{ + *to.StringPtr("[parameters('masterAddressPrefix')]"), + }, RouteTable: &mgmtnetwork.RouteTable{ ID: to.StringPtr("[resourceid('Microsoft.Network/routeTables', concat(parameters('clusterName'), '-rt'))]"), }, diff --git a/pkg/validate/dynamic/dynamic.go b/pkg/validate/dynamic/dynamic.go index 5e8c3e72ed6..4b1df4bccca 100644 --- a/pkg/validate/dynamic/dynamic.go +++ b/pkg/validate/dynamic/dynamic.go @@ -51,6 +51,8 @@ var ( errMsgInvalidVNetLocation = "The vnet location '%s' must match the cluster location '%s'." ) +const minimumSubnetMaskSize int = 27 + type Subnet struct { // ID is a resource id of the subnet ID string @@ -764,23 +766,41 @@ func (dv *dynamic) ValidateSubnets(ctx context.Context, oc *api.OpenShiftCluster ) } - _, net, err := net.ParseCIDR(*ss.AddressPrefix) - if err != nil { - return err + // Handle both addressPrefix & addressPrefixes + if ss.AddressPrefix == nil { + for _, address := range *ss.AddressPrefixes { + if err = validateSubnetSize(s, address); err != nil { + return err + } + } + } else { + if err = validateSubnetSize(s, *ss.AddressPrefix); err != nil { + return err + } } + } - ones, _ := net.Mask.Size() - if ones > 27 { - return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidLinkedVNet, - s.Path, - errMsgSubnetInvalidSize, - s.ID, - ) - } + return nil +} + +// validateSubnetSize checks if the subnet mask is >27, and returns +// an error if so as it is too small for OCP +func validateSubnetSize(s Subnet, address string) error { + _, net, err := net.ParseCIDR(address) + if err != nil { + return err } + ones, _ := net.Mask.Size() + if ones > minimumSubnetMaskSize { + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidLinkedVNet, + s.Path, + errMsgSubnetInvalidSize, + s.ID, + ) + } return nil } diff --git a/pkg/validate/dynamic/dynamic_test.go b/pkg/validate/dynamic/dynamic_test.go index 1e69c6c86a1..d24190385a9 100644 --- a/pkg/validate/dynamic/dynamic_test.go +++ b/pkg/validate/dynamic/dynamic_test.go @@ -6,6 +6,7 @@ package dynamic import ( "context" "errors" + "fmt" "net/http" "testing" "time" @@ -1727,3 +1728,31 @@ func TestCheckBYONsg(t *testing.T) { }) } } + +func TestValidateSubnetSize(t *testing.T) { + subnetId := "id" + subnetPath := "path" + for _, tt := range []struct { + name string + address string + subnet Subnet + wantErr string + }{ + { + name: "subnet size is too small", + address: "10.0.0.0/32", + subnet: Subnet{ID: subnetId, Path: subnetPath}, + wantErr: fmt.Sprintf("400: InvalidLinkedVNet: %s: The provided subnet '%s' is invalid: must be /27 or larger.", subnetPath, subnetId), + }, + { + name: "subnet size is gucci gang", + address: "10.0.0.0/27", + subnet: Subnet{ID: subnetId, Path: subnetPath}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + err := validateSubnetSize(tt.subnet, tt.address) + utilerror.AssertErrorMessage(t, err, tt.wantErr) + }) + } +}