Skip to content

Commit

Permalink
Feature gate for 23.07 Trident features
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Meade <[email protected]>
  • Loading branch information
VinayKumarHavanur and ameade authored Jul 18, 2023
1 parent 6bd58b9 commit 724169b
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 2 deletions.
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ var (
6: "SINGLE_NODE_SINGLE_WRITER",
7: "SINGLE_NODE_MULTI_WRITER",
}

// DisableExtraFeatures makes a subset of Trident features disabled
// This can be removed when ACP replaces feature-gating
DisableExtraFeatures = true
)

func IsValidProtocol(p Protocol) bool {
Expand Down
5 changes: 5 additions & 0 deletions frontend/crd/snapshot_restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"

"github.com/netapp/trident/config"
. "github.com/netapp/trident/logging"
netappv1 "github.com/netapp/trident/persistent_store/crd/apis/netapp/v1"
"github.com/netapp/trident/storage"
Expand Down Expand Up @@ -61,6 +62,10 @@ func (c *TridentCrdController) handleActionSnapshotRestore(keyItem *KeyItem) (re
}
}()

if config.DisableExtraFeatures {
return errors.UnsupportedError("snapshot restore is not enabled")
}

// Detect a CR that is in progress but is not a retry from the workqueue. This can only happen
// if Trident restarted while processing a CR, in which case we move the CR directly to Failed.
if actionCR.InProgress() && !keyItem.isRetry {
Expand Down
75 changes: 75 additions & 0 deletions frontend/crd/snapshot_restore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/netapp/trident/config"
mockcore "github.com/netapp/trident/mocks/mock_core"
netappv1 "github.com/netapp/trident/persistent_store/crd/apis/netapp/v1"
"github.com/netapp/trident/utils"
Expand Down Expand Up @@ -141,6 +142,9 @@ func fakeTASR(name, namespace, pvcName, vsName string) *netappv1.TridentActionSn
}

func TestHandleActionSnapshotRestore(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

Expand Down Expand Up @@ -237,7 +241,78 @@ func TestHandleActionSnapshotRestore(t *testing.T) {
assert.True(t, apierrors.IsNotFound(err), "TASR should not have been found")
}

func TestHandleActionSnapshotRestore_Disabled(t *testing.T) {
mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

tridentNamespace := "trident"
kubeClient := GetTestKubernetesClientset()
snapClient := GetTestSnapshotClientset()
crdClient := GetTestCrdClientset()
crdController, err := newTridentCrdControllerImpl(orchestrator, tridentNamespace, kubeClient, snapClient, crdClient)
if err != nil {
t.Fatalf("cannot create Trident CRD controller frontend; %v", err)
}

// Activate the CRD controller and start monitoring
if err = crdController.Activate(); err != nil {
t.Fatalf("error while activating; %v", err)
}
time.Sleep(250 * time.Millisecond)

pvc := fakeSnapRestorePVC(snapRestorePVC1, namespace1, snapRestorePV1)
_, _ = kubeClient.CoreV1().PersistentVolumeClaims(namespace1).Create(ctx(), pvc, createOpts)

pv := fakePV(snapRestorePVC1, namespace1, snapRestorePV1)
_, _ = kubeClient.CoreV1().PersistentVolumes().Create(ctx(), pv, createOpts)

vs1Time := time.Now()
vs2Time := vs1Time.Add(1 * time.Second)
vs3Time := vs2Time.Add(1 * time.Second)

vs1 := fakeVS(snapRestoreSnap1, namespace1, snapRestoreVSC1, snapRestorePVC1, vs1Time)
_, _ = snapClient.SnapshotV1().VolumeSnapshots(namespace1).Create(ctx(), vs1, createOpts)

vsc1 := fakeVSC(snapRestoreSnap1, namespace1, snapRestoreVSC1, snapRestoreSnapHandle1, vs1Time)
_, _ = snapClient.SnapshotV1().VolumeSnapshotContents().Create(ctx(), vsc1, createOpts)

vs2 := fakeVS(snapRestoreSnap2, namespace1, snapRestoreVSC2, snapRestorePVC1, vs2Time)
_, _ = snapClient.SnapshotV1().VolumeSnapshots(namespace1).Create(ctx(), vs2, createOpts)

vsc2 := fakeVSC(snapRestoreSnap2, namespace1, snapRestoreVSC2, snapRestoreSnapHandle2, vs2Time)
_, _ = snapClient.SnapshotV1().VolumeSnapshotContents().Create(ctx(), vsc2, createOpts)

vs3 := fakeVS(snapRestoreSnap3, namespace1, snapRestoreVSC3, snapRestorePVC1, vs3Time)
_, _ = snapClient.SnapshotV1().VolumeSnapshots(namespace1).Create(ctx(), vs3, createOpts)

vsc3 := fakeVSC(snapRestoreSnap3, namespace1, snapRestoreVSC3, snapRestoreSnapHandle3, vs3Time)
_, _ = snapClient.SnapshotV1().VolumeSnapshotContents().Create(ctx(), vsc3, createOpts)

tasr := fakeTASR(tasr1, namespace1, snapRestorePVC1, snapRestoreSnap3)
_, _ = crdClient.TridentV1().TridentActionSnapshotRestores(namespace1).Create(ctx(), tasr, createOpts)

// Wait until the operation completes
for i := 0; i < 20; i++ {
time.Sleep(250 * time.Millisecond)

tasr, err = crdClient.TridentV1().TridentActionSnapshotRestores(namespace1).Get(ctx(), tasr1, getOpts)
if err != nil {
if apierrors.IsNotFound(err) {
continue
}
break
} else if tasr.IsComplete() {
break
}
}

assert.True(t, tasr.Failed(), "TASR operation did not fail")
}

func TestHandleActionSnapshotRestore_InProgressError(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

Expand Down
5 changes: 5 additions & 0 deletions frontend/crd/trident_action_mirror_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"

"github.com/netapp/trident/config"
. "github.com/netapp/trident/logging"
netappv1 "github.com/netapp/trident/persistent_store/crd/apis/netapp/v1"
"github.com/netapp/trident/storage"
Expand Down Expand Up @@ -62,6 +63,10 @@ func (c *TridentCrdController) handleActionMirrorUpdate(keyItem *KeyItem) (updat
}
}()

if config.DisableExtraFeatures {
return errors.UnsupportedError("mirror update is not enabled")
}

// Detect a CR that is in progress but is not a retry from the workqueue.
// This can only happen if Trident restarted while processing a CR, in which case we move the CR directly to Failed.
if actionCR.InProgress() && !keyItem.isRetry {
Expand Down
65 changes: 65 additions & 0 deletions frontend/crd/trident_action_mirror_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/netapp/trident/config"
mockcore "github.com/netapp/trident/mocks/mock_core"
netappv1 "github.com/netapp/trident/persistent_store/crd/apis/netapp/v1"
"github.com/netapp/trident/utils"
Expand Down Expand Up @@ -107,6 +108,9 @@ func fakeTAMU(name, namespace, tmrName, snapshotHandle string) *netappv1.Trident
}

func TestHandleActionMirrorUpdate(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

Expand Down Expand Up @@ -258,6 +262,9 @@ func TestHandleActionMirrorUpdate_ValidateFailure(t *testing.T) {
}

func TestHandleActionMirrorUpdate_InProgress(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

Expand Down Expand Up @@ -320,7 +327,56 @@ func TestHandleActionMirrorUpdate_InProgress(t *testing.T) {
assert.True(t, tamu.Succeeded(), "TAMU operation failed")
}

func TestHandleActionMirrorUpdate_InProgress_Disabled(t *testing.T) {
mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

tridentNamespace := "trident"
kubeClient := GetTestKubernetesClientset()
snapClient := GetTestSnapshotClientset()
crdClient := GetTestCrdClientset()
crdController, err := newTridentCrdControllerImpl(orchestrator, tridentNamespace, kubeClient, snapClient, crdClient)
if err != nil {
t.Fatalf("cannot create Trident CRD controller frontend, error: %v", err.Error())
}

// Activate the CRD controller and start monitoring
if err = crdController.Activate(); err != nil {
t.Fatalf("error while activating: %v", err.Error())
}
delaySeconds(1)

pvc := fakePVC(pvc1, namespace1, pv1)
_, _ = kubeClient.CoreV1().PersistentVolumeClaims(namespace1).Create(ctx(), pvc, createOpts)

tmr := fakeTMR(tmrName1, namespace1, pvc1)
_, _ = crdClient.TridentV1().TridentMirrorRelationships(namespace1).Create(ctx(), tmr, createOpts)

tamu := fakeTAMU(tamu1, namespace1, tmrName1, snapHandle1)
_, _ = crdClient.TridentV1().TridentActionMirrorUpdates(namespace1).Create(ctx(), tamu, createOpts)

// Wait until the operation completes
for i := 0; i < 5; i++ {
time.Sleep(250 * time.Millisecond)

tamu, err = crdClient.TridentV1().TridentActionMirrorUpdates(namespace1).Get(ctx(), tamu1, getOpts)
if err != nil {
if apierrors.IsNotFound(err) {
continue
}
break
} else if tamu.IsComplete() {
break
}
}

assert.True(t, tamu.Failed(), "TAMU operation was not disabled")
}

func TestHandleActionMirrorUpdate_InProgressAtStartup(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

Expand Down Expand Up @@ -362,6 +418,9 @@ func TestHandleActionMirrorUpdate_InProgressAtStartup(t *testing.T) {
}

func TestUpdateActionMirrorUpdateCRInProgress(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)
transferTime, _ := time.Parse(utils.TimestampFormat, previousTransferTime)
Expand Down Expand Up @@ -397,6 +456,9 @@ func TestUpdateActionMirrorUpdateCRInProgress(t *testing.T) {
}

func TestUpdateActionMirrorUpdateCRComplete_Succeeded(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

Expand Down Expand Up @@ -430,6 +492,9 @@ func TestUpdateActionMirrorUpdateCRComplete_Succeeded(t *testing.T) {
}

func TestUpdateActionMirrorUpdateCRComplete_Failed(t *testing.T) {
defer func() { config.DisableExtraFeatures = true }()
config.DisableExtraFeatures = false

mockCtrl := gomock.NewController(t)
orchestrator := mockcore.NewMockOrchestrator(mockCtrl)

Expand Down
6 changes: 6 additions & 0 deletions storage/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,12 @@ func (b *StorageBackend) CloneVolume(
"cloneVolumeInternal": cloneVolConfig.InternalName,
}).Debug("Attempting volume clone.")

if cloneVolConfig.ReadOnlyClone {
if tridentconfig.DisableExtraFeatures {
return nil, errors.UnsupportedError("read only clone is not supported")
}
}

// Ensure volume is managed
if cloneVolConfig.ImportNotManaged {
return nil, errors.NotManagedError("volume %s is not managed by Trident", cloneVolConfig.InternalName)
Expand Down
61 changes: 61 additions & 0 deletions storage/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"testing"

"github.com/stretchr/testify/assert"

tridentconfig "github.com/netapp/trident/config"
"github.com/netapp/trident/utils/errors"
)

func TestBackendState(t *testing.T) {
Expand Down Expand Up @@ -143,3 +146,61 @@ func TestDeleteSnapshot_NotManaged(t *testing.T) {

assert.Errorf(t, err, "expected err")
}

func TestCloneVolume_FeatureDisabled(t *testing.T) {
volumeName := "pvc-e9748b6b-8240-4fd8-97bc-868bf064ecd4"
volumeInternalName := "trident_pvc_e9748b6b_8240_4fd8_97bc_868bf064ecd4"
volumeConfig := &VolumeConfig{
Version: "",
Name: volumeName,
InternalName: volumeInternalName,
}
volumeConfigDest := &VolumeConfig{
Version: "",
Name: "pvc-deadbeef-8240-4fd8-97bc-868bf064ecd4",
InternalName: "trident_pvc_deadbeef_8240_4fd8_97bc_868bf064ecd4",
ReadOnlyClone: true,
}

backend := &StorageBackend{
state: Offline,
}
pool := NewStoragePool(nil, "test-pool1")

// Both volume and snapshot not managed
_, err := backend.CloneVolume(context.Background(), volumeConfig, volumeConfigDest, pool, false)

assert.Error(t, err, "expected err")
assert.True(t, errors.IsUnsupportedError(err))
}

func TestCloneVolume_BackendOffline(t *testing.T) {
volumeName := "pvc-e9748b6b-8240-4fd8-97bc-868bf064ecd4"
volumeInternalName := "trident_pvc_e9748b6b_8240_4fd8_97bc_868bf064ecd4"
volumeConfig := &VolumeConfig{
Version: "",
Name: volumeName,
InternalName: volumeInternalName,
ReadOnlyClone: true,
}
volumeConfigDest := &VolumeConfig{
Version: "",
Name: "pvc-deadbeef-8240-4fd8-97bc-868bf064ecd4",
InternalName: "trident_pvc_deadbeef_8240_4fd8_97bc_868bf064ecd4",
ReadOnlyClone: false,
}

backend := &StorageBackend{
state: Offline,
name: "test-backend",
}
pool := NewStoragePool(nil, "test-pool1")

tridentconfig.DisableExtraFeatures = false

// Both volume and snapshot not managed
_, err := backend.CloneVolume(context.Background(), volumeConfig, volumeConfigDest, pool, false)

assert.Errorf(t, err, "expected err")
assert.Equal(t, err.Error(), "backend test-backend is not Online")
}
5 changes: 4 additions & 1 deletion storage_drivers/ontap/ontap_nas_qtree.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,6 @@ func (d *NASQtreeStorageDriver) CreateClone(

// If RO clone is requested, validate the snapshot directory access and return
if cloneVolConfig.ReadOnlyClone {

_, flexvol, _, err := d.ParseQtreeInternalID(sourceVolConfig.InternalID)
if err != nil {
return errors.WrapWithNotFoundError(err, "error while getting flexvol")
Expand Down Expand Up @@ -902,6 +901,10 @@ func (d *NASQtreeStorageDriver) CreateSnapshot(
Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> CreateSnapshot")
defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< CreateSnapshot")

if tridentconfig.DisableExtraFeatures {
return nil, errors.UnsupportedError(fmt.Sprintf("snapshots are not supported by backend type %s", d.Name()))
}

if volConfig.ReadOnlyClone {
// This is a read-only volume and hence do not create snapshot of it
return nil, fmt.Errorf("snapshot is not supported for a read-only volume")
Expand Down
Loading

0 comments on commit 724169b

Please sign in to comment.