From aaf3c68030a5dcf5d5de8ba72983adca2e65074f Mon Sep 17 00:00:00 2001 From: Bohdan Siryk Date: Wed, 9 Aug 2023 15:09:39 +0300 Subject: [PATCH] added primaryTargetCadence to Cadence spec and secondaryTargetCadence to its status --- apis/clusters/v1beta1/cadence_types.go | 56 ++++++++++++++++++- apis/clusters/v1beta1/cadence_webhook.go | 4 ++ .../clusters/v1beta1/zz_generated.deepcopy.go | 37 ++++++++++++ .../clusters.instaclustr.com_cadences.yaml | 24 ++++++++ config/samples/clusters_v1beta1_cadence.yaml | 10 +++- controllers/clusters/cadence_controller.go | 16 +++++- pkg/models/cadence_apiv2.go | 31 ++++++---- 7 files changed, 161 insertions(+), 17 deletions(-) diff --git a/apis/clusters/v1beta1/cadence_types.go b/apis/clusters/v1beta1/cadence_types.go index 9c67a5568..f1b3711c2 100644 --- a/apis/clusters/v1beta1/cadence_types.go +++ b/apis/clusters/v1beta1/cadence_types.go @@ -70,6 +70,7 @@ type CadenceSpec struct { StandardProvisioning []*StandardProvisioning `json:"standardProvisioning,omitempty"` SharedProvisioning []*SharedProvisioning `json:"sharedProvisioning,omitempty"` PackagedProvisioning []*PackagedProvisioning `json:"packagedProvisioning,omitempty"` + TargetPrimaryCadence []*TargetCadence `json:"targetPrimaryCadence,omitempty"` } type AWSArchival struct { @@ -110,6 +111,11 @@ type TargetOpenSearch struct { DependencyVPCType string `json:"dependencyVpcType"` } +type TargetCadence struct { + DependencyCDCID string `json:"dependencyCdcId"` + DependencyVPCType string `json:"dependencyVpcType"` +} + type AdvancedVisibility struct { TargetKafka *TargetKafka `json:"targetKafka"` TargetOpenSearch *TargetOpenSearch `json:"targetOpenSearch"` @@ -117,7 +123,8 @@ type AdvancedVisibility struct { // CadenceStatus defines the observed state of Cadence type CadenceStatus struct { - ClusterStatus `json:",inline"` + ClusterStatus `json:",inline"` + TargetSecondaryCadence []*TargetCadence `json:"targetSecondaryCadence,omitempty"` } //+kubebuilder:object:root=true @@ -180,6 +187,7 @@ func (cs *CadenceSpec) ToInstAPI(ctx context.Context, k8sClient client.Client) ( AWSArchival: awsArchival, SharedProvisioning: sharedProvisioning, StandardProvisioning: standardProvisioning, + TargetPrimaryCadence: cs.TargetCadenceToInstAPI(), }, nil } @@ -305,6 +313,18 @@ func (cd *CadenceDataCentre) privateLinkToInstAPI() (iPrivateLink []*models.Priv return } +func (cs *CadenceSpec) TargetCadenceToInstAPI() []*models.TargetCadence { + var targets []*models.TargetCadence + for _, target := range cs.TargetPrimaryCadence { + targets = append(targets, &models.TargetCadence{ + DependencyCDCID: target.DependencyCDCID, + DependencyVPCType: target.DependencyVPCType, + }) + } + + return targets +} + func (cs *CadenceSpec) DCsToInstAPI() (iDCs []*models.CadenceDataCentre) { for _, dc := range cs.DataCentres { iDCs = append(iDCs, dc.ToInstAPI()) @@ -385,9 +405,22 @@ func (cs *CadenceStatus) FromInstAPI(iCad *models.CadenceCluster) CadenceStatus CurrentClusterOperationStatus: iCad.CurrentClusterOperationStatus, MaintenanceEvents: cs.MaintenanceEvents, }, + TargetSecondaryCadence: cs.SecondaryTargetsFromInstAPI(iCad), } } +func (cs *CadenceStatus) SecondaryTargetsFromInstAPI(iCad *models.CadenceCluster) []*TargetCadence { + var targets []*TargetCadence + for _, target := range iCad.TargetSecondaryCadence { + targets = append(targets, &TargetCadence{ + DependencyCDCID: target.DependencyCDCID, + DependencyVPCType: target.DependencyVPCType, + }) + } + + return targets +} + func (cs *CadenceStatus) DCsFromInstAPI(iDCs []*models.CadenceDataCentre) (dcs []*DataCentreStatus) { for _, iDC := range iDCs { dcs = append(dcs, cs.ClusterStatus.DCFromInstAPI(iDC.DataCentre)) @@ -457,6 +490,11 @@ func (cs *CadenceSpec) validateUpdate(oldSpec CadenceSpec) error { return err } + err = cs.validateTargetsPrimaryCadence(&oldSpec) + if err != nil { + return err + } + return nil } @@ -554,6 +592,22 @@ func (cs *CadenceSpec) validatePackagedProvisioning(old []*PackagedProvisioning) return nil } +func (cs *CadenceSpec) validateTargetsPrimaryCadence(old *CadenceSpec) error { + if len(cs.TargetPrimaryCadence) != len(old.TargetPrimaryCadence) { + return fmt.Errorf("targetPrimaryCadence is immutable") + } + + for _, oldTarget := range old.TargetPrimaryCadence { + for _, newTarget := range cs.TargetPrimaryCadence { + if *oldTarget != *newTarget { + return fmt.Errorf("targetPrimaryCadence is immutable") + } + } + } + + return nil +} + func (cdc *CadenceDataCentre) newImmutableFields() *immutableCadenceDCFields { return &immutableCadenceDCFields{ immutableDC: immutableDC{ diff --git a/apis/clusters/v1beta1/cadence_webhook.go b/apis/clusters/v1beta1/cadence_webhook.go index b45283e7e..7adbd7b5f 100644 --- a/apis/clusters/v1beta1/cadence_webhook.go +++ b/apis/clusters/v1beta1/cadence_webhook.go @@ -161,6 +161,10 @@ func (cv *cadenceValidator) ValidateCreate(ctx context.Context, obj runtime.Obje } } + if len(c.Spec.TargetPrimaryCadence) > 1 { + return fmt.Errorf("targetPrimaryCadence array must consist of <= 1 elements") + } + if len(c.Spec.DataCentres) == 0 { return fmt.Errorf("data centres field is empty") } diff --git a/apis/clusters/v1beta1/zz_generated.deepcopy.go b/apis/clusters/v1beta1/zz_generated.deepcopy.go index 957868ae4..75e8dce4b 100644 --- a/apis/clusters/v1beta1/zz_generated.deepcopy.go +++ b/apis/clusters/v1beta1/zz_generated.deepcopy.go @@ -286,6 +286,17 @@ func (in *CadenceSpec) DeepCopyInto(out *CadenceSpec) { } } } + if in.TargetPrimaryCadence != nil { + in, out := &in.TargetPrimaryCadence, &out.TargetPrimaryCadence + *out = make([]*TargetCadence, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TargetCadence) + **out = **in + } + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CadenceSpec. @@ -302,6 +313,17 @@ func (in *CadenceSpec) DeepCopy() *CadenceSpec { func (in *CadenceStatus) DeepCopyInto(out *CadenceStatus) { *out = *in in.ClusterStatus.DeepCopyInto(&out.ClusterStatus) + if in.TargetSecondaryCadence != nil { + in, out := &in.TargetSecondaryCadence, &out.TargetSecondaryCadence + *out = make([]*TargetCadence, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TargetCadence) + **out = **in + } + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CadenceStatus. @@ -2044,6 +2066,21 @@ func (in *StandardProvisioning) DeepCopy() *StandardProvisioning { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetCadence) DeepCopyInto(out *TargetCadence) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetCadence. +func (in *TargetCadence) DeepCopy() *TargetCadence { + if in == nil { + return nil + } + out := new(TargetCadence) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetCassandra) DeepCopyInto(out *TargetCassandra) { *out = *in diff --git a/config/crd/bases/clusters.instaclustr.com_cadences.yaml b/config/crd/bases/clusters.instaclustr.com_cadences.yaml index a722f40e9..5443589e9 100644 --- a/config/crd/bases/clusters.instaclustr.com_cadences.yaml +++ b/config/crd/bases/clusters.instaclustr.com_cadences.yaml @@ -241,6 +241,18 @@ spec: - targetCassandra type: object type: array + targetPrimaryCadence: + items: + properties: + dependencyCdcId: + type: string + dependencyVpcType: + type: string + required: + - dependencyCdcId + - dependencyVpcType + type: object + type: array twoFactorDelete: items: properties: @@ -335,6 +347,18 @@ spec: type: object state: type: string + targetSecondaryCadence: + items: + properties: + dependencyCdcId: + type: string + dependencyVpcType: + type: string + required: + - dependencyCdcId + - dependencyVpcType + type: object + type: array twoFactorDeleteEnabled: type: boolean type: object diff --git a/config/samples/clusters_v1beta1_cadence.yaml b/config/samples/clusters_v1beta1_cadence.yaml index 1f1b19a19..9baa23444 100644 --- a/config/samples/clusters_v1beta1_cadence.yaml +++ b/config/samples/clusters_v1beta1_cadence.yaml @@ -42,11 +42,17 @@ spec: dataCentres: - region: "US_EAST_1" network: "10.12.0.0/16" + # if you use multi-region mode please provide + # non-overlapping CIDR block for the secondary mode cluster +# network: "10.16.0.0/16" cloudProvider: "AWS_VPC" name: "testdc" +# nodeSize: "CAD-PRD-m5ad.large-75" nodeSize: "CAD-DEV-t3.small-5" nodesNumber: 2 clientEncryption: false slaTier: "NON_PRODUCTION" - useCadenceWebAuth: true - + useCadenceWebAuth: false +# targetPrimaryCadence: +# - dependencyCdcId: "cce79be3-7f41-4cad-837c-86d3d8b4be77" +# dependencyVpcType: "SEPARATE_VPC" diff --git a/controllers/clusters/cadence_controller.go b/controllers/clusters/cadence_controller.go index 6b7d8a2b5..250f3f691 100644 --- a/controllers/clusters/cadence_controller.go +++ b/controllers/clusters/cadence_controller.go @@ -771,7 +771,7 @@ func (r *CadenceReconciler) newWatchStatusJob(cadence *v1beta1.Cadence) schedule if k8serrors.IsNotFound(err) { l.Info("Resource is not found in the k8s cluster. Closing Instaclustr status sync.", "namespaced name", namespacedName) - r.Scheduler.RemoveJob(cadence.GetJobID(scheduler.StatusChecker)) + go r.Scheduler.RemoveJob(cadence.GetJobID(scheduler.StatusChecker)) return nil } if err != nil { @@ -839,7 +839,8 @@ func (r *CadenceReconciler) newWatchStatusJob(cadence *v1beta1.Cadence) schedule return err } - if !areStatusesEqual(&iCadence.Status.ClusterStatus, &cadence.Status.ClusterStatus) { + if !areStatusesEqual(&iCadence.Status.ClusterStatus, &cadence.Status.ClusterStatus) || + !areSecondaryCadenceTargetsEqual(cadence.Status.TargetSecondaryCadence, iCadence.Status.TargetSecondaryCadence) { l.Info("Updating Cadence cluster status", "new status", iCadence.Status.ClusterStatus, "old status", cadence.Status.ClusterStatus, @@ -849,6 +850,7 @@ func (r *CadenceReconciler) newWatchStatusJob(cadence *v1beta1.Cadence) schedule patch := cadence.NewPatch() cadence.Status.ClusterStatus = iCadence.Status.ClusterStatus + cadence.Status.TargetSecondaryCadence = iCadence.Status.TargetSecondaryCadence err = r.Status().Patch(context.Background(), cadence, patch) if err != nil { l.Error(err, "Cannot patch Cadence cluster", @@ -1179,6 +1181,16 @@ func (r *CadenceReconciler) updateDescriptionAndTwoFactorDelete(cadence *v1beta1 return nil } +func areSecondaryCadenceTargetsEqual(k8sTargets, iTargets []*v1beta1.TargetCadence) bool { + for _, iTarget := range iTargets { + for _, k8sTarget := range k8sTargets { + return *iTarget == *k8sTarget + } + } + + return len(iTargets) == len(k8sTargets) +} + // SetupWithManager sets up the controller with the Manager. func (r *CadenceReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/pkg/models/cadence_apiv2.go b/pkg/models/cadence_apiv2.go index 09b0bc732..db859aaa5 100644 --- a/pkg/models/cadence_apiv2.go +++ b/pkg/models/cadence_apiv2.go @@ -26,18 +26,20 @@ const ( ) type CadenceCluster struct { - ClusterStatus `json:",inline"` - Name string `json:"name"` - CadenceVersion string `json:"cadenceVersion"` - DataCentres []*CadenceDataCentre `json:"dataCentres"` - SharedProvisioning []*CadenceSharedProvisioning `json:"sharedProvisioning,omitempty"` - StandardProvisioning []*CadenceStandardProvisioning `json:"standardProvisioning,omitempty"` - PCIComplianceMode bool `json:"pciComplianceMode"` - TwoFactorDelete []*TwoFactorDelete `json:"twoFactorDelete,omitempty"` - UseCadenceWebAuth bool `json:"useCadenceWebAuth"` - PrivateNetworkCluster bool `json:"privateNetworkCluster"` - SLATier string `json:"slaTier"` - AWSArchival []*AWSArchival `json:"awsArchival,omitempty"` + ClusterStatus `json:",inline"` + Name string `json:"name"` + CadenceVersion string `json:"cadenceVersion"` + DataCentres []*CadenceDataCentre `json:"dataCentres"` + SharedProvisioning []*CadenceSharedProvisioning `json:"sharedProvisioning,omitempty"` + StandardProvisioning []*CadenceStandardProvisioning `json:"standardProvisioning,omitempty"` + PCIComplianceMode bool `json:"pciComplianceMode"` + TwoFactorDelete []*TwoFactorDelete `json:"twoFactorDelete,omitempty"` + UseCadenceWebAuth bool `json:"useCadenceWebAuth"` + PrivateNetworkCluster bool `json:"privateNetworkCluster"` + SLATier string `json:"slaTier"` + AWSArchival []*AWSArchival `json:"awsArchival,omitempty"` + TargetPrimaryCadence []*TargetCadence `json:"targetPrimaryCadence,omitempty"` + TargetSecondaryCadence []*TargetCadence `json:"targetSecondaryCadence,omitempty"` } type CadenceDataCentre struct { @@ -75,6 +77,11 @@ type TargetCassandra struct { DependencyVPCType string `json:"dependencyVpcType"` } +type TargetCadence struct { + DependencyCDCID string `json:"dependencyCdcId"` + DependencyVPCType string `json:"dependencyVpcType"` +} + type AWSArchival struct { ArchivalS3Region string `json:"archivalS3Region,omitempty"` AWSAccessKeyID string `json:"awsAccessKeyId,omitempty"`