k8s.io/kubernetes@v1.29.3/test/e2e/storage/csi_mock/csi_snapshot.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package csi_mock
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	"github.com/onsi/ginkgo/v2"
    25  	"google.golang.org/grpc/codes"
    26  	"google.golang.org/grpc/status"
    27  	v1 "k8s.io/api/core/v1"
    28  	storagev1 "k8s.io/api/storage/v1"
    29  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    32  	clientset "k8s.io/client-go/kubernetes"
    33  	"k8s.io/kubernetes/test/e2e/feature"
    34  	"k8s.io/kubernetes/test/e2e/framework"
    35  	e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"
    36  	e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
    37  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    38  	"k8s.io/kubernetes/test/e2e/storage/drivers"
    39  	storageframework "k8s.io/kubernetes/test/e2e/storage/framework"
    40  	"k8s.io/kubernetes/test/e2e/storage/utils"
    41  	admissionapi "k8s.io/pod-security-admission/api"
    42  )
    43  
    44  var _ = utils.SIGDescribe("CSI Mock volume snapshot", func() {
    45  	f := framework.NewDefaultFramework("csi-mock-volumes-snapshot")
    46  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
    47  	m := newMockDriverSetup(f)
    48  
    49  	f.Context("CSI Volume Snapshots", feature.VolumeSnapshotDataSource, func() {
    50  		tests := []struct {
    51  			name               string
    52  			createSnapshotHook func(counter int64) error
    53  		}{
    54  			{
    55  				name: "volumesnapshotcontent and pvc in Bound state with deletion timestamp set should not get deleted while snapshot finalizer exists",
    56  				createSnapshotHook: func(counter int64) error {
    57  					if counter < 8 {
    58  						return status.Error(codes.DeadlineExceeded, "fake error")
    59  					}
    60  					return nil
    61  				},
    62  			},
    63  		}
    64  		for _, test := range tests {
    65  			test := test
    66  			ginkgo.It(test.name, func(ctx context.Context) {
    67  				var hooks *drivers.Hooks
    68  				if test.createSnapshotHook != nil {
    69  					hooks = createPreHook("CreateSnapshot", test.createSnapshotHook)
    70  				}
    71  				m.init(ctx, testParameters{
    72  					disableAttach:  true,
    73  					registerDriver: true,
    74  					enableSnapshot: true,
    75  					hooks:          hooks,
    76  				})
    77  				sDriver, ok := m.driver.(storageframework.SnapshottableTestDriver)
    78  				if !ok {
    79  					e2eskipper.Skipf("mock driver %s does not support snapshots -- skipping", m.driver.GetDriverInfo().Name)
    80  
    81  				}
    82  				ctx, cancel := context.WithTimeout(ctx, csiPodRunningTimeout)
    83  				defer cancel()
    84  				ginkgo.DeferCleanup(m.cleanup)
    85  
    86  				sc := m.driver.GetDynamicProvisionStorageClass(ctx, m.config, "")
    87  				ginkgo.By("Creating storage class")
    88  				class, err := m.cs.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{})
    89  				framework.ExpectNoError(err, "Failed to create class: %v", err)
    90  				m.sc[class.Name] = class
    91  				claim := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
    92  					// Use static name so that the volumesnapshot can be created before the pvc.
    93  					Name:             "snapshot-test-pvc",
    94  					StorageClassName: &(class.Name),
    95  				}, f.Namespace.Name)
    96  
    97  				ginkgo.By("Creating snapshot")
    98  				// TODO: Test VolumeSnapshots with Retain policy
    99  				parameters := map[string]string{}
   100  				snapshotClass, snapshot := storageframework.CreateSnapshot(ctx, sDriver, m.config, storageframework.DynamicSnapshotDelete, claim.Name, claim.Namespace, f.Timeouts, parameters)
   101  				framework.ExpectNoError(err, "failed to create snapshot")
   102  				m.vsc[snapshotClass.GetName()] = snapshotClass
   103  				volumeSnapshotName := snapshot.GetName()
   104  
   105  				ginkgo.By(fmt.Sprintf("Creating PVC %s/%s", claim.Namespace, claim.Name))
   106  				claim, err = m.cs.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Create(context.TODO(), claim, metav1.CreateOptions{})
   107  				framework.ExpectNoError(err, "Failed to create claim: %v", err)
   108  
   109  				ginkgo.By(fmt.Sprintf("Wait for finalizer to be added to claim %s/%s", claim.Namespace, claim.Name))
   110  				err = e2epv.WaitForPVCFinalizer(ctx, m.cs, claim.Name, claim.Namespace, pvcAsSourceProtectionFinalizer, 1*time.Millisecond, 1*time.Minute)
   111  				framework.ExpectNoError(err)
   112  
   113  				ginkgo.By("Wait for PVC to be Bound")
   114  				_, err = e2epv.WaitForPVClaimBoundPhase(ctx, m.cs, []*v1.PersistentVolumeClaim{claim}, 1*time.Minute)
   115  				framework.ExpectNoError(err, "Failed to create claim: %v", err)
   116  
   117  				ginkgo.By(fmt.Sprintf("Delete PVC %s", claim.Name))
   118  				err = e2epv.DeletePersistentVolumeClaim(ctx, m.cs, claim.Name, claim.Namespace)
   119  				framework.ExpectNoError(err, "failed to delete pvc")
   120  
   121  				ginkgo.By("Get PVC from API server and verify deletion timestamp is set")
   122  				claim, err = m.cs.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(context.TODO(), claim.Name, metav1.GetOptions{})
   123  				if err != nil {
   124  					if !apierrors.IsNotFound(err) {
   125  						framework.ExpectNoError(err, "Failed to get claim: %v", err)
   126  					}
   127  					framework.Logf("PVC not found. Continuing to test VolumeSnapshotContent finalizer")
   128  				} else if claim.DeletionTimestamp == nil {
   129  					framework.Failf("Expected deletion timestamp to be set on PVC: %v", claim)
   130  				}
   131  
   132  				ginkgo.By(fmt.Sprintf("Get VolumeSnapshotContent bound to VolumeSnapshot %s", snapshot.GetName()))
   133  				snapshotContent := utils.GetSnapshotContentFromSnapshot(ctx, m.config.Framework.DynamicClient, snapshot, f.Timeouts.SnapshotCreate)
   134  				volumeSnapshotContentName := snapshotContent.GetName()
   135  
   136  				ginkgo.By(fmt.Sprintf("Verify VolumeSnapshotContent %s contains finalizer %s", snapshot.GetName(), volumeSnapshotContentFinalizer))
   137  				err = utils.WaitForGVRFinalizer(ctx, m.config.Framework.DynamicClient, utils.SnapshotContentGVR, volumeSnapshotContentName, "", volumeSnapshotContentFinalizer, 1*time.Millisecond, 1*time.Minute)
   138  				framework.ExpectNoError(err)
   139  
   140  				ginkgo.By(fmt.Sprintf("Delete VolumeSnapshotContent %s", snapshotContent.GetName()))
   141  				err = m.config.Framework.DynamicClient.Resource(utils.SnapshotContentGVR).Delete(ctx, snapshotContent.GetName(), metav1.DeleteOptions{})
   142  				framework.ExpectNoError(err, "Failed to delete snapshotcontent: %v", err)
   143  
   144  				ginkgo.By("Get VolumeSnapshotContent from API server and verify deletion timestamp is set")
   145  				snapshotContent, err = m.config.Framework.DynamicClient.Resource(utils.SnapshotContentGVR).Get(context.TODO(), snapshotContent.GetName(), metav1.GetOptions{})
   146  				framework.ExpectNoError(err)
   147  
   148  				if snapshotContent.GetDeletionTimestamp() == nil {
   149  					framework.Failf("Expected deletion timestamp to be set on snapshotcontent")
   150  				}
   151  
   152  				// If the claim is non existent, the Get() call on the API server returns
   153  				// an non-nil claim object with all fields unset.
   154  				// Refer https://github.com/kubernetes/kubernetes/pull/99167#issuecomment-781670012
   155  				if claim != nil && claim.Spec.VolumeName != "" {
   156  					ginkgo.By(fmt.Sprintf("Wait for PV %s to be deleted", claim.Spec.VolumeName))
   157  					err = e2epv.WaitForPersistentVolumeDeleted(ctx, m.cs, claim.Spec.VolumeName, framework.Poll, 3*time.Minute)
   158  					framework.ExpectNoError(err, fmt.Sprintf("failed to delete PV %s", claim.Spec.VolumeName))
   159  				}
   160  
   161  				ginkgo.By(fmt.Sprintf("Verify VolumeSnapshot %s contains finalizer %s", snapshot.GetName(), volumeSnapshotBoundFinalizer))
   162  				err = utils.WaitForGVRFinalizer(ctx, m.config.Framework.DynamicClient, utils.SnapshotGVR, volumeSnapshotName, f.Namespace.Name, volumeSnapshotBoundFinalizer, 1*time.Millisecond, 1*time.Minute)
   163  				framework.ExpectNoError(err)
   164  
   165  				ginkgo.By("Delete VolumeSnapshot")
   166  				err = utils.DeleteAndWaitSnapshot(ctx, m.config.Framework.DynamicClient, f.Namespace.Name, volumeSnapshotName, framework.Poll, framework.SnapshotDeleteTimeout)
   167  				framework.ExpectNoError(err, fmt.Sprintf("failed to delete VolumeSnapshot %s", volumeSnapshotName))
   168  
   169  				ginkgo.By(fmt.Sprintf("Wait for VolumeSnapshotContent %s to be deleted", volumeSnapshotContentName))
   170  				err = utils.WaitForGVRDeletion(ctx, m.config.Framework.DynamicClient, utils.SnapshotContentGVR, volumeSnapshotContentName, framework.Poll, framework.SnapshotDeleteTimeout)
   171  				framework.ExpectNoError(err, fmt.Sprintf("failed to delete VolumeSnapshotContent %s", volumeSnapshotContentName))
   172  			})
   173  		}
   174  	})
   175  
   176  	f.Context("CSI Volume Snapshots secrets", feature.VolumeSnapshotDataSource, func() {
   177  
   178  		var (
   179  			// CSISnapshotterSecretName is the name of the secret to be created
   180  			CSISnapshotterSecretName string = "snapshot-secret"
   181  
   182  			// CSISnapshotterSecretNameAnnotation is the annotation key for the CSI snapshotter secret name in VolumeSnapshotClass.parameters
   183  			CSISnapshotterSecretNameAnnotation string = "csi.storage.k8s.io/snapshotter-secret-name"
   184  
   185  			// CSISnapshotterSecretNamespaceAnnotation is the annotation key for the CSI snapshotter secret namespace in VolumeSnapshotClass.parameters
   186  			CSISnapshotterSecretNamespaceAnnotation string = "csi.storage.k8s.io/snapshotter-secret-namespace"
   187  
   188  			// anotations holds the annotations object
   189  			annotations interface{}
   190  		)
   191  
   192  		tests := []struct {
   193  			name               string
   194  			createSnapshotHook func(counter int64) error
   195  		}{
   196  			{
   197  				// volume snapshot should be created using secrets successfully even if there is a failure in the first few attempts,
   198  				name: "volume snapshot create/delete with secrets",
   199  				// Fail the first 8 calls to create snapshot and succeed the  9th call.
   200  				createSnapshotHook: func(counter int64) error {
   201  					if counter < 8 {
   202  						return status.Error(codes.DeadlineExceeded, "fake error")
   203  					}
   204  					return nil
   205  				},
   206  			},
   207  		}
   208  		for _, test := range tests {
   209  			test := test
   210  			ginkgo.It(test.name, func(ctx context.Context) {
   211  				hooks := createPreHook("CreateSnapshot", test.createSnapshotHook)
   212  				m.init(ctx, testParameters{
   213  					disableAttach:  true,
   214  					registerDriver: true,
   215  					enableSnapshot: true,
   216  					hooks:          hooks,
   217  				})
   218  
   219  				sDriver, ok := m.driver.(storageframework.SnapshottableTestDriver)
   220  				if !ok {
   221  					e2eskipper.Skipf("mock driver does not support snapshots -- skipping")
   222  				}
   223  				ginkgo.DeferCleanup(m.cleanup)
   224  
   225  				var sc *storagev1.StorageClass
   226  				if dDriver, ok := m.driver.(storageframework.DynamicPVTestDriver); ok {
   227  					sc = dDriver.GetDynamicProvisionStorageClass(ctx, m.config, "")
   228  				}
   229  				ginkgo.By("Creating storage class")
   230  				class, err := m.cs.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{})
   231  				framework.ExpectNoError(err, "Failed to create storage class: %v", err)
   232  				m.sc[class.Name] = class
   233  				pvc := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   234  					Name:             "snapshot-test-pvc",
   235  					StorageClassName: &(class.Name),
   236  				}, f.Namespace.Name)
   237  
   238  				ginkgo.By(fmt.Sprintf("Creating PVC %s/%s", pvc.Namespace, pvc.Name))
   239  				pvc, err = m.cs.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Create(context.TODO(), pvc, metav1.CreateOptions{})
   240  				framework.ExpectNoError(err, "Failed to create claim: %v", err)
   241  
   242  				ginkgo.By("Wait for PVC to be Bound")
   243  				_, err = e2epv.WaitForPVClaimBoundPhase(ctx, m.cs, []*v1.PersistentVolumeClaim{pvc}, 1*time.Minute)
   244  				framework.ExpectNoError(err, "Failed to create claim: %v", err)
   245  
   246  				m.pvcs = append(m.pvcs, pvc)
   247  
   248  				ginkgo.By("Creating Secret")
   249  				secret := &v1.Secret{
   250  					ObjectMeta: metav1.ObjectMeta{
   251  						Namespace: f.Namespace.Name,
   252  						Name:      CSISnapshotterSecretName,
   253  					},
   254  					Data: map[string][]byte{
   255  						"secret-data": []byte("secret-value-1"),
   256  					},
   257  				}
   258  
   259  				if secret, err := m.cs.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil {
   260  					framework.Failf("unable to create test secret %s: %v", secret.Name, err)
   261  				}
   262  
   263  				ginkgo.By("Creating snapshot with secrets")
   264  				parameters := map[string]string{
   265  					CSISnapshotterSecretNameAnnotation:      CSISnapshotterSecretName,
   266  					CSISnapshotterSecretNamespaceAnnotation: f.Namespace.Name,
   267  				}
   268  
   269  				_, snapshot := storageframework.CreateSnapshot(ctx, sDriver, m.config, storageframework.DynamicSnapshotDelete, pvc.Name, pvc.Namespace, f.Timeouts, parameters)
   270  				framework.ExpectNoError(err, "failed to create snapshot")
   271  				snapshotcontent := utils.GetSnapshotContentFromSnapshot(ctx, m.config.Framework.DynamicClient, snapshot, f.Timeouts.SnapshotCreate)
   272  				if annotations, ok = snapshotcontent.Object["metadata"].(map[string]interface{})["annotations"]; !ok {
   273  					framework.Failf("Unable to get volume snapshot content annotations")
   274  				}
   275  
   276  				// checks if delete snapshot secrets annotation is applied to the VolumeSnapshotContent.
   277  				checkDeleteSnapshotSecrets(m.cs, annotations)
   278  
   279  				// delete the snapshot and check if the snapshot is deleted.
   280  				deleteSnapshot(m.cs, m.config, snapshot)
   281  			})
   282  		}
   283  	})
   284  
   285  	f.Context("CSI Snapshot Controller metrics", feature.VolumeSnapshotDataSource, func() {
   286  		tests := []struct {
   287  			name    string
   288  			pattern storageframework.TestPattern
   289  		}{
   290  			{
   291  				name:    "snapshot controller should emit dynamic CreateSnapshot, CreateSnapshotAndReady, and DeleteSnapshot metrics",
   292  				pattern: storageframework.DynamicSnapshotDelete,
   293  			},
   294  			{
   295  				name:    "snapshot controller should emit pre-provisioned CreateSnapshot, CreateSnapshotAndReady, and DeleteSnapshot metrics",
   296  				pattern: storageframework.PreprovisionedSnapshotDelete,
   297  			},
   298  		}
   299  		for _, test := range tests {
   300  			test := test
   301  			ginkgo.It(test.name, func(ctx context.Context) {
   302  				m.init(ctx, testParameters{
   303  					disableAttach:  true,
   304  					registerDriver: true,
   305  					enableSnapshot: true,
   306  				})
   307  
   308  				sDriver, ok := m.driver.(storageframework.SnapshottableTestDriver)
   309  				if !ok {
   310  					e2eskipper.Skipf("mock driver does not support snapshots -- skipping")
   311  				}
   312  				ginkgo.DeferCleanup(m.cleanup)
   313  
   314  				metricsGrabber, err := e2emetrics.NewMetricsGrabber(ctx, m.config.Framework.ClientSet, nil, f.ClientConfig(), false, false, false, false, false, true)
   315  				if err != nil {
   316  					framework.Failf("Error creating metrics grabber : %v", err)
   317  				}
   318  
   319  				// Grab initial metrics - if this fails, snapshot controller metrics are not setup. Skip in this case.
   320  				_, err = metricsGrabber.GrabFromSnapshotController(ctx, framework.TestContext.SnapshotControllerPodName, framework.TestContext.SnapshotControllerHTTPPort)
   321  				if err != nil {
   322  					e2eskipper.Skipf("Snapshot controller metrics not found -- skipping")
   323  				}
   324  
   325  				ginkgo.By("getting all initial metric values")
   326  				metricsTestConfig := newSnapshotMetricsTestConfig("snapshot_controller_operation_total_seconds_count",
   327  					"count",
   328  					m.config.GetUniqueDriverName(),
   329  					"CreateSnapshot",
   330  					"success",
   331  					"",
   332  					test.pattern)
   333  				createSnapshotMetrics := newSnapshotControllerMetrics(metricsTestConfig, metricsGrabber)
   334  				originalCreateSnapshotCount, _ := createSnapshotMetrics.getSnapshotControllerMetricValue(ctx)
   335  				metricsTestConfig.operationName = "CreateSnapshotAndReady"
   336  				createSnapshotAndReadyMetrics := newSnapshotControllerMetrics(metricsTestConfig, metricsGrabber)
   337  				originalCreateSnapshotAndReadyCount, _ := createSnapshotAndReadyMetrics.getSnapshotControllerMetricValue(ctx)
   338  
   339  				metricsTestConfig.operationName = "DeleteSnapshot"
   340  				deleteSnapshotMetrics := newSnapshotControllerMetrics(metricsTestConfig, metricsGrabber)
   341  				originalDeleteSnapshotCount, _ := deleteSnapshotMetrics.getSnapshotControllerMetricValue(ctx)
   342  
   343  				ginkgo.By("Creating storage class")
   344  				var sc *storagev1.StorageClass
   345  				if dDriver, ok := m.driver.(storageframework.DynamicPVTestDriver); ok {
   346  					sc = dDriver.GetDynamicProvisionStorageClass(ctx, m.config, "")
   347  				}
   348  				class, err := m.cs.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{})
   349  				framework.ExpectNoError(err, "Failed to create storage class: %v", err)
   350  				m.sc[class.Name] = class
   351  				pvc := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   352  					Name:             "snapshot-test-pvc",
   353  					StorageClassName: &(class.Name),
   354  				}, f.Namespace.Name)
   355  
   356  				ginkgo.By(fmt.Sprintf("Creating PVC %s/%s", pvc.Namespace, pvc.Name))
   357  				pvc, err = m.cs.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Create(context.TODO(), pvc, metav1.CreateOptions{})
   358  				framework.ExpectNoError(err, "Failed to create claim: %v", err)
   359  
   360  				ginkgo.By("Wait for PVC to be Bound")
   361  				_, err = e2epv.WaitForPVClaimBoundPhase(ctx, m.cs, []*v1.PersistentVolumeClaim{pvc}, 1*time.Minute)
   362  				framework.ExpectNoError(err, "Failed to create claim: %v", err)
   363  
   364  				ginkgo.By("Creating snapshot")
   365  				parameters := map[string]string{}
   366  				sr := storageframework.CreateSnapshotResource(ctx, sDriver, m.config, test.pattern, pvc.Name, pvc.Namespace, f.Timeouts, parameters)
   367  				framework.ExpectNoError(err, "failed to create snapshot")
   368  
   369  				ginkgo.By("Checking for CreateSnapshot metrics")
   370  				createSnapshotMetrics.waitForSnapshotControllerMetric(ctx, originalCreateSnapshotCount+1.0, f.Timeouts.SnapshotControllerMetrics)
   371  
   372  				ginkgo.By("Checking for CreateSnapshotAndReady metrics")
   373  				err = utils.WaitForSnapshotReady(ctx, m.config.Framework.DynamicClient, pvc.Namespace, sr.Vs.GetName(), framework.Poll, f.Timeouts.SnapshotCreate)
   374  				framework.ExpectNoError(err, "failed to wait for snapshot ready")
   375  				createSnapshotAndReadyMetrics.waitForSnapshotControllerMetric(ctx, originalCreateSnapshotAndReadyCount+1.0, f.Timeouts.SnapshotControllerMetrics)
   376  
   377  				// delete the snapshot and check if the snapshot is deleted
   378  				deleteSnapshot(m.cs, m.config, sr.Vs)
   379  
   380  				ginkgo.By("check for delete metrics")
   381  				metricsTestConfig.operationName = "DeleteSnapshot"
   382  				deleteSnapshotMetrics.waitForSnapshotControllerMetric(ctx, originalDeleteSnapshotCount+1.0, f.Timeouts.SnapshotControllerMetrics)
   383  			})
   384  		}
   385  	})
   386  })
   387  
   388  // checkDeleteSnapshotSecrets checks if delete snapshot secrets annotation is applied to the VolumeSnapshotContent.
   389  func checkDeleteSnapshotSecrets(cs clientset.Interface, annotations interface{}) error {
   390  	ginkgo.By("checking if delete snapshot secrets annotation is applied to the VolumeSnapshotContent")
   391  
   392  	var (
   393  		annDeletionSecretName      string
   394  		annDeletionSecretNamespace string
   395  		ok                         bool
   396  		err                        error
   397  
   398  		// CSISnapshotterDeleteSecretNameAnnotation is the annotation key for the CSI snapshotter delete secret name in VolumeSnapshotClass.parameters
   399  		CSISnapshotterDeleteSecretNameAnnotation string = "snapshot.storage.kubernetes.io/deletion-secret-name"
   400  
   401  		// CSISnapshotterDeleteSecretNamespaceAnnotation is the annotation key for the CSI snapshotter delete secret namespace in VolumeSnapshotClass.parameters
   402  		CSISnapshotterDeleteSecretNamespaceAnnotation string = "snapshot.storage.kubernetes.io/deletion-secret-namespace"
   403  	)
   404  
   405  	annotationsObj, ok := annotations.(map[string]interface{})
   406  	if !ok {
   407  		framework.Failf("failed to get annotations from annotations object")
   408  	}
   409  
   410  	if annDeletionSecretName, ok = annotationsObj[CSISnapshotterDeleteSecretNameAnnotation].(string); !ok {
   411  		framework.Failf("unable to get secret annotation name")
   412  	}
   413  	if annDeletionSecretNamespace, ok = annotationsObj[CSISnapshotterDeleteSecretNamespaceAnnotation].(string); !ok {
   414  		framework.Failf("unable to get secret annotation namespace")
   415  	}
   416  
   417  	// verify if secrets exists
   418  	if _, err = cs.CoreV1().Secrets(annDeletionSecretNamespace).Get(context.TODO(), annDeletionSecretName, metav1.GetOptions{}); err != nil {
   419  		framework.Failf("unable to get test secret %s: %v", annDeletionSecretName, err)
   420  	}
   421  
   422  	return err
   423  }
   424  
   425  func deleteSnapshot(cs clientset.Interface, config *storageframework.PerTestConfig, snapshot *unstructured.Unstructured) {
   426  	// delete the given snapshot
   427  	dc := config.Framework.DynamicClient
   428  	err := dc.Resource(utils.SnapshotGVR).Namespace(snapshot.GetNamespace()).Delete(context.TODO(), snapshot.GetName(), metav1.DeleteOptions{})
   429  	framework.ExpectNoError(err)
   430  
   431  	// check if the snapshot is deleted
   432  	_, err = dc.Resource(utils.SnapshotGVR).Get(context.TODO(), snapshot.GetName(), metav1.GetOptions{})
   433  	framework.ExpectError(err)
   434  }
   435  
   436  type snapshotMetricsTestConfig struct {
   437  	// expected values
   438  	metricName      string
   439  	metricType      string
   440  	driverName      string
   441  	operationName   string
   442  	operationStatus string
   443  	snapshotType    string
   444  	le              string
   445  }
   446  
   447  type snapshotControllerMetrics struct {
   448  	// configuration for metric
   449  	cfg            snapshotMetricsTestConfig
   450  	metricsGrabber *e2emetrics.Grabber
   451  
   452  	// results
   453  	countMetrics  map[string]float64
   454  	sumMetrics    map[string]float64
   455  	bucketMetrics map[string]float64
   456  }
   457  
   458  func newSnapshotMetricsTestConfig(metricName, metricType, driverName, operationName, operationStatus, le string, pattern storageframework.TestPattern) snapshotMetricsTestConfig {
   459  	var snapshotType string
   460  	switch pattern.SnapshotType {
   461  	case storageframework.DynamicCreatedSnapshot:
   462  		snapshotType = "dynamic"
   463  
   464  	case storageframework.PreprovisionedCreatedSnapshot:
   465  		snapshotType = "pre-provisioned"
   466  
   467  	default:
   468  		framework.Failf("invalid snapshotType: %v", pattern.SnapshotType)
   469  	}
   470  
   471  	return snapshotMetricsTestConfig{
   472  		metricName:      metricName,
   473  		metricType:      metricType,
   474  		driverName:      driverName,
   475  		operationName:   operationName,
   476  		operationStatus: operationStatus,
   477  		snapshotType:    snapshotType,
   478  		le:              le,
   479  	}
   480  }
   481  
   482  func newSnapshotControllerMetrics(cfg snapshotMetricsTestConfig, metricsGrabber *e2emetrics.Grabber) *snapshotControllerMetrics {
   483  	return &snapshotControllerMetrics{
   484  		cfg:            cfg,
   485  		metricsGrabber: metricsGrabber,
   486  
   487  		countMetrics:  make(map[string]float64),
   488  		sumMetrics:    make(map[string]float64),
   489  		bucketMetrics: make(map[string]float64),
   490  	}
   491  }
   492  
   493  func (scm *snapshotControllerMetrics) waitForSnapshotControllerMetric(ctx context.Context, expectedValue float64, timeout time.Duration) {
   494  	metricKey := scm.getMetricKey()
   495  	if successful := utils.WaitUntil(10*time.Second, timeout, func() bool {
   496  		// get metric value
   497  		actualValue, err := scm.getSnapshotControllerMetricValue(ctx)
   498  		if err != nil {
   499  			return false
   500  		}
   501  
   502  		// Another operation could have finished from a previous test,
   503  		// so we check if we have at least the expected value.
   504  		if actualValue < expectedValue {
   505  			return false
   506  		}
   507  
   508  		return true
   509  	}); successful {
   510  		return
   511  	}
   512  
   513  	scm.showMetricsFailure(metricKey)
   514  	framework.Failf("Unable to get valid snapshot controller metrics after %v", timeout)
   515  }
   516  
   517  func (scm *snapshotControllerMetrics) getSnapshotControllerMetricValue(ctx context.Context) (float64, error) {
   518  	metricKey := scm.getMetricKey()
   519  
   520  	// grab and parse into readable format
   521  	err := scm.grabSnapshotControllerMetrics(ctx)
   522  	if err != nil {
   523  		return 0, err
   524  	}
   525  
   526  	metrics := scm.getMetricsTable()
   527  	actual, ok := metrics[metricKey]
   528  	if !ok {
   529  		return 0, fmt.Errorf("did not find metric for key %s", metricKey)
   530  	}
   531  
   532  	return actual, nil
   533  }
   534  
   535  func (scm *snapshotControllerMetrics) getMetricsTable() map[string]float64 {
   536  	var metrics map[string]float64
   537  	switch scm.cfg.metricType {
   538  	case "count":
   539  		metrics = scm.countMetrics
   540  
   541  	case "sum":
   542  		metrics = scm.sumMetrics
   543  
   544  	case "bucket":
   545  		metrics = scm.bucketMetrics
   546  	}
   547  
   548  	return metrics
   549  }
   550  
   551  func (scm *snapshotControllerMetrics) showMetricsFailure(metricKey string) {
   552  	framework.Logf("failed to find metric key %s inside of the following metrics:", metricKey)
   553  
   554  	metrics := scm.getMetricsTable()
   555  	for k, v := range metrics {
   556  		framework.Logf("%s: %v", k, v)
   557  	}
   558  }
   559  
   560  func (scm *snapshotControllerMetrics) grabSnapshotControllerMetrics(ctx context.Context) error {
   561  	// pull all metrics
   562  	metrics, err := scm.metricsGrabber.GrabFromSnapshotController(ctx, framework.TestContext.SnapshotControllerPodName, framework.TestContext.SnapshotControllerHTTPPort)
   563  	if err != nil {
   564  		return err
   565  	}
   566  
   567  	for method, samples := range metrics {
   568  
   569  		for _, sample := range samples {
   570  			operationName := string(sample.Metric["operation_name"])
   571  			driverName := string(sample.Metric["driver_name"])
   572  			operationStatus := string(sample.Metric["operation_status"])
   573  			snapshotType := string(sample.Metric["snapshot_type"])
   574  			le := string(sample.Metric["le"])
   575  			key := snapshotMetricKey(scm.cfg.metricName, driverName, operationName, operationStatus, snapshotType, le)
   576  
   577  			switch method {
   578  			case "snapshot_controller_operation_total_seconds_count":
   579  				for _, sample := range samples {
   580  					scm.countMetrics[key] = float64(sample.Value)
   581  				}
   582  
   583  			case "snapshot_controller_operation_total_seconds_sum":
   584  				for _, sample := range samples {
   585  					scm.sumMetrics[key] = float64(sample.Value)
   586  				}
   587  
   588  			case "snapshot_controller_operation_total_seconds_bucket":
   589  				for _, sample := range samples {
   590  					scm.bucketMetrics[key] = float64(sample.Value)
   591  				}
   592  			}
   593  		}
   594  	}
   595  
   596  	return nil
   597  }
   598  
   599  func (scm *snapshotControllerMetrics) getMetricKey() string {
   600  	return snapshotMetricKey(scm.cfg.metricName, scm.cfg.driverName, scm.cfg.operationName, scm.cfg.operationStatus, scm.cfg.snapshotType, scm.cfg.le)
   601  }
   602  
   603  func snapshotMetricKey(metricName, driverName, operationName, operationStatus, snapshotType, le string) string {
   604  	key := driverName
   605  
   606  	// build key for shorthand metrics storage
   607  	for _, s := range []string{metricName, operationName, operationStatus, snapshotType, le} {
   608  		if s != "" {
   609  			key = fmt.Sprintf("%s_%s", key, s)
   610  		}
   611  	}
   612  
   613  	return key
   614  }