From f53bc4ebe076ae082937c88b22f76e3766a7477f Mon Sep 17 00:00:00 2001 From: Chakravarthy Nelluri Date: Fri, 15 Apr 2016 02:11:37 -0700 Subject: [PATCH] Flexvolume: Add support for multiple secrets --- api/swagger-spec/batch_v1.json | 2 +- api/swagger-spec/extensions_v1beta1.json | 2 +- api/swagger-spec/v1.json | 2 +- docs/api-reference/batch/v1/definitions.html | 4 +- .../extensions/v1beta1/definitions.html | 4 +- docs/api-reference/v1/definitions.html | 4 +- examples/flexvolume/README.md | 50 +++++++++++++++++-- examples/flexvolume/lvm | 8 ++- pkg/api/types.go | 6 ++- pkg/api/v1/types.go | 6 ++- pkg/api/v1/types_swagger_doc_generated.go | 2 +- pkg/volume/flexvolume/flexvolume.go | 23 +++++---- pkg/volume/flexvolume/flexvolume_test.go | 8 ++- 13 files changed, 90 insertions(+), 31 deletions(-) diff --git a/api/swagger-spec/batch_v1.json b/api/swagger-spec/batch_v1.json index 86da1e1278e..8fac0dedc3a 100644 --- a/api/swagger-spec/batch_v1.json +++ b/api/swagger-spec/batch_v1.json @@ -1516,7 +1516,7 @@ }, "secretRef": { "$ref": "v1.LocalObjectReference", - "description": "Optional: SecretRef is reference to the authentication secret for User, default is empty." + "description": "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts." }, "readOnly": { "type": "boolean", diff --git a/api/swagger-spec/extensions_v1beta1.json b/api/swagger-spec/extensions_v1beta1.json index 7b550f3b575..6954df73407 100644 --- a/api/swagger-spec/extensions_v1beta1.json +++ b/api/swagger-spec/extensions_v1beta1.json @@ -6933,7 +6933,7 @@ }, "secretRef": { "$ref": "v1.LocalObjectReference", - "description": "Optional: SecretRef is reference to the authentication secret for User, default is empty." + "description": "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts." }, "readOnly": { "type": "boolean", diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index a644b1f8fd9..c3995023403 100644 --- a/api/swagger-spec/v1.json +++ b/api/swagger-spec/v1.json @@ -16373,7 +16373,7 @@ }, "secretRef": { "$ref": "v1.LocalObjectReference", - "description": "Optional: SecretRef is reference to the authentication secret for User, default is empty." + "description": "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts." }, "readOnly": { "type": "boolean", diff --git a/docs/api-reference/batch/v1/definitions.html b/docs/api-reference/batch/v1/definitions.html index 3cff29e8621..f311cd020c7 100755 --- a/docs/api-reference/batch/v1/definitions.html +++ b/docs/api-reference/batch/v1/definitions.html @@ -2617,7 +2617,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i

secretRef

-

Optional: SecretRef is reference to the authentication secret for User, default is empty.

+

Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.

false

v1.LocalObjectReference

@@ -3882,7 +3882,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i diff --git a/docs/api-reference/extensions/v1beta1/definitions.html b/docs/api-reference/extensions/v1beta1/definitions.html index 18a51d89feb..6c0786756ee 100755 --- a/docs/api-reference/extensions/v1beta1/definitions.html +++ b/docs/api-reference/extensions/v1beta1/definitions.html @@ -2373,7 +2373,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i

secretRef

-

Optional: SecretRef is reference to the authentication secret for User, default is empty.

+

Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.

false

v1.LocalObjectReference

@@ -5872,7 +5872,7 @@ Both these may change in the future. Incoming requests are matched against the h diff --git a/docs/api-reference/v1/definitions.html b/docs/api-reference/v1/definitions.html index a7ba7ed7d5f..14b640242a0 100755 --- a/docs/api-reference/v1/definitions.html +++ b/docs/api-reference/v1/definitions.html @@ -2870,7 +2870,7 @@ The resulting set of endpoints can be viewed as:

secretRef

-

Optional: SecretRef is reference to the authentication secret for User, default is empty.

+

Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.

false

v1.LocalObjectReference

@@ -7742,7 +7742,7 @@ The resulting set of endpoints can be viewed as:
diff --git a/examples/flexvolume/README.md b/examples/flexvolume/README.md index f0125a5c6b1..4a60fd85ce5 100644 --- a/examples/flexvolume/README.md +++ b/examples/flexvolume/README.md @@ -53,22 +53,62 @@ Driver will be invoked with 'Init' to initialize the driver. It will be invoked ### Driver invocation model: Init: -\ init + +``` + init +``` Attach: -\ attach \ + +``` + attach +``` Detach: -\ detach \ + +``` + detach +``` Mount: -\ mount \ \ \ + +``` + mount +``` Unmount: -\ unmount \ + +``` + unmount +``` See lvm[lvm] for a quick example on how to write a simple flexvolume driver. +### Driver output: + +Flexvolume expects the driver to reply with the status of the operation in the +following format. + +``` +{ + "status": "", + "message": "", + "device": "" +} +``` + +### Default Json options + +In addition to the flags specified by the user in the Options field of the FlexVolumeSource, the following flags are also passed to the executable. + +``` +"kubernetes.io/fsType":"", +"kubernetes.io/readwrite":"", +"kubernetes.io/secret/key1":"" +... +"kubernetes.io/secret/keyN":"" +``` + ### Example of Flexvolume See nginx.yaml[nginx.yaml] for a quick example on how to use Flexvolume in a pod. diff --git a/examples/flexvolume/lvm b/examples/flexvolume/lvm index 22c32ca6bfe..ae9a1b15c4e 100755 --- a/examples/flexvolume/lvm +++ b/examples/flexvolume/lvm @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Notes: +# - Please install "jq" package before using this driver. usage() { err "Invalid usage. Usage: " err "\t$0 init" @@ -46,6 +48,10 @@ attach() { SIZE=$(echo $1 | jq -r '.size') VG=$(echo $1|jq -r '.volumegroup') + # LVM substitutes - with -- + VOLUMEID= `echo $VOLUMEID|sed s/-/--/g` + VG=`echo $VG|sed s/-/--/g` + DMDEV="/dev/mapper/${VG}-${VOLUMEID}" if [ ! -b "${DMDEV}" ]; then err "{\"status\": \"Failure\", \"message\": \"Volume ${VOLUMEID} does not exist\"}" @@ -77,7 +83,7 @@ domount() { VOLFSTYPE=`blkid -o udev ${DMDEV} 2>/dev/null|grep "ID_FS_TYPE"|cut -d"=" -f2` if [ "${VOLFSTYPE}" == "" ]; then - mkfs -t ${FSTYPE} ${DMDEV} + mkfs -t ${FSTYPE} ${DMDEV} >/dev/null 2>&1 if [ $? -ne 0 ]; then err "{ \"status\": \"Failure\", \"message\": \"Failed to create fs ${FSTYPE} on device ${DMDEV}\"}" exit 1 diff --git a/pkg/api/types.go b/pkg/api/types.go index 69c8a876a64..09eff349810 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -512,7 +512,11 @@ type FlexVolumeSource struct { // Must be a filesystem type supported by the host operating system. // Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. FSType string `json:"fsType,omitempty"` - // Optional: SecretRef is reference to the authentication secret for User, default is empty. + // Optional: SecretRef is reference to the secret object containing + // sensitive information to pass to the plugin scripts. This may be + // empty if no secret object is specified. If the secret object + // contains more than one secret, all secrets are passed to the plugin + // scripts. SecretRef *LocalObjectReference `json:"secretRef,omitempty"` // Optional: Defaults to false (read/write). ReadOnly here will force // the ReadOnly setting in VolumeMounts. diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 517d5e6413a..9f53d8b6280 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -675,7 +675,11 @@ type FlexVolumeSource struct { // Must be a filesystem type supported by the host operating system. // Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. FSType string `json:"fsType,omitempty"` - // Optional: SecretRef is reference to the authentication secret for User, default is empty. + // Optional: SecretRef is reference to the secret object containing + // sensitive information to pass to the plugin scripts. This may be + // empty if no secret object is specified. If the secret object + // contains more than one secret, all secrets are passed to the plugin + // scripts. SecretRef *LocalObjectReference `json:"secretRef,omitempty"` // Optional: Defaults to false (read/write). ReadOnly here will force // the ReadOnly setting in VolumeMounts. diff --git a/pkg/api/v1/types_swagger_doc_generated.go b/pkg/api/v1/types_swagger_doc_generated.go index b36490982c1..4c1ad3c7b50 100644 --- a/pkg/api/v1/types_swagger_doc_generated.go +++ b/pkg/api/v1/types_swagger_doc_generated.go @@ -477,7 +477,7 @@ var map_FlexVolumeSource = map[string]string{ "": "FlexVolume represents a generic volume resource that is provisioned/attached using a exec based plugin. This is an alpha feature and may change in future.", "driver": "Driver is the name of the driver to use for this volume.", "fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", - "secretRef": "Optional: SecretRef is reference to the authentication secret for User, default is empty.", + "secretRef": "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.", "readOnly": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", "options": "Optional: Extra command options if any.", } diff --git a/pkg/volume/flexvolume/flexvolume.go b/pkg/volume/flexvolume/flexvolume.go index 0f36ff2cf05..3db25470142 100644 --- a/pkg/volume/flexvolume/flexvolume.go +++ b/pkg/volume/flexvolume/flexvolume.go @@ -17,6 +17,7 @@ limitations under the License. package flexvolume import ( + "encoding/base64" "fmt" "io/ioutil" "os" @@ -103,7 +104,7 @@ func (plugin *flexVolumePlugin) getVolumeSource(spec *volume.Spec) *api.FlexVolu // NewMounter is the mounter routine to build the volume. func (plugin *flexVolumePlugin) NewMounter(spec *volume.Spec, pod *api.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { fv := plugin.getVolumeSource(spec) - secret := "" + secrets := make(map[string]string) if fv.SecretRef != nil { kubeClient := plugin.host.GetKubeClient() if kubeClient == nil { @@ -116,15 +117,15 @@ func (plugin *flexVolumePlugin) NewMounter(spec *volume.Spec, pod *api.Pod, _ vo return nil, err } for name, data := range secretName.Data { - secret = string(data) + secrets[name] = base64.StdEncoding.EncodeToString(data) glog.V(1).Infof("found flex volume secret info: %s", name) } } - return plugin.newMounterInternal(spec, pod, &flexVolumeUtil{}, plugin.host.GetMounter(), exec.New(), secret) + return plugin.newMounterInternal(spec, pod, &flexVolumeUtil{}, plugin.host.GetMounter(), exec.New(), secrets) } // newMounterInternal is the internal mounter routine to build the volume. -func (plugin *flexVolumePlugin) newMounterInternal(spec *volume.Spec, pod *api.Pod, manager flexVolumeManager, mounter mount.Interface, runner exec.Interface, secret string) (volume.Mounter, error) { +func (plugin *flexVolumePlugin) newMounterInternal(spec *volume.Spec, pod *api.Pod, manager flexVolumeManager, mounter mount.Interface, runner exec.Interface, secrets map[string]string) (volume.Mounter, error) { source := plugin.getVolumeSource(spec) return &flexVolumeMounter{ flexVolumeDisk: &flexVolumeDisk{ @@ -136,7 +137,7 @@ func (plugin *flexVolumePlugin) newMounterInternal(spec *volume.Spec, pod *api.P execPath: plugin.getExecutable(), mounter: mounter, plugin: plugin, - secret: secret, + secrets: secrets, }, fsType: source.FSType, readOnly: source.ReadOnly, @@ -186,8 +187,8 @@ type flexVolumeDisk struct { // block device. mounter mount.Interface // secret for the volume. - secret string - plugin *flexVolumePlugin + secrets map[string]string + plugin *flexVolumePlugin } // FlexVolumeUnmounter is the disk that will be cleaned by this plugin. @@ -275,8 +276,8 @@ func (f *flexVolumeMounter) SetUpAt(dir string, fsGroup *int64) error { } // Extract secret and pass it as options. - if f.secret != "" { - f.options[optionKeySecret] = f.secret + for name, secret := range f.secrets { + f.options[optionKeySecret+"/"+name] = secret } device, err := f.manager.attach(f) @@ -301,8 +302,8 @@ func (f *flexVolumeMounter) SetUpAt(dir string, fsGroup *int64) error { options = append(options, "rw") } // Extract secret and pass it as options. - if f.secret != "" { - options = append(options, "secret="+f.secret) + for name, secret := range f.secrets { + f.options[optionKeySecret+"/"+name] = secret } os.MkdirAll(dir, 0750) diff --git a/pkg/volume/flexvolume/flexvolume_test.go b/pkg/volume/flexvolume/flexvolume_test.go index 7190c5079a6..83a03932049 100644 --- a/pkg/volume/flexvolume/flexvolume_test.go +++ b/pkg/volume/flexvolume/flexvolume_test.go @@ -18,6 +18,7 @@ package flexvolume import ( "bytes" + "encoding/base64" "fmt" "os" "path" @@ -239,7 +240,9 @@ func doTestPluginAttachDetach(t *testing.T, spec *volume.Spec, tmpDir string) { } fake := &mount.FakeMounter{} pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} - mounter, err := plugin.(*flexVolumePlugin).newMounterInternal(spec, pod, &flexVolumeUtil{}, fake, exec.New(), "") + secretMap := make(map[string]string) + secretMap["flexsecret"] = base64.StdEncoding.EncodeToString([]byte("foo")) + mounter, err := plugin.(*flexVolumePlugin).newMounterInternal(spec, pod, &flexVolumeUtil{}, fake, exec.New(), secretMap) volumePath := mounter.GetPath() if err != nil { t.Errorf("Failed to make a new Mounter: %v", err) @@ -318,7 +321,8 @@ func doTestPluginMountUnmount(t *testing.T, spec *volume.Spec, tmpDir string) { } fake := &mount.FakeMounter{} pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} - mounter, err := plugin.(*flexVolumePlugin).newMounterInternal(spec, pod, &flexVolumeUtil{}, fake, exec.New(), "") + // Use nil secret to test for nil secret case. + mounter, err := plugin.(*flexVolumePlugin).newMounterInternal(spec, pod, &flexVolumeUtil{}, fake, exec.New(), nil) volumePath := mounter.GetPath() if err != nil { t.Errorf("Failed to make a new Mounter: %v", err)