diff --git a/apis/v1beta1/vsphereclusteridentity_types.go b/apis/v1beta1/vsphereclusteridentity_types.go index 502a1831e6..8691bc4a5b 100644 --- a/apis/v1beta1/vsphereclusteridentity_types.go +++ b/apis/v1beta1/vsphereclusteridentity_types.go @@ -28,6 +28,27 @@ const ( VSphereClusterIdentityFinalizer = "vsphereclusteridentity/infrastructure.cluster.x-k8s.io" ) +// VSphereClusterIdentity's CredentialsAvailable condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // VSphereClusterIdentityCredentialsAvailableV1Beta2Condition documents the status of the credentials for a VSphereClusterIdentity. + VSphereClusterIdentityCredentialsAvailableV1Beta2Condition = "CredentialsAvailable" + + // VSphereClusterIdentityCredentialsAvailableV1Beta2Reason surfaces when the VSphereClusterIdentity credentials are available. + VSphereClusterIdentityCredentialsAvailableV1Beta2Reason = clusterv1.AvailableV1Beta2Reason + + // VSphereClusterIdentityCredentialsSecretNotAvailableV1Beta2Reason surfaces when the VSphereClusterIdentity secret is not available. + VSphereClusterIdentityCredentialsSecretNotAvailableV1Beta2Reason = "SecretNotAvailable" + + // VSphereClusterIdentityCredentialsSecretAlreadyInUseV1Beta2Reason surfaces when the VSphereClusterIdentity secret is already in use. + VSphereClusterIdentityCredentialsSecretAlreadyInUseV1Beta2Reason = "SecretAlreadyInUse" + + // VSphereClusterIdentityCredentialsSettingSecretOwnerReferenceFailedV1Beta2Reason surfaces when setting the owner reference on the VSphereClusterIdentity secret failed. + VSphereClusterIdentityCredentialsSettingSecretOwnerReferenceFailedV1Beta2Reason = "SettingSecretOwnerReferenceFailed" + + // VSphereClusterIdentityCredentialsDeletingV1Beta2Reason surfaces when the credentials for a VSphereClusterIdentity are being deleted. + VSphereClusterIdentityCredentialsDeletingV1Beta2Reason = clusterv1.DeletingV1Beta2Reason +) + // VSphereClusterIdentitySpec contains a secret reference and a group of allowed namespaces. type VSphereClusterIdentitySpec struct { // SecretName references a Secret inside the controller namespace with the credentials to use @@ -59,7 +80,7 @@ type VSphereClusterIdentityStatus struct { // See https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more context. type VSphereClusterIdentityV1Beta2Status struct { // conditions represents the observations of a VSphereClusterIdentity's current state. - // Known condition types are Paused. + // Known condition types are CredentialsAvailable and Paused. // +optional // +listType=map // +listMapKey=type diff --git a/apis/v1beta1/vspheredeploymentzone_types.go b/apis/v1beta1/vspheredeploymentzone_types.go index cf9d7b847a..36a36dcbe3 100644 --- a/apis/v1beta1/vspheredeploymentzone_types.go +++ b/apis/v1beta1/vspheredeploymentzone_types.go @@ -28,6 +28,95 @@ const ( DeploymentZoneFinalizer = "vspheredeploymentzone.infrastructure.cluster.x-k8s.io" ) +// VSphereDeploymentZone's Ready condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // VSphereDeploymentZoneReadyV1Beta2Condition is true if the VSphereDeploymentZone's deletionTimestamp is not set, VSphereDeploymentZone's + // VCenterAvailable, PlacementConstraintReady and FailureDomainValidated conditions are true. + VSphereDeploymentZoneReadyV1Beta2Condition = clusterv1.ReadyV1Beta2Condition + + // VSphereDeploymentZoneReadyV1Beta2Reason surfaces when the VSphereDeploymentZone readiness criteria is met. + VSphereDeploymentZoneReadyV1Beta2Reason = clusterv1.ReadyV1Beta2Reason + + // VSphereDeploymentZoneNotReadyV1Beta2Reason surfaces when the VSphereDeploymentZone readiness criteria is not met. + VSphereDeploymentZoneNotReadyV1Beta2Reason = clusterv1.NotReadyV1Beta2Reason + + // VSphereDeploymentZoneReadyUnknownV1Beta2Reason surfaces when at least one VSphereDeploymentZone readiness criteria is unknown + // and no VSphereDeploymentZone readiness criteria is not met. + VSphereDeploymentZoneReadyUnknownV1Beta2Reason = clusterv1.ReadyUnknownV1Beta2Reason +) + +// VSphereDeploymentZone's VCenterAvailable condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // VSphereDeploymentZoneVCenterAvailableV1Beta2Condition documents the status of vCenter for a VSphereDeploymentZone. + VSphereDeploymentZoneVCenterAvailableV1Beta2Condition = "VCenterAvailable" + + // VSphereDeploymentZoneVCenterAvailableV1Beta2Reason surfaces when the vCenter for a VSphereDeploymentZone is available. + VSphereDeploymentZoneVCenterAvailableV1Beta2Reason = clusterv1.AvailableV1Beta2Reason + + // VSphereDeploymentZoneVCenterUnreachableV1Beta2Reason surfaces when the vCenter for a VSphereDeploymentZone is unreachable. + VSphereDeploymentZoneVCenterUnreachableV1Beta2Reason = "VCenterUnreachable" + + // VSphereDeploymentZoneVCenterAvailableDeletingV1Beta2Reason surfaces when the vCenter for a VSphereDeploymentZone is being deleted. + VSphereDeploymentZoneVCenterAvailableDeletingV1Beta2Reason = clusterv1.DeletingV1Beta2Reason +) + +// VSphereDeploymentZone's PlacementConstraintReady condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition documents the placement constraint status for a VSphereDeploymentZone. + VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition = "PlacementConstraintReady" + + // VSphereDeploymentZonePlacementConstraintReadyV1Beta2Reason surfaces when the placement status for a VSphereDeploymentZone is ready. + VSphereDeploymentZonePlacementConstraintReadyV1Beta2Reason = clusterv1.ReadyV1Beta2Reason + + // VSphereDeploymentZonePlacementConstraintResourcePoolNotFoundV1Beta2Reason surfaces when the resource pool for a VSphereDeploymentZone is not found. + VSphereDeploymentZonePlacementConstraintResourcePoolNotFoundV1Beta2Reason = "ResourcePoolNotFound" + + // VSphereDeploymentZonePlacementConstraintFolderNotFoundV1Beta2Reason surfaces when the folder for a VSphereDeploymentZone is not found. + VSphereDeploymentZonePlacementConstraintFolderNotFoundV1Beta2Reason = "FolderNotFound" + + // VSphereDeploymentZonePlacementConstraintDeletingV1Beta2Reason surfaces when the VSphereDeploymentZone is being deleted. + VSphereDeploymentZonePlacementConstraintDeletingV1Beta2Reason = clusterv1.DeletingV1Beta2Reason +) + +// VSphereDeploymentZone's FailureDomainValidated condition and corresponding reasons that will be used in v1Beta2 API version. +const ( + // VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition documents failure domain validation status for a VSphereDeploymentZone. + VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition = "FailureDomainValidated" + + // VSphereDeploymentZoneFailureDomainValidatedV1Beta2Reason surfaces when the failure domain for a VSphereDeploymentZone is validated. + VSphereDeploymentZoneFailureDomainValidatedV1Beta2Reason = "Validated" + + // VSphereDeploymentZoneFailureDomainValidationFailedV1Beta2Reason surfaces when the failure domain's validation for a VSphereDeploymentZone failed. + VSphereDeploymentZoneFailureDomainValidationFailedV1Beta2Reason = "ValidationFailed" + + // VSphereDeploymentZoneFailureDomainRegionMisconfiguredV1Beta2Reason surfaces when the failure domain's region for a VSphereDeploymentZone is misconfigured. + VSphereDeploymentZoneFailureDomainRegionMisconfiguredV1Beta2Reason = "RegionMisconfigured" + + // VSphereDeploymentZoneFailureDomainZoneMisconfiguredV1Beta2Reason surfaces when the failure domain's zone for a VSphereDeploymentZone is misconfigured. + VSphereDeploymentZoneFailureDomainZoneMisconfiguredV1Beta2Reason = "ZoneMisconfigured" + + // VSphereDeploymentZoneFailureDomainHostsMisconfiguredV1Beta2Reason surfaces when the failure domain's hosts for a VSphereDeploymentZone are misconfigured. + VSphereDeploymentZoneFailureDomainHostsMisconfiguredV1Beta2Reason = "HostsMisconfigured" + + // VSphereDeploymentZoneFailureDomainHostsAffinityMisconfiguredV1Beta2Reason surfaces when the failure domain's hosts for a VSphereDeploymentZone are misconfigured. + VSphereDeploymentZoneFailureDomainHostsAffinityMisconfiguredV1Beta2Reason = "HostsAffinityMisconfigured" + + // VSphereDeploymentZoneFailureDomainDatastoreNotFoundV1Beta2Reason surfaces when the failure domain's datastore for a VSphereDeploymentZone is not found. + VSphereDeploymentZoneFailureDomainDatastoreNotFoundV1Beta2Reason = "DatastoreNotFound" + + // VSphereDeploymentZoneFailureDomainNetworkNotFoundV1Beta2Reason surfaces when the failure domain's network for a VSphereDeploymentZone is not found. + VSphereDeploymentZoneFailureDomainNetworkNotFoundV1Beta2Reason = "NetworkNotFound" + + // VSphereDeploymentZoneFailureDomainComputeClusterNotFoundV1Beta2Reason surfaces when the failure domain's compute cluster for a VSphereDeploymentZone is not found. + VSphereDeploymentZoneFailureDomainComputeClusterNotFoundV1Beta2Reason = "ComputeClusterNotFound" + + // VSphereDeploymentZoneFailureDomainResourcePoolNotFoundV1Beta2Reason surfaces when the failure domain's resource pool for a VSphereDeploymentZone is not found. + VSphereDeploymentZoneFailureDomainResourcePoolNotFoundV1Beta2Reason = "ResourcePoolNotFound" + + // VSphereDeploymentZoneFailureDomainDeletingV1Beta2Reason surfaces when the VSphereDeploymentZone is being deleted. + VSphereDeploymentZoneFailureDomainDeletingV1Beta2Reason = clusterv1.DeletingV1Beta2Reason +) + // VSphereDeploymentZoneSpec defines the desired state of VSphereDeploymentZone. type VSphereDeploymentZoneSpec struct { @@ -93,7 +182,7 @@ type VSphereDeploymentZoneStatus struct { // See https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more context. type VSphereDeploymentZoneV1Beta2Status struct { // conditions represents the observations of a VSphereDeploymentZone's current state. - // Known condition types are Paused. + // Known condition types are Ready, VCenterAvailable, PlacementConstraintReady, FailureDomainValidated and Paused. // +optional // +listType=map // +listMapKey=type diff --git a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheredeploymentzones.yaml b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheredeploymentzones.yaml index 8f169ab54b..a2860cbc3f 100644 --- a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheredeploymentzones.yaml +++ b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheredeploymentzones.yaml @@ -371,7 +371,7 @@ spec: conditions: description: |- conditions represents the observations of a VSphereDeploymentZone's current state. - Known condition types are Paused. + Known condition types are Ready, VCenterAvailable, PlacementConstraintReady, FailureDomainValidated and Paused. items: description: Condition contains details for one aspect of the current state of this API Resource. diff --git a/controllers/vsphereclusteridentity_controller.go b/controllers/vsphereclusteridentity_controller.go index a00047c95a..ea54b8e2ee 100644 --- a/controllers/vsphereclusteridentity_controller.go +++ b/controllers/vsphereclusteridentity_controller.go @@ -30,6 +30,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" clusterutilv1 "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/conditions" + v1beta2conditions "sigs.k8s.io/cluster-api/util/conditions/v1beta2" "sigs.k8s.io/cluster-api/util/finalizers" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/paused" @@ -102,6 +103,7 @@ func (r clusterIdentityReconciler) Reconcile(ctx context.Context, req reconcile. if err := patchHelper.Patch(ctx, identity, patch.WithOwnedV1Beta2Conditions{Conditions: []string{ clusterv1.PausedV1Beta2Condition, + infrav1.VSphereClusterIdentityCredentialsAvailableV1Beta2Condition, }}); err != nil { reterr = kerrors.NewAggregate([]error{reterr, err}) } @@ -119,12 +121,24 @@ func (r clusterIdentityReconciler) Reconcile(ctx context.Context, req reconcile. } if err := r.Client.Get(ctx, secretKey, secret); err != nil { conditions.MarkFalse(identity, infrav1.CredentialsAvailableCondidtion, infrav1.SecretNotAvailableReason, clusterv1.ConditionSeverityWarning, err.Error()) + v1beta2conditions.Set(identity, metav1.Condition{ + Type: infrav1.VSphereClusterIdentityCredentialsAvailableV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereClusterIdentityCredentialsSecretNotAvailableV1Beta2Reason, + Message: err.Error(), + }) return reconcile.Result{}, errors.Wrapf(err, "failed to get Secret %s", klog.KRef(secretKey.Namespace, secretKey.Name)) } // If this secret is owned by a different VSphereClusterIdentity or a VSphereCluster, mark the identity as not ready and return an error. if !clusterutilv1.IsOwnedByObject(secret, identity) && pkgidentity.IsOwnedByIdentityOrCluster(secret.GetOwnerReferences()) { conditions.MarkFalse(identity, infrav1.CredentialsAvailableCondidtion, infrav1.SecretAlreadyInUseReason, clusterv1.ConditionSeverityError, "secret being used by another Cluster/VSphereIdentity") + v1beta2conditions.Set(identity, metav1.Condition{ + Type: infrav1.VSphereClusterIdentityCredentialsAvailableV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereClusterIdentityCredentialsSecretAlreadyInUseV1Beta2Reason, + Message: "secret being used by another Cluster/VSphereIdentity", + }) identity.Status.Ready = false return reconcile.Result{}, errors.New("secret being used by another Cluster/VSphereIdentity") } @@ -145,10 +159,22 @@ func (r clusterIdentityReconciler) Reconcile(ctx context.Context, req reconcile. err = r.Client.Update(ctx, secret) if err != nil { conditions.MarkFalse(identity, infrav1.CredentialsAvailableCondidtion, infrav1.SecretOwnerReferenceFailedReason, clusterv1.ConditionSeverityWarning, err.Error()) + v1beta2conditions.Set(identity, metav1.Condition{ + Type: infrav1.VSphereClusterIdentityCredentialsAvailableV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereClusterIdentityCredentialsSettingSecretOwnerReferenceFailedV1Beta2Reason, + Message: err.Error(), + }) return reconcile.Result{}, err } conditions.MarkTrue(identity, infrav1.CredentialsAvailableCondidtion) + v1beta2conditions.Set(identity, metav1.Condition{ + Type: infrav1.VSphereClusterIdentityCredentialsAvailableV1Beta2Condition, + Status: metav1.ConditionTrue, + Reason: infrav1.VSphereClusterIdentityCredentialsAvailableV1Beta2Reason, + }) + identity.Status.Ready = true return reconcile.Result{}, nil } @@ -160,6 +186,13 @@ func (r clusterIdentityReconciler) reconcileDelete(ctx context.Context, identity Namespace: r.ControllerManagerCtx.Namespace, Name: identity.Spec.SecretName, } + + v1beta2conditions.Set(identity, metav1.Condition{ + Type: infrav1.VSphereClusterIdentityCredentialsAvailableV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereClusterIdentityCredentialsDeletingV1Beta2Reason, + }) + err := r.Client.Get(ctx, secretKey, secret) if err != nil { if apierrors.IsNotFound(err) { diff --git a/controllers/vspheredeploymentzone_controller.go b/controllers/vspheredeploymentzone_controller.go index 7ebd6d3900..a1aef6cfe6 100644 --- a/controllers/vspheredeploymentzone_controller.go +++ b/controllers/vspheredeploymentzone_controller.go @@ -31,6 +31,7 @@ import ( clusterutilv1 "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/collections" "sigs.k8s.io/cluster-api/util/conditions" + v1beta2conditions "sigs.k8s.io/cluster-api/util/conditions/v1beta2" "sigs.k8s.io/cluster-api/util/finalizers" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/paused" @@ -122,7 +123,7 @@ func (r vsphereDeploymentZoneReconciler) Reconcile(ctx context.Context, request PatchHelper: patchHelper, } defer func() { - if err := vsphereDeploymentZoneContext.Patch(ctx); err != nil { + if err := r.patch(ctx, vsphereDeploymentZoneContext); err != nil { reterr = kerrors.NewAggregate([]error{reterr, err}) } }() @@ -134,6 +135,48 @@ func (r vsphereDeploymentZoneReconciler) Reconcile(ctx context.Context, request return ctrl.Result{}, r.reconcileNormal(ctx, vsphereDeploymentZoneContext) } +// Patch patches the VSphereDeploymentZone. +func (r vsphereDeploymentZoneReconciler) patch(ctx context.Context, vsphereDeploymentZoneContext *capvcontext.VSphereDeploymentZoneContext) error { + conditions.SetSummary(vsphereDeploymentZoneContext.VSphereDeploymentZone, + conditions.WithConditions( + infrav1.VCenterAvailableCondition, + infrav1.VSphereFailureDomainValidatedCondition, + infrav1.PlacementConstraintMetCondition, + ), + ) + + if err := v1beta2conditions.SetSummaryCondition(vsphereDeploymentZoneContext.VSphereDeploymentZone, vsphereDeploymentZoneContext.VSphereDeploymentZone, infrav1.VSphereDeploymentZoneReadyV1Beta2Condition, + v1beta2conditions.ForConditionTypes{ + infrav1.VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition, + infrav1.VSphereDeploymentZoneVCenterAvailableV1Beta2Condition, + infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + }, + // Using a custom merge strategy to override reasons applied during merge. + v1beta2conditions.CustomMergeStrategy{ + MergeStrategy: v1beta2conditions.DefaultMergeStrategy( + // Use custom reasons. + v1beta2conditions.ComputeReasonFunc(v1beta2conditions.GetDefaultComputeMergeReasonFunc( + infrav1.VSphereDeploymentZoneNotReadyV1Beta2Reason, + infrav1.VSphereDeploymentZoneReadyUnknownV1Beta2Reason, + infrav1.VSphereDeploymentZoneReadyV1Beta2Reason, + )), + ), + }, + ); err != nil { + return errors.Wrapf(err, "failed to set %s condition", infrav1.VSphereDeploymentZoneReadyV1Beta2Condition) + } + + return vsphereDeploymentZoneContext.PatchHelper.Patch(ctx, vsphereDeploymentZoneContext.VSphereDeploymentZone, + patch.WithOwnedV1Beta2Conditions{Conditions: []string{ + clusterv1.PausedV1Beta2Condition, + infrav1.VSphereDeploymentZoneReadyV1Beta2Condition, + infrav1.VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition, + infrav1.VSphereDeploymentZoneVCenterAvailableV1Beta2Condition, + infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + }}, + ) +} + func (r vsphereDeploymentZoneReconciler) reconcileNormal(ctx context.Context, deploymentZoneCtx *capvcontext.VSphereDeploymentZoneContext) error { failureDomain := &infrav1.VSphereFailureDomain{} failureDomainKey := client.ObjectKey{Name: deploymentZoneCtx.VSphereDeploymentZone.Spec.FailureDomain} @@ -144,17 +187,28 @@ func (r vsphereDeploymentZoneReconciler) reconcileNormal(ctx context.Context, de authSession, err := r.getVCenterSession(ctx, deploymentZoneCtx, failureDomain.Spec.Topology.Datacenter) if err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VCenterAvailableCondition, infrav1.VCenterUnreachableReason, clusterv1.ConditionSeverityError, err.Error()) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneVCenterAvailableV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneVCenterUnreachableV1Beta2Reason, + Message: err.Error(), + }) deploymentZoneCtx.VSphereDeploymentZone.Status.Ready = ptr.To(false) return err } + deploymentZoneCtx.AuthSession = authSession conditions.MarkTrue(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VCenterAvailableCondition) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneVCenterAvailableV1Beta2Condition, + Status: metav1.ConditionTrue, + Reason: infrav1.VSphereDeploymentZoneVCenterAvailableV1Beta2Reason, + }) if err := r.reconcilePlacementConstraint(ctx, deploymentZoneCtx); err != nil { deploymentZoneCtx.VSphereDeploymentZone.Status.Ready = ptr.To(false) return err } - conditions.MarkTrue(deploymentZoneCtx.VSphereDeploymentZone, infrav1.PlacementConstraintMetCondition) // reconcile the failure domain if err := r.reconcileFailureDomain(ctx, deploymentZoneCtx, failureDomain); err != nil { @@ -173,6 +227,12 @@ func (r vsphereDeploymentZoneReconciler) reconcilePlacementConstraint(ctx contex if resourcePool := placementConstraint.ResourcePool; resourcePool != "" { if _, err := deploymentZoneCtx.AuthSession.Finder.ResourcePool(ctx, resourcePool); err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.PlacementConstraintMetCondition, infrav1.ResourcePoolNotFoundReason, clusterv1.ConditionSeverityError, "resource pool %s is misconfigured", resourcePool) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZonePlacementConstraintResourcePoolNotFoundV1Beta2Reason, + Message: fmt.Sprintf("resource pool %s is misconfigured", resourcePool), + }) return errors.Wrapf(err, "failed to reconcile placement contraint: unable to find resource pool %s", resourcePool) } } @@ -180,9 +240,23 @@ func (r vsphereDeploymentZoneReconciler) reconcilePlacementConstraint(ctx contex if folder := placementConstraint.Folder; folder != "" { if _, err := deploymentZoneCtx.AuthSession.Finder.Folder(ctx, placementConstraint.Folder); err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.PlacementConstraintMetCondition, infrav1.FolderNotFoundReason, clusterv1.ConditionSeverityError, "folder %s is misconfigured", folder) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZonePlacementConstraintFolderNotFoundV1Beta2Reason, + Message: fmt.Sprintf("folder %s is misconfigured", folder), + }) return errors.Wrapf(err, "failed to reconcile placement contraint: unable to find folder %s", folder) } } + + conditions.MarkTrue(deploymentZoneCtx.VSphereDeploymentZone, infrav1.PlacementConstraintMetCondition) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition, + Status: metav1.ConditionTrue, + Reason: infrav1.VSphereDeploymentZonePlacementConstraintReadyV1Beta2Reason, + }) + return nil } @@ -228,6 +302,22 @@ func (r vsphereDeploymentZoneReconciler) getVCenterSession(ctx context.Context, func (r vsphereDeploymentZoneReconciler) reconcileDelete(ctx context.Context, deploymentZoneCtx *capvcontext.VSphereDeploymentZoneContext) error { log := ctrl.LoggerFrom(ctx) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneVCenterAvailableV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneVCenterAvailableDeletingV1Beta2Reason, + }) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZonePlacementConstraintReadyV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZonePlacementConstraintDeletingV1Beta2Reason, + }) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainDeletingV1Beta2Reason, + }) + machines := &clusterv1.MachineList{} if err := r.Client.List(ctx, machines); err != nil { return errors.Wrapf(err, "failed to list Machines") diff --git a/controllers/vspheredeploymentzone_controller_domain.go b/controllers/vspheredeploymentzone_controller_domain.go index 32052bf12f..5dc516d198 100644 --- a/controllers/vspheredeploymentzone_controller_domain.go +++ b/controllers/vspheredeploymentzone_controller_domain.go @@ -18,6 +18,7 @@ package controllers import ( "context" + "fmt" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,6 +26,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" clusterutilv1 "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/conditions" + v1beta2conditions "sigs.k8s.io/cluster-api/util/conditions/v1beta2" ctrl "sigs.k8s.io/controller-runtime" infrav1 "sigs.k8s.io/cluster-api-provider-vsphere/apis/v1beta1" @@ -38,12 +40,24 @@ func (r vsphereDeploymentZoneReconciler) reconcileFailureDomain(ctx context.Cont // verify the failure domain for the region if err := r.reconcileInfraFailureDomain(ctx, deploymentZoneCtx, vsphereFailureDomain, vsphereFailureDomain.Spec.Region); err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.RegionMisconfiguredReason, clusterv1.ConditionSeverityError, err.Error()) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainRegionMisconfiguredV1Beta2Reason, + Message: err.Error(), + }) return errors.Wrapf(err, "failed to reconcile failure domain: region is not configured correctly") } // verify the failure domain for the zone if err := r.reconcileInfraFailureDomain(ctx, deploymentZoneCtx, vsphereFailureDomain, vsphereFailureDomain.Spec.Zone); err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.ZoneMisconfiguredReason, clusterv1.ConditionSeverityError, err.Error()) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainZoneMisconfiguredV1Beta2Reason, + Message: err.Error(), + }) return errors.Wrapf(err, "failed to reconcile failure domain: zone is not configured correctly") } @@ -69,11 +83,22 @@ func (r vsphereDeploymentZoneReconciler) reconcileFailureDomain(ctx context.Cont UID: deploymentZoneCtx.VSphereDeploymentZone.UID, }) }); err != nil { + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainValidationFailedV1Beta2Reason, + Message: "failed to update owner references of failure domain", + }) return err } // Mark the VSphereDeploymentZone as having a valid VSphereFailureDomain. conditions.MarkTrue(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionTrue, + Reason: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Reason, + }) return nil } @@ -89,6 +114,12 @@ func (r vsphereDeploymentZoneReconciler) reconcileTopology(ctx context.Context, if datastore := topology.Datastore; datastore != "" { if _, err := deploymentZoneCtx.AuthSession.Finder.Datastore(ctx, datastore); err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.DatastoreNotFoundReason, clusterv1.ConditionSeverityError, "datastore %s is misconfigured", datastore) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainDatastoreNotFoundV1Beta2Reason, + Message: fmt.Sprintf("datastore %s is misconfigured", datastore), + }) return errors.Wrapf(err, "unable to find datastore %s", datastore) } } @@ -96,6 +127,12 @@ func (r vsphereDeploymentZoneReconciler) reconcileTopology(ctx context.Context, for _, network := range topology.Networks { if _, err := deploymentZoneCtx.AuthSession.Finder.Network(ctx, network); err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.NetworkNotFoundReason, clusterv1.ConditionSeverityError, "network %s is not found", network) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainNetworkNotFoundV1Beta2Reason, + Message: fmt.Sprintf("network %s is not found", network), + }) return errors.Wrapf(err, "unable to find network %s", network) } } @@ -103,23 +140,34 @@ func (r vsphereDeploymentZoneReconciler) reconcileTopology(ctx context.Context, for _, networkConfig := range topology.NetworkConfigurations { if _, err := deploymentZoneCtx.AuthSession.Finder.Network(ctx, networkConfig.NetworkName); err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.NetworkNotFoundReason, clusterv1.ConditionSeverityError, "network %s is not found", networkConfig.NetworkName) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainNetworkNotFoundV1Beta2Reason, + Message: fmt.Sprintf("network %s is not found", networkConfig.NetworkName), + }) return errors.Wrapf(err, "unable to find network %s", networkConfig.NetworkName) } } if hostPlacementInfo := topology.Hosts; hostPlacementInfo != nil { rule, err := cluster.VerifyAffinityRule(ctx, deploymentZoneCtx, *topology.ComputeCluster, hostPlacementInfo.HostGroupName, hostPlacementInfo.VMGroupName) - switch { - case err != nil: + if err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.HostsMisconfiguredReason, clusterv1.ConditionSeverityError, "vm host affinity does not exist") + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainHostsMisconfiguredV1Beta2Reason, + Message: "vm host affinity rule does not exist", + }) return err - case rule.Disabled(): + } + + if rule.Disabled() { ctrl.LoggerFrom(ctx).V(4).Info("WARNING: vm-host rule for the failure domain is disabled", "hostGroup", hostPlacementInfo.HostGroupName, "vmGroup", hostPlacementInfo.VMGroupName) - conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.HostsAffinityMisconfiguredReason, clusterv1.ConditionSeverityWarning, "vm host affinity is disabled") - default: - conditions.MarkTrue(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition) } } + return nil } @@ -132,6 +180,12 @@ func (r vsphereDeploymentZoneReconciler) reconcileComputeCluster(ctx context.Con ccr, err := deploymentZoneCtx.AuthSession.Finder.ClusterComputeResource(ctx, *computeCluster) if err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.ComputeClusterNotFoundReason, clusterv1.ConditionSeverityError, "compute cluster %s not found", *computeCluster) + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainComputeClusterNotFoundV1Beta2Reason, + Message: fmt.Sprintf("compute cluster %s not found", *computeCluster), + }) return errors.Wrap(err, "compute cluster not found") } @@ -144,10 +198,22 @@ func (r vsphereDeploymentZoneReconciler) reconcileComputeCluster(ctx context.Con ref, err := rp.Owner(ctx) if err != nil { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.ComputeClusterNotFoundReason, clusterv1.ConditionSeverityError, "resource pool owner not found") + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainComputeClusterNotFoundV1Beta2Reason, + Message: "resource pool owner not found", + }) return errors.Wrap(err, "unable to find owner compute resource") } if ref.Reference() != ccr.Reference() { conditions.MarkFalse(deploymentZoneCtx.VSphereDeploymentZone, infrav1.VSphereFailureDomainValidatedCondition, infrav1.ResourcePoolNotFoundReason, clusterv1.ConditionSeverityError, "resource pool is not owned by compute cluster") + v1beta2conditions.Set(deploymentZoneCtx.VSphereDeploymentZone, metav1.Condition{ + Type: infrav1.VSphereDeploymentZoneFailureDomainValidatedV1Beta2Condition, + Status: metav1.ConditionFalse, + Reason: infrav1.VSphereDeploymentZoneFailureDomainResourcePoolNotFoundV1Beta2Reason, + Message: "resource pool is not owned by compute cluster", + }) return errors.Errorf("compute cluster %s does not own resource pool %s", *computeCluster, resourcePool) } } diff --git a/pkg/context/vspheredeploymentzone_context.go b/pkg/context/vspheredeploymentzone_context.go index 30ffcac1a3..3e84cc4760 100644 --- a/pkg/context/vspheredeploymentzone_context.go +++ b/pkg/context/vspheredeploymentzone_context.go @@ -17,11 +17,8 @@ limitations under the License. package context import ( - "context" "fmt" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" infrav1 "sigs.k8s.io/cluster-api-provider-vsphere/apis/v1beta1" @@ -36,20 +33,6 @@ type VSphereDeploymentZoneContext struct { AuthSession *session.Session } -// Patch patches the VSphereDeploymentZone. -func (c *VSphereDeploymentZoneContext) Patch(ctx context.Context) error { - conditions.SetSummary(c.VSphereDeploymentZone, - conditions.WithConditions( - infrav1.VCenterAvailableCondition, - infrav1.VSphereFailureDomainValidatedCondition, - infrav1.PlacementConstraintMetCondition, - ), - ) - return c.PatchHelper.Patch(ctx, c.VSphereDeploymentZone, patch.WithOwnedV1Beta2Conditions{Conditions: []string{ - clusterv1.PausedV1Beta2Condition, - }}) -} - // String returns a string with the GroupVersionKind and name of the VSphereDeploymentZone. func (c *VSphereDeploymentZoneContext) String() string { return fmt.Sprintf("%s %s", c.VSphereDeploymentZone.GroupVersionKind(), c.VSphereDeploymentZone.Name)