Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion pkg/apis/core/v1beta1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions pkg/controller/storageprofile-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ const (
counterLabelRWX = "rwx"
counterLabelSmartClone = "smartclone"
counterLabelDegraded = "degraded"

recognizedProvisionerMessage = "Provisioner is recognized"
unrecognizedProvisionerMessage = "Provisioner is not recognized"
unrecognizedStorageClassParametersMessage = "Storage class parameters are not recognized"
)

// StorageProfileReconciler members
Expand Down Expand Up @@ -125,6 +129,7 @@ func (r *StorageProfileReconciler) reconcileStorageProfile(sc *storagev1.Storage
}

storageProfile.Status.ClaimPropertySets = claimPropertySets
r.reconcileConditions(context.TODO(), sc, storageProfile)

util.SetRecommendedLabels(storageProfile, r.installerLabels, "cdi-controller")
if err := r.updateStorageProfile(prevStorageProfile, storageProfile, log); err != nil {
Expand Down Expand Up @@ -198,6 +203,32 @@ func (r *StorageProfileReconciler) getStorageProfile(sc *storagev1.StorageClass)
return storageProfile, prevStorageProfile, nil
}

func (r *StorageProfileReconciler) reconcileConditions(ctx context.Context, sc *storagev1.StorageClass, sp *cdiv1.StorageProfile) {
cond := findStorageProfileConditionByType(sp, cdiv1.StorageProfileRecognized)
if cond == nil {
sp.Status.Conditions = append(sp.Status.Conditions, cdiv1.StorageProfileCondition{Type: cdiv1.StorageProfileRecognized})
cond = &sp.Status.Conditions[len(sp.Status.Conditions)-1]
}

switch reason := storagecapabilities.IsRecognized(sc); reason {
case storagecapabilities.RecognizedProvisioner:
updateConditionState(&cond.ConditionState, v1.ConditionTrue, recognizedProvisionerMessage, string(reason))
case storagecapabilities.UnrecognizedProvisioner:
updateConditionState(&cond.ConditionState, v1.ConditionFalse, unrecognizedProvisionerMessage, string(reason))
case storagecapabilities.UnrecognizedStorageClassParameters:
updateConditionState(&cond.ConditionState, v1.ConditionFalse, unrecognizedStorageClassParametersMessage, string(reason))
}
}

func findStorageProfileConditionByType(sp *cdiv1.StorageProfile, condType cdiv1.StorageProfileConditionType) *cdiv1.StorageProfileCondition {
for i := range sp.Status.Conditions {
if sp.Status.Conditions[i].Type == condType {
return &sp.Status.Conditions[i]
}
}
return nil
}

func (r *StorageProfileReconciler) reconcilePropertySets(sc *storagev1.StorageClass) []cdiv1.ClaimPropertySet {
claimPropertySets := []cdiv1.ClaimPropertySet{}
capabilities, found := storagecapabilities.GetCapabilities(r.client, sc)
Expand Down
29 changes: 29 additions & 0 deletions pkg/controller/storageprofile-controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,35 @@ var _ = Describe("Storage profile controller reconcile loop", func() {
Entry("Without RWX, on SNO, not degraded", v1.ReadWriteOnce, true, false),
)

DescribeTable("Should set Recognized condition", func(provisioner string, scParameters map[string]string, expectedStatus v1.ConditionStatus, expectedReason, expectedMessage string) {
storageClass := CreateStorageClassWithProvisioner(storageClassName, nil, nil, provisioner)
storageClass.Parameters = scParameters
reconciler = createStorageProfileReconciler(storageClass)
_, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Name: storageClassName}})
Expect(err).ToNot(HaveOccurred())

sp := &cdiv1.StorageProfile{}
err = reconciler.client.Get(context.TODO(), types.NamespacedName{Name: storageClassName}, sp, &client.GetOptions{})
Expect(err).ToNot(HaveOccurred())

Expect(sp.Status.Conditions).To(HaveLen(1))
cond := sp.Status.Conditions[0]
Expect(cond.Type).To(Equal(cdiv1.StorageProfileRecognized))
Expect(cond.Status).To(Equal(expectedStatus))
Expect(cond.Reason).To(Equal(expectedReason))
Expect(cond.Message).To(Equal(expectedMessage))
},
Entry("recognized provisioner",
cephProvisioner, nil,
v1.ConditionTrue, string(storagecapabilities.RecognizedProvisioner), recognizedProvisionerMessage),
Entry("unrecognized provisioner",
"unknown-provisioner", nil,
v1.ConditionFalse, string(storagecapabilities.UnrecognizedProvisioner), unrecognizedProvisionerMessage),
Entry("recognized provisioner with unrecognized parameters",
"infinibox-csi-driver", map[string]string{"storage_protocol": "unsupported"},
v1.ConditionFalse, string(storagecapabilities.UnrecognizedStorageClassParameters), unrecognizedStorageClassParametersMessage),
)

})

func createStorageProfileReconciler(objects ...runtime.Object) *StorageProfileReconciler {
Expand Down
28 changes: 28 additions & 0 deletions pkg/operator/resources/crds_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions pkg/storagecapabilities/storagecapabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,27 @@ var UnsupportedProvisioners = map[string]struct{}{
storagehelpers.NotSupportedProvisioner: {},
}

// StorageClassRecognizeReason represents the reason for the storage class recognition condition
type StorageClassRecognizeReason string

const (
RecognizedProvisioner StorageClassRecognizeReason = "RecognizedProvisioner"
UnrecognizedProvisioner StorageClassRecognizeReason = "UnrecognizedProvisioner"
UnrecognizedStorageClassParameters StorageClassRecognizeReason = "UnrecognizedStorageClassParameters"
)

// IsRecognized checks if the storage class provisioner and parameters are recognized so capabilities are available
func IsRecognized(sc *storagev1.StorageClass) StorageClassRecognizeReason {
provisionerKey := storageProvisionerKey(sc)
if provisionerKey == "UNKNOWN" {
return UnrecognizedStorageClassParameters
}
if _, found := CapabilitiesByProvisionerKey[provisionerKey]; found {
return RecognizedProvisioner
}
return UnrecognizedProvisioner
}

// GetCapabilities finds and returns a predefined StorageCapabilities for a given StorageClass
func GetCapabilities(cl client.Client, sc *storagev1.StorageClass) ([]StorageCapabilities, bool) {
provisionerKey := storageProvisionerKey(sc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,24 @@ type StorageProfileStatus struct {
DataImportCronSourceFormat *DataImportCronSourceFormat `json:"dataImportCronSourceFormat,omitempty"`
// SnapshotClass is optional specific VolumeSnapshotClass for CloneStrategySnapshot. If not set, a VolumeSnapshotClass is chosen according to the provisioner.
SnapshotClass *string `json:"snapshotClass,omitempty"`
// Conditions contains the current conditions observed for the StorageProfile
Conditions []StorageProfileCondition `json:"conditions,omitempty" optional:"true"`
}

// StorageProfileCondition represents the state of a storage profile condition
type StorageProfileCondition struct {
Type StorageProfileConditionType `json:"type" description:"type of condition ie. Recognized"`
ConditionState `json:",inline"`
}

// StorageProfileConditionType is the string representation of known condition types
type StorageProfileConditionType string

const (
// StorageProfileRecognized is the condition that indicates if the storage class provisioner and parameters are recognized by CDI
StorageProfileRecognized StorageProfileConditionType = "Recognized"
)

// ClaimPropertySet is a set of properties applicable to PVC
type ClaimPropertySet struct {
// AccessModes contains the desired access modes the volume should have.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.