Skip to content

Commit

Permalink
Add credentials to volume snapshot locations.
Browse files Browse the repository at this point in the history
This is analogous to the BSL creds work that was done previously, but for VSLs instead.

Signed-off-by: Scott Seago <sseago@redhat.com>
  • Loading branch information
sseago committed Apr 26, 2022
1 parent 40261dc commit 343bfd1
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 21 deletions.
18 changes: 18 additions & 0 deletions config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ spec:
type: string
description: Config is for provider-specific configuration fields.
type: object
credential:
description: Credential contains the credential information intended
to be used with this location
properties:
key:
description: The key of the secret to select from. Must be a
valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
provider:
description: Provider is the provider of the volume storage.
type: string
Expand Down
2 changes: 1 addition & 1 deletion config/crd/v1/crds/crds.go

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion pkg/apis/velero/v1/volume_snapshot_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ limitations under the License.

package v1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
import (
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down Expand Up @@ -55,6 +58,10 @@ type VolumeSnapshotLocationSpec struct {
// Config is for provider-specific configuration fields.
// +optional
Config map[string]string `json:"config,omitempty"`

// Credential contains the credential information intended to be used with this location
// +optional
Credential *corev1api.SecretKeySelector `json:"credential,omitempty"`
}

// VolumeSnapshotLocationPhase is the lifecycle phase of a Velero VolumeSnapshotLocation.
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/velero/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion pkg/builder/volume_snapshot_location_builder.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,8 @@ package builder
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

corev1api "k8s.io/api/core/v1"

velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
)

Expand Down Expand Up @@ -62,3 +64,9 @@ func (b *VolumeSnapshotLocationBuilder) Provider(name string) *VolumeSnapshotLoc
b.object.Spec.Provider = name
return b
}

// Credential sets the VolumeSnapshotLocation's credential selector.
func (b *VolumeSnapshotLocationBuilder) Credential(selector *corev1api.SecretKeySelector) *VolumeSnapshotLocationBuilder {
b.object.Spec.Credential = selector
return b
}
30 changes: 23 additions & 7 deletions pkg/cmd/cli/snapshotlocation/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/cmd"
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
Expand Down Expand Up @@ -54,22 +55,27 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command {
}

type CreateOptions struct {
Name string
Provider string
Config flag.Map
Labels flag.Map
Name string
Provider string
Config flag.Map
Labels flag.Map
Credential flag.Map
secretName string
secretKey string
}

func NewCreateOptions() *CreateOptions {
return &CreateOptions{
Config: flag.NewMap(),
Config: flag.NewMap(),
Credential: flag.NewMap(),
}
}

func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
flags.StringVar(&o.Provider, "provider", o.Provider, "Name of the volume snapshot provider (e.g. aws, azure, gcp).")
flags.Var(&o.Config, "config", "Configuration key-value pairs.")
flags.Var(&o.Labels, "labels", "Labels to apply to the volume snapshot location.")
flags.Var(&o.Credential, "credential", "The credential to be used by this location as a key-value pair, where the key is the Kubernetes Secret name, and the value is the data key name within the Secret. Optional, one value only.")
}

func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
Expand All @@ -81,6 +87,15 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto
return errors.New("--provider is required")
}

if len(o.Credential.Data()) > 1 {
return errors.New("--credential can only contain 1 key/value pair")
}

for k, v := range o.Credential.Data() {
o.secretName = k
o.secretKey = v
break
}
return nil
}

Expand All @@ -97,8 +112,9 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
Labels: o.Labels.Data(),
},
Spec: api.VolumeSnapshotLocationSpec{
Provider: o.Provider,
Config: o.Config.Data(),
Provider: o.Provider,
Config: o.Config.Data(),
Credential: builder.ForSecretKeySelector(o.secretName, o.secretKey).Result(),
},
}

Expand Down
24 changes: 18 additions & 6 deletions pkg/cmd/cli/snapshotlocation/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import (
kbclient "sigs.k8s.io/controller-runtime/pkg/client"

velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/cmd"
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
)

Expand All @@ -39,9 +41,6 @@ func NewSetCommand(f client.Factory, use string) *cobra.Command {
Use: use + " NAME",
Short: "Set specific features for a snapshot location",
Args: cobra.ExactArgs(1),
// Mark this command as hidden until more functionality is added
// as part of https://github.com/vmware-tanzu/velero/issues/2426
Hidden: true,
Run: func(c *cobra.Command, args []string) {
cmd.CheckError(o.Complete(args, f))
cmd.CheckError(o.Validate(c, args, f))
Expand All @@ -54,21 +53,29 @@ func NewSetCommand(f client.Factory, use string) *cobra.Command {
}

type SetOptions struct {
Name string
Name string
Credential flag.Map
}

func NewSetOptions() *SetOptions {
return &SetOptions{}
return &SetOptions{
Credential: flag.NewMap(),
}
}

func (o *SetOptions) BindFlags(*pflag.FlagSet) {
func (o *SetOptions) BindFlags(flags *pflag.FlagSet) {
flags.Var(&o.Credential, "credential", "Sets the credential to be used by this location as a key-value pair, where the key is the Kubernetes Secret name, and the value is the data key name within the Secret. Optional, one value only.")
}

func (o *SetOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
if err := output.ValidateFlags(c); err != nil {
return err
}

if len(o.Credential.Data()) > 1 {
return errors.New("--credential can only contain 1 key/value pair")
}

return nil
}

Expand All @@ -92,6 +99,11 @@ func (o *SetOptions) Run(c *cobra.Command, f client.Factory) error {
return errors.WithStack(err)
}

for name, key := range o.Credential.Data() {
location.Spec.Credential = builder.ForSecretKeySelector(name, key).Result()
break
}

if err := kbClient.Update(context.Background(), location, &kbclient.UpdateOptions{}); err != nil {
return errors.WithStack(err)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
csiVSCLister,
csiVSClassLister,
backupStoreGetter,
s.credentialFileStore,
)

return controllerRunInfo{
Expand Down Expand Up @@ -713,6 +714,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
s.logger,
podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()),
s.kubeClient.CoreV1().RESTClient(),
s.credentialFileStore,
)
cmd.CheckError(err)

Expand Down
13 changes: 13 additions & 0 deletions pkg/controller/backup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
snapshotv1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1"

"github.com/vmware-tanzu/velero/internal/credentials"
"github.com/vmware-tanzu/velero/internal/storage"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
Expand Down Expand Up @@ -89,6 +90,7 @@ type backupController struct {
volumeSnapshotLister snapshotv1listers.VolumeSnapshotLister
volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister
volumeSnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister
credentialFileStore credentials.FileStore
}

func NewBackupController(
Expand All @@ -112,6 +114,7 @@ func NewBackupController(
volumeSnapshotContentLister snapshotv1listers.VolumeSnapshotContentLister,
volumesnapshotClassLister snapshotv1listers.VolumeSnapshotClassLister,
backupStoreGetter persistence.ObjectBackupStoreGetter,
credentialStore credentials.FileStore,
) Interface {
c := &backupController{
genericController: newGenericController(Backup, logger),
Expand All @@ -135,6 +138,7 @@ func NewBackupController(
volumeSnapshotContentLister: volumeSnapshotContentLister,
volumeSnapshotClassLister: volumesnapshotClassLister,
backupStoreGetter: backupStoreGetter,
credentialFileStore: credentialStore,
}

c.syncHandler = c.processBackup
Expand Down Expand Up @@ -531,6 +535,15 @@ func (c *backupController) validateAndGetSnapshotLocations(backup *velerov1api.B
return nil, errors
}

// add credential to config for each location
for _, location := range providerLocations {
err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(location, c.credentialFileStore, c.logger)
if err != nil {
errors = append(errors, fmt.Sprintf("error adding credentials to volume snapshot location named %s: %v", location.Name, err))
continue
}
}

return providerLocations, nil
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/controller/backup_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -998,12 +998,15 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) {

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
formatFlag := logging.FormatText
var (
client = fake.NewSimpleClientset()
sharedInformers = informers.NewSharedInformerFactory(client, 0)
logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag)
)

c := &backupController{
genericController: newGenericController("backup-test", logger),
snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
defaultSnapshotLocations: test.defaultLocations,
}
Expand Down
11 changes: 9 additions & 2 deletions pkg/restore/pv_restorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"github.com/vmware-tanzu/velero/internal/credentials"
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
Expand All @@ -39,6 +40,7 @@ type pvRestorer struct {
volumeSnapshots []*volume.Snapshot
volumeSnapshotterGetter VolumeSnapshotterGetter
snapshotLocationLister listers.VolumeSnapshotLocationLister
credentialFileStore credentials.FileStore
}

func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
Expand All @@ -59,7 +61,7 @@ func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructu

log := r.logger.WithFields(logrus.Fields{"persistentVolume": pvName})

snapshotInfo, err := getSnapshotInfo(pvName, r.backup, r.volumeSnapshots, r.snapshotLocationLister)
snapshotInfo, err := getSnapshotInfo(pvName, r.backup, r.volumeSnapshots, r.snapshotLocationLister, r.credentialFileStore, r.logger)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -103,7 +105,7 @@ type snapshotInfo struct {
location *api.VolumeSnapshotLocation
}

func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volume.Snapshot, snapshotLocationLister listers.VolumeSnapshotLocationLister) (*snapshotInfo, error) {
func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volume.Snapshot, snapshotLocationLister listers.VolumeSnapshotLocationLister, credentialStore credentials.FileStore, logger logrus.FieldLogger) (*snapshotInfo, error) {
var pvSnapshot *volume.Snapshot
for _, snapshot := range volumeSnapshots {
if snapshot.Spec.PersistentVolumeName == pvName {
Expand All @@ -120,6 +122,11 @@ func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volum
if err != nil {
return nil, errors.WithStack(err)
}
// add credential to config
err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(loc, credentialStore, logger)
if err != nil {
return nil, errors.WithStack(err)
}

return &snapshotInfo{
providerSnapshotID: pvSnapshot.Status.ProviderSnapshotID,
Expand Down
11 changes: 8 additions & 3 deletions pkg/restore/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"

"github.com/vmware-tanzu/velero/internal/credentials"
"github.com/vmware-tanzu/velero/internal/hook"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/archive"
Expand Down Expand Up @@ -113,6 +114,7 @@ type kubernetesRestorer struct {
logger logrus.FieldLogger
podCommandExecutor podexec.PodCommandExecutor
podGetter cache.Getter
credentialFileStore credentials.FileStore
}

// NewKubernetesRestorer creates a new kubernetesRestorer.
Expand All @@ -128,6 +130,7 @@ func NewKubernetesRestorer(
logger logrus.FieldLogger,
podCommandExecutor podexec.PodCommandExecutor,
podGetter cache.Getter,
credentialStore credentials.FileStore,
) (Restorer, error) {
return &kubernetesRestorer{
restoreClient: restoreClient,
Expand All @@ -147,9 +150,10 @@ func NewKubernetesRestorer(
veleroCloneName := "velero-clone-" + veleroCloneUuid.String()
return veleroCloneName, nil
},
fileSystem: filesystem.NewFileSystem(),
podCommandExecutor: podCommandExecutor,
podGetter: podGetter,
fileSystem: filesystem.NewFileSystem(),
podCommandExecutor: podCommandExecutor,
podGetter: podGetter,
credentialFileStore: credentialStore,
}, nil
}

Expand Down Expand Up @@ -254,6 +258,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers(
volumeSnapshots: req.VolumeSnapshots,
volumeSnapshotterGetter: volumeSnapshotterGetter,
snapshotLocationLister: snapshotLocationLister,
credentialFileStore: kr.credentialFileStore,
}

restoreCtx := &restoreContext{
Expand Down
Loading

0 comments on commit 343bfd1

Please sign in to comment.