From 32779b04510401f1071216364b25d6e455239b13 Mon Sep 17 00:00:00 2001 From: tengu-alt Date: Wed, 25 Oct 2023 17:57:03 +0300 Subject: [PATCH] clusterresources for postgres were migrated to reference pattern --- .../awssecuritygroupfirewallrule_types.go | 9 + .../v1beta1/awsvpcpeering_types.go | 21 +- .../v1beta1/awsvpcpeering_webhook.go | 4 - .../v1beta1/azurevnetpeering_types.go | 17 +- .../v1beta1/azurevnetpeering_webhook.go | 4 - .../v1beta1/clusterbackup_types.go | 11 +- .../clusternetworkfirewallrule_types.go | 9 + .../v1beta1/exclusionwindow_types.go | 20 +- .../v1beta1/gcpvpcpeering_types.go | 9 + .../v1beta1/maintenanceevents_types.go | 1 - apis/clusterresources/v1beta1/structs.go | 12 +- apis/clusters/v1beta1/cassandra_types.go | 2 +- apis/clusters/v1beta1/kafka_types.go | 2 +- apis/clusters/v1beta1/opensearch_types.go | 2 +- apis/clusters/v1beta1/postgresql_types.go | 23 +- apis/clusters/v1beta1/redis_types.go | 2 +- apis/clusters/v1beta1/structs.go | 2 +- .../clusters/v1beta1/zz_generated.deepcopy.go | 143 +++++++++--- ...str.com_awssecuritygroupfirewallrules.yaml | 5 +- ...ources.instaclustr.com_awsvpcpeerings.yaml | 5 +- ...ces.instaclustr.com_azurevnetpeerings.yaml | 5 +- ...ources.instaclustr.com_clusterbackups.yaml | 3 +- ...lustr.com_clusternetworkfirewallrules.yaml | 5 +- ...rces.instaclustr.com_exclusionwindows.yaml | 7 +- ...ources.instaclustr.com_gcpvpcpeerings.yaml | 5 +- ...ces.instaclustr.com_maintenanceevents.yaml | 3 - .../clusters.instaclustr.com_postgresqls.yaml | 87 ++++++++ ..._v1beta1_awssecuritygroupfirewallrule.yaml | 4 +- ...lusterresources_v1beta1_awsvpcpeering.yaml | 2 +- ...terresources_v1beta1_azurevnetpeering.yaml | 6 +- ...lusterresources_v1beta1_clusterbackup.yaml | 4 +- ...es_v1beta1_clusternetworkfirewallrule.yaml | 6 +- ...sterresources_v1beta1_exclusionwindow.yaml | 2 +- ...lusterresources_v1beta1_gcpvpcpeering.yaml | 6 +- ...erresources_v1beta1_maintenanceevents.yaml | 7 +- .../samples/clusters_v1beta1_postgresql.yaml | 30 ++- ...awssecuritygroupfirewallrule_controller.go | 71 +++--- ...curitygroupfirewallrule_controller_test.go | 18 +- .../awsvpcpeering_controller.go | 51 +++-- .../awsvpcpeering_controller_test.go | 13 +- .../azurevnetpeering_controller.go | 51 +++-- .../azurevnetpeering_controller_test.go | 13 +- .../clusterbackup_controller.go | 29 +-- .../clusternetworkfirewallrule_controller.go | 84 ++++---- ...sternetworkfirewallrule_controller_test.go | 14 +- .../exclusionwindow_controller.go | 52 +++-- .../gcpvpcpeering_controller.go | 55 +++-- .../gcpvpcpeering_controller_test.go | 13 +- controllers/clusterresources/helpers.go | 8 + .../postgresqluser_controller.go | 12 +- controllers/clusters/cassandra_controller.go | 8 +- controllers/clusters/kafka_controller.go | 8 +- controllers/clusters/opensearch_controller.go | 8 +- controllers/clusters/postgresql_controller.go | 204 +++++++++++++++++- controllers/clusters/redis_controller.go | 8 +- .../tests/cassandra_plus_users_test.go | 12 +- controllers/tests/helpers.go | 2 +- .../tests/opensearch_plus_users_test.go | 12 +- pkg/instaclustr/client.go | 170 ++++++++++++++- pkg/instaclustr/interfaces.go | 7 +- pkg/instaclustr/mock/client.go | 32 ++- pkg/models/operator.go | 3 + 62 files changed, 1092 insertions(+), 351 deletions(-) diff --git a/apis/clusterresources/v1beta1/awssecuritygroupfirewallrule_types.go b/apis/clusterresources/v1beta1/awssecuritygroupfirewallrule_types.go index 063af3fee..1d0d9a409 100644 --- a/apis/clusterresources/v1beta1/awssecuritygroupfirewallrule_types.go +++ b/apis/clusterresources/v1beta1/awssecuritygroupfirewallrule_types.go @@ -65,6 +65,15 @@ func (fr *AWSSecurityGroupFirewallRule) NewPatch() client.Patch { return client.MergeFrom(old) } +func (fr *AWSSecurityGroupFirewallRule) AttachToCluster(id string) { + fr.Status.ClusterID = id + fr.Status.ClusterEvent = models.CreatingEvent +} + +func (fr *AWSSecurityGroupFirewallRule) DetachFromCluster() { + fr.Status.ClusterEvent = models.DeletingEvent +} + func init() { SchemeBuilder.Register(&AWSSecurityGroupFirewallRule{}, &AWSSecurityGroupFirewallRuleList{}) } diff --git a/apis/clusterresources/v1beta1/awsvpcpeering_types.go b/apis/clusterresources/v1beta1/awsvpcpeering_types.go index bc9083fb9..13116d817 100644 --- a/apis/clusterresources/v1beta1/awsvpcpeering_types.go +++ b/apis/clusterresources/v1beta1/awsvpcpeering_types.go @@ -71,13 +71,21 @@ func (aws *AWSVPCPeering) NewPatch() client.Patch { return client.MergeFrom(old) } +func (aws *AWSVPCPeering) AttachToCluster(id string) { + aws.Status.CDCID = id + aws.Status.ClusterEvent = models.CreatingEvent +} + +func (aws *AWSVPCPeering) DetachFromCluster() { + aws.Status.ClusterEvent = models.DeletingEvent +} + func init() { SchemeBuilder.Register(&AWSVPCPeering{}, &AWSVPCPeeringList{}) } type immutableAWSVPCPeeringFields struct { specificFields specificAWSVPCPeeringFields - peering immutablePeeringFields } type specificAWSVPCPeeringFields struct { @@ -91,9 +99,6 @@ func (aws *AWSVPCPeeringSpec) newImmutableFields() *immutableAWSVPCPeeringFields peerAWSAccountID: aws.PeerAWSAccountID, peerRegion: aws.PeerRegion, }, - immutablePeeringFields{ - DataCentreID: aws.DataCentreID, - }, } } @@ -106,8 +111,7 @@ func (aws *AWSVPCPeeringSpec) ValidateUpdate(oldSpec AWSVPCPeeringSpec) error { return err } - if newImmutableFields.peering != oldImmutableFields.peering || - newImmutableFields.specificFields != oldImmutableFields.specificFields { + if newImmutableFields.specificFields != oldImmutableFields.specificFields { return fmt.Errorf("cannot update immutable fields: old spec: %+v: new spec: %+v", oldSpec, aws) } @@ -125,11 +129,6 @@ func (aws *AWSVPCPeeringSpec) Validate(availableRegions []string) error { return fmt.Errorf("VPC ID must begin with 'vpc-' and fit pattern: %s. %v", models.PeerVPCIDRegExp, err) } - dataCentreIDMatched, err := regexp.Match(models.UUIDStringRegExp, []byte(aws.DataCentreID)) - if !dataCentreIDMatched || err != nil { - return fmt.Errorf("data centre ID is a UUID formated string. It must fit the pattern: %s. %v", models.UUIDStringRegExp, err) - } - if !validation.Contains(aws.PeerRegion, availableRegions) { return fmt.Errorf("AWS Region to peer: %s is unavailable, available regions: %v", aws.PeerRegion, availableRegions) diff --git a/apis/clusterresources/v1beta1/awsvpcpeering_webhook.go b/apis/clusterresources/v1beta1/awsvpcpeering_webhook.go index edda260c7..af23491a3 100644 --- a/apis/clusterresources/v1beta1/awsvpcpeering_webhook.go +++ b/apis/clusterresources/v1beta1/awsvpcpeering_webhook.go @@ -67,10 +67,6 @@ func (r *AWSVPCPeering) ValidateCreate() error { return fmt.Errorf("peer AWS Account Region is empty") } - if r.Spec.DataCentreID == "" { - return fmt.Errorf("dataCentre ID is empty") - } - if r.Spec.PeerSubnets == nil { return fmt.Errorf("peer Subnets list is empty") } diff --git a/apis/clusterresources/v1beta1/azurevnetpeering_types.go b/apis/clusterresources/v1beta1/azurevnetpeering_types.go index 4de680fb0..e88f1f764 100644 --- a/apis/clusterresources/v1beta1/azurevnetpeering_types.go +++ b/apis/clusterresources/v1beta1/azurevnetpeering_types.go @@ -71,19 +71,20 @@ func (azure *AzureVNetPeering) NewPatch() client.Patch { return client.MergeFrom(old) } +func (azure *AzureVNetPeering) AttachToCluster(id string) { + azure.Status.CDCID = id + azure.Status.ClusterEvent = models.CreatingEvent +} + +func (azure *AzureVNetPeering) DetachFromCluster() { + azure.Status.ClusterEvent = models.DeletingEvent +} + func init() { SchemeBuilder.Register(&AzureVNetPeering{}, &AzureVNetPeeringList{}) } func (azure *AzureVNetPeeringSpec) Validate() error { - dataCentreIDMatched, err := regexp.Match(models.UUIDStringRegExp, []byte(azure.DataCentreID)) - if err != nil { - return err - } - if !dataCentreIDMatched { - return fmt.Errorf("data centre ID is a UUID formated string. It must fit the pattern: %s", models.UUIDStringRegExp) - } - for _, subnet := range azure.PeerSubnets { peerSubnetMatched, err := regexp.Match(models.PeerSubnetsRegExp, []byte(subnet)) if err != nil { diff --git a/apis/clusterresources/v1beta1/azurevnetpeering_webhook.go b/apis/clusterresources/v1beta1/azurevnetpeering_webhook.go index 39ab9c7e3..7be21f7a1 100644 --- a/apis/clusterresources/v1beta1/azurevnetpeering_webhook.go +++ b/apis/clusterresources/v1beta1/azurevnetpeering_webhook.go @@ -71,10 +71,6 @@ func (r *AzureVNetPeering) ValidateCreate() error { return fmt.Errorf("peer Subscription ID is empty") } - if r.Spec.DataCentreID == "" { - return fmt.Errorf("dataCentre ID is empty") - } - if r.Spec.PeerSubnets == nil { return fmt.Errorf("peer Subnets list is empty") } diff --git a/apis/clusterresources/v1beta1/clusterbackup_types.go b/apis/clusterresources/v1beta1/clusterbackup_types.go index be942299d..6e21d5912 100644 --- a/apis/clusterresources/v1beta1/clusterbackup_types.go +++ b/apis/clusterresources/v1beta1/clusterbackup_types.go @@ -27,7 +27,7 @@ import ( // ClusterBackupSpec defines the desired state of ClusterBackup type ClusterBackupSpec struct { - ClusterID string `json:"clusterId"` + ClusterID string `json:"clusterId,omitempty"` ClusterKind string `json:"clusterKind"` } @@ -37,6 +37,7 @@ type ClusterBackupStatus struct { Progress string `json:"progress,omitempty"` Start int `json:"start,omitempty"` End int `json:"end,omitempty"` + ClusterID string `json:"clusterID,omitempty"` } //+kubebuilder:object:root=true @@ -71,6 +72,14 @@ func (cbs *ClusterBackupStatus) UpdateStatus(instBackup *models.BackupEvent) { cbs.Progress = fmt.Sprintf("%f", instBackup.Progress) } +func (cb *ClusterBackup) AttachToCluster(id string) { + cb.Status.ClusterID = id +} + +func (cb *ClusterBackup) DetachFromCluster() { + +} + func init() { SchemeBuilder.Register(&ClusterBackup{}, &ClusterBackupList{}) } diff --git a/apis/clusterresources/v1beta1/clusternetworkfirewallrule_types.go b/apis/clusterresources/v1beta1/clusternetworkfirewallrule_types.go index af3573106..001303f14 100644 --- a/apis/clusterresources/v1beta1/clusternetworkfirewallrule_types.go +++ b/apis/clusterresources/v1beta1/clusternetworkfirewallrule_types.go @@ -65,6 +65,15 @@ func (fr *ClusterNetworkFirewallRule) NewPatch() client.Patch { return client.MergeFrom(old) } +func (fr *ClusterNetworkFirewallRule) AttachToCluster(id string) { + fr.Status.ClusterID = id + fr.Status.ClusterEvent = models.CreatingEvent +} + +func (fr *ClusterNetworkFirewallRule) DetachFromCluster() { + fr.Status.ClusterEvent = models.DeletingEvent +} + func init() { SchemeBuilder.Register(&ClusterNetworkFirewallRule{}, &ClusterNetworkFirewallRuleList{}) } diff --git a/apis/clusterresources/v1beta1/exclusionwindow_types.go b/apis/clusterresources/v1beta1/exclusionwindow_types.go index c4b9c3b9f..367b978d0 100644 --- a/apis/clusterresources/v1beta1/exclusionwindow_types.go +++ b/apis/clusterresources/v1beta1/exclusionwindow_types.go @@ -17,13 +17,14 @@ limitations under the License. package v1beta1 import ( + "github.com/instaclustr/operator/pkg/models" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) // ExclusionWindowSpec defines the desired state of ExclusionWindow type ExclusionWindowSpec struct { - ClusterID string `json:"clusterId"` + ClusterID string `json:"clusterId,omitempty"` DayOfWeek string `json:"dayOfWeek"` //+kubebuilder:validation:Minimum:=0 //+kubebuilder:validation:Maximum:=23 @@ -36,7 +37,9 @@ type ExclusionWindowSpec struct { // ExclusionWindowStatus defines the observed state of ExclusionWindow type ExclusionWindowStatus struct { - ID string `json:"id"` + ID string `json:"id,omitempty"` + ClusterID string `json:"clusterID,omitempty"` + ClusterEvent string `json:"clusterEvent,omitempty"` } //+kubebuilder:object:root=true @@ -60,11 +63,20 @@ type ExclusionWindowList struct { Items []ExclusionWindow `json:"items"` } +func (ew *ExclusionWindow) AttachToCluster(id string) { + ew.Status.ClusterID = id + ew.Status.ClusterEvent = models.CreatingEvent +} + +func (ew *ExclusionWindow) DetachFromCluster() { + ew.Status.ClusterEvent = models.DeletingEvent +} + func init() { SchemeBuilder.Register(&ExclusionWindow{}, &ExclusionWindowList{}) } -func (r *ExclusionWindow) NewPatch() client.Patch { - old := r.DeepCopy() +func (ew *ExclusionWindow) NewPatch() client.Patch { + old := ew.DeepCopy() return client.MergeFrom(old) } diff --git a/apis/clusterresources/v1beta1/gcpvpcpeering_types.go b/apis/clusterresources/v1beta1/gcpvpcpeering_types.go index 64e41d2b9..3dbcb013f 100644 --- a/apis/clusterresources/v1beta1/gcpvpcpeering_types.go +++ b/apis/clusterresources/v1beta1/gcpvpcpeering_types.go @@ -69,6 +69,15 @@ func (gcp *GCPVPCPeering) NewPatch() client.Patch { return client.MergeFrom(old) } +func (gcp *GCPVPCPeering) AttachToCluster(id string) { + gcp.Status.CDCID = id + gcp.Status.ClusterEvent = models.CreatingEvent +} + +func (gcp *GCPVPCPeering) DetachFromCluster() { + gcp.Status.ClusterEvent = models.DeletingEvent +} + func init() { SchemeBuilder.Register(&GCPVPCPeering{}, &GCPVPCPeeringList{}) } diff --git a/apis/clusterresources/v1beta1/maintenanceevents_types.go b/apis/clusterresources/v1beta1/maintenanceevents_types.go index 2c59bd541..ea830688d 100644 --- a/apis/clusterresources/v1beta1/maintenanceevents_types.go +++ b/apis/clusterresources/v1beta1/maintenanceevents_types.go @@ -23,7 +23,6 @@ import ( // MaintenanceEventsSpec defines the desired state of MaintenanceEvents type MaintenanceEventsSpec struct { - ClusterID string `json:"clusterId"` MaintenanceEventsReschedules []*MaintenanceEventReschedule `json:"maintenanceEventsReschedule"` } diff --git a/apis/clusterresources/v1beta1/structs.go b/apis/clusterresources/v1beta1/structs.go index 0670d6da4..e2a36c845 100644 --- a/apis/clusterresources/v1beta1/structs.go +++ b/apis/clusterresources/v1beta1/structs.go @@ -21,7 +21,7 @@ import ( ) type VPCPeeringSpec struct { - DataCentreID string `json:"cdcId"` + DataCentreID string `json:"cdcId,omitempty"` PeerSubnets []string `json:"peerSubnets"` } @@ -30,6 +30,8 @@ type PeeringStatus struct { StatusCode string `json:"statusCode,omitempty"` Name string `json:"name,omitempty"` FailureReason string `json:"failureReason,omitempty"` + CDCID string `json:"cdcid,omitempty"` + ClusterEvent string `json:"clusterEvent,omitempty"` } type PatchRequest struct { @@ -39,7 +41,7 @@ type PatchRequest struct { } type FirewallRuleSpec struct { - ClusterID string `json:"clusterId"` + ClusterID string `json:"clusterId,omitempty"` Type string `json:"type"` } @@ -47,10 +49,8 @@ type FirewallRuleStatus struct { ID string `json:"id,omitempty"` DeferredReason string `json:"deferredReason,omitempty"` Status string `json:"status,omitempty"` -} - -type immutablePeeringFields struct { - DataCentreID string + ClusterID string `json:"clusterID,omitempty"` + ClusterEvent string `json:"clusterEvent,omitempty"` } type SecretReference struct { diff --git a/apis/clusters/v1beta1/cassandra_types.go b/apis/clusters/v1beta1/cassandra_types.go index 060a043fd..8aadf677e 100644 --- a/apis/clusters/v1beta1/cassandra_types.go +++ b/apis/clusters/v1beta1/cassandra_types.go @@ -62,7 +62,7 @@ type CassandraSpec struct { PasswordAndUserAuth bool `json:"passwordAndUserAuth,omitempty"` Spark []*Spark `json:"spark,omitempty"` BundledUseOnly bool `json:"bundledUseOnly,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*NamespacedNameRef `json:"userRefs,omitempty"` //+kubebuilder:validate:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } diff --git a/apis/clusters/v1beta1/kafka_types.go b/apis/clusters/v1beta1/kafka_types.go index 2c6900c26..5ca8b3523 100644 --- a/apis/clusters/v1beta1/kafka_types.go +++ b/apis/clusters/v1beta1/kafka_types.go @@ -88,7 +88,7 @@ type KafkaSpec struct { KarapaceRestProxy []*KarapaceRestProxy `json:"karapaceRestProxy,omitempty"` KarapaceSchemaRegistry []*KarapaceSchemaRegistry `json:"karapaceSchemaRegistry,omitempty"` BundledUseOnly bool `json:"bundledUseOnly,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*NamespacedNameRef `json:"userRefs,omitempty"` Kraft []*Kraft `json:"kraft,omitempty"` } diff --git a/apis/clusters/v1beta1/opensearch_types.go b/apis/clusters/v1beta1/opensearch_types.go index 8856b2d98..d114b0ac4 100644 --- a/apis/clusters/v1beta1/opensearch_types.go +++ b/apis/clusters/v1beta1/opensearch_types.go @@ -53,7 +53,7 @@ type OpenSearchSpec struct { IndexManagementPlugin bool `json:"indexManagementPlugin,omitempty"` AlertingPlugin bool `json:"alertingPlugin,omitempty"` BundledUseOnly bool `json:"bundledUseOnly,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*NamespacedNameRef `json:"userRefs,omitempty"` //+kubuilder:validation:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } diff --git a/apis/clusters/v1beta1/postgresql_types.go b/apis/clusters/v1beta1/postgresql_types.go index 91924f5d7..c642de9f1 100644 --- a/apis/clusters/v1beta1/postgresql_types.go +++ b/apis/clusters/v1beta1/postgresql_types.go @@ -75,14 +75,25 @@ type PgRestoreFrom struct { type PgSpec struct { PgRestoreFrom *PgRestoreFrom `json:"pgRestoreFrom,omitempty"` Cluster `json:",inline"` - DataCentres []*PgDataCentre `json:"dataCentres,omitempty"` - ClusterConfigurations map[string]string `json:"clusterConfigurations,omitempty"` - SynchronousModeStrict bool `json:"synchronousModeStrict,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + DataCentres []*PgDataCentre `json:"dataCentres,omitempty"` + ClusterConfigurations map[string]string `json:"clusterConfigurations,omitempty"` + SynchronousModeStrict bool `json:"synchronousModeStrict,omitempty"` + UserRefs []*NamespacedNameRef `json:"userRefs,omitempty"` + ClusterResources ClusterResourceRefs `json:"clusterResources,omitempty"` //+kubebuilder:validate:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } +type ClusterResourceRefs struct { + ClusterBackups []*NamespacedNameRef `json:"clusterBackups,omitempty"` + ClusterNetworkFirewallRules []*NamespacedNameRef `json:"clusterNetworkFirewallRules,omitempty"` + AWSVPCPeerings []*NamespacedNameRef `json:"awsVPCPeerings,omitempty"` + AWSSecurityGroupFirewallRules []*NamespacedNameRef `json:"awsSecurityGroupFirewallRules,omitempty"` + ExclusionWindows []*NamespacedNameRef `json:"exclusionWindows,omitempty"` + GCPVPCPeerings []*NamespacedNameRef `json:"gcpVPCPeerings,omitempty"` + AzureVNetPeerings []*NamespacedNameRef `json:"azureVNetPeerings,omitempty"` +} + // PgStatus defines the observed state of PostgreSQL type PgStatus struct { ClusterStatus `json:",inline"` @@ -154,12 +165,11 @@ func (pg *PostgreSQL) NewBackupSpec(startTimestamp int) *clusterresourcesv1beta1 ObjectMeta: ctrl.ObjectMeta{ Name: models.PgBackupPrefix + pg.Status.ID + "-" + strconv.Itoa(startTimestamp), Namespace: pg.Namespace, - Annotations: map[string]string{models.StartTimestampAnnotation: strconv.Itoa(startTimestamp)}, + Annotations: map[string]string{models.StartTimestampAnnotation: strconv.Itoa(startTimestamp), models.ClusterIDAnnotation: pg.Status.ID}, Labels: map[string]string{models.ClusterIDLabel: pg.Status.ID}, Finalizers: []string{models.DeletionFinalizer}, }, Spec: clusterresourcesv1beta1.ClusterBackupSpec{ - ClusterID: pg.Status.ID, ClusterKind: models.PgClusterKind, }, } @@ -195,7 +205,6 @@ func (pgs *PgSpec) ToInstAPI() *models.PGCluster { PostgreSQLVersion: pgs.Version, DataCentres: pgs.DCsToInstAPI(), SynchronousModeStrict: pgs.SynchronousModeStrict, - Description: pgs.Description, PrivateNetworkCluster: pgs.PrivateNetworkCluster, SLATier: pgs.SLATier, TwoFactorDelete: pgs.TwoFactorDeletesToInstAPI(), diff --git a/apis/clusters/v1beta1/redis_types.go b/apis/clusters/v1beta1/redis_types.go index bd384d6b2..ed2a6a1df 100644 --- a/apis/clusters/v1beta1/redis_types.go +++ b/apis/clusters/v1beta1/redis_types.go @@ -69,7 +69,7 @@ type RedisSpec struct { //+kubebuilder:validation:MaxItems:=2 DataCentres []*RedisDataCentre `json:"dataCentres,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + UserRefs []*NamespacedNameRef `json:"userRefs,omitempty"` //+kubebuilder:validation:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } diff --git a/apis/clusters/v1beta1/structs.go b/apis/clusters/v1beta1/structs.go index 1ee503af5..301074037 100644 --- a/apis/clusters/v1beta1/structs.go +++ b/apis/clusters/v1beta1/structs.go @@ -711,7 +711,7 @@ func (cs *ClusterStatus) PrivateLinkStatusesEqual(iStatus *ClusterStatus) bool { return true } -type UserReference struct { +type NamespacedNameRef struct { Namespace string `json:"namespace"` Name string `json:"name"` } diff --git a/apis/clusters/v1beta1/zz_generated.deepcopy.go b/apis/clusters/v1beta1/zz_generated.deepcopy.go index eb4beebbd..902fd0e20 100644 --- a/apis/clusters/v1beta1/zz_generated.deepcopy.go +++ b/apis/clusters/v1beta1/zz_generated.deepcopy.go @@ -482,11 +482,11 @@ func (in *CassandraSpec) DeepCopyInto(out *CassandraSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*NamespacedNameRef, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(NamespacedNameRef) **out = **in } } @@ -586,6 +586,98 @@ func (in *ClusterManagerNodes) DeepCopy() *ClusterManagerNodes { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterResourceRefs) DeepCopyInto(out *ClusterResourceRefs) { + *out = *in + if in.ClusterBackups != nil { + in, out := &in.ClusterBackups, &out.ClusterBackups + *out = make([]*NamespacedNameRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NamespacedNameRef) + **out = **in + } + } + } + if in.ClusterNetworkFirewallRules != nil { + in, out := &in.ClusterNetworkFirewallRules, &out.ClusterNetworkFirewallRules + *out = make([]*NamespacedNameRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NamespacedNameRef) + **out = **in + } + } + } + if in.AWSVPCPeerings != nil { + in, out := &in.AWSVPCPeerings, &out.AWSVPCPeerings + *out = make([]*NamespacedNameRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NamespacedNameRef) + **out = **in + } + } + } + if in.AWSSecurityGroupFirewallRules != nil { + in, out := &in.AWSSecurityGroupFirewallRules, &out.AWSSecurityGroupFirewallRules + *out = make([]*NamespacedNameRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NamespacedNameRef) + **out = **in + } + } + } + if in.ExclusionWindows != nil { + in, out := &in.ExclusionWindows, &out.ExclusionWindows + *out = make([]*NamespacedNameRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NamespacedNameRef) + **out = **in + } + } + } + if in.GCPVPCPeerings != nil { + in, out := &in.GCPVPCPeerings, &out.GCPVPCPeerings + *out = make([]*NamespacedNameRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NamespacedNameRef) + **out = **in + } + } + } + if in.AzureVNetPeerings != nil { + in, out := &in.AzureVNetPeerings, &out.AzureVNetPeerings + *out = make([]*NamespacedNameRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NamespacedNameRef) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceRefs. +func (in *ClusterResourceRefs) DeepCopy() *ClusterResourceRefs { + if in == nil { + return nil + } + out := new(ClusterResourceRefs) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) { *out = *in @@ -1189,11 +1281,11 @@ func (in *KafkaSpec) DeepCopyInto(out *KafkaSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*NamespacedNameRef, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(NamespacedNameRef) **out = **in } } @@ -1297,6 +1389,21 @@ func (in *ManagedCluster) DeepCopy() *ManagedCluster { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NamespacedNameRef) DeepCopyInto(out *NamespacedNameRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedNameRef. +func (in *NamespacedNameRef) DeepCopy() *NamespacedNameRef { + if in == nil { + return nil + } + out := new(NamespacedNameRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Node) DeepCopyInto(out *Node) { *out = *in @@ -1520,11 +1627,11 @@ func (in *OpenSearchSpec) DeepCopyInto(out *OpenSearchSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*NamespacedNameRef, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(NamespacedNameRef) **out = **in } } @@ -1752,15 +1859,16 @@ func (in *PgSpec) DeepCopyInto(out *PgSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*NamespacedNameRef, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(NamespacedNameRef) **out = **in } } } + in.ClusterResources.DeepCopyInto(&out.ClusterResources) if in.ResizeSettings != nil { in, out := &in.ResizeSettings, &out.ResizeSettings *out = make([]*ResizeSettings, len(*in)) @@ -2053,11 +2161,11 @@ func (in *RedisSpec) DeepCopyInto(out *RedisSpec) { } if in.UserRefs != nil { in, out := &in.UserRefs, &out.UserRefs - *out = make([]*UserReference, len(*in)) + *out = make([]*NamespacedNameRef, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(UserReference) + *out = new(NamespacedNameRef) **out = **in } } @@ -2395,21 +2503,6 @@ func (in *TwoFactorDelete) DeepCopy() *TwoFactorDelete { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UserReference) DeepCopyInto(out *UserReference) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserReference. -func (in *UserReference) DeepCopy() *UserReference { - if in == nil { - return nil - } - out := new(UserReference) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Zookeeper) DeepCopyInto(out *Zookeeper) { *out = *in diff --git a/config/crd/bases/clusterresources.instaclustr.com_awssecuritygroupfirewallrules.yaml b/config/crd/bases/clusterresources.instaclustr.com_awssecuritygroupfirewallrules.yaml index 66567ac8c..1fb1b1672 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_awssecuritygroupfirewallrules.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_awssecuritygroupfirewallrules.yaml @@ -44,7 +44,6 @@ spec: type: type: string required: - - clusterId - securityGroupId - type type: object @@ -52,6 +51,10 @@ spec: description: AWSSecurityGroupFirewallRuleStatus defines the observed state of AWSSecurityGroupFirewallRule properties: + clusterEvent: + type: string + clusterID: + type: string deferredReason: type: string id: diff --git a/config/crd/bases/clusterresources.instaclustr.com_awsvpcpeerings.yaml b/config/crd/bases/clusterresources.instaclustr.com_awsvpcpeerings.yaml index 4bde626a6..56dfb9eb4 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_awsvpcpeerings.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_awsvpcpeerings.yaml @@ -48,7 +48,6 @@ spec: peerVpcId: type: string required: - - cdcId - peerAwsAccountId - peerSubnets - peerVpcId @@ -56,6 +55,10 @@ spec: status: description: AWSVPCPeeringStatus defines the observed state of AWSVPCPeering properties: + cdcid: + type: string + clusterEvent: + type: string failureReason: type: string id: diff --git a/config/crd/bases/clusterresources.instaclustr.com_azurevnetpeerings.yaml b/config/crd/bases/clusterresources.instaclustr.com_azurevnetpeerings.yaml index b29ca2c93..f9b08921d 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_azurevnetpeerings.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_azurevnetpeerings.yaml @@ -50,7 +50,6 @@ spec: peerVirtualNetworkName: type: string required: - - cdcId - peerResourceGroup - peerSubnets - peerSubscriptionId @@ -59,6 +58,10 @@ spec: status: description: AzureVNetPeeringStatus defines the observed state of AzureVNetPeering properties: + cdcid: + type: string + clusterEvent: + type: string failureReason: type: string id: diff --git a/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml b/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml index 8e9b8bc17..96e34a629 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml @@ -40,12 +40,13 @@ spec: clusterKind: type: string required: - - clusterId - clusterKind type: object status: description: ClusterBackupStatus defines the observed state of ClusterBackup properties: + clusterID: + type: string end: type: integer operationStatus: diff --git a/config/crd/bases/clusterresources.instaclustr.com_clusternetworkfirewallrules.yaml b/config/crd/bases/clusterresources.instaclustr.com_clusternetworkfirewallrules.yaml index 714e426fa..d5866887f 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_clusternetworkfirewallrules.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_clusternetworkfirewallrules.yaml @@ -44,7 +44,6 @@ spec: type: type: string required: - - clusterId - network - type type: object @@ -52,6 +51,10 @@ spec: description: ClusterNetworkFirewallRuleStatus defines the observed state of ClusterNetworkFirewallRule properties: + clusterEvent: + type: string + clusterID: + type: string deferredReason: type: string id: diff --git a/config/crd/bases/clusterresources.instaclustr.com_exclusionwindows.yaml b/config/crd/bases/clusterresources.instaclustr.com_exclusionwindows.yaml index 81d6e8ab1..010a1f2c6 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_exclusionwindows.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_exclusionwindows.yaml @@ -49,7 +49,6 @@ spec: minimum: 0 type: integer required: - - clusterId - dayOfWeek - durationInHours - startHour @@ -57,10 +56,12 @@ spec: status: description: ExclusionWindowStatus defines the observed state of ExclusionWindow properties: + clusterEvent: + type: string + clusterID: + type: string id: type: string - required: - - id type: object type: object served: true diff --git a/config/crd/bases/clusterresources.instaclustr.com_gcpvpcpeerings.yaml b/config/crd/bases/clusterresources.instaclustr.com_gcpvpcpeerings.yaml index 1fbad4150..f921d64e4 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_gcpvpcpeerings.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_gcpvpcpeerings.yaml @@ -46,7 +46,6 @@ spec: peerVpcNetworkName: type: string required: - - cdcId - peerProjectId - peerSubnets - peerVpcNetworkName @@ -54,6 +53,10 @@ spec: status: description: GCPVPCPeeringStatus defines the observed state of GCPVPCPeering properties: + cdcid: + type: string + clusterEvent: + type: string failureReason: type: string id: diff --git a/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml b/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml index e7a5ce71c..f1eac4d71 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml @@ -35,8 +35,6 @@ spec: spec: description: MaintenanceEventsSpec defines the desired state of MaintenanceEvents properties: - clusterId: - type: string maintenanceEventsReschedule: items: properties: @@ -50,7 +48,6 @@ spec: type: object type: array required: - - clusterId - maintenanceEventsReschedule type: object status: diff --git a/config/crd/bases/clusters.instaclustr.com_postgresqls.yaml b/config/crd/bases/clusters.instaclustr.com_postgresqls.yaml index 86fbce8df..0abe205c7 100644 --- a/config/crd/bases/clusters.instaclustr.com_postgresqls.yaml +++ b/config/crd/bases/clusters.instaclustr.com_postgresqls.yaml @@ -52,6 +52,93 @@ spec: additionalProperties: type: string type: object + clusterResources: + properties: + awsSecurityGroupFirewallRules: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + awsVPCPeerings: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + azureVNetPeerings: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + clusterBackups: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + clusterNetworkFirewallRules: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + exclusionWindows: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + gcpVPCPeerings: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + type: object dataCentres: items: properties: diff --git a/config/samples/clusterresources_v1beta1_awssecuritygroupfirewallrule.yaml b/config/samples/clusterresources_v1beta1_awssecuritygroupfirewallrule.yaml index d90f56e8c..81645a5ad 100644 --- a/config/samples/clusterresources_v1beta1_awssecuritygroupfirewallrule.yaml +++ b/config/samples/clusterresources_v1beta1_awssecuritygroupfirewallrule.yaml @@ -9,6 +9,6 @@ metadata: app.kubernetes.io/created-by: operator name: awssecuritygroupfirewallrule-sample spec: - securityGroupId: sg-0d681e2d0fe0f0a39 - clusterId: ef924204-3139-43e9-8e03-c29278e6eccd + securityGroupId: sg-0ab978e9e4f443cc8 +# clusterId: ef924204-3139-43e9-8e03-c29278e6eccd type: POSTGRESQL diff --git a/config/samples/clusterresources_v1beta1_awsvpcpeering.yaml b/config/samples/clusterresources_v1beta1_awsvpcpeering.yaml index b0112bdd7..6dfe90004 100644 --- a/config/samples/clusterresources_v1beta1_awsvpcpeering.yaml +++ b/config/samples/clusterresources_v1beta1_awsvpcpeering.yaml @@ -15,4 +15,4 @@ spec: - "192.168.0.0/16" peerVpcId: "vpc-87241ae1" peerRegion: "US_EAST_1" - cdcId: "85b26d7e-f8ff-4ce6-9fd1-b0d25e6659a9" \ No newline at end of file +# cdcId: "249e86af-7afa-4674-8fab-10250661c5b4" \ No newline at end of file diff --git a/config/samples/clusterresources_v1beta1_azurevnetpeering.yaml b/config/samples/clusterresources_v1beta1_azurevnetpeering.yaml index 69330698e..5e06b3d25 100644 --- a/config/samples/clusterresources_v1beta1_azurevnetpeering.yaml +++ b/config/samples/clusterresources_v1beta1_azurevnetpeering.yaml @@ -1,11 +1,11 @@ apiVersion: clusterresources.instaclustr.com/v1beta1 kind: AzureVNetPeering metadata: - name: azurevnetpeering-sample + name: azurevnetpeering-sample-trough spec: - cdcId: f8581465-098c-4576-9e52-ea8308a27d8a +# cdcId: f8581465-098c-4576-9e52-ea8308a27d8a peerResourceGroup: rnd peerSubnets: - 10.224.0.0/16 peerSubscriptionId: 1a2f3ab8-6815-49c5-a47e-b1a354b51240 - peerVirtualNetworkName: aks-vnet-17973335 + peerVirtualNetworkName: aks-vnet-17973335222666 diff --git a/config/samples/clusterresources_v1beta1_clusterbackup.yaml b/config/samples/clusterresources_v1beta1_clusterbackup.yaml index 5c1afa170..117f8716f 100644 --- a/config/samples/clusterresources_v1beta1_clusterbackup.yaml +++ b/config/samples/clusterresources_v1beta1_clusterbackup.yaml @@ -1,7 +1,7 @@ apiVersion: clusterresources.instaclustr.com/v1beta1 kind: ClusterBackup metadata: - name: clusterbackup-sample + name: clusterbackup-sample-two spec: - clusterId: 2ae611cf-ac91-4325-941c-a35c043f9c34 +# clusterId: 2ae611cf-ac91-4325-941c-a35c043f9c34 clusterKind: PostgreSQL \ No newline at end of file diff --git a/config/samples/clusterresources_v1beta1_clusternetworkfirewallrule.yaml b/config/samples/clusterresources_v1beta1_clusternetworkfirewallrule.yaml index b475ddf1f..d5412190c 100644 --- a/config/samples/clusterresources_v1beta1_clusternetworkfirewallrule.yaml +++ b/config/samples/clusterresources_v1beta1_clusternetworkfirewallrule.yaml @@ -9,6 +9,6 @@ metadata: app.kubernetes.io/created-by: operator name: clusternetworkfirewallrule-sample spec: - network: 62.212.64.19/32 - clusterId: 944cfe6b-441f-4c5a-865b-42fd40c7d816 - type: KAFKA + network: 54.198.214.167/32 +# clusterId: c47c6a1c-9e2b-4a5c-aa5e-7013bceecd09 + type: POSTGRESQL diff --git a/config/samples/clusterresources_v1beta1_exclusionwindow.yaml b/config/samples/clusterresources_v1beta1_exclusionwindow.yaml index 56abb7e9a..022f6821f 100644 --- a/config/samples/clusterresources_v1beta1_exclusionwindow.yaml +++ b/config/samples/clusterresources_v1beta1_exclusionwindow.yaml @@ -3,7 +3,7 @@ kind: ExclusionWindow metadata: name: exclusionwindow-sample spec: - clusterId: "4b453851-9002-475a-a603-f8fb1e0ae7df" +# clusterId: "d72b0c01-d263-40c7-8d3d-adb837602647" dayOfWeek: "MONDAY" startHour: 10 durationInHours: 40 diff --git a/config/samples/clusterresources_v1beta1_gcpvpcpeering.yaml b/config/samples/clusterresources_v1beta1_gcpvpcpeering.yaml index 7b6cd3318..1b2974d30 100644 --- a/config/samples/clusterresources_v1beta1_gcpvpcpeering.yaml +++ b/config/samples/clusterresources_v1beta1_gcpvpcpeering.yaml @@ -6,6 +6,6 @@ spec: cdcId: ab974700-1ba9-4fcd-8399-3dc83fc2a3c3 peerProjectId: netapp-hcl-seclab peerSubnets: - - 192.168.0.0/16 - - 172.16.0.0/16 - peerVpcNetworkName: hcl-seclab-client-vpc1 + - 192.169.0.0/16 + - 172.17.0.0/16 + peerVpcNetworkName: hcl-seclab-client-vpc57x diff --git a/config/samples/clusterresources_v1beta1_maintenanceevents.yaml b/config/samples/clusterresources_v1beta1_maintenanceevents.yaml index 307734950..63fad8fbd 100644 --- a/config/samples/clusterresources_v1beta1_maintenanceevents.yaml +++ b/config/samples/clusterresources_v1beta1_maintenanceevents.yaml @@ -3,9 +3,6 @@ kind: MaintenanceEvents metadata: name: maintenanceevents-sample spec: - clusterId: "9cf09a53-a09e-450a-ba7d-e98b3c724911" maintenanceEventsReschedule: - - scheduledStartTime: "2023-11-09T04:30:00Z" - maintenanceEventId: "0d25b466-bc22-44a8-b15d-8f92e815cb6e" - - scheduledStartTime: "2023-11-15T06:00:00Z" - maintenanceEventId: "d4806381-cd1e-48df-b9ba-70f9b0829c72" + - scheduledStartTime: "2023-11-09T02:30:00Z" + maintenanceEventId: "d9199351-8438-4da9-8828-ab7a0dde640e" diff --git a/config/samples/clusters_v1beta1_postgresql.yaml b/config/samples/clusters_v1beta1_postgresql.yaml index 11c3b5859..6759a2668 100644 --- a/config/samples/clusters_v1beta1_postgresql.yaml +++ b/config/samples/clusters_v1beta1_postgresql.yaml @@ -6,7 +6,7 @@ metadata: # annotations: # testAnnotation: test spec: - name: "username-test" + name: "oleksandr-pg" version: "15.4.0" dataCentres: - region: "US_WEST_2" @@ -45,6 +45,34 @@ spec: # userRefs: # - namespace: default # name: postgresqluser-sample + clusterResources: + clusterBackups: +# - namespace: default +# name: clusterbackup-sample +# - namespace: default +# name: clusterbackup-sample-two + clusterNetworkFirewallRules: +# - namespace: default +# name: clusternetworkfirewallrule-sample + awsVPCPeerings: +# - namespace: default +# name: awsvpcpeering-sample + awsSecurityGroupFirewallRules: +# - namespace: default +# name: awssecuritygroupfirewallrule-sample + exclusionWindows: +# - namespace: default +# name: exclusionwindow-sample + gcpVPCPeerings: +# - namespace: default +# name: gcpvpcpeering-sample +# - namespace: default +# name: gcpvpcpeering-sample-two + azureVNetPeerings: +# - namespace: default +# name: azurevnetpeering-sample +# - namespace: default +# name: azurevnetpeering-sample-trough privateNetworkCluster: false synchronousModeStrict: false # resizeSettings: diff --git a/controllers/clusterresources/awssecuritygroupfirewallrule_controller.go b/controllers/clusterresources/awssecuritygroupfirewallrule_controller.go index 11da47237..db6823732 100644 --- a/controllers/clusterresources/awssecuritygroupfirewallrule_controller.go +++ b/controllers/clusterresources/awssecuritygroupfirewallrule_controller.go @@ -74,16 +74,17 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) Reconcile(ctx context.Context, return models.ReconcileRequeue, err } - switch firewallRule.Annotations[models.ResourceStateAnnotation] { - case models.CreatingEvent: - reconcileResult := r.handleCreateFirewallRule(ctx, firewallRule, &l) - return reconcileResult, nil - case models.DeletingEvent: - reconcileResult := r.handleDeleteFirewallRule(ctx, firewallRule, &l) - return reconcileResult, nil - case models.GenericEvent: + if firewallRule.Status.ClusterEvent == models.CreatingEvent { + return r.handleCreateFirewallRule(ctx, firewallRule, &l), nil + } + + if firewallRule.Status.ClusterEvent == models.DeletingEvent { + return r.handleDeleteFirewallRule(ctx, firewallRule, &l), nil + } + + if firewallRule.Annotations[models.ResourceStateAnnotation] == models.GenericEvent { l.Info("AWS security group firewall rule event isn't handled", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, "request", req, "event", firewallRule.Annotations[models.ResourceStateAnnotation]) @@ -101,13 +102,13 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleCreateFirewallRule( if firewallRule.Status.ID == "" { l.Info( "Creating AWS security group firewall rule", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) patch := firewallRule.NewPatch() - firewallRuleStatus, err := r.API.CreateFirewallRule(instaclustr.AWSSecurityGroupFirewallRuleEndpoint, &firewallRule.Spec) + firewallRuleStatus, err := r.API.CreateAWSSecurityGroupFirewallRule(instaclustr.AWSSecurityGroupFirewallRuleEndpoint, &firewallRule.Spec, firewallRule.Status.ClusterID) if err != nil { l.Error( err, "Cannot create AWS security group firewall rule", @@ -127,6 +128,7 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleCreateFirewallRule( ) firewallRule.Status.FirewallRuleStatus = *firewallRuleStatus + firewallRule.Status.ClusterEvent = models.CreatedEvent err = r.Status().Patch(ctx, firewallRule, patch) if err != nil { l.Error(err, "Cannot patch AWS security group firewall rule status ", "ID", firewallRule.Status.ID) @@ -138,12 +140,11 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleCreateFirewallRule( return models.ReconcileRequeue } - firewallRule.Annotations[models.ResourceStateAnnotation] = models.CreatedEvent controllerutil.AddFinalizer(firewallRule, models.DeletionFinalizer) err = r.Patch(ctx, firewallRule, patch) if err != nil { l.Error(err, "Cannot patch AWS security group firewall rule", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) r.EventRecorder.Eventf( @@ -156,7 +157,7 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleCreateFirewallRule( l.Info( "AWS security group firewall rule resource has been created", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) } @@ -190,7 +191,7 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleDeleteFirewallRule( err := r.Patch(ctx, firewallRule, patch) if err != nil { l.Error(err, "Cannot patch AWS security group firewall rule metadata", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) @@ -206,7 +207,7 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleDeleteFirewallRule( if err != nil && !errors.Is(err, instaclustr.NotFound) { l.Error( err, "Cannot get AWS security group firewall rule status from the Instaclustr API", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) @@ -223,7 +224,7 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleDeleteFirewallRule( if err != nil { l.Error(err, "Cannot delete AWS security group firewall rule", "rule ID", firewallRule.Status.ID, - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) @@ -240,13 +241,24 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleDeleteFirewallRule( ) } + firewallRule.Status.ClusterEvent = models.DeletedEvent + err = r.Status().Patch(ctx, firewallRule, patch) + if err != nil { + l.Error(err, "Cannot patch AWS security group firewall rule status ", "ID", firewallRule.Status.ID) + r.EventRecorder.Eventf( + firewallRule, models.Warning, models.PatchFailed, + "Resource status patch is failed. Reason: %v", + err, + ) + return models.ReconcileRequeue + } + r.Scheduler.RemoveJob(firewallRule.GetJobID(scheduler.StatusChecker)) controllerutil.RemoveFinalizer(firewallRule, models.DeletionFinalizer) - firewallRule.Annotations[models.ResourceStateAnnotation] = models.DeletedEvent err = r.Patch(ctx, firewallRule, patch) if err != nil { l.Error(err, "Cannot patch AWS security group firewall rule metadata", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, "status", firewallRule.Status, ) @@ -260,7 +272,7 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) handleDeleteFirewallRule( } l.Info("AWS security group firewall rule has been deleted", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, "status", firewallRule.Status, ) @@ -354,32 +366,21 @@ func (r *AWSSecurityGroupFirewallRuleReconciler) SetupWithManager(mgr ctrl.Manag return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.AWSSecurityGroupFirewallRule{}, builder.WithPredicates(predicate.Funcs{ CreateFunc: func(event event.CreateEvent) bool { - if event.Object.GetDeletionTimestamp() != nil { - event.Object.GetAnnotations()[models.ResourceStateAnnotation] = models.DeletingEvent - return true - } - - event.Object.GetAnnotations()[models.ResourceStateAnnotation] = models.CreatingEvent return true }, UpdateFunc: func(event event.UpdateEvent) bool { newObj := event.ObjectNew.(*v1beta1.AWSSecurityGroupFirewallRule) - if newObj.Generation == event.ObjectOld.GetGeneration() { - return false - } + oldObj := event.ObjectOld.(*v1beta1.AWSSecurityGroupFirewallRule) - if newObj.DeletionTimestamp != nil { - event.ObjectNew.GetAnnotations()[models.ResourceStateAnnotation] = models.DeletingEvent + if oldObj.Status.ClusterEvent == "" && newObj.Status.ClusterEvent == models.CreatingEvent { return true } - if newObj.Status.ID == "" { - newObj.Annotations[models.ResourceStateAnnotation] = models.CreatingEvent + if newObj.Status.ClusterEvent == models.DeletingEvent { return true } - newObj.Annotations[models.ResourceStateAnnotation] = models.UpdatingEvent - return true + return false }, GenericFunc: func(genericEvent event.GenericEvent) bool { genericEvent.Object.GetAnnotations()[models.ResourceStateAnnotation] = models.GenericEvent diff --git a/controllers/clusterresources/awssecuritygroupfirewallrule_controller_test.go b/controllers/clusterresources/awssecuritygroupfirewallrule_controller_test.go index 522ddc8f1..d59f58d10 100644 --- a/controllers/clusterresources/awssecuritygroupfirewallrule_controller_test.go +++ b/controllers/clusterresources/awssecuritygroupfirewallrule_controller_test.go @@ -42,18 +42,26 @@ var _ = Describe("Successful creation of a AWS Security Group Firewall Rule reso ctx := context.Background() resource := v1beta1.AWSSecurityGroupFirewallRule{ ObjectMeta: metav1.ObjectMeta{ - Name: "awssgfwrule", - Namespace: "default", - Annotations: map[string]string{ - models.ResourceStateAnnotation: models.CreatingEvent, - }, + Name: "awssgfwrule", + Namespace: "default", + Annotations: map[string]string{}, }, Spec: awsSGFirewallRuleSpec, + Status: v1beta1.AWSSecurityGroupFirewallRuleStatus{ + FirewallRuleStatus: v1beta1.FirewallRuleStatus{ + ClusterID: "375e4d1c-2f77-4d02-a6f2-1af617ff2ab2", + }, + }, } It("Should create a AWS Security Group Firewall Rule resources", func() { Expect(k8sClient.Create(ctx, &resource)).Should(Succeed()) + patch := resource.NewPatch() + resource.Status.ClusterID = "375e4d1c-2f77-4d02-a6f2-1af617ff2ab2" + resource.Status.ClusterEvent = models.CreatingEvent + Expect(k8sClient.Status().Patch(ctx, &resource, patch)).Should(Succeed()) + By("Sending AWS Security Group Firewall Rule Specification to Instaclustr API v2") var awsSGFirewallRule v1beta1.AWSSecurityGroupFirewallRule Eventually(func() bool { diff --git a/controllers/clusterresources/awsvpcpeering_controller.go b/controllers/clusterresources/awsvpcpeering_controller.go index 7aa4708fd..ae0c95a14 100644 --- a/controllers/clusterresources/awsvpcpeering_controller.go +++ b/controllers/clusterresources/awsvpcpeering_controller.go @@ -72,13 +72,16 @@ func (r *AWSVPCPeeringReconciler) Reconcile(ctx context.Context, req ctrl.Reques return models.ReconcileRequeue, err } - switch aws.Annotations[models.ResourceStateAnnotation] { - case models.CreatingEvent: + if aws.Status.ClusterEvent == models.CreatingEvent { return r.handleCreatePeering(ctx, aws, l), nil + } + if aws.Status.ClusterEvent == models.DeletingEvent { + return r.handleDeletePeering(ctx, aws, l), nil + } + + switch aws.Annotations[models.ResourceStateAnnotation] { case models.UpdatingEvent: return r.handleUpdatePeering(ctx, aws, l), nil - case models.DeletingEvent: - return r.handleDeletePeering(ctx, aws, l), nil default: l.Info("event isn't handled", "AWS Account ID", aws.Spec.PeerAWSAccountID, @@ -103,7 +106,7 @@ func (r *AWSVPCPeeringReconciler) handleCreatePeering( "Region", aws.Spec.PeerRegion, ) - awsStatus, err := r.API.CreatePeering(instaclustr.AWSPeeringEndpoint, &aws.Spec) + awsStatus, err := r.API.CreateAWSVPCPeering(&aws.Spec, aws.Status.CDCID) if err != nil { l.Error( err, "cannot create AWS VPC Peering resource", @@ -125,6 +128,7 @@ func (r *AWSVPCPeeringReconciler) handleCreatePeering( patch := aws.NewPatch() aws.Status.PeeringStatus = *awsStatus + aws.Status.ClusterEvent = models.CreatedEvent err = r.Status().Patch(ctx, aws, patch) if err != nil { l.Error(err, "cannot patch AWS VPC Peering resource status", @@ -143,7 +147,6 @@ func (r *AWSVPCPeeringReconciler) handleCreatePeering( } controllerutil.AddFinalizer(aws, models.DeletionFinalizer) - aws.Annotations[models.ResourceStateAnnotation] = models.CreatedEvent err = r.Patch(ctx, aws, patch) if err != nil { l.Error(err, "cannot patch AWS VPC Peering resource metadata", @@ -286,7 +289,7 @@ func (r *AWSVPCPeeringReconciler) handleUpdatePeering( "AWS Account ID", aws.Spec.PeerAWSAccountID, "VPC ID", aws.Spec.PeerVPCID, "Region", aws.Spec.PeerRegion, - "AWS VPC Peering Data Centre ID", aws.Spec.DataCentreID, + "AWS VPC Peering Data Centre ID", aws.Status.CDCID, "AWS VPC Peering Status", aws.Status.PeeringStatus, ) @@ -342,8 +345,26 @@ func (r *AWSVPCPeeringReconciler) handleDeletePeering( r.Scheduler.RemoveJob(aws.GetJobID(scheduler.StatusChecker)) patch := aws.NewPatch() + + aws.Status.ClusterEvent = models.DeletedEvent + err = r.Status().Patch(ctx, aws, patch) + if err != nil { + l.Error(err, "cannot patch AWS VPC Peering resource status", + "AWS Peering ID", aws.Status.ID, + "AWS Account ID", aws.Spec.PeerAWSAccountID, + "VPC ID", aws.Spec.PeerVPCID, + "Region", aws.Spec.PeerRegion, + "AWS VPC Peering metadata", aws.ObjectMeta, + ) + r.EventRecorder.Eventf( + aws, models.Warning, models.PatchFailed, + "Resource status patch is failed. Reason: %v", + err, + ) + return models.ReconcileRequeue + } + controllerutil.RemoveFinalizer(aws, models.DeletionFinalizer) - aws.Annotations[models.ResourceStateAnnotation] = models.DeletedEvent err = r.Patch(ctx, aws, patch) if err != nil { l.Error(err, "cannot patch AWS VPC Peering resource metadata", @@ -365,7 +386,7 @@ func (r *AWSVPCPeeringReconciler) handleDeletePeering( "AWS VPC Peering ID", aws.Status.ID, "VPC ID", aws.Spec.PeerVPCID, "Region", aws.Spec.PeerRegion, - "AWS VPC Peering Data Centre ID", aws.Spec.DataCentreID, + "AWS VPC Peering Data Centre ID", aws.Status.CDCID, "AWS VPC Peering Status", aws.Status.PeeringStatus, ) @@ -473,21 +494,17 @@ func (r *AWSVPCPeeringReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.AWSVPCPeering{}, builder.WithPredicates(predicate.Funcs{ CreateFunc: func(event event.CreateEvent) bool { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.CreatingEvent}) - if event.Object.GetDeletionTimestamp() != nil { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.DeletingEvent}) - } return true }, UpdateFunc: func(event event.UpdateEvent) bool { newObj := event.ObjectNew.(*v1beta1.AWSVPCPeering) - if newObj.DeletionTimestamp != nil { - newObj.Annotations[models.ResourceStateAnnotation] = models.DeletingEvent + oldObj := event.ObjectOld.(*v1beta1.AWSVPCPeering) + + if oldObj.Status.ClusterEvent == "" && newObj.Status.ClusterEvent == models.CreatingEvent { return true } - if newObj.Status.ID == "" { - newObj.Annotations[models.ResourceStateAnnotation] = models.CreatingEvent + if newObj.Status.ClusterEvent == models.DeletingEvent { return true } diff --git a/controllers/clusterresources/awsvpcpeering_controller_test.go b/controllers/clusterresources/awsvpcpeering_controller_test.go index a014d8871..2c8a6d8b7 100644 --- a/controllers/clusterresources/awsvpcpeering_controller_test.go +++ b/controllers/clusterresources/awsvpcpeering_controller_test.go @@ -44,11 +44,9 @@ var _ = Describe("Successful creation of a AWS VPC Peering resource", func() { ctx := context.Background() resource := v1beta1.AWSVPCPeering{ ObjectMeta: metav1.ObjectMeta{ - Name: "awsvpcpeering", - Namespace: "default", - Annotations: map[string]string{ - models.ResourceStateAnnotation: models.CreatingEvent, - }, + Name: "awsvpcpeering", + Namespace: "default", + Annotations: map[string]string{}, }, Spec: awsVPCPeeringSpec, } @@ -56,6 +54,11 @@ var _ = Describe("Successful creation of a AWS VPC Peering resource", func() { It("Should create a AWS VPC Peering resources", func() { Expect(k8sClient.Create(ctx, &resource)).Should(Succeed()) + patch := resource.NewPatch() + resource.Status.CDCID = "375e4d1c-2f77-4d02-a6f2-1af617ff2ab2" + resource.Status.ClusterEvent = models.CreatingEvent + Expect(k8sClient.Status().Patch(ctx, &resource, patch)).Should(Succeed()) + By("Sending AWS VPC Peering Specification to Instaclustr API v2") var awsVPCPeering v1beta1.AWSVPCPeering Eventually(func() bool { diff --git a/controllers/clusterresources/azurevnetpeering_controller.go b/controllers/clusterresources/azurevnetpeering_controller.go index f3871baa9..06069425e 100644 --- a/controllers/clusterresources/azurevnetpeering_controller.go +++ b/controllers/clusterresources/azurevnetpeering_controller.go @@ -72,15 +72,17 @@ func (r *AzureVNetPeeringReconciler) Reconcile(ctx context.Context, req ctrl.Req return models.ReconcileRequeue, err } - switch azure.Annotations[models.ResourceStateAnnotation] { - case models.CreatingEvent: - return r.handleCreatePeering(ctx, azure, l), nil + if azure.Status.ClusterEvent == models.CreatingEvent { + return r.handleCreatePeering(ctx, azure, &l), nil + } + if azure.Status.ClusterEvent == models.DeletingEvent { + return r.handleDeletePeering(ctx, azure, &l), nil + } + + switch azure.Annotations[models.ResourceStateAnnotation] { case models.UpdatingEvent: return r.handleUpdatePeering(ctx, azure, &l), nil - - case models.DeletingEvent: - return r.handleDeletePeering(ctx, azure, &l), nil default: l.Info("event isn't handled", "Azure Subscription ID", azure.Spec.PeerSubscriptionID, @@ -96,7 +98,7 @@ func (r *AzureVNetPeeringReconciler) Reconcile(ctx context.Context, req ctrl.Req func (r *AzureVNetPeeringReconciler) handleCreatePeering( ctx context.Context, azure *v1beta1.AzureVNetPeering, - l logr.Logger, + l *logr.Logger, ) reconcile.Result { if azure.Status.ID == "" { l.Info( @@ -107,7 +109,7 @@ func (r *AzureVNetPeeringReconciler) handleCreatePeering( "Vnet Name", azure.Spec.PeerVirtualNetworkName, ) - azureStatus, err := r.API.CreatePeering(instaclustr.AzurePeeringEndpoint, &azure.Spec) + azureStatus, err := r.API.CreateAzureVNetPeering(&azure.Spec, azure.Status.CDCID) if err != nil { l.Error( err, "cannot create Azure VNet Peering resource", @@ -129,6 +131,7 @@ func (r *AzureVNetPeeringReconciler) handleCreatePeering( patch := azure.NewPatch() azure.Status.PeeringStatus = *azureStatus + azure.Status.ClusterEvent = models.CreatedEvent err = r.Status().Patch(ctx, azure, patch) if err != nil { l.Error(err, "cannot patch Azure VNet Peering resource status", @@ -146,7 +149,6 @@ func (r *AzureVNetPeeringReconciler) handleCreatePeering( } controllerutil.AddFinalizer(azure, models.DeletionFinalizer) - azure.Annotations[models.ResourceStateAnnotation] = models.CreatedEvent err = r.Patch(ctx, azure, patch) if err != nil { l.Error(err, "cannot patch Azure VNet Peering resource metadata", @@ -252,8 +254,25 @@ func (r *AzureVNetPeeringReconciler) handleDeletePeering( } patch := azure.NewPatch() + + azure.Status.ClusterEvent = models.DeletedEvent + err = r.Status().Patch(ctx, azure, patch) + if err != nil { + l.Error(err, "cannot patch Azure VNet Peering resource status", + "Azure Subscription ID", azure.Spec.PeerSubscriptionID, + "AD Object ID", azure.Spec.PeerADObjectID, + "Resource Group", azure.Spec.PeerResourceGroup, + "Vnet Name", azure.Spec.PeerVirtualNetworkName, + ) + r.EventRecorder.Eventf( + azure, models.Warning, models.PatchFailed, + "Resource status patch is failed. Reason: %v", + err, + ) + return models.ReconcileRequeue + } + controllerutil.RemoveFinalizer(azure, models.DeletionFinalizer) - azure.Annotations[models.ResourceStateAnnotation] = models.DeletedEvent err = r.Patch(ctx, azure, patch) if err != nil { l.Error(err, "cannot patch Azure VNet Peering resource metadata", @@ -372,21 +391,17 @@ func (r *AzureVNetPeeringReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.AzureVNetPeering{}, builder.WithPredicates(predicate.Funcs{ CreateFunc: func(event event.CreateEvent) bool { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.CreatingEvent}) - if event.Object.GetDeletionTimestamp() != nil { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.DeletingEvent}) - } return true }, UpdateFunc: func(event event.UpdateEvent) bool { newObj := event.ObjectNew.(*v1beta1.AzureVNetPeering) - if newObj.DeletionTimestamp != nil { - newObj.Annotations[models.ResourceStateAnnotation] = models.DeletingEvent + oldObj := event.ObjectOld.(*v1beta1.AzureVNetPeering) + + if oldObj.Status.ClusterEvent == "" && newObj.Status.ClusterEvent == models.CreatingEvent { return true } - if newObj.Status.ID == "" { - newObj.Annotations[models.ResourceStateAnnotation] = models.CreatingEvent + if newObj.Status.ClusterEvent == models.DeletingEvent { return true } diff --git a/controllers/clusterresources/azurevnetpeering_controller_test.go b/controllers/clusterresources/azurevnetpeering_controller_test.go index 22482be52..e7166868e 100644 --- a/controllers/clusterresources/azurevnetpeering_controller_test.go +++ b/controllers/clusterresources/azurevnetpeering_controller_test.go @@ -45,11 +45,9 @@ var _ = Describe("Successful creation of a Azure VNet Peering resource", func() ctx := context.Background() resource := v1beta1.AzureVNetPeering{ ObjectMeta: metav1.ObjectMeta{ - Name: "azurevnetpeering", - Namespace: "default", - Annotations: map[string]string{ - models.ResourceStateAnnotation: models.CreatingEvent, - }, + Name: "azurevnetpeering", + Namespace: "default", + Annotations: map[string]string{}, }, Spec: azureVNetPeeringSpec, } @@ -57,6 +55,11 @@ var _ = Describe("Successful creation of a Azure VNet Peering resource", func() It("Should create a Azure VNet Peering resources", func() { Expect(k8sClient.Create(ctx, &resource)).Should(Succeed()) + patch := resource.NewPatch() + resource.Status.CDCID = "375e4d1c-2f77-4d02-a6f2-1af617ff2ab2" + resource.Status.ClusterEvent = models.CreatingEvent + Expect(k8sClient.Status().Patch(ctx, &resource, patch)).Should(Succeed()) + By("Sending Azure VNet Peering Specification to Instaclustr API v2") var azureVNetPeering v1beta1.AzureVNetPeering Eventually(func() bool { diff --git a/controllers/clusterresources/clusterbackup_controller.go b/controllers/clusterresources/clusterbackup_controller.go index cbd72f2c6..d331d1c1f 100644 --- a/controllers/clusterresources/clusterbackup_controller.go +++ b/controllers/clusterresources/clusterbackup_controller.go @@ -77,11 +77,15 @@ func (r *ClusterBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reques patch := backup.NewPatch() - if backup.Labels[models.ClusterIDLabel] != backup.Spec.ClusterID { + if backup.Status.ClusterID == "" { + return models.ExitReconcile, nil + } + + if backup.Labels[models.ClusterIDLabel] != backup.Status.ClusterID { if backup.Labels == nil { - backup.Labels = map[string]string{models.ClusterIDLabel: backup.Spec.ClusterID} + backup.Labels = map[string]string{models.ClusterIDLabel: backup.Status.ClusterID} } else { - backup.Labels[models.ClusterIDLabel] = backup.Spec.ClusterID + backup.Labels[models.ClusterIDLabel] = backup.Status.ClusterID } err = r.Patch(ctx, backup, patch) if err != nil { @@ -98,11 +102,11 @@ func (r *ClusterBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } - backupsList, err := r.listClusterBackups(ctx, backup.Spec.ClusterID, backup.Namespace) + backupsList, err := r.listClusterBackups(ctx, backup.Status.ClusterID, backup.Namespace) if err != nil { logger.Error(err, "Cannot get cluster backups", "backup name", backup.Name, - "cluster ID", backup.Spec.ClusterID, + "cluster ID", backup.Status.ClusterID, ) r.EventRecorder.Eventf( @@ -118,11 +122,11 @@ func (r *ClusterBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reques clusterKind = models.PgAppKind } - iBackup, err := r.API.GetClusterBackups(backup.Spec.ClusterID, clusterKind) + iBackup, err := r.API.GetClusterBackups(backup.Status.ClusterID, clusterKind) if err != nil { logger.Error(err, "Cannot get cluster backups from Instaclustr", "backup name", backup.Name, - "cluster ID", backup.Spec.ClusterID, + "cluster ID", backup.Status.ClusterID, ) r.EventRecorder.Eventf( @@ -136,11 +140,11 @@ func (r *ClusterBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reques iBackupEvents := iBackup.GetBackupEvents(backup.Spec.ClusterKind) if len(iBackupEvents) < len(backupsList.Items) { - err = r.API.TriggerClusterBackup(backup.Spec.ClusterID, models.ClusterKindsMap[backup.Spec.ClusterKind]) + err = r.API.TriggerClusterBackup(backup.Status.ClusterID, models.ClusterKindsMap[backup.Spec.ClusterKind]) if err != nil { logger.Error(err, "Cannot trigger cluster backup", "backup name", backup.Name, - "cluster ID", backup.Spec.ClusterID, + "cluster ID", backup.Status.ClusterID, ) r.EventRecorder.Eventf( @@ -156,7 +160,7 @@ func (r *ClusterBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reques "Resource creation request is sent", ) logger.Info("New cluster backup request was sent", - "cluster ID", backup.Spec.ClusterID, + "cluster ID", backup.Status.ClusterID, ) } @@ -214,7 +218,7 @@ func (r *ClusterBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reques logger.Info("Cluster backup resource was reconciled", "backup name", backup.Name, - "cluster ID", backup.Spec.ClusterID, + "cluster ID", backup.Status.ClusterID, ) return models.ExitReconcile, nil @@ -239,7 +243,8 @@ func (r *ClusterBackupReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.ClusterBackup{}, builder.WithPredicates(predicate.Funcs{ UpdateFunc: func(event event.UpdateEvent) bool { - return false + newObj := event.ObjectNew.(*v1beta1.ClusterBackup) + return newObj.Status.ClusterID != "" }, })). Complete(r) diff --git a/controllers/clusterresources/clusternetworkfirewallrule_controller.go b/controllers/clusterresources/clusternetworkfirewallrule_controller.go index 2e8afe312..87cb7ad78 100644 --- a/controllers/clusterresources/clusternetworkfirewallrule_controller.go +++ b/controllers/clusterresources/clusternetworkfirewallrule_controller.go @@ -78,19 +78,24 @@ func (r *ClusterNetworkFirewallRuleReconciler) Reconcile(ctx context.Context, re return models.ReconcileRequeue, err } + if firewallRule.Status.ClusterID == "" { + return models.ExitReconcile, nil + } + + if firewallRule.Status.ClusterEvent == models.CreatingEvent { + return r.HandleCreateFirewallRule(ctx, firewallRule, &l), nil + } + + if firewallRule.Status.ClusterEvent == models.DeletingEvent { + return r.HandleDeleteFirewallRule(ctx, firewallRule, &l), nil + } + switch firewallRule.Annotations[models.ResourceStateAnnotation] { - case models.CreatingEvent: - reconcileResult := r.HandleCreateFirewallRule(ctx, firewallRule, &l) - return reconcileResult, nil case models.UpdatingEvent: - reconcileResult := r.HandleUpdateFirewallRule(ctx, firewallRule, &l) - return reconcileResult, nil - case models.DeletingEvent: - reconcileResult := r.HandleDeleteFirewallRule(ctx, firewallRule, &l) - return reconcileResult, nil + return r.HandleUpdateFirewallRule(ctx, firewallRule, &l), nil case models.GenericEvent: l.Info("Cluster network firewall rule event isn't handled", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, "request", req, "event", firewallRule.Annotations[models.ResourceStateAnnotation]) @@ -108,13 +113,13 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleCreateFirewallRule( if firewallRule.Status.ID == "" { l.Info( "Creating cluster network firewall rule", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) patch := firewallRule.NewPatch() - firewallRuleStatus, err := r.API.CreateFirewallRule(instaclustr.ClusterNetworkFirewallRuleEndpoint, &firewallRule.Spec) + firewallRuleStatus, err := r.API.CreateClusterNetworkFirewallRule(&firewallRule.Spec, firewallRule.Status.ClusterID) if err != nil { l.Error( err, "Cannot create cluster network firewall rule", @@ -134,7 +139,7 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleCreateFirewallRule( ) firewallRule.Status.FirewallRuleStatus = *firewallRuleStatus - + firewallRule.Status.ClusterEvent = models.CreatedEvent err = r.Status().Patch(ctx, firewallRule, patch) if err != nil { l.Error(err, "Cannot patch cluster network firewall rule status ", "ID", firewallRule.Status.ID) @@ -146,13 +151,12 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleCreateFirewallRule( return models.ReconcileRequeue } - firewallRule.Annotations[models.ResourceStateAnnotation] = models.CreatedEvent controllerutil.AddFinalizer(firewallRule, models.DeletionFinalizer) err = r.Patch(ctx, firewallRule, patch) if err != nil { l.Error(err, "Cannot patch cluster network firewall rule", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) r.EventRecorder.Eventf( @@ -165,7 +169,7 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleCreateFirewallRule( l.Info( "Cluster network firewall rule resource has been created", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) } @@ -196,7 +200,7 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleUpdateFirewallRule( l *logr.Logger, ) reconcile.Result { l.Info("Cluster network firewall rule update is not implemented", - "firewall rule ID", firewallRule.Spec.ClusterID, + "firewall rule ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) @@ -209,25 +213,12 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleDeleteFirewallRule( l *logr.Logger, ) reconcile.Result { patch := firewallRule.NewPatch() - err := r.Patch(ctx, firewallRule, patch) - if err != nil { - l.Error(err, "Cannot patch cluster network firewall rule metadata", - "cluster ID", firewallRule.Spec.ClusterID, - "type", firewallRule.Spec.Type, - ) - r.EventRecorder.Eventf( - firewallRule, models.Warning, models.PatchFailed, - "Resource patch is failed. Reason: %v", - err, - ) - return models.ReconcileRequeue - } status, err := r.API.GetFirewallRuleStatus(firewallRule.Status.ID, instaclustr.ClusterNetworkFirewallRuleEndpoint) if err != nil && !errors.Is(err, instaclustr.NotFound) { l.Error( err, "Cannot get cluster network firewall rule status from the Instaclustr API", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) r.EventRecorder.Eventf( @@ -243,7 +234,7 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleDeleteFirewallRule( if err != nil { l.Error(err, "Cannot delete cluster network firewall rule", "rule ID", firewallRule.Status.ID, - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, ) r.EventRecorder.Eventf( @@ -260,13 +251,24 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleDeleteFirewallRule( ) } + firewallRule.Status.ClusterEvent = models.DeletedEvent + err = r.Status().Patch(ctx, firewallRule, patch) + if err != nil { + l.Error(err, "Cannot patch cluster network firewall rule status ", "ID", firewallRule.Status.ID) + r.EventRecorder.Eventf( + firewallRule, models.Warning, models.PatchFailed, + "Resource status patch is failed. Reason: %v", + err, + ) + return models.ReconcileRequeue + } + r.Scheduler.RemoveJob(firewallRule.GetJobID(scheduler.StatusChecker)) controllerutil.RemoveFinalizer(firewallRule, models.DeletionFinalizer) - firewallRule.Annotations[models.ResourceStateAnnotation] = models.DeletedEvent err = r.Patch(ctx, firewallRule, patch) if err != nil { l.Error(err, "Cannot patch cluster network firewall rule metadata", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, "status", firewallRule.Status, ) @@ -279,7 +281,7 @@ func (r *ClusterNetworkFirewallRuleReconciler) HandleDeleteFirewallRule( } l.Info("Cluster network firewall rule has been deleted", - "cluster ID", firewallRule.Spec.ClusterID, + "cluster ID", firewallRule.Status.ClusterID, "type", firewallRule.Spec.Type, "status", firewallRule.Status, ) @@ -332,23 +334,17 @@ func (r *ClusterNetworkFirewallRuleReconciler) SetupWithManager(mgr ctrl.Manager return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.ClusterNetworkFirewallRule{}, builder.WithPredicates(predicate.Funcs{ CreateFunc: func(event event.CreateEvent) bool { - if event.Object.GetDeletionTimestamp() != nil { - event.Object.GetAnnotations()[models.ResourceStateAnnotation] = models.DeletingEvent - return true - } - - event.Object.GetAnnotations()[models.ResourceStateAnnotation] = models.CreatingEvent return true }, UpdateFunc: func(event event.UpdateEvent) bool { newObj := event.ObjectNew.(*v1beta1.ClusterNetworkFirewallRule) - if newObj.DeletionTimestamp != nil { - newObj.Annotations[models.ResourceStateAnnotation] = models.DeletingEvent + oldObj := event.ObjectOld.(*v1beta1.ClusterNetworkFirewallRule) + + if oldObj.Status.ClusterEvent == "" && newObj.Status.ClusterEvent == models.CreatingEvent { return true } - if newObj.Status.ID == "" { - newObj.Annotations[models.ResourceStateAnnotation] = models.CreatingEvent + if newObj.Status.ClusterEvent == models.DeletingEvent { return true } diff --git a/controllers/clusterresources/clusternetworkfirewallrule_controller_test.go b/controllers/clusterresources/clusternetworkfirewallrule_controller_test.go index 774b3dbd8..698030010 100644 --- a/controllers/clusterresources/clusternetworkfirewallrule_controller_test.go +++ b/controllers/clusterresources/clusternetworkfirewallrule_controller_test.go @@ -18,7 +18,6 @@ package clusterresources import ( "context" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -42,11 +41,9 @@ var _ = Describe("Successful creation of a Cluster Network Firewall Rule resourc ctx := context.Background() resource := v1beta1.ClusterNetworkFirewallRule{ ObjectMeta: metav1.ObjectMeta{ - Name: "clusternetworkfwrule", - Namespace: "default", - Annotations: map[string]string{ - models.ResourceStateAnnotation: models.CreatingEvent, - }, + Name: "clusternetworkfwrule", + Namespace: "default", + Annotations: map[string]string{}, }, Spec: clusterNetworkFirewallRuleSpec, } @@ -54,6 +51,11 @@ var _ = Describe("Successful creation of a Cluster Network Firewall Rule resourc It("Should create a Cluster Network Firewall Rule resources", func() { Expect(k8sClient.Create(ctx, &resource)).Should(Succeed()) + patch := resource.NewPatch() + resource.Status.ClusterID = "375e4d1c-2f77-4d02-a6f2-1af617ff2ab2" + resource.Status.ClusterEvent = models.CreatingEvent + Expect(k8sClient.Status().Patch(ctx, &resource, patch)).Should(Succeed()) + By("Sending Cluster Network Firewall Rule Specification to Instaclustr API v2") var clusterNetworkFirewallRule v1beta1.ClusterNetworkFirewallRule Eventually(func() bool { diff --git a/controllers/clusterresources/exclusionwindow_controller.go b/controllers/clusterresources/exclusionwindow_controller.go index d2600c2d2..2b556b7f2 100644 --- a/controllers/clusterresources/exclusionwindow_controller.go +++ b/controllers/clusterresources/exclusionwindow_controller.go @@ -74,14 +74,14 @@ func (r *ExclusionWindowReconciler) Reconcile(ctx context.Context, req ctrl.Requ return models.ReconcileRequeue, nil } - switch ew.Annotations[models.ResourceStateAnnotation] { + switch ew.Status.ClusterEvent { case models.CreatingEvent: return r.handleCreateWindow(ctx, ew, l), nil case models.DeletingEvent: return r.handleDeleteWindow(ctx, ew, l), nil default: l.Info("event isn't handled", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, "Request", req, "event", ew.Annotations[models.ResourceStateAnnotation]) @@ -97,11 +97,11 @@ func (r *ExclusionWindowReconciler) handleCreateWindow( if ew.Status.ID == "" { l.Info( "Creating Exclusion Window resource", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, ) - id, err := r.API.CreateExclusionWindow(ew.Spec.ClusterID, &ew.Spec) + id, err := r.API.CreateExclusionWindow(ew.Status.ClusterID, &ew.Spec) if err != nil { l.Error( err, "cannot create Exclusion Window resource", @@ -122,10 +122,11 @@ func (r *ExclusionWindowReconciler) handleCreateWindow( patch := ew.NewPatch() ew.Status.ID = id + ew.Status.ClusterEvent = models.CreatedEvent err = r.Status().Patch(ctx, ew, patch) if err != nil { l.Error(err, "cannot patch Exclusion Window resource status after creation", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, "Exclusion Window metadata", ew.ObjectMeta, ) @@ -138,11 +139,10 @@ func (r *ExclusionWindowReconciler) handleCreateWindow( } controllerutil.AddFinalizer(ew, models.DeletionFinalizer) - ew.Annotations[models.ResourceStateAnnotation] = models.CreatedEvent err = r.Patch(ctx, ew, patch) if err != nil { l.Error(err, "cannot patch Exclusion Window resource metadata with created event", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, "Exclusion Window metadata", ew.ObjectMeta, ) @@ -156,7 +156,7 @@ func (r *ExclusionWindowReconciler) handleCreateWindow( l.Info( "Exclusion Window resource was created", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, ) } @@ -172,7 +172,7 @@ func (r *ExclusionWindowReconciler) handleDeleteWindow( if err != nil && !errors.Is(err, instaclustr.NotFound) { l.Error( err, "cannot get Exclusion Window status from the Instaclustr API", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, ) r.EventRecorder.Eventf( @@ -187,7 +187,7 @@ func (r *ExclusionWindowReconciler) handleDeleteWindow( err = r.API.DeleteExclusionWindow(ew.Status.ID) if err != nil { l.Error(err, "cannot delete Exclusion Window resource", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, "Exclusion Window metadata", ew.ObjectMeta, ) @@ -205,12 +205,26 @@ func (r *ExclusionWindowReconciler) handleDeleteWindow( } patch := ew.NewPatch() + ew.Status.ClusterEvent = models.DeletedEvent + err = r.Status().Patch(ctx, ew, patch) + if err != nil { + l.Error(err, "cannot patch Exclusion Window resource status after creation", + "Cluster ID", ew.Status.ClusterID, + "Exclusion Window Spec", ew.Spec, + "Exclusion Window metadata", ew.ObjectMeta, + ) + r.EventRecorder.Eventf( + ew, models.Warning, models.PatchFailed, + "Status patch is failed after resource creation. Reason: %v", + err, + ) + return models.ReconcileRequeue + } controllerutil.RemoveFinalizer(ew, models.DeletionFinalizer) - ew.Annotations[models.ResourceStateAnnotation] = models.DeletedEvent err = r.Patch(ctx, ew, patch) if err != nil { l.Error(err, "cannot patch Exclusion Window resource metadata with deleted event", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, "Exclusion Window metadata", ew.ObjectMeta, ) @@ -223,7 +237,7 @@ func (r *ExclusionWindowReconciler) handleDeleteWindow( } l.Info("Exclusion Window has been deleted", - "Cluster ID", ew.Spec.ClusterID, + "Cluster ID", ew.Status.ClusterID, "Exclusion Window Spec", ew.Spec, "Exclusion Window Status", ew.Status, ) @@ -241,21 +255,17 @@ func (r *ExclusionWindowReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.ExclusionWindow{}, builder.WithPredicates(predicate.Funcs{ CreateFunc: func(event event.CreateEvent) bool { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.CreatingEvent}) - if event.Object.GetDeletionTimestamp() != nil { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.DeletingEvent}) - } return true }, UpdateFunc: func(event event.UpdateEvent) bool { newObj := event.ObjectNew.(*v1beta1.ExclusionWindow) - if newObj.DeletionTimestamp != nil { - newObj.Annotations[models.ResourceStateAnnotation] = models.DeletingEvent + oldObj := event.ObjectOld.(*v1beta1.ExclusionWindow) + + if oldObj.Status.ClusterEvent == "" && newObj.Status.ClusterEvent == models.CreatingEvent { return true } - if newObj.Status.ID == "" { - newObj.Annotations[models.ResourceStateAnnotation] = models.CreatingEvent + if newObj.Status.ClusterEvent == models.DeletingEvent { return true } diff --git a/controllers/clusterresources/gcpvpcpeering_controller.go b/controllers/clusterresources/gcpvpcpeering_controller.go index f2ce92a0d..ed1405efc 100644 --- a/controllers/clusterresources/gcpvpcpeering_controller.go +++ b/controllers/clusterresources/gcpvpcpeering_controller.go @@ -72,15 +72,16 @@ func (r *GCPVPCPeeringReconciler) Reconcile(ctx context.Context, req ctrl.Reques return models.ReconcileRequeue, err } - switch gcp.Annotations[models.ResourceStateAnnotation] { - case models.CreatingEvent: - return r.handleCreateCluster(ctx, gcp, l), nil + if gcp.Status.ClusterEvent == models.CreatingEvent { + return r.handleCreatePeering(ctx, gcp, l), nil + } + if gcp.Status.ClusterEvent == models.DeletingEvent { + return r.handleDeletePeering(ctx, gcp, l), nil + } + switch gcp.Annotations[models.ResourceStateAnnotation] { case models.UpdatingEvent: - return r.handleUpdateCluster(ctx, gcp, l), nil - - case models.DeletingEvent: - return r.handleDeleteCluster(ctx, gcp, l), nil + return r.handleUpdatePeering(ctx, gcp, l), nil default: l.Info("Event isn't handled", "project ID", gcp.Spec.PeerProjectID, @@ -91,7 +92,7 @@ func (r *GCPVPCPeeringReconciler) Reconcile(ctx context.Context, req ctrl.Reques } } -func (r *GCPVPCPeeringReconciler) handleCreateCluster( +func (r *GCPVPCPeeringReconciler) handleCreatePeering( ctx context.Context, gcp *v1beta1.GCPVPCPeering, l logr.Logger, @@ -103,7 +104,7 @@ func (r *GCPVPCPeeringReconciler) handleCreateCluster( "network name", gcp.Spec.PeerVPCNetworkName, ) - gcpStatus, err := r.API.CreatePeering(instaclustr.GCPPeeringEndpoint, &gcp.Spec) + gcpStatus, err := r.API.CreateGCPVPCPeering(&gcp.Spec, gcp.Status.CDCID) if err != nil { l.Error( err, "Cannot create GCP VPC Peering resource", @@ -125,6 +126,7 @@ func (r *GCPVPCPeeringReconciler) handleCreateCluster( patch := gcp.NewPatch() gcp.Status.PeeringStatus = *gcpStatus + gcp.Status.ClusterEvent = models.CreatedEvent err = r.Status().Patch(ctx, gcp, patch) if err != nil { l.Error(err, "Cannot patch GCP VPC Peering resource status", @@ -141,7 +143,6 @@ func (r *GCPVPCPeeringReconciler) handleCreateCluster( } controllerutil.AddFinalizer(gcp, models.DeletionFinalizer) - gcp.Annotations[models.ResourceStateAnnotation] = models.CreatedEvent err = r.Patch(ctx, gcp, patch) if err != nil { l.Error(err, "Cannot patch GCP VPC Peering resource metadata", @@ -184,7 +185,7 @@ func (r *GCPVPCPeeringReconciler) handleCreateCluster( return models.ExitReconcile } -func (r *GCPVPCPeeringReconciler) handleUpdateCluster( +func (r *GCPVPCPeeringReconciler) handleUpdatePeering( ctx context.Context, gcp *v1beta1.GCPVPCPeering, l logr.Logger, @@ -194,7 +195,7 @@ func (r *GCPVPCPeeringReconciler) handleUpdateCluster( return models.ExitReconcile } -func (r *GCPVPCPeeringReconciler) handleDeleteCluster( +func (r *GCPVPCPeeringReconciler) handleDeletePeering( ctx context.Context, gcp *v1beta1.GCPVPCPeering, l logr.Logger, @@ -203,7 +204,7 @@ func (r *GCPVPCPeeringReconciler) handleDeleteCluster( if err != nil && !errors.Is(err, instaclustr.NotFound) { l.Error( err, "Cannot get GCP VPC Peering status from the Instaclustr API", - "id", status.ID, + "id", gcp.Status.ID, "project ID", gcp.Spec.PeerProjectID, "network name", gcp.Spec.PeerVPCNetworkName, ) @@ -238,9 +239,23 @@ func (r *GCPVPCPeeringReconciler) handleDeleteCluster( } patch := gcp.NewPatch() + gcp.Status.ClusterEvent = models.DeletedEvent + err = r.Status().Patch(ctx, gcp, patch) + if err != nil { + l.Error(err, "Cannot patch GCP VPC Peering resource status", + "project ID", gcp.Spec.PeerProjectID, + "network name", gcp.Spec.PeerVPCNetworkName, + "metadata", gcp.ObjectMeta, + ) + r.EventRecorder.Eventf( + gcp, models.Warning, models.PatchFailed, + "Resource status patch is failed. Reason: %v", + err, + ) + return models.ReconcileRequeue + } r.Scheduler.RemoveJob(gcp.GetJobID(scheduler.StatusChecker)) controllerutil.RemoveFinalizer(gcp, models.DeletionFinalizer) - gcp.Annotations[models.ResourceStateAnnotation] = models.DeletedEvent err = r.Patch(ctx, gcp, patch) if err != nil { l.Error(err, "Cannot patch GCP VPC Peering resource metadata", @@ -355,21 +370,17 @@ func (r *GCPVPCPeeringReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.GCPVPCPeering{}, builder.WithPredicates(predicate.Funcs{ CreateFunc: func(event event.CreateEvent) bool { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.CreatingEvent}) - if event.Object.GetDeletionTimestamp() != nil { - event.Object.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.DeletingEvent}) - } return true }, UpdateFunc: func(event event.UpdateEvent) bool { newObj := event.ObjectNew.(*v1beta1.GCPVPCPeering) - if newObj.DeletionTimestamp != nil { - newObj.Annotations[models.ResourceStateAnnotation] = models.DeletingEvent + oldObj := event.ObjectOld.(*v1beta1.GCPVPCPeering) + + if oldObj.Status.ClusterEvent == "" && newObj.Status.ClusterEvent == models.CreatingEvent { return true } - if newObj.Status.ID == "" { - newObj.Annotations[models.ResourceStateAnnotation] = models.CreatingEvent + if newObj.Status.ClusterEvent == models.DeletingEvent { return true } diff --git a/controllers/clusterresources/gcpvpcpeering_controller_test.go b/controllers/clusterresources/gcpvpcpeering_controller_test.go index bcc3c116b..7fbc13694 100644 --- a/controllers/clusterresources/gcpvpcpeering_controller_test.go +++ b/controllers/clusterresources/gcpvpcpeering_controller_test.go @@ -43,11 +43,9 @@ var _ = Describe("Successful creation of a GCP VPC Peering resource", func() { ctx := context.Background() resource := v1beta1.GCPVPCPeering{ ObjectMeta: metav1.ObjectMeta{ - Name: "gcpvpcpeering", - Namespace: "default", - Annotations: map[string]string{ - models.ResourceStateAnnotation: models.CreatingEvent, - }, + Name: "gcpvpcpeering", + Namespace: "default", + Annotations: map[string]string{}, }, Spec: gcpVPCPeeringSpec, } @@ -55,6 +53,11 @@ var _ = Describe("Successful creation of a GCP VPC Peering resource", func() { It("Should create a GCP VPC Peering resources", func() { Expect(k8sClient.Create(ctx, &resource)).Should(Succeed()) + patch := resource.NewPatch() + resource.Status.CDCID = "375e4d1c-2f77-4d02-a6f2-1af617ff2ab2" + resource.Status.ClusterEvent = models.CreatingEvent + Expect(k8sClient.Status().Patch(ctx, &resource, patch)).Should(Succeed()) + By("Sending GCP VPC Peering Specification to Instaclustr API v2") var gcpVNetPeering v1beta1.GCPVPCPeering Eventually(func() bool { diff --git a/controllers/clusterresources/helpers.go b/controllers/clusterresources/helpers.go index 799ae9bae..12315255f 100644 --- a/controllers/clusterresources/helpers.go +++ b/controllers/clusterresources/helpers.go @@ -21,6 +21,7 @@ import ( k8sCore "k8s.io/api/core/v1" "k8s.io/utils/strings/slices" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/instaclustr/operator/apis/clusterresources/v1beta1" "github.com/instaclustr/operator/pkg/instaclustr" @@ -119,3 +120,10 @@ func subnetsEqual(subnets1, subnets2 []string) bool { return true } + +type Object interface { + client.Object + NewPatch() client.Patch + AttachToCluster(id string) + DetachFromCluster() +} diff --git a/controllers/clusterresources/postgresqluser_controller.go b/controllers/clusterresources/postgresqluser_controller.go index 6269b4e7a..24b44fc31 100644 --- a/controllers/clusterresources/postgresqluser_controller.go +++ b/controllers/clusterresources/postgresqluser_controller.go @@ -412,18 +412,22 @@ func (r *PostgreSQLUserReconciler) createPostgreSQLFirewallRule( ObjectMeta: ctrl.ObjectMeta{ Name: firewallRuleName, Namespace: ns, - Annotations: map[string]string{models.ResourceStateAnnotation: models.CreatingEvent}, + Annotations: map[string]string{}, Labels: map[string]string{models.ClusterIDLabel: clusterID}, Finalizers: []string{models.DeletionFinalizer}, }, Spec: clusterresourcesv1beta1.ClusterNetworkFirewallRuleSpec{ FirewallRuleSpec: clusterresourcesv1beta1.FirewallRuleSpec{ - ClusterID: clusterID, - Type: models.PgAppType, + Type: models.PgAppType, }, Network: fmt.Sprintf("%s/%s", nodeAddress, "32"), }, - Status: clusterresourcesv1beta1.ClusterNetworkFirewallRuleStatus{}, + Status: clusterresourcesv1beta1.ClusterNetworkFirewallRuleStatus{ + FirewallRuleStatus: clusterresourcesv1beta1.FirewallRuleStatus{ + ClusterID: clusterID, + ClusterEvent: models.CreatingEvent, + }, + }, } err = r.Create(ctx, firewallRule) diff --git a/controllers/clusters/cassandra_controller.go b/controllers/clusters/cassandra_controller.go index 04db4671b..e14fb2d49 100644 --- a/controllers/clusters/cassandra_controller.go +++ b/controllers/clusters/cassandra_controller.go @@ -613,7 +613,7 @@ func (r *CassandraReconciler) handleUsersCreate( ctx context.Context, l logr.Logger, c *v1beta1.Cassandra, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -672,7 +672,7 @@ func (r *CassandraReconciler) handleUsersDelete( ctx context.Context, l logr.Logger, c *v1beta1.Cassandra, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -728,7 +728,7 @@ func (r *CassandraReconciler) handleUsersDetach( ctx context.Context, l logr.Logger, c *v1beta1.Cassandra, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -776,7 +776,7 @@ func (r *CassandraReconciler) handleUsersDetach( func (r *CassandraReconciler) handleUserEvent( newObj *v1beta1.Cassandra, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.NamespacedNameRef, ) { ctx := context.TODO() l := log.FromContext(ctx) diff --git a/controllers/clusters/kafka_controller.go b/controllers/clusters/kafka_controller.go index f257df0ae..3d56498a4 100644 --- a/controllers/clusters/kafka_controller.go +++ b/controllers/clusters/kafka_controller.go @@ -318,7 +318,7 @@ func (r *KafkaReconciler) handleCreateUser( ctx context.Context, kafka *v1beta1.Kafka, l logr.Logger, - userRef *v1beta1.UserReference, + userRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: userRef.Namespace, @@ -378,7 +378,7 @@ func (r *KafkaReconciler) handleDeleteUser( ctx context.Context, kafka *v1beta1.Kafka, l logr.Logger, - userRef *v1beta1.UserReference, + userRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: userRef.Namespace, @@ -434,7 +434,7 @@ func (r *KafkaReconciler) detachUser( ctx context.Context, kafka *v1beta1.Kafka, l logr.Logger, - userRef *v1beta1.UserReference) error { + userRef *v1beta1.NamespacedNameRef) error { req := types.NamespacedName{ Namespace: userRef.Namespace, Name: userRef.Name, @@ -483,7 +483,7 @@ func (r *KafkaReconciler) detachUser( func (r *KafkaReconciler) handleUserEvent( newObj *v1beta1.Kafka, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.NamespacedNameRef, ) { ctx := context.TODO() l := log.FromContext(ctx) diff --git a/controllers/clusters/opensearch_controller.go b/controllers/clusters/opensearch_controller.go index 76c685380..2a02fd16c 100644 --- a/controllers/clusters/opensearch_controller.go +++ b/controllers/clusters/opensearch_controller.go @@ -944,7 +944,7 @@ func (r *OpenSearchReconciler) deleteUser( ctx context.Context, l logr.Logger, c *v1beta1.OpenSearch, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -997,7 +997,7 @@ func (r *OpenSearchReconciler) createUser( ctx context.Context, logger logr.Logger, c *v1beta1.OpenSearch, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -1056,7 +1056,7 @@ func (r *OpenSearchReconciler) detachUserResource( ctx context.Context, l logr.Logger, c *v1beta1.OpenSearch, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -1102,7 +1102,7 @@ func (r *OpenSearchReconciler) detachUserResource( func (r *OpenSearchReconciler) handleUserEvent( newObj *v1beta1.OpenSearch, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.NamespacedNameRef, ) { ctx := context.TODO() l := log.FromContext(ctx) diff --git a/controllers/clusters/postgresql_controller.go b/controllers/clusters/postgresql_controller.go index d90296410..113225477 100644 --- a/controllers/clusters/postgresql_controller.go +++ b/controllers/clusters/postgresql_controller.go @@ -41,6 +41,7 @@ import ( clusterresourcesv1beta1 "github.com/instaclustr/operator/apis/clusterresources/v1beta1" "github.com/instaclustr/operator/apis/clusters/v1beta1" + "github.com/instaclustr/operator/controllers/clusterresources" "github.com/instaclustr/operator/pkg/exposeservice" "github.com/instaclustr/operator/pkg/instaclustr" "github.com/instaclustr/operator/pkg/models" @@ -492,11 +493,205 @@ func (r *PostgreSQLReconciler) handleUpdateCluster( return models.ExitReconcile } +func (r *PostgreSQLReconciler) handleClusterResourcesEvents( + newObj *v1beta1.PostgreSQL, + oldObjSpec *v1beta1.PgSpec, +) { + r.HandleResourceEvent(newObj, "ClusterBackup", oldObjSpec.ClusterResources.ClusterBackups, newObj.Spec.ClusterResources.ClusterBackups) + r.HandleResourceEvent(newObj, "ClusterNetworkFirewallRule", oldObjSpec.ClusterResources.ClusterNetworkFirewallRules, newObj.Spec.ClusterResources.ClusterNetworkFirewallRules) + r.HandleResourceEvent(newObj, "AWSVPCPeering", oldObjSpec.ClusterResources.AWSVPCPeerings, newObj.Spec.ClusterResources.AWSVPCPeerings) + r.HandleResourceEvent(newObj, "AWSSecurityGroupFirewallRule", oldObjSpec.ClusterResources.AWSSecurityGroupFirewallRules, newObj.Spec.ClusterResources.AWSSecurityGroupFirewallRules) + r.HandleResourceEvent(newObj, "ExclusionWindow", oldObjSpec.ClusterResources.ExclusionWindows, newObj.Spec.ClusterResources.ExclusionWindows) + r.HandleResourceEvent(newObj, "GCPVPCPeering", oldObjSpec.ClusterResources.GCPVPCPeerings, newObj.Spec.ClusterResources.GCPVPCPeerings) + r.HandleResourceEvent(newObj, "AzureVNetPeering", oldObjSpec.ClusterResources.AzureVNetPeerings, newObj.Spec.ClusterResources.AzureVNetPeerings) +} + +func (r *PostgreSQLReconciler) HandleResourceEvent( + pg *v1beta1.PostgreSQL, + resourceKind string, + oldRefs, newRefs []*v1beta1.NamespacedNameRef, +) { + ctx := context.TODO() + l := log.FromContext(ctx) + + for _, ref := range newRefs { + var exist bool + for _, oldRef := range oldRefs { + if *ref == *oldRef { + exist = true + break + } + } + + if exist { + continue + } + + err := r.handleCreateResource(ctx, l, resourceKind, ref, pg) + if err != nil { + l.Error(err, "Cannot create clusterresource", "resource kind", resourceKind, "namespace and name", ref) + r.EventRecorder.Eventf(pg, models.Warning, models.CreatingEvent, + "Cannot create resource. Reason: %v", err) + } + oldRefs = append(oldRefs, ref) + } + for _, oldRef := range oldRefs { + var exist bool + for _, ref := range newRefs { + if *oldRef == *ref { + exist = true + break + } + } + + if exist { + continue + } + + err := r.handleDeleteResource(ctx, l, resourceKind, oldRef) + if err != nil { + l.Error(err, "Cannot delete clusterresource", "resource kind", resourceKind, "namespace and name", oldRef) + r.EventRecorder.Eventf(pg, models.Warning, models.DeletingEvent, + "Cannot delete resource. Reason: %v", err) + } + } +} + +func (r *PostgreSQLReconciler) handleCreateResource( + ctx context.Context, + l logr.Logger, + kind string, + ref *v1beta1.NamespacedNameRef, + pg *v1beta1.PostgreSQL, +) error { + req := types.NamespacedName{ + Namespace: ref.Namespace, + Name: ref.Name, + } + + var resource clusterresources.Object + var isCDC bool + + switch kind { + case "ClusterBackup": + resource = &clusterresourcesv1beta1.ClusterBackup{} + case "ClusterNetworkFirewallRule": + resource = &clusterresourcesv1beta1.ClusterNetworkFirewallRule{} + case "AWSVPCPeering": + resource = &clusterresourcesv1beta1.AWSVPCPeering{} + isCDC = true + case "AWSSecurityGroupFirewallRule": + resource = &clusterresourcesv1beta1.AWSSecurityGroupFirewallRule{} + case "ExclusionWindow": + resource = &clusterresourcesv1beta1.ExclusionWindow{} + case "GCPVPCPeering": + resource = &clusterresourcesv1beta1.GCPVPCPeering{} + isCDC = true + case "AzureVNetPeering": + resource = &clusterresourcesv1beta1.AzureVNetPeering{} + isCDC = true + default: + l.Info("Provided reference to resource that is not support deletion", "kind", kind, "resource", resource) + return nil + } + + err := r.Get(ctx, req, resource) + if err != nil { + if k8serrors.IsNotFound(err) { + l.Error(err, "Cannot create a cluster resource. The resource is not found", "request", req) + return err + } + l.Error(err, "Cannot get cluster resource", "Resource", resource) + return err + } + + patch := resource.NewPatch() + + if isCDC { + resource.AttachToCluster(pg.Status.DataCentres[0].ID) + } else { + resource.AttachToCluster(pg.Status.ID) + } + + err = r.Status().Patch(ctx, resource, patch) + if err != nil { + return err + } + + l.Info("PostgreSQL clusterresource was patched", + "Resource name", ref.Name, + "Resource Kind", kind, + "Event", models.CreatingEvent, + ) + + return nil +} + +func (r *PostgreSQLReconciler) handleDeleteResource( + ctx context.Context, + l logr.Logger, + kind string, + ref *v1beta1.NamespacedNameRef, +) error { + req := types.NamespacedName{ + Namespace: ref.Namespace, + Name: ref.Name, + } + + var resource clusterresources.Object + + switch kind { + case "ClusterNetworkFirewallRule": + resource = &clusterresourcesv1beta1.ClusterNetworkFirewallRule{} + case "AWSVPCPeering": + resource = &clusterresourcesv1beta1.AWSVPCPeering{} + case "AWSSecurityGroupFirewallRule": + resource = &clusterresourcesv1beta1.AWSSecurityGroupFirewallRule{} + case "ExclusionWindow": + resource = &clusterresourcesv1beta1.ExclusionWindow{} + case "GCPVPCPeering": + resource = &clusterresourcesv1beta1.GCPVPCPeering{} + case "AzureVNetPeering": + resource = &clusterresourcesv1beta1.AzureVNetPeering{} + default: + l.Info("Provided reference to resource that is not supported", "kind", kind, "resource", resource) + return nil + } + + err := r.Get(ctx, req, resource) + if err != nil { + if k8serrors.IsNotFound(err) { + l.Error(err, "Cannot delete a cluster resource. The resource is not found", "request", req) + return err + } + l.Error(err, "Cannot get cluster resource", "Resource", resource) + return err + } + + patch := resource.NewPatch() + + resource.DetachFromCluster() + + err = r.Status().Patch(ctx, resource, patch) + + if err != nil { + return err + } + + l.Info("PostgreSQL clusterresource was updated", + "Resource name", ref.Name, + "Resource Kind", kind, + "Event", models.DeletingEvent, + ) + + return nil +} + func (r *PostgreSQLReconciler) createUser( ctx context.Context, l logr.Logger, c *v1beta1.PostgreSQL, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -575,7 +770,7 @@ func (r *PostgreSQLReconciler) handleUsersDelete( ctx context.Context, l logr.Logger, pg *v1beta1.PostgreSQL, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -639,7 +834,7 @@ func (r *PostgreSQLReconciler) handleUsersDetach( ctx context.Context, l logr.Logger, c *v1beta1.PostgreSQL, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -696,7 +891,7 @@ func (r *PostgreSQLReconciler) handleUsersDetach( func (r *PostgreSQLReconciler) handleUserEvent( newObj *v1beta1.PostgreSQL, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.NamespacedNameRef, ) { ctx := context.TODO() l := log.FromContext(ctx) @@ -1675,6 +1870,7 @@ func (r *PostgreSQLReconciler) SetupWithManager(mgr ctrl.Manager) error { oldObj := event.ObjectOld.(*v1beta1.PostgreSQL) r.handleUserEvent(newObj, oldObj.Spec.UserRefs) + r.handleClusterResourcesEvents(newObj, &oldObj.Spec) event.ObjectNew.GetAnnotations()[models.ResourceStateAnnotation] = models.UpdatingEvent return true diff --git a/controllers/clusters/redis_controller.go b/controllers/clusters/redis_controller.go index 0ee7b3eda..5691bab4f 100644 --- a/controllers/clusters/redis_controller.go +++ b/controllers/clusters/redis_controller.go @@ -417,7 +417,7 @@ func (r *RedisReconciler) handleCreateUsers( ctx context.Context, redis *v1beta1.Redis, l logr.Logger, - userRefs *v1beta1.UserReference, + userRefs *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: userRefs.Namespace, @@ -671,7 +671,7 @@ func (r *RedisReconciler) detachUserResource( ctx context.Context, l logr.Logger, redis *v1beta1.Redis, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, @@ -717,7 +717,7 @@ func (r *RedisReconciler) detachUserResource( func (r *RedisReconciler) handleUserEvent( newObj *v1beta1.Redis, - oldUsers []*v1beta1.UserReference, + oldUsers []*v1beta1.NamespacedNameRef, ) { ctx := context.TODO() l := log.FromContext(ctx) @@ -775,7 +775,7 @@ func (r *RedisReconciler) handleUsersDelete( ctx context.Context, l logr.Logger, c *v1beta1.Redis, - uRef *v1beta1.UserReference, + uRef *v1beta1.NamespacedNameRef, ) error { req := types.NamespacedName{ Namespace: uRef.Namespace, diff --git a/controllers/tests/cassandra_plus_users_test.go b/controllers/tests/cassandra_plus_users_test.go index 816d41974..b3ca72d14 100644 --- a/controllers/tests/cassandra_plus_users_test.go +++ b/controllers/tests/cassandra_plus_users_test.go @@ -140,9 +140,9 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont }) }) - When("add the user to a Cassandra UserReference", func() { + When("add the user to a Cassandra NamespacedNameRef", func() { It("should create the user for the cluster", func() { - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.NamespacedNameRef{{ Namespace: userManifest1.Namespace, Name: userManifest1.Name, }} @@ -169,13 +169,13 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont }) }) - When("remove the user from the Cassandra UserReference", func() { + When("remove the user from the Cassandra NamespacedNameRef", func() { It("should delete the user for the cluster", func() { Expect(k8sClient.Get(ctx, cassandraNamespacedName1, &cassandra1)).Should(Succeed()) patch := cassandra1.NewPatch() // removing user - cassandra1.Spec.UserRefs = []*v1beta1.UserReference{} + cassandra1.Spec.UserRefs = []*v1beta1.NamespacedNameRef{} Expect(k8sClient.Patch(ctx, &cassandra1, patch)).Should(Succeed()) By("going to Cassandra(cluster) controller predicate and put user entity to deletion state. " + "Finally deletes the user for the corresponded cluster") @@ -221,7 +221,7 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont Expect(k8sClient.Create(ctx, &userManifest2)).Should(Succeed()) By("adding the batch of users to the cluster, Cassandra(cluster) controller predicate set them creation state") - newUsers := []*v1beta1.UserReference{ + newUsers := []*v1beta1.NamespacedNameRef{ { Namespace: userManifest1.Namespace, Name: userManifest1.Name, @@ -342,7 +342,7 @@ var _ = Describe("Basic Cassandra User controller + Basic Cassandra cluster cont Expect(k8sClient.Get(ctx, userNamespacedName2, &user2)).Should(Succeed()) By("creating another Cassandra cluster manifest with filled user ref, " + "we make sure the user creation job works properly and show us that the user is available for use") - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.NamespacedNameRef{{ Namespace: user2.Namespace, Name: user2.Name, }} diff --git a/controllers/tests/helpers.go b/controllers/tests/helpers.go index bf3046f5f..fe0af2be3 100644 --- a/controllers/tests/helpers.go +++ b/controllers/tests/helpers.go @@ -17,6 +17,6 @@ func NewChannelWithTimeout(timeout time.Duration) chan struct{} { return done } -func removeUserByIndex(s []*v1beta1.UserReference, index int) []*v1beta1.UserReference { +func removeUserByIndex(s []*v1beta1.NamespacedNameRef, index int) []*v1beta1.NamespacedNameRef { return append(s[:index], s[index+1:]...) } diff --git a/controllers/tests/opensearch_plus_users_test.go b/controllers/tests/opensearch_plus_users_test.go index 517d0f8c2..5f8a74646 100644 --- a/controllers/tests/opensearch_plus_users_test.go +++ b/controllers/tests/opensearch_plus_users_test.go @@ -139,9 +139,9 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co }) }) - When("add the user to a openSearch UserReference", func() { + When("add the user to a openSearch NamespacedNameRef", func() { It("should create the user for the cluster", func() { - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.NamespacedNameRef{{ Namespace: userManifest1.Namespace, Name: userManifest1.Name, }} @@ -168,13 +168,13 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co }) }) - When("remove the user from the openSearch UserReference", func() { + When("remove the user from the openSearch NamespacedNameRef", func() { It("should delete the user for the cluster", func() { Expect(k8sClient.Get(ctx, openSearchNamespacedName1, &openSearch1)).Should(Succeed()) patch := openSearch1.NewPatch() // removing user - openSearch1.Spec.UserRefs = []*v1beta1.UserReference{} + openSearch1.Spec.UserRefs = []*v1beta1.NamespacedNameRef{} Expect(k8sClient.Patch(ctx, &openSearch1, patch)).Should(Succeed()) By("going to openSearch(cluster) controller predicate and put user entity to deletion state. " + "Finally deletes the user for the corresponded cluster") @@ -220,7 +220,7 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co Expect(k8sClient.Create(ctx, &userManifest2)).Should(Succeed()) By("adding the batch of users to the cluster, openSearch(cluster) controller predicate set them creation state") - newUsers := []*v1beta1.UserReference{ + newUsers := []*v1beta1.NamespacedNameRef{ { Namespace: userManifest1.Namespace, Name: userManifest1.Name, @@ -342,7 +342,7 @@ var _ = Describe("Basic openSearch User controller + Basic openSearch cluster co Expect(k8sClient.Get(ctx, userNamespacedName2, &user2)).Should(Succeed()) By("creating another openSearch cluster manifest with filled user ref, " + "we make sure the user creation job works properly and show us that the user is available for use") - newUsers := []*v1beta1.UserReference{{ + newUsers := []*v1beta1.NamespacedNameRef{{ Namespace: user2.Namespace, Name: user2.Name, }} diff --git a/pkg/instaclustr/client.go b/pkg/instaclustr/client.go index 4bfa8119c..2cafadc33 100644 --- a/pkg/instaclustr/client.go +++ b/pkg/instaclustr/client.go @@ -598,14 +598,117 @@ func (c *Client) GetPeeringStatus(peerID, return &peeringStatus, nil } -func (c *Client) CreatePeering(url string, peeringSpec any) (*clusterresourcesv1beta1.PeeringStatus, error) { +func (c *Client) CreateAzureVNetPeering(peeringSpec *clusterresourcesv1beta1.AzureVNetPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) { + payload := &struct { + PeerSubnets []string `json:"peerSubnets"` + PeerResourceGroup string `json:"peerResourceGroup"` + PeerSubscriptionID string `json:"peerSubscriptionId"` + PeerADObjectID string `json:"peerAdObjectId,omitempty"` + PeerVirtualNetworkName string `json:"peerVirtualNetworkName"` + CDCid string `json:"cdcId"` + }{ + PeerSubnets: peeringSpec.PeerSubnets, + PeerResourceGroup: peeringSpec.PeerResourceGroup, + PeerADObjectID: peeringSpec.PeerADObjectID, + PeerSubscriptionID: peeringSpec.PeerSubscriptionID, + PeerVirtualNetworkName: peeringSpec.PeerVirtualNetworkName, + CDCid: cdcId, + } - jsonDataCreate, err := json.Marshal(peeringSpec) + jsonDataCreate, err := json.Marshal(payload) if err != nil { return nil, err } - url = c.serverHostname + url + url := c.serverHostname + AzurePeeringEndpoint + resp, err := c.DoRequest(url, http.MethodPost, jsonDataCreate) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusAccepted { + return nil, fmt.Errorf("status code: %d, message: %s", resp.StatusCode, body) + } + + var creationResponse *clusterresourcesv1beta1.PeeringStatus + err = json.Unmarshal(body, &creationResponse) + if err != nil { + return nil, err + } + + return creationResponse, nil +} + +func (c *Client) CreateAWSVPCPeering(peeringSpec *clusterresourcesv1beta1.AWSVPCPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) { + payload := &struct { + PeerSubnets []string `json:"peerSubnets"` + PeerAWSAccountID string `json:"peerAwsAccountId"` + PeerVPCID string `json:"peerVpcId"` + PeerRegion string `json:"peerRegion,omitempty"` + CDCid string `json:"cdcId"` + }{ + PeerSubnets: peeringSpec.PeerSubnets, + PeerAWSAccountID: peeringSpec.PeerAWSAccountID, + PeerVPCID: peeringSpec.PeerVPCID, + PeerRegion: peeringSpec.PeerRegion, + CDCid: cdcId, + } + + jsonDataCreate, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + url := c.serverHostname + AWSPeeringEndpoint + resp, err := c.DoRequest(url, http.MethodPost, jsonDataCreate) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusAccepted { + return nil, fmt.Errorf("status code: %d, message: %s", resp.StatusCode, body) + } + + var creationResponse *clusterresourcesv1beta1.PeeringStatus + err = json.Unmarshal(body, &creationResponse) + if err != nil { + return nil, err + } + + return creationResponse, nil +} + +func (c *Client) CreateGCPVPCPeering(peeringSpec *clusterresourcesv1beta1.GCPVPCPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) { + payload := &struct { + PeerSubnets []string `json:"peerSubnets"` + PeerVPCNetworkName string `json:"peerVpcNetworkName"` + PeerProjectID string `json:"peerProjectId"` + CDCid string `json:"cdcId"` + }{ + PeerSubnets: peeringSpec.PeerSubnets, + PeerVPCNetworkName: peeringSpec.PeerVPCNetworkName, + PeerProjectID: peeringSpec.PeerProjectID, + CDCid: cdcId, + } + + jsonDataCreate, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + url := c.serverHostname + GCPPeeringEndpoint resp, err := c.DoRequest(url, http.MethodPost, jsonDataCreate) if err != nil { return nil, err @@ -718,11 +821,22 @@ func (c *Client) GetFirewallRuleStatus( return firewallRuleStatus, nil } -func (c *Client) CreateFirewallRule( +func (c *Client) CreateAWSSecurityGroupFirewallRule( url string, - firewallRuleSpec any, + firewallRuleSpec *clusterresourcesv1beta1.AWSSecurityGroupFirewallRuleSpec, + clusterID string, ) (*clusterresourcesv1beta1.FirewallRuleStatus, error) { - jsonFirewallRule, err := json.Marshal(firewallRuleSpec) + payload := &struct { + SecurityGroupID string `json:"securityGroupId"` + ClusterID string `json:"clusterId,omitempty"` + Type string `json:"type"` + }{ + SecurityGroupID: firewallRuleSpec.SecurityGroupID, + ClusterID: clusterID, + Type: firewallRuleSpec.Type, + } + + jsonFirewallRule, err := json.Marshal(payload) if err != nil { return nil, err } @@ -752,6 +866,50 @@ func (c *Client) CreateFirewallRule( return creationResponse, nil } +func (c *Client) CreateClusterNetworkFirewallRule( + firewallRuleSpec *clusterresourcesv1beta1.ClusterNetworkFirewallRuleSpec, + clusterID string, +) (*clusterresourcesv1beta1.FirewallRuleStatus, error) { + payload := &struct { + ClusterID string `json:"clusterId"` + Type string `json:"type"` + Network string `json:"network"` + }{ + ClusterID: clusterID, + Type: firewallRuleSpec.Type, + Network: firewallRuleSpec.Network, + } + + jsonFirewallRule, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + url := c.serverHostname + ClusterNetworkFirewallRuleEndpoint + resp, err := c.DoRequest(url, http.MethodPost, jsonFirewallRule) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusAccepted { + return nil, fmt.Errorf("status code: %d, message: %s", resp.StatusCode, body) + } + + var creationResponse *clusterresourcesv1beta1.FirewallRuleStatus + err = json.Unmarshal(body, &creationResponse) + if err != nil { + return nil, err + } + + return creationResponse, nil +} + func (c *Client) DeleteFirewallRule( firewallRuleID string, firewallRuleEndpoint string, diff --git a/pkg/instaclustr/interfaces.go b/pkg/instaclustr/interfaces.go index 6e19e5041..7c502aa86 100644 --- a/pkg/instaclustr/interfaces.go +++ b/pkg/instaclustr/interfaces.go @@ -37,9 +37,12 @@ type API interface { GetAWSVPCPeering(peerID string) (*models.AWSVPCPeering, error) UpdatePeering(peerID, peeringEndpoint string, peerSpec any) error DeletePeering(peerID, peeringEndpoint string) error - CreatePeering(url string, peeringSpec any) (*clusterresourcesv1beta1.PeeringStatus, error) + CreateAzureVNetPeering(peeringSpec *clusterresourcesv1beta1.AzureVNetPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) + CreateGCPVPCPeering(peeringSpec *clusterresourcesv1beta1.GCPVPCPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) + CreateAWSVPCPeering(peeringSpec *clusterresourcesv1beta1.AWSVPCPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) GetFirewallRuleStatus(firewallRuleID string, firewallRuleEndpoint string) (*clusterresourcesv1beta1.FirewallRuleStatus, error) - CreateFirewallRule(url string, firewallRuleSpec any) (*clusterresourcesv1beta1.FirewallRuleStatus, error) + CreateAWSSecurityGroupFirewallRule(url string, firewallRuleSpec *clusterresourcesv1beta1.AWSSecurityGroupFirewallRuleSpec, clusterID string) (*clusterresourcesv1beta1.FirewallRuleStatus, error) + CreateClusterNetworkFirewallRule(firewallRuleSpec *clusterresourcesv1beta1.ClusterNetworkFirewallRuleSpec, clusterID string) (*clusterresourcesv1beta1.FirewallRuleStatus, error) DeleteFirewallRule(firewallRuleID string, firewallRuleEndpoint string) error CreateKafkaUser(url string, kafkaUser *models.KafkaUser) (*kafkamanagementv1beta1.KafkaUserStatus, error) UpdateKafkaUser(kafkaUserID string, kafkaUserSpec *models.KafkaUser) error diff --git a/pkg/instaclustr/mock/client.go b/pkg/instaclustr/mock/client.go index 36c8b1e32..39166a83a 100644 --- a/pkg/instaclustr/mock/client.go +++ b/pkg/instaclustr/mock/client.go @@ -77,7 +77,27 @@ func (c *mockClient) DeletePeering(peerID, peeringEndpoint string) error { panic("DeletePeering: is not implemented") } -func (c *mockClient) CreatePeering(url string, peeringSpec any) (*clusterresourcesv1beta1.PeeringStatus, error) { +func (c *mockClient) CreateAzureVNetPeering(peeringSpec *clusterresourcesv1beta1.AzureVNetPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) { + ps := &clusterresourcesv1beta1.PeeringStatus{ + ID: StatusID, + Name: "name", + StatusCode: "statusCode", + FailureReason: "failureReason", + } + return ps, nil +} + +func (c *mockClient) CreateAWSVPCPeering(peeringSpec *clusterresourcesv1beta1.AWSVPCPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) { + ps := &clusterresourcesv1beta1.PeeringStatus{ + ID: StatusID, + Name: "name", + StatusCode: "statusCode", + FailureReason: "failureReason", + } + return ps, nil +} + +func (c *mockClient) CreateGCPVPCPeering(peeringSpec *clusterresourcesv1beta1.GCPVPCPeeringSpec, cdcId string) (*clusterresourcesv1beta1.PeeringStatus, error) { ps := &clusterresourcesv1beta1.PeeringStatus{ ID: StatusID, Name: "name", @@ -96,7 +116,15 @@ func (c *mockClient) GetFirewallRuleStatus(firewallRuleID string, firewallRuleEn return fwRule, nil } -func (c *mockClient) CreateFirewallRule(url string, firewallRuleSpec any) (*clusterresourcesv1beta1.FirewallRuleStatus, error) { +func (c *mockClient) CreateAWSSecurityGroupFirewallRule(url string, firewallRuleSpec *clusterresourcesv1beta1.AWSSecurityGroupFirewallRuleSpec, clusterID string) (*clusterresourcesv1beta1.FirewallRuleStatus, error) { + fwRule := &clusterresourcesv1beta1.FirewallRuleStatus{ + ID: StatusID, + Status: "OK", + DeferredReason: "NO", + } + return fwRule, nil +} +func (c *mockClient) CreateClusterNetworkFirewallRule(firewallRuleSpec *clusterresourcesv1beta1.ClusterNetworkFirewallRuleSpec, clusterID string) (*clusterresourcesv1beta1.FirewallRuleStatus, error) { fwRule := &clusterresourcesv1beta1.FirewallRuleStatus{ ID: StatusID, Status: "OK", diff --git a/pkg/models/operator.go b/pkg/models/operator.go index 435480682..6dc0f0d8d 100644 --- a/pkg/models/operator.go +++ b/pkg/models/operator.go @@ -29,6 +29,9 @@ const ( DeletionFinalizer = "instaclustr.com/deletionFinalizer" StartTimestampAnnotation = "instaclustr.com/startTimestamp" UpdateQueuedAnnotation = "instaclustr.com/updateQueued" + ClusterIDAnnotation = "clusterID" + CDCIDAnnotation = "cdcId" + ClusterEventAnnotation = "clusterEvent" DefaultSecretLabel = "instaclustr.com/defaultSecret" ControlledByLabel = "instaclustr.com/controlledBy"