diff --git a/pkg/kubelet/root_context_linux.go b/pkg/kubelet/root_context_linux.go new file mode 100644 index 00000000000..f0da41b66ed --- /dev/null +++ b/pkg/kubelet/root_context_linux.go @@ -0,0 +1,47 @@ +// +build linux + +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubelet + +import ( + "github.com/docker/libcontainer/selinux" +) + +// getRootContext gets the SELinux context of the kubelet rootDir +// or returns an error. +func (kl *Kubelet) getRootDirContext() (string, error) { + // If SELinux is not enabled, return an empty string + if !selinux.SelinuxEnabled() { + return "", nil + } + + // Get the SELinux context of the rootDir. + rootContext, err := selinux.Getfilecon(kl.getRootDir()) + if err != nil { + return "", err + } + + // There is a libcontainer bug where the null byte is not stripped from + // the result of reading some selinux xattrs; strip it. + // + // TODO: remove when https://github.com/docker/libcontainer/issues/499 + // is fixed + rootContext = rootContext[:len(rootContext)-1] + + return rootContext, nil +} diff --git a/pkg/kubelet/root_context_unsupported.go b/pkg/kubelet/root_context_unsupported.go new file mode 100644 index 00000000000..528db9a247d --- /dev/null +++ b/pkg/kubelet/root_context_unsupported.go @@ -0,0 +1,24 @@ +// +build !linux + +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubelet + +func (kl *Kubelet) getRootDirContext() (string, error) { + // For now, just return a blank security context on unsupported platforms + return "", nil +} diff --git a/pkg/kubelet/volumes.go b/pkg/kubelet/volumes.go index e3e81183883..6bedded4a8d 100644 --- a/pkg/kubelet/volumes.go +++ b/pkg/kubelet/volumes.go @@ -54,8 +54,8 @@ func (vh *volumeHost) GetKubeClient() client.Interface { return vh.kubelet.kubeClient } -func (vh *volumeHost) NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { - b, err := vh.kubelet.newVolumeBuilderFromPlugins(spec, podRef) +func (vh *volumeHost) NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) { + b, err := vh.kubelet.newVolumeBuilderFromPlugins(spec, podRef, opts) if err == nil && b == nil { return nil, errUnsupportedVolumeType } @@ -78,7 +78,7 @@ func (vh *volumeHost) NewWrapperCleaner(spec *api.Volume, podUID types.UID) (vol return c, nil } -func (kl *Kubelet) newVolumeBuilderFromPlugins(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (kl *Kubelet) newVolumeBuilderFromPlugins(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) { plugin, err := kl.volumePluginMgr.FindPluginBySpec(spec) if err != nil { return nil, fmt.Errorf("can't use volume plugins for %s: %v", spew.Sprintf("%#v", *spec), err) @@ -87,7 +87,7 @@ func (kl *Kubelet) newVolumeBuilderFromPlugins(spec *api.Volume, podRef *api.Obj // Not found but not an error return nil, nil } - builder, err := plugin.NewBuilder(spec, podRef) + builder, err := plugin.NewBuilder(spec, podRef, opts) if err != nil { return nil, fmt.Errorf("failed to instantiate volume plugin for %s: %v", spew.Sprintf("%#v", *spec), err) } @@ -106,8 +106,13 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (volumeMap, error) { return nil, err } + rootContext, err := kl.getRootDirContext() + if err != nil { + return nil, err + } + // Try to use a plugin for this volume. - builder, err := kl.newVolumeBuilderFromPlugins(volSpec, podRef) + builder, err := kl.newVolumeBuilderFromPlugins(volSpec, podRef, volume.VolumeOptions{rootContext}) if err != nil { glog.Errorf("Could not create volume builder for pod %s: %v", pod.UID, err) return nil, err diff --git a/pkg/volume/aws_ebs/aws_ebs.go b/pkg/volume/aws_ebs/aws_ebs.go index e6351c230e6..0e8562d6038 100644 --- a/pkg/volume/aws_ebs/aws_ebs.go +++ b/pkg/volume/aws_ebs/aws_ebs.go @@ -71,7 +71,7 @@ func (plugin *awsElasticBlockStorePlugin) GetAccessModes() []api.AccessModeType } } -func (plugin *awsElasticBlockStorePlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *awsElasticBlockStorePlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, _ volume.VolumeOptions) (volume.Builder, error) { // Inject real implementations here, test through the internal function. return plugin.newBuilderInternal(spec, podRef.UID, &AWSDiskUtil{}, mount.New()) } diff --git a/pkg/volume/empty_dir/empty_dir.go b/pkg/volume/empty_dir/empty_dir.go index bcaab71d83d..7187b9941cd 100644 --- a/pkg/volume/empty_dir/empty_dir.go +++ b/pkg/volume/empty_dir/empty_dir.go @@ -25,6 +25,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume" + "github.com/golang/glog" ) // This is the primary entrypoint for volume plugins. @@ -80,12 +81,12 @@ func (plugin *emptyDirPlugin) CanSupport(spec *api.Volume) bool { return false } -func (plugin *emptyDirPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *emptyDirPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) { // Inject real implementations here, test through the internal function. - return plugin.newBuilderInternal(spec, podRef, plugin.mounter, &realMountDetector{plugin.mounter}) + return plugin.newBuilderInternal(spec, podRef, plugin.mounter, &realMountDetector{plugin.mounter}, opts) } -func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference, mounter mount.Interface, mountDetector mountDetector) (volume.Builder, error) { +func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference, mounter mount.Interface, mountDetector mountDetector, opts volume.VolumeOptions) (volume.Builder, error) { if plugin.legacyMode { // Legacy mode instances can be cleaned up but not created anew. return nil, fmt.Errorf("legacy mode: can not create new instances") @@ -102,6 +103,7 @@ func (plugin *emptyDirPlugin) newBuilderInternal(spec *api.Volume, podRef *api.O mountDetector: mountDetector, plugin: plugin, legacyMode: false, + rootContext: opts.RootContext, }, nil } @@ -154,6 +156,7 @@ type emptyDir struct { mountDetector mountDetector plugin *emptyDirPlugin legacyMode bool + rootContext string } // SetUp creates new directory. @@ -192,10 +195,29 @@ func (ed *emptyDir) setupTmpfs(dir string) error { if err != nil { return err } + // If the directory is a mountpoint with medium memory, there is no + // work to do since we are already in the desired state. if isMnt && medium == mediumMemory { - return nil // current state is what we expect + return nil } - return ed.mounter.Mount("tmpfs", dir, "tmpfs", 0, "") + // By default a tmpfs mount will receive a different SELinux context + // from that of the Kubelet root directory which is not readable from + // the SELinux context of a docker container. + // + // getTmpfsMountOptions gets the mount option to set the context of + // the tmpfs mount so that it can be read from the SELinux context of + // the container. + opts := ed.getTmpfsMountOptions() + glog.V(3).Infof("pod %v: mounting tmpfs for volume %v with opts %v", ed.podUID, ed.volName, opts) + return ed.mounter.Mount("tmpfs", dir, "tmpfs", 0, opts) +} + +func (ed *emptyDir) getTmpfsMountOptions() string { + if ed.rootContext == "" { + return "" + } + + return fmt.Sprintf("rootcontext=\"%v\"", ed.rootContext) } func (ed *emptyDir) GetPath() string { diff --git a/pkg/volume/empty_dir/empty_dir_test.go b/pkg/volume/empty_dir/empty_dir_test.go index 8aeae61e352..c7a1b48e266 100644 --- a/pkg/volume/empty_dir/empty_dir_test.go +++ b/pkg/volume/empty_dir/empty_dir_test.go @@ -74,7 +74,7 @@ func TestPlugin(t *testing.T) { } mounter := mount.FakeMounter{} mountDetector := fakeMountDetector{} - builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector) + builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector, volume.VolumeOptions{""}) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } @@ -133,7 +133,7 @@ func TestPluginTmpfs(t *testing.T) { } mounter := mount.FakeMounter{} mountDetector := fakeMountDetector{} - builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector) + builder, err := plug.(*emptyDirPlugin).newBuilderInternal(spec, &api.ObjectReference{UID: types.UID("poduid")}, &mounter, &mountDetector, volume.VolumeOptions{""}) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } @@ -197,7 +197,7 @@ func TestPluginBackCompat(t *testing.T) { spec := &api.Volume{ Name: "vol1", } - builder, err := plug.NewBuilder(spec, &api.ObjectReference{UID: types.UID("poduid")}) + builder, err := plug.NewBuilder(spec, &api.ObjectReference{UID: types.UID("poduid")}, volume.VolumeOptions{""}) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } @@ -222,7 +222,7 @@ func TestPluginLegacy(t *testing.T) { } spec := api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}} - if _, err := plug.(*emptyDirPlugin).newBuilderInternal(&spec, &api.ObjectReference{UID: types.UID("poduid")}, &mount.FakeMounter{}, &fakeMountDetector{}); err == nil { + if _, err := plug.(*emptyDirPlugin).newBuilderInternal(&spec, &api.ObjectReference{UID: types.UID("poduid")}, &mount.FakeMounter{}, &fakeMountDetector{}, volume.VolumeOptions{""}); err == nil { t.Errorf("Expected failiure") } diff --git a/pkg/volume/gce_pd/gce_pd.go b/pkg/volume/gce_pd/gce_pd.go index b4e61906031..e2cb7f41911 100644 --- a/pkg/volume/gce_pd/gce_pd.go +++ b/pkg/volume/gce_pd/gce_pd.go @@ -78,7 +78,7 @@ func (plugin *gcePersistentDiskPlugin) GetAccessModes() []api.AccessModeType { } } -func (plugin *gcePersistentDiskPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *gcePersistentDiskPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, _ volume.VolumeOptions) (volume.Builder, error) { // Inject real implementations here, test through the internal function. return plugin.newBuilderInternal(spec, podRef.UID, &GCEDiskUtil{}, mount.New()) } diff --git a/pkg/volume/gce_pd/gce_pd_test.go b/pkg/volume/gce_pd/gce_pd_test.go index 2f6d19f4c6f..cfd962f7935 100644 --- a/pkg/volume/gce_pd/gce_pd_test.go +++ b/pkg/volume/gce_pd/gce_pd_test.go @@ -185,7 +185,7 @@ func TestPluginLegacy(t *testing.T) { t.Errorf("Expected false") } - if _, err := plug.NewBuilder(&api.Volume{VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}}}, &api.ObjectReference{UID: types.UID("poduid")}); err == nil { + if _, err := plug.NewBuilder(&api.Volume{VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}}}, &api.ObjectReference{UID: types.UID("poduid")}, volume.VolumeOptions{""}); err == nil { t.Errorf("Expected failiure") } diff --git a/pkg/volume/git_repo/git_repo.go b/pkg/volume/git_repo/git_repo.go index 6265d047b88..5488d61077f 100644 --- a/pkg/volume/git_repo/git_repo.go +++ b/pkg/volume/git_repo/git_repo.go @@ -70,7 +70,7 @@ func (plugin *gitRepoPlugin) CanSupport(spec *api.Volume) bool { return false } -func (plugin *gitRepoPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *gitRepoPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) { if plugin.legacyMode { // Legacy mode instances can be cleaned up but not created anew. return nil, fmt.Errorf("legacy mode: can not create new instances") @@ -83,6 +83,7 @@ func (plugin *gitRepoPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectRefe exec: exec.New(), plugin: plugin, legacyMode: false, + opts: opts, }, nil } @@ -109,6 +110,7 @@ type gitRepo struct { exec exec.Interface plugin *gitRepoPlugin legacyMode bool + opts volume.VolumeOptions } // SetUp creates new directory and clones a git repo. @@ -132,7 +134,7 @@ func (gr *gitRepo) SetUpAt(dir string) error { } // Wrap EmptyDir, let it do the setup. - wrapped, err := gr.plugin.host.NewWrapperBuilder(wrappedVolumeSpec, &gr.podRef) + wrapped, err := gr.plugin.host.NewWrapperBuilder(wrappedVolumeSpec, &gr.podRef, gr.opts) if err != nil { return err } diff --git a/pkg/volume/git_repo/git_repo_test.go b/pkg/volume/git_repo/git_repo_test.go index 9b6e163799f..0ea387a1dff 100644 --- a/pkg/volume/git_repo/git_repo_test.go +++ b/pkg/volume/git_repo/git_repo_test.go @@ -118,7 +118,7 @@ func TestPlugin(t *testing.T) { }, }, } - builder, err := plug.NewBuilder(spec, &api.ObjectReference{UID: types.UID("poduid")}) + builder, err := plug.NewBuilder(spec, &api.ObjectReference{UID: types.UID("poduid")}, volume.VolumeOptions{""}) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } @@ -173,7 +173,7 @@ func TestPluginLegacy(t *testing.T) { t.Errorf("Expected false") } - if _, err := plug.NewBuilder(&api.Volume{VolumeSource: api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{}}}, &api.ObjectReference{UID: types.UID("poduid")}); err == nil { + if _, err := plug.NewBuilder(&api.Volume{VolumeSource: api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{}}}, &api.ObjectReference{UID: types.UID("poduid")}, volume.VolumeOptions{""}); err == nil { t.Errorf("Expected failiure") } diff --git a/pkg/volume/glusterfs/glusterfs.go b/pkg/volume/glusterfs/glusterfs.go index b711f5d4021..2d44c7c6742 100644 --- a/pkg/volume/glusterfs/glusterfs.go +++ b/pkg/volume/glusterfs/glusterfs.go @@ -68,7 +68,7 @@ func (plugin *glusterfsPlugin) GetAccessModes() []api.AccessModeType { } } -func (plugin *glusterfsPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *glusterfsPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, _ volume.VolumeOptions) (volume.Builder, error) { ep_name := spec.VolumeSource.Glusterfs.EndpointsName ns := api.NamespaceDefault ep, err := plugin.host.GetKubeClient().Endpoints(ns).Get(ep_name) diff --git a/pkg/volume/host_path/host_path.go b/pkg/volume/host_path/host_path.go index 12f7b428d59..f18e79c53b9 100644 --- a/pkg/volume/host_path/host_path.go +++ b/pkg/volume/host_path/host_path.go @@ -60,7 +60,7 @@ func (plugin *hostPathPlugin) GetAccessModes() []api.AccessModeType { } } -func (plugin *hostPathPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *hostPathPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, _ volume.VolumeOptions) (volume.Builder, error) { return &hostPath{spec.HostPath.Path}, nil } diff --git a/pkg/volume/host_path/host_path_test.go b/pkg/volume/host_path/host_path_test.go index 0c57e7d04cc..8ed9251e97c 100644 --- a/pkg/volume/host_path/host_path_test.go +++ b/pkg/volume/host_path/host_path_test.go @@ -68,7 +68,7 @@ func TestPlugin(t *testing.T) { Name: "vol1", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{"/vol1"}}, } - builder, err := plug.NewBuilder(spec, &api.ObjectReference{UID: types.UID("poduid")}) + builder, err := plug.NewBuilder(spec, &api.ObjectReference{UID: types.UID("poduid")}, volume.VolumeOptions{}) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } diff --git a/pkg/volume/iscsi/iscsi.go b/pkg/volume/iscsi/iscsi.go index f43ecd0fe40..4e1aa611d3f 100644 --- a/pkg/volume/iscsi/iscsi.go +++ b/pkg/volume/iscsi/iscsi.go @@ -72,7 +72,7 @@ func (plugin *ISCSIPlugin) GetAccessModes() []api.AccessModeType { } } -func (plugin *ISCSIPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *ISCSIPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, _ volume.VolumeOptions) (volume.Builder, error) { // Inject real implementations here, test through the internal function. return plugin.newBuilderInternal(spec, podRef.UID, &ISCSIUtil{}, mount.New()) } diff --git a/pkg/volume/nfs/nfs.go b/pkg/volume/nfs/nfs.go index 3beced38686..e2e4baf055c 100644 --- a/pkg/volume/nfs/nfs.go +++ b/pkg/volume/nfs/nfs.go @@ -65,7 +65,7 @@ func (plugin *nfsPlugin) GetAccessModes() []api.AccessModeType { } } -func (plugin *nfsPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { +func (plugin *nfsPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, _ volume.VolumeOptions) (volume.Builder, error) { return plugin.newBuilderInternal(spec, podRef, plugin.mounter) } diff --git a/pkg/volume/plugins.go b/pkg/volume/plugins.go index e88ca168a55..ac98aa25f23 100644 --- a/pkg/volume/plugins.go +++ b/pkg/volume/plugins.go @@ -29,6 +29,17 @@ import ( "github.com/golang/glog" ) +// VolumeOptions contains option information about a volume. +// +// Currently, this struct containers only a single field for the +// rootcontext of the volume. This is a temporary measure in order +// to set the rootContext of tmpfs mounts correctly; it will be replaced +// and expanded on by future SecurityContext work. +type VolumeOptions struct { + // The rootcontext to use when performing mounts for a volume. + RootContext string +} + // VolumePlugin is an interface to volume plugins that can be used on a // kubernetes node (e.g. by kubelet) to instantiate and manage volumes. type VolumePlugin interface { @@ -51,7 +62,7 @@ type VolumePlugin interface { // Ownership of the spec pointer in *not* transferred. // - spec: The api.Volume spec // - podRef: a reference to the enclosing pod - NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (Builder, error) + NewBuilder(spec *api.Volume, podRef *api.ObjectReference, opts VolumeOptions) (Builder, error) // NewCleaner creates a new volume.Cleaner from recoverable state. // - name: The volume name, as per the api.Volume spec. @@ -94,7 +105,7 @@ type VolumeHost interface { // the provided spec. This is used to implement volume plugins which // "wrap" other plugins. For example, the "secret" volume is // implemented in terms of the "emptyDir" volume. - NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference) (Builder, error) + NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference, opts VolumeOptions) (Builder, error) // NewWrapperCleaner finds an appropriate plugin with which to handle // the provided spec. See comments on NewWrapperBuilder for more diff --git a/pkg/volume/secret/secret.go b/pkg/volume/secret/secret.go index 07b977010db..28a71024571 100644 --- a/pkg/volume/secret/secret.go +++ b/pkg/volume/secret/secret.go @@ -58,12 +58,12 @@ func (plugin *secretPlugin) CanSupport(spec *api.Volume) bool { return false } -func (plugin *secretPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { - return plugin.newBuilderInternal(spec, podRef) +func (plugin *secretPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) { + return plugin.newBuilderInternal(spec, podRef, opts) } -func (plugin *secretPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { - return &secretVolume{spec.Name, *podRef, plugin, spec.Secret.SecretName}, nil +func (plugin *secretPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) { + return &secretVolume{spec.Name, *podRef, plugin, spec.Secret.SecretName, &opts}, nil } func (plugin *secretPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { @@ -71,7 +71,7 @@ func (plugin *secretPlugin) NewCleaner(volName string, podUID types.UID) (volume } func (plugin *secretPlugin) newCleanerInternal(volName string, podUID types.UID) (volume.Cleaner, error) { - return &secretVolume{volName, api.ObjectReference{UID: podUID}, plugin, ""}, nil + return &secretVolume{volName, api.ObjectReference{UID: podUID}, plugin, "", nil}, nil } // secretVolume handles retrieving secrets from the API server @@ -81,6 +81,7 @@ type secretVolume struct { podRef api.ObjectReference plugin *secretPlugin secretName string + opts *volume.VolumeOptions } func (sv *secretVolume) SetUp() error { @@ -97,7 +98,7 @@ func (sv *secretVolume) SetUpAt(dir string) error { glog.V(3).Infof("Setting up volume %v for pod %v at %v", sv.volName, sv.podRef.UID, dir) // Wrap EmptyDir, let it do the setup. - wrapped, err := sv.plugin.host.NewWrapperBuilder(wrappedVolumeSpec, &sv.podRef) + wrapped, err := sv.plugin.host.NewWrapperBuilder(wrappedVolumeSpec, &sv.podRef, *sv.opts) if err != nil { return err } @@ -126,7 +127,7 @@ func (sv *secretVolume) SetUpAt(dir string) error { for name, data := range secret.Data { hostFilePath := path.Join(dir, name) glog.V(3).Infof("Writing secret data %v/%v/%v (%v bytes) to host file %v", sv.podRef.Namespace, sv.secretName, name, len(data), hostFilePath) - err := ioutil.WriteFile(hostFilePath, data, 0777) + err := ioutil.WriteFile(hostFilePath, data, 0444) if err != nil { glog.Errorf("Error writing secret data to host path: %v, %v", hostFilePath, err) return err diff --git a/pkg/volume/secret/secret_test.go b/pkg/volume/secret/secret_test.go index 1095fa2d173..93d42897a46 100644 --- a/pkg/volume/secret/secret_test.go +++ b/pkg/volume/secret/secret_test.go @@ -97,7 +97,7 @@ func TestPlugin(t *testing.T) { t.Errorf("Can't find the plugin by name") } - builder, err := plugin.NewBuilder(volumeSpec, &api.ObjectReference{UID: types.UID(testPodUID)}) + builder, err := plugin.NewBuilder(volumeSpec, &api.ObjectReference{UID: types.UID(testPodUID)}, volume.VolumeOptions{}) if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } diff --git a/pkg/volume/testing.go b/pkg/volume/testing.go index 0461fbb953f..790ae0466be 100644 --- a/pkg/volume/testing.go +++ b/pkg/volume/testing.go @@ -55,12 +55,12 @@ func (f *fakeVolumeHost) GetKubeClient() client.Interface { return f.kubeClient } -func (f *fakeVolumeHost) NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference) (Builder, error) { +func (f *fakeVolumeHost) NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference, opts VolumeOptions) (Builder, error) { plug, err := f.pluginMgr.FindPluginBySpec(spec) if err != nil { return nil, err } - return plug.NewBuilder(spec, podRef) + return plug.NewBuilder(spec, podRef, opts) } func (f *fakeVolumeHost) NewWrapperCleaner(spec *api.Volume, podUID types.UID) (Cleaner, error) { @@ -95,7 +95,7 @@ func (plugin *FakeVolumePlugin) CanSupport(spec *api.Volume) bool { return true } -func (plugin *FakeVolumePlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (Builder, error) { +func (plugin *FakeVolumePlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, opts VolumeOptions) (Builder, error) { return &FakeVolume{podRef.UID, spec.Name, plugin}, nil } diff --git a/test/e2e/secrets.go b/test/e2e/secrets.go index ff0fea1b2a3..6edf4a98d49 100644 --- a/test/e2e/secrets.go +++ b/test/e2e/secrets.go @@ -83,9 +83,11 @@ var _ = Describe("Secrets", func() { }, Containers: []api.Container{ { - Name: "catcont", - Image: "gcr.io/google_containers/busybox", - Command: []string{"sh", "-c", "cat /etc/secret-volume/data-1"}, + Name: "catcont", + Image: "kubernetes/mounttest:0.1", + Args: []string{ + "--file_content=/etc/secret-volume/data-1", + "--file_mode=/etc/secret-volume/data-1"}, VolumeMounts: []api.VolumeMount{ { Name: volumeName, @@ -100,7 +102,8 @@ var _ = Describe("Secrets", func() { } testContainerOutput("consume secrets", c, pod, []string{ - "value-1", + "content of file \"/etc/secret-volume/data-1\": value-1", + "mode of file \"/etc/secret-volume/data-1\": -r--r--r--", }) }) })