diff --git a/api/swagger-spec/batch_v1.json b/api/swagger-spec/batch_v1.json index 86da1e1278ec2..8fac0dedc3a9d 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 7b550f3b57558..6954df7340731 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 a644b1f8fd967..c399502340350 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 3cff29e8621ab..f311cd020c706 100755 --- a/docs/api-reference/batch/v1/definitions.html +++ b/docs/api-reference/batch/v1/definitions.html @@ -2617,7 +2617,7 @@

v1.FlexVolumeSource

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 @@

any

diff --git a/docs/api-reference/extensions/v1beta1/definitions.html b/docs/api-reference/extensions/v1beta1/definitions.html index 18a51d89feb3e..6c0786756ee11 100755 --- a/docs/api-reference/extensions/v1beta1/definitions.html +++ b/docs/api-reference/extensions/v1beta1/definitions.html @@ -2373,7 +2373,7 @@

v1.FlexVolumeSource

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 @@

any

diff --git a/docs/api-reference/v1/definitions.html b/docs/api-reference/v1/definitions.html index a7ba7ed7d5f5d..14b640242a073 100755 --- a/docs/api-reference/v1/definitions.html +++ b/docs/api-reference/v1/definitions.html @@ -2870,7 +2870,7 @@

v1.FlexVolumeSource

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 @@

any

diff --git a/examples/flexvolume/README.md b/examples/flexvolume/README.md index f0125a5c6b14e..4a60fd85ce5ff 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 22c32ca6bfe93..ae9a1b15c4ea5 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 69c8a876a6407..09eff349810e9 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 517d5e6413ab9..9f53d8b628093 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 b36490982c119..4c1ad3c7b508d 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 0f36ff2cf051e..3db25470142ce 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 7190c5079a661..83a0393204964 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)