From 9d62cafb77f88a2005e448d8eb041fedb67aa5f5 Mon Sep 17 00:00:00 2001 From: tengu-alt Date: Thu, 28 Sep 2023 13:51:56 +0300 Subject: [PATCH] [WIP] clusterbackup refs for postgres were implemented --- Makefile | 2 +- .../v1beta1/clusterbackup_types.go | 8 +- .../v1beta1/clusterbackup_webhook.go | 10 +- .../v1beta1/maintenanceevents_types.go | 2 +- apis/clusters/v1beta1/postgresql_types.go | 15 +-- .../clusters/v1beta1/zz_generated.deepcopy.go | 15 +++ ...ources.instaclustr.com_clusterbackups.yaml | 3 - ...ces.instaclustr.com_maintenanceevents.yaml | 1 - .../clusters.instaclustr.com_postgresqls.yaml | 12 ++ ...lusterresources_v1beta1_clusterbackup.yaml | 2 +- ...erresources_v1beta1_maintenanceevents.yaml | 4 +- .../samples/clusters_v1beta1_postgresql.yaml | 6 +- .../clusterbackup_controller.go | 34 ++++-- controllers/clusterresources/helpers.go | 11 ++ .../maintenanceevents_controller.go | 1 + controllers/clusters/kafka_controller.go | 2 + controllers/clusters/postgresql_controller.go | 113 ++++++++++++++++++ 17 files changed, 204 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index b12ed5db3..485f9c67c 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go .PHONY: docker-build -docker-build: manifests generate test ## Build docker image with the manager. +docker-build: manifests generate ## Build docker image with the manager. docker build -t ${IMG} . .PHONY: docker-push diff --git a/apis/clusterresources/v1beta1/clusterbackup_types.go b/apis/clusterresources/v1beta1/clusterbackup_types.go index be942299d..d81648e8a 100644 --- a/apis/clusterresources/v1beta1/clusterbackup_types.go +++ b/apis/clusterresources/v1beta1/clusterbackup_types.go @@ -27,8 +27,8 @@ import ( // ClusterBackupSpec defines the desired state of ClusterBackup type ClusterBackupSpec struct { - ClusterID string `json:"clusterId"` - ClusterKind string `json:"clusterKind"` + ClusterID string `json:"clusterId,omitempty"` + ClusterKind string `json:"clusterKind,omitempty"` } // ClusterBackupStatus defines the observed state of ClusterBackup @@ -71,6 +71,10 @@ func (cbs *ClusterBackupStatus) UpdateStatus(instBackup *models.BackupEvent) { cbs.Progress = fmt.Sprintf("%f", instBackup.Progress) } +func (cb *ClusterBackup) DeepCopyObjecte() client.Object { + return cb.DeepCopy() +} + func init() { SchemeBuilder.Register(&ClusterBackup{}, &ClusterBackupList{}) } diff --git a/apis/clusterresources/v1beta1/clusterbackup_webhook.go b/apis/clusterresources/v1beta1/clusterbackup_webhook.go index e969452d0..963f9056a 100644 --- a/apis/clusterresources/v1beta1/clusterbackup_webhook.go +++ b/apis/clusterresources/v1beta1/clusterbackup_webhook.go @@ -21,8 +21,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" - - "github.com/instaclustr/operator/pkg/models" ) // log is for logging in this package. @@ -54,10 +52,10 @@ var _ webhook.Validator = &ClusterBackup{} func (r *ClusterBackup) ValidateCreate() error { clusterbackuplog.Info("validate create", "name", r.Name) - _, ok := models.ClusterKindsMap[r.Spec.ClusterKind] - if !ok { - return models.ErrUnsupportedBackupClusterKind - } + //_, ok := models.ClusterKindsMap[r.Spec.ClusterKind] + //if !ok { + // return models.ErrUnsupportedBackupClusterKind + //} return nil } diff --git a/apis/clusterresources/v1beta1/maintenanceevents_types.go b/apis/clusterresources/v1beta1/maintenanceevents_types.go index 2c59bd541..25551cd8d 100644 --- a/apis/clusterresources/v1beta1/maintenanceevents_types.go +++ b/apis/clusterresources/v1beta1/maintenanceevents_types.go @@ -23,7 +23,7 @@ import ( // MaintenanceEventsSpec defines the desired state of MaintenanceEvents type MaintenanceEventsSpec struct { - ClusterID string `json:"clusterId"` + ClusterID string `json:"clusterId,omitempty"` MaintenanceEventsReschedules []*MaintenanceEventReschedule `json:"maintenanceEventsReschedule"` } diff --git a/apis/clusters/v1beta1/postgresql_types.go b/apis/clusters/v1beta1/postgresql_types.go index a4b062809..8cb5af4c2 100644 --- a/apis/clusters/v1beta1/postgresql_types.go +++ b/apis/clusters/v1beta1/postgresql_types.go @@ -75,11 +75,12 @@ 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"` - Description string `json:"description,omitempty"` - SynchronousModeStrict bool `json:"synchronousModeStrict,omitempty"` - UserRefs []*UserReference `json:"userRefs,omitempty"` + DataCentres []*PgDataCentre `json:"dataCentres,omitempty"` + ClusterConfigurations map[string]string `json:"clusterConfigurations,omitempty"` + Description string `json:"description,omitempty"` + SynchronousModeStrict bool `json:"synchronousModeStrict,omitempty"` + UserRefs []*UserReference `json:"userRefs,omitempty"` + ClusterResourceRefs map[string]*UserReference `json:"clusterResourceRefs,omitempty"` //+kubebuilder:validate:MaxItems:=1 ResizeSettings []*ResizeSettings `json:"resizeSettings,omitempty"` } @@ -155,12 +156,12 @@ 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), "clusterID": pg.Status.ID}, Labels: map[string]string{models.ClusterIDLabel: pg.Status.ID}, Finalizers: []string{models.DeletionFinalizer}, }, Spec: clusterresourcesv1beta1.ClusterBackupSpec{ - ClusterID: pg.Status.ID, + //ClusterID: pg.Status.ID, ClusterKind: models.PgClusterKind, }, } diff --git a/apis/clusters/v1beta1/zz_generated.deepcopy.go b/apis/clusters/v1beta1/zz_generated.deepcopy.go index eb4beebbd..2f7c73823 100644 --- a/apis/clusters/v1beta1/zz_generated.deepcopy.go +++ b/apis/clusters/v1beta1/zz_generated.deepcopy.go @@ -1761,6 +1761,21 @@ func (in *PgSpec) DeepCopyInto(out *PgSpec) { } } } + if in.ClusterResourceRefs != nil { + in, out := &in.ClusterResourceRefs, &out.ClusterResourceRefs + *out = make(map[string]*UserReference, len(*in)) + for key, val := range *in { + var outVal *UserReference + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(UserReference) + **out = **in + } + (*out)[key] = outVal + } + } if in.ResizeSettings != nil { in, out := &in.ResizeSettings, &out.ResizeSettings *out = make([]*ResizeSettings, len(*in)) diff --git a/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml b/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml index 8e9b8bc17..1fc42da1a 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_clusterbackups.yaml @@ -39,9 +39,6 @@ spec: type: string clusterKind: type: string - required: - - clusterId - - clusterKind type: object status: description: ClusterBackupStatus defines the observed state of ClusterBackup diff --git a/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml b/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml index e7a5ce71c..e3992aaa2 100644 --- a/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml +++ b/config/crd/bases/clusterresources.instaclustr.com_maintenanceevents.yaml @@ -50,7 +50,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..754895186 100644 --- a/config/crd/bases/clusters.instaclustr.com_postgresqls.yaml +++ b/config/crd/bases/clusters.instaclustr.com_postgresqls.yaml @@ -52,6 +52,18 @@ spec: additionalProperties: type: string type: object + clusterResourceRefs: + additionalProperties: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: object dataCentres: items: properties: diff --git a/config/samples/clusterresources_v1beta1_clusterbackup.yaml b/config/samples/clusterresources_v1beta1_clusterbackup.yaml index 5c1afa170..9ee13b783 100644 --- a/config/samples/clusterresources_v1beta1_clusterbackup.yaml +++ b/config/samples/clusterresources_v1beta1_clusterbackup.yaml @@ -3,5 +3,5 @@ kind: ClusterBackup metadata: name: clusterbackup-sample 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_maintenanceevents.yaml b/config/samples/clusterresources_v1beta1_maintenanceevents.yaml index 307734950..078270fbb 100644 --- a/config/samples/clusterresources_v1beta1_maintenanceevents.yaml +++ b/config/samples/clusterresources_v1beta1_maintenanceevents.yaml @@ -3,9 +3,7 @@ kind: MaintenanceEvents metadata: name: maintenanceevents-sample spec: - clusterId: "9cf09a53-a09e-450a-ba7d-e98b3c724911" +# clusterId: "f78c42f9-2b31-4922-8c2f-ab03f8caed5e" 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" diff --git a/config/samples/clusters_v1beta1_postgresql.yaml b/config/samples/clusters_v1beta1_postgresql.yaml index 0747a3920..c71a8f80e 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,10 @@ spec: # userRef: # - namespace: default # name: postgresqluser-sample + clusterResourceRefs: +# ClusterBackup: +# namespace: default +# name: clusterbackup-sample privateNetworkCluster: false synchronousModeStrict: false # resizeSettings: diff --git a/controllers/clusterresources/clusterbackup_controller.go b/controllers/clusterresources/clusterbackup_controller.go index cbd72f2c6..acba4c701 100644 --- a/controllers/clusterresources/clusterbackup_controller.go +++ b/controllers/clusterresources/clusterbackup_controller.go @@ -18,6 +18,7 @@ package clusterresources import ( "context" + "fmt" "strconv" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -77,11 +78,15 @@ func (r *ClusterBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reques patch := backup.NewPatch() - if backup.Labels[models.ClusterIDLabel] != backup.Spec.ClusterID { + if backup.Annotations["clusterID"] == "" { + return models.ExitReconcile, nil + } + + if backup.Labels[models.ClusterIDLabel] != backup.Annotations["clusterID"] { if backup.Labels == nil { - backup.Labels = map[string]string{models.ClusterIDLabel: backup.Spec.ClusterID} + backup.Labels = map[string]string{models.ClusterIDLabel: backup.Annotations["clusterID"]} } else { - backup.Labels[models.ClusterIDLabel] = backup.Spec.ClusterID + backup.Labels[models.ClusterIDLabel] = backup.Annotations["clusterID"] } err = r.Patch(ctx, backup, patch) if err != nil { @@ -98,11 +103,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.Annotations["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.Annotations["clusterID"], ) r.EventRecorder.Eventf( @@ -118,11 +123,12 @@ 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.Annotations["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.Annotations["clusterID"], + "LENGTH OF ANNOTS", len(backup.Annotations), ) r.EventRecorder.Eventf( @@ -136,11 +142,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.Annotations["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.Annotations["clusterID"], ) r.EventRecorder.Eventf( @@ -156,7 +162,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.Annotations["clusterID"], ) } @@ -214,7 +220,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.Annotations["clusterID"], ) return models.ExitReconcile, nil @@ -239,7 +245,13 @@ 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 { + //newObj := event.ObjectNew.(*v1beta1.ClusterBackup) + //oldObj := event.ObjectOld.(*v1beta1.ClusterBackup) + if fmt.Sprint(event.ObjectOld.(*v1beta1.ClusterBackup).Annotations) != fmt.Sprint(event.ObjectNew.(*v1beta1.ClusterBackup).Annotations) { + return true + } return false + //return true }, })). Complete(r) diff --git a/controllers/clusterresources/helpers.go b/controllers/clusterresources/helpers.go index e97a06c20..b21073a74 100644 --- a/controllers/clusterresources/helpers.go +++ b/controllers/clusterresources/helpers.go @@ -17,6 +17,7 @@ limitations under the License. package clusterresources import ( + "sigs.k8s.io/controller-runtime/pkg/client" "strings" k8sCore "k8s.io/api/core/v1" @@ -87,3 +88,13 @@ func getUserCreds(secret *k8sCore.Secret) (username, password string, err error) return username, password, nil } + +type Object interface { + client.Object + NewPatch() client.Patch +} + +//func NewObjectPatch(obj Object) client.Patch { +// //old := obj.DeepCopyObjecte() +// return client.StrategicMergeFrom(obj.DeepCopyObjecte()) +//} diff --git a/controllers/clusterresources/maintenanceevents_controller.go b/controllers/clusterresources/maintenanceevents_controller.go index 01eca05d5..670dce7f9 100644 --- a/controllers/clusterresources/maintenanceevents_controller.go +++ b/controllers/clusterresources/maintenanceevents_controller.go @@ -105,6 +105,7 @@ func (r *MaintenanceEventsReconciler) Reconcile(ctx context.Context, req ctrl.Re MaintenanceEventID: me.Spec.MaintenanceEventsReschedules[len(me.Spec.MaintenanceEventsReschedules)-1].MaintenanceEventID, ScheduledStartTime: me.Spec.MaintenanceEventsReschedules[len(me.Spec.MaintenanceEventsReschedules)-1].ScheduledStartTime, } + // err = r.API.RescheduleMaintenanceEvent(meReschedule) if err != nil { diff --git a/controllers/clusters/kafka_controller.go b/controllers/clusters/kafka_controller.go index 2af4b7d81..0e1e9c74b 100644 --- a/controllers/clusters/kafka_controller.go +++ b/controllers/clusters/kafka_controller.go @@ -335,6 +335,8 @@ func (r *KafkaReconciler) handleCreateUser( u.Status.ClustersEvents = make(map[string]string) } + // + u.Status.ClustersEvents[kafka.Status.ID] = models.CreatingEvent err = r.Status().Patch(ctx, u, patch) if err != nil { diff --git a/controllers/clusters/postgresql_controller.go b/controllers/clusters/postgresql_controller.go index c69088ecf..fb5d010de 100644 --- a/controllers/clusters/postgresql_controller.go +++ b/controllers/clusters/postgresql_controller.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "errors" + "github.com/instaclustr/operator/controllers/clusterresources" "strconv" "github.com/go-logr/logr" @@ -490,6 +491,117 @@ func (r *PostgreSQLReconciler) handleUpdateCluster( return models.ExitReconcile } +func (r *PostgreSQLReconciler) handleClusterResourceEvent( + newObj *v1beta1.PostgreSQL, + oldRefs map[string]*v1beta1.UserReference, +) { + ctx := context.TODO() + l := log.FromContext(ctx) + + for resourceType, ref := range newObj.Spec.ClusterResourceRefs { + var exist bool + for _, oldRef := range oldRefs { + if *ref == *oldRef { + exist = true + break + } + } + + if exist { + continue + } + + err := r.handleCreateResource(ctx, l, resourceType, ref, newObj) + if err != nil { + l.Error(err, "Cannot create clusterresource", "resource type", resourceType, "namespace and name", ref) + r.EventRecorder.Eventf(newObj, models.Warning, models.CreatingEvent, + "Cannot create resource. Reason: %v", err) + } + if oldRefs == nil { + oldRefs = make(map[string]*v1beta1.UserReference) + } + oldRefs[resourceType] = ref + } + //for resourceType, oldRef := range oldRefs { + // var exist bool + // for _, ref := range newObj.Spec.ClusterResourceRefs { + // if *oldRef == *ref { + // exist = true + // break + // } + // } + // + // if exist { + // continue + // } + // + // err := r.handleDeleteUser(ctx, newObj, l, oldRef) + // if err != nil { + // l.Error(err, "Cannot delete clusterresource", "resource type", resourceType, "namespace and name", oldRef) + // r.EventRecorder.Eventf(newObj, models.Warning, models.CreatingEvent, + // "Cannot delete resource. Reason: %v", err) + // } + //} +} + +func (r *PostgreSQLReconciler) handleCreateResource( + ctx context.Context, + l logr.Logger, + kind string, + ns *v1beta1.UserReference, + newObj *v1beta1.PostgreSQL, +) error { + req := types.NamespacedName{ + Namespace: ns.Namespace, + Name: ns.Name, + } + + l.Info("RESOURCE RECONCILER", "kind", kind, "resource", ns) + + var resource clusterresources.Object + + switch kind { + case "ClusterBackup": + resource = &clusterresourcesv1beta1.ClusterBackup{} + default: + l.Info("DEFAULT", "kind", kind, "resource", resource) + } + + l.Info("RESOURCE", "kind", kind, "resource", resource) + + 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() + + //annots := resource.GetAnnotations() + //if annots == nil { + annots := make(map[string]string) + //} + annots["clusterID"] = newObj.Status.ID + //annots["clusterKind"] = newObj.Kind + annots["clusterEvent"] = models.CreatingEvent + + resource.SetAnnotations(annots) + + err = r.Patch(ctx, resource, patch) + + if err != nil { + return err + } + + //resource.SetAnnotations(map[string]string{models.ResourceStateAnnotation: models.CreatingEvent}) + + return nil +} + func (r *PostgreSQLReconciler) createUser( ctx context.Context, l logr.Logger, @@ -1587,6 +1699,7 @@ func (r *PostgreSQLReconciler) SetupWithManager(mgr ctrl.Manager) error { oldObj := event.ObjectOld.(*v1beta1.PostgreSQL) r.handleUserEvent(newObj, oldObj.Spec.UserRefs) + r.handleClusterResourceEvent(newObj, oldObj.Spec.ClusterResourceRefs) event.ObjectNew.GetAnnotations()[models.ResourceStateAnnotation] = models.UpdatingEvent return true