From be86eedd55a6bca491682de1b1696534794a3f08 Mon Sep 17 00:00:00 2001 From: RAJAT SINGH Date: Fri, 8 May 2020 14:43:03 +0530 Subject: [PATCH] Ceph: Create resources for external cluster The JSON BLOB will be passed from the UI and appropiate resources will be created automatically for the external cluster. Signed-off-by: RAJAT SINGH --- pkg/apis/ocs/v1/storagecluster_types.go | 4 + pkg/apis/ocs/v1/zz_generated.openapi.go | 7 + .../initialization_reconciler.go | 147 +++++++++++++++++- pkg/controller/storagecluster/reconcile.go | 1 + 4 files changed, 154 insertions(+), 5 deletions(-) diff --git a/pkg/apis/ocs/v1/storagecluster_types.go b/pkg/apis/ocs/v1/storagecluster_types.go index 519949a397..4cc3faebea 100644 --- a/pkg/apis/ocs/v1/storagecluster_types.go +++ b/pkg/apis/ocs/v1/storagecluster_types.go @@ -104,6 +104,10 @@ type StorageClusterStatus struct { // +optional FailureDomain string `json:"failureDomain,omitempty"` + // ExternalSecretFound indicates whether a Secret containing information + // about an external CephCluster was found or not + ExternalSecretFound bool `json:"externalSecretFound,omitempty"` + StorageClassesCreated bool `json:"storageClassesCreated,omitempty"` CephObjectStoresCreated bool `json:"cephObjectStoresCreated,omitempty"` CephBlockPoolsCreated bool `json:"cephBlockPoolsCreated,omitempty"` diff --git a/pkg/apis/ocs/v1/zz_generated.openapi.go b/pkg/apis/ocs/v1/zz_generated.openapi.go index 959d169f6f..92f2b2307d 100644 --- a/pkg/apis/ocs/v1/zz_generated.openapi.go +++ b/pkg/apis/ocs/v1/zz_generated.openapi.go @@ -306,6 +306,13 @@ func schema_pkg_apis_ocs_v1_StorageClusterStatus(ref common.ReferenceCallback) c Format: "", }, }, + "externalSecretFound": { + SchemaProps: spec.SchemaProps{ + Description: "ExternalSecretFound indicates whether a Secret containing information about an external CephCluster was found or not", + Type: []string{"boolean"}, + Format: "", + }, + }, "storageClassesCreated": { SchemaProps: spec.SchemaProps{ Type: []string{"boolean"}, diff --git a/pkg/controller/storagecluster/initialization_reconciler.go b/pkg/controller/storagecluster/initialization_reconciler.go index de2d1d1c47..f1b23a778c 100644 --- a/pkg/controller/storagecluster/initialization_reconciler.go +++ b/pkg/controller/storagecluster/initialization_reconciler.go @@ -2,6 +2,7 @@ package storagecluster import ( "context" + "encoding/json" "fmt" "github.com/go-logr/logr" @@ -16,6 +17,21 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) +const ( + externalClusterDetailsSecret = "rook-ceph-external-cluster-details" + externalClusterDetailsKey = "external_cluster_details" + cephFsStorageClassName = "cephfs" + cephRbdStorageClassName = "ceph-rbd" + cephRgwStorageClassName = "ceph-rgw" +) + +// ExternalResource containes a list of External Cluster Resources +type ExternalResource struct { + Kind string `json:"kind"` + Data map[string]string `json:"data"` + Name string `json:"name"` +} + // ensureStorageClasses ensures that StorageClass resources exist in the desired // state. func (r *ReconcileStorageCluster) ensureStorageClasses(instance *ocsv1.StorageCluster, reqLogger logr.Logger) error { @@ -28,9 +44,18 @@ func (r *ReconcileStorageCluster) ensureStorageClasses(instance *ocsv1.StorageCl if err != nil { return err } + err = r.createStorageClasses(scs, reqLogger) + if err != nil { + return err + } + instance.Status.StorageClassesCreated = true + return nil +} + +func (r *ReconcileStorageCluster) createStorageClasses(scs []*storagev1.StorageClass, reqLogger logr.Logger) error { for _, sc := range scs { existing := storagev1.StorageClass{} - err = r.client.Get(context.TODO(), types.NamespacedName{Name: sc.Name, Namespace: sc.Namespace}, &existing) + err := r.client.Get(context.TODO(), types.NamespacedName{Name: sc.Name, Namespace: sc.Namespace}, &existing) switch { case err == nil: @@ -55,9 +80,117 @@ func (r *ReconcileStorageCluster) ensureStorageClasses(instance *ocsv1.StorageCl } } } + return nil +} - instance.Status.StorageClassesCreated = true - +// ensureExternalStorageClusterResources ensures that requested resources for the external cluster +// being created +func (r *ReconcileStorageCluster) ensureExternalStorageClusterResources(instance *ocsv1.StorageCluster, reqLogger logr.Logger) error { + // check for the status boolean value accepted or not + if instance.Status.ExternalSecretFound { + return nil + } + found := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: externalClusterDetailsSecret, + Namespace: instance.Namespace, + }, + } + err := r.client.Get(context.TODO(), types.NamespacedName{Name: found.Name, Namespace: found.Namespace}, found) + if err != nil { + return err + } + ownerRef := metav1.OwnerReference{ + UID: instance.UID, + APIVersion: instance.APIVersion, + Kind: instance.Kind, + Name: instance.Name, + } + var data []ExternalResource + err = json.Unmarshal(found.Data[externalClusterDetailsKey], &data) + if err != nil { + reqLogger.Error(err, "could not parse json blob") + return err + } + scs, err := r.newStorageClasses(instance) + if err != nil { + reqLogger.Error(err, "failed to create StorageClasses") + return err + } + for _, d := range data { + objectMeta := metav1.ObjectMeta{ + Name: d.Name, + Namespace: instance.Namespace, + OwnerReferences: []metav1.OwnerReference{ownerRef}, + } + objectKey := types.NamespacedName{Name: d.Name, Namespace: instance.Namespace} + switch d.Kind { + case "ConfigMap": + cm := &corev1.ConfigMap{ + ObjectMeta: objectMeta, + Data: d.Data, + } + found := &corev1.ConfigMap{ObjectMeta: objectMeta} + err := r.client.Get(context.TODO(), objectKey, found) + if err != nil { + if errors.IsNotFound(err) { + reqLogger.Info(fmt.Sprintf("creating configmap: %s", cm.Name)) + err = r.client.Create(context.TODO(), cm) + if err != nil { + reqLogger.Error(err, "creation of configmap failed") + return err + } + } else { + reqLogger.Error(err, "unable the get the configmap") + return err + } + } + case "Secret": + sec := &corev1.Secret{ + ObjectMeta: objectMeta, + Data: make(map[string][]byte), + } + for k, v := range d.Data { + sec.Data[k] = []byte(v) + } + found := &corev1.Secret{ObjectMeta: objectMeta} + err := r.client.Get(context.TODO(), objectKey, found) + if err != nil { + if errors.IsNotFound(err) { + reqLogger.Info(fmt.Sprintf("creating secret: %s", sec.Name)) + err = r.client.Create(context.TODO(), sec) + if err != nil { + reqLogger.Error(err, "creation of secret failed") + return err + } + } else { + reqLogger.Error(err, "unable to get the secret") + return err + } + } + case "StorageClass": + var sc *storagev1.StorageClass + if d.Name == cephFsStorageClassName { + // Setting the fsname for cephfs StorageClass + sc = scs[0] + } else if d.Name == cephRbdStorageClassName { + // Setting the PoolName for RBD StorageClass + sc = scs[1] + } else if d.Name == cephRgwStorageClassName { + // Setting the Endpoint for OBC StorageClass + sc = scs[2] + } + for k, v := range d.Data { + sc.Parameters[k] = v + } + } + } + // creating all the storageClasses once we set the values + err = r.createStorageClasses(scs, reqLogger) + if err != nil { + return err + } + instance.Status.ExternalSecretFound = true return nil } @@ -124,8 +257,12 @@ func (r *ReconcileStorageCluster) newStorageClasses(initData *ocsv1.StorageClust }, }, } - // OBC StorageClass will be added only if it is not a cloud platform - if platform, err := r.platform.GetPlatform(r.client); err == nil && !isValidCloudPlatform(platform) { + // OBC storageclass will be returned only in TWO conditions, + // a. either 'externalStorage' is enabled + // OR + // b. current platform is not a cloud-based platform + platform, err := r.platform.GetPlatform(r.client) + if initData.Spec.ExternalStorage.Enable || err == nil && !isValidCloudPlatform(platform) { ret = append(ret, r.newOBCStorageClass(initData)) } return ret, nil diff --git a/pkg/controller/storagecluster/reconcile.go b/pkg/controller/storagecluster/reconcile.go index 0b9c7c9e6e..961bc77cf7 100644 --- a/pkg/controller/storagecluster/reconcile.go +++ b/pkg/controller/storagecluster/reconcile.go @@ -231,6 +231,7 @@ func (r *ReconcileStorageCluster) Reconcile(request reconcile.Request) (reconcil } else { // for external cluster, we have a different set of ensure functions ensureFs = []ensureFunc{ + r.ensureExternalStorageClusterResources, r.ensureCephCluster, } }