diff --git a/cloud/services/compute/subnets/reconcile.go b/cloud/services/compute/subnets/reconcile.go index 403c49fb7..2a186d6aa 100644 --- a/cloud/services/compute/subnets/reconcile.go +++ b/cloud/services/compute/subnets/reconcile.go @@ -43,7 +43,7 @@ func (s *Service) Delete(ctx context.Context) error { logger := log.FromContext(ctx) for _, subnetSpec := range s.scope.SubnetSpecs() { logger.V(2).Info("Deleting a subnet", "name", subnetSpec.Name) - subnetKey := meta.RegionalKey(subnetSpec.Name, s.scope.Region()) + subnetKey := meta.RegionalKey(subnetSpec.Name, s.getSubnetRegion(subnetSpec)) err := s.subnets.Delete(ctx, subnetKey) if err != nil && !gcperrors.IsNotFound(err) { logger.Error(err, "Error deleting subnet", "name", subnetSpec.Name) @@ -60,7 +60,7 @@ func (s *Service) createOrGetSubnets(ctx context.Context) ([]*compute.Subnetwork subnets := []*compute.Subnetwork{} for _, subnetSpec := range s.scope.SubnetSpecs() { logger.V(2).Info("Looking for subnet", "name", subnetSpec.Name) - subnetKey := meta.RegionalKey(subnetSpec.Name, s.scope.Region()) + subnetKey := meta.RegionalKey(subnetSpec.Name, s.getSubnetRegion(subnetSpec)) subnet, err := s.subnets.Get(ctx, subnetKey) if err != nil { if !gcperrors.IsNotFound(err) { @@ -86,3 +86,11 @@ func (s *Service) createOrGetSubnets(ctx context.Context) ([]*compute.Subnetwork return subnets, nil } + +// getSubnetRegion returns subnet region if user provided it, otherwise returns default scope region. +func (s *Service) getSubnetRegion(subnetSpec *compute.Subnetwork) string { + if subnetSpec.Region != "" { + return subnetSpec.Region + } + return s.scope.Region() +} diff --git a/cloud/services/container/clusters/reconcile.go b/cloud/services/container/clusters/reconcile.go index 4317e058b..5f307e64c 100644 --- a/cloud/services/container/clusters/reconcile.go +++ b/cloud/services/container/clusters/reconcile.go @@ -248,10 +248,10 @@ func (s *Service) createCluster(ctx context.Context, log *logr.Logger) error { } isRegional := shared.IsRegional(s.scope.Region()) - cluster := &containerpb.Cluster{ - Name: s.scope.ClusterName(), - Network: *s.scope.GCPManagedCluster.Spec.Network.Name, + Name: s.scope.ClusterName(), + Network: *s.scope.GCPManagedCluster.Spec.Network.Name, + Subnetwork: s.getSubnetNameInClusterRegion(), Autopilot: &containerpb.Autopilot{ Enabled: s.scope.GCPManagedControlPlane.Spec.EnableAutopilot, }, @@ -281,6 +281,16 @@ func (s *Service) createCluster(ctx context.Context, log *logr.Logger) error { return nil } +// getSubnetNameInClusterRegion returns the subnet which is in the same region as cluster. If not found it returns empty string. +func (s *Service) getSubnetNameInClusterRegion() string { + for _, subnet := range s.scope.GCPManagedCluster.Spec.Network.Subnets { + if subnet.Region == s.scope.Region() { + return subnet.Name + } + } + return "" +} + func (s *Service) updateCluster(ctx context.Context, updateClusterRequest *containerpb.UpdateClusterRequest, log *logr.Logger) error { _, err := s.scope.ManagedControlPlaneClient().UpdateCluster(ctx, updateClusterRequest) if err != nil { diff --git a/exp/api/v1beta1/gcpmanagedcluster_webhook.go b/exp/api/v1beta1/gcpmanagedcluster_webhook.go index 4b2c33844..09191bd69 100644 --- a/exp/api/v1beta1/gcpmanagedcluster_webhook.go +++ b/exp/api/v1beta1/gcpmanagedcluster_webhook.go @@ -20,6 +20,7 @@ import ( "github.com/google/go-cmp/cmp" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + kerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/validation/field" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -52,7 +53,7 @@ var _ webhook.Validator = &GCPManagedCluster{} func (r *GCPManagedCluster) ValidateCreate() error { gcpmanagedclusterlog.Info("validate create", "name", r.Name) - return nil + return r.validate() } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. @@ -95,3 +96,36 @@ func (r *GCPManagedCluster) ValidateDelete() error { return nil } + +func (r *GCPManagedCluster) validate() error { + validators := []func() error{ + r.validateCustomSubnet, + } + + var errs []error + for _, validator := range validators { + if err := validator(); err != nil { + errs = append(errs, err) + } + } + + return kerrors.NewAggregate(errs) +} + +func (r *GCPManagedCluster) validateCustomSubnet() error { + gcpmanagedclusterlog.Info("validate custom subnet", "name", r.Name) + if r.Spec.Network.AutoCreateSubnetworks == nil || *r.Spec.Network.AutoCreateSubnetworks { + return nil + } + var isSubnetExistInClusterRegion = false + for _, subnet := range r.Spec.Network.Subnets { + if subnet.Region == r.Spec.Region { + isSubnetExistInClusterRegion = true + } + } + + if !isSubnetExistInClusterRegion { + return field.Required(field.NewPath("spec", "network", "subnet"), "at least one given subnets region should be same as spec.network.region when spec.network.autoCreateSubnetworks is false") + } + return nil +}