Add annotations to RBD node secrets in import-external-cluster.sh so that externally created secrets with custom names can be discovered at runtime. Update CreateUpdateClientProfileRadosNamespace to look up secrets by annotation instead of using hardcoded names. Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
246 lines
8.6 KiB
Go
246 lines
8.6 KiB
Go
/*
|
|
Copyright 2024 The Rook Authors. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package csi
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
|
|
cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
|
|
cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
|
|
"github.com/rook/rook/pkg/operator/k8sutil"
|
|
|
|
csiopv1 "github.com/ceph/ceph-csi-operator/api/v1"
|
|
"github.com/pkg/errors"
|
|
v1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
)
|
|
|
|
var (
|
|
CephFSDriverName string
|
|
RBDDriverName string
|
|
|
|
ConfigName = "rook-ceph-csi-config"
|
|
ConfigKey = "csi-cluster-config-json"
|
|
)
|
|
|
|
const (
|
|
CsiRBDPlugin = "csi-rbdplugin"
|
|
CsiCephFSPlugin = "csi-cephfsplugin"
|
|
CsiNFSPlugin = "csi-nfsplugin"
|
|
|
|
rbdDriverSuffix = "rbd.csi.ceph.com"
|
|
cephFSDriverSuffix = "cephfs.csi.ceph.com"
|
|
)
|
|
|
|
func CreateUpdateClientProfileRadosNamespace(ctx context.Context, c client.Client, clusterInfo *cephclient.ClusterInfo, cephBlockPoolRadosNamespaceName, clusterID string) error {
|
|
logger.Info("creating ceph-csi clientProfile CR for rados namespace")
|
|
|
|
rbdProvisionerSecretName, err := getSecretNameByAnnotation(c, ctx, clusterInfo.Namespace, "csi.rook.io/RBDProvisionerSecret", CsiRBDProvisionerSecret)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rbdNodeSecretName, err := getSecretNameByAnnotation(c, ctx, clusterInfo.Namespace, "csi.rook.io/RBDNodeSecret", CsiRBDNodeSecret)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
csiOpClientProfile := &csiopv1.ClientProfile{}
|
|
csiOpClientProfile.Name = clusterID
|
|
csiOpClientProfile.Namespace = os.Getenv(k8sutil.PodNamespaceEnvVar)
|
|
csiOpClientProfile.Spec = csiopv1.ClientProfileSpec{
|
|
CephConnectionRef: v1.LocalObjectReference{
|
|
Name: clusterInfo.Namespace,
|
|
},
|
|
Rbd: &csiopv1.RbdConfigSpec{
|
|
RadosNamespace: cephBlockPoolRadosNamespaceName,
|
|
CephCsiSecrets: &csiopv1.CephCsiSecretsSpec{
|
|
ControllerPublishSecret: v1.SecretReference{
|
|
Name: rbdProvisionerSecretName,
|
|
Namespace: clusterInfo.Namespace,
|
|
},
|
|
NodePublishSecret: v1.SecretReference{
|
|
Name: rbdNodeSecretName,
|
|
Namespace: clusterInfo.Namespace,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return createUpdateClientProfile(c, clusterInfo, csiOpClientProfile)
|
|
}
|
|
|
|
func CreateUpdateClientProfileSubVolumeGroup(ctx context.Context, c client.Client, clusterInfo *cephclient.ClusterInfo, cephFilesystemSubVolumeGroupName, clusterID string, csiMetadataRadosNamespace string) error {
|
|
logger.Info("Creating ceph-csi clientProfile CR for subvolume group")
|
|
|
|
csiOpClientProfile, err := generateProfileSubVolumeGroupSpec(c, clusterInfo, cephFilesystemSubVolumeGroupName, clusterID, csiMetadataRadosNamespace)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return createUpdateClientProfile(c, clusterInfo, csiOpClientProfile)
|
|
}
|
|
|
|
func generateProfileSubVolumeGroupSpec(c client.Client, clusterInfo *cephclient.ClusterInfo, cephFilesystemSubVolumeGroupName, clusterID string, csiMetadataRadosNamespace string) (*csiopv1.ClientProfile, error) {
|
|
cephFSSecretName, err := getSecretNameByAnnotation(c, clusterInfo.Context, clusterInfo.Namespace, "csi.rook.io/CephFSProvisionerSecret", CsiCephFSProvisionerSecret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
csiOpClientProfile := &csiopv1.ClientProfile{}
|
|
csiOpClientProfile.Name = clusterID
|
|
csiOpClientProfile.Namespace = os.Getenv(k8sutil.PodNamespaceEnvVar)
|
|
csiOpClientProfile.Spec = csiopv1.ClientProfileSpec{
|
|
CephConnectionRef: v1.LocalObjectReference{
|
|
Name: clusterInfo.Namespace,
|
|
},
|
|
CephFs: &csiopv1.CephFsConfigSpec{
|
|
SubVolumeGroup: cephFilesystemSubVolumeGroupName,
|
|
RadosNamespace: &csiMetadataRadosNamespace,
|
|
CephCsiSecrets: &csiopv1.CephCsiSecretsSpec{
|
|
ControllerPublishSecret: v1.SecretReference{
|
|
Name: cephFSSecretName,
|
|
Namespace: clusterInfo.Namespace,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
applyCephFSMountOptions(clusterInfo, csiOpClientProfile.Spec.CephFs)
|
|
|
|
return csiOpClientProfile, nil
|
|
}
|
|
|
|
func getSecretNameByAnnotation(c client.Client, ctx context.Context, namespace, annotationKey, defaultName string) (string, error) {
|
|
secretList := &v1.SecretList{}
|
|
err := c.List(ctx, secretList, client.InNamespace(namespace))
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "failed to list secrets in namespace %q", namespace)
|
|
}
|
|
for _, secret := range secretList.Items {
|
|
if val, ok := secret.Annotations[annotationKey]; ok && val == "true" {
|
|
return secret.Name, nil
|
|
}
|
|
}
|
|
return defaultName, nil
|
|
}
|
|
|
|
// CreateDefaultClientProfile creates a default client profile for csi-operator to connect driver
|
|
func CreateDefaultClientProfile(c client.Client, clusterInfo *cephclient.ClusterInfo) error {
|
|
logger.Info("Creating ceph-csi clientProfile default CR")
|
|
csiOpClientProfile := &csiopv1.ClientProfile{}
|
|
csiOpClientProfile.Name = clusterInfo.Namespace
|
|
csiOpClientProfile.Namespace = os.Getenv(k8sutil.PodNamespaceEnvVar)
|
|
csiOpClientProfile.Spec = csiopv1.ClientProfileSpec{
|
|
CephConnectionRef: v1.LocalObjectReference{
|
|
Name: clusterInfo.Namespace,
|
|
},
|
|
}
|
|
|
|
// set cephFS ControllerPublish Secret
|
|
if csiOpClientProfile.Spec.CephFs == nil {
|
|
csiOpClientProfile.Spec.CephFs = &csiopv1.CephFsConfigSpec{}
|
|
}
|
|
|
|
applyCephFSMountOptions(clusterInfo, csiOpClientProfile.Spec.CephFs)
|
|
|
|
csiOpClientProfile.Spec.CephFs.CephCsiSecrets = &csiopv1.CephCsiSecretsSpec{
|
|
ControllerPublishSecret: v1.SecretReference{
|
|
Name: CsiCephFSProvisionerSecret,
|
|
Namespace: clusterInfo.Namespace,
|
|
},
|
|
}
|
|
if csiOpClientProfile.Spec.Rbd == nil {
|
|
csiOpClientProfile.Spec.Rbd = &csiopv1.RbdConfigSpec{}
|
|
}
|
|
|
|
// set RBD ControllerPublish Secret
|
|
csiOpClientProfile.Spec.Rbd.CephCsiSecrets = &csiopv1.CephCsiSecretsSpec{
|
|
ControllerPublishSecret: v1.SecretReference{
|
|
Name: CsiRBDProvisionerSecret,
|
|
Namespace: clusterInfo.Namespace,
|
|
},
|
|
NodePublishSecret: v1.SecretReference{
|
|
Name: CsiRBDNodeSecret,
|
|
Namespace: clusterInfo.Namespace,
|
|
},
|
|
}
|
|
|
|
return createUpdateClientProfile(c, clusterInfo, csiOpClientProfile)
|
|
}
|
|
|
|
func createUpdateClientProfile(c client.Client, clusterInfo *cephclient.ClusterInfo, clientProfile *csiopv1.ClientProfile) error {
|
|
existingCsiOpClientProfile := &csiopv1.ClientProfile{}
|
|
err := c.Get(clusterInfo.Context, types.NamespacedName{Name: clientProfile.Name, Namespace: clientProfile.Namespace}, existingCsiOpClientProfile)
|
|
if err != nil {
|
|
if apierrors.IsNotFound(err) {
|
|
err = c.Create(clusterInfo.Context, clientProfile)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to create ceph-csi for clientProfile CR %q", clientProfile.Name)
|
|
}
|
|
logger.Infof("successfully created ceph-csi for clientProfile CR %q", clientProfile.Name)
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
existingCsiOpClientProfile.Spec = clientProfile.Spec
|
|
err = c.Update(clusterInfo.Context, existingCsiOpClientProfile)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to create ceph-csi for clientProfile CR %q", clientProfile.Name)
|
|
}
|
|
logger.Infof("successfully updated ceph-csi for clientProfile CR %q", clientProfile.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
func applyCephFSMountOptions(clusterInfo *cephclient.ClusterInfo, cephFs *csiopv1.CephFsConfigSpec) {
|
|
if reflect.DeepEqual(clusterInfo.CSIDriverSpec.CephFS, cephv1.CSICephFSSpec{}) {
|
|
return
|
|
}
|
|
|
|
if clusterInfo.CSIDriverSpec.CephFS.KernelMountOptions != "" {
|
|
cephFs.KernelMountOptions = parseMountOptions(clusterInfo.CSIDriverSpec.CephFS.KernelMountOptions)
|
|
} else if clusterInfo.CSIDriverSpec.CephFS.FuseMountOptions != "" {
|
|
cephFs.FuseMountOptions = parseMountOptions(clusterInfo.CSIDriverSpec.CephFS.FuseMountOptions)
|
|
}
|
|
}
|
|
|
|
func parseMountOptions(options string) map[string]string {
|
|
logger.Debugf("parsing CephFS mount options : %s ", options)
|
|
result := map[string]string{}
|
|
// Example: SplitSeq("ms_mode=prefer-secure,recover_session=clean", ",") iterates over ["ms_mode=prefer-secure", "recover_session=clean"]
|
|
for part := range strings.SplitSeq(options, ",") {
|
|
part = strings.TrimSpace(part)
|
|
if part == "" {
|
|
continue
|
|
}
|
|
// Example: SplitN("ms_mode=prefer-secure", "=", 2) returns ["ms_mode", "prefer-secure"]
|
|
keyVal := strings.SplitN(part, "=", 2)
|
|
if len(keyVal) == 2 {
|
|
result[strings.TrimSpace(keyVal[0])] = strings.TrimSpace(keyVal[1])
|
|
}
|
|
}
|
|
logger.Debugf("parsed CephFS mount options: %v", result)
|
|
return result
|
|
}
|