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
20 changed files with 158 additions and 43 deletions

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.
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/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 {

View File

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

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.
return plugin.newBuilderInternal(spec, podRef.UID, &GCEDiskUtil{}, mount.New())
}

View File

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

View File

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

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 {
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")
}

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
ns := api.NamespaceDefault
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
}

View File

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

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.
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)
}

View File

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

View File

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

View File

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

View File

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