Skip to content

Commit 0d2cdd7

Browse files
authored
Merge pull request #79 from xing-yang/release-1.0
Cherry-pick adding new reserved prefixed parameter keys fix
2 parents bb552a6 + a8c5778 commit 0d2cdd7

File tree

6 files changed

+238
-57
lines changed

6 files changed

+238
-57
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# CSI Snapshotter
22

3-
The CSI external-snapshotter is part of Kubernetes implementation of [Container Storage Interface (CSI)](https://github.com/container-storage-interface/spec)
3+
The CSI external-snapshotter is part of Kubernetes implementation of [Container Storage Interface (CSI)](https://github.com/container-storage-interface/spec).
4+
5+
The volume snapshot feature supports CSI v1.0 and it has been an Alpha feature in Kubernetes since v1.12.
46

57
## Overview
68

pkg/controller/csi_handler.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ func (handler *csiHandler) CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume
6565
if err != nil {
6666
return "", "", 0, 0, false, err
6767
}
68-
return handler.csiConnection.CreateSnapshot(ctx, snapshotName, volume, parameters, snapshotterCredentials)
68+
newParameters, err := removePrefixedParameters(parameters)
69+
if err != nil {
70+
return "", "", 0, 0, false, fmt.Errorf("failed to remove CSI Parameters of prefixed keys: %v", err)
71+
}
72+
return handler.csiConnection.CreateSnapshot(ctx, snapshotName, volume, newParameters, snapshotterCredentials)
6973
}
7074

7175
func (handler *csiHandler) DeleteSnapshot(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) error {

pkg/controller/snapshot_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,11 +542,11 @@ func (ctrl *csiSnapshotController) getCreateSnapshotInput(snapshot *crdv1.Volume
542542
contentName := GetSnapshotContentNameForSnapshot(snapshot)
543543

544544
// Resolve snapshotting secret credentials.
545-
snapshotterSecretRef, err := GetSecretReference(class.Parameters, contentName, snapshot)
545+
snapshotterSecretRef, err := getSecretReference(class.Parameters, contentName, snapshot)
546546
if err != nil {
547547
return nil, nil, "", nil, err
548548
}
549-
snapshotterCredentials, err := GetCredentials(ctrl.client, snapshotterSecretRef)
549+
snapshotterCredentials, err := getCredentials(ctrl.client, snapshotterSecretRef)
550550
if err != nil {
551551
return nil, nil, "", nil, err
552552
}
@@ -710,11 +710,11 @@ func (ctrl *csiSnapshotController) deleteSnapshotContentOperation(content *crdv1
710710
if snapshotClass, err := ctrl.classLister.Get(*snapshotClassName); err == nil {
711711
// Resolve snapshotting secret credentials.
712712
// No VolumeSnapshot is provided when resolving delete secret names, since the VolumeSnapshot may or may not exist at delete time.
713-
snapshotterSecretRef, err := GetSecretReference(snapshotClass.Parameters, content.Name, nil)
713+
snapshotterSecretRef, err := getSecretReference(snapshotClass.Parameters, content.Name, nil)
714714
if err != nil {
715715
return err
716716
}
717-
snapshotterCredentials, err = GetCredentials(ctrl.client, snapshotterSecretRef)
717+
snapshotterCredentials, err = getCredentials(ctrl.client, snapshotterSecretRef)
718718
if err != nil {
719719
return err
720720
}

pkg/controller/snapshot_create_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func TestCreateSnapshotSync(t *testing.T) {
230230
initialContents: nocontents,
231231
expectedContents: nocontents,
232232
initialSnapshots: newSnapshotArray("snap7-2", invalidSecretClass, "", "snapuid7-2", "claim7-2", false, nil, nil, nil),
233-
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: \"csiSnapshotterSecretName and csiSnapshotterSecretNamespace parameters must be specified together\""), nil, nil),
233+
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),
234234
initialClaims: newClaimArray("claim7-2", "pvc-uid7-2", "1Gi", "volume7-2", v1.ClaimBound, &classEmpty),
235235
initialVolumes: newVolumeArray("volume7-2", "pv-uid7-2", "pv-handle7-2", "1Gi", "pvc-uid7-2", "claim7-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, classEmpty),
236236
expectedEvents: []string{"Warning SnapshotCreationFailed"},

pkg/controller/util.go

Lines changed: 130 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package controller
1818

1919
import (
2020
"fmt"
21+
"strings"
22+
2123
"github.com/golang/glog"
2224
crdv1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1"
2325
"k8s.io/api/core/v1"
@@ -37,12 +39,41 @@ var (
3739
keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc
3840
)
3941

40-
const snapshotterSecretNameKey = "csiSnapshotterSecretName"
41-
const snapshotterSecretNamespaceKey = "csiSnapshotterSecretNamespace"
42+
type deprecatedSecretParamsMap struct {
43+
name string
44+
deprecatedSecretNameKey string
45+
deprecatedSecretNamespaceKey string
46+
secretNameKey string
47+
secretNamespaceKey string
48+
}
49+
50+
const (
51+
// CSI Parameters prefixed with csiParameterPrefix are not passed through
52+
// to the driver on CreateSnapshotRequest calls. Instead they are intended
53+
// to be used by the CSI external-snapshotter and maybe used to populate
54+
// fields in subsequent CSI calls or Kubernetes API objects.
55+
csiParameterPrefix = "csi.storage.k8s.io/"
56+
57+
prefixedSnapshotterSecretNameKey = csiParameterPrefix + "snapshotter-secret-name"
58+
prefixedSnapshotterSecretNamespaceKey = csiParameterPrefix + "snapshotter-secret-namespace"
4259

43-
// Name of finalizer on VolumeSnapshotContents that are bound by VolumeSnapshots
44-
const VolumeSnapshotContentFinalizer = "snapshot.storage.kubernetes.io/volumesnapshotcontent-protection"
45-
const VolumeSnapshotFinalizer = "snapshot.storage.kubernetes.io/volumesnapshot-protection"
60+
// [Deprecated] CSI Parameters that are put into fields but
61+
// NOT stripped from the parameters passed to CreateSnapshot
62+
snapshotterSecretNameKey = "csiSnapshotterSecretName"
63+
snapshotterSecretNamespaceKey = "csiSnapshotterSecretNamespace"
64+
65+
// Name of finalizer on VolumeSnapshotContents that are bound by VolumeSnapshots
66+
VolumeSnapshotContentFinalizer = "snapshot.storage.kubernetes.io/volumesnapshotcontent-protection"
67+
VolumeSnapshotFinalizer = "snapshot.storage.kubernetes.io/volumesnapshot-protection"
68+
)
69+
70+
var snapshotterSecretParams = deprecatedSecretParamsMap{
71+
name: "Snapshotter",
72+
deprecatedSecretNameKey: snapshotterSecretNameKey,
73+
deprecatedSecretNamespaceKey: snapshotterSecretNamespaceKey,
74+
secretNameKey: prefixedSnapshotterSecretNameKey,
75+
secretNamespaceKey: prefixedSnapshotterSecretNamespaceKey,
76+
}
4677

4778
func snapshotKey(vs *crdv1.VolumeSnapshot) string {
4879
return fmt.Sprintf("%s/%s", vs.Namespace, vs.Name)
@@ -130,9 +161,54 @@ func IsDefaultAnnotation(obj metav1.ObjectMeta) bool {
130161
return false
131162
}
132163

133-
// GetSecretReference returns a reference to the secret specified in the given nameKey and namespaceKey parameters, or an error if the parameters are not specified correctly.
134-
// if neither the name or namespace parameter are set, a nil reference and no error is returned.
135-
// no lookup of the referenced secret is performed, and the secret may or may not exist.
164+
// verifyAndGetSecretNameAndNamespaceTemplate gets the values (templates) associated
165+
// with the parameters specified in "secret" and verifies that they are specified correctly.
166+
func verifyAndGetSecretNameAndNamespaceTemplate(secret deprecatedSecretParamsMap, snapshotClassParams map[string]string) (nameTemplate, namespaceTemplate string, err error) {
167+
numName := 0
168+
numNamespace := 0
169+
if t, ok := snapshotClassParams[secret.deprecatedSecretNameKey]; ok {
170+
nameTemplate = t
171+
numName += 1
172+
glog.Warning(deprecationWarning(secret.deprecatedSecretNameKey, secret.secretNameKey, ""))
173+
}
174+
if t, ok := snapshotClassParams[secret.deprecatedSecretNamespaceKey]; ok {
175+
namespaceTemplate = t
176+
numNamespace += 1
177+
glog.Warning(deprecationWarning(secret.deprecatedSecretNamespaceKey, secret.secretNamespaceKey, ""))
178+
}
179+
if t, ok := snapshotClassParams[secret.secretNameKey]; ok {
180+
nameTemplate = t
181+
numName += 1
182+
}
183+
if t, ok := snapshotClassParams[secret.secretNamespaceKey]; ok {
184+
namespaceTemplate = t
185+
numNamespace += 1
186+
}
187+
188+
if numName > 1 || numNamespace > 1 {
189+
// Double specified error
190+
return "", "", fmt.Errorf("%s secrets specified in paramaters with both \"csi\" and \"%s\" keys", secret.name, csiParameterPrefix)
191+
} else if numName != numNamespace {
192+
// Not both 0 or both 1
193+
return "", "", fmt.Errorf("either name and namespace for %s secrets specified, Both must be specified", secret.name)
194+
} else if numName == 1 {
195+
// Case where we've found a name and a namespace template
196+
if nameTemplate == "" || namespaceTemplate == "" {
197+
return "", "", fmt.Errorf("%s secrets specified in parameters but value of either namespace or name is empty", secret.name)
198+
}
199+
return nameTemplate, namespaceTemplate, nil
200+
} else if numName == 0 {
201+
// No secrets specified
202+
return "", "", nil
203+
} else {
204+
// THIS IS NOT A VALID CASE
205+
return "", "", fmt.Errorf("unknown error with getting secret name and namespace templates")
206+
}
207+
}
208+
209+
// getSecretReference returns a reference to the secret specified in the given nameTemplate
210+
// and namespaceTemplate, or an error if the templates are not specified correctly.
211+
// No lookup of the referenced secret is performed, and the secret may or may not exist.
136212
//
137213
// supported tokens for name resolution:
138214
// - ${volumesnapshotcontent.name}
@@ -145,20 +221,17 @@ func IsDefaultAnnotation(obj metav1.ObjectMeta) bool {
145221
// - ${volumesnapshot.namespace}
146222
//
147223
// an error is returned in the following situations:
148-
// - only one of name or namespace is provided
149-
// - the name or namespace parameter contains a token that cannot be resolved
224+
// - the nameTemplate or namespaceTemplate contains a token that cannot be resolved
150225
// - the resolved name is not a valid secret name
151226
// - the resolved namespace is not a valid namespace name
152-
func GetSecretReference(snapshotClassParams map[string]string, snapContentName string, snapshot *crdv1.VolumeSnapshot) (*v1.SecretReference, error) {
153-
nameTemplate, hasName := snapshotClassParams[snapshotterSecretNameKey]
154-
namespaceTemplate, hasNamespace := snapshotClassParams[snapshotterSecretNamespaceKey]
155-
156-
if !hasName && !hasNamespace {
157-
return nil, nil
227+
func getSecretReference(snapshotClassParams map[string]string, snapContentName string, snapshot *crdv1.VolumeSnapshot) (*v1.SecretReference, error) {
228+
nameTemplate, namespaceTemplate, err := verifyAndGetSecretNameAndNamespaceTemplate(snapshotterSecretParams, snapshotClassParams)
229+
if err != nil {
230+
return nil, fmt.Errorf("failed to get name and namespace template from params: %v", err)
158231
}
159232

160-
if len(nameTemplate) == 0 || len(namespaceTemplate) == 0 {
161-
return nil, fmt.Errorf("%s and %s parameters must be specified together", snapshotterSecretNameKey, snapshotterSecretNamespaceKey)
233+
if nameTemplate == "" && namespaceTemplate == "" {
234+
return nil, nil
162235
}
163236

164237
ref := &v1.SecretReference{}
@@ -174,15 +247,15 @@ func GetSecretReference(snapshotClassParams map[string]string, snapContentName s
174247

175248
resolvedNamespace, err := resolveTemplate(namespaceTemplate, namespaceParams)
176249
if err != nil {
177-
return nil, fmt.Errorf("error resolving %s value %q: %v", snapshotterSecretNamespaceKey, namespaceTemplate, err)
250+
return nil, fmt.Errorf("error resolving value %q: %v", namespaceTemplate, err)
178251
}
179252
glog.V(4).Infof("GetSecretReference namespaceTemplate %s, namespaceParams: %+v, resolved %s", namespaceTemplate, namespaceParams, resolvedNamespace)
180253

181254
if len(validation.IsDNS1123Label(resolvedNamespace)) > 0 {
182255
if namespaceTemplate != resolvedNamespace {
183-
return nil, fmt.Errorf("%s parameter %q resolved to %q which is not a valid namespace name", snapshotterSecretNamespaceKey, namespaceTemplate, resolvedNamespace)
256+
return nil, fmt.Errorf("%q resolved to %q which is not a valid namespace name", namespaceTemplate, resolvedNamespace)
184257
}
185-
return nil, fmt.Errorf("%s parameter %q is not a valid namespace name", snapshotterSecretNamespaceKey, namespaceTemplate)
258+
return nil, fmt.Errorf("%q is not a valid namespace name", namespaceTemplate)
186259
}
187260
ref.Namespace = resolvedNamespace
188261

@@ -199,13 +272,13 @@ func GetSecretReference(snapshotClassParams map[string]string, snapContentName s
199272
}
200273
resolvedName, err := resolveTemplate(nameTemplate, nameParams)
201274
if err != nil {
202-
return nil, fmt.Errorf("error resolving %s value %q: %v", snapshotterSecretNameKey, nameTemplate, err)
275+
return nil, fmt.Errorf("error resolving value %q: %v", nameTemplate, err)
203276
}
204277
if len(validation.IsDNS1123Subdomain(resolvedName)) > 0 {
205278
if nameTemplate != resolvedName {
206-
return nil, fmt.Errorf("%s parameter %q resolved to %q which is not a valid secret name", snapshotterSecretNameKey, nameTemplate, resolvedName)
279+
return nil, fmt.Errorf("%q resolved to %q which is not a valid secret name", nameTemplate, resolvedName)
207280
}
208-
return nil, fmt.Errorf("%s parameter %q is not a valid secret name", snapshotterSecretNameKey, nameTemplate)
281+
return nil, fmt.Errorf("%q is not a valid secret name", nameTemplate)
209282
}
210283
ref.Name = resolvedName
211284

@@ -229,8 +302,8 @@ func resolveTemplate(template string, params map[string]string) (string, error)
229302
return resolved, nil
230303
}
231304

232-
// GetCredentials retrieves credentials stored in v1.SecretReference
233-
func GetCredentials(k8s kubernetes.Interface, ref *v1.SecretReference) (map[string]string, error) {
305+
// getCredentials retrieves credentials stored in v1.SecretReference
306+
func getCredentials(k8s kubernetes.Interface, ref *v1.SecretReference) (map[string]string, error) {
234307
if ref == nil {
235308
return nil, nil
236309
}
@@ -271,3 +344,34 @@ func isSnapshotDeletionCandidate(snapshot *crdv1.VolumeSnapshot) bool {
271344
func needToAddSnapshotFinalizer(snapshot *crdv1.VolumeSnapshot) bool {
272345
return snapshot.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotFinalizer, nil)
273346
}
347+
348+
func deprecationWarning(deprecatedParam, newParam, removalVersion string) string {
349+
if removalVersion == "" {
350+
removalVersion = "a future release"
351+
}
352+
newParamPhrase := ""
353+
if len(newParam) != 0 {
354+
newParamPhrase = fmt.Sprintf(", please use \"%s\" instead", newParam)
355+
}
356+
return fmt.Sprintf("\"%s\" is deprecated and will be removed in %s%s", deprecatedParam, removalVersion, newParamPhrase)
357+
}
358+
359+
func removePrefixedParameters(param map[string]string) (map[string]string, error) {
360+
newParam := map[string]string{}
361+
for k, v := range param {
362+
if strings.HasPrefix(k, csiParameterPrefix) {
363+
// Check if its well known
364+
switch k {
365+
case prefixedSnapshotterSecretNameKey:
366+
case prefixedSnapshotterSecretNamespaceKey:
367+
default:
368+
return map[string]string{}, fmt.Errorf("found unknown parameter key \"%s\" with reserved namespace %s", k, csiParameterPrefix)
369+
}
370+
} else {
371+
// Don't strip, add this key-value to new map
372+
// Deprecated parameters prefixed with "csi" are not stripped to preserve backwards compatibility
373+
newParam[k] = v
374+
}
375+
}
376+
return newParam, nil
377+
}

0 commit comments

Comments
 (0)