Skip to content

Commit d1858ea

Browse files
committed
controllers: Generates VGSClass
Signed-off-by: raaizik <[email protected]>
1 parent 273a0c9 commit d1858ea

File tree

9 files changed

+1107
-7
lines changed

9 files changed

+1107
-7
lines changed

bundle/manifests/ocs-client-operator.clusterserviceversion.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,24 @@ spec:
208208
- list
209209
- update
210210
- watch
211+
- apiGroups:
212+
- groupsnapshot.storage.k8s.io
213+
resources:
214+
- volumegroupsnapshotclasses
215+
verbs:
216+
- create
217+
- delete
218+
- get
219+
- list
220+
- watch
221+
- apiGroups:
222+
- groupsnapshot.storage.k8s.io
223+
resources:
224+
- volumegroupsnapshotcontents
225+
verbs:
226+
- get
227+
- list
228+
- watch
211229
- apiGroups:
212230
- monitoring.coreos.com
213231
resources:

config/rbac/role.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,24 @@ rules:
165165
- list
166166
- update
167167
- watch
168+
- apiGroups:
169+
- groupsnapshot.storage.k8s.io
170+
resources:
171+
- volumegroupsnapshotclasses
172+
verbs:
173+
- create
174+
- delete
175+
- get
176+
- list
177+
- watch
178+
- apiGroups:
179+
- groupsnapshot.storage.k8s.io
180+
resources:
181+
- volumegroupsnapshotcontents
182+
verbs:
183+
- get
184+
- list
185+
- watch
168186
- apiGroups:
169187
- monitoring.coreos.com
170188
resources:

internal/controller/operatorconfigmap_controller.go

+4
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,10 @@ func (c *OperatorConfigMapReconciler) reconcileDelegatedCSI() error {
351351
templates.CSIOperatorConfigSpec.DeepCopyInto(&csiOperatorConfig.Spec)
352352
csiOperatorConfig.Spec.DriverSpecDefaults.ImageSet = &corev1.LocalObjectReference{Name: cmName}
353353
csiOperatorConfig.Spec.DriverSpecDefaults.ClusterName = ptr.To(string(clusterVersion.Spec.ClusterID))
354+
if c.AvailableCrds[volumeGroupSnapshotClassCrd] {
355+
csiOperatorConfig.Spec.DriverSpecDefaults.SnapshotPolicy = csiopv1a1.VolumeGroupSnapshotPolicy
356+
}
357+
354358
return nil
355359
}); err != nil {
356360
return fmt.Errorf("failed to reconcile csi operator config: %v", err)

internal/controller/storageclaim_controller.go

+118-7
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,23 @@ import (
2020
"context"
2121
"encoding/json"
2222
"fmt"
23+
"slices"
24+
"strings"
25+
2326
v1alpha1 "github.com/red-hat-storage/ocs-client-operator/api/v1alpha1"
2427
"github.com/red-hat-storage/ocs-client-operator/pkg/templates"
2528
"github.com/red-hat-storage/ocs-client-operator/pkg/utils"
26-
"slices"
27-
"strings"
2829

2930
csiopv1a1 "github.com/ceph/ceph-csi-operator/api/v1alpha1"
3031
"github.com/go-logr/logr"
3132

3233
replicationv1alpha1 "github.com/csi-addons/kubernetes-csi-addons/api/replication.storage/v1alpha1"
34+
groupsnapapi "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumegroupsnapshot/v1alpha1"
3335
snapapi "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1"
3436
providerclient "github.com/red-hat-storage/ocs-operator/services/provider/api/v4/client"
3537
corev1 "k8s.io/api/core/v1"
3638
storagev1 "k8s.io/api/storage/v1"
39+
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3740
"k8s.io/apimachinery/pkg/api/errors"
3841
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3942
"k8s.io/apimachinery/pkg/runtime"
@@ -42,6 +45,7 @@ import (
4245
"sigs.k8s.io/controller-runtime/pkg/cache"
4346
"sigs.k8s.io/controller-runtime/pkg/client"
4447
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
48+
"sigs.k8s.io/controller-runtime/pkg/handler"
4549
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
4650
"sigs.k8s.io/controller-runtime/pkg/predicate"
4751
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -52,8 +56,11 @@ const (
5256
storageClaimAnnotation = "ocs.openshift.io/storageclaim"
5357
keyRotationAnnotation = "keyrotation.csiaddons.openshift.io/schedule"
5458

55-
pvClusterIDIndexName = "index:persistentVolumeClusterID"
56-
vscClusterIDIndexName = "index:volumeSnapshotContentCSIDriver"
59+
pvClusterIDIndexName = "index:persistentVolumeClusterID"
60+
vscClusterIDIndexName = "index:volumeSnapshotContentCSIDriver"
61+
vgscClusterIDIndexName = "index:volumeGroupSnapshotContentCSIDriver"
62+
volumeGroupSnapshotClassCrd = "groupsnapshot.storage.k8s.io/volumegroupsnapshotclass"
63+
drClusterConfigCRDName = "drclusterconfigs.ramendr.openshift.io"
5764
)
5865

5966
// StorageClaimReconciler reconciles a StorageClaim object
@@ -62,6 +69,7 @@ type StorageClaimReconciler struct {
6269
cache.Cache
6370
Scheme *runtime.Scheme
6471
OperatorNamespace string
72+
AvailableCrds map[string]bool
6573

6674
log logr.Logger
6775
ctx context.Context
@@ -104,13 +112,46 @@ func (r *StorageClaimReconciler) SetupWithManager(mgr ctrl.Manager) error {
104112
return fmt.Errorf("unable to set up FieldIndexer for VSC csi driver name: %v", err)
105113
}
106114

115+
if err := mgr.GetCache().IndexField(ctx, &groupsnapapi.VolumeGroupSnapshotContent{}, vgscClusterIDIndexName, func(o client.Object) []string {
116+
vgsc := o.(*groupsnapapi.VolumeGroupSnapshotContent)
117+
if vgsc != nil &&
118+
slices.Contains(csiDrivers, vgsc.Spec.Driver) &&
119+
vgsc.Status != nil &&
120+
vgsc.Status.VolumeGroupSnapshotHandle != nil {
121+
parts := strings.Split(*vgsc.Status.VolumeGroupSnapshotHandle, "-")
122+
if len(parts) == 9 {
123+
// second entry in the volumeID is clusterID which is unique across the cluster
124+
return []string{parts[2]}
125+
}
126+
}
127+
return nil
128+
}); err != nil {
129+
return fmt.Errorf("unable to set up FieldIndexer for VSC csi driver name: %v", err)
130+
}
131+
107132
generationChangePredicate := predicate.GenerationChangedPredicate{}
108133
bldr := ctrl.NewControllerManagedBy(mgr).
109134
For(&v1alpha1.StorageClaim{}, builder.WithPredicates(generationChangePredicate)).
110135
Owns(&storagev1.StorageClass{}).
111136
Owns(&snapapi.VolumeSnapshotClass{}).
112-
Owns(&replicationv1alpha1.VolumeReplicationClass{}, builder.WithPredicates(generationChangePredicate)).
113-
Owns(&csiopv1a1.ClientProfile{}, builder.WithPredicates(generationChangePredicate))
137+
Owns(&csiopv1a1.ClientProfile{}, builder.WithPredicates(generationChangePredicate)).
138+
Watches(
139+
&extv1.CustomResourceDefinition{},
140+
&handler.EnqueueRequestForObject{},
141+
builder.WithPredicates(
142+
utils.NamePredicate(volumeGroupSnapshotClassCrd),
143+
utils.EventTypePredicate(
144+
!r.AvailableCrds[volumeGroupSnapshotClassCrd],
145+
false,
146+
r.AvailableCrds[volumeGroupSnapshotClassCrd],
147+
false,
148+
),
149+
),
150+
builder.OnlyMetadata,
151+
)
152+
if r.AvailableCrds[volumeGroupSnapshotClassCrd] {
153+
bldr = bldr.Owns(&groupsnapapi.VolumeGroupSnapshotClass{})
154+
}
114155

115156
return bldr.Complete(r)
116157
}
@@ -121,8 +162,10 @@ func (r *StorageClaimReconciler) SetupWithManager(mgr ctrl.Manager) error {
121162
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete
122163
//+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch;create;update;patch;delete
123164
//+kubebuilder:rbac:groups=snapshot.storage.k8s.io,resources=volumesnapshotclasses,verbs=get;list;watch;create;delete
165+
//+kubebuilder:rbac:groups=groupsnapshot.storage.k8s.io,resources=volumegroupsnapshotclasses,verbs=create;delete;get;list;watch
124166
//+kubebuilder:rbac:groups=core,resources=persistentvolumes,verbs=get;list;watch
125167
//+kubebuilder:rbac:groups=snapshot.storage.k8s.io,resources=volumesnapshotcontents,verbs=get;list;watch
168+
//+kubebuilder:rbac:groups=groupsnapshot.storage.k8s.io,resources=volumegroupsnapshotcontents,verbs=get;list;watch
126169
//+kubebuilder:rbac:groups=csi.ceph.io,resources=clientprofiles,verbs=get;list;update;create;watch;delete
127170
//+kubebuilder:rbac:groups=replication.storage.openshift.io,resources=volumereplicationclasses,verbs=get;list;watch;create;delete
128171

@@ -141,6 +184,17 @@ func (r *StorageClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request
141184
r.ctx = ctrllog.IntoContext(ctx, r.log)
142185
r.log.Info("Reconciling StorageClaim.")
143186

187+
crd := &metav1.PartialObjectMetadata{}
188+
for _, crdName := range []string{volumeGroupSnapshotClassCrd, drClusterConfigCRDName} {
189+
crd.SetGroupVersionKind(extv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"))
190+
crd.Name = crdName
191+
if err := r.Client.Get(ctx, client.ObjectKeyFromObject(crd), crd); client.IgnoreNotFound(err) != nil {
192+
r.log.Error(err, "Failed to get CRD", "CRD", crdName)
193+
return reconcile.Result{}, err
194+
}
195+
}
196+
utils.AssertEqual(r.AvailableCrds[crd.Name], crd.UID != "", utils.ExitCodeThatShouldRestartTheProcess)
197+
144198
// Fetch the StorageClaim instance
145199
r.storageClaim = &v1alpha1.StorageClaim{}
146200
r.storageClaim.Name = req.Name
@@ -401,7 +455,31 @@ func (r *StorageClaimReconciler) reconcilePhases() (reconcile.Result, error) {
401455
return nil
402456
})
403457
if err != nil {
404-
return reconcile.Result{}, fmt.Errorf("failed to create or update VolumeSnapshotClass: %s", err)
458+
return reconcile.Result{}, fmt.Errorf("failed to create or update StorageClass: %s", err)
459+
}
460+
case "VolumeGroupSnapshotClass":
461+
// check for CRD availability
462+
if r.AvailableCrds[("VolumeGroupSnapshotClass")] {
463+
var volumeGroupSnapshotClass *groupsnapapi.VolumeGroupSnapshotClass
464+
data := map[string]string{}
465+
err = json.Unmarshal(resource.Data, &data)
466+
if err != nil {
467+
return reconcile.Result{}, fmt.Errorf("failed to unmarshal StorageClaim configuration response: %v", err)
468+
}
469+
data["csi.storage.k8s.io/group-snapshotter-secret-namespace"] = r.OperatorNamespace
470+
// generate a new clusterID for cephfs subvolumegroup, as
471+
// storageclaim is clusterscoped resources using its
472+
// hash as the clusterID
473+
data["clusterID"] = r.storageClaimHash
474+
volumeGroupSnapshotClass = r.getCephDriverVolumeGroupSnapshotClass(resource.Labels, resource.Annotations)
475+
utils.AddAnnotation(volumeGroupSnapshotClass, storageClaimAnnotation, r.storageClaim.Name)
476+
err = utils.CreateOrReplace(r.ctx, r.Client, volumeGroupSnapshotClass, func() error {
477+
volumeGroupSnapshotClass.Parameters = data
478+
return nil
479+
})
480+
if err != nil {
481+
return reconcile.Result{}, fmt.Errorf("failed to create or update StorageClass: %s", err)
482+
}
405483
}
406484
case "VolumeReplicationClass":
407485
vrc := &replicationv1alpha1.VolumeReplicationClass{}
@@ -466,6 +544,11 @@ func (r *StorageClaimReconciler) reconcilePhases() (reconcile.Result, error) {
466544
} else if exist {
467545
return reconcile.Result{}, fmt.Errorf("one or more volumesnapshotcontents exist that are dependent on storageclaim %s", r.storageClaim.Name)
468546
}
547+
if exist, err := r.hasVolumeGroupSnapshotContents(); err != nil {
548+
return reconcile.Result{}, fmt.Errorf("failed to verify volumegroupsnapshotcontents dependent on storageclaim %q: %v", r.storageClaim.Name, err)
549+
} else if exist {
550+
return reconcile.Result{}, fmt.Errorf("one or more volumegroupsnapshotcontents exist that are dependent on storageclaim %s", r.storageClaim.Name)
551+
}
469552

470553
// Call `RevokeStorageClaim` service on the provider server with StorageClaim as a request message.
471554
// Check if StorageClaim is still exists (it might have been manually removed during the StorageClass
@@ -550,6 +633,20 @@ func (r *StorageClaimReconciler) getCephRBDVolumeSnapshotClass() *snapapi.Volume
550633
return volumesnapshotclass
551634
}
552635

636+
func (r *StorageClaimReconciler) getCephDriverVolumeGroupSnapshotClass(
637+
labels map[string]string, annotations map[string]string) *groupsnapapi.VolumeGroupSnapshotClass {
638+
volumegroupsnapshotclass := &groupsnapapi.VolumeGroupSnapshotClass{
639+
ObjectMeta: metav1.ObjectMeta{
640+
Name: r.storageClaim.Name,
641+
Labels: labels,
642+
Annotations: annotations,
643+
},
644+
Driver: templates.RBDDriverName,
645+
DeletionPolicy: snapapi.VolumeSnapshotContentDelete,
646+
}
647+
return volumegroupsnapshotclass
648+
}
649+
553650
func (r *StorageClaimReconciler) get(obj client.Object) error {
554651
key := client.ObjectKeyFromObject(obj)
555652
return r.Client.Get(r.ctx, key, obj)
@@ -594,3 +691,17 @@ func (r *StorageClaimReconciler) hasVolumeSnapshotContents() (bool, error) {
594691

595692
return false, nil
596693
}
694+
695+
func (r *StorageClaimReconciler) hasVolumeGroupSnapshotContents() (bool, error) {
696+
vscList := &groupsnapapi.VolumeGroupSnapshotContentList{}
697+
if err := r.list(vscList, client.MatchingFields{vgscClusterIDIndexName: r.storageClaimHash}); err != nil {
698+
return false, fmt.Errorf("failed to list volume group snapshot content resources: %v", err)
699+
}
700+
701+
if len(vscList.Items) != 0 {
702+
r.log.Info(fmt.Sprintf("VolumeGroupSnapshotContent referring storageclaim %q exists", r.storageClaim.Name))
703+
return true, nil
704+
}
705+
706+
return false, nil
707+
}

vendor/github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumegroupsnapshot/v1alpha1/doc.go

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumegroupsnapshot/v1alpha1/register.go

+57
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)