Merge pull request #9 from xing-yang/snapshot_controller_snapclass
Address review comments
This commit is contained in:
@@ -15,7 +15,6 @@ package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
crdv1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1"
|
||||
@@ -25,7 +24,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
@@ -122,18 +120,3 @@ func CreateCRD(clientset apiextensionsclient.Interface) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForSnapshotResource waits for the snapshot resource
|
||||
func WaitForSnapshotResource(snapshotClient *rest.RESTClient) error {
|
||||
return wait.Poll(100*time.Millisecond, 60*time.Second, func() (bool, error) {
|
||||
_, err := snapshotClient.Get().
|
||||
Resource(crdv1.VolumeSnapshotContentResourcePlural).DoRaw()
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if apierrors.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
})
|
||||
}
|
||||
|
@@ -47,7 +47,7 @@ const (
|
||||
|
||||
// Command line flags
|
||||
var (
|
||||
snapshotter = flag.String("snapshotter", "", "Name of the snapshotter. The snapshotter will only create snapshot data for snapshot that request a StorageClass with a snapshotter field set equal to this name.")
|
||||
snapshotter = flag.String("snapshotter", "", "Name of the snapshotter. The snapshotter will only create snapshot content for snapshot that requests a VolumeSnapshotClass with a snapshotter field set equal to this name.")
|
||||
kubeconfig = flag.String("kubeconfig", "", "Absolute path to the kubeconfig file. Required only when running out of cluster.")
|
||||
resync = flag.Duration("resync", 10*time.Second, "Resync interval of the controller.")
|
||||
connectionTimeout = flag.Duration("connection-timeout", 1*time.Minute, "Timeout for waiting for CSI driver socket.")
|
||||
@@ -56,7 +56,7 @@ var (
|
||||
createSnapshotContentInterval = flag.Duration("createSnapshotContentInterval", 10*time.Second, "Interval between retries when we create a snapshot data object for a snapshot.")
|
||||
resyncPeriod = flag.Duration("resyncPeriod", 60*time.Second, "The period that should be used to re-sync the snapshot.")
|
||||
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")
|
||||
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.")
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -105,10 +105,20 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Find driver name.
|
||||
// Pass a context with a timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
|
||||
defer cancel()
|
||||
|
||||
// Find driver name
|
||||
if snapshotter == nil {
|
||||
*snapshotter, err = csiConn.GetDriverName(ctx)
|
||||
if err != nil {
|
||||
glog.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
glog.V(2).Infof("CSI driver name: %q", *snapshotter)
|
||||
|
||||
// Check it's ready
|
||||
if err = waitForDriverReady(csiConn, *connectionTimeout); err != nil {
|
||||
glog.Error(err.Error())
|
||||
@@ -122,11 +132,11 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
if !supportsCreateSnapshot {
|
||||
glog.Error("CSI driver does not support ControllerCreateSnapshot")
|
||||
glog.Errorf("CSI driver %s does not support ControllerCreateSnapshot", *snapshotter)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
glog.V(2).Infof("Start NewCSISnapshotController with snapshotter %s", *snapshotter)
|
||||
glog.V(2).Infof("Start NewCSISnapshotController with snapshotter [%s] kubeconfig [%s] resync [%+v] connectionTimeout [%+v] csiAddress [%s] createSnapshotContentRetryCount [%d] createSnapshotContentInterval [%+v] resyncPeriod [%+v] snapshotNamePrefix [%s] snapshotNameUUIDLength [%d]", *snapshotter, *kubeconfig, *resync, *connectionTimeout, *csiAddress, createSnapshotContentRetryCount, *createSnapshotContentInterval, *resyncPeriod, *snapshotNamePrefix, snapshotNameUUIDLength)
|
||||
|
||||
ctrl := controller.NewCSISnapshotController(
|
||||
snapClient,
|
||||
|
@@ -80,6 +80,10 @@ type VolumeSnapshotSpec struct {
|
||||
// be used if it is available.
|
||||
// +optional
|
||||
VolumeSnapshotClassName string `json:"snapshotClassName" protobuf:"bytes,3,opt,name=snapshotClassName"`
|
||||
|
||||
// A description of the volume snapshot's resources and size.
|
||||
// +optional
|
||||
Size core_v1.ResourceList `json:"size,omitempty" protobuf:"bytes,4,rep,name=size,casttype=ResourceList,castkey=ResourceName"`
|
||||
}
|
||||
|
||||
// VolumeSnapshotStatus is the status of the VolumeSnapshot
|
||||
@@ -194,6 +198,11 @@ type VolumeSnapshotContentSpec struct {
|
||||
// taken from. It becomes non-nil when VolumeSnapshot and VolumeSnapshotContent are bound.
|
||||
// +optional
|
||||
PersistentVolumeRef *core_v1.ObjectReference `json:"persistentVolumeRef" protobuf:"bytes,3,opt,name=persistentVolumeRef"`
|
||||
|
||||
// Name of the VolumeSnapshotClass used by the VolumeSnapshot. If not specified, a default snapshot class will
|
||||
// be used if it is available.
|
||||
// +optional
|
||||
VolumeSnapshotClassName string `json:"snapshotClassName" protobuf:"bytes,4,opt,name=snapshotClassName"`
|
||||
}
|
||||
|
||||
// VolumeSnapshotSource represents the actual location and type of the snapshot. Only one of its members may be specified.
|
||||
@@ -206,19 +215,26 @@ type VolumeSnapshotSource struct {
|
||||
// Represents the source from CSI volume snapshot
|
||||
type CSIVolumeSnapshotSource struct {
|
||||
// Driver is the name of the driver to use for this snapshot.
|
||||
// This MUST be the same name returned by the CSI GetPluginName() call for
|
||||
// that driver.
|
||||
// Required.
|
||||
Driver string `json:"driver"`
|
||||
Driver string `json:"driver" protobuf:"bytes,1,opt,name=driver"`
|
||||
|
||||
// SnapshotHandle is the unique snapshot id returned by the CSI volume
|
||||
// plugin’s CreateSnapshot to refer to the snapshot on all subsequent calls.
|
||||
// Required.
|
||||
SnapshotHandle string `json:"snapshotHandle"`
|
||||
SnapshotHandle string `json:"snapshotHandle" protobuf:"bytes,2,opt,name=snapshotHandle"`
|
||||
|
||||
// Timestamp when the point-in-time snapshot is taken on the storage
|
||||
// system. This timestamp will be generated by the CSI volume driver after
|
||||
// the snapshot is cut. The format of this field should be a Unix nanoseconds
|
||||
// time encoded as an int64. On Unix, the command `date +%s%N` returns
|
||||
// the current time in nanoseconds since 1970-01-01 00:00:00 UTC.
|
||||
// This field is REQUIRED.
|
||||
// This field is required in the CSI spec but optional here to support static binding.
|
||||
// +optional
|
||||
CreatedAt int64 `json:"createdAt,omitempty" protobuf:"varint,3,opt,name=createdAt"`
|
||||
|
||||
// A description of the volume snapshot's resources and size.
|
||||
// +optional
|
||||
Size core_v1.ResourceList `json:"size,omitempty" protobuf:"bytes,4,rep,name=size,casttype=ResourceList,castkey=ResourceName"`
|
||||
}
|
||||
|
@@ -29,6 +29,13 @@ import (
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CSIVolumeSnapshotSource) DeepCopyInto(out *CSIVolumeSnapshotSource) {
|
||||
*out = *in
|
||||
if in.Size != nil {
|
||||
in, out := &in.Size, &out.Size
|
||||
*out = make(v1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -278,7 +285,7 @@ func (in *VolumeSnapshotSource) DeepCopyInto(out *VolumeSnapshotSource) {
|
||||
if in.CSI != nil {
|
||||
in, out := &in.CSI, &out.CSI
|
||||
*out = new(CSIVolumeSnapshotSource)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -301,6 +308,13 @@ func (in *VolumeSnapshotSpec) DeepCopyInto(out *VolumeSnapshotSpec) {
|
||||
*out = new(TypedLocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.Size != nil {
|
||||
in, out := &in.Size, &out.Size
|
||||
*out = make(v1.ResourceList, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -50,7 +50,7 @@ type CSIConnection interface {
|
||||
CreateSnapshot(ctx context.Context, snapshotName string, snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (driverName string, snapshotId string, timestamp int64, status *csi.SnapshotStatus, err error)
|
||||
|
||||
// DeleteSnapshot deletes a snapshot from a volume
|
||||
DeleteSnapshot(ctx context.Context, snapshotID string) (err error)
|
||||
DeleteSnapshot(ctx context.Context, snapshotID string, snapshotterCredentials map[string]string) (err error)
|
||||
|
||||
// GetSnapshotStatus lists snapshot from a volume
|
||||
GetSnapshotStatus(ctx context.Context, snapshotID string) (*csi.SnapshotStatus, int64, error)
|
||||
@@ -218,12 +218,12 @@ func (c *csiConnection) CreateSnapshot(ctx context.Context, snapshotName string,
|
||||
return driverName, rsp.Snapshot.Id, rsp.Snapshot.CreatedAt, rsp.Snapshot.Status, nil
|
||||
}
|
||||
|
||||
func (c *csiConnection) DeleteSnapshot(ctx context.Context, snapshotID string) (err error) {
|
||||
func (c *csiConnection) DeleteSnapshot(ctx context.Context, snapshotID string, snapshotterCredentials map[string]string) (err error) {
|
||||
client := csi.NewControllerClient(c.conn)
|
||||
|
||||
req := csi.DeleteSnapshotRequest{
|
||||
SnapshotId: snapshotID,
|
||||
DeleteSnapshotSecrets: nil,
|
||||
DeleteSnapshotSecrets: snapshotterCredentials,
|
||||
}
|
||||
|
||||
if _, err := client.DeleteSnapshot(ctx, &req); err != nil {
|
||||
|
@@ -31,7 +31,7 @@ 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, *csi.SnapshotStatus, error)
|
||||
DeleteSnapshot(content *crdv1.VolumeSnapshotContent) error
|
||||
DeleteSnapshot(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) error
|
||||
GetSnapshotStatus(content *crdv1.VolumeSnapshotContent) (*csi.SnapshotStatus, int64, error)
|
||||
}
|
||||
|
||||
@@ -69,14 +69,14 @@ func (handler *csiHandler) CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume
|
||||
return handler.csiConnection.CreateSnapshot(ctx, snapshotName, snapshot, volume, parameters, snapshotterCredentials)
|
||||
}
|
||||
|
||||
func (handler *csiHandler) DeleteSnapshot(content *crdv1.VolumeSnapshotContent) error {
|
||||
func (handler *csiHandler) DeleteSnapshot(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) error {
|
||||
if content.Spec.CSI == nil {
|
||||
return fmt.Errorf("CSISnapshot not defined in spec")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), handler.timeout)
|
||||
defer cancel()
|
||||
|
||||
err := handler.csiConnection.DeleteSnapshot(ctx, content.Spec.CSI.SnapshotHandle)
|
||||
err := handler.csiConnection.DeleteSnapshot(ctx, content.Spec.CSI.SnapshotHandle, snapshotterCredentials)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete snapshot data %s: %q", content.Name, err)
|
||||
}
|
||||
|
@@ -81,7 +81,7 @@ const snapshotterSecretNameKey = "csiSnapshotterSecretName"
|
||||
const snapshotterSecretNamespaceKey = "csiSnapshotterSecretNamespace"
|
||||
|
||||
// syncContent deals with one key off the queue. It returns false when it's time to quit.
|
||||
func (ctrl *CSISnapshotController) syncContent(content *crdv1.VolumeSnapshotContent) error {
|
||||
func (ctrl *csiSnapshotController) syncContent(content *crdv1.VolumeSnapshotContent) error {
|
||||
glog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]", content.Name)
|
||||
|
||||
// VolumeSnapshotContent is not bind to any VolumeSnapshot, this case rare and we just return err
|
||||
@@ -134,7 +134,7 @@ func (ctrl *CSISnapshotController) syncContent(content *crdv1.VolumeSnapshotCont
|
||||
// created, updated or periodically synced. We do not differentiate between
|
||||
// these events.
|
||||
// For easier readability, it is split into syncCompleteSnapshot and syncUncompleteSnapshot
|
||||
func (ctrl *CSISnapshotController) syncSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
func (ctrl *csiSnapshotController) syncSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
glog.V(4).Infof("synchonizing VolumeSnapshot[%s]: %s", snapshotKey(snapshot), getSnapshotStatusForLogging(snapshot))
|
||||
|
||||
if !snapshot.Status.Ready {
|
||||
@@ -146,7 +146,7 @@ func (ctrl *CSISnapshotController) syncSnapshot(snapshot *crdv1.VolumeSnapshot)
|
||||
|
||||
// syncCompleteSnapshot checks the snapshot which has been bound to snapshot content succesfully before.
|
||||
// If there is any problem with the binding (e.g., snapshot points to a non-exist snapshot content), update the snapshot status and emit event.
|
||||
func (ctrl *CSISnapshotController) syncBoundSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
func (ctrl *csiSnapshotController) syncBoundSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
if snapshot.Spec.SnapshotContentName == "" {
|
||||
if _, err := ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotLost", "Bound snapshot has lost reference to VolumeSnapshotContent"); err != nil {
|
||||
return err
|
||||
@@ -184,7 +184,7 @@ func (ctrl *CSISnapshotController) syncBoundSnapshot(snapshot *crdv1.VolumeSnaps
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) syncUnboundSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
func (ctrl *csiSnapshotController) syncUnboundSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
uniqueSnapshotName := snapshotKey(snapshot)
|
||||
glog.V(4).Infof("syncSnapshot %s", uniqueSnapshotName)
|
||||
|
||||
@@ -241,7 +241,7 @@ func (ctrl *CSISnapshotController) syncUnboundSnapshot(snapshot *crdv1.VolumeSna
|
||||
}
|
||||
|
||||
// getMatchSnapshotContent looks up VolumeSnapshotContent for a VolumeSnapshot named snapshotName
|
||||
func (ctrl *CSISnapshotController) getMatchSnapshotContent(vs *crdv1.VolumeSnapshot) *crdv1.VolumeSnapshotContent {
|
||||
func (ctrl *csiSnapshotController) getMatchSnapshotContent(vs *crdv1.VolumeSnapshot) *crdv1.VolumeSnapshotContent {
|
||||
var snapshotDataObj *crdv1.VolumeSnapshotContent
|
||||
var found bool
|
||||
|
||||
@@ -267,7 +267,7 @@ func (ctrl *CSISnapshotController) getMatchSnapshotContent(vs *crdv1.VolumeSnaps
|
||||
}
|
||||
|
||||
// deleteSnapshotContent starts delete action.
|
||||
func (ctrl *CSISnapshotController) deleteSnapshotContent(content *crdv1.VolumeSnapshotContent) {
|
||||
func (ctrl *csiSnapshotController) deleteSnapshotContent(content *crdv1.VolumeSnapshotContent) {
|
||||
operationName := fmt.Sprintf("delete-%s[%s]", content.Name, string(content.UID))
|
||||
glog.V(4).Infof("Snapshotter is about to delete volume snapshot and the operation named %s", operationName)
|
||||
ctrl.scheduleOperation(operationName, func() error {
|
||||
@@ -277,7 +277,7 @@ func (ctrl *CSISnapshotController) deleteSnapshotContent(content *crdv1.VolumeSn
|
||||
|
||||
// scheduleOperation starts given asynchronous operation on given volume. It
|
||||
// makes sure the operation is already not running.
|
||||
func (ctrl *CSISnapshotController) scheduleOperation(operationName string, operation func() error) {
|
||||
func (ctrl *csiSnapshotController) scheduleOperation(operationName string, operation func() error) {
|
||||
glog.V(4).Infof("scheduleOperation[%s]", operationName)
|
||||
|
||||
err := ctrl.runningOperations.Run(operationName, operation)
|
||||
@@ -293,16 +293,16 @@ func (ctrl *CSISnapshotController) scheduleOperation(operationName string, opera
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) storeSnapshotUpdate(vs interface{}) (bool, error) {
|
||||
func (ctrl *csiSnapshotController) storeSnapshotUpdate(vs interface{}) (bool, error) {
|
||||
return storeObjectUpdate(ctrl.snapshotStore, vs, "vs")
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) storeContentUpdate(content interface{}) (bool, error) {
|
||||
func (ctrl *csiSnapshotController) storeContentUpdate(content interface{}) (bool, error) {
|
||||
return storeObjectUpdate(ctrl.contentStore, content, "content")
|
||||
}
|
||||
|
||||
// createSnapshot starts new asynchronous operation to create snapshot data for snapshot
|
||||
func (ctrl *CSISnapshotController) createSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
func (ctrl *csiSnapshotController) createSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
glog.V(4).Infof("createSnapshot[%s]: started", snapshotKey(snapshot))
|
||||
opName := fmt.Sprintf("create-%s[%s]", snapshotKey(snapshot), string(snapshot.UID))
|
||||
ctrl.scheduleOperation(opName, func() error {
|
||||
@@ -323,7 +323,7 @@ func (ctrl *CSISnapshotController) createSnapshot(snapshot *crdv1.VolumeSnapshot
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) checkandUpdateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) error {
|
||||
func (ctrl *csiSnapshotController) checkandUpdateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) error {
|
||||
glog.V(5).Infof("checkandUpdateSnapshotStatus[%s] started", snapshotKey(snapshot))
|
||||
opName := fmt.Sprintf("check-%s[%s]", snapshotKey(snapshot), string(snapshot.UID))
|
||||
ctrl.scheduleOperation(opName, func() error {
|
||||
@@ -349,7 +349,7 @@ func (ctrl *CSISnapshotController) checkandUpdateSnapshotStatus(snapshot *crdv1.
|
||||
// Parameters:
|
||||
// snapshot - snapshot to update
|
||||
// eventtype, reason, message - event to send, see EventRecorder.Event()
|
||||
func (ctrl *CSISnapshotController) updateSnapshotErrorStatusWithEvent(snapshot *crdv1.VolumeSnapshot, eventtype, reason, message string) (*crdv1.VolumeSnapshot, error) {
|
||||
func (ctrl *csiSnapshotController) updateSnapshotErrorStatusWithEvent(snapshot *crdv1.VolumeSnapshot, eventtype, reason, message string) (*crdv1.VolumeSnapshot, error) {
|
||||
glog.V(4).Infof("updateSnapshotStatusWithEvent[%s]", snapshotKey(snapshot))
|
||||
|
||||
if snapshot.Status.Error != nil && snapshot.Status.Ready == false {
|
||||
@@ -384,7 +384,7 @@ func (ctrl *CSISnapshotController) updateSnapshotErrorStatusWithEvent(snapshot *
|
||||
return newSnapshot, nil
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) updateSnapshotBoundWithEvent(snapshot *crdv1.VolumeSnapshot, eventtype, reason, message string) (*crdv1.VolumeSnapshot, error) {
|
||||
func (ctrl *csiSnapshotController) updateSnapshotBoundWithEvent(snapshot *crdv1.VolumeSnapshot, eventtype, reason, message string) (*crdv1.VolumeSnapshot, error) {
|
||||
glog.V(4).Infof("updateSnapshotBoundWithEvent[%s]", snapshotKey(snapshot))
|
||||
if snapshot.Status.Ready && snapshot.Status.Error == nil {
|
||||
// Nothing to do.
|
||||
@@ -425,7 +425,7 @@ func IsSnapshotBound(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapsh
|
||||
return false
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) bindSnapshotContent(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) error {
|
||||
func (ctrl *csiSnapshotController) bindSnapshotContent(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) error {
|
||||
if content.Spec.VolumeSnapshotRef == nil || content.Spec.VolumeSnapshotRef.Name != snapshot.Name {
|
||||
return fmt.Errorf("Could not bind snapshot %s and content %s, the VolumeSnapshotRef does not match", snapshot.Name, content.Name)
|
||||
} else if content.Spec.VolumeSnapshotRef.UID != "" && content.Spec.VolumeSnapshotRef.UID != snapshot.UID {
|
||||
@@ -447,7 +447,7 @@ func (ctrl *CSISnapshotController) bindSnapshotContent(snapshot *crdv1.VolumeSna
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) checkandUpdateSnapshotStatusOperation(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshot, error) {
|
||||
func (ctrl *csiSnapshotController) checkandUpdateSnapshotStatusOperation(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshot, error) {
|
||||
status, _, err := ctrl.handler.GetSnapshotStatus(content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check snapshot status %s with error %v", snapshot.Name, err)
|
||||
@@ -465,7 +465,7 @@ func (ctrl *CSISnapshotController) checkandUpdateSnapshotStatusOperation(snapsho
|
||||
// 2. Update VolumeSnapshot status with creationtimestamp information
|
||||
// 3. Create the VolumeSnapshotContent object with the snapshot id information.
|
||||
// 4. Bind the VolumeSnapshot and VolumeSnapshotContent object
|
||||
func (ctrl *CSISnapshotController) createSnapshotOperation(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
|
||||
func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
|
||||
glog.Infof("createSnapshot: Creating snapshot %s through the plugin ...", snapshotKey(snapshot))
|
||||
|
||||
class, err := ctrl.GetClassFromVolumeSnapshot(snapshot)
|
||||
@@ -483,7 +483,7 @@ func (ctrl *CSISnapshotController) createSnapshotOperation(snapshot *crdv1.Volum
|
||||
contentName := GetSnapshotContentNameForSnapshot(snapshot)
|
||||
|
||||
// Resolve snapshotting secret credentials.
|
||||
snapshotterSecretRef, err := GetSecretReference(snapshotterSecretNameKey, snapshotterSecretNamespaceKey, class.Parameters, contentName, nil)
|
||||
snapshotterSecretRef, err := GetSecretReference(snapshotterSecretNameKey, snapshotterSecretNamespaceKey, class.Parameters, contentName, snapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -574,10 +574,28 @@ func (ctrl *CSISnapshotController) createSnapshotOperation(snapshot *crdv1.Volum
|
||||
// 3. Delete the SnapshotContent object
|
||||
// 4. Remove the Snapshot from vsStore
|
||||
// 5. Finish
|
||||
func (ctrl *CSISnapshotController) DeleteSnapshotContentOperation(content *crdv1.VolumeSnapshotContent) error {
|
||||
func (ctrl *csiSnapshotController) DeleteSnapshotContentOperation(content *crdv1.VolumeSnapshotContent) error {
|
||||
glog.V(4).Infof("deleteSnapshotOperation [%s] started", content.Name)
|
||||
|
||||
err := ctrl.handler.DeleteSnapshot(content)
|
||||
// get secrets if VolumeSnapshotClass specifies it
|
||||
var snapshotterCredentials map[string]string
|
||||
snapshotClassName := content.Spec.VolumeSnapshotClassName
|
||||
if len(snapshotClassName) != 0 {
|
||||
if snapshotClass, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshotClasses().Get(snapshotClassName, metav1.GetOptions{}); err == nil {
|
||||
// Resolve snapshotting secret credentials.
|
||||
// No VolumeSnapshot is provided when resolving delete secret names, since the VolumeSnapshot may or may not exist at delete time.
|
||||
snapshotterSecretRef, err := GetSecretReference(snapshotterSecretNameKey, snapshotterSecretNamespaceKey, snapshotClass.Parameters, content.Name, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshotterCredentials, err = GetCredentials(ctrl.client, snapshotterSecretRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := ctrl.handler.DeleteSnapshot(content, snapshotterCredentials)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete snapshot %#v, err: %v", content.Name, err)
|
||||
}
|
||||
@@ -590,7 +608,7 @@ func (ctrl *CSISnapshotController) DeleteSnapshotContentOperation(content *crdv1
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) bindandUpdateVolumeSnapshot(snapshotContent *crdv1.VolumeSnapshotContent, snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
|
||||
func (ctrl *csiSnapshotController) bindandUpdateVolumeSnapshot(snapshotContent *crdv1.VolumeSnapshotContent, snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
|
||||
glog.V(4).Infof("bindandUpdateVolumeSnapshot for snapshot [%s]: snapshotContent [%s]", snapshot.Name, snapshotContent.Name)
|
||||
snapshotObj, err := ctrl.clientset.VolumesnapshotV1alpha1().VolumeSnapshots(snapshot.Namespace).Get(snapshot.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
@@ -622,7 +640,7 @@ func (ctrl *CSISnapshotController) bindandUpdateVolumeSnapshot(snapshotContent *
|
||||
}
|
||||
|
||||
// UpdateSnapshotStatus converts snapshot status to crdv1.VolumeSnapshotCondition
|
||||
func (ctrl *CSISnapshotController) updateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, csistatus *csi.SnapshotStatus, timestamp time.Time, bound bool) (*crdv1.VolumeSnapshot, error) {
|
||||
func (ctrl *csiSnapshotController) updateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, csistatus *csi.SnapshotStatus, timestamp time.Time, bound bool) (*crdv1.VolumeSnapshot, error) {
|
||||
glog.V(4).Infof("updating VolumeSnapshot[]%s, set status %v, timestamp %v", snapshotKey(snapshot), csistatus, timestamp)
|
||||
status := snapshot.Status
|
||||
change := false
|
||||
@@ -668,7 +686,7 @@ func (ctrl *CSISnapshotController) updateSnapshotStatus(snapshot *crdv1.VolumeSn
|
||||
}
|
||||
|
||||
// getVolumeFromVolumeSnapshot is a helper function to get PV from VolumeSnapshot.
|
||||
func (ctrl *CSISnapshotController) getVolumeFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*v1.PersistentVolume, error) {
|
||||
func (ctrl *csiSnapshotController) getVolumeFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*v1.PersistentVolume, error) {
|
||||
pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -686,7 +704,7 @@ func (ctrl *CSISnapshotController) getVolumeFromVolumeSnapshot(snapshot *crdv1.V
|
||||
}
|
||||
|
||||
// GetClassFromVolumeSnapshot is a helper function to get storage class from VolumeSnapshot.
|
||||
func (ctrl *CSISnapshotController) GetClassFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshotClass, error) {
|
||||
func (ctrl *csiSnapshotController) GetClassFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshotClass, error) {
|
||||
className := snapshot.Spec.VolumeSnapshotClassName
|
||||
glog.V(5).Infof("getClassFromVolumeSnapshot [%s]: VolumeSnapshotClassName [%s]", snapshot.Name, className)
|
||||
if len(className) > 0 {
|
||||
@@ -734,7 +752,7 @@ func (ctrl *CSISnapshotController) GetClassFromVolumeSnapshot(snapshot *crdv1.Vo
|
||||
}
|
||||
|
||||
// getClaimFromVolumeSnapshot is a helper function to get PV from VolumeSnapshot.
|
||||
func (ctrl *CSISnapshotController) getClaimFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*v1.PersistentVolumeClaim, error) {
|
||||
func (ctrl *csiSnapshotController) getClaimFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*v1.PersistentVolumeClaim, error) {
|
||||
if snapshot.Spec.Source == nil || snapshot.Spec.Source.Kind != pvcKind {
|
||||
return nil, fmt.Errorf("The snapshot source is not the right type. Expected %s, Got %v", pvcKind, snapshot.Spec.Source)
|
||||
}
|
@@ -39,7 +39,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/goroutinemap"
|
||||
)
|
||||
|
||||
type CSISnapshotController struct {
|
||||
type csiSnapshotController struct {
|
||||
clientset clientset.Interface
|
||||
client kubernetes.Interface
|
||||
snapshotterName string
|
||||
@@ -66,7 +66,7 @@ type CSISnapshotController struct {
|
||||
resyncPeriod time.Duration
|
||||
}
|
||||
|
||||
// NewCSISnapshotController returns a new *CSISnapshotController
|
||||
// NewCSISnapshotController returns a new *csiSnapshotController
|
||||
func NewCSISnapshotController(
|
||||
clientset clientset.Interface,
|
||||
client kubernetes.Interface,
|
||||
@@ -81,13 +81,13 @@ func NewCSISnapshotController(
|
||||
resyncPeriod time.Duration,
|
||||
snapshotNamePrefix string,
|
||||
snapshotNameUUIDLength int,
|
||||
) *CSISnapshotController {
|
||||
) *csiSnapshotController {
|
||||
broadcaster := record.NewBroadcaster()
|
||||
broadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: client.Core().Events(v1.NamespaceAll)})
|
||||
var eventRecorder record.EventRecorder
|
||||
eventRecorder = broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: fmt.Sprintf("csi-snapshotter %s", snapshotterName)})
|
||||
|
||||
ctrl := &CSISnapshotController{
|
||||
ctrl := &csiSnapshotController{
|
||||
clientset: clientset,
|
||||
client: client,
|
||||
snapshotterName: snapshotterName,
|
||||
@@ -131,7 +131,7 @@ func NewCSISnapshotController(
|
||||
return ctrl
|
||||
}
|
||||
|
||||
func (ctrl *CSISnapshotController) Run(workers int, stopCh <-chan struct{}) {
|
||||
func (ctrl *csiSnapshotController) Run(workers int, stopCh <-chan struct{}) {
|
||||
defer ctrl.snapshotQueue.ShutDown()
|
||||
defer ctrl.contentQueue.ShutDown()
|
||||
|
||||
@@ -154,7 +154,7 @@ func (ctrl *CSISnapshotController) Run(workers int, stopCh <-chan struct{}) {
|
||||
}
|
||||
|
||||
// enqueueSnapshotWork adds snapshot to given work queue.
|
||||
func (ctrl *CSISnapshotController) enqueueSnapshotWork(obj interface{}) {
|
||||
func (ctrl *csiSnapshotController) enqueueSnapshotWork(obj interface{}) {
|
||||
// Beware of "xxx deleted" events
|
||||
if unknown, ok := obj.(cache.DeletedFinalStateUnknown); ok && unknown.Obj != nil {
|
||||
obj = unknown.Obj
|
||||
@@ -171,7 +171,7 @@ func (ctrl *CSISnapshotController) enqueueSnapshotWork(obj interface{}) {
|
||||
}
|
||||
|
||||
// enqueueContentWork adds snapshot data to given work queue.
|
||||
func (ctrl *CSISnapshotController) enqueueContentWork(obj interface{}) {
|
||||
func (ctrl *csiSnapshotController) enqueueContentWork(obj interface{}) {
|
||||
// Beware of "xxx deleted" events
|
||||
if unknown, ok := obj.(cache.DeletedFinalStateUnknown); ok && unknown.Obj != nil {
|
||||
obj = unknown.Obj
|
||||
@@ -189,7 +189,7 @@ func (ctrl *CSISnapshotController) enqueueContentWork(obj interface{}) {
|
||||
|
||||
// snapshotWorker processes items from snapshotQueue. It must run only once,
|
||||
// syncSnapshot is not assured to be reentrant.
|
||||
func (ctrl *CSISnapshotController) snapshotWorker() {
|
||||
func (ctrl *csiSnapshotController) snapshotWorker() {
|
||||
workFunc := func() bool {
|
||||
keyObj, quit := ctrl.snapshotQueue.Get()
|
||||
if quit {
|
||||
@@ -252,7 +252,7 @@ func (ctrl *CSISnapshotController) snapshotWorker() {
|
||||
|
||||
// contentWorker processes items from contentQueue. It must run only once,
|
||||
// syncContent is not assured to be reentrant.
|
||||
func (ctrl *CSISnapshotController) contentWorker() {
|
||||
func (ctrl *csiSnapshotController) contentWorker() {
|
||||
workFunc := func() bool {
|
||||
keyObj, quit := ctrl.contentQueue.Get()
|
||||
if quit {
|
||||
@@ -311,7 +311,7 @@ func (ctrl *CSISnapshotController) contentWorker() {
|
||||
|
||||
// shouldProcessSnapshot detect if snapshotter in the VolumeSnapshotClass is the same as the snapshotter
|
||||
// in external controller.
|
||||
func (ctrl *CSISnapshotController) shouldProcessSnapshot(snapshot *crdv1.VolumeSnapshot) bool {
|
||||
func (ctrl *csiSnapshotController) shouldProcessSnapshot(snapshot *crdv1.VolumeSnapshot) bool {
|
||||
class, err := ctrl.GetClassFromVolumeSnapshot(snapshot)
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -326,7 +326,7 @@ func (ctrl *CSISnapshotController) shouldProcessSnapshot(snapshot *crdv1.VolumeS
|
||||
|
||||
// updateSnapshot runs in worker thread and handles "snapshot added",
|
||||
// "snapshot updated" and "periodic sync" events.
|
||||
func (ctrl *CSISnapshotController) updateSnapshot(vs *crdv1.VolumeSnapshot) {
|
||||
func (ctrl *csiSnapshotController) updateSnapshot(vs *crdv1.VolumeSnapshot) {
|
||||
// Store the new vs version in the cache and do not process it if this is
|
||||
// an old version.
|
||||
glog.V(5).Infof("updateSnapshot %q", snapshotKey(vs))
|
||||
@@ -351,7 +351,7 @@ func (ctrl *CSISnapshotController) updateSnapshot(vs *crdv1.VolumeSnapshot) {
|
||||
|
||||
// updateContent runs in worker thread and handles "content added",
|
||||
// "content updated" and "periodic sync" events.
|
||||
func (ctrl *CSISnapshotController) updateContent(content *crdv1.VolumeSnapshotContent) {
|
||||
func (ctrl *csiSnapshotController) updateContent(content *crdv1.VolumeSnapshotContent) {
|
||||
// Store the new vs version in the cache and do not process it if this is
|
||||
// an old version.
|
||||
new, err := ctrl.storeContentUpdate(content)
|
||||
@@ -374,7 +374,7 @@ func (ctrl *CSISnapshotController) updateContent(content *crdv1.VolumeSnapshotCo
|
||||
}
|
||||
|
||||
// deleteSnapshot runs in worker thread and handles "snapshot deleted" event.
|
||||
func (ctrl *CSISnapshotController) deleteSnapshot(vs *crdv1.VolumeSnapshot) {
|
||||
func (ctrl *csiSnapshotController) deleteSnapshot(vs *crdv1.VolumeSnapshot) {
|
||||
_ = ctrl.snapshotStore.Delete(vs)
|
||||
glog.V(4).Infof("vs %q deleted", snapshotKey(vs))
|
||||
|
||||
@@ -391,7 +391,7 @@ func (ctrl *CSISnapshotController) deleteSnapshot(vs *crdv1.VolumeSnapshot) {
|
||||
}
|
||||
|
||||
// deleteContent runs in worker thread and handles "snapshot deleted" event.
|
||||
func (ctrl *CSISnapshotController) deleteContent(content *crdv1.VolumeSnapshotContent) {
|
||||
func (ctrl *csiSnapshotController) deleteContent(content *crdv1.VolumeSnapshotContent) {
|
||||
_ = ctrl.contentStore.Delete(content)
|
||||
glog.V(4).Infof("content %q deleted", content.Name)
|
||||
|
||||
@@ -410,7 +410,7 @@ func (ctrl *CSISnapshotController) deleteContent(content *crdv1.VolumeSnapshotCo
|
||||
// initializeCaches fills all controller caches with initial data from etcd in
|
||||
// order to have the caches already filled when first addSnapshot/addContent to
|
||||
// perform initial synchronization of the controller.
|
||||
func (ctrl *CSISnapshotController) initializeCaches(snapshotLister storagelisters.VolumeSnapshotLister, contentLister storagelisters.VolumeSnapshotContentLister) {
|
||||
func (ctrl *csiSnapshotController) initializeCaches(snapshotLister storagelisters.VolumeSnapshotLister, contentLister storagelisters.VolumeSnapshotContentLister) {
|
||||
vsList, err := snapshotLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("CSISnapshotController can't initialize caches: %v", err)
|
@@ -159,56 +159,57 @@ func GetSecretReference(nameKey, namespaceKey string, snapshotClassParams map[st
|
||||
|
||||
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 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}
|
||||
// snapshot may be nil when resolving create/delete snapshot secret names because the
|
||||
// snapshot may or may not exist at delete time
|
||||
if snapshot != nil {
|
||||
namespaceParams["volumesnapshot.namespace"] = snapshot.Namespace
|
||||
}
|
||||
|
||||
{
|
||||
// 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
|
||||
resolvedNamespace, err := resolveTemplate(namespaceTemplate, namespaceParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving %s value %q: %v", namespaceKey, namespaceTemplate, err)
|
||||
}
|
||||
glog.V(4).Infof("GetSecretReference namespaceTemplate %s, namespaceParams: %+v, resolved %s", namespaceTemplate, namespaceParams, resolvedNamespace)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// resolveTemplate resolves the template by checking if the value is missing for a key
|
||||
func resolveTemplate(template string, params map[string]string) (string, error) {
|
||||
missingParams := sets.NewString()
|
||||
resolved := os.Expand(template, func(k string) string {
|
||||
@@ -224,6 +225,7 @@ func resolveTemplate(template string, params map[string]string) (string, error)
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
// GetCredentials retrieves credentials stored in v1.SecretReference
|
||||
func GetCredentials(k8s kubernetes.Interface, ref *v1.SecretReference) (map[string]string, error) {
|
||||
if ref == nil {
|
||||
return nil, nil
|
||||
|
Reference in New Issue
Block a user