mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Merge pull request #6379 from pmorie/rootcontext
Skeletal security context to facilitate tmpfs mount
This commit is contained in:
commit
f318da8344
47
pkg/kubelet/root_context_linux.go
Normal file
47
pkg/kubelet/root_context_linux.go
Normal 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
|
||||||
|
}
|
24
pkg/kubelet/root_context_unsupported.go
Normal file
24
pkg/kubelet/root_context_unsupported.go
Normal 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
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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--",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user