Skip to content
Closed
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/kubernetes-csi/external-snapshotter/v2
go 1.12

require (
github.com/container-storage-interface/spec v1.1.0
github.com/container-storage-interface/spec v1.2.0
github.com/golang/mock v1.2.0
github.com/golang/protobuf v1.3.2
github.com/google/go-cmp v0.3.1 // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/container-storage-interface/spec v1.1.0 h1:qPsTqtR1VUPvMPeK0UnCZMtXaKGyyLPG8gj/wG6VqMs=
github.com/container-storage-interface/spec v1.1.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4=
github.com/container-storage-interface/spec v1.2.0 h1:bD9KIVgaVKKkQ/UbVUY9kCaH/CJbhNxe0eeB4JeJV2s=
github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
6 changes: 6 additions & 0 deletions pkg/common-controller/framework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1455,13 +1455,17 @@ func secretAnnotations() map[string]string {
return map[string]string{
utils.AnnDeletionSecretRefName: "secret",
utils.AnnDeletionSecretRefNamespace: "default",
utils.AnnListingSecretRefName: "secret",
utils.AnnListingSecretRefNamespace: "default",
}
}

func emptyNamespaceSecretAnnotations() map[string]string {
return map[string]string{
utils.AnnDeletionSecretRefName: "name",
utils.AnnDeletionSecretRefNamespace: "",
utils.AnnListingSecretRefName: "secret",
utils.AnnListingSecretRefNamespace: "",
}
}

Expand All @@ -1470,5 +1474,7 @@ func emptyDataSecretAnnotations() map[string]string {
return map[string]string{
utils.AnnDeletionSecretRefName: "emptysecret",
utils.AnnDeletionSecretRefNamespace: "default",
utils.AnnListingSecretRefName: "emptysecret",
utils.AnnListingSecretRefNamespace: "default",
}
}
9 changes: 8 additions & 1 deletion pkg/common-controller/snapshot_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,13 +501,20 @@ func (ctrl *csiSnapshotCommonController) createSnapshotContent(snapshot *crdv1.V
},
}

// Set AnnDeletionSecretRefName and AnnDeletionSecretRefNamespace
if snapshotterSecretRef != nil {
// Set AnnDeletionSecretRefName and AnnDeletionSecretRefNamespace
klog.V(5).Infof("createSnapshotContent: set annotation [%s] on content [%s].", utils.AnnDeletionSecretRefName, snapshotContent.Name)
metav1.SetMetaDataAnnotation(&snapshotContent.ObjectMeta, utils.AnnDeletionSecretRefName, snapshotterSecretRef.Name)

klog.V(5).Infof("createSnapshotContent: set annotation [%s] on content [%s].", utils.AnnDeletionSecretRefNamespace, snapshotContent.Name)
metav1.SetMetaDataAnnotation(&snapshotContent.ObjectMeta, utils.AnnDeletionSecretRefNamespace, snapshotterSecretRef.Namespace)

// Set AnnListingSecretRefName and AnnListingSecretRefNamespace
klog.V(5).Infof("createSnapshotContent: set annotation [%s] on content [%s].", utils.AnnListingSecretRefName, snapshotContent.Name)
metav1.SetMetaDataAnnotation(&snapshotContent.ObjectMeta, utils.AnnListingSecretRefName, snapshotterSecretRef.Name)

klog.V(5).Infof("createSnapshotContent: set annotation [%s] on content [%s].", utils.AnnListingSecretRefNamespace, snapshotContent.Name)
metav1.SetMetaDataAnnotation(&snapshotContent.ObjectMeta, utils.AnnListingSecretRefNamespace, snapshotterSecretRef.Namespace)
}

var updateContent *crdv1.VolumeSnapshotContent
Expand Down
2 changes: 1 addition & 1 deletion pkg/sidecar-controller/content_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestSyncContent(t *testing.T) {
readyToUse: true,
},
},
expectedListCalls: []listCall{{"sid1-1", true, time.Now(), 1, nil}},
expectedListCalls: []listCall{{"sid1-1", true, time.Now(), 1, map[string]string{}, nil}},
errors: noerrors,
test: testSyncContent,
})
Expand Down
6 changes: 3 additions & 3 deletions pkg/sidecar-controller/csi_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
type Handler interface {
CreateSnapshot(content *crdv1.VolumeSnapshotContent, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error)
DeleteSnapshot(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) error
GetSnapshotStatus(content *crdv1.VolumeSnapshotContent) (bool, time.Time, int64, error)
GetSnapshotStatus(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) (bool, time.Time, int64, error)
}

// csiHandler is a handler that calls CSI to create/delete volume snapshot.
Expand Down Expand Up @@ -103,7 +103,7 @@ func (handler *csiHandler) DeleteSnapshot(content *crdv1.VolumeSnapshotContent,
return nil
}

func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotContent) (bool, time.Time, int64, error) {
func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) (bool, time.Time, int64, error) {
ctx, cancel := context.WithTimeout(context.Background(), handler.timeout)
defer cancel()

Expand All @@ -117,7 +117,7 @@ func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotConten
return false, time.Time{}, 0, fmt.Errorf("failed to list snapshot for content %s: snapshotHandle is missing", content.Name)
}

csiSnapshotStatus, timestamp, size, err := handler.snapshotter.GetSnapshotStatus(ctx, snapshotHandle)
csiSnapshotStatus, timestamp, size, err := handler.snapshotter.GetSnapshotStatus(ctx, snapshotHandle, snapshotterCredentials)
if err != nil {
return false, time.Time{}, 0, fmt.Errorf("failed to list snapshot for content %s: %q", content.Name, err)
}
Expand Down
16 changes: 15 additions & 1 deletion pkg/sidecar-controller/framework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ func newSnapshotReactor(kubeClient *kubefake.Clientset, client *fake.Clientset,
client.AddReactor("get", "volumesnapshotcontents", reactor.React)
client.AddReactor("delete", "volumesnapshotcontents", reactor.React)

kubeClient.AddReactor("get", "secrets", reactor.React)

return reactor
}

Expand Down Expand Up @@ -790,13 +792,17 @@ func secretAnnotations() map[string]string {
return map[string]string{
utils.AnnDeletionSecretRefName: "secret",
utils.AnnDeletionSecretRefNamespace: "default",
utils.AnnListingSecretRefName: "secret",
utils.AnnListingSecretRefNamespace: "default",
}
}

func emptyNamespaceSecretAnnotations() map[string]string {
return map[string]string{
utils.AnnDeletionSecretRefName: "name",
utils.AnnDeletionSecretRefNamespace: "",
utils.AnnListingSecretRefName: "secret",
utils.AnnListingSecretRefNamespace: "",
}
}

Expand All @@ -805,6 +811,8 @@ func emptyDataSecretAnnotations() map[string]string {
return map[string]string{
utils.AnnDeletionSecretRefName: "emptysecret",
utils.AnnDeletionSecretRefNamespace: "default",
utils.AnnListingSecretRefName: "emptysecret",
utils.AnnListingSecretRefNamespace: "default",
}
}

Expand All @@ -814,6 +822,7 @@ type listCall struct {
readyToUse bool
createTime time.Time
size int64
secrets map[string]string
err error
}

Expand Down Expand Up @@ -911,7 +920,7 @@ func (f *fakeSnapshotter) DeleteSnapshot(ctx context.Context, snapshotID string,
return call.err
}

func (f *fakeSnapshotter) GetSnapshotStatus(ctx context.Context, snapshotID string) (bool, time.Time, int64, error) {
func (f *fakeSnapshotter) GetSnapshotStatus(ctx context.Context, snapshotID string, snapshotterCredentials map[string]string) (bool, time.Time, int64, error) {
if f.listCallCounter >= len(f.listCalls) {
f.t.Errorf("Unexpected CSI list Snapshot call: snapshotID=%s, index: %d, calls: %+v", snapshotID, f.createCallCounter, f.createCalls)
return false, time.Time{}, 0, fmt.Errorf("unexpected call")
Expand All @@ -925,6 +934,11 @@ func (f *fakeSnapshotter) GetSnapshotStatus(ctx context.Context, snapshotID stri
err = fmt.Errorf("unexpected List snapshot call")
}

if !reflect.DeepEqual(call.secrets, snapshotterCredentials) {
f.t.Errorf("Wrong CSI List Snapshot call: snapshotID=%s, expected secrets %+v, got %+v", snapshotID, call.secrets, snapshotterCredentials)
err = fmt.Errorf("unexpected Delete Snapshot call")
}

if err != nil {
return false, time.Time{}, 0, fmt.Errorf("unexpected call")
}
Expand Down
26 changes: 15 additions & 11 deletions pkg/sidecar-controller/snapshot_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (ctrl *csiSnapshotSideCarController) updateContentErrorStatusWithEvent(cont
return nil
}

func (ctrl *csiSnapshotSideCarController) getCSISnapshotInput(content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshotClass, map[string]string, error) {
func (ctrl *csiSnapshotSideCarController) getCSISnapshotInput(content *crdv1.VolumeSnapshotContent, name string, namespace string) (*crdv1.VolumeSnapshotClass, map[string]string, error) {
className := content.Spec.VolumeSnapshotClassName
klog.V(5).Infof("getCSISnapshotInput for content [%s]", content.Name)
var class *crdv1.VolumeSnapshotClass
Expand All @@ -237,7 +237,7 @@ func (ctrl *csiSnapshotSideCarController) getCSISnapshotInput(content *crdv1.Vol
}

// Resolve snapshotting secret credentials.
snapshotterCredentials, err := ctrl.GetCredentialsFromAnnotation(content)
snapshotterCredentials, err := ctrl.GetCredentialsFromAnnotation(content, name, namespace)
if err != nil {
return nil, nil, err
}
Expand All @@ -254,16 +254,21 @@ func (ctrl *csiSnapshotSideCarController) checkandUpdateContentStatusOperation(c
var snapshotID string

if content.Spec.Source.SnapshotHandle != nil {
_, snapshotterCredentials, err := ctrl.getCSISnapshotInput(content, utils.AnnListingSecretRefName, utils.AnnListingSecretRefNamespace)
if err != nil {
return nil, fmt.Errorf("failed to get input parameters to get snapshot status for content %s: %q", content.Name, err)
}

klog.V(5).Infof("checkandUpdateContentStatusOperation: call GetSnapshotStatus for snapshot which is pre-bound to content [%s]", content.Name)
readyToUse, creationTime, size, err = ctrl.handler.GetSnapshotStatus(content)
readyToUse, creationTime, size, err = ctrl.handler.GetSnapshotStatus(content, snapshotterCredentials)
if err != nil {
klog.Errorf("checkandUpdateContentStatusOperation: failed to call get snapshot status to check whether snapshot is ready to use %q", err)
return nil, err
}
driverName = content.Spec.Driver
snapshotID = *content.Spec.Source.SnapshotHandle
} else {
class, snapshotterCredentials, err := ctrl.getCSISnapshotInput(content)
class, snapshotterCredentials, err := ctrl.getCSISnapshotInput(content, utils.AnnDeletionSecretRefName, utils.AnnDeletionSecretRefNamespace)
if err != nil {
return nil, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", content.Name, err)
}
Expand Down Expand Up @@ -303,7 +308,7 @@ func (ctrl *csiSnapshotSideCarController) createSnapshotOperation(content *crdv1
return content, nil
}

class, snapshotterCredentials, err := ctrl.getCSISnapshotInput(content)
class, snapshotterCredentials, err := ctrl.getCSISnapshotInput(content, utils.AnnDeletionSecretRefName, utils.AnnDeletionSecretRefNamespace)
if err != nil {
return nil, fmt.Errorf("failed to get input parameters to create snapshot for content %s: %q", content.Name, err)
}
Expand Down Expand Up @@ -339,8 +344,7 @@ func (ctrl *csiSnapshotSideCarController) createSnapshotOperation(content *crdv1
// Delete a snapshot: Ask the backend to remove the snapshot device
func (ctrl *csiSnapshotSideCarController) deleteCSISnapshotOperation(content *crdv1.VolumeSnapshotContent) error {
klog.V(5).Infof("deleteCSISnapshotOperation [%s] started", content.Name)

_, snapshotterCredentials, err := ctrl.getCSISnapshotInput(content)
_, snapshotterCredentials, err := ctrl.getCSISnapshotInput(content, utils.AnnDeletionSecretRefName, utils.AnnDeletionSecretRefNamespace)
if err != nil {
ctrl.eventRecorder.Event(content, v1.EventTypeWarning, "SnapshotDeleteError", "Failed to get snapshot class or credentials")
return fmt.Errorf("failed to get input parameters to delete snapshot for content %s: %q", content.Name, err)
Expand Down Expand Up @@ -484,15 +488,15 @@ func isControllerUpdateFailError(err *crdv1.VolumeSnapshotError) bool {
return false
}

func (ctrl *csiSnapshotSideCarController) GetCredentialsFromAnnotation(content *crdv1.VolumeSnapshotContent) (map[string]string, error) {
func (ctrl *csiSnapshotSideCarController) GetCredentialsFromAnnotation(content *crdv1.VolumeSnapshotContent, name string, namespace string) (map[string]string, error) {
// get secrets if VolumeSnapshotClass specifies it
var snapshotterCredentials map[string]string
var err error

// Check if annotation exists
if metav1.HasAnnotation(content.ObjectMeta, utils.AnnDeletionSecretRefName) && metav1.HasAnnotation(content.ObjectMeta, utils.AnnDeletionSecretRefNamespace) {
annDeletionSecretName := content.Annotations[utils.AnnDeletionSecretRefName]
annDeletionSecretNamespace := content.Annotations[utils.AnnDeletionSecretRefNamespace]
if metav1.HasAnnotation(content.ObjectMeta, name) && metav1.HasAnnotation(content.ObjectMeta, namespace) {
annDeletionSecretName := content.Annotations[name]
annDeletionSecretNamespace := content.Annotations[namespace]

snapshotterSecretRef := &v1.SecretReference{}

Expand Down
134 changes: 134 additions & 0 deletions pkg/sidecar-controller/snapshot_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,19 @@ limitations under the License.
package sidecar_controller

import (
"reflect"
"testing"
"time"

crdv1 "github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1"
"github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned/fake"
storagelisters "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/listers/volumesnapshot/v1beta1"
"github.com/kubernetes-csi/external-snapshotter/v2/pkg/utils"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/diff"
kubefake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
)

Expand Down Expand Up @@ -140,3 +148,129 @@ func TestShouldDelete(t *testing.T) {

}
}

func TestCheckandUpdateContentStatus(t *testing.T) {
tests := []struct {
name string
initialContent *crdv1.VolumeSnapshotContent
initialSecret *v1.Secret
snapshotHandle string
expectedContent *crdv1.VolumeSnapshotContent
expectedReadyToUse bool
expectedError error
expectedCreateCalls []createCall
expectedListCalls []listCall
}{
{
name: "Update volumesnapshotcontent status",
initialContent: newContent("test-content", "snap-uuid", "snapName", "desiredHandle", defaultClass, "desiredHandle", "volHandle", crdv1.VolumeSnapshotContentDelete, nil, &defaultSize, false, nil),
initialSecret: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret",
Namespace: "default",
},
Data: map[string][]byte{
"foo": []byte("bar"),
},
},
snapshotHandle: "snapName",
expectedContent: newContent("test-content", "snap-uuid", "snapName", "desiredHandle", defaultClass, "desiredHandle", "volHandle", crdv1.VolumeSnapshotContentDelete, nil, &defaultSize, false, nil),
expectedReadyToUse: true,
expectedError: nil,
expectedListCalls: []listCall{{"desiredHandle", true, time.Now(), 1, map[string]string{"foo": "bar"}, nil}},
},
{
name: "Create and update volumesnapshotcontent",
initialContent: &crdv1.VolumeSnapshotContent{
ObjectMeta: metav1.ObjectMeta{
Name: "snapuid1-1",
},
Spec: crdv1.VolumeSnapshotContentSpec{
VolumeSnapshotClassName: &defaultClass,
VolumeSnapshotRef: v1.ObjectReference{
Kind: "VolumeSnapshot",
APIVersion: "snapshot.storage.k8s.io/v1beta1",
UID: types.UID("snap-uuid"),
Namespace: testNamespace,
Name: "snapName",
},
Source: crdv1.VolumeSnapshotContentSource{
VolumeHandle: (func(s string) *string {
return &s
})("volHandle"),
},
},
},
expectedContent: newContent("snapuid1-1", "snap-uuid", "snapName", "desiredHandle", defaultClass, "desiredHandle", "volHandle", crdv1.VolumeSnapshotContentDelete, nil, &defaultSize, false, nil),
expectedReadyToUse: true,
expectedError: nil,
expectedCreateCalls: []createCall{{
volumeHandle: "volHandle",
snapshotName: "snapshot-snap-uuid",
driverName: mockDriverName,
snapshotId: "snapuid1-1",
creationTime: timeNow,
readyToUse: true,
}},
expectedListCalls: []listCall{{"desiredHandle", true, time.Now(), 1, map[string]string{"foo": "bar"}, nil}},
},
}

for _, test := range tests {
// Initialize the controller
kubeClient := &kubefake.Clientset{}
client := &fake.Clientset{}

ctrl, err := newTestController(kubeClient, client, nil, t, controllerTest{
expectedCreateCalls: test.expectedCreateCalls,
expectedListCalls: test.expectedListCalls,
})

if err != nil {
t.Fatalf("Test %q construct persistent content failed: %v", test.name, err)
}

if test.snapshotHandle != "" {
test.initialContent.Spec.Source.SnapshotHandle = &test.snapshotHandle
test.expectedContent.Spec.Source.SnapshotHandle = &test.snapshotHandle
}

if test.initialSecret != nil {
test.initialContent.ObjectMeta.Annotations = secretAnnotations()
test.expectedContent.ObjectMeta.Annotations = secretAnnotations()
}

// Setup reactor
reactor := newSnapshotReactor(kubeClient, client, ctrl, nil, nil, nil)
if ctrl.isDriverMatch(test.initialContent) {
ctrl.contentStore.Add(test.initialContent)
reactor.contents[test.initialContent.Name] = test.initialContent
reactor.secrets[test.initialSecret.ObjectMeta.Name] = test.initialSecret
}

if test.initialContent.Spec.Source.SnapshotHandle == nil {
reactor.contents[test.expectedContent.Name] = test.expectedContent
}

// Inject classes into controller via a custom lister.
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
for _, class := range snapshotClasses {
indexer.Add(class)
}
ctrl.classLister = storagelisters.NewVolumeSnapshotClassLister(indexer)

// Run test
content, err := ctrl.checkandUpdateContentStatusOperation(test.initialContent)
if !reflect.DeepEqual(err, test.expectedError) {
t.Errorf("error check failed for test %s: %s", test.name, diff.ObjectDiff(test.expectedError, err))
}

test.expectedContent.Status.CreationTime = content.Status.CreationTime
test.expectedContent.ObjectMeta.ResourceVersion = content.ObjectMeta.ResourceVersion
test.expectedContent.Status.ReadyToUse = &test.expectedReadyToUse

if !reflect.DeepEqual(content, test.expectedContent) {
t.Errorf("content check failed for test %s: %s", test.name, diff.ObjectDiff(test.expectedContent, content))
}
}
}
Loading