Skip to content

Commit

Permalink
Create kubernetes.io tags on provisioned volumes
Browse files Browse the repository at this point in the history
Add following tags to provisioned volumes:
    
kubernetes.io/created-for/pv/name:       pvc-447cc711-bb65-4b4d-836d-a822e4e77e43
kubernetes.io/created-for/pvc/name:      myclaim
kubernetes.io/created-for/pvc/namespace: default
    
This is for beckward compatibility with in-tree aws-ebs volume plugin, that
introduced these tags.

The tags are added to volumes only when the external-provisioner sidecar is
running with --extra-create-metadata=true option.
  • Loading branch information
jsafrane committed Jul 10, 2020
1 parent c5d5870 commit 3e24f97
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 21 deletions.
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func main() {
driver.WithExtraVolumeTags(options.ControllerOptions.ExtraVolumeTags),
driver.WithMode(options.DriverMode),
driver.WithVolumeAttachLimit(options.NodeOptions.VolumeAttachLimit),
driver.WithClusterID(options.ControllerOptions.ClusterID),
driver.WithKubernetesClusterID(options.ControllerOptions.KubernetesClusterID),
)
if err != nil {
klog.Fatalln(err)
Expand Down
4 changes: 2 additions & 2 deletions cmd/options/controller_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ type ControllerOptions struct {
ExtraVolumeTags map[string]string
// ID of the kubernetes cluster. This is used only to create the same tags on volumes that
// in-tree volume volume plugin does.
ClusterID string
KubernetesClusterID string
}

func (s *ControllerOptions) AddFlags(fs *flag.FlagSet) {
fs.Var(cliflag.NewMapStringString(&s.ExtraVolumeTags), "extra-volume-tags", "Extra volume tags to attach to each dynamically provisioned volume. It is a comma separated list of key value pairs like '<key1>=<value1>,<key2>=<value2>'")
fs.StringVar(&s.ClusterID, "cluster-id", "", "ID of the Kubernetes cluster (optional).")
fs.StringVar(&s.KubernetesClusterID, "k8s-tag-cluster-id", "", "ID of the Kubernetes cluster used for tagging provisioned EBS volumes (optional).")
}
30 changes: 29 additions & 1 deletion pkg/driver/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ const (

// KmsKeyId represents key for KMS encryption key
KmsKeyIDKey = "kmskeyid"

// PVCNameKey contains name of the PVC for which is a volume provisioned.
PVCNameKey = "csi.storage.k8s.io/pvc/name"

// PVCNamespaceKey contains namespace of the PVC for which is a volume provisioned.
PVCNamespaceKey = "csi.storage.k8s.io/pvc/namespace"

// PVNameKey contains name of the final PV that will be used for the dynamically
// provisioned volume
PVNameKey = "csi.storage.k8s.io/pv/name"
)

// constants for volume tags and their values
Expand All @@ -50,9 +60,27 @@ const (
// From k8s.io/legacy-cloud-providers/aws/tags.go.
ResourceLifecycleOwned = "owned"

// NameTag is tag for provisioned EBS volume for backward compatibility with
// NameTag is tag applied to provisioned EBS volume for backward compatibility with
// in-tree volume plugin. Used only when --cluster-id is set.
NameTag = "Name"

// PVCNameTag is tag applied to provisioned EBS volume for backward compatibility
// with in-tree volume plugin. Value of the tag is PVC name. It is applied only when
// the external provisioner sidecar is started with --extra-create-metadata=true and
// thus provides such metadata to the CSI driver.
PVCNameTag = "kubernetes.io/created-for/pvc/name"

// PVCNamespaceTag is tag applied to provisioned EBS volume for backward compatibility
// with in-tree volume plugin. Value of the tag is PVC namespace. It is applied only when
// the external provisioner sidecar is started with --extra-create-metadata=true and
// thus provides such metadata to the CSI driver.
PVCNamespaceTag = "kubernetes.io/created-for/pvc/namespace"

// PVNameTag is tag applied to provisioned EBS volume for backward compatibility
// with in-tree volume plugin. Value of the tag is PV name. It is applied only when
// the external provisioner sidecar is started with --extra-create-metadata=true and
// thus provides such metadata to the CSI driver.
PVNameTag = "kubernetes.io/created-for/pv/name"
)

// constants for default command line flag values
Expand Down
18 changes: 12 additions & 6 deletions pkg/driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
iopsPerGB int
isEncrypted bool
kmsKeyID string
volumeTags = map[string]string{
cloud.VolumeNameTagKey: volName,
}
)

for key, value := range req.GetParameters() {
Expand All @@ -149,6 +152,12 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
}
case KmsKeyIDKey:
kmsKeyID = value
case PVCNameKey:
volumeTags[PVCNameTag] = value
case PVCNamespaceKey:
volumeTags[PVCNamespaceTag] = value
case PVNameKey:
volumeTags[PVNameTag] = value
default:
return nil, status.Errorf(codes.InvalidArgument, "Invalid parameter key %s for CreateVolume", key)
}
Expand Down Expand Up @@ -179,13 +188,10 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
zone := pickAvailabilityZone(req.GetAccessibilityRequirements())

// fill volume tags
volumeTags := map[string]string{
cloud.VolumeNameTagKey: volName,
}
if d.driverOptions.clusterID != "" {
resourceLifecycleTag := ResourceLifecycleTagPrefix + d.driverOptions.clusterID
if d.driverOptions.kubernetesClusterID != "" {
resourceLifecycleTag := ResourceLifecycleTagPrefix + d.driverOptions.kubernetesClusterID
volumeTags[resourceLifecycleTag] = ResourceLifecycleOwned
volumeTags[NameTag] = d.driverOptions.clusterID + "-dynamic-" + volName
volumeTags[NameTag] = d.driverOptions.kubernetesClusterID + "-dynamic-" + volName
}
for k, v := range d.driverOptions.extraVolumeTags {
volumeTags[k] = v
Expand Down
66 changes: 65 additions & 1 deletion pkg/driver/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1103,10 +1103,74 @@ func TestCreateVolume(t *testing.T) {
awsDriver := controllerService{
cloud: mockCloud,
driverOptions: &DriverOptions{
clusterID: clusterID,
kubernetesClusterID: clusterID,
},
}

_, err := awsDriver.CreateVolume(ctx, req)
if err != nil {
srvErr, ok := status.FromError(err)
if !ok {
t.Fatalf("Could not get error status code from error: %v", srvErr)
}
t.Fatalf("Unexpected error: %v", srvErr.Code())
}
},
},
{
name: "success with legacy tags",
testFunc: func(t *testing.T) {
const (
volumeName = "random-vol-name"
clusterID = "test-cluster-id"
expectedPVCNameTag = "kubernetes.io/created-for/pvc/name"
expectedPVCNamespaceTag = "kubernetes.io/created-for/pvc/namespace"
expectedPVNameTag = "kubernetes.io/created-for/pv/name"
pvcNamespace = "default"
pvcName = "my-pvc"
pvName = volumeName
)
req := &csi.CreateVolumeRequest{
Name: volumeName,
CapacityRange: stdCapRange,
VolumeCapabilities: stdVolCap,
Parameters: map[string]string{
"csi.storage.k8s.io/pvc/name": pvcName,
"csi.storage.k8s.io/pvc/namespace": pvcNamespace,
"csi.storage.k8s.io/pv/name": pvName,
},
}

ctx := context.Background()

mockDisk := &cloud.Disk{
VolumeID: req.Name,
AvailabilityZone: expZone,
CapacityGiB: util.BytesToGiB(stdVolSize),
}

diskOptions := &cloud.DiskOptions{
CapacityBytes: stdVolSize,
Tags: map[string]string{
cloud.VolumeNameTagKey: volumeName,
expectedPVCNameTag: pvcName,
expectedPVCNamespaceTag: pvcNamespace,
expectedPVNameTag: pvName,
},
}

mockCtl := gomock.NewController(t)
defer mockCtl.Finish()

mockCloud := mocks.NewMockCloud(mockCtl)
mockCloud.EXPECT().GetDiskByName(gomock.Eq(ctx), gomock.Eq(req.Name), gomock.Eq(stdVolSize)).Return(nil, cloud.ErrNotFound)
mockCloud.EXPECT().CreateDisk(gomock.Eq(ctx), gomock.Eq(req.Name), gomock.Eq(diskOptions)).Return(mockDisk, nil)

awsDriver := controllerService{
cloud: mockCloud,
driverOptions: &DriverOptions{},
}

_, err := awsDriver.CreateVolume(ctx, req)
if err != nil {
srvErr, ok := status.FromError(err)
Expand Down
14 changes: 7 additions & 7 deletions pkg/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ type Driver struct {
}

type DriverOptions struct {
endpoint string
extraVolumeTags map[string]string
mode Mode
volumeAttachLimit int64
clusterID string
endpoint string
extraVolumeTags map[string]string
mode Mode
volumeAttachLimit int64
kubernetesClusterID string
}

func NewDriver(options ...func(*DriverOptions)) (*Driver, error) {
Expand Down Expand Up @@ -164,8 +164,8 @@ func WithVolumeAttachLimit(volumeAttachLimit int64) func(*DriverOptions) {
}
}

func WithClusterID(clusterID string) func(*DriverOptions) {
func WithKubernetesClusterID(clusterID string) func(*DriverOptions) {
return func(o *DriverOptions) {
o.clusterID = clusterID
o.kubernetesClusterID = clusterID
}
}
6 changes: 3 additions & 3 deletions pkg/driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func TestWithVolumeAttachLimit(t *testing.T) {
func TestWithClusterID(t *testing.T) {
var id string = "test-cluster-id"
options := &DriverOptions{}
WithClusterID(id)(options)
if options.clusterID != id {
t.Fatalf("expected clusterID option got set to %s but is set to %s", id, options.clusterID)
WithKubernetesClusterID(id)(options)
if options.kubernetesClusterID != id {
t.Fatalf("expected kubernetesClusterID option got set to %s but is set to %s", id, options.kubernetesClusterID)
}
}

0 comments on commit 3e24f97

Please sign in to comment.