Handle Secrets in CreateSnapshot
This PR adds handling for Secrets in CreateSnapshot.
This commit is contained in:
@@ -23,7 +23,11 @@ import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -121,3 +125,118 @@ func IsDefaultAnnotation(obj metav1.ObjectMeta) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 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.
|
||||
// if neither the name or namespace parameter are set, a nil reference and no error is returned.
|
||||
// no lookup of the referenced secret is performed, and the secret may or may not exist.
|
||||
//
|
||||
// supported tokens for name resolution:
|
||||
// - ${volumesnapshotcontent.name}
|
||||
// - ${volumesnapshot.namespace}
|
||||
// - ${volumesnapshot.name}
|
||||
// - ${volumesnapshot.annotations['ANNOTATION_KEY']} (e.g. ${pvc.annotations['example.com/snapshot-create-secret-name']})
|
||||
//
|
||||
// supported tokens for namespace resolution:
|
||||
// - ${volumesnapshotcontent.name}
|
||||
// - ${volumesnapshot.namespace}
|
||||
//
|
||||
// an error is returned in the following situations:
|
||||
// - only one of name or namespace is provided
|
||||
// - the name or namespace parameter contains a token that cannot be resolved
|
||||
// - the resolved name is not a valid secret name
|
||||
// - the resolved namespace is not a valid namespace name
|
||||
func GetSecretReference(nameKey, namespaceKey string, snapshotClassParams map[string]string, snapContentName string, snapshot *crdv1.VolumeSnapshot) (*v1.SecretReference, error) {
|
||||
nameTemplate, hasName := snapshotClassParams[nameKey]
|
||||
namespaceTemplate, hasNamespace := snapshotClassParams[namespaceKey]
|
||||
|
||||
if !hasName && !hasNamespace {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(nameTemplate) == 0 || len(namespaceTemplate) == 0 {
|
||||
return nil, fmt.Errorf("%s and %s parameters must be specified together", nameKey, namespaceKey)
|
||||
}
|
||||
|
||||
ref := &v1.SecretReference{}
|
||||
|
||||
{
|
||||
// Secret namespace template can make use of the VolumeSnapshotContent name or the VolumeSnapshot namespace.
|
||||
// Note that neither of those things are under the control of the VolumeSnapshot user.
|
||||
namespaceParams := map[string]string{"volumesnapshotcontent.name": snapContentName}
|
||||
if snapshot != nil {
|
||||
namespaceParams["volumesnapshot.namespace"] = snapshot.Namespace
|
||||
}
|
||||
|
||||
resolvedNamespace, err := resolveTemplate(namespaceTemplate, namespaceParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving %s value %q: %v", namespaceKey, namespaceTemplate, err)
|
||||
}
|
||||
if len(validation.IsDNS1123Label(resolvedNamespace)) > 0 {
|
||||
if namespaceTemplate != resolvedNamespace {
|
||||
return nil, fmt.Errorf("%s parameter %q resolved to %q which is not a valid namespace name", namespaceKey, namespaceTemplate, resolvedNamespace)
|
||||
}
|
||||
return nil, fmt.Errorf("%s parameter %q is not a valid namespace name", namespaceKey, namespaceTemplate)
|
||||
}
|
||||
ref.Namespace = resolvedNamespace
|
||||
}
|
||||
|
||||
{
|
||||
// Secret name template can make use of the VolumeSnapshotContent name, VolumeSnapshot name or namespace,
|
||||
// or a VolumeSnapshot annotation.
|
||||
// Note that VolumeSnapshot name and annotations are under the VolumeSnapshot user's control.
|
||||
nameParams := map[string]string{"volumesnapshotcontent.name": snapContentName}
|
||||
if snapshot != nil {
|
||||
nameParams["volumesnapshot.name"] = snapshot.Name
|
||||
nameParams["volumesnapshot.namespace"] = snapshot.Namespace
|
||||
for k, v := range snapshot.Annotations {
|
||||
nameParams["volumesnapshot.annotations['"+k+"']"] = v
|
||||
}
|
||||
}
|
||||
resolvedName, err := resolveTemplate(nameTemplate, nameParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving %s value %q: %v", nameKey, nameTemplate, err)
|
||||
}
|
||||
if len(validation.IsDNS1123Subdomain(resolvedName)) > 0 {
|
||||
if nameTemplate != resolvedName {
|
||||
return nil, fmt.Errorf("%s parameter %q resolved to %q which is not a valid secret name", nameKey, nameTemplate, resolvedName)
|
||||
}
|
||||
return nil, fmt.Errorf("%s parameter %q is not a valid secret name", nameKey, nameTemplate)
|
||||
}
|
||||
ref.Name = resolvedName
|
||||
}
|
||||
|
||||
glog.V(4).Infof("GetSecretReference validated Secret: %+v", ref)
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func resolveTemplate(template string, params map[string]string) (string, error) {
|
||||
missingParams := sets.NewString()
|
||||
resolved := os.Expand(template, func(k string) string {
|
||||
v, ok := params[k]
|
||||
if !ok {
|
||||
missingParams.Insert(k)
|
||||
}
|
||||
return v
|
||||
})
|
||||
if missingParams.Len() > 0 {
|
||||
return "", fmt.Errorf("invalid tokens: %q", missingParams.List())
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
func GetCredentials(k8s kubernetes.Interface, ref *v1.SecretReference) (map[string]string, error) {
|
||||
if ref == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
secret, err := k8s.CoreV1().Secrets(ref.Namespace).Get(ref.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting secret %s in namespace %s: %v", ref.Name, ref.Namespace, err)
|
||||
}
|
||||
|
||||
credentials := map[string]string{}
|
||||
for key, value := range secret.Data {
|
||||
credentials[key] = string(value)
|
||||
}
|
||||
return credentials, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user