diff --git a/cloud/services/container/clusters/errors.go b/cloud/services/container/clusters/errors.go index 6b25e36b6..2d9fbabbd 100644 --- a/cloud/services/container/clusters/errors.go +++ b/cloud/services/container/clusters/errors.go @@ -23,6 +23,9 @@ import ( // ErrAutopilotClusterMachinePoolsNotAllowed is used when there are machine pools specified for an autopilot enabled cluster. var ErrAutopilotClusterMachinePoolsNotAllowed = errors.New("cannot use machine pools with an autopilot enabled cluster") +// ErrGCPManagedClusterHasNoNetworkDefined is used when there is no Network definition provided in a GCPManagedCluster. +var ErrGCPManagedClusterHasNoNetworkDefined = errors.New("cannot reconcile GCPManagedCluster if Network is not defined") + // NewErrUnexpectedClusterStatus creates a new error for an unexpected cluster status. func NewErrUnexpectedClusterStatus(status string) error { return &UnexpectedClusterStatusError{status} diff --git a/cloud/services/container/clusters/reconcile.go b/cloud/services/container/clusters/reconcile.go index 5a3f8cb08..edf56df4c 100644 --- a/cloud/services/container/clusters/reconcile.go +++ b/cloud/services/container/clusters/reconcile.go @@ -30,6 +30,7 @@ import ( "github.com/googleapis/gax-go/v2/apierror" "github.com/pkg/errors" "google.golang.org/grpc/codes" + infrav1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1" infrav1exp "sigs.k8s.io/cluster-api-provider-gcp/exp/api/v1beta1" "sigs.k8s.io/cluster-api-provider-gcp/util/reconciler" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -55,6 +56,14 @@ func (s *Service) Reconcile(ctx context.Context) (ctrl.Result, error) { s.scope.GCPManagedControlPlane.Status.Initialized = false s.scope.GCPManagedControlPlane.Status.Ready = false + if !isNetworkSpecValid(s.scope.GCPManagedCluster.Spec.Network) { + log.Error(ErrGCPManagedClusterHasNoNetworkDefined, "Error Reconciling GCPManagedControlPlane", "name", s.scope.ClusterName()) + conditions.MarkFalse(s.scope.ConditionSetter(), clusterv1.ReadyCondition, infrav1exp.GKEControlPlaneReconciliationFailedReason, clusterv1.ConditionSeverityError, ErrGCPManagedClusterHasNoNetworkDefined.Error()) + conditions.MarkFalse(s.scope.ConditionSetter(), infrav1exp.GKEControlPlaneReadyCondition, infrav1exp.GKEControlPlaneReconciliationFailedReason, clusterv1.ConditionSeverityError, ErrGCPManagedClusterHasNoNetworkDefined.Error()) + conditions.MarkFalse(s.scope.ConditionSetter(), infrav1exp.GKEControlPlaneCreatingCondition, infrav1exp.GKEControlPlaneReconciliationFailedReason, clusterv1.ConditionSeverityError, ErrGCPManagedClusterHasNoNetworkDefined.Error()) + return ctrl.Result{}, ErrGCPManagedClusterHasNoNetworkDefined + } + nodePools, _, err := s.scope.GetAllNodePools(ctx) if err != nil { conditions.MarkFalse(s.scope.ConditionSetter(), clusterv1.ReadyCondition, infrav1exp.GKEControlPlaneReconciliationFailedReason, clusterv1.ConditionSeverityError, err.Error()) @@ -449,3 +458,12 @@ func compareMasterAuthorizedNetworksConfig(a, b *containerpb.MasterAuthorizedNet } return true } + +// isNetworkSpecValid checks for the Name and Subnets field inside infrav1.NetworkSpec and returns true +// if both are present. As of now only checking for Name and Subnets field as that is what is being used in the createCluster flow. +func isNetworkSpecValid(network infrav1.NetworkSpec) bool { + if network.Name == nil || len(network.Subnets) == 0 { + return false + } + return true +}