Skip to content

Commit 8067853

Browse files
authored
Enhancement - Introduce pvcName input param in CNSUnregisterVolume (#3658)
* Enhancement - Introduce `pvcName` input param in CNSUnregisterVolume spec Adds validation rules using x-kubernetes-validations to ensure that only one of the volumeID or pvcName is specified to avoid ambiguity. Updates the reconciler to use the informer cache to find out the PV and VolumeID when the PVC Name is supplied. Optimises the reconciler logic to ignore all such events that do not increment the generation. Optimises the usage calculation logic by failing fast when usages are detected. Updates the reconciler to add a finalizer to the CR before reconciling to have control over deletion process Updates the reconciler to process the delete events and remove the finalizer for graceful deletion. Updates the reconciler to protect the PVC using a finalizer to gracefully reconciler in case of retries Removes the code that retains the persistent volume as it's no longer required Updates the reconciler to remove the finalizer on the PVC once unregistration is successful for graceful deletion of the PVC. Adds/updates unit tests wherever necessary and applicable. Updates the enablement logic to use WCP capability supports_mobility_non_disruptive_import Removes cns-unregister-volume fss from csi-feature-states configmap * Feature enablement related changes * Updated reconciler to handle CNS Faults in both delete and normal reconcile workflows * Updated manager.UnregisterVolume to return fault along with the error * Updated tests with the new expectations * Refactorings and comments
1 parent 1fcce80 commit 8067853

File tree

27 files changed

+1500
-575
lines changed

27 files changed

+1500
-575
lines changed

manifests/supervisorcluster/1.29/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,6 @@ data:
582582
"cnsmgr-suspend-create-volume": "true"
583583
"storage-quota-m2": "true"
584584
"vdpp-on-stretched-supervisor": "true"
585-
"cns-unregister-volume": "false"
586585
"workload-domain-isolation": "false"
587586
"sv-pvc-snapshot-protection-finalizer": "true"
588587
kind: ConfigMap

manifests/supervisorcluster/1.30/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,6 @@ data:
584584
"list-volumes": "true"
585585
"cnsmgr-suspend-create-volume": "true"
586586
"storage-quota-m2": "true"
587-
"cns-unregister-volume": "false"
588587
"workload-domain-isolation": "false"
589588
"sv-pvc-snapshot-protection-finalizer": "true"
590589
kind: ConfigMap

manifests/supervisorcluster/1.31/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,6 @@ data:
584584
"list-volumes": "true"
585585
"cnsmgr-suspend-create-volume": "true"
586586
"storage-quota-m2": "true"
587-
"cns-unregister-volume": "false"
588587
"workload-domain-isolation": "false"
589588
"sv-pvc-snapshot-protection-finalizer": "true"
590589
kind: ConfigMap

manifests/supervisorcluster/1.32/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,6 @@ data:
584584
"list-volumes": "true"
585585
"cnsmgr-suspend-create-volume": "true"
586586
"storage-quota-m2": "true"
587-
"cns-unregister-volume": "false"
588587
"workload-domain-isolation": "false"
589588
"sv-pvc-snapshot-protection-finalizer": "true"
590589
kind: ConfigMap

pkg/apis/cnsoperator/cnsunregistervolume/v1alpha1/cnsunregistervolume_types.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import (
2424
// +k8s:openapi-gen=true
2525
type CnsUnregisterVolumeSpec struct {
2626
// VolumeID indicates the volume handle of CNS volume to be unregistered
27-
VolumeID string `json:"volumeID"`
27+
VolumeID string `json:"volumeID,omitempty"`
28+
29+
// PVCName indicates the name of the PVC to be unregistered.
30+
PVCName string `json:"pvcName,omitempty"`
2831

2932
// RetainFCD indicates if the volume should be retained as an FCD.
3033
// If set to false or not specified, the volume will be retained as a VMDK.
Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
---
32
apiVersion: apiextensions.k8s.io/v1
43
kind: CustomResourceDefinition
@@ -16,66 +15,80 @@ spec:
1615
singular: cnsunregistervolume
1716
scope: Namespaced
1817
versions:
19-
- name: v1alpha1
20-
schema:
21-
openAPIV3Schema:
22-
description: CnsUnregisterVolume is the Schema for the cnsunregistervolumes API
23-
properties:
24-
apiVersion:
25-
description: 'APIVersion defines the versioned schema of this representation
18+
- name: v1alpha1
19+
schema:
20+
openAPIV3Schema:
21+
description: CnsUnregisterVolume is the Schema for the cnsunregistervolumes API
22+
properties:
23+
apiVersion:
24+
description: 'APIVersion defines the versioned schema of this representation
2625
of an object. Servers should convert recognized schemas to the latest
2726
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
28-
type: string
29-
kind:
30-
description: 'Kind is a string value representing the REST resource this
27+
type: string
28+
kind:
29+
description: 'Kind is a string value representing the REST resource this
3130
object represents. Servers may infer this from the endpoint the client
3231
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
33-
type: string
34-
metadata:
35-
type: object
36-
spec:
37-
description: CnsUnregisterVolumeSpec defines the desired state of CnsUnregisterVolume
38-
properties:
39-
volumeID:
40-
description: VolumeID indicates the volume handle of CNS volume to be unregistered.
41-
type: string
42-
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
43-
retainFCD:
44-
description: RetainFCD indicates if the volume should be retained as an FCD.
45-
If set to false or not specified, the volume will be retained as a VMDK.
46-
type: boolean
47-
forceUnregister:
48-
description: ForceUnregister indicates if the volume should be forcefully unregistered.
49-
If set to true, the volume will be unregistered even if it is still in use by any VM.
50-
This should be used with caution as it may lead to data loss.
51-
type: boolean
52-
required:
53-
- volumeID
54-
type: object
55-
status:
56-
description: CnsUnregisterVolumeStatus defines the observed state of CnsUnregisterVolume
57-
properties:
58-
error:
59-
description: The last error encountered during export operation, if
60-
any. This field must only be set by the entity completing the export
61-
operation, i.e. the CNS Operator.
62-
type: string
63-
unregistered:
64-
description: Indicates the volume is successfully unregistered.
65-
This field must only be set by the entity completing the unregister
66-
operation, i.e. the CNS Operator.
67-
type: boolean
68-
required:
69-
- unregistered
70-
type: object
71-
type: object
72-
served: true
73-
storage: true
74-
subresources:
75-
status: {}
32+
type: string
33+
metadata:
34+
type: object
35+
spec:
36+
description: CnsUnregisterVolumeSpec defines the desired state of CnsUnregisterVolume
37+
properties:
38+
volumeID:
39+
description: VolumeID indicates the volume handle of CNS volume to be unregistered.
40+
type: string
41+
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
42+
pvcName:
43+
description: PVCName indicates the name of the PVC to be unregistered.
44+
type: string
45+
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
46+
retainFCD:
47+
description: RetainFCD indicates if the volume should be retained as an FCD.
48+
If set to false or not specified, the volume will be retained as a VMDK.
49+
type: boolean
50+
forceUnregister:
51+
description: ForceUnregister indicates if the volume should be forcefully unregistered.
52+
If set to true, the volume will be unregistered even if it is still in use by any VM.
53+
This should be used with caution as it may lead to data loss.
54+
type: boolean
55+
type: object
56+
x-kubernetes-validations:
57+
- rule: "has(self.volumeID) || has(self.pvcName)"
58+
message: "Either 'volumeID' or 'pvcName' must be specified, but not both"
59+
- rule: "!(has(self.volumeID) && has(self.pvcName))"
60+
message: "Cannot specify both 'volumeID' and 'pvcName' at the same time. Please specify only one of them"
61+
status:
62+
description: CnsUnregisterVolumeStatus defines the observed state of CnsUnregisterVolume
63+
properties:
64+
error:
65+
description: The last error encountered during export operation, if
66+
any. This field must only be set by the entity completing the export
67+
operation, i.e. the CNS Operator.
68+
type: string
69+
unregistered:
70+
description: Indicates the volume is successfully unregistered.
71+
This field must only be set by the entity completing the unregister
72+
operation, i.e. the CNS Operator.
73+
type: boolean
74+
required:
75+
- unregistered
76+
type: object
77+
type: object
78+
served: true
79+
storage: true
80+
additionalPrinterColumns:
81+
- jsonPath: .status.unregistered
82+
name: Unregistered
83+
type: boolean
84+
- jsonPath: .metadata.creationTimestamp
85+
name: Age
86+
type: date
87+
subresources:
88+
status: { }
7689
status:
7790
acceptedNames:
7891
kind: ""
7992
plural: ""
80-
conditions: []
81-
storedVersions: []
93+
conditions: [ ]
94+
storedVersions: [ ]

pkg/common/cns-lib/volume/manager.go

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ type Manager interface {
157157
vm *cnsvsphere.VirtualMachine, batchAttachRequest []BatchAttachRequest) ([]BatchAttachResult, string, error)
158158
// UnregisterVolume unregisters a volume from CNS.
159159
// If unregisterDisk is true, it will also unregister the disk from FCD.
160-
UnregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) error
160+
UnregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) (string, error)
161161
// SyncVolume returns the aggregated capacity for volumes
162162
SyncVolume(ctx context.Context, syncVolumeSpecs []cnstypes.CnsSyncVolumeSpec) (string, error)
163163
}
@@ -3734,32 +3734,32 @@ func (m *defaultManager) SetListViewNotReady(ctx context.Context) {
37343734

37353735
// UnregisterVolume unregisters a volume from CNS.
37363736
// If unregisterDisk is true, it will also unregister the disk from FCD.
3737-
func (m *defaultManager) UnregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) error {
3737+
func (m *defaultManager) UnregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) (string, error) {
37383738
ctx, cancelFunc := ensureOperationContextHasATimeout(ctx)
37393739
defer cancelFunc()
37403740
start := time.Now()
3741-
err := m.unregisterVolume(ctx, volumeID, unregisterDisk)
3741+
faultType, err := m.unregisterVolume(ctx, volumeID, unregisterDisk)
37423742
if err != nil {
37433743
prometheus.CnsControlOpsHistVec.WithLabelValues(prometheus.PrometheusUnregisterVolumeOpType,
37443744
prometheus.PrometheusFailStatus).Observe(time.Since(start).Seconds())
3745-
return err
3745+
return faultType, err
37463746
}
37473747

37483748
prometheus.CnsControlOpsHistVec.WithLabelValues(prometheus.PrometheusUnregisterVolumeOpType,
37493749
prometheus.PrometheusPassStatus).Observe(time.Since(start).Seconds())
3750-
return nil
3750+
return "", nil
37513751
}
37523752

3753-
func (m *defaultManager) unregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) error {
3754-
log := logger.GetLogger(ctx)
3753+
func (m *defaultManager) unregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) (string, error) {
3754+
log := logger.GetLogger(ctx).With("volumeID", volumeID, "unregisterDisk", unregisterDisk)
37553755

37563756
if m.virtualCenter == nil {
3757-
return errors.New("invalid manager instance")
3757+
return "", errors.New("virtual Center connection not established")
37583758
}
37593759

37603760
err := m.virtualCenter.ConnectCns(ctx)
37613761
if err != nil {
3762-
return errors.New("connecting to CNS failed")
3762+
return ExtractFaultTypeFromErr(ctx, err), errors.New("connecting to CNS failed")
37633763
}
37643764

37653765
targetVolumeType := "FCD"
@@ -3774,50 +3774,50 @@ func (m *defaultManager) unregisterVolume(ctx context.Context, volumeID string,
37743774
}
37753775
task, err := m.virtualCenter.CnsClient.UnregisterVolume(ctx, spec)
37763776
if err != nil {
3777-
msg := "failed to invoke UnregisterVolume API"
3778-
log.Errorf("%s from vCenter %q with err: %v", msg, m.virtualCenter.Config.Host, err)
3779-
return errors.New(msg)
3777+
log.Errorf("CNS UnregisterVolume failed from vCenter %q with err: %v",
3778+
m.virtualCenter.Config.Host, err)
3779+
return ExtractFaultTypeFromErr(ctx, err), err
37803780
}
37813781

37823782
taskInfo, err := m.waitOnTask(ctx, task.Reference())
37833783
if err != nil {
3784-
msg := "failed to get UnregisterVolume taskInfo"
3785-
log.Errorf("%s from vCenter %q with err: %v",
3786-
msg, m.virtualCenter.Config.Host, err)
3787-
return errors.New(msg)
3784+
log.Errorf("failed to wait for UnregisterVolume task completion from vCenter %q with err: %v",
3785+
m.virtualCenter.Config.Host, err)
3786+
return ExtractFaultTypeFromErr(ctx, err), err
37883787
}
37893788

37903789
if taskInfo == nil {
3791-
msg := "taskInfo is empty for UnregisterVolume task"
3792-
log.Errorf("%s from vCenter %q. task: %q",
3790+
msg := "taskInfo is nil for UnregisterVolume task"
3791+
log.Errorf("%s from vCenter %q. taskID: %q",
37933792
msg, m.virtualCenter.Config.Host, task.Reference().Value)
3794-
return errors.New(msg)
3793+
return csifault.CSITaskInfoEmptyFault, errors.New(msg)
37953794
}
37963795

37973796
log.Infof("processing UnregisterVolume task: %q, opId: %q",
37983797
taskInfo.Task.Value, taskInfo.ActivationId)
37993798
res, err := getTaskResultFromTaskInfo(ctx, taskInfo)
38003799
if err != nil {
3801-
msg := "failed to get UnregisterVolume task result"
3802-
log.Errorf("%s with error: %v", msg, err)
3803-
return errors.New(msg)
3800+
log.Errorf("failed to get the task result for UnregisterVolume task from vCenter %q with err: %v",
3801+
m.virtualCenter.Config.Host, err)
3802+
return ExtractFaultTypeFromErr(ctx, err), err
38043803
}
38053804

38063805
if res == nil {
3807-
msg := "task result is empty for UnregisterVolume task"
3808-
log.Errorf("%s from vCenter %q. taskID: %q, opId: %q",
3806+
msg := "task result is nil for UnregisterVolume task"
3807+
log.Errorf("%s from vCenter %q. taskID: %q, opId: %q", msg,
38093808
m.virtualCenter.Config.Host, taskInfo.Task.Value, taskInfo.ActivationId)
3810-
return errors.New(msg)
3809+
return csifault.CSITaskResultEmptyFault, errors.New(msg)
38113810
}
38123811

38133812
volOpRes := res.GetCnsVolumeOperationResult()
38143813
if volOpRes.Fault != nil {
3815-
msg := "observed a fault in UnregisterVolume result"
3816-
log.Errorf("%s volume: %q, fault: %q, opID: %q",
3817-
msg, volumeID, ExtractFaultTypeFromVolumeResponseResult(ctx, volOpRes), taskInfo.ActivationId)
3818-
return errors.New(msg)
3814+
msg := "volume operation result contains fault"
3815+
fault := ExtractFaultTypeFromVolumeResponseResult(ctx, volOpRes)
3816+
log.Errorf("%s from vCenter %q. fault: %q, opId: %q", msg,
3817+
m.virtualCenter.Config.Host, fault, taskInfo.ActivationId)
3818+
return fault, errors.New(msg)
38193819
}
38203820

38213821
log.Infof("volume %q unregistered successfully", volumeID)
3822-
return nil
3822+
return "", nil
38233823
}

pkg/common/cns-lib/volume/manager_mock.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,19 @@ type MockManager struct {
3131
failRequest bool
3232
// err is used to store the error that should be returned by the mock manager.
3333
err error
34+
// faultType is used to store the fault type that should be returned by the mock manager.
35+
faultType string
3436
}
3537

36-
func NewMockManager(failReq bool, err error) *MockManager {
38+
func NewMockManager(failReq bool, err error, faultType string) *MockManager {
3739
if !failReq {
3840
return &MockManager{}
3941
}
4042

4143
return &MockManager{
4244
failRequest: failReq,
4345
err: err,
46+
faultType: faultType,
4447
}
4548
}
4649

@@ -184,12 +187,12 @@ func (m MockManager) BatchAttachVolumes(ctx context.Context, vm *cnsvsphere.Virt
184187
panic("implement me")
185188
}
186189

187-
func (m MockManager) UnregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) error {
190+
func (m MockManager) UnregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) (string, error) {
188191
if m.failRequest {
189-
return m.err
192+
return "", m.err
190193
}
191194

192-
return nil
195+
return "", nil
193196
}
194197

195198
func (m MockManager) SyncVolume(ctx context.Context, syncVolumeSpecs []cnstypes.CnsSyncVolumeSpec) (string, error) {

pkg/common/fault/constants.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,6 @@ const (
9494
VimFaultInvalidArgument = VimFaultPrefix + "InvalidArgument"
9595
// VimFaultCNSFault is the fault returned from CNS when a generic CNS fault occurs.
9696
VimFaultCNSFault = VimFaultPrefix + "CnsFault"
97+
// VimFaultNotSupported is the fault returned from CNS when the operation is not supported on the object.
98+
VimFaultNotSupported = VimFaultPrefix + "NotSupported"
9799
)

pkg/common/unittestcommon/types.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ type MockVolumeManager struct {
108108
extraParams interface{}) (*cnsvolume.CnsVolumeInfo, string, error)
109109
}
110110

111-
func (m *MockVolumeManager) UnregisterVolume(ctx context.Context, volumeID string, unregisterDisk bool) error {
111+
func (m *MockVolumeManager) UnregisterVolume(ctx context.Context, volumeID string,
112+
unregisterDisk bool) (string, error) {
112113
//TODO implement me
113-
return nil
114+
return "", nil
114115
}
115116

116117
func (m *MockVolumeManager) CreateVolume(ctx context.Context, spec *cnstypes.CnsVolumeCreateSpec,

0 commit comments

Comments
 (0)