diff --git a/CHANGELOG-1.2.md b/CHANGELOG-1.2.md new file mode 100644 index 000000000..102739201 --- /dev/null +++ b/CHANGELOG-1.2.md @@ -0,0 +1,25 @@ +# Changelog since v1.2.1 + +## Notable Changes + +- Cherry picks PR #172: Added extra verification of source PersistentVolumeClaim before creating snapshot.([#173](https://github.com/kubernetes-csi/external-snapshotter/pull/173), [@xing-yang](https://github.com/xing-yang)) + +# Changelog since v1.2.0 + +## Notable Changes + +- Cherry picks PR #138: Prebound snapshots will work correctly with CSI drivers that does not support ListSnasphots.([#156](https://github.com/kubernetes-csi/external-snapshotter/pull/156), [@hakanmemisoglu](https://github.com/hakanmemisoglu)) + +# Changelog since v1.1.0 + +## Breaking Changes + +- Changes the API group name for the fake VolumeSnapshot object to "snapshot.storage.k8s.io" to be in-sync with the group name of the real VolumeSnapshot object. As a result, the generated interfaces for clientset and informers of VolumeSnapshot are also changed from "VolumeSnapshot" to "Snapshot". ([#123](https://github.com/kubernetes-csi/external-snapshotter/pull/123), [@xing-yang](https://github.com/xing-yang)) + +## New Features + +- Adds Finalizer on the snapshot source PVC to prevent it from being deleted when a snapshot is being created from it. ([#47](https://github.com/kubernetes-csi/external-snapshotter/pull/47), [@xing-yang](https://github.com/xing-yang)) + +## Other Notable Changes + +- Add Status subresource for VolumeSnapshot. ([#121](https://github.com/kubernetes-csi/external-snapshotter/pull/121), [@zhucan](https://github.com/zhucan)) diff --git a/Dockerfile.openshift b/Dockerfile.openshift index 3df508a8a..c8f4e649c 100644 --- a/Dockerfile.openshift +++ b/Dockerfile.openshift @@ -1,4 +1,4 @@ -FROM registry.svc.ci.openshift.org/openshift/release:golang-1.11 AS builder +FROM registry.svc.ci.openshift.org/openshift/release:golang-1.12 AS builder WORKDIR /go/src/github.com/kubernetes-csi/external-snapshotter COPY . . RUN make build diff --git a/Dockerfile.openshift.rhel7 b/Dockerfile.openshift.rhel7 index bb7eb94ae..d8edd66cf 100644 --- a/Dockerfile.openshift.rhel7 +++ b/Dockerfile.openshift.rhel7 @@ -1,4 +1,4 @@ -FROM registry.svc.ci.openshift.org/ocp/builder:golang-1.11 AS builder +FROM registry.svc.ci.openshift.org/ocp/builder:golang-1.12 AS builder WORKDIR /go/src/github.com/kubernetes-csi/external-snapshotter COPY . . RUN make build diff --git a/Gopkg.lock b/Gopkg.lock index 2c17f2478..95ed8acaf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -62,7 +62,6 @@ "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp", "ptypes/wrappers", ] pruneopts = "NUT" @@ -754,7 +753,6 @@ "github.com/container-storage-interface/spec/lib/go/csi", "github.com/golang/mock/gomock", "github.com/golang/protobuf/ptypes", - "github.com/golang/protobuf/ptypes/timestamp", "github.com/kubernetes-csi/csi-lib-utils/connection", "github.com/kubernetes-csi/csi-lib-utils/leaderelection", "github.com/kubernetes-csi/csi-lib-utils/rpc", diff --git a/cmd/csi-snapshotter/create_crd.go b/cmd/csi-snapshotter/create_crd.go index 656e1bd2d..0460f691f 100644 --- a/cmd/csi-snapshotter/create_crd.go +++ b/cmd/csi-snapshotter/create_crd.go @@ -38,8 +38,12 @@ func CreateCRD(clientset apiextensionsclient.Interface) error { Plural: crdv1.VolumeSnapshotClassResourcePlural, Kind: reflect.TypeOf(crdv1.VolumeSnapshotClass{}).Name(), }, + Subresources: &apiextensionsv1beta1.CustomResourceSubresources{ + Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}, + }, }, } + res, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) if err != nil && !apierrors.IsAlreadyExists(err) { @@ -80,6 +84,9 @@ func CreateCRD(clientset apiextensionsclient.Interface) error { Plural: crdv1.VolumeSnapshotResourcePlural, Kind: reflect.TypeOf(crdv1.VolumeSnapshot{}).Name(), }, + Subresources: &apiextensionsv1beta1.CustomResourceSubresources{ + Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}, + }, }, } res, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) diff --git a/cmd/csi-snapshotter/main.go b/cmd/csi-snapshotter/main.go index 880320c05..4b93a4a22 100644 --- a/cmd/csi-snapshotter/main.go +++ b/cmd/csi-snapshotter/main.go @@ -66,7 +66,7 @@ var ( snapshotNamePrefix = flag.String("snapshot-name-prefix", "snapshot", "Prefix to apply to the name of a created snapshot") snapshotNameUUIDLength = flag.Int("snapshot-name-uuid-length", -1, "Length in characters for the generated uuid of a created snapshot. Defaults behavior is to NOT truncate.") showVersion = flag.Bool("version", false, "Show version.") - csiTimeout = flag.Duration("timeout", defaultCSITimeout, "The timeout for any RPCs to the CSI driver. Default is 10s.") + csiTimeout = flag.Duration("timeout", defaultCSITimeout, "The timeout for any RPCs to the CSI driver. Default is 1 minute.") leaderElection = flag.Bool("leader-election", false, "Enables leader election.") leaderElectionNamespace = flag.String("leader-election-namespace", "", "The namespace where the leader election resource exists. Defaults to the pod namespace if not set.") @@ -184,9 +184,9 @@ func main() { snapClient, kubeClient, *snapshotterName, - factory.Volumesnapshot().V1alpha1().VolumeSnapshots(), - factory.Volumesnapshot().V1alpha1().VolumeSnapshotContents(), - factory.Volumesnapshot().V1alpha1().VolumeSnapshotClasses(), + factory.Snapshot().V1alpha1().VolumeSnapshots(), + factory.Snapshot().V1alpha1().VolumeSnapshotContents(), + factory.Snapshot().V1alpha1().VolumeSnapshotClasses(), coreFactory.Core().V1().PersistentVolumeClaims(), *createSnapshotContentRetryCount, *createSnapshotContentInterval, diff --git a/deploy/kubernetes/rbac.yaml b/deploy/kubernetes/rbac.yaml index 3cda54874..6a07ee321 100644 --- a/deploy/kubernetes/rbac.yaml +++ b/deploy/kubernetes/rbac.yaml @@ -25,7 +25,7 @@ rules: verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["persistentvolumeclaims"] - verbs: ["get", "list", "watch"] + verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] @@ -44,9 +44,12 @@ rules: - apiGroups: ["snapshot.storage.k8s.io"] resources: ["volumesnapshots"] verbs: ["get", "list", "watch", "update"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots/status"] + verbs: ["update"] - apiGroups: ["apiextensions.k8s.io"] resources: ["customresourcedefinitions"] - verbs: ["create", "list", "watch", "delete"] + verbs: ["create", "list", "watch", "delete", "get", "update"] --- kind: ClusterRoleBinding diff --git a/deploy/kubernetes/setup-csi-snapshotter.yaml b/deploy/kubernetes/setup-csi-snapshotter.yaml index d3b9e4ade..74020e699 100644 --- a/deploy/kubernetes/setup-csi-snapshotter.yaml +++ b/deploy/kubernetes/setup-csi-snapshotter.yaml @@ -72,7 +72,7 @@ spec: serviceAccount: csi-snapshotter containers: - name: csi-provisioner - image: quay.io/k8scsi/csi-provisioner:v1.0.1 + image: quay.io/k8scsi/csi-provisioner:v1.1.0 args: - "--provisioner=csi-hostpath" - "--csi-address=$(ADDRESS)" @@ -85,7 +85,7 @@ spec: - name: socket-dir mountPath: /csi - name: csi-snapshotter - image: quay.io/k8scsi/csi-snapshotter:v1.0.1 + image: quay.io/k8scsi/csi-snapshotter:v1.1.0 args: - "--csi-address=$(ADDRESS)" - "--connection-timeout=15s" @@ -98,7 +98,7 @@ spec: - name: socket-dir mountPath: /csi - name: hostpath - image: quay.io/k8scsi/hostpathplugin:v1.0.1 + image: quay.io/k8scsi/hostpathplugin:v1.1.0 args: - "--v=5" - "--endpoint=$(CSI_ENDPOINT)" diff --git a/pkg/apis/volumesnapshot/v1alpha1/doc.go b/pkg/apis/volumesnapshot/v1alpha1/doc.go index fd1b5f86c..90642a58b 100644 --- a/pkg/apis/volumesnapshot/v1alpha1/doc.go +++ b/pkg/apis/volumesnapshot/v1alpha1/doc.go @@ -15,5 +15,6 @@ limitations under the License. */ // +k8s:deepcopy-gen=package +// +groupName=snapshot.storage.k8s.io package v1alpha1 diff --git a/pkg/apis/volumesnapshot/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/volumesnapshot/v1alpha1/zz_generated.deepcopy.go index baed77c6f..38db004a8 100644 --- a/pkg/apis/volumesnapshot/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/volumesnapshot/v1alpha1/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ // +build !ignore_autogenerated /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 5aefca141..5bcff9985 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ limitations under the License. package versioned import ( - volumesnapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1" + snapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" @@ -27,27 +27,19 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface - VolumesnapshotV1alpha1() volumesnapshotv1alpha1.VolumesnapshotV1alpha1Interface - // Deprecated: please explicitly pick a version if possible. - Volumesnapshot() volumesnapshotv1alpha1.VolumesnapshotV1alpha1Interface + SnapshotV1alpha1() snapshotv1alpha1.SnapshotV1alpha1Interface } // Clientset contains the clients for groups. Each group has exactly one // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient - volumesnapshotV1alpha1 *volumesnapshotv1alpha1.VolumesnapshotV1alpha1Client + snapshotV1alpha1 *snapshotv1alpha1.SnapshotV1alpha1Client } -// VolumesnapshotV1alpha1 retrieves the VolumesnapshotV1alpha1Client -func (c *Clientset) VolumesnapshotV1alpha1() volumesnapshotv1alpha1.VolumesnapshotV1alpha1Interface { - return c.volumesnapshotV1alpha1 -} - -// Deprecated: Volumesnapshot retrieves the default version of VolumesnapshotClient. -// Please explicitly pick a version. -func (c *Clientset) Volumesnapshot() volumesnapshotv1alpha1.VolumesnapshotV1alpha1Interface { - return c.volumesnapshotV1alpha1 +// SnapshotV1alpha1 retrieves the SnapshotV1alpha1Client +func (c *Clientset) SnapshotV1alpha1() snapshotv1alpha1.SnapshotV1alpha1Interface { + return c.snapshotV1alpha1 } // Discovery retrieves the DiscoveryClient @@ -66,7 +58,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { } var cs Clientset var err error - cs.volumesnapshotV1alpha1, err = volumesnapshotv1alpha1.NewForConfig(&configShallowCopy) + cs.snapshotV1alpha1, err = snapshotv1alpha1.NewForConfig(&configShallowCopy) if err != nil { return nil, err } @@ -82,7 +74,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { // panics if there is an error in the config. func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset - cs.volumesnapshotV1alpha1 = volumesnapshotv1alpha1.NewForConfigOrDie(c) + cs.snapshotV1alpha1 = snapshotv1alpha1.NewForConfigOrDie(c) cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) return &cs @@ -91,7 +83,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { // New creates a new Clientset for the given RESTClient. func New(c rest.Interface) *Clientset { var cs Clientset - cs.volumesnapshotV1alpha1 = volumesnapshotv1alpha1.New(c) + cs.snapshotV1alpha1 = snapshotv1alpha1.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) return &cs diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go index 006b79214..dc992b90b 100644 --- a/pkg/client/clientset/versioned/doc.go +++ b/pkg/client/clientset/versioned/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index d1c26394c..0c5b82937 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ package fake import ( clientset "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned" - volumesnapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1" - fakevolumesnapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake" + snapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1" + fakesnapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" @@ -71,12 +71,7 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface { var _ clientset.Interface = &Clientset{} -// VolumesnapshotV1alpha1 retrieves the VolumesnapshotV1alpha1Client -func (c *Clientset) VolumesnapshotV1alpha1() volumesnapshotv1alpha1.VolumesnapshotV1alpha1Interface { - return &fakevolumesnapshotv1alpha1.FakeVolumesnapshotV1alpha1{Fake: &c.Fake} -} - -// Volumesnapshot retrieves the VolumesnapshotV1alpha1Client -func (c *Clientset) Volumesnapshot() volumesnapshotv1alpha1.VolumesnapshotV1alpha1Interface { - return &fakevolumesnapshotv1alpha1.FakeVolumesnapshotV1alpha1{Fake: &c.Fake} +// SnapshotV1alpha1 retrieves the SnapshotV1alpha1Client +func (c *Clientset) SnapshotV1alpha1() snapshotv1alpha1.SnapshotV1alpha1Interface { + return &fakesnapshotv1alpha1.FakeSnapshotV1alpha1{Fake: &c.Fake} } diff --git a/pkg/client/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go index 0bc260bca..acfa6173b 100644 --- a/pkg/client/clientset/versioned/fake/doc.go +++ b/pkg/client/clientset/versioned/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 1f1b72611..903889fd9 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ limitations under the License. package fake import ( - volumesnapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1" + snapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -31,7 +31,7 @@ var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) var parameterCodec = runtime.NewParameterCodec(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ - volumesnapshotv1alpha1.AddToScheme, + snapshotv1alpha1.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/pkg/client/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go index 5c5c8debb..7f61dc1f9 100644 --- a/pkg/client/clientset/versioned/scheme/doc.go +++ b/pkg/client/clientset/versioned/scheme/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index 6215c628f..46efa8fe5 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ limitations under the License. package scheme import ( - volumesnapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1" + snapshotv1alpha1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -31,7 +31,7 @@ var Scheme = runtime.NewScheme() var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ - volumesnapshotv1alpha1.AddToScheme, + snapshotv1alpha1.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/doc.go index 69ca30111..9752e759c 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/doc.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/doc.go index 87a1873ed..ab4fd43ad 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/doc.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot.go index 5da6c9314..05016a1a2 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,13 +30,13 @@ import ( // FakeVolumeSnapshots implements VolumeSnapshotInterface type FakeVolumeSnapshots struct { - Fake *FakeVolumesnapshotV1alpha1 + Fake *FakeSnapshotV1alpha1 ns string } -var volumesnapshotsResource = schema.GroupVersionResource{Group: "volumesnapshot", Version: "v1alpha1", Resource: "volumesnapshots"} +var volumesnapshotsResource = schema.GroupVersionResource{Group: "snapshot.storage.k8s.io", Version: "v1alpha1", Resource: "volumesnapshots"} -var volumesnapshotsKind = schema.GroupVersionKind{Group: "volumesnapshot", Version: "v1alpha1", Kind: "VolumeSnapshot"} +var volumesnapshotsKind = schema.GroupVersionKind{Group: "snapshot.storage.k8s.io", Version: "v1alpha1", Kind: "VolumeSnapshot"} // Get takes name of the volumeSnapshot, and returns the corresponding volumeSnapshot object, and an error if there is any. func (c *FakeVolumeSnapshots) Get(name string, options v1.GetOptions) (result *v1alpha1.VolumeSnapshot, err error) { diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot_client.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot_client.go index 7433d14f2..6c8b11a57 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot_client.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshot_client.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,25 +24,25 @@ import ( testing "k8s.io/client-go/testing" ) -type FakeVolumesnapshotV1alpha1 struct { +type FakeSnapshotV1alpha1 struct { *testing.Fake } -func (c *FakeVolumesnapshotV1alpha1) VolumeSnapshots(namespace string) v1alpha1.VolumeSnapshotInterface { +func (c *FakeSnapshotV1alpha1) VolumeSnapshots(namespace string) v1alpha1.VolumeSnapshotInterface { return &FakeVolumeSnapshots{c, namespace} } -func (c *FakeVolumesnapshotV1alpha1) VolumeSnapshotClasses() v1alpha1.VolumeSnapshotClassInterface { +func (c *FakeSnapshotV1alpha1) VolumeSnapshotClasses() v1alpha1.VolumeSnapshotClassInterface { return &FakeVolumeSnapshotClasses{c} } -func (c *FakeVolumesnapshotV1alpha1) VolumeSnapshotContents() v1alpha1.VolumeSnapshotContentInterface { +func (c *FakeSnapshotV1alpha1) VolumeSnapshotContents() v1alpha1.VolumeSnapshotContentInterface { return &FakeVolumeSnapshotContents{c} } // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. -func (c *FakeVolumesnapshotV1alpha1) RESTClient() rest.Interface { +func (c *FakeSnapshotV1alpha1) RESTClient() rest.Interface { var ret *rest.RESTClient return ret } diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotclass.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotclass.go index 03c18b686..5c756b20b 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotclass.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotclass.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,12 +30,12 @@ import ( // FakeVolumeSnapshotClasses implements VolumeSnapshotClassInterface type FakeVolumeSnapshotClasses struct { - Fake *FakeVolumesnapshotV1alpha1 + Fake *FakeSnapshotV1alpha1 } -var volumesnapshotclassesResource = schema.GroupVersionResource{Group: "volumesnapshot", Version: "v1alpha1", Resource: "volumesnapshotclasses"} +var volumesnapshotclassesResource = schema.GroupVersionResource{Group: "snapshot.storage.k8s.io", Version: "v1alpha1", Resource: "volumesnapshotclasses"} -var volumesnapshotclassesKind = schema.GroupVersionKind{Group: "volumesnapshot", Version: "v1alpha1", Kind: "VolumeSnapshotClass"} +var volumesnapshotclassesKind = schema.GroupVersionKind{Group: "snapshot.storage.k8s.io", Version: "v1alpha1", Kind: "VolumeSnapshotClass"} // Get takes name of the volumeSnapshotClass, and returns the corresponding volumeSnapshotClass object, and an error if there is any. func (c *FakeVolumeSnapshotClasses) Get(name string, options v1.GetOptions) (result *v1alpha1.VolumeSnapshotClass, err error) { diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotcontent.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotcontent.go index 2bdd3c5f6..f646d1d9d 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotcontent.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/fake/fake_volumesnapshotcontent.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,12 +30,12 @@ import ( // FakeVolumeSnapshotContents implements VolumeSnapshotContentInterface type FakeVolumeSnapshotContents struct { - Fake *FakeVolumesnapshotV1alpha1 + Fake *FakeSnapshotV1alpha1 } -var volumesnapshotcontentsResource = schema.GroupVersionResource{Group: "volumesnapshot", Version: "v1alpha1", Resource: "volumesnapshotcontents"} +var volumesnapshotcontentsResource = schema.GroupVersionResource{Group: "snapshot.storage.k8s.io", Version: "v1alpha1", Resource: "volumesnapshotcontents"} -var volumesnapshotcontentsKind = schema.GroupVersionKind{Group: "volumesnapshot", Version: "v1alpha1", Kind: "VolumeSnapshotContent"} +var volumesnapshotcontentsKind = schema.GroupVersionKind{Group: "snapshot.storage.k8s.io", Version: "v1alpha1", Kind: "VolumeSnapshotContent"} // Get takes name of the volumeSnapshotContent, and returns the corresponding volumeSnapshotContent object, and an error if there is any. func (c *FakeVolumeSnapshotContents) Get(name string, options v1.GetOptions) (result *v1alpha1.VolumeSnapshotContent, err error) { diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/generated_expansion.go index 5c57b1149..9b641f106 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot.go index f383e8073..a2f80805a 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -56,7 +56,7 @@ type volumeSnapshots struct { } // newVolumeSnapshots returns a VolumeSnapshots -func newVolumeSnapshots(c *VolumesnapshotV1alpha1Client, namespace string) *volumeSnapshots { +func newVolumeSnapshots(c *SnapshotV1alpha1Client, namespace string) *volumeSnapshots { return &volumeSnapshots{ client: c.RESTClient(), ns: namespace, diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot_client.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot_client.go index 7209839dc..ba06f6576 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot_client.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshot_client.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,32 +25,32 @@ import ( rest "k8s.io/client-go/rest" ) -type VolumesnapshotV1alpha1Interface interface { +type SnapshotV1alpha1Interface interface { RESTClient() rest.Interface VolumeSnapshotsGetter VolumeSnapshotClassesGetter VolumeSnapshotContentsGetter } -// VolumesnapshotV1alpha1Client is used to interact with features provided by the volumesnapshot group. -type VolumesnapshotV1alpha1Client struct { +// SnapshotV1alpha1Client is used to interact with features provided by the snapshot.storage.k8s.io group. +type SnapshotV1alpha1Client struct { restClient rest.Interface } -func (c *VolumesnapshotV1alpha1Client) VolumeSnapshots(namespace string) VolumeSnapshotInterface { +func (c *SnapshotV1alpha1Client) VolumeSnapshots(namespace string) VolumeSnapshotInterface { return newVolumeSnapshots(c, namespace) } -func (c *VolumesnapshotV1alpha1Client) VolumeSnapshotClasses() VolumeSnapshotClassInterface { +func (c *SnapshotV1alpha1Client) VolumeSnapshotClasses() VolumeSnapshotClassInterface { return newVolumeSnapshotClasses(c) } -func (c *VolumesnapshotV1alpha1Client) VolumeSnapshotContents() VolumeSnapshotContentInterface { +func (c *SnapshotV1alpha1Client) VolumeSnapshotContents() VolumeSnapshotContentInterface { return newVolumeSnapshotContents(c) } -// NewForConfig creates a new VolumesnapshotV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*VolumesnapshotV1alpha1Client, error) { +// NewForConfig creates a new SnapshotV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*SnapshotV1alpha1Client, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err @@ -59,12 +59,12 @@ func NewForConfig(c *rest.Config) (*VolumesnapshotV1alpha1Client, error) { if err != nil { return nil, err } - return &VolumesnapshotV1alpha1Client{client}, nil + return &SnapshotV1alpha1Client{client}, nil } -// NewForConfigOrDie creates a new VolumesnapshotV1alpha1Client for the given config and +// NewForConfigOrDie creates a new SnapshotV1alpha1Client for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *VolumesnapshotV1alpha1Client { +func NewForConfigOrDie(c *rest.Config) *SnapshotV1alpha1Client { client, err := NewForConfig(c) if err != nil { panic(err) @@ -72,9 +72,9 @@ func NewForConfigOrDie(c *rest.Config) *VolumesnapshotV1alpha1Client { return client } -// New creates a new VolumesnapshotV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *VolumesnapshotV1alpha1Client { - return &VolumesnapshotV1alpha1Client{c} +// New creates a new SnapshotV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *SnapshotV1alpha1Client { + return &SnapshotV1alpha1Client{c} } func setConfigDefaults(config *rest.Config) error { @@ -92,7 +92,7 @@ func setConfigDefaults(config *rest.Config) error { // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. -func (c *VolumesnapshotV1alpha1Client) RESTClient() rest.Interface { +func (c *SnapshotV1alpha1Client) RESTClient() rest.Interface { if c == nil { return nil } diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotclass.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotclass.go index 9e48bd945..8b788df4a 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotclass.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotclass.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ type volumeSnapshotClasses struct { } // newVolumeSnapshotClasses returns a VolumeSnapshotClasses -func newVolumeSnapshotClasses(c *VolumesnapshotV1alpha1Client) *volumeSnapshotClasses { +func newVolumeSnapshotClasses(c *SnapshotV1alpha1Client) *volumeSnapshotClasses { return &volumeSnapshotClasses{ client: c.RESTClient(), } diff --git a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotcontent.go b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotcontent.go index 11aac5159..e393ccdfe 100644 --- a/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotcontent.go +++ b/pkg/client/clientset/versioned/typed/volumesnapshot/v1alpha1/volumesnapshotcontent.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ type volumeSnapshotContents struct { } // newVolumeSnapshotContents returns a VolumeSnapshotContents -func newVolumeSnapshotContents(c *VolumesnapshotV1alpha1Client) *volumeSnapshotContents { +func newVolumeSnapshotContents(c *SnapshotV1alpha1Client) *volumeSnapshotContents { return &volumeSnapshotContents{ client: c.RESTClient(), } diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 0a478827b..f01d7993b 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -172,9 +172,9 @@ type SharedInformerFactory interface { ForResource(resource schema.GroupVersionResource) (GenericInformer, error) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - Volumesnapshot() volumesnapshot.Interface + Snapshot() volumesnapshot.Interface } -func (f *sharedInformerFactory) Volumesnapshot() volumesnapshot.Interface { +func (f *sharedInformerFactory) Snapshot() volumesnapshot.Interface { return volumesnapshot.New(f, f.namespace, f.tweakListOptions) } diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index ff7e7d464..193942d0e 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -52,13 +52,13 @@ func (f *genericInformer) Lister() cache.GenericLister { // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { - // Group=volumesnapshot, Version=v1alpha1 + // Group=snapshot.storage.k8s.io, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("volumesnapshots"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Volumesnapshot().V1alpha1().VolumeSnapshots().Informer()}, nil + return &genericInformer{resource: resource.GroupResource(), informer: f.Snapshot().V1alpha1().VolumeSnapshots().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("volumesnapshotclasses"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Volumesnapshot().V1alpha1().VolumeSnapshotClasses().Informer()}, nil + return &genericInformer{resource: resource.GroupResource(), informer: f.Snapshot().V1alpha1().VolumeSnapshotClasses().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("volumesnapshotcontents"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Volumesnapshot().V1alpha1().VolumeSnapshotContents().Informer()}, nil + return &genericInformer{resource: resource.GroupResource(), informer: f.Snapshot().V1alpha1().VolumeSnapshotContents().Informer()}, nil } diff --git a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go index a11e57cd4..daf0bfe89 100644 --- a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/volumesnapshot/interface.go b/pkg/client/informers/externalversions/volumesnapshot/interface.go index 49bd322b1..ba406ea94 100644 --- a/pkg/client/informers/externalversions/volumesnapshot/interface.go +++ b/pkg/client/informers/externalversions/volumesnapshot/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ limitations under the License. // Code generated by informer-gen. DO NOT EDIT. -package volumesnapshot +package snapshot import ( internalinterfaces "github.com/kubernetes-csi/external-snapshotter/pkg/client/informers/externalversions/internalinterfaces" diff --git a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/interface.go b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/interface.go index 1a41d0c38..ded10e911 100644 --- a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshot.go b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshot.go index c642e70f9..babb79b87 100644 --- a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshot.go +++ b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshot.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -61,13 +61,13 @@ func NewFilteredVolumeSnapshotInformer(client versioned.Interface, namespace str if tweakListOptions != nil { tweakListOptions(&options) } - return client.VolumesnapshotV1alpha1().VolumeSnapshots(namespace).List(options) + return client.SnapshotV1alpha1().VolumeSnapshots(namespace).List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.VolumesnapshotV1alpha1().VolumeSnapshots(namespace).Watch(options) + return client.SnapshotV1alpha1().VolumeSnapshots(namespace).Watch(options) }, }, &volumesnapshotv1alpha1.VolumeSnapshot{}, diff --git a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotclass.go b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotclass.go index d0b0fcc71..0026e5572 100644 --- a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotclass.go +++ b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotclass.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -60,13 +60,13 @@ func NewFilteredVolumeSnapshotClassInformer(client versioned.Interface, resyncPe if tweakListOptions != nil { tweakListOptions(&options) } - return client.VolumesnapshotV1alpha1().VolumeSnapshotClasses().List(options) + return client.SnapshotV1alpha1().VolumeSnapshotClasses().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.VolumesnapshotV1alpha1().VolumeSnapshotClasses().Watch(options) + return client.SnapshotV1alpha1().VolumeSnapshotClasses().Watch(options) }, }, &volumesnapshotv1alpha1.VolumeSnapshotClass{}, diff --git a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotcontent.go b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotcontent.go index 906aad462..2b34344d5 100644 --- a/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotcontent.go +++ b/pkg/client/informers/externalversions/volumesnapshot/v1alpha1/volumesnapshotcontent.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -60,13 +60,13 @@ func NewFilteredVolumeSnapshotContentInformer(client versioned.Interface, resync if tweakListOptions != nil { tweakListOptions(&options) } - return client.VolumesnapshotV1alpha1().VolumeSnapshotContents().List(options) + return client.SnapshotV1alpha1().VolumeSnapshotContents().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.VolumesnapshotV1alpha1().VolumeSnapshotContents().Watch(options) + return client.SnapshotV1alpha1().VolumeSnapshotContents().Watch(options) }, }, &volumesnapshotv1alpha1.VolumeSnapshotContent{}, diff --git a/pkg/client/listers/volumesnapshot/v1alpha1/expansion_generated.go b/pkg/client/listers/volumesnapshot/v1alpha1/expansion_generated.go index 15740671e..f92624ed7 100644 --- a/pkg/client/listers/volumesnapshot/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/volumesnapshot/v1alpha1/expansion_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshot.go b/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshot.go index 4cc5cdc5d..ffa8f16d8 100644 --- a/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshot.go +++ b/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshot.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotclass.go b/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotclass.go index bcb5cd65a..505d397bf 100644 --- a/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotclass.go +++ b/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotclass.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotcontent.go b/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotcontent.go index b12fdb552..97d4e90aa 100644 --- a/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotcontent.go +++ b/pkg/client/listers/volumesnapshot/v1alpha1/volumesnapshotcontent.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/controller/csi_handler.go b/pkg/controller/csi_handler.go index 2abee0978..3b3a9000a 100644 --- a/pkg/controller/csi_handler.go +++ b/pkg/controller/csi_handler.go @@ -30,9 +30,9 @@ import ( // Handler is responsible for handling VolumeSnapshot events from informer. type Handler interface { - CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, int64, int64, bool, error) + CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, 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, int64, int64, error) + GetSnapshotStatus(content *crdv1.VolumeSnapshotContent) (bool, time.Time, int64, error) } // csiHandler is a handler that calls CSI to create/delete volume snapshot. @@ -58,18 +58,18 @@ func NewCSIHandler( } } -func (handler *csiHandler) CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, int64, int64, bool, error) { +func (handler *csiHandler) CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error) { ctx, cancel := context.WithTimeout(context.Background(), handler.timeout) defer cancel() snapshotName, err := makeSnapshotName(handler.snapshotNamePrefix, string(snapshot.UID), handler.snapshotNameUUIDLength) if err != nil { - return "", "", 0, 0, false, err + return "", "", time.Time{}, 0, false, err } newParameters, err := removePrefixedParameters(parameters) if err != nil { - return "", "", 0, 0, false, fmt.Errorf("failed to remove CSI Parameters of prefixed keys: %v", err) + return "", "", time.Time{}, 0, false, fmt.Errorf("failed to remove CSI Parameters of prefixed keys: %v", err) } return handler.snapshotter.CreateSnapshot(ctx, snapshotName, volume, newParameters, snapshotterCredentials) } @@ -89,16 +89,16 @@ func (handler *csiHandler) DeleteSnapshot(content *crdv1.VolumeSnapshotContent, return nil } -func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotContent) (bool, int64, int64, error) { +func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotContent) (bool, time.Time, int64, error) { if content.Spec.CSI == nil { - return false, 0, 0, fmt.Errorf("CSISnapshot not defined in spec") + return false, time.Time{}, 0, fmt.Errorf("CSISnapshot not defined in spec") } ctx, cancel := context.WithTimeout(context.Background(), handler.timeout) defer cancel() csiSnapshotStatus, timestamp, size, err := handler.snapshotter.GetSnapshotStatus(ctx, content.Spec.CSI.SnapshotHandle) if err != nil { - return false, 0, 0, fmt.Errorf("failed to list snapshot content %s: %q", content.Name, err) + return false, time.Time{}, 0, fmt.Errorf("failed to list snapshot content %s: %q", content.Name, err) } return csiSnapshotStatus, timestamp, size, nil diff --git a/pkg/controller/framework_test.go b/pkg/controller/framework_test.go index a9ad4832e..3ae96e218 100644 --- a/pkg/controller/framework_test.go +++ b/pkg/controller/framework_test.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "reflect" + sysruntime "runtime" "strconv" "strings" "sync" @@ -53,6 +54,7 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/klog" + "k8s.io/kubernetes/pkg/util/slice" ) // This is a unit test framework for snapshot controller. @@ -110,7 +112,8 @@ type controllerTest struct { // List of expected CSI list snapshot calls expectedListCalls []listCall // Function to call as the test. - test testCall + test testCall + expectSuccess bool } type testCall func(ctrl *csiSnapshotController, reactor *snapshotReactor, test controllerTest) error @@ -177,6 +180,11 @@ func withContentFinalizer(content *crdv1.VolumeSnapshotContent) *crdv1.VolumeSna return content } +func withPVCFinalizer(pvc *v1.PersistentVolumeClaim) *v1.PersistentVolumeClaim { + pvc.ObjectMeta.Finalizers = append(pvc.ObjectMeta.Finalizers, PVCFinalizer) + return pvc +} + // React is a callback called by fake kubeClient from the controller. // In other words, every snapshot/content change performed by the controller ends // here. @@ -331,6 +339,32 @@ func (r *snapshotReactor) React(action core.Action) (handled bool, ret runtime.O klog.V(4).Infof("GetClaim: claim %s not found", name) return true, nil, fmt.Errorf("cannot find claim %s", name) + case action.Matches("update", "persistentvolumeclaims"): + obj := action.(core.UpdateAction).GetObject() + claim := obj.(*v1.PersistentVolumeClaim) + + // Check and bump object version + storedClaim, found := r.claims[claim.Name] + if found { + storedVer, _ := strconv.Atoi(storedClaim.ResourceVersion) + requestedVer, _ := strconv.Atoi(claim.ResourceVersion) + if storedVer != requestedVer { + return true, obj, errVersionConflict + } + // Don't modify the existing object + claim = claim.DeepCopy() + claim.ResourceVersion = strconv.Itoa(storedVer + 1) + } else { + return true, nil, fmt.Errorf("cannot update claim %s: claim not found", claim.Name) + } + + // Store the updated object to appropriate places. + r.claims[claim.Name] = claim + r.changedObjects = append(r.changedObjects, claim) + r.changedSinceLastSync++ + klog.V(4).Infof("saved updated claim %s", claim.Name) + return true, claim, nil + case action.Matches("get", "storageclasses"): name := action.(core.GetAction).GetName() storageClass, found := r.storageClasses[name] @@ -550,6 +584,9 @@ func (r *snapshotReactor) syncAll() { for _, v := range r.contents { r.changedObjects = append(r.changedObjects, v) } + for _, pvc := range r.claims { + r.changedObjects = append(r.changedObjects, pvc) + } r.changedSinceLastSync = 0 } @@ -699,6 +736,7 @@ func newSnapshotReactor(kubeClient *kubefake.Clientset, client *fake.Clientset, client.AddReactor("delete", "volumesnapshotcontents", reactor.React) client.AddReactor("delete", "volumesnapshots", reactor.React) kubeClient.AddReactor("get", "persistentvolumeclaims", reactor.React) + kubeClient.AddReactor("update", "persistentvolumeclaims", reactor.React) kubeClient.AddReactor("get", "persistentvolumes", reactor.React) kubeClient.AddReactor("get", "storageclasses", reactor.React) kubeClient.AddReactor("get", "secrets", reactor.React) @@ -728,9 +766,9 @@ func newTestController(kubeClient kubernetes.Interface, clientset clientset.Inte clientset, kubeClient, mockDriverName, - informerFactory.Volumesnapshot().V1alpha1().VolumeSnapshots(), - informerFactory.Volumesnapshot().V1alpha1().VolumeSnapshotContents(), - informerFactory.Volumesnapshot().V1alpha1().VolumeSnapshotClasses(), + informerFactory.Snapshot().V1alpha1().VolumeSnapshots(), + informerFactory.Snapshot().V1alpha1().VolumeSnapshotContents(), + informerFactory.Snapshot().V1alpha1().VolumeSnapshotClasses(), coreFactory.Core().V1().PersistentVolumeClaims(), 3, 5*time.Millisecond, @@ -746,6 +784,7 @@ func newTestController(kubeClient kubernetes.Interface, clientset clientset.Inte ctrl.contentListerSynced = alwaysReady ctrl.snapshotListerSynced = alwaysReady ctrl.classListerSynced = alwaysReady + ctrl.pvcListerSynced = alwaysReady return ctrl, nil } @@ -845,7 +884,7 @@ func newSnapshotArray(name, className, boundToContent, snapshotUID, claimName st } // newClaim returns a new claim with given attributes -func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.PersistentVolumeClaimPhase, class *string) *v1.PersistentVolumeClaim { +func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.PersistentVolumeClaimPhase, class *string, bFinalizer bool) *v1.PersistentVolumeClaim { claim := v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -877,6 +916,9 @@ func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.Persisten claim.Status.Capacity = claim.Spec.Resources.Requests } + if bFinalizer { + return withPVCFinalizer(&claim) + } return &claim } @@ -884,12 +926,28 @@ func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.Persisten // newClaim() with the same parameters. func newClaimArray(name, claimUID, capacity, boundToVolume string, phase v1.PersistentVolumeClaimPhase, class *string) []*v1.PersistentVolumeClaim { return []*v1.PersistentVolumeClaim{ - newClaim(name, claimUID, capacity, boundToVolume, phase, class), + newClaim(name, claimUID, capacity, boundToVolume, phase, class, false), + } +} + +// newClaimArrayFinalizer returns array with a single claim that would be returned by +// newClaim() with the same parameters plus finalizer. +func newClaimArrayFinalizer(name, claimUID, capacity, boundToVolume string, phase v1.PersistentVolumeClaimPhase, class *string) []*v1.PersistentVolumeClaim { + return []*v1.PersistentVolumeClaim{ + newClaim(name, claimUID, capacity, boundToVolume, phase, class, true), } } // newVolume returns a new volume with given attributes -func newVolume(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundToClaimName string, phase v1.PersistentVolumePhase, reclaimPolicy v1.PersistentVolumeReclaimPolicy, class string, annotations ...string) *v1.PersistentVolume { +func newVolume(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundToClaimName string, phase v1.PersistentVolumePhase, reclaimPolicy v1.PersistentVolumeReclaimPolicy, class string, driver string, namespace string, annotations ...string) *v1.PersistentVolume { + inDriverName := mockDriverName + if driver != "" { + inDriverName = driver + } + inNamespace := testNamespace + if namespace != "" { + inNamespace = namespace + } volume := v1.PersistentVolume{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -903,7 +961,7 @@ func newVolume(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundTo }, PersistentVolumeSource: v1.PersistentVolumeSource{ CSI: &v1.CSIPersistentVolumeSource{ - Driver: mockDriverName, + Driver: inDriverName, VolumeHandle: volumeHandle, }, }, @@ -921,7 +979,7 @@ func newVolume(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundTo Kind: "PersistentVolumeClaim", APIVersion: "v1", UID: types.UID(boundToClaimUID), - Namespace: testNamespace, + Namespace: inNamespace, Name: boundToClaimName, } } @@ -931,9 +989,9 @@ func newVolume(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundTo // newVolumeArray returns array with a single volume that would be returned by // newVolume() with the same parameters. -func newVolumeArray(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundToClaimName string, phase v1.PersistentVolumePhase, reclaimPolicy v1.PersistentVolumeReclaimPolicy, class string) []*v1.PersistentVolume { +func newVolumeArray(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundToClaimName string, phase v1.PersistentVolumePhase, reclaimPolicy v1.PersistentVolumeReclaimPolicy, class string, driver string, namespace string) []*v1.PersistentVolume { return []*v1.PersistentVolume{ - newVolume(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundToClaimName, phase, reclaimPolicy, class), + newVolume(name, volumeUID, volumeHandle, capacity, boundToClaimUID, boundToClaimName, phase, reclaimPolicy, class, driver, namespace), } } @@ -961,6 +1019,14 @@ func testSyncContent(ctrl *csiSnapshotController, reactor *snapshotReactor, test return ctrl.syncContent(test.initialContents[0]) } +func testAddPVCFinalizer(ctrl *csiSnapshotController, reactor *snapshotReactor, test controllerTest) error { + return ctrl.ensureSnapshotSourceFinalizer(test.initialSnapshots[0]) +} + +func testRemovePVCFinalizer(ctrl *csiSnapshotController, reactor *snapshotReactor, test controllerTest) error { + return ctrl.checkandRemoveSnapshotSourceFinalizer(test.initialSnapshots[0]) +} + var ( classEmpty string classGold = "gold" @@ -1097,6 +1163,113 @@ func runSyncTests(t *testing.T, tests []controllerTest, snapshotClasses []*crdv1 } } +// This tests ensureSnapshotSourceFinalizer and checkandRemoveSnapshotSourceFinalizer +func runPVCFinalizerTests(t *testing.T, tests []controllerTest, snapshotClasses []*crdv1.VolumeSnapshotClass) { + snapshotscheme.AddToScheme(scheme.Scheme) + for _, test := range tests { + klog.V(4).Infof("starting test %q", test.name) + + // Initialize the controller + kubeClient := &kubefake.Clientset{} + client := &fake.Clientset{} + + ctrl, err := newTestController(kubeClient, client, nil, t, test) + if err != nil { + t.Fatalf("Test %q construct persistent content failed: %v", test.name, err) + } + + reactor := newSnapshotReactor(kubeClient, client, ctrl, nil, nil, test.errors) + for _, snapshot := range test.initialSnapshots { + ctrl.snapshotStore.Add(snapshot) + reactor.snapshots[snapshot.Name] = snapshot + } + for _, content := range test.initialContents { + if ctrl.isDriverMatch(test.initialContents[0]) { + ctrl.contentStore.Add(content) + reactor.contents[content.Name] = content + } + } + + pvcIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + for _, claim := range test.initialClaims { + reactor.claims[claim.Name] = claim + pvcIndexer.Add(claim) + } + ctrl.pvcLister = corelisters.NewPersistentVolumeClaimLister(pvcIndexer) + + for _, volume := range test.initialVolumes { + reactor.volumes[volume.Name] = volume + } + for _, storageClass := range test.initialStorageClasses { + reactor.storageClasses[storageClass.Name] = storageClass + } + for _, secret := range test.initialSecrets { + reactor.secrets[secret.Name] = secret + } + + // 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 the tested functions + err = test.test(ctrl, reactor, test) + if err != nil { + t.Errorf("Test %q failed: %v", test.name, err) + } + + // Verify PVCFinalizer tests results + evaluatePVCFinalizerTests(ctrl, reactor, test, t) + } +} + +// Evaluate PVCFinalizer tests results +func evaluatePVCFinalizerTests(ctrl *csiSnapshotController, reactor *snapshotReactor, test controllerTest, t *testing.T) { + // Evaluate results + bHasPVCFinalizer := false + name := sysruntime.FuncForPC(reflect.ValueOf(test.test).Pointer()).Name() + index := strings.LastIndex(name, ".") + if index == -1 { + t.Errorf("Test %q: failed to test finalizer - invalid test call name [%s]", test.name, name) + return + } + names := []rune(name) + funcName := string(names[index+1 : len(name)]) + klog.V(4).Infof("test %q: PVCFinalizer test func name: [%s]", test.name, funcName) + + if funcName == "testAddPVCFinalizer" { + for _, pvc := range reactor.claims { + if test.initialClaims[0].Name == pvc.Name { + if !slice.ContainsString(test.initialClaims[0].ObjectMeta.Finalizers, PVCFinalizer, nil) && slice.ContainsString(pvc.ObjectMeta.Finalizers, PVCFinalizer, nil) { + klog.V(4).Infof("test %q succeeded. PVCFinalizer is added to PVC %s", test.name, pvc.Name) + bHasPVCFinalizer = true + } + break + } + } + if test.expectSuccess && !bHasPVCFinalizer { + t.Errorf("Test %q: failed to add finalizer to PVC %s", test.name, test.initialClaims[0].Name) + } + } + bHasPVCFinalizer = true + if funcName == "testRemovePVCFinalizer" { + for _, pvc := range reactor.claims { + if test.initialClaims[0].Name == pvc.Name { + if slice.ContainsString(test.initialClaims[0].ObjectMeta.Finalizers, PVCFinalizer, nil) && !slice.ContainsString(pvc.ObjectMeta.Finalizers, PVCFinalizer, nil) { + klog.V(4).Infof("test %q succeeded. PVCFinalizer is removed from PVC %s", test.name, pvc.Name) + bHasPVCFinalizer = false + } + break + } + } + if test.expectSuccess && bHasPVCFinalizer { + t.Errorf("Test %q: failed to remove finalizer from PVC %s", test.name, test.initialClaims[0].Name) + } + } +} + func getSize(size int64) *resource.Quantity { return resource.NewQuantity(size, resource.BinarySI) } @@ -1126,7 +1299,7 @@ type listCall struct { snapshotID string // information to return readyToUse bool - createTime int64 + createTime time.Time size int64 err error } @@ -1144,12 +1317,12 @@ type createCall struct { parameters map[string]string secrets map[string]string // information to return - driverName string - snapshotId string - timestamp int64 - size int64 - readyToUse bool - err error + driverName string + snapshotId string + creationTime time.Time + size int64 + readyToUse bool + err error } // Fake SnapShotter implementation that check that Attach/Detach is called @@ -1164,10 +1337,10 @@ type fakeSnapshotter struct { t *testing.T } -func (f *fakeSnapshotter) CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, int64, int64, bool, error) { +func (f *fakeSnapshotter) CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error) { if f.createCallCounter >= len(f.createCalls) { f.t.Errorf("Unexpected CSI Create Snapshot call: snapshotName=%s, volume=%v, index: %d, calls: %+v", snapshotName, volume.Name, f.createCallCounter, f.createCalls) - return "", "", 0, 0, false, fmt.Errorf("unexpected call") + return "", "", time.Time{}, 0, false, fmt.Errorf("unexpected call") } call := f.createCalls[f.createCallCounter] f.createCallCounter++ @@ -1194,10 +1367,10 @@ func (f *fakeSnapshotter) CreateSnapshot(ctx context.Context, snapshotName strin } if err != nil { - return "", "", 0, 0, false, fmt.Errorf("unexpected call") + return "", "", time.Time{}, 0, false, fmt.Errorf("unexpected call") } - return call.driverName, call.snapshotId, call.timestamp, call.size, call.readyToUse, call.err + return call.driverName, call.snapshotId, call.creationTime, call.size, call.readyToUse, call.err } func (f *fakeSnapshotter) DeleteSnapshot(ctx context.Context, snapshotID string, snapshotterCredentials map[string]string) error { @@ -1226,10 +1399,10 @@ func (f *fakeSnapshotter) DeleteSnapshot(ctx context.Context, snapshotID string, return call.err } -func (f *fakeSnapshotter) GetSnapshotStatus(ctx context.Context, snapshotID string) (bool, int64, int64, error) { +func (f *fakeSnapshotter) GetSnapshotStatus(ctx context.Context, snapshotID 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, 0, 0, fmt.Errorf("unexpected call") + return false, time.Time{}, 0, fmt.Errorf("unexpected call") } call := f.listCalls[f.listCallCounter] f.listCallCounter++ @@ -1241,7 +1414,7 @@ func (f *fakeSnapshotter) GetSnapshotStatus(ctx context.Context, snapshotID stri } if err != nil { - return false, 0, 0, fmt.Errorf("unexpected call") + return false, time.Time{}, 0, fmt.Errorf("unexpected call") } return call.readyToUse, call.createTime, call.size, call.err diff --git a/pkg/controller/snapshot_controller.go b/pkg/controller/snapshot_controller.go index 3d30844af..6f01aebc8 100644 --- a/pkg/controller/snapshot_controller.go +++ b/pkg/controller/snapshot_controller.go @@ -192,11 +192,19 @@ func (ctrl *csiSnapshotController) syncSnapshot(snapshot *crdv1.VolumeSnapshot) return ctrl.addSnapshotFinalizer(snapshot) } + klog.V(5).Infof("syncSnapshot[%s]: check if we should remove finalizer on snapshot source and remove it if we can", snapshotKey(snapshot)) + // Check if we should remove finalizer on snapshot source and remove it if we can. + errFinalizer := ctrl.checkandRemoveSnapshotSourceFinalizer(snapshot) + if errFinalizer != nil { + klog.Errorf("error check and remove snapshot source finalizer for snapshot [%s]: %v", snapshot.Name, errFinalizer) + // Log an event and keep the original error from syncUnready/ReadySnapshot + ctrl.eventRecorder.Event(snapshot, v1.EventTypeWarning, "ErrorSnapshotSourceFinalizer", "Error check and remove PVC Finalizer for VolumeSnapshot") + } + if !snapshot.Status.ReadyToUse { return ctrl.syncUnreadySnapshot(snapshot) } return ctrl.syncReadySnapshot(snapshot) - } // syncReadySnapshot checks the snapshot which has been bound to snapshot content successfully before. @@ -367,7 +375,6 @@ func (ctrl *csiSnapshotController) createSnapshot(snapshot *crdv1.VolumeSnapshot // We will get an "snapshot update" event soon, this is not a big error klog.V(4).Infof("createSnapshot [%s]: cannot update internal cache: %v", snapshotKey(snapshotObj), updateErr) } - return nil }) return nil @@ -415,9 +422,9 @@ func (ctrl *csiSnapshotController) updateSnapshotErrorStatusWithEvent(snapshot * Message: message, } snapshotClone.Status.Error = statusError - snapshotClone.Status.ReadyToUse = false - newSnapshot, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) + newSnapshot, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).UpdateStatus(snapshotClone) + if err != nil { klog.V(4).Infof("updating VolumeSnapshot[%s] error status failed %v", snapshotKey(snapshot), err) return err @@ -451,7 +458,7 @@ func IsSnapshotBound(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapsh // isSnapshotConentBeingUsed checks if snapshot content is bound to snapshot. func (ctrl *csiSnapshotController) isSnapshotContentBeingUsed(content *crdv1.VolumeSnapshotContent) bool { if content.Spec.VolumeSnapshotRef != nil { - snapshotObj, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(content.Spec.VolumeSnapshotRef.Namespace).Get(content.Spec.VolumeSnapshotRef.Name, metav1.GetOptions{}) + snapshotObj, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(content.Spec.VolumeSnapshotRef.Namespace).Get(content.Spec.VolumeSnapshotRef.Name, metav1.GetOptions{}) if err != nil { klog.Infof("isSnapshotContentBeingUsed: Cannot get snapshot %s from api server: [%v]. VolumeSnapshot object may be deleted already.", content.Spec.VolumeSnapshotRef.Name, err) return false @@ -503,7 +510,7 @@ func (ctrl *csiSnapshotController) checkandBindSnapshotContent(snapshot *crdv1.V contentClone.Spec.VolumeSnapshotRef.UID = snapshot.UID className := *(snapshot.Spec.VolumeSnapshotClassName) contentClone.Spec.VolumeSnapshotClassName = &className - newContent, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) + newContent, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) if err != nil { klog.V(4).Infof("updating VolumeSnapshotContent[%s] error status failed %v", newContent.Name, err) return nil, err @@ -556,7 +563,7 @@ func (ctrl *csiSnapshotController) getCreateSnapshotInput(snapshot *crdv1.Volume func (ctrl *csiSnapshotController) checkandUpdateBoundSnapshotStatusOperation(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshot, error) { var err error - var timestamp int64 + var creationTime time.Time var size int64 var readyToUse = false var driverName string @@ -564,7 +571,7 @@ func (ctrl *csiSnapshotController) checkandUpdateBoundSnapshotStatusOperation(sn if snapshot.Spec.Source == nil { klog.V(5).Infof("checkandUpdateBoundSnapshotStatusOperation: checking whether snapshot [%s] is pre-bound to content [%s]", snapshot.Name, content.Name) - readyToUse, timestamp, size, err = ctrl.handler.GetSnapshotStatus(content) + readyToUse, creationTime, size, err = ctrl.handler.GetSnapshotStatus(content) if err != nil { klog.Errorf("checkandUpdateBoundSnapshotStatusOperation: failed to call get snapshot status to check whether snapshot is ready to use %q", err) return nil, err @@ -577,18 +584,18 @@ func (ctrl *csiSnapshotController) checkandUpdateBoundSnapshotStatusOperation(sn if err != nil { return nil, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err) } - driverName, snapshotID, timestamp, size, readyToUse, err = ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials) + driverName, snapshotID, creationTime, size, readyToUse, err = ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials) if err != nil { klog.Errorf("checkandUpdateBoundSnapshotStatusOperation: failed to call create snapshot to check whether the snapshot is ready to use %q", err) return nil, err } } - klog.V(5).Infof("checkandUpdateBoundSnapshotStatusOperation: driver %s, snapshotId %s, timestamp %d, size %d, readyToUse %t", driverName, snapshotID, timestamp, size, readyToUse) + klog.V(5).Infof("checkandUpdateBoundSnapshotStatusOperation: driver %s, snapshotId %s, creationTime %v, size %d, readyToUse %t", driverName, snapshotID, creationTime, size, readyToUse) - if timestamp == 0 { - timestamp = time.Now().UnixNano() + if creationTime.IsZero() { + creationTime = time.Now() } - newSnapshot, err := ctrl.updateSnapshotStatus(snapshot, readyToUse, timestamp, size, IsSnapshotBound(snapshot, content)) + newSnapshot, err := ctrl.updateSnapshotStatus(snapshot, readyToUse, creationTime, size, IsSnapshotBound(snapshot, content)) if err != nil { return nil, err } @@ -612,22 +619,31 @@ func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.Volum return snapshot, nil } + // If PVC is not being deleted and finalizer is not added yet, a finalizer should be added. + klog.V(5).Infof("createSnapshotOperation: Check if PVC is not being deleted and add Finalizer for source of snapshot [%s] if needed", snapshot.Name) + err := ctrl.ensureSnapshotSourceFinalizer(snapshot) + if err != nil { + klog.Errorf("createSnapshotOperation failed to add finalizer for source of snapshot %s", err) + return nil, err + } + class, volume, contentName, snapshotterCredentials, err := ctrl.getCreateSnapshotInput(snapshot) if err != nil { return nil, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err) } - driverName, snapshotID, timestamp, size, readyToUse, err := ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials) + driverName, snapshotID, creationTime, size, readyToUse, err := ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials) if err != nil { return nil, fmt.Errorf("failed to take snapshot of the volume, %s: %q", volume.Name, err) } - klog.V(5).Infof("Created snapshot: driver %s, snapshotId %s, timestamp %d, size %d, readyToUse %t", driverName, snapshotID, timestamp, size, readyToUse) + + klog.V(5).Infof("Created snapshot: driver %s, snapshotId %s, creationTime %v, size %d, readyToUse %t", driverName, snapshotID, creationTime, size, readyToUse) var newSnapshot *crdv1.VolumeSnapshot - // Update snapshot status with timestamp + // Update snapshot status with creationTime for i := 0; i < ctrl.createSnapshotContentRetryCount; i++ { klog.V(5).Infof("createSnapshot [%s]: trying to update snapshot creation timestamp", snapshotKey(snapshot)) - newSnapshot, err = ctrl.updateSnapshotStatus(snapshot, readyToUse, timestamp, size, false) + newSnapshot, err = ctrl.updateSnapshotStatus(snapshot, readyToUse, creationTime, size, false) if err == nil { break } @@ -651,6 +667,7 @@ func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.Volum class.DeletionPolicy = new(crdv1.DeletionPolicy) *class.DeletionPolicy = crdv1.VolumeSnapshotContentDelete } + timestamp := creationTime.UnixNano() snapshotContent := &crdv1.VolumeSnapshotContent{ ObjectMeta: metav1.ObjectMeta{ Name: contentName, @@ -674,7 +691,7 @@ func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.Volum // Try to create the VolumeSnapshotContent object several times for i := 0; i < ctrl.createSnapshotContentRetryCount; i++ { klog.V(5).Infof("createSnapshot [%s]: trying to save volume snapshot content %s", snapshotKey(snapshot), snapshotContent.Name) - if _, err = ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshotContents().Create(snapshotContent); err == nil || apierrs.IsAlreadyExists(err) { + if _, err = ctrl.clientset.SnapshotV1alpha1().VolumeSnapshotContents().Create(snapshotContent); err == nil || apierrs.IsAlreadyExists(err) { // Save succeeded. if err != nil { klog.V(3).Infof("volume snapshot content %q for snapshot %q already exists, reusing", snapshotContent.Name, snapshotKey(snapshot)) @@ -741,7 +758,7 @@ func (ctrl *csiSnapshotController) deleteSnapshotContentOperation(content *crdv1 return fmt.Errorf("failed to delete snapshot %#v, err: %v", content.Name, err) } - err = ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshotContents().Delete(content.Name, &metav1.DeleteOptions{}) + err = ctrl.clientset.SnapshotV1alpha1().VolumeSnapshotContents().Delete(content.Name, &metav1.DeleteOptions{}) if err != nil { ctrl.eventRecorder.Event(content, v1.EventTypeWarning, "SnapshotContentObjectDeleteError", "Failed to delete snapshot content API object") return fmt.Errorf("failed to delete VolumeSnapshotContent %s from API server: %q", content.Name, err) @@ -752,7 +769,7 @@ func (ctrl *csiSnapshotController) deleteSnapshotContentOperation(content *crdv1 func (ctrl *csiSnapshotController) bindandUpdateVolumeSnapshot(snapshotContent *crdv1.VolumeSnapshotContent, snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) { klog.V(5).Infof("bindandUpdateVolumeSnapshot for snapshot [%s]: snapshotContent [%s]", snapshot.Name, snapshotContent.Name) - snapshotObj, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshot.Namespace).Get(snapshot.Name, metav1.GetOptions{}) + snapshotObj, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(snapshot.Namespace).Get(snapshot.Name, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error get snapshot %s from api server: %v", snapshotKey(snapshot), err) } @@ -765,7 +782,7 @@ func (ctrl *csiSnapshotController) bindandUpdateVolumeSnapshot(snapshotContent * } else { klog.Infof("bindVolumeSnapshotContentToVolumeSnapshot: before bind VolumeSnapshot %s to volumeSnapshotContent [%s]", snapshot.Name, snapshotContent.Name) snapshotCopy.Spec.SnapshotContentName = snapshotContent.Name - updateSnapshot, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshot.Namespace).Update(snapshotCopy) + updateSnapshot, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(snapshot.Namespace).Update(snapshotCopy) if err != nil { klog.Infof("bindVolumeSnapshotContentToVolumeSnapshot: Error binding VolumeSnapshot %s to volumeSnapshotContent [%s]. Error [%#v]", snapshot.Name, snapshotContent.Name, err) return nil, newControllerUpdateError(snapshotKey(snapshot), err.Error()) @@ -791,7 +808,7 @@ func (ctrl *csiSnapshotController) updateSnapshotContentSize(content *crdv1.Volu } contentClone := content.DeepCopy() contentClone.Spec.VolumeSnapshotSource.CSI.RestoreSize = &size - _, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) + _, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) if err != nil { return newControllerUpdateError(content.Name, err.Error()) } @@ -804,12 +821,12 @@ func (ctrl *csiSnapshotController) updateSnapshotContentSize(content *crdv1.Volu } // UpdateSnapshotStatus converts snapshot status to crdv1.VolumeSnapshotCondition -func (ctrl *csiSnapshotController) updateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, readyToUse bool, createdAt, size int64, bound bool) (*crdv1.VolumeSnapshot, error) { +func (ctrl *csiSnapshotController) updateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, readyToUse bool, createdAt time.Time, size int64, bound bool) (*crdv1.VolumeSnapshot, error) { klog.V(5).Infof("updating VolumeSnapshot[]%s, readyToUse %v, timestamp %v", snapshotKey(snapshot), readyToUse, createdAt) status := snapshot.Status change := false timeAt := &metav1.Time{ - Time: time.Unix(0, createdAt), + Time: createdAt, } snapshotClone := snapshot.DeepCopy() @@ -831,7 +848,7 @@ func (ctrl *csiSnapshotController) updateSnapshotStatus(snapshot *crdv1.VolumeSn status.RestoreSize = resource.NewQuantity(size, resource.BinarySI) } snapshotClone.Status = status - newSnapshotObj, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) + newSnapshotObj, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).UpdateStatus(snapshotClone) if err != nil { return nil, newControllerUpdateError(snapshotKey(snapshot), err.Error()) } @@ -858,11 +875,40 @@ func (ctrl *csiSnapshotController) getVolumeFromVolumeSnapshot(snapshot *crdv1.V return nil, fmt.Errorf("failed to retrieve PV %s from the API server: %q", pvName, err) } + // Verify binding between PV/PVC is still valid + bound := ctrl.IsVolumeBoundToClaim(pv, pvc) + if bound == false { + klog.Warningf("binding between PV %s and PVC %s is broken", pvName, pvc.Name) + return nil, fmt.Errorf("claim in dataSource not bound or invalid") + } + + // Verify driver for PVC is the same as driver for VolumeSnapshot + if pv.Spec.PersistentVolumeSource.CSI == nil || pv.Spec.PersistentVolumeSource.CSI.Driver != ctrl.snapshotterName { + klog.Warningf("driver for PV %s is different from driver %s for snapshot %s", pvName, ctrl.snapshotterName, snapshot.Name) + return nil, fmt.Errorf("claim in dataSource not bound or invalid") + } + klog.V(5).Infof("getVolumeFromVolumeSnapshot: snapshot [%s] PV name [%s]", snapshot.Name, pvName) return pv, nil } +// IsVolumeBoundToClaim returns true, if given volume is pre-bound or bound +// to specific claim. Both claim.Name and claim.Namespace must be equal. +// If claim.UID is present in volume.Spec.ClaimRef, it must be equal too. +func (ctrl *csiSnapshotController) IsVolumeBoundToClaim(volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) bool { + if volume.Spec.ClaimRef == nil { + return false + } + if claim.Name != volume.Spec.ClaimRef.Name || claim.Namespace != volume.Spec.ClaimRef.Namespace { + return false + } + if volume.Spec.ClaimRef.UID != "" && claim.UID != volume.Spec.ClaimRef.UID { + return false + } + return true +} + func (ctrl *csiSnapshotController) getStorageClassFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*storagev1.StorageClass, error) { // Get storage class from PVC or PV pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot) @@ -932,7 +978,7 @@ func (ctrl *csiSnapshotController) SetDefaultSnapshotClass(snapshot *crdv1.Volum klog.V(5).Infof("setDefaultSnapshotClass [%s]: default VolumeSnapshotClassName [%s]", snapshot.Name, defaultClasses[0].Name) snapshotClone := snapshot.DeepCopy() snapshotClone.Spec.VolumeSnapshotClassName = &(defaultClasses[0].Name) - newSnapshot, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) + newSnapshot, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) if err != nil { klog.V(4).Infof("updating VolumeSnapshot[%s] default class failed %v", snapshotKey(snapshot), err) } @@ -999,7 +1045,7 @@ func (ctrl *csiSnapshotController) addContentFinalizer(content *crdv1.VolumeSnap contentClone := content.DeepCopy() contentClone.ObjectMeta.Finalizers = append(contentClone.ObjectMeta.Finalizers, VolumeSnapshotContentFinalizer) - _, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) + _, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) if err != nil { return newControllerUpdateError(content.Name, err.Error()) } @@ -1018,7 +1064,7 @@ func (ctrl *csiSnapshotController) removeContentFinalizer(content *crdv1.VolumeS contentClone := content.DeepCopy() contentClone.ObjectMeta.Finalizers = slice.RemoveString(contentClone.ObjectMeta.Finalizers, VolumeSnapshotContentFinalizer, nil) - _, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) + _, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshotContents().Update(contentClone) if err != nil { return newControllerUpdateError(content.Name, err.Error()) } @@ -1036,7 +1082,7 @@ func (ctrl *csiSnapshotController) removeContentFinalizer(content *crdv1.VolumeS func (ctrl *csiSnapshotController) addSnapshotFinalizer(snapshot *crdv1.VolumeSnapshot) error { snapshotClone := snapshot.DeepCopy() snapshotClone.ObjectMeta.Finalizers = append(snapshotClone.ObjectMeta.Finalizers, VolumeSnapshotFinalizer) - _, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) + _, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) if err != nil { return newControllerUpdateError(snapshot.Name, err.Error()) } @@ -1055,7 +1101,7 @@ func (ctrl *csiSnapshotController) removeSnapshotFinalizer(snapshot *crdv1.Volum snapshotClone := snapshot.DeepCopy() snapshotClone.ObjectMeta.Finalizers = slice.RemoveString(snapshotClone.ObjectMeta.Finalizers, VolumeSnapshotFinalizer, nil) - _, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) + _, err := ctrl.clientset.SnapshotV1alpha1().VolumeSnapshots(snapshotClone.Namespace).Update(snapshotClone) if err != nil { return newControllerUpdateError(snapshot.Name, err.Error()) } @@ -1068,3 +1114,113 @@ func (ctrl *csiSnapshotController) removeSnapshotFinalizer(snapshot *crdv1.Volum klog.V(5).Infof("Removed protection finalizer from volume snapshot %s", snapshotKey(snapshot)) return nil } + +// ensureSnapshotSourceFinalizer checks if a Finalizer needs to be added for the snapshot source; +// if true, adds a Finalizer for VolumeSnapshot Source PVC +func (ctrl *csiSnapshotController) ensureSnapshotSourceFinalizer(snapshot *crdv1.VolumeSnapshot) error { + // Get snapshot source which is a PVC + pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot) + if err != nil { + klog.Infof("cannot get claim from snapshot [%s]: [%v] Claim may be deleted already.", snapshot.Name, err) + return nil + } + + // If PVC is not being deleted and PVCFinalizer is not added yet, the PVCFinalizer should be added. + if pvc.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(pvc.ObjectMeta.Finalizers, PVCFinalizer, nil) { + // Add the finalizer + pvcClone := pvc.DeepCopy() + pvcClone.ObjectMeta.Finalizers = append(pvcClone.ObjectMeta.Finalizers, PVCFinalizer) + _, err = ctrl.client.CoreV1().PersistentVolumeClaims(pvcClone.Namespace).Update(pvcClone) + if err != nil { + klog.Errorf("cannot add finalizer on claim [%s] for snapshot [%s]: [%v]", pvc.Name, snapshot.Name, err) + return newControllerUpdateError(pvcClone.Name, err.Error()) + } + klog.Infof("Added protection finalizer to persistent volume claim %s", pvc.Name) + } + + return nil +} + +// removeSnapshotSourceFinalizer removes a Finalizer for VolumeSnapshot Source PVC. +func (ctrl *csiSnapshotController) removeSnapshotSourceFinalizer(snapshot *crdv1.VolumeSnapshot) error { + // Get snapshot source which is a PVC + pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot) + if err != nil { + klog.Infof("cannot get claim from snapshot [%s]: [%v] Claim may be deleted already. No need to remove finalizer on the claim.", snapshot.Name, err) + return nil + } + + pvcClone := pvc.DeepCopy() + pvcClone.ObjectMeta.Finalizers = slice.RemoveString(pvcClone.ObjectMeta.Finalizers, PVCFinalizer, nil) + + _, err = ctrl.client.CoreV1().PersistentVolumeClaims(pvcClone.Namespace).Update(pvcClone) + if err != nil { + return newControllerUpdateError(pvcClone.Name, err.Error()) + } + + klog.V(5).Infof("Removed protection finalizer from persistent volume claim %s", pvc.Name) + return nil +} + +// isSnapshotSourceBeingUsed checks if a PVC is being used as a source to create a snapshot +func (ctrl *csiSnapshotController) isSnapshotSourceBeingUsed(snapshot *crdv1.VolumeSnapshot) bool { + klog.V(5).Infof("isSnapshotSourceBeingUsed[%s]: started", snapshotKey(snapshot)) + // Get snapshot source which is a PVC + pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot) + if err != nil { + klog.Infof("isSnapshotSourceBeingUsed: cannot to get claim from snapshot: %v", err) + return false + } + + // Going through snapshots in the cache (snapshotLister). If a snapshot's PVC source + // is the same as the input snapshot's PVC source and snapshot's ReadyToUse status + // is false, the snapshot is still being created from the PVC and the PVC is in-use. + snapshots, err := ctrl.snapshotLister.VolumeSnapshots(snapshot.Namespace).List(labels.Everything()) + if err != nil { + return false + } + for _, snap := range snapshots { + // Skip static bound snapshot without a PVC source + if snap.Spec.Source == nil { + klog.V(4).Infof("Skipping static bound snapshot %s when checking PVC %s/%s", snap.Name, pvc.Namespace, pvc.Name) + continue + } + if pvc.Name == snap.Spec.Source.Name && snap.Status.ReadyToUse == false { + klog.V(2).Infof("Keeping PVC %s/%s, it is used by snapshot %s/%s", pvc.Namespace, pvc.Name, snap.Namespace, snap.Name) + return true + } + } + + klog.V(5).Infof("isSnapshotSourceBeingUsed: no snapshot is being created from PVC %s/%s", pvc.Namespace, pvc.Name) + return false +} + +// checkandRemoveSnapshotSourceFinalizer checks if the snapshot source finalizer should be removed +// and removed it if needed. +func (ctrl *csiSnapshotController) checkandRemoveSnapshotSourceFinalizer(snapshot *crdv1.VolumeSnapshot) error { + // Get snapshot source which is a PVC + pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot) + if err != nil { + klog.Infof("cannot get claim from snapshot [%s]: [%v] Claim may be deleted already. No need to remove finalizer on the claim.", snapshot.Name, err) + return nil + } + + klog.V(5).Infof("checkandRemoveSnapshotSourceFinalizer for snapshot [%s]: snapshot status [%#v]", snapshot.Name, snapshot.Status) + + // Check if there is a Finalizer on PVC to be removed + if slice.ContainsString(pvc.ObjectMeta.Finalizers, PVCFinalizer, nil) { + // There is a Finalizer on PVC. Check if PVC is used + // and remove finalizer if it's not used. + isUsed := ctrl.isSnapshotSourceBeingUsed(snapshot) + if !isUsed { + klog.Infof("checkandRemoveSnapshotSourceFinalizer[%s]: Remove Finalizer for PVC %s as it is not used by snapshots in creation", snapshot.Name, pvc.Name) + err = ctrl.removeSnapshotSourceFinalizer(snapshot) + if err != nil { + klog.Errorf("checkandRemoveSnapshotSourceFinalizer [%s]: removeSnapshotSourceFinalizer failed to remove finalizer %v", snapshot.Name, err) + return err + } + } + } + + return nil +} diff --git a/pkg/controller/snapshot_create_test.go b/pkg/controller/snapshot_create_test.go index bfb925a6d..4615191fb 100644 --- a/pkg/controller/snapshot_create_test.go +++ b/pkg/controller/snapshot_create_test.go @@ -27,10 +27,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var timeNow = time.Now().UnixNano() +var timeNow = time.Now() +var timeNowStamp = timeNow.UnixNano() var metaTimeNowUnix = &metav1.Time{ - Time: time.Unix(0, timeNow), + Time: timeNow, } var defaultSize int64 = 1000 @@ -68,22 +69,22 @@ func TestCreateSnapshotSync(t *testing.T) { { name: "6-1 - successful create snapshot with snapshot class gold", initialContents: nocontents, - expectedContents: newContentArray("snapcontent-snapuid6-1", classGold, "sid6-1", "pv-uid6-1", "volume6-1", "snapuid6-1", "snap6-1", &deletePolicy, &defaultSize, &timeNow, false), + expectedContents: newContentArray("snapcontent-snapuid6-1", classGold, "sid6-1", "pv-uid6-1", "volume6-1", "snapuid6-1", "snap6-1", &deletePolicy, &defaultSize, &timeNowStamp, false), initialSnapshots: newSnapshotArray("snap6-1", classGold, "", "snapuid6-1", "claim6-1", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap6-1", classGold, "snapcontent-snapuid6-1", "snapuid6-1", "claim6-1", false, nil, metaTimeNowUnix, getSize(defaultSize)), initialClaims: newClaimArray("claim6-1", "pvc-uid6-1", "1Gi", "volume6-1", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume6-1", "pv-uid6-1", "pv-handle6-1", "1Gi", "pvc-uid6-1", "claim6-1", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume6-1", "pv-uid6-1", "pv-handle6-1", "1Gi", "pvc-uid6-1", "claim6-1", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid6-1", - volume: newVolume("volume6-1", "pv-uid6-1", "pv-handle6-1", "1Gi", "pvc-uid6-1", "claim6-1", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume6-1", "pv-uid6-1", "pv-handle6-1", "1Gi", "pvc-uid6-1", "claim6-1", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: map[string]string{"param1": "value1"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid6-1", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid6-1", + creationTime: timeNow, + readyToUse: true, }, }, errors: noerrors, @@ -92,22 +93,22 @@ func TestCreateSnapshotSync(t *testing.T) { { name: "6-2 - successful create snapshot with snapshot class silver", initialContents: nocontents, - expectedContents: newContentArray("snapcontent-snapuid6-2", classSilver, "sid6-2", "pv-uid6-2", "volume6-2", "snapuid6-2", "snap6-2", &deletePolicy, &defaultSize, &timeNow, false), + expectedContents: newContentArray("snapcontent-snapuid6-2", classSilver, "sid6-2", "pv-uid6-2", "volume6-2", "snapuid6-2", "snap6-2", &deletePolicy, &defaultSize, &timeNowStamp, false), initialSnapshots: newSnapshotArray("snap6-2", classSilver, "", "snapuid6-2", "claim6-2", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap6-2", classSilver, "snapcontent-snapuid6-2", "snapuid6-2", "claim6-2", false, nil, metaTimeNowUnix, getSize(defaultSize)), initialClaims: newClaimArray("claim6-2", "pvc-uid6-2", "1Gi", "volume6-2", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume6-2", "pv-uid6-2", "pv-handle6-2", "1Gi", "pvc-uid6-2", "claim6-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume6-2", "pv-uid6-2", "pv-handle6-2", "1Gi", "pvc-uid6-2", "claim6-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid6-2", - volume: newVolume("volume6-2", "pv-uid6-2", "pv-handle6-2", "1Gi", "pvc-uid6-2", "claim6-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume6-2", "pv-uid6-2", "pv-handle6-2", "1Gi", "pvc-uid6-2", "claim6-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: map[string]string{"param2": "value2"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid6-2", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid6-2", + creationTime: timeNow, + readyToUse: true, }, }, errors: noerrors, @@ -116,24 +117,24 @@ func TestCreateSnapshotSync(t *testing.T) { { name: "6-3 - successful create snapshot with snapshot class valid-secret-class", initialContents: nocontents, - expectedContents: newContentArray("snapcontent-snapuid6-3", validSecretClass, "sid6-3", "pv-uid6-3", "volume6-3", "snapuid6-3", "snap6-3", &deletePolicy, &defaultSize, &timeNow, false), + expectedContents: newContentArray("snapcontent-snapuid6-3", validSecretClass, "sid6-3", "pv-uid6-3", "volume6-3", "snapuid6-3", "snap6-3", &deletePolicy, &defaultSize, &timeNowStamp, false), initialSnapshots: newSnapshotArray("snap6-3", validSecretClass, "", "snapuid6-3", "claim6-3", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap6-3", validSecretClass, "snapcontent-snapuid6-3", "snapuid6-3", "claim6-3", false, nil, metaTimeNowUnix, getSize(defaultSize)), initialClaims: newClaimArray("claim6-3", "pvc-uid6-3", "1Gi", "volume6-3", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume6-3", "pv-uid6-3", "pv-handle6-3", "1Gi", "pvc-uid6-3", "claim6-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume6-3", "pv-uid6-3", "pv-handle6-3", "1Gi", "pvc-uid6-3", "claim6-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), initialSecrets: []*v1.Secret{secret()}, expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid6-3", - volume: newVolume("volume6-3", "pv-uid6-3", "pv-handle6-3", "1Gi", "pvc-uid6-3", "claim6-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume6-3", "pv-uid6-3", "pv-handle6-3", "1Gi", "pvc-uid6-3", "claim6-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: class5Parameters, secrets: map[string]string{"foo": "bar"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid6-3", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid6-3", + creationTime: timeNow, + readyToUse: true, }, }, errors: noerrors, @@ -142,24 +143,24 @@ func TestCreateSnapshotSync(t *testing.T) { { name: "6-4 - successful create snapshot with snapshot class empty-secret-class", initialContents: nocontents, - expectedContents: newContentArray("snapcontent-snapuid6-4", emptySecretClass, "sid6-4", "pv-uid6-4", "volume6-4", "snapuid6-4", "snap6-4", &deletePolicy, &defaultSize, &timeNow, false), + expectedContents: newContentArray("snapcontent-snapuid6-4", emptySecretClass, "sid6-4", "pv-uid6-4", "volume6-4", "snapuid6-4", "snap6-4", &deletePolicy, &defaultSize, &timeNowStamp, false), initialSnapshots: newSnapshotArray("snap6-4", emptySecretClass, "", "snapuid6-4", "claim6-4", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap6-4", emptySecretClass, "snapcontent-snapuid6-4", "snapuid6-4", "claim6-4", false, nil, metaTimeNowUnix, getSize(defaultSize)), initialClaims: newClaimArray("claim6-4", "pvc-uid6-4", "1Gi", "volume6-4", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume6-4", "pv-uid6-4", "pv-handle6-4", "1Gi", "pvc-uid6-4", "claim6-4", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume6-4", "pv-uid6-4", "pv-handle6-4", "1Gi", "pvc-uid6-4", "claim6-4", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), initialSecrets: []*v1.Secret{emptySecret()}, expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid6-4", - volume: newVolume("volume6-4", "pv-uid6-4", "pv-handle6-4", "1Gi", "pvc-uid6-4", "claim6-4", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume6-4", "pv-uid6-4", "pv-handle6-4", "1Gi", "pvc-uid6-4", "claim6-4", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: class4Parameters, secrets: map[string]string{}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid6-4", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid6-4", + creationTime: timeNow, + readyToUse: true, }, }, errors: noerrors, @@ -168,22 +169,22 @@ func TestCreateSnapshotSync(t *testing.T) { { name: "6-5 - successful create snapshot with status uploading", initialContents: nocontents, - expectedContents: newContentArray("snapcontent-snapuid6-5", classGold, "sid6-5", "pv-uid6-5", "volume6-5", "snapuid6-5", "snap6-5", &deletePolicy, &defaultSize, &timeNow, false), + expectedContents: newContentArray("snapcontent-snapuid6-5", classGold, "sid6-5", "pv-uid6-5", "volume6-5", "snapuid6-5", "snap6-5", &deletePolicy, &defaultSize, &timeNowStamp, false), initialSnapshots: newSnapshotArray("snap6-5", classGold, "", "snapuid6-5", "claim6-5", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap6-5", classGold, "snapcontent-snapuid6-5", "snapuid6-5", "claim6-5", false, nil, metaTimeNowUnix, getSize(defaultSize)), initialClaims: newClaimArray("claim6-5", "pvc-uid6-5", "1Gi", "volume6-5", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume6-5", "pv-uid6-5", "pv-handle6-5", "1Gi", "pvc-uid6-5", "claim6-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume6-5", "pv-uid6-5", "pv-handle6-5", "1Gi", "pvc-uid6-5", "claim6-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid6-5", - volume: newVolume("volume6-5", "pv-uid6-5", "pv-handle6-5", "1Gi", "pvc-uid6-5", "claim6-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume6-5", "pv-uid6-5", "pv-handle6-5", "1Gi", "pvc-uid6-5", "claim6-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: map[string]string{"param1": "value1"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid6-5", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid6-5", + creationTime: timeNow, + readyToUse: true, }, }, errors: noerrors, @@ -192,22 +193,22 @@ func TestCreateSnapshotSync(t *testing.T) { { name: "6-6 - successful create snapshot with status error uploading", initialContents: nocontents, - expectedContents: newContentArray("snapcontent-snapuid6-6", classGold, "sid6-6", "pv-uid6-6", "volume6-6", "snapuid6-6", "snap6-6", &deletePolicy, &defaultSize, &timeNow, false), + expectedContents: newContentArray("snapcontent-snapuid6-6", classGold, "sid6-6", "pv-uid6-6", "volume6-6", "snapuid6-6", "snap6-6", &deletePolicy, &defaultSize, &timeNowStamp, false), initialSnapshots: newSnapshotArray("snap6-6", classGold, "", "snapuid6-6", "claim6-6", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap6-6", classGold, "snapcontent-snapuid6-6", "snapuid6-6", "claim6-6", false, nil, metaTimeNowUnix, getSize(defaultSize)), initialClaims: newClaimArray("claim6-6", "pvc-uid6-6", "1Gi", "volume6-6", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume6-6", "pv-uid6-6", "pv-handle6-6", "1Gi", "pvc-uid6-6", "claim6-6", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume6-6", "pv-uid6-6", "pv-handle6-6", "1Gi", "pvc-uid6-6", "claim6-6", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid6-6", - volume: newVolume("volume6-6", "pv-uid6-6", "pv-handle6-6", "1Gi", "pvc-uid6-6", "claim6-6", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume6-6", "pv-uid6-6", "pv-handle6-6", "1Gi", "pvc-uid6-6", "claim6-6", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: map[string]string{"param1": "value1"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid6-6", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid6-6", + creationTime: timeNow, + readyToUse: true, }, }, errors: noerrors, @@ -220,7 +221,7 @@ func TestCreateSnapshotSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap7-1", classNonExisting, "", "snapuid7-1", "claim7-1", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap7-1", classNonExisting, "", "snapuid7-1", "claim7-1", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap7-1: \"failed to retrieve snapshot class non-existing from the informer: \\\"volumesnapshotclass.snapshot.storage.k8s.io \\\\\\\"non-existing\\\\\\\" not found\\\"\""), nil, nil), initialClaims: newClaimArray("claim7-1", "pvc-uid7-1", "1Gi", "volume7-1", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume7-1", "pv-uid7-1", "pv-handle7-1", "1Gi", "pvc-uid7-1", "claim7-1", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume7-1", "pv-uid7-1", "pv-handle7-1", "1Gi", "pvc-uid7-1", "claim7-1", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedEvents: []string{"Warning SnapshotCreationFailed"}, errors: noerrors, test: testSyncSnapshot, @@ -232,7 +233,7 @@ func TestCreateSnapshotSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap7-2", invalidSecretClass, "", "snapuid7-2", "claim7-2", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap7-2", invalidSecretClass, "", "snapuid7-2", "claim7-2", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap7-2: \"failed to get name and namespace template from params: either name and namespace for Snapshotter secrets specified, Both must be specified\""), nil, nil), initialClaims: newClaimArray("claim7-2", "pvc-uid7-2", "1Gi", "volume7-2", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume7-2", "pv-uid7-2", "pv-handle7-2", "1Gi", "pvc-uid7-2", "claim7-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume7-2", "pv-uid7-2", "pv-handle7-2", "1Gi", "pvc-uid7-2", "claim7-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedEvents: []string{"Warning SnapshotCreationFailed"}, errors: noerrors, test: testSyncSnapshot, @@ -244,7 +245,7 @@ func TestCreateSnapshotSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap7-3", "", "", "snapuid7-3", "claim7-3", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap7-3", "", "", "snapuid7-3", "claim7-3", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap7-3: \"failed to retrieve snapshot class from the informer: \\\"volumesnapshotclass.snapshot.storage.k8s.io \\\\\\\"\\\\\\\" not found\\\"\""), nil, nil), initialClaims: newClaimArray("claim7-3", "pvc-uid7-3", "1Gi", "volume7-3", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume7-3", "pv-uid7-3", "pv-handle7-3", "1Gi", "pvc-uid7-3", "claim7-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume7-3", "pv-uid7-3", "pv-handle7-3", "1Gi", "pvc-uid7-3", "claim7-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), initialStorageClasses: []*storage.StorageClass{diffDriverStorageClass}, expectedEvents: []string{"Warning SnapshotCreationFailed"}, errors: noerrors, @@ -256,7 +257,7 @@ func TestCreateSnapshotSync(t *testing.T) { expectedContents: nocontents, initialSnapshots: newSnapshotArray("snap7-4", classGold, "", "snapuid7-4", "claim7-4", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap7-4", classGold, "", "snapuid7-4", "claim7-4", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap7-4: \"failed to retrieve PVC claim7-4 from the lister: \\\"persistentvolumeclaim \\\\\\\"claim7-4\\\\\\\" not found\\\"\""), nil, nil), - initialVolumes: newVolumeArray("volume7-4", "pv-uid7-4", "pv-handle7-4", "1Gi", "pvc-uid7-4", "claim7-4", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume7-4", "pv-uid7-4", "pv-handle7-4", "1Gi", "pvc-uid7-4", "claim7-4", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedEvents: []string{"Warning SnapshotCreationFailed"}, errors: noerrors, test: testSyncSnapshot, @@ -290,11 +291,11 @@ func TestCreateSnapshotSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap7-7", classGold, "", "snapuid7-7", "claim7-7", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap7-7", classGold, "", "snapuid7-7", "claim7-7", false, newVolumeError("Failed to create snapshot: failed to take snapshot of the volume, volume7-7: \"mock create snapshot error\""), nil, nil), initialClaims: newClaimArray("claim7-7", "pvc-uid7-7", "1Gi", "volume7-7", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume7-7", "pv-uid7-7", "pv-handle7-7", "1Gi", "pvc-uid7-7", "claim7-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume7-7", "pv-uid7-7", "pv-handle7-7", "1Gi", "pvc-uid7-7", "claim7-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid7-7", - volume: newVolume("volume7-7", "pv-uid7-7", "pv-handle7-7", "1Gi", "pvc-uid7-7", "claim7-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume7-7", "pv-uid7-7", "pv-handle7-7", "1Gi", "pvc-uid7-7", "claim7-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: map[string]string{"param1": "value1"}, // information to return err: errors.New("mock create snapshot error"), @@ -311,18 +312,18 @@ func TestCreateSnapshotSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap7-8", classGold, "", "snapuid7-8", "claim7-8", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap7-8", classGold, "", "snapuid7-8", "claim7-8", false, newVolumeError("Failed to create snapshot: snapshot controller failed to update default/snap7-8 on API server: mock update error"), nil, nil), initialClaims: newClaimArray("claim7-8", "pvc-uid7-8", "1Gi", "volume7-8", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume7-8", "pv-uid7-8", "pv-handle7-8", "1Gi", "pvc-uid7-8", "claim7-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume7-8", "pv-uid7-8", "pv-handle7-8", "1Gi", "pvc-uid7-8", "claim7-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid7-8", - volume: newVolume("volume7-8", "pv-uid7-8", "pv-handle7-8", "1Gi", "pvc-uid7-8", "claim7-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume7-8", "pv-uid7-8", "pv-handle7-8", "1Gi", "pvc-uid7-8", "claim7-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: map[string]string{"param1": "value1"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid7-8", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid7-8", + creationTime: timeNow, + readyToUse: true, }, }, errors: []reactorError{ @@ -342,18 +343,18 @@ func TestCreateSnapshotSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap7-9", classGold, "", "snapuid7-9", "claim7-9", false, nil, nil, nil), expectedSnapshots: newSnapshotArray("snap7-9", classGold, "", "snapuid7-9", "claim7-9", false, nil, metaTimeNowUnix, getSize(defaultSize)), initialClaims: newClaimArray("claim7-9", "pvc-uid7-9", "1Gi", "volume7-9", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume7-9", "pv-uid7-9", "pv-handle7-9", "1Gi", "pvc-uid7-9", "claim7-9", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume7-9", "pv-uid7-9", "pv-handle7-9", "1Gi", "pvc-uid7-9", "claim7-9", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid7-9", - volume: newVolume("volume7-9", "pv-uid7-9", "pv-handle7-9", "1Gi", "pvc-uid7-9", "claim7-9", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume7-9", "pv-uid7-9", "pv-handle7-9", "1Gi", "pvc-uid7-9", "claim7-9", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: map[string]string{"param1": "value1"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid7-9", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid7-9", + creationTime: timeNow, + readyToUse: true, }, }, errors: []reactorError{ @@ -364,6 +365,78 @@ func TestCreateSnapshotSync(t *testing.T) { expectedEvents: []string{"Warning CreateSnapshotContentFailed"}, test: testSyncSnapshot, }, + { + name: "7-10 - fail create snapshot with secret not found", + initialContents: nocontents, + expectedContents: nocontents, + initialSnapshots: newSnapshotArray("snap7-10", validSecretClass, "", "snapuid7-10", "claim7-10", false, nil, nil, nil), + expectedSnapshots: newSnapshotArray("snap7-10", validSecretClass, "", "snapuid7-10", "claim7-10", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap7-10: \"error getting secret secret in namespace default: cannot find secret secret\""), nil, nil), + initialClaims: newClaimArray("claim7-10", "pvc-uid7-10", "1Gi", "volume7-10", v1.ClaimBound, &classEmpty), + initialVolumes: newVolumeArray("volume7-10", "pv-uid7-10", "pv-handle7-10", "1Gi", "pvc-uid7-10", "claim7-10", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), + initialSecrets: []*v1.Secret{}, // no initial secret created + errors: noerrors, + test: testSyncSnapshot, + }, + { + name: "8-1 - fail create snapshot with PVC using unbound PV", + initialContents: nocontents, + expectedContents: nocontents, + initialSnapshots: newSnapshotArray("snap8-1", classGold, "", "snapuid8-1", "claim8-1", false, nil, nil, nil), + expectedSnapshots: newSnapshotArray("snap8-1", classGold, "", "snapuid8-1", "claim8-1", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap8-1: \"claim in dataSource not bound or invalid\""), nil, nil), + initialClaims: newClaimArray("claim8-1", "pvc-uid8-1", "1Gi", "volume8-1", v1.ClaimBound, &classEmpty), + initialVolumes: newVolumeArray("volume8-1", "pv-uid8-1", "pv-handle8-1", "1Gi", "pvc-uid8-1", "", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), + errors: noerrors, + expectedEvents: []string{"Warning SnapshotCreationFailed"}, + test: testSyncSnapshot, + }, + { + name: "8-2 - fail create snapshot with PVC using PV bound to another PVC (with wrong UID)", + initialContents: nocontents, + expectedContents: nocontents, + initialSnapshots: newSnapshotArray("snap8-2", classGold, "", "snapuid8-2", "claim8-2", false, nil, nil, nil), + expectedSnapshots: newSnapshotArray("snap8-2", classGold, "", "snapuid8-2", "claim8-2", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap8-2: \"claim in dataSource not bound or invalid\""), nil, nil), + initialClaims: newClaimArray("claim8-2", "pvc-uid8-2", "1Gi", "volume8-2", v1.ClaimBound, &classEmpty), + initialVolumes: newVolumeArray("volume8-2", "pv-uid8-2", "pv-handle8-2", "1Gi", "pvc-uid8-2-wrong-UID", "claim8-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), + errors: noerrors, + expectedEvents: []string{"Warning SnapshotCreationFailed"}, + test: testSyncSnapshot, + }, + { + name: "8-3 - fail create snapshot with PVC using PV bound to another PVC (with wrong namespace)", + initialContents: nocontents, + expectedContents: nocontents, + initialSnapshots: newSnapshotArray("snap8-3", classGold, "", "snapuid8-3", "claim8-3", false, nil, nil, nil), + expectedSnapshots: newSnapshotArray("snap8-3", classGold, "", "snapuid8-3", "claim8-3", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap8-3: \"claim in dataSource not bound or invalid\""), nil, nil), + initialClaims: newClaimArray("claim8-3", "pvc-uid8-3", "1Gi", "volume8-3", v1.ClaimBound, &classEmpty), + initialVolumes: newVolumeArray("volume8-3", "pv-uid8-3", "pv-handle8-3", "1Gi", "pvc-uid8-3", "claim8-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, "wrong-namespace"), + errors: noerrors, + expectedEvents: []string{"Warning SnapshotCreationFailed"}, + test: testSyncSnapshot, + }, + { + name: "8-4 - fail create snapshot with PVC using PV bound to another PVC (with wrong name)", + initialContents: nocontents, + expectedContents: nocontents, + initialSnapshots: newSnapshotArray("snap8-4", classGold, "", "snapuid8-4", "claim8-4", false, nil, nil, nil), + expectedSnapshots: newSnapshotArray("snap8-4", classGold, "", "snapuid8-4", "claim8-4", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap8-4: \"claim in dataSource not bound or invalid\""), nil, nil), + initialClaims: newClaimArray("claim8-4", "pvc-uid8-4", "1Gi", "volume8-4", v1.ClaimBound, &classEmpty), + initialVolumes: newVolumeArray("volume8-4", "pv-uid8-4", "pv-handle8-4", "1Gi", "pvc-uid8-4", "claim8-4-wrong-name", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), + errors: noerrors, + expectedEvents: []string{"Warning SnapshotCreationFailed"}, + test: testSyncSnapshot, + }, + { + name: "8-5 - fail create snapshot with PVC bound to PV with wrong provisioner", + initialContents: nocontents, + expectedContents: nocontents, + initialSnapshots: newSnapshotArray("snap8-5", classGold, "", "snapuid8-5", "claim8-5", false, nil, nil, nil), + expectedSnapshots: newSnapshotArray("snap8-5", classGold, "", "snapuid8-5", "claim8-5", false, newVolumeError("Failed to create snapshot: failed to get input parameters to create snapshot snap8-5: \"claim in dataSource not bound or invalid\""), nil, nil), + initialClaims: newClaimArray("claim8-5", "pvc-uid8-5", "1Gi", "volume8-5", v1.ClaimBound, &classEmpty), + initialVolumes: newVolumeArray("volume8-5", "pv-uid8-5", "pv-handle8-5", "1Gi", "pvc-uid8-5", "claim8-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, "wrong-driver", testNamespace), + errors: noerrors, + expectedEvents: []string{"Warning SnapshotCreationFailed"}, + test: testSyncSnapshot, + }, } runSyncTests(t, tests, snapshotClasses) } diff --git a/pkg/controller/snapshot_finalizer_test.go b/pkg/controller/snapshot_finalizer_test.go new file mode 100644 index 000000000..bfce33a9b --- /dev/null +++ b/pkg/controller/snapshot_finalizer_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "testing" + + "k8s.io/api/core/v1" +) + +// Test single call to ensureSnapshotSourceFinalizer and checkandRemoveSnapshotSourceFinalizer, +// expecting PVCFinalizer to be added or removed +func TestPVCFinalizer(t *testing.T) { + + tests := []controllerTest{ + { + name: "1-1 - successful add PVC finalizer", + initialSnapshots: newSnapshotArray("snap6-2", classSilver, "", "snapuid6-2", "claim6-2", false, nil, nil, nil), + initialClaims: newClaimArray("claim6-2", "pvc-uid6-2", "1Gi", "volume6-2", v1.ClaimBound, &classEmpty), + test: testAddPVCFinalizer, + expectSuccess: true, + }, + { + name: "1-2 - won't add PVC finalizer; already added", + initialSnapshots: newSnapshotArray("snap6-2", classSilver, "", "snapuid6-2", "claim6-2", false, nil, nil, nil), + initialClaims: newClaimArrayFinalizer("claim6-2", "pvc-uid6-2", "1Gi", "volume6-2", v1.ClaimBound, &classEmpty), + test: testAddPVCFinalizer, + expectSuccess: false, + }, + { + name: "1-3 - successful remove PVC finalizer", + initialSnapshots: newSnapshotArray("snap6-2", classSilver, "", "snapuid6-2", "claim6-2", false, nil, nil, nil), + initialClaims: newClaimArrayFinalizer("claim6-2", "pvc-uid6-2", "1Gi", "volume6-2", v1.ClaimBound, &classEmpty), + test: testRemovePVCFinalizer, + expectSuccess: true, + }, + { + name: "1-4 - won't remove PVC finalizer; already removed", + initialSnapshots: newSnapshotArray("snap6-2", classSilver, "", "snapuid6-2", "claim6-2", false, nil, nil, nil), + initialClaims: newClaimArray("claim6-2", "pvc-uid6-2", "1Gi", "volume6-2", v1.ClaimBound, &classEmpty), + test: testRemovePVCFinalizer, + expectSuccess: false, + }, + { + name: "1-5 - won't remove PVC finalizer; PVC in-use", + initialSnapshots: newSnapshotArray("snap6-2", classSilver, "", "snapuid6-2", "claim6-2", false, nil, nil, nil), + initialClaims: newClaimArray("claim6-2", "pvc-uid6-2", "1Gi", "volume6-2", v1.ClaimBound, &classEmpty), + test: testRemovePVCFinalizer, + expectSuccess: false, + }, + } + runPVCFinalizerTests(t, tests, snapshotClasses) +} diff --git a/pkg/controller/snapshot_ready_test.go b/pkg/controller/snapshot_ready_test.go index b495af781..7a64925bc 100644 --- a/pkg/controller/snapshot_ready_test.go +++ b/pkg/controller/snapshot_ready_test.go @@ -70,19 +70,19 @@ func TestSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap2-3", validSecretClass, "content2-3", "snapuid2-3", "claim2-3", false, nil, metaTimeNow, nil), expectedSnapshots: newSnapshotArray("snap2-3", validSecretClass, "content2-3", "snapuid2-3", "claim2-3", false, nil, metaTimeNow, nil), initialClaims: newClaimArray("claim2-3", "pvc-uid2-3", "1Gi", "volume2-3", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume2-3", "pv-uid2-3", "pv-handle2-3", "1Gi", "pvc-uid2-3", "claim2-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume2-3", "pv-uid2-3", "pv-handle2-3", "1Gi", "pvc-uid2-3", "claim2-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), initialSecrets: []*v1.Secret{secret()}, expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid2-3", - volume: newVolume("volume2-3", "pv-uid2-3", "pv-handle2-3", "1Gi", "pvc-uid2-3", "claim2-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume2-3", "pv-uid2-3", "pv-handle2-3", "1Gi", "pvc-uid2-3", "claim2-3", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: class5Parameters, secrets: map[string]string{"foo": "bar"}, // information to return - driverName: mockDriverName, - snapshotId: "sid2-3", - timestamp: timeNow, - readyToUse: false, + driverName: mockDriverName, + snapshotId: "sid2-3", + creationTime: timeNow, + readyToUse: false, }, }, errors: noerrors, @@ -105,19 +105,19 @@ func TestSync(t *testing.T) { initialSnapshots: newSnapshotArray("snap2-5", validSecretClass, "content2-5", "snapuid2-5", "claim2-5", false, nil, metaTimeNow, nil), expectedSnapshots: newSnapshotArray("snap2-5", validSecretClass, "content2-5", "snapuid2-5", "claim2-5", true, nil, metaTimeNow, nil), initialClaims: newClaimArray("claim2-5", "pvc-uid2-5", "1Gi", "volume2-5", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume2-5", "pv-uid2-5", "pv-handle2-5", "1Gi", "pvc-uid2-5", "claim2-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume2-5", "pv-uid2-5", "pv-handle2-5", "1Gi", "pvc-uid2-5", "claim2-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), initialSecrets: []*v1.Secret{secret()}, expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid2-5", - volume: newVolume("volume2-5", "pv-uid2-5", "pv-handle2-5", "1Gi", "pvc-uid2-5", "claim2-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume2-5", "pv-uid2-5", "pv-handle2-5", "1Gi", "pvc-uid2-5", "claim2-5", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: class5Parameters, secrets: map[string]string{"foo": "bar"}, // information to return - driverName: mockDriverName, - snapshotId: "sid2-5", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + snapshotId: "sid2-5", + creationTime: timeNow, + readyToUse: true, }, }, errors: noerrors, @@ -125,8 +125,8 @@ func TestSync(t *testing.T) { }, { name: "2-6 - snapshot bound to prebound content correctly, status ready false -> true, ref.UID '' -> 'snapuid2-6'", - initialContents: newContentArray("content2-6", validSecretClass, "sid2-6", noVolume, noVolume, noBoundUID, "snap2-6", &deletePolicy, nil, &timeNow, false), - expectedContents: newContentArray("content2-6", validSecretClass, "sid2-6", noVolume, noVolume, "snapuid2-6", "snap2-6", &deletePolicy, nil, &timeNow, false), + initialContents: newContentArray("content2-6", validSecretClass, "sid2-6", noVolume, noVolume, noBoundUID, "snap2-6", &deletePolicy, nil, &timeNowStamp, false), + expectedContents: newContentArray("content2-6", validSecretClass, "sid2-6", noVolume, noVolume, "snapuid2-6", "snap2-6", &deletePolicy, nil, &timeNowStamp, false), initialSnapshots: newSnapshotArray("snap2-6", validSecretClass, "content2-6", "snapuid2-6", noClaim, false, nil, metaTimeNow, nil), expectedSnapshots: newSnapshotArray("snap2-6", validSecretClass, "content2-6", "snapuid2-6", noClaim, true, nil, metaTimeNow, nil), expectedListCalls: []listCall{ @@ -146,12 +146,12 @@ func TestSync(t *testing.T) { expectedSnapshots: newSnapshotArray("snap2-7", validSecretClass, "content2-7", "snapuid2-7", "claim2-7", false, newVolumeError("Failed to check and update snapshot: mock create snapshot error"), metaTimeNow, nil), expectedEvents: []string{"Warning SnapshotCheckandUpdateFailed"}, initialClaims: newClaimArray("claim2-7", "pvc-uid2-7", "1Gi", "volume2-7", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume2-7", "pv-uid2-7", "pv-handle2-7", "1Gi", "pvc-uid2-7", "claim2-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume2-7", "pv-uid2-7", "pv-handle2-7", "1Gi", "pvc-uid2-7", "claim2-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), initialSecrets: []*v1.Secret{secret()}, expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid2-7", - volume: newVolume("volume2-7", "pv-uid2-7", "pv-handle2-7", "1Gi", "pvc-uid2-7", "claim2-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume2-7", "pv-uid2-7", "pv-handle2-7", "1Gi", "pvc-uid2-7", "claim2-7", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: class5Parameters, secrets: map[string]string{"foo": "bar"}, // information to return @@ -169,20 +169,20 @@ func TestSync(t *testing.T) { expectedSnapshots: newSnapshotArray("snap2-8", validSecretClass, "content2-8", "snapuid2-8", "claim2-8", false, newVolumeError("Failed to check and update snapshot: snapshot controller failed to update default/snap2-8 on API server: mock update error"), metaTimeNow, nil), expectedEvents: []string{"Warning SnapshotCheckandUpdateFailed"}, initialClaims: newClaimArray("claim2-8", "pvc-uid2-8", "1Gi", "volume2-8", v1.ClaimBound, &classEmpty), - initialVolumes: newVolumeArray("volume2-8", "pv-uid2-8", "pv-handle2-8", "1Gi", "pvc-uid2-8", "claim2-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + initialVolumes: newVolumeArray("volume2-8", "pv-uid2-8", "pv-handle2-8", "1Gi", "pvc-uid2-8", "claim2-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), initialSecrets: []*v1.Secret{secret()}, expectedCreateCalls: []createCall{ { snapshotName: "snapshot-snapuid2-8", - volume: newVolume("volume2-8", "pv-uid2-8", "pv-handle2-8", "1Gi", "pvc-uid2-8", "claim2-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty), + volume: newVolume("volume2-8", "pv-uid2-8", "pv-handle2-8", "1Gi", "pvc-uid2-8", "claim2-8", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty, mockDriverName, testNamespace), parameters: class5Parameters, secrets: map[string]string{"foo": "bar"}, // information to return - driverName: mockDriverName, - size: defaultSize, - snapshotId: "sid2-8", - timestamp: timeNow, - readyToUse: true, + driverName: mockDriverName, + size: defaultSize, + snapshotId: "sid2-8", + creationTime: timeNow, + readyToUse: true, }, }, errors: []reactorError{ diff --git a/pkg/controller/util.go b/pkg/controller/util.go index 75593d499..b91f32591 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -75,6 +75,9 @@ var snapshotterSecretParams = deprecatedSecretParamsMap{ secretNamespaceKey: prefixedSnapshotterSecretNamespaceKey, } +// Name of finalizer on PVCs that have been used as a source to create VolumeSnapshots +const PVCFinalizer = "snapshot.storage.kubernetes.io/pvc-protection" + func snapshotKey(vs *crdv1.VolumeSnapshot) string { return fmt.Sprintf("%s/%s", vs.Namespace, vs.Name) } diff --git a/pkg/snapshotter/snapshotter.go b/pkg/snapshotter/snapshotter.go index ee5031dfc..157f08972 100644 --- a/pkg/snapshotter/snapshotter.go +++ b/pkg/snapshotter/snapshotter.go @@ -19,10 +19,10 @@ package snapshotter import ( "context" "fmt" + "time" "github.com/container-storage-interface/spec/lib/go/csi" "github.com/golang/protobuf/ptypes" - "github.com/golang/protobuf/ptypes/timestamp" csirpc "github.com/kubernetes-csi/csi-lib-utils/rpc" "google.golang.org/grpc" @@ -34,13 +34,13 @@ import ( // Snapshotter implements CreateSnapshot/DeleteSnapshot operations against a remote CSI driver. type Snapshotter interface { // CreateSnapshot creates a snapshot for a volume - CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (driverName string, snapshotId string, timestamp int64, size int64, readyToUse bool, err error) + CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (driverName string, snapshotId string, timestamp time.Time, size int64, readyToUse bool, err error) // DeleteSnapshot deletes a snapshot from a volume DeleteSnapshot(ctx context.Context, snapshotID string, snapshotterCredentials map[string]string) (err error) // GetSnapshotStatus returns if a snapshot is ready to use, creation time, and restore size. - GetSnapshotStatus(ctx context.Context, snapshotID string) (bool, int64, int64, error) + GetSnapshotStatus(ctx context.Context, snapshotID string) (bool, time.Time, int64, error) } type snapshot struct { @@ -53,17 +53,17 @@ func NewSnapshotter(conn *grpc.ClientConn) Snapshotter { } } -func (s *snapshot) CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, int64, int64, bool, error) { +func (s *snapshot) CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error) { klog.V(5).Infof("CSI CreateSnapshot: %s", snapshotName) if volume.Spec.CSI == nil { - return "", "", 0, 0, false, fmt.Errorf("CSIPersistentVolumeSource not defined in spec") + return "", "", time.Time{}, 0, false, fmt.Errorf("CSIPersistentVolumeSource not defined in spec") } client := csi.NewControllerClient(s.conn) driverName, err := csirpc.GetDriverName(ctx, s.conn) if err != nil { - return "", "", 0, 0, false, err + return "", "", time.Time{}, 0, false, err } req := csi.CreateSnapshotRequest{ @@ -75,13 +75,13 @@ func (s *snapshot) CreateSnapshot(ctx context.Context, snapshotName string, volu rsp, err := client.CreateSnapshot(ctx, &req) if err != nil { - return "", "", 0, 0, false, err + return "", "", time.Time{}, 0, false, err } klog.V(5).Infof("CSI CreateSnapshot: %s driver name [%s] snapshot ID [%s] time stamp [%d] size [%d] readyToUse [%v]", snapshotName, driverName, rsp.Snapshot.SnapshotId, rsp.Snapshot.CreationTime, rsp.Snapshot.SizeBytes, rsp.Snapshot.ReadyToUse) - creationTime, err := timestampToUnixTime(rsp.Snapshot.CreationTime) + creationTime, err := ptypes.Timestamp(rsp.Snapshot.CreationTime) if err != nil { - return "", "", 0, 0, false, err + return "", "", time.Time{}, 0, false, err } return driverName, rsp.Snapshot.SnapshotId, creationTime, rsp.Snapshot.SizeBytes, rsp.Snapshot.ReadyToUse, nil } @@ -101,35 +101,48 @@ func (s *snapshot) DeleteSnapshot(ctx context.Context, snapshotID string, snapsh return nil } -func (s *snapshot) GetSnapshotStatus(ctx context.Context, snapshotID string) (bool, int64, int64, error) { +func (s *snapshot) isListSnapshotsSupported(ctx context.Context) (bool, error) { + client := csi.NewControllerClient(s.conn) + capRsp, err := client.ControllerGetCapabilities(ctx, &csi.ControllerGetCapabilitiesRequest{}) + if err != nil { + return false, err + } + + for _, cap := range capRsp.Capabilities { + if cap.GetRpc().GetType() == csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS { + return true, nil + } + } + + return false, nil +} + +func (s *snapshot) GetSnapshotStatus(ctx context.Context, snapshotID string) (bool, time.Time, int64, error) { client := csi.NewControllerClient(s.conn) + // If the driver does not support ListSnapshots, assume the snapshot ID is valid. + listSnapshotsSupported, err := s.isListSnapshotsSupported(ctx) + if err != nil { + return false, time.Time{}, 0, fmt.Errorf("failed to check if ListSnapshots is supported: %s", err.Error()) + } + if !listSnapshotsSupported { + return true, time.Time{}, 0, nil + } req := csi.ListSnapshotsRequest{ SnapshotId: snapshotID, } - rsp, err := client.ListSnapshots(ctx, &req) if err != nil { - return false, 0, 0, err + return false, time.Time{}, 0, err } if rsp.Entries == nil || len(rsp.Entries) == 0 { - return false, 0, 0, fmt.Errorf("can not find snapshot for snapshotID %s", snapshotID) + return false, time.Time{}, 0, fmt.Errorf("can not find snapshot for snapshotID %s", snapshotID) } - creationTime, err := timestampToUnixTime(rsp.Entries[0].Snapshot.CreationTime) + creationTime, err := ptypes.Timestamp(rsp.Entries[0].Snapshot.CreationTime) if err != nil { - return false, 0, 0, err + return false, time.Time{}, 0, err } return rsp.Entries[0].Snapshot.ReadyToUse, creationTime, rsp.Entries[0].Snapshot.SizeBytes, nil } - -func timestampToUnixTime(t *timestamp.Timestamp) (int64, error) { - time, err := ptypes.Timestamp(t) - if err != nil { - return -1, err - } - // TODO: clean this up, we probably don't need this translation layer - // and can just use time.Time - return time.UnixNano(), nil -} diff --git a/pkg/snapshotter/snapshotter_test.go b/pkg/snapshotter/snapshotter_test.go index dbe973d38..b0ff50f10 100644 --- a/pkg/snapshotter/snapshotter_test.go +++ b/pkg/snapshotter/snapshotter_test.go @@ -21,6 +21,7 @@ import ( "fmt" "reflect" "testing" + "time" "github.com/container-storage-interface/spec/lib/go/csi" "github.com/golang/mock/gomock" @@ -118,7 +119,7 @@ func TestCreateSnapshot(t *testing.T) { type snapshotResult struct { driverName string snapshotId string - timestamp int64 + timestamp time.Time size int64 readyToUse bool } @@ -127,7 +128,7 @@ func TestCreateSnapshot(t *testing.T) { size: 1000, driverName: driverName, snapshotId: defaultID, - timestamp: createTime.UnixNano(), + timestamp: createTime, readyToUse: true, } @@ -369,41 +370,56 @@ func TestGetSnapshotStatus(t *testing.T) { } tests := []struct { - name string - snapshotID string - input *csi.ListSnapshotsRequest - output *csi.ListSnapshotsResponse - injectError codes.Code - expectError bool - expectReady bool - expectCreateAt int64 - expectSize int64 + name string + snapshotID string + listSnapshotsSupported bool + input *csi.ListSnapshotsRequest + output *csi.ListSnapshotsResponse + injectError codes.Code + expectError bool + expectReady bool + expectCreateAt time.Time + expectSize int64 }{ { - name: "success", - snapshotID: defaultID, - input: defaultRequest, - output: defaultResponse, - expectError: false, - expectReady: true, - expectCreateAt: createTime.UnixNano(), - expectSize: size, + name: "success", + snapshotID: defaultID, + listSnapshotsSupported: true, + input: defaultRequest, + output: defaultResponse, + expectError: false, + expectReady: true, + expectCreateAt: createTime, + expectSize: size, }, { - name: "gRPC transient error", - snapshotID: defaultID, - input: defaultRequest, - output: nil, - injectError: codes.DeadlineExceeded, - expectError: true, + name: "ListSnapshots not supported", + snapshotID: defaultID, + listSnapshotsSupported: false, + input: defaultRequest, + output: defaultResponse, + expectError: false, + expectReady: true, + expectCreateAt: time.Time{}, + expectSize: 0, }, { - name: "gRPC final error", - snapshotID: defaultID, - input: defaultRequest, - output: nil, - injectError: codes.NotFound, - expectError: true, + name: "gRPC transient error", + snapshotID: defaultID, + listSnapshotsSupported: true, + input: defaultRequest, + output: nil, + injectError: codes.DeadlineExceeded, + expectError: true, + }, + { + name: "gRPC final error", + snapshotID: defaultID, + listSnapshotsSupported: true, + input: defaultRequest, + output: nil, + injectError: codes.NotFound, + expectError: true, }, } @@ -424,8 +440,25 @@ func TestGetSnapshotStatus(t *testing.T) { } // Setup expectation + listSnapshotsCap := &csi.ControllerServiceCapability{ + Type: &csi.ControllerServiceCapability_Rpc{ + Rpc: &csi.ControllerServiceCapability_RPC{ + Type: csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS, + }, + }, + } + + var controllerCapabilities []*csi.ControllerServiceCapability + if test.listSnapshotsSupported { + controllerCapabilities = append(controllerCapabilities, listSnapshotsCap) + } if in != nil { - controllerServer.EXPECT().ListSnapshots(gomock.Any(), in).Return(out, injectedErr).Times(1) + controllerServer.EXPECT().ControllerGetCapabilities(gomock.Any(), gomock.Any()).Return(&csi.ControllerGetCapabilitiesResponse{ + Capabilities: controllerCapabilities, + }, nil).Times(1) + if test.listSnapshotsSupported { + controllerServer.EXPECT().ListSnapshots(gomock.Any(), in).Return(out, injectedErr).Times(1) + } } s := NewSnapshotter(csiConn) diff --git a/release-tools/build.make b/release-tools/build.make index e3d4795da..142c85780 100644 --- a/release-tools/build.make +++ b/release-tools/build.make @@ -118,14 +118,39 @@ test-fmt: fi # This test only runs when dep >= 0.5 is installed, which is the case for the CI setup. +# When using 'go mod', we allow the test to be skipped in the Prow CI under some special +# circumstances, because it depends on accessing all remote repos and thus +# running it all the time would defeat the purpose of vendoring: +# - not handling a PR or +# - the fabricated merge commit leaves go.mod, go.sum and vendor dir unchanged +# - release-tools also didn't change (changing rules or Go version might lead to +# a different result and thus must be tested) .PHONY: test-vendor test: test-vendor test-vendor: @ echo; echo "### $@:" - @ case "$$(dep version 2>/dev/null | grep 'version *:')" in \ - *v0.[56789]*) dep check && echo "vendor up-to-date" || false;; \ - *) echo "skipping check, dep >= 0.5 required";; \ - esac + @ if [ -f Gopkg.toml ]; then \ + echo "Repo uses 'dep' for vendoring."; \ + case "$$(dep version 2>/dev/null | grep 'version *:')" in \ + *v0.[56789]*) dep check && echo "vendor up-to-date" || false;; \ + *) echo "skipping check, dep >= 0.5 required";; \ + esac; \ + else \ + echo "Repo uses 'go mod' for vendoring."; \ + if [ "$${JOB_NAME}" ] && \ + ( [ "$${JOB_TYPE}" != "presubmit" ] || \ + [ $$(git diff "${PULL_BASE_SHA}..HEAD" -- go.mod go.sum vendor release-tools | wc -l) -eq 0 ] ); then \ + echo "Skipping vendor check because the Prow pre-submit job does not change vendoring."; \ + elif ! GO111MODULE=on go mod vendor; then \ + echo "ERROR: vendor check failed."; \ + false; \ + elif [ $$(git status --porcelain -- vendor | wc -l) -gt 0 ]; then \ + echo "ERROR: vendor directory *not* up-to-date, it did get modified by 'GO111MODULE=on go mod vendor':"; \ + git status -- vendor; \ + git diff -- vendor; \ + false; \ + fi; \ + fi; .PHONY: test-subtree test: test-subtree diff --git a/release-tools/prow.sh b/release-tools/prow.sh index 204cc294c..dcfcec614 100755 --- a/release-tools/prow.sh +++ b/release-tools/prow.sh @@ -52,6 +52,26 @@ configvar () { eval echo "\$3:" "$1=\${$1}" } +# Takes the minor version of $CSI_PROW_KUBERNETES_VERSION and overrides it to +# $1 if they are equal minor versions. Ignores versions that begin with +# "release-". +override_k8s_version () { + local current_minor_version + local override_minor_version + + # Ignore: See if you can use ${variable//search/replace} instead. + # shellcheck disable=SC2001 + current_minor_version="$(echo "${CSI_PROW_KUBERNETES_VERSION}" | sed -e 's/\([0-9]*\)\.\([0-9]*\).*/\1\.\2/')" + + # Ignore: See if you can use ${variable//search/replace} instead. + # shellcheck disable=SC2001 + override_minor_version="$(echo "${1}" | sed -e 's/\([0-9]*\)\.\([0-9]*\).*/\1\.\2/')" + if [ "${current_minor_version}" == "${override_minor_version}" ]; then + CSI_PROW_KUBERNETES_VERSION="$1" + echo "Overriding CSI_PROW_KUBERNETES_VERSION with $1: $CSI_PROW_KUBERNETES_VERSION" + fi +} + # Prints the value of a variable + version suffix, falling back to variable + "LATEST". get_versioned_variable () { local var="$1" @@ -81,7 +101,7 @@ configvar CSI_PROW_GO_VERSION_GINKGO "${CSI_PROW_GO_VERSION_BUILD}" "Go version # kind version to use. If the pre-installed version is different, # the desired version is downloaded from https://github.com/kubernetes-sigs/kind/releases/download/ # (if available), otherwise it is built from source. -configvar CSI_PROW_KIND_VERSION 0.2.1 "kind" +configvar CSI_PROW_KIND_VERSION v0.4.0 "kind" # ginkgo test runner version to use. If the pre-installed version is # different, the desired version is built from source. @@ -108,6 +128,18 @@ configvar CSI_PROW_BUILD_JOB true "building code in repo enabled" # deprecating or changing the implementation of an alpha feature. configvar CSI_PROW_KUBERNETES_VERSION 1.13.3 "Kubernetes" +# This is a hack to workaround the issue that each version +# of kind currently only supports specific patch versions of +# Kubernetes. We need to override CSI_PROW_KUBERNETES_VERSION +# passed in by our CI/pull jobs to the versions that +# kind v0.4.0 supports. +# +# If the version is prefixed with "release-", then nothing +# is overridden. +override_k8s_version "1.13.7" +override_k8s_version "1.14.3" +override_k8s_version "1.15.0" + # CSI_PROW_KUBERNETES_VERSION reduced to first two version numbers and # with underscore (1_13 instead of 1.13.3) and in uppercase (LATEST # instead of latest). @@ -151,9 +183,10 @@ configvar CSI_PROW_WORK "$(mkdir -p "$GOPATH/pkg" && mktemp -d "$GOPATH/pkg/csip # # When no deploy script is found (nothing in `deploy` directory, # CSI_PROW_HOSTPATH_REPO=none), nothing gets deployed. -configvar CSI_PROW_HOSTPATH_VERSION fc52d13ba07922c80555a24616a5b16480350c3f "hostpath driver" # pre-1.1.0 +configvar CSI_PROW_HOSTPATH_VERSION "v1.2.0-rc2" "hostpath driver" configvar CSI_PROW_HOSTPATH_REPO https://github.com/kubernetes-csi/csi-driver-host-path "hostpath repo" configvar CSI_PROW_DEPLOYMENT "" "deployment" +configvar CSI_PROW_HOSTPATH_DRIVER_NAME "hostpath.csi.k8s.io" "the hostpath driver name" # If CSI_PROW_HOSTPATH_CANARY is set (typically to "canary", but also # "1.0-canary"), then all image versions are replaced with that @@ -169,6 +202,7 @@ configvar CSI_PROW_HOSTPATH_CANARY "" "hostpath image" # CSI_PROW_E2E_REPO=none disables E2E testing. configvar CSI_PROW_E2E_VERSION_1_13 v1.14.0 "E2E version for Kubernetes 1.13.x" # we can't use the one from 1.13.x because it didn't have --storage.testdriver configvar CSI_PROW_E2E_VERSION_1_14 v1.14.0 "E2E version for Kubernetes 1.14.x" +configvar CSI_PROW_E2E_VERSION_1_15 v1.15.0 "E2E version for Kubernetes 1.15.x" # TODO: add new CSI_PROW_E2E_VERSION entry for future Kubernetes releases configvar CSI_PROW_E2E_VERSION_LATEST master "E2E version for Kubernetes master" # testing against Kubernetes master is already tracking a moving target, so we might as well use a moving E2E version configvar CSI_PROW_E2E_REPO_LATEST https://github.com/kubernetes/kubernetes "E2E repo for Kubernetes >= 1.13.x" # currently the same for all versions @@ -276,6 +310,7 @@ configvar CSI_PROW_E2E_ALPHA "$(get_versioned_variable CSI_PROW_E2E_ALPHA "${csi # it anymore for older releases. configvar CSI_PROW_E2E_ALPHA_GATES_1_13 'VolumeSnapshotDataSource=true,BlockVolume=true,CSIBlockVolume=true' "alpha feature gates for Kubernetes 1.13" configvar CSI_PROW_E2E_ALPHA_GATES_1_14 'VolumeSnapshotDataSource=true,ExpandCSIVolumes=true' "alpha feature gates for Kubernetes 1.14" +configvar CSI_PROW_E2E_ALPHA_GATES_1_15 'VolumeSnapshotDataSource=true,ExpandCSIVolumes=true' "alpha feature gates for Kubernetes 1.15" # TODO: add new CSI_PROW_ALPHA_GATES_xxx entry for future Kubernetes releases and # add new gates to CSI_PROW_E2E_ALPHA_GATES_LATEST. configvar CSI_PROW_E2E_ALPHA_GATES_LATEST 'VolumeSnapshotDataSource=true,ExpandCSIVolumes=true' "alpha feature gates for latest Kubernetes" @@ -450,7 +485,7 @@ start_cluster () { tag="$(echo "${CSI_PROW_KUBERNETES_VERSION}" | sed -e 's/release-\(.*\)/v\1.0-release./')";; *) # We have to make something up. v1.0.0 did not work for some reasons. - tag="v1.14.0-";; + tag="v999.999.999-";; esac tag="$tag$(cd "$GOPATH/src/k8s.io/kubernetes" && git rev-list --abbrev-commit HEAD).csiprow" (cd "$GOPATH/src/k8s.io/kubernetes" && run git tag -f "$tag") || die "git tag failed" @@ -463,40 +498,51 @@ start_cluster () { image="kindest/node:v${CSI_PROW_KUBERNETES_VERSION}" fi cat >"${CSI_PROW_WORK}/kind-config.yaml" <>"${CSI_PROW_WORK}/kind-config.yaml" <"${CSI_PROW_WORK}/hostpath-test-driver.yaml" <"${CSI_PROW_WORK}/test-driver.yaml" || die "generating test-driver.yaml failed" # Rename, merge and filter JUnit files. Necessary in case that we run the E2E suite again # and to avoid the large number of "skipped" tests that we get from using @@ -727,7 +780,7 @@ EOF trap move_junit EXIT cd "${GOPATH}/src/${CSI_PROW_E2E_IMPORT_PATH}" && - run_with_loggers ginkgo -v "$@" "${CSI_PROW_WORK}/e2e.test" -- -report-dir "${ARTIFACTS}" -storage.testdriver="${CSI_PROW_WORK}/hostpath-test-driver.yaml" + run_with_loggers ginkgo -v "$@" "${CSI_PROW_WORK}/e2e.test" -- -report-dir "${ARTIFACTS}" -storage.testdriver="${CSI_PROW_WORK}/test-driver.yaml" ) # Run csi-sanity against installed CSI driver. diff --git a/release-tools/travis.yml b/release-tools/travis.yml index 1c05dfd97..5b0425226 100644 --- a/release-tools/travis.yml +++ b/release-tools/travis.yml @@ -4,7 +4,7 @@ services: - docker matrix: include: - - go: 1.11.1 + - go: 1.12.4 before_script: - mkdir -p bin - wget https://github.com/golang/dep/releases/download/v0.5.1/dep-linux-amd64 -O bin/dep