Merge pull request #6379 from pmorie/rootcontext

Skeletal security context to facilitate tmpfs mount
This commit is contained in:
Tim Hockin 2015-04-13 12:01:11 -07:00
commit f318da8344
20 changed files with 158 additions and 43 deletions

View File

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

View File

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

View File

@ -54,8 +54,8 @@ func (vh *volumeHost) GetKubeClient() client.Interface {
return vh.kubelet.kubeClient return vh.kubelet.kubeClient
} }
func (vh *volumeHost) NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { func (vh *volumeHost) NewWrapperBuilder(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) {
b, err := vh.kubelet.newVolumeBuilderFromPlugins(spec, podRef) b, err := vh.kubelet.newVolumeBuilderFromPlugins(spec, podRef, opts)
if err == nil && b == nil { if err == nil && b == nil {
return nil, errUnsupportedVolumeType return nil, errUnsupportedVolumeType
} }
@ -78,7 +78,7 @@ func (vh *volumeHost) NewWrapperCleaner(spec *api.Volume, podUID types.UID) (vol
return c, nil 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) plugin, err := kl.volumePluginMgr.FindPluginBySpec(spec)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't use volume plugins for %s: %v", spew.Sprintf("%#v", *spec), err) 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 // Not found but not an error
return nil, nil return nil, nil
} }
builder, err := plugin.NewBuilder(spec, podRef) builder, err := plugin.NewBuilder(spec, podRef, opts)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to instantiate volume plugin for %s: %v", spew.Sprintf("%#v", *spec), err) 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 return nil, err
} }
rootContext, err := kl.getRootDirContext()
if err != nil {
return nil, err
}
// Try to use a plugin for this volume. // 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 { if err != nil {
glog.Errorf("Could not create volume builder for pod %s: %v", pod.UID, err) glog.Errorf("Could not create volume builder for pod %s: %v", pod.UID, err)
return nil, err return nil, err

View File

@ -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. // Inject real implementations here, test through the internal function.
return plugin.newBuilderInternal(spec, podRef.UID, &AWSDiskUtil{}, mount.New()) return plugin.newBuilderInternal(spec, podRef.UID, &AWSDiskUtil{}, mount.New())
} }

View File

@ -25,6 +25,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
"github.com/golang/glog"
) )
// This is the primary entrypoint for volume plugins. // This is the primary entrypoint for volume plugins.
@ -80,12 +81,12 @@ func (plugin *emptyDirPlugin) CanSupport(spec *api.Volume) bool {
return false 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. // 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 { if plugin.legacyMode {
// Legacy mode instances can be cleaned up but not created anew. // Legacy mode instances can be cleaned up but not created anew.
return nil, fmt.Errorf("legacy mode: can not create new instances") 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, mountDetector: mountDetector,
plugin: plugin, plugin: plugin,
legacyMode: false, legacyMode: false,
rootContext: opts.RootContext,
}, nil }, nil
} }
@ -154,6 +156,7 @@ type emptyDir struct {
mountDetector mountDetector mountDetector mountDetector
plugin *emptyDirPlugin plugin *emptyDirPlugin
legacyMode bool legacyMode bool
rootContext string
} }
// SetUp creates new directory. // SetUp creates new directory.
@ -192,10 +195,29 @@ func (ed *emptyDir) setupTmpfs(dir string) error {
if err != nil { if err != nil {
return err 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 { 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 { func (ed *emptyDir) GetPath() string {

View File

@ -74,7 +74,7 @@ func TestPlugin(t *testing.T) {
} }
mounter := mount.FakeMounter{} mounter := mount.FakeMounter{}
mountDetector := fakeMountDetector{} 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 { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) t.Errorf("Failed to make a new Builder: %v", err)
} }
@ -133,7 +133,7 @@ func TestPluginTmpfs(t *testing.T) {
} }
mounter := mount.FakeMounter{} mounter := mount.FakeMounter{}
mountDetector := fakeMountDetector{} 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 { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) t.Errorf("Failed to make a new Builder: %v", err)
} }
@ -197,7 +197,7 @@ func TestPluginBackCompat(t *testing.T) {
spec := &api.Volume{ spec := &api.Volume{
Name: "vol1", 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 { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) 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{}}} 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") t.Errorf("Expected failiure")
} }

View File

@ -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. // Inject real implementations here, test through the internal function.
return plugin.newBuilderInternal(spec, podRef.UID, &GCEDiskUtil{}, mount.New()) return plugin.newBuilderInternal(spec, podRef.UID, &GCEDiskUtil{}, mount.New())
} }

View File

@ -185,7 +185,7 @@ func TestPluginLegacy(t *testing.T) {
t.Errorf("Expected false") 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") t.Errorf("Expected failiure")
} }

View File

@ -70,7 +70,7 @@ func (plugin *gitRepoPlugin) CanSupport(spec *api.Volume) bool {
return false 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 { if plugin.legacyMode {
// Legacy mode instances can be cleaned up but not created anew. // Legacy mode instances can be cleaned up but not created anew.
return nil, fmt.Errorf("legacy mode: can not create new instances") 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(), exec: exec.New(),
plugin: plugin, plugin: plugin,
legacyMode: false, legacyMode: false,
opts: opts,
}, nil }, nil
} }
@ -109,6 +110,7 @@ type gitRepo struct {
exec exec.Interface exec exec.Interface
plugin *gitRepoPlugin plugin *gitRepoPlugin
legacyMode bool legacyMode bool
opts volume.VolumeOptions
} }
// SetUp creates new directory and clones a git repo. // 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. // 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 { if err != nil {
return err return err
} }

View File

@ -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 { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) t.Errorf("Failed to make a new Builder: %v", err)
} }
@ -173,7 +173,7 @@ func TestPluginLegacy(t *testing.T) {
t.Errorf("Expected false") 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") t.Errorf("Expected failiure")
} }

View File

@ -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 ep_name := spec.VolumeSource.Glusterfs.EndpointsName
ns := api.NamespaceDefault ns := api.NamespaceDefault
ep, err := plugin.host.GetKubeClient().Endpoints(ns).Get(ep_name) ep, err := plugin.host.GetKubeClient().Endpoints(ns).Get(ep_name)

View File

@ -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 return &hostPath{spec.HostPath.Path}, nil
} }

View File

@ -68,7 +68,7 @@ func TestPlugin(t *testing.T) {
Name: "vol1", Name: "vol1",
VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{"/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 { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) t.Errorf("Failed to make a new Builder: %v", err)
} }

View File

@ -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. // Inject real implementations here, test through the internal function.
return plugin.newBuilderInternal(spec, podRef.UID, &ISCSIUtil{}, mount.New()) return plugin.newBuilderInternal(spec, podRef.UID, &ISCSIUtil{}, mount.New())
} }

View File

@ -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) return plugin.newBuilderInternal(spec, podRef, plugin.mounter)
} }

View File

@ -29,6 +29,17 @@ import (
"github.com/golang/glog" "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 // VolumePlugin is an interface to volume plugins that can be used on a
// kubernetes node (e.g. by kubelet) to instantiate and manage volumes. // kubernetes node (e.g. by kubelet) to instantiate and manage volumes.
type VolumePlugin interface { type VolumePlugin interface {
@ -51,7 +62,7 @@ type VolumePlugin interface {
// Ownership of the spec pointer in *not* transferred. // Ownership of the spec pointer in *not* transferred.
// - spec: The api.Volume spec // - spec: The api.Volume spec
// - podRef: a reference to the enclosing pod // - 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. // NewCleaner creates a new volume.Cleaner from recoverable state.
// - name: The volume name, as per the api.Volume spec. // - 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 // the provided spec. This is used to implement volume plugins which
// "wrap" other plugins. For example, the "secret" volume is // "wrap" other plugins. For example, the "secret" volume is
// implemented in terms of the "emptyDir" volume. // 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 // NewWrapperCleaner finds an appropriate plugin with which to handle
// the provided spec. See comments on NewWrapperBuilder for more // the provided spec. See comments on NewWrapperBuilder for more

View File

@ -58,12 +58,12 @@ func (plugin *secretPlugin) CanSupport(spec *api.Volume) bool {
return false return false
} }
func (plugin *secretPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { func (plugin *secretPlugin) NewBuilder(spec *api.Volume, podRef *api.ObjectReference, opts volume.VolumeOptions) (volume.Builder, error) {
return plugin.newBuilderInternal(spec, podRef) return plugin.newBuilderInternal(spec, podRef, opts)
} }
func (plugin *secretPlugin) newBuilderInternal(spec *api.Volume, podRef *api.ObjectReference) (volume.Builder, error) { 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}, nil return &secretVolume{spec.Name, *podRef, plugin, spec.Secret.SecretName, &opts}, nil
} }
func (plugin *secretPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { 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) { 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 // secretVolume handles retrieving secrets from the API server
@ -81,6 +81,7 @@ type secretVolume struct {
podRef api.ObjectReference podRef api.ObjectReference
plugin *secretPlugin plugin *secretPlugin
secretName string secretName string
opts *volume.VolumeOptions
} }
func (sv *secretVolume) SetUp() error { 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) 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. // 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 { if err != nil {
return err return err
} }
@ -126,7 +127,7 @@ func (sv *secretVolume) SetUpAt(dir string) error {
for name, data := range secret.Data { for name, data := range secret.Data {
hostFilePath := path.Join(dir, name) 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) 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 { if err != nil {
glog.Errorf("Error writing secret data to host path: %v, %v", hostFilePath, err) glog.Errorf("Error writing secret data to host path: %v, %v", hostFilePath, err)
return err return err

View File

@ -97,7 +97,7 @@ func TestPlugin(t *testing.T) {
t.Errorf("Can't find the plugin by name") 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 { if err != nil {
t.Errorf("Failed to make a new Builder: %v", err) t.Errorf("Failed to make a new Builder: %v", err)
} }

View File

@ -55,12 +55,12 @@ func (f *fakeVolumeHost) GetKubeClient() client.Interface {
return f.kubeClient 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) plug, err := f.pluginMgr.FindPluginBySpec(spec)
if err != nil { if err != nil {
return nil, err 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) { 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 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 return &FakeVolume{podRef.UID, spec.Name, plugin}, nil
} }

View File

@ -83,9 +83,11 @@ var _ = Describe("Secrets", func() {
}, },
Containers: []api.Container{ Containers: []api.Container{
{ {
Name: "catcont", Name: "catcont",
Image: "gcr.io/google_containers/busybox", Image: "kubernetes/mounttest:0.1",
Command: []string{"sh", "-c", "cat /etc/secret-volume/data-1"}, Args: []string{
"--file_content=/etc/secret-volume/data-1",
"--file_mode=/etc/secret-volume/data-1"},
VolumeMounts: []api.VolumeMount{ VolumeMounts: []api.VolumeMount{
{ {
Name: volumeName, Name: volumeName,
@ -100,7 +102,8 @@ var _ = Describe("Secrets", func() {
} }
testContainerOutput("consume secrets", c, pod, []string{ 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--",
}) })
}) })
}) })