Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import (
csifault "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/fault"
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/prometheus"
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/utils"
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common"
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common/commonco"
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger"
k8s "sigs.k8s.io/vsphere-csi-driver/v3/pkg/kubernetes"
cnsoptypes "sigs.k8s.io/vsphere-csi-driver/v3/pkg/syncer/cnsoperator/types"
Expand All @@ -69,6 +71,7 @@ const (
var (
backOffDuration map[k8stypes.NamespacedName]time.Duration
backOffDurationMapMutex = sync.Mutex{}
isSharedDiskEnabled bool
)

// Mockable function variables for testing
Expand Down Expand Up @@ -135,6 +138,9 @@ func Add(mgr manager.Manager, clusterFlavor cnstypes.CnsClusterFlavor,
return err
}

// If the capability gets enabled at a later point, then container will be restarted and this value will be reinitialized.
isSharedDiskEnabled = commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.SharedDiskFss)

recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: cnsoperatorapis.GroupName})
return add(mgr, newReconciler(mgr, configInfo, volumeManager, vmOperatorClient, recorder))
}
Expand Down Expand Up @@ -347,6 +353,17 @@ func (r *ReconcileCnsNodeVMAttachment) Reconcile(ctx context.Context,
}
nodeUUID := instance.Spec.NodeUUID
if !instance.Status.Attached && instance.DeletionTimestamp == nil {

if isSharedDiskEnabled {
// If isSharedDiskEnabled is enabled, then all new attach operations should happen via the
// new CnsNodeVMBatchAttachment CRD.
err := r.updateErrorOnInstanceToDisallowAttach(ctx, instance)
if err != nil {
return reconcile.Result{RequeueAfter: timeout}, csifault.CSIInternalFault, nil
}
return reconcile.Result{}, "", nil
}

nodeVM, err := getVMByUUIDFromVCenter(internalCtx, dc, nodeUUID)
if err != nil {
msg := fmt.Sprintf("failed to find the VM with UUID: %q for CnsNodeVmAttachment "+
Expand Down Expand Up @@ -817,6 +834,25 @@ func isVmCrPresent(ctx context.Context, vmOperatorClient client.Client,
return nil, nil
}

// updateErrorOnInstanceToDisallowAttach sets error on the CnsNodeVMAttachment CR
// so that devops user can detach this volume
// from the VM and re-attach it so that the new Batch Attach flow is triggered.
func (r *ReconcileCnsNodeVMAttachment) updateErrorOnInstanceToDisallowAttach(ctx context.Context,
instance *v1a1.CnsNodeVmAttachment) error {
log := logger.GetLogger(ctx)

log.Infof("Attach should happen via the new CnsNodeVMBatchAttachment CRD. Skipping attach.")
msg := "CnsNodeVMAttachment CR is deprecated. Please detach this PVC from the VM and then reattach it."
instance.Status.Error = msg
err := k8s.UpdateStatus(ctx, r.client, instance)
if err != nil {
log.Errorf("updateCnsNodeVMAttachment failed. err: %v", err)
return err
}
recordEvent(ctx, r, instance, v1.EventTypeWarning, msg)
return nil
}

// getVCDatacenterFromConfig returns datacenter registered for each vCenter
func getVCDatacentersFromConfig(cfg *config.Config) (map[string][]string, error) {
var err error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,60 @@ func TestReconcileDetachWithVolumeIDFallbackFailure(t *testing.T) {

t.Logf("✓ Reconcile correctly handled getVolumeID failure and requeued for retry")
}

func TestUpdateErrorOnInstanceToDisallowAttach(t *testing.T) {
ctx := context.Background()

// Set up the scheme
SchemeGroupVersion := schema.GroupVersion{
Group: "cns.vmware.com",
Version: "v1alpha1",
}
s := scheme.Scheme
s.AddKnownTypes(SchemeGroupVersion, &v1a1.CnsNodeVmAttachment{})
metav1.AddToGroupVersion(s, SchemeGroupVersion)

// Create CnsNodeVmAttachment instance
instance := &v1a1.CnsNodeVmAttachment{
ObjectMeta: metav1.ObjectMeta{
Name: "test-attachment",
Namespace: "test-ns",
},
}

// Create fake client with status subresource
fakeClient := fake.NewClientBuilder().
WithScheme(s).
WithStatusSubresource(instance).
WithRuntimeObjects(instance).
Build()

// Create reconciler
r := &ReconcileCnsNodeVMAttachment{
client: fakeClient,
recorder: record.NewFakeRecorder(10),
scheme: s,
}

backOffDuration = make(map[k8stypes.NamespacedName]time.Duration)

// Call the function
err := r.updateErrorOnInstanceToDisallowAttach(ctx, instance)

// Assertions
assert.NoError(t, err, "Function should not return an error")
assert.Equal(t,
"CnsNodeVMAttachment CR is deprecated. Please detach this PVC from the VM and then reattach it.",
instance.Status.Error,
"Status.Error should be updated with the deprecation message",
)

// Verify that the status was persisted by fetching the object from the fake client
fetched := &v1a1.CnsNodeVmAttachment{}
err = fakeClient.Get(ctx, k8stypes.NamespacedName{
Name: "test-attachment",
Namespace: "test-ns",
}, fetched)
assert.NoError(t, err, "Should be able to fetch the instance from fake client")
assert.Equal(t, instance.Status.Error, fetched.Status.Error, "Status should be persisted in fake client")
}