Skip to content

Commit

Permalink
Ceph: Create resources for external cluster
Browse files Browse the repository at this point in the history
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 <rajasing@redhat.com>
  • Loading branch information
RAJAT SINGH authored and jarrpa committed May 22, 2020
1 parent 0966215 commit be86eed
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 5 deletions.
4 changes: 4 additions & 0 deletions pkg/apis/ocs/v1/storagecluster_types.go
Expand Up @@ -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"`
Expand Down
7 changes: 7 additions & 0 deletions pkg/apis/ocs/v1/zz_generated.openapi.go
Expand Up @@ -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"},
Expand Down
147 changes: 142 additions & 5 deletions pkg/controller/storagecluster/initialization_reconciler.go
Expand Up @@ -2,6 +2,7 @@ package storagecluster

import (
"context"
"encoding/json"
"fmt"

"github.com/go-logr/logr"
Expand All @@ -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 {
Expand All @@ -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:
Expand All @@ -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
}

Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions pkg/controller/storagecluster/reconcile.go
Expand Up @@ -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,
}
}
Expand Down

0 comments on commit be86eed

Please sign in to comment.