From c0d49450e48723b4c38a688c7c9c76bffc2a54d2 Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 10 Dec 2015 18:07:17 +0800 Subject: [PATCH 1/2] Fix volume wrapper doesn't have name Integration test --- cmd/integration/integration.go | 91 +++++++++++++++++++++- pkg/volume/downwardapi/downwardapi.go | 26 ++++--- pkg/volume/downwardapi/downwardapi_test.go | 42 +++++++--- pkg/volume/git_repo/git_repo.go | 31 ++++---- pkg/volume/git_repo/git_repo_test.go | 26 +++++-- pkg/volume/secret/secret.go | 39 +++++++--- pkg/volume/secret/secret_test.go | 20 +++-- pkg/volume/volume.go | 24 ++++++ 8 files changed, 234 insertions(+), 65 deletions(-) diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index ca5804a6751..b6d5d693b13 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -55,7 +55,10 @@ import ( "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/wait" + "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/empty_dir" + "k8s.io/kubernetes/pkg/volume/git_repo" + "k8s.io/kubernetes/pkg/volume/secret" "k8s.io/kubernetes/plugin/pkg/scheduler" _ "k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider" "k8s.io/kubernetes/plugin/pkg/scheduler/factory" @@ -211,7 +214,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string 10250, /* KubeletPort */ 0, /* ReadOnlyPort */ api.NamespaceDefault, - empty_dir.ProbeVolumePlugins(), + probeVolumePlugins(), nil, cadvisorInterface, configFilePath, @@ -244,7 +247,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string 10251, /* KubeletPort */ 0, /* ReadOnlyPort */ api.NamespaceDefault, - empty_dir.ProbeVolumePlugins(), + probeVolumePlugins(), nil, cadvisorInterface, "", @@ -265,6 +268,16 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string return apiServer.URL, configFilePath } +// The list of plugins to probe in this test +func probeVolumePlugins() []volume.VolumePlugin { + allPlugins := []volume.VolumePlugin{} + + allPlugins = append(allPlugins, empty_dir.ProbeVolumePlugins()...) + allPlugins = append(allPlugins, git_repo.ProbeVolumePlugins()...) + allPlugins = append(allPlugins, secret.ProbeVolumePlugins()...) + return allPlugins +} + func makeTempDirOrDie(prefix string, baseDir string) string { if baseDir == "" { baseDir = "/tmp" @@ -989,6 +1002,7 @@ func main() { runServiceTest, runAPIVersionsTest, runMasterServiceTest, + runWrapperVolumesPluginsTest, func(c *client.Client) { runSelfLinkTestOnNamespace(c, api.NamespaceDefault) runSelfLinkTestOnNamespace(c, "other") @@ -1038,8 +1052,8 @@ func main() { // 1 pod infra container + 1 container from the service test. // The total number of container created is 9 - if len(createdConts) != 12 { - glog.Fatalf("Expected 12 containers; got %v\n\nlist of created containers:\n\n%#v\n\nDocker 1 Created:\n\n%#v\n\nDocker 2 Created:\n\n%#v\n\n", len(createdConts), createdConts.List(), fakeDocker1.Created, fakeDocker2.Created) + if len(createdConts) != 14 { + glog.Fatalf("Expected 14 containers; got %v\n\nlist of created containers:\n\n%#v\n\nDocker 1 Created:\n\n%#v\n\nDocker 2 Created:\n\n%#v\n\n", len(createdConts), createdConts.List(), fakeDocker1.Created, fakeDocker2.Created) } glog.Infof("OK - found created containers: %#v", createdConts.List()) @@ -1097,3 +1111,72 @@ const ( } }` ) + +// This is a test to make sure multiple empty_dir wrapper based volumes should not affect each other +func runWrapperVolumesPluginsTest(client *client.Client) { + // Make a secret object. + ns := api.NamespaceDefault + s := api.Secret{ + ObjectMeta: api.ObjectMeta{ + Name: "secret-name", + Namespace: ns, + }, + Data: map[string][]byte{ + "data": []byte("value1\n"), + }, + } + + if _, err := client.Secrets(s.Namespace).Create(&s); err != nil { + glog.Fatalf("unable to create test secret: %v", err) + } + + pod := &api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "vol-container", + Image: "kubernetes/pause", + VolumeMounts: []api.VolumeMount{ + { + Name: "vol-secret", + MountPath: "/fake/path-1", + ReadOnly: true, + }, + { + Name: "vol-git", + MountPath: "/fake/path-2", + }, + }, + }, + }, + Volumes: []api.Volume{ + { + Name: "vol-secret", + VolumeSource: api.VolumeSource{ + Secret: &api.SecretVolumeSource{ + SecretName: "secret-name", + }, + }, + }, + { + Name: "vol-git", + VolumeSource: api.VolumeSource{ + GitRepo: &api.GitRepoVolumeSource{ + Repository: "https://github.com/kubernetes/kubedash", + }, + }, + }, + }, + }, + } + + pod.ObjectMeta.Name = "wrappedvolumes.foo" + foo, err := client.Pods(api.NamespaceDefault).Create(pod) + if err != nil { + glog.Fatalf("Failed to create pod: %v, %v", pod, err) + } + if err := wait.Poll(time.Second, longTestTimeout, podRunning(client, foo.Namespace, foo.Name)); err != nil { + glog.Fatalf("FAILED: pod never started running %v", err) + } + glog.Info("empty_dir wrapper based volumes doesn't affect each other: test passed.") +} diff --git a/pkg/volume/downwardapi/downwardapi.go b/pkg/volume/downwardapi/downwardapi.go index be71ab431c4..eb2602bfc90 100644 --- a/pkg/volume/downwardapi/downwardapi.go +++ b/pkg/volume/downwardapi/downwardapi.go @@ -66,10 +66,11 @@ func (plugin *downwardAPIPlugin) CanSupport(spec *volume.Spec) bool { func (plugin *downwardAPIPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { v := &downwardAPIVolume{ - volName: spec.Name(), - pod: pod, - podUID: pod.UID, - plugin: plugin, + volName: spec.Name(), + pod: pod, + podUID: pod.UID, + plugin: plugin, + wrappedVolumeSpec: volume.GetWrappedVolumeSpec(spec.Name(), downwardAPIPluginName), } v.fieldReferenceFileNames = make(map[string]string) for _, fileInfo := range spec.Volume.DownwardAPI.Items { @@ -81,7 +82,12 @@ func (plugin *downwardAPIPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opt } func (plugin *downwardAPIPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { - return &downwardAPIVolumeCleaner{&downwardAPIVolume{volName: volName, podUID: podUID, plugin: plugin}}, nil + return &downwardAPIVolumeCleaner{&downwardAPIVolume{ + volName: volName, + podUID: podUID, + plugin: plugin, + wrappedVolumeSpec: volume.GetWrappedVolumeSpec(volName, downwardAPIPluginName)}, + }, nil } // downwardAPIVolume retrieves downward API data and placing them into the volume on the host. @@ -92,11 +98,7 @@ type downwardAPIVolume struct { podUID types.UID // TODO: remove this redundancy as soon NewCleaner func will have *api.POD and not only types.UID plugin *downwardAPIPlugin volume.MetricsNil -} - -// This is the spec for the volume that this plugin wraps. -var wrappedVolumeSpec = &volume.Spec{ - Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}}, + wrappedVolumeSpec *volume.Spec } // downwardAPIVolumeBuilder fetches info from downward API from the pod @@ -130,7 +132,7 @@ func (b *downwardAPIVolumeBuilder) SetUp() error { func (b *downwardAPIVolumeBuilder) SetUpAt(dir string) error { glog.V(3).Infof("Setting up a downwardAPI volume %v for pod %v/%v at %v", b.volName, b.pod.Namespace, b.pod.Name, dir) // Wrap EmptyDir. Here we rely on the idempotency of the wrapped plugin to avoid repeatedly mounting - wrapped, err := b.plugin.host.NewWrapperBuilder(wrappedVolumeSpec, b.pod, *b.opts) + wrapped, err := b.plugin.host.NewWrapperBuilder(b.wrappedVolumeSpec, b.pod, *b.opts) if err != nil { glog.Errorf("Couldn't setup downwardAPI volume %v for pod %v/%v: %s", b.volName, b.pod.Namespace, b.pod.Name, err.Error()) return err @@ -364,7 +366,7 @@ func (c *downwardAPIVolumeCleaner) TearDownAt(dir string) error { glog.V(3).Infof("Tearing down volume %v for pod %v at %v", c.volName, c.podUID, dir) // Wrap EmptyDir, let it do the teardown. - wrapped, err := c.plugin.host.NewWrapperCleaner(wrappedVolumeSpec, c.podUID) + wrapped, err := c.plugin.host.NewWrapperCleaner(c.wrappedVolumeSpec, c.podUID) if err != nil { return err } diff --git a/pkg/volume/downwardapi/downwardapi_test.go b/pkg/volume/downwardapi/downwardapi_test.go index 6c2559e38d4..cec87886011 100644 --- a/pkg/volume/downwardapi/downwardapi_test.go +++ b/pkg/volume/downwardapi/downwardapi_test.go @@ -39,12 +39,12 @@ func formatMap(m map[string]string) (fmtstr string) { return } -func newTestHost(t *testing.T, client client.Interface, basePath string) volume.VolumeHost { +func newTestHost(t *testing.T, client client.Interface, basePath string) (string, volume.VolumeHost) { tempDir, err := ioutil.TempDir(basePath, "downwardApi_volume_test.") if err != nil { t.Fatalf("can't make a temp rootdir: %v", err) } - return volume.NewFakeVolumeHost(tempDir, client, empty_dir.ProbeVolumePlugins()) + return tempDir, volume.NewFakeVolumeHost(tempDir, client, empty_dir.ProbeVolumePlugins()) } func TestCanSupport(t *testing.T) { @@ -54,7 +54,8 @@ func TestCanSupport(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, nil, tmpDir)) + _, host := newTestHost(t, nil, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) if err != nil { @@ -110,7 +111,8 @@ func TestLabels(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + rootDir, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) volumeSpec := &api.Volume{ Name: testVolumeName, @@ -141,6 +143,17 @@ func TestLabels(t *testing.T) { t.Errorf("Failed to setup volume: %v", err) } + // gitRepo volume should create it's own empty wrapper path + podWrapperMetadataDir := fmt.Sprintf("%v/pods/%v/plugins/kubernetes.io~empty-dir/wrapped_%v", rootDir, testPodUID, testVolumeName) + + if _, err := os.Stat(podWrapperMetadataDir); err != nil { + if os.IsNotExist(err) { + t.Errorf("SetUp() failed, empty-dir wrapper path was not created: %s", podWrapperMetadataDir) + } else { + t.Errorf("SetUp() failed: %v", err) + } + } + var data []byte data, err = ioutil.ReadFile(path.Join(volumePath, "labels")) if err != nil { @@ -189,7 +202,8 @@ func TestAnnotations(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + _, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) if err != nil { t.Errorf("Can't find the plugin by name") @@ -254,7 +268,8 @@ func TestName(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + _, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) if err != nil { t.Errorf("Can't find the plugin by name") @@ -320,7 +335,8 @@ func TestNamespace(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + _, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) if err != nil { t.Errorf("Can't find the plugin by name") @@ -379,7 +395,8 @@ func TestWriteTwiceNoUpdate(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + _, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) volumeSpec := &api.Volume{ Name: testVolumeName, @@ -468,7 +485,8 @@ func TestWriteTwiceWithUpdate(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + _, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) volumeSpec := &api.Volume{ Name: testVolumeName, @@ -577,7 +595,8 @@ func TestWriteWithUnixPath(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + _, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) volumeSpec := &api.Volume{ Name: testVolumeName, @@ -656,7 +675,8 @@ func TestWriteWithUnixPathBadPath(t *testing.T) { } defer os.RemoveAll(tmpDir) pluginMgr := volume.VolumePluginMgr{} - pluginMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t, fake, tmpDir)) + _, host := newTestHost(t, fake, tmpDir) + pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) if err != nil { t.Errorf("Can't find the plugin by name") diff --git a/pkg/volume/git_repo/git_repo.go b/pkg/volume/git_repo/git_repo.go index cd6bd05c5a6..5c329eddac8 100644 --- a/pkg/volume/git_repo/git_repo.go +++ b/pkg/volume/git_repo/git_repo.go @@ -61,9 +61,10 @@ func (plugin *gitRepoPlugin) CanSupport(spec *volume.Spec) bool { func (plugin *gitRepoPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { return &gitRepoVolumeBuilder{ gitRepoVolume: &gitRepoVolume{ - volName: spec.Name(), - podUID: pod.UID, - plugin: plugin, + volName: spec.Name(), + podUID: pod.UID, + plugin: plugin, + wrappedVolumeSpec: volume.GetWrappedVolumeSpec(spec.Name(), gitRepoPluginName), }, pod: *pod, source: spec.Volume.GitRepo.Repository, @@ -77,9 +78,10 @@ func (plugin *gitRepoPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts vo func (plugin *gitRepoPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { return &gitRepoVolumeCleaner{ &gitRepoVolume{ - volName: volName, - podUID: podUID, - plugin: plugin, + volName: volName, + podUID: podUID, + plugin: plugin, + wrappedVolumeSpec: volume.GetWrappedVolumeSpec(volName, gitRepoPluginName), }, }, nil } @@ -87,9 +89,10 @@ func (plugin *gitRepoPlugin) NewCleaner(volName string, podUID types.UID) (volum // gitRepo volumes are directories which are pre-filled from a git repository. // These do not persist beyond the lifetime of a pod. type gitRepoVolume struct { - volName string - podUID types.UID - plugin *gitRepoPlugin + volName string + podUID types.UID + plugin *gitRepoPlugin + wrappedVolumeSpec *volume.Spec volume.MetricsNil } @@ -128,11 +131,6 @@ func (b *gitRepoVolumeBuilder) SetUp() error { return b.SetUpAt(b.GetPath()) } -// This is the spec for the volume that this plugin wraps. -var wrappedVolumeSpec = &volume.Spec{ - Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, -} - // SetUpAt creates new directory and clones a git repo. func (b *gitRepoVolumeBuilder) SetUpAt(dir string) error { if volumeutil.IsReady(b.getMetaDir()) { @@ -140,7 +138,7 @@ func (b *gitRepoVolumeBuilder) SetUpAt(dir string) error { } // Wrap EmptyDir, let it do the setup. - wrapped, err := b.plugin.host.NewWrapperBuilder(wrappedVolumeSpec, &b.pod, b.opts) + wrapped, err := b.plugin.host.NewWrapperBuilder(b.wrappedVolumeSpec, &b.pod, b.opts) if err != nil { return err } @@ -218,8 +216,9 @@ func (c *gitRepoVolumeCleaner) TearDown() error { // TearDownAt simply deletes everything in the directory. func (c *gitRepoVolumeCleaner) TearDownAt(dir string) error { + // Wrap EmptyDir, let it do the teardown. - wrapped, err := c.plugin.host.NewWrapperCleaner(wrappedVolumeSpec, c.podUID) + wrapped, err := c.plugin.host.NewWrapperCleaner(c.wrappedVolumeSpec, c.podUID) 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 e4c588d9dfa..d2deab7df92 100644 --- a/pkg/volume/git_repo/git_repo_test.go +++ b/pkg/volume/git_repo/git_repo_test.go @@ -32,17 +32,18 @@ import ( "k8s.io/kubernetes/pkg/volume/empty_dir" ) -func newTestHost(t *testing.T) volume.VolumeHost { +func newTestHost(t *testing.T) (string, volume.VolumeHost) { tempDir, err := ioutil.TempDir("/tmp", "git_repo_test.") if err != nil { t.Fatalf("can't make a temp rootdir: %v", err) } - return volume.NewFakeVolumeHost(tempDir, nil, empty_dir.ProbeVolumePlugins()) + return tempDir, volume.NewFakeVolumeHost(tempDir, nil, empty_dir.ProbeVolumePlugins()) } func TestCanSupport(t *testing.T) { plugMgr := volume.VolumePluginMgr{} - plugMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t)) + _, host := newTestHost(t) + plugMgr.InitPlugins(ProbeVolumePlugins(), host) plug, err := plugMgr.FindPluginByName("kubernetes.io/git-repo") if err != nil { @@ -218,7 +219,8 @@ func doTestPlugin(scenario struct { allErrs := []error{} plugMgr := volume.VolumePluginMgr{} - plugMgr.InitPlugins(ProbeVolumePlugins(), newTestHost(t)) + rootDir, host := newTestHost(t) + plugMgr.InitPlugins(ProbeVolumePlugins(), host) plug, err := plugMgr.FindPluginByName("kubernetes.io/git-repo") if err != nil { @@ -241,7 +243,8 @@ func doTestPlugin(scenario struct { } path := builder.GetPath() - if !strings.HasSuffix(path, "pods/poduid/volumes/kubernetes.io~git-repo/vol1") { + suffix := fmt.Sprintf("pods/poduid/volumes/kubernetes.io~git-repo/%v", scenario.vol.Name) + if !strings.HasSuffix(path, suffix) { allErrs = append(allErrs, fmt.Errorf("Got unexpected path: %s", path)) return allErrs @@ -263,6 +266,19 @@ func doTestPlugin(scenario struct { } } + // gitRepo volume should create it's own empty wrapper path + podWrapperMetadataDir := fmt.Sprintf("%v/pods/poduid/plugins/kubernetes.io~empty-dir/wrapped_%v", rootDir, scenario.vol.Name) + + if _, err := os.Stat(podWrapperMetadataDir); err != nil { + if os.IsNotExist(err) { + allErrs = append(allErrs, + fmt.Errorf("SetUp() failed, empty-dir wrapper path is not created: %s", podWrapperMetadataDir)) + } else { + allErrs = append(allErrs, + fmt.Errorf("SetUp() failed: %v", err)) + } + } + cleaner, err := plug.NewCleaner("vol1", types.UID("poduid")) if err != nil { allErrs = append(allErrs, diff --git a/pkg/volume/secret/secret.go b/pkg/volume/secret/secret.go index 2bb426d3c60..5a9ed5b8908 100644 --- a/pkg/volume/secret/secret.go +++ b/pkg/volume/secret/secret.go @@ -62,14 +62,33 @@ func (plugin *secretPlugin) CanSupport(spec *volume.Spec) bool { func (plugin *secretPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { return &secretVolumeBuilder{ - secretVolume: &secretVolume{spec.Name(), pod.UID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}}, - secretName: spec.Volume.Secret.SecretName, - pod: *pod, - opts: &opts}, nil + secretVolume: &secretVolume{ + spec.Name(), + pod.UID, + plugin, + plugin.host.GetMounter(), + plugin.host.GetWriter(), + volume.MetricsNil{}, + volume.GetWrappedVolumeSpec(spec.Name(), secretPluginName), + }, + secretName: spec.Volume.Secret.SecretName, + pod: *pod, + opts: &opts, + }, nil } func (plugin *secretPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { - return &secretVolumeCleaner{&secretVolume{volName, podUID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}}}, nil + return &secretVolumeCleaner{ + &secretVolume{ + volName, + podUID, + plugin, + plugin.host.GetMounter(), + plugin.host.GetWriter(), + volume.MetricsNil{}, + volume.GetWrappedVolumeSpec(volName, secretPluginName), + }, + }, nil } type secretVolume struct { @@ -79,6 +98,7 @@ type secretVolume struct { mounter mount.Interface writer ioutil.Writer volume.MetricsNil + wrappedVolumeSpec *volume.Spec } var _ volume.Volume = &secretVolume{} @@ -111,11 +131,6 @@ func (b *secretVolumeBuilder) SetUp() error { return b.SetUpAt(b.GetPath()) } -// This is the spec for the volume that this plugin wraps. -var wrappedVolumeSpec = &volume.Spec{ - Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}}, -} - func (b *secretVolumeBuilder) getMetaDir() string { return path.Join(b.plugin.host.GetPodPluginDir(b.podUID, util.EscapeQualifiedNameForDisk(secretPluginName)), b.volName) } @@ -137,7 +152,7 @@ func (b *secretVolumeBuilder) SetUpAt(dir string) error { glog.V(3).Infof("Setting up volume %v for pod %v at %v", b.volName, b.pod.UID, dir) // Wrap EmptyDir, let it do the setup. - wrapped, err := b.plugin.host.NewWrapperBuilder(wrappedVolumeSpec, &b.pod, *b.opts) + wrapped, err := b.plugin.host.NewWrapperBuilder(b.wrappedVolumeSpec, &b.pod, *b.opts) if err != nil { return err } @@ -202,7 +217,7 @@ func (c *secretVolumeCleaner) TearDownAt(dir string) error { glog.V(3).Infof("Tearing down volume %v for pod %v at %v", c.volName, c.podUID, dir) // Wrap EmptyDir, let it do the teardown. - wrapped, err := c.plugin.host.NewWrapperCleaner(wrappedVolumeSpec, c.podUID) + wrapped, err := c.plugin.host.NewWrapperCleaner(c.wrappedVolumeSpec, c.podUID) if err != nil { return err } diff --git a/pkg/volume/secret/secret_test.go b/pkg/volume/secret/secret_test.go index 5ccc6aa7654..b5d49fb9fb7 100644 --- a/pkg/volume/secret/secret_test.go +++ b/pkg/volume/secret/secret_test.go @@ -70,11 +70,11 @@ func TestPlugin(t *testing.T) { testNamespace = "test_secret_namespace" testName = "test_secret_name" - volumeSpec = volumeSpec(testVolumeName, testName) - secret = secret(testNamespace, testName) - client = testclient.NewSimpleFake(&secret) - pluginMgr = volume.VolumePluginMgr{} - _, host = newTestHost(t, client) + volumeSpec = volumeSpec(testVolumeName, testName) + secret = secret(testNamespace, testName) + client = testclient.NewSimpleFake(&secret) + pluginMgr = volume.VolumePluginMgr{} + rootDir, host = newTestHost(t, client) ) pluginMgr.InitPlugins(ProbeVolumePlugins(), host) @@ -110,6 +110,16 @@ func TestPlugin(t *testing.T) { } } + // secret volume should create it's own empty wrapper path + podWrapperMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid/plugins/kubernetes.io~empty-dir/wrapped_test_volume_name", rootDir) + + if _, err := os.Stat(podWrapperMetadataDir); err != nil { + if os.IsNotExist(err) { + t.Errorf("SetUp() failed, empty-dir wrapper path is not created: %s", podWrapperMetadataDir) + } else { + t.Errorf("SetUp() failed: %v", err) + } + } doTestSecretDataInVolume(volumePath, secret, t) doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t) } diff --git a/pkg/volume/volume.go b/pkg/volume/volume.go index aab01158f2b..7ff136b40a4 100644 --- a/pkg/volume/volume.go +++ b/pkg/volume/volume.go @@ -17,6 +17,7 @@ limitations under the License. package volume import ( + "fmt" "io/ioutil" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" @@ -24,6 +25,10 @@ import ( "path" ) +const ( + randomLength = 5 +) + // Volume represents a directory used by pods or hosts on a node. // All method implementations of methods in the volume interface must be idempotent. type Volume interface { @@ -132,3 +137,22 @@ func RenameDirectory(oldPath, newName string) (string, error) { } return newPath, nil } + +// Return the wrapper volume spec of specific volume plugin +func GetWrappedVolumeSpec(volName string, pluginName string) *Spec { + // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" + wrapperVolumeName := fmt.Sprintf("wrapped_%v", volName) + + var volumeSpec *Spec + switch pluginName { + case "kubernetes.io/downward-api", "kubernetes.io/secret": + volumeSpec = &Spec{ + Volume: &api.Volume{Name: wrapperVolumeName, VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}}, + } + case "kubernetes.io/git-repo": + volumeSpec = &Spec{ + Volume: &api.Volume{Name: wrapperVolumeName, VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, + } + } + return volumeSpec +} From 0fa5b6c4f7f93dd1a349fda292716d5e0088d7ae Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 14 Dec 2015 15:32:37 +0800 Subject: [PATCH 2/2] Add e2e test for wrapper volumes Use git server in e2e and refactor wrapper builder Update e2e test to use a fake git server --- cmd/integration/integration.go | 91 +-------- ...persistentvolume_provisioner_controller.go | 4 +- .../persistentvolume_recycler_controller.go | 4 +- pkg/kubelet/volumes.go | 20 +- pkg/volume/downwardapi/downwardapi.go | 32 ++-- pkg/volume/downwardapi/downwardapi_test.go | 2 +- pkg/volume/git_repo/git_repo.go | 29 +-- pkg/volume/git_repo/git_repo_test.go | 2 +- .../persistent_claim/persistent_claim.go | 2 +- pkg/volume/plugins.go | 4 +- pkg/volume/secret/secret.go | 11 +- pkg/volume/secret/secret_test.go | 2 +- pkg/volume/testing.go | 20 +- pkg/volume/volume.go | 24 --- test/e2e/empty_dir_wrapper.go | 179 ++++++++++++++++++ 15 files changed, 263 insertions(+), 163 deletions(-) create mode 100644 test/e2e/empty_dir_wrapper.go diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index b6d5d693b13..ca5804a6751 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -55,10 +55,7 @@ import ( "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/wait" - "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/empty_dir" - "k8s.io/kubernetes/pkg/volume/git_repo" - "k8s.io/kubernetes/pkg/volume/secret" "k8s.io/kubernetes/plugin/pkg/scheduler" _ "k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider" "k8s.io/kubernetes/plugin/pkg/scheduler/factory" @@ -214,7 +211,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string 10250, /* KubeletPort */ 0, /* ReadOnlyPort */ api.NamespaceDefault, - probeVolumePlugins(), + empty_dir.ProbeVolumePlugins(), nil, cadvisorInterface, configFilePath, @@ -247,7 +244,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string 10251, /* KubeletPort */ 0, /* ReadOnlyPort */ api.NamespaceDefault, - probeVolumePlugins(), + empty_dir.ProbeVolumePlugins(), nil, cadvisorInterface, "", @@ -268,16 +265,6 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string return apiServer.URL, configFilePath } -// The list of plugins to probe in this test -func probeVolumePlugins() []volume.VolumePlugin { - allPlugins := []volume.VolumePlugin{} - - allPlugins = append(allPlugins, empty_dir.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, git_repo.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, secret.ProbeVolumePlugins()...) - return allPlugins -} - func makeTempDirOrDie(prefix string, baseDir string) string { if baseDir == "" { baseDir = "/tmp" @@ -1002,7 +989,6 @@ func main() { runServiceTest, runAPIVersionsTest, runMasterServiceTest, - runWrapperVolumesPluginsTest, func(c *client.Client) { runSelfLinkTestOnNamespace(c, api.NamespaceDefault) runSelfLinkTestOnNamespace(c, "other") @@ -1052,8 +1038,8 @@ func main() { // 1 pod infra container + 1 container from the service test. // The total number of container created is 9 - if len(createdConts) != 14 { - glog.Fatalf("Expected 14 containers; got %v\n\nlist of created containers:\n\n%#v\n\nDocker 1 Created:\n\n%#v\n\nDocker 2 Created:\n\n%#v\n\n", len(createdConts), createdConts.List(), fakeDocker1.Created, fakeDocker2.Created) + if len(createdConts) != 12 { + glog.Fatalf("Expected 12 containers; got %v\n\nlist of created containers:\n\n%#v\n\nDocker 1 Created:\n\n%#v\n\nDocker 2 Created:\n\n%#v\n\n", len(createdConts), createdConts.List(), fakeDocker1.Created, fakeDocker2.Created) } glog.Infof("OK - found created containers: %#v", createdConts.List()) @@ -1111,72 +1097,3 @@ const ( } }` ) - -// This is a test to make sure multiple empty_dir wrapper based volumes should not affect each other -func runWrapperVolumesPluginsTest(client *client.Client) { - // Make a secret object. - ns := api.NamespaceDefault - s := api.Secret{ - ObjectMeta: api.ObjectMeta{ - Name: "secret-name", - Namespace: ns, - }, - Data: map[string][]byte{ - "data": []byte("value1\n"), - }, - } - - if _, err := client.Secrets(s.Namespace).Create(&s); err != nil { - glog.Fatalf("unable to create test secret: %v", err) - } - - pod := &api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "vol-container", - Image: "kubernetes/pause", - VolumeMounts: []api.VolumeMount{ - { - Name: "vol-secret", - MountPath: "/fake/path-1", - ReadOnly: true, - }, - { - Name: "vol-git", - MountPath: "/fake/path-2", - }, - }, - }, - }, - Volumes: []api.Volume{ - { - Name: "vol-secret", - VolumeSource: api.VolumeSource{ - Secret: &api.SecretVolumeSource{ - SecretName: "secret-name", - }, - }, - }, - { - Name: "vol-git", - VolumeSource: api.VolumeSource{ - GitRepo: &api.GitRepoVolumeSource{ - Repository: "https://github.com/kubernetes/kubedash", - }, - }, - }, - }, - }, - } - - pod.ObjectMeta.Name = "wrappedvolumes.foo" - foo, err := client.Pods(api.NamespaceDefault).Create(pod) - if err != nil { - glog.Fatalf("Failed to create pod: %v, %v", pod, err) - } - if err := wait.Poll(time.Second, longTestTimeout, podRunning(client, foo.Namespace, foo.Name)); err != nil { - glog.Fatalf("FAILED: pod never started running %v", err) - } - glog.Info("empty_dir wrapper based volumes doesn't affect each other: test passed.") -} diff --git a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go index 846bc090ee9..394c2cdba04 100644 --- a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go +++ b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go @@ -467,11 +467,11 @@ func (c *PersistentVolumeProvisionerController) GetKubeClient() client.Interface return c.client.GetKubeClient() } -func (c *PersistentVolumeProvisionerController) NewWrapperBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { +func (c *PersistentVolumeProvisionerController) NewWrapperBuilder(volName string, spec volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { return nil, fmt.Errorf("NewWrapperBuilder not supported by PVClaimBinder's VolumeHost implementation") } -func (c *PersistentVolumeProvisionerController) NewWrapperCleaner(spec *volume.Spec, podUID types.UID) (volume.Cleaner, error) { +func (c *PersistentVolumeProvisionerController) NewWrapperCleaner(volName string, spec volume.Spec, podUID types.UID) (volume.Cleaner, error) { return nil, fmt.Errorf("NewWrapperCleaner not supported by PVClaimBinder's VolumeHost implementation") } diff --git a/pkg/controller/persistentvolume/persistentvolume_recycler_controller.go b/pkg/controller/persistentvolume/persistentvolume_recycler_controller.go index cdd08d4f576..57e61d0b360 100644 --- a/pkg/controller/persistentvolume/persistentvolume_recycler_controller.go +++ b/pkg/controller/persistentvolume/persistentvolume_recycler_controller.go @@ -289,11 +289,11 @@ func (f *PersistentVolumeRecycler) GetKubeClient() client.Interface { return f.kubeClient } -func (f *PersistentVolumeRecycler) NewWrapperBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { +func (f *PersistentVolumeRecycler) NewWrapperBuilder(volName string, spec volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { return nil, fmt.Errorf("NewWrapperBuilder not supported by PVClaimBinder's VolumeHost implementation") } -func (f *PersistentVolumeRecycler) NewWrapperCleaner(spec *volume.Spec, podUID types.UID) (volume.Cleaner, error) { +func (f *PersistentVolumeRecycler) NewWrapperCleaner(volName string, spec volume.Spec, podUID types.UID) (volume.Cleaner, error) { return nil, fmt.Errorf("NewWrapperCleaner not supported by PVClaimBinder's VolumeHost implementation") } diff --git a/pkg/kubelet/volumes.go b/pkg/kubelet/volumes.go index 60a569feef9..32781cf1de4 100644 --- a/pkg/kubelet/volumes.go +++ b/pkg/kubelet/volumes.go @@ -57,16 +57,28 @@ func (vh *volumeHost) GetKubeClient() client.Interface { return vh.kubelet.kubeClient } -func (vh *volumeHost) NewWrapperBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { - b, err := vh.kubelet.newVolumeBuilderFromPlugins(spec, pod, opts) +func (vh *volumeHost) NewWrapperBuilder(volName string, spec volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { + // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" + wrapperVolumeName := "wrapped_" + volName + if spec.Volume != nil { + spec.Volume.Name = wrapperVolumeName + } + + b, err := vh.kubelet.newVolumeBuilderFromPlugins(&spec, pod, opts) if err == nil && b == nil { return nil, errUnsupportedVolumeType } return b, nil } -func (vh *volumeHost) NewWrapperCleaner(spec *volume.Spec, podUID types.UID) (volume.Cleaner, error) { - plugin, err := vh.kubelet.volumePluginMgr.FindPluginBySpec(spec) +func (vh *volumeHost) NewWrapperCleaner(volName string, spec volume.Spec, podUID types.UID) (volume.Cleaner, error) { + // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" + wrapperVolumeName := "wrapped_" + volName + if spec.Volume != nil { + spec.Volume.Name = wrapperVolumeName + } + + plugin, err := vh.kubelet.volumePluginMgr.FindPluginBySpec(&spec) if err != nil { return nil, err } diff --git a/pkg/volume/downwardapi/downwardapi.go b/pkg/volume/downwardapi/downwardapi.go index eb2602bfc90..cec79932f68 100644 --- a/pkg/volume/downwardapi/downwardapi.go +++ b/pkg/volume/downwardapi/downwardapi.go @@ -51,6 +51,10 @@ type downwardAPIPlugin struct { var _ volume.VolumePlugin = &downwardAPIPlugin{} +var wrappedVolumeSpec = volume.Spec{ + Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}}, +} + func (plugin *downwardAPIPlugin) Init(host volume.VolumeHost) error { plugin.host = host return nil @@ -66,11 +70,10 @@ func (plugin *downwardAPIPlugin) CanSupport(spec *volume.Spec) bool { func (plugin *downwardAPIPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { v := &downwardAPIVolume{ - volName: spec.Name(), - pod: pod, - podUID: pod.UID, - plugin: plugin, - wrappedVolumeSpec: volume.GetWrappedVolumeSpec(spec.Name(), downwardAPIPluginName), + volName: spec.Name(), + pod: pod, + podUID: pod.UID, + plugin: plugin, } v.fieldReferenceFileNames = make(map[string]string) for _, fileInfo := range spec.Volume.DownwardAPI.Items { @@ -78,15 +81,17 @@ func (plugin *downwardAPIPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opt } return &downwardAPIVolumeBuilder{ downwardAPIVolume: v, - opts: &opts}, nil + opts: &opts, + }, nil } func (plugin *downwardAPIPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { - return &downwardAPIVolumeCleaner{&downwardAPIVolume{ - volName: volName, - podUID: podUID, - plugin: plugin, - wrappedVolumeSpec: volume.GetWrappedVolumeSpec(volName, downwardAPIPluginName)}, + return &downwardAPIVolumeCleaner{ + &downwardAPIVolume{ + volName: volName, + podUID: podUID, + plugin: plugin, + }, }, nil } @@ -98,7 +103,6 @@ type downwardAPIVolume struct { podUID types.UID // TODO: remove this redundancy as soon NewCleaner func will have *api.POD and not only types.UID plugin *downwardAPIPlugin volume.MetricsNil - wrappedVolumeSpec *volume.Spec } // downwardAPIVolumeBuilder fetches info from downward API from the pod @@ -132,7 +136,7 @@ func (b *downwardAPIVolumeBuilder) SetUp() error { func (b *downwardAPIVolumeBuilder) SetUpAt(dir string) error { glog.V(3).Infof("Setting up a downwardAPI volume %v for pod %v/%v at %v", b.volName, b.pod.Namespace, b.pod.Name, dir) // Wrap EmptyDir. Here we rely on the idempotency of the wrapped plugin to avoid repeatedly mounting - wrapped, err := b.plugin.host.NewWrapperBuilder(b.wrappedVolumeSpec, b.pod, *b.opts) + wrapped, err := b.plugin.host.NewWrapperBuilder(b.volName, wrappedVolumeSpec, b.pod, *b.opts) if err != nil { glog.Errorf("Couldn't setup downwardAPI volume %v for pod %v/%v: %s", b.volName, b.pod.Namespace, b.pod.Name, err.Error()) return err @@ -366,7 +370,7 @@ func (c *downwardAPIVolumeCleaner) TearDownAt(dir string) error { glog.V(3).Infof("Tearing down volume %v for pod %v at %v", c.volName, c.podUID, dir) // Wrap EmptyDir, let it do the teardown. - wrapped, err := c.plugin.host.NewWrapperCleaner(c.wrappedVolumeSpec, c.podUID) + wrapped, err := c.plugin.host.NewWrapperCleaner(c.volName, wrappedVolumeSpec, c.podUID) if err != nil { return err } diff --git a/pkg/volume/downwardapi/downwardapi_test.go b/pkg/volume/downwardapi/downwardapi_test.go index cec87886011..20432d73e91 100644 --- a/pkg/volume/downwardapi/downwardapi_test.go +++ b/pkg/volume/downwardapi/downwardapi_test.go @@ -143,7 +143,7 @@ func TestLabels(t *testing.T) { t.Errorf("Failed to setup volume: %v", err) } - // gitRepo volume should create it's own empty wrapper path + // downwardAPI volume should create its own empty wrapper path podWrapperMetadataDir := fmt.Sprintf("%v/pods/%v/plugins/kubernetes.io~empty-dir/wrapped_%v", rootDir, testPodUID, testVolumeName) if _, err := os.Stat(podWrapperMetadataDir); err != nil { diff --git a/pkg/volume/git_repo/git_repo.go b/pkg/volume/git_repo/git_repo.go index 5c329eddac8..5fc3ccf8fab 100644 --- a/pkg/volume/git_repo/git_repo.go +++ b/pkg/volume/git_repo/git_repo.go @@ -41,6 +41,10 @@ type gitRepoPlugin struct { var _ volume.VolumePlugin = &gitRepoPlugin{} +var wrappedVolumeSpec = volume.Spec{ + Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, +} + const ( gitRepoPluginName = "kubernetes.io/git-repo" ) @@ -61,10 +65,9 @@ func (plugin *gitRepoPlugin) CanSupport(spec *volume.Spec) bool { func (plugin *gitRepoPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) { return &gitRepoVolumeBuilder{ gitRepoVolume: &gitRepoVolume{ - volName: spec.Name(), - podUID: pod.UID, - plugin: plugin, - wrappedVolumeSpec: volume.GetWrappedVolumeSpec(spec.Name(), gitRepoPluginName), + volName: spec.Name(), + podUID: pod.UID, + plugin: plugin, }, pod: *pod, source: spec.Volume.GitRepo.Repository, @@ -78,10 +81,9 @@ func (plugin *gitRepoPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts vo func (plugin *gitRepoPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) { return &gitRepoVolumeCleaner{ &gitRepoVolume{ - volName: volName, - podUID: podUID, - plugin: plugin, - wrappedVolumeSpec: volume.GetWrappedVolumeSpec(volName, gitRepoPluginName), + volName: volName, + podUID: podUID, + plugin: plugin, }, }, nil } @@ -89,10 +91,9 @@ func (plugin *gitRepoPlugin) NewCleaner(volName string, podUID types.UID) (volum // gitRepo volumes are directories which are pre-filled from a git repository. // These do not persist beyond the lifetime of a pod. type gitRepoVolume struct { - volName string - podUID types.UID - plugin *gitRepoPlugin - wrappedVolumeSpec *volume.Spec + volName string + podUID types.UID + plugin *gitRepoPlugin volume.MetricsNil } @@ -138,7 +139,7 @@ func (b *gitRepoVolumeBuilder) SetUpAt(dir string) error { } // Wrap EmptyDir, let it do the setup. - wrapped, err := b.plugin.host.NewWrapperBuilder(b.wrappedVolumeSpec, &b.pod, b.opts) + wrapped, err := b.plugin.host.NewWrapperBuilder(b.volName, wrappedVolumeSpec, &b.pod, b.opts) if err != nil { return err } @@ -218,7 +219,7 @@ func (c *gitRepoVolumeCleaner) TearDown() error { func (c *gitRepoVolumeCleaner) TearDownAt(dir string) error { // Wrap EmptyDir, let it do the teardown. - wrapped, err := c.plugin.host.NewWrapperCleaner(c.wrappedVolumeSpec, c.podUID) + wrapped, err := c.plugin.host.NewWrapperCleaner(c.volName, wrappedVolumeSpec, c.podUID) 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 d2deab7df92..4f13f5d5194 100644 --- a/pkg/volume/git_repo/git_repo_test.go +++ b/pkg/volume/git_repo/git_repo_test.go @@ -266,7 +266,7 @@ func doTestPlugin(scenario struct { } } - // gitRepo volume should create it's own empty wrapper path + // gitRepo volume should create its own empty wrapper path podWrapperMetadataDir := fmt.Sprintf("%v/pods/poduid/plugins/kubernetes.io~empty-dir/wrapped_%v", rootDir, scenario.vol.Name) if _, err := os.Stat(podWrapperMetadataDir); err != nil { diff --git a/pkg/volume/persistent_claim/persistent_claim.go b/pkg/volume/persistent_claim/persistent_claim.go index 219b9c5cb47..0807358e96b 100644 --- a/pkg/volume/persistent_claim/persistent_claim.go +++ b/pkg/volume/persistent_claim/persistent_claim.go @@ -80,7 +80,7 @@ func (plugin *persistentClaimPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, return nil, err } - builder, err := plugin.host.NewWrapperBuilder(volume.NewSpecFromPersistentVolume(pv, spec.ReadOnly), pod, opts) + builder, err := plugin.host.NewWrapperBuilder(claim.Spec.VolumeName, *volume.NewSpecFromPersistentVolume(pv, spec.ReadOnly), pod, opts) if err != nil { glog.Errorf("Error creating builder for claim: %+v\n", claim.Name) return nil, err diff --git a/pkg/volume/plugins.go b/pkg/volume/plugins.go index 80af44e298e..9398859b64a 100644 --- a/pkg/volume/plugins.go +++ b/pkg/volume/plugins.go @@ -144,12 +144,12 @@ 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 *Spec, pod *api.Pod, opts VolumeOptions) (Builder, error) + NewWrapperBuilder(volName string, spec Spec, pod *api.Pod, opts VolumeOptions) (Builder, error) // NewWrapperCleaner finds an appropriate plugin with which to handle // the provided spec. See comments on NewWrapperBuilder for more // context. - NewWrapperCleaner(spec *Spec, podUID types.UID) (Cleaner, error) + NewWrapperCleaner(volName string, spec Spec, podUID types.UID) (Cleaner, error) // Get cloud provider from kubelet. GetCloudProvider() cloudprovider.Interface diff --git a/pkg/volume/secret/secret.go b/pkg/volume/secret/secret.go index 5a9ed5b8908..3a8c521835f 100644 --- a/pkg/volume/secret/secret.go +++ b/pkg/volume/secret/secret.go @@ -47,6 +47,10 @@ type secretPlugin struct { var _ volume.VolumePlugin = &secretPlugin{} +var wrappedVolumeSpec = volume.Spec{ + Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}}, +} + func (plugin *secretPlugin) Init(host volume.VolumeHost) error { plugin.host = host return nil @@ -69,7 +73,6 @@ func (plugin *secretPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts vol plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}, - volume.GetWrappedVolumeSpec(spec.Name(), secretPluginName), }, secretName: spec.Volume.Secret.SecretName, pod: *pod, @@ -86,7 +89,6 @@ func (plugin *secretPlugin) NewCleaner(volName string, podUID types.UID) (volume plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}, - volume.GetWrappedVolumeSpec(volName, secretPluginName), }, }, nil } @@ -98,7 +100,6 @@ type secretVolume struct { mounter mount.Interface writer ioutil.Writer volume.MetricsNil - wrappedVolumeSpec *volume.Spec } var _ volume.Volume = &secretVolume{} @@ -152,7 +153,7 @@ func (b *secretVolumeBuilder) SetUpAt(dir string) error { glog.V(3).Infof("Setting up volume %v for pod %v at %v", b.volName, b.pod.UID, dir) // Wrap EmptyDir, let it do the setup. - wrapped, err := b.plugin.host.NewWrapperBuilder(b.wrappedVolumeSpec, &b.pod, *b.opts) + wrapped, err := b.plugin.host.NewWrapperBuilder(b.volName, wrappedVolumeSpec, &b.pod, *b.opts) if err != nil { return err } @@ -217,7 +218,7 @@ func (c *secretVolumeCleaner) TearDownAt(dir string) error { glog.V(3).Infof("Tearing down volume %v for pod %v at %v", c.volName, c.podUID, dir) // Wrap EmptyDir, let it do the teardown. - wrapped, err := c.plugin.host.NewWrapperCleaner(c.wrappedVolumeSpec, c.podUID) + wrapped, err := c.plugin.host.NewWrapperCleaner(c.volName, wrappedVolumeSpec, c.podUID) if err != nil { return err } diff --git a/pkg/volume/secret/secret_test.go b/pkg/volume/secret/secret_test.go index b5d49fb9fb7..654f3782fb8 100644 --- a/pkg/volume/secret/secret_test.go +++ b/pkg/volume/secret/secret_test.go @@ -110,7 +110,7 @@ func TestPlugin(t *testing.T) { } } - // secret volume should create it's own empty wrapper path + // secret volume should create its own empty wrapper path podWrapperMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid/plugins/kubernetes.io~empty-dir/wrapped_test_volume_name", rootDir) if _, err := os.Stat(podWrapperMetadataDir); err != nil { diff --git a/pkg/volume/testing.go b/pkg/volume/testing.go index 02f8fba72fc..2f4af9cf94f 100644 --- a/pkg/volume/testing.go +++ b/pkg/volume/testing.go @@ -80,16 +80,26 @@ func (f *fakeVolumeHost) GetWriter() io.Writer { return f.writer } -func (f *fakeVolumeHost) NewWrapperBuilder(spec *Spec, pod *api.Pod, opts VolumeOptions) (Builder, error) { - plug, err := f.pluginMgr.FindPluginBySpec(spec) +func (f *fakeVolumeHost) NewWrapperBuilder(volName string, spec Spec, pod *api.Pod, opts VolumeOptions) (Builder, error) { + // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" + wrapperVolumeName := "wrapped_" + volName + if spec.Volume != nil { + spec.Volume.Name = wrapperVolumeName + } + plug, err := f.pluginMgr.FindPluginBySpec(&spec) if err != nil { return nil, err } - return plug.NewBuilder(spec, pod, opts) + return plug.NewBuilder(&spec, pod, opts) } -func (f *fakeVolumeHost) NewWrapperCleaner(spec *Spec, podUID types.UID) (Cleaner, error) { - plug, err := f.pluginMgr.FindPluginBySpec(spec) +func (f *fakeVolumeHost) NewWrapperCleaner(volName string, spec Spec, podUID types.UID) (Cleaner, error) { + // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" + wrapperVolumeName := "wrapped_" + volName + if spec.Volume != nil { + spec.Volume.Name = wrapperVolumeName + } + plug, err := f.pluginMgr.FindPluginBySpec(&spec) if err != nil { return nil, err } diff --git a/pkg/volume/volume.go b/pkg/volume/volume.go index 7ff136b40a4..aab01158f2b 100644 --- a/pkg/volume/volume.go +++ b/pkg/volume/volume.go @@ -17,7 +17,6 @@ limitations under the License. package volume import ( - "fmt" "io/ioutil" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" @@ -25,10 +24,6 @@ import ( "path" ) -const ( - randomLength = 5 -) - // Volume represents a directory used by pods or hosts on a node. // All method implementations of methods in the volume interface must be idempotent. type Volume interface { @@ -137,22 +132,3 @@ func RenameDirectory(oldPath, newName string) (string, error) { } return newPath, nil } - -// Return the wrapper volume spec of specific volume plugin -func GetWrappedVolumeSpec(volName string, pluginName string) *Spec { - // The name of wrapper volume is set to "wrapped_{wrapped_volume_name}" - wrapperVolumeName := fmt.Sprintf("wrapped_%v", volName) - - var volumeSpec *Spec - switch pluginName { - case "kubernetes.io/downward-api", "kubernetes.io/secret": - volumeSpec = &Spec{ - Volume: &api.Volume{Name: wrapperVolumeName, VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}}, - } - case "kubernetes.io/git-repo": - volumeSpec = &Spec{ - Volume: &api.Volume{Name: wrapperVolumeName, VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, - } - } - return volumeSpec -} diff --git a/test/e2e/empty_dir_wrapper.go b/test/e2e/empty_dir_wrapper.go new file mode 100644 index 00000000000..9a2208c19a1 --- /dev/null +++ b/test/e2e/empty_dir_wrapper.go @@ -0,0 +1,179 @@ +/* +Copyright 2015 The Kubernetes Authors 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 e2e + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/intstr" + + "strconv" + + . "github.com/onsi/ginkgo" +) + +// This test will create a pod with a secret volume and gitRepo volume +// Thus requests a secret, a git server pod, and a git server service +var _ = Describe("EmptyDir wrapper volumes", func() { + f := NewFramework("secrets") + + It("should becomes running [Conformance]", func() { + name := "secret-test-" + string(util.NewUUID()) + volumeName := "secret-volume" + volumeMountPath := "/etc/secret-volume" + + secret := &api.Secret{ + ObjectMeta: api.ObjectMeta{ + Namespace: f.Namespace.Name, + Name: name, + }, + Data: map[string][]byte{ + "data-1": []byte("value-1\n"), + }, + } + + var err error + if secret, err = f.Client.Secrets(f.Namespace.Name).Create(secret); err != nil { + Failf("unable to create test secret %s: %v", secret.Name, err) + } + + gitServerPodName := "git-server-" + string(util.NewUUID()) + containerPort := 8000 + + labels := map[string]string{"name": gitServerPodName} + + gitServerPod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: gitServerPodName, + Labels: labels, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "git-repo", + Image: "gcr.io/google_containers/fakegitserver:0.1", + ImagePullPolicy: "IfNotPresent", + Ports: []api.ContainerPort{ + {ContainerPort: containerPort}, + }, + }, + }, + }, + } + + if gitServerPod, err = f.Client.Pods(f.Namespace.Name).Create(gitServerPod); err != nil { + Failf("unable to create test git server pod %s: %v", gitServerPod.Name, err) + } + + // Portal IP and port + httpPort := 2345 + + gitServerSvc := &api.Service{ + ObjectMeta: api.ObjectMeta{ + Name: "git-server-svc", + }, + Spec: api.ServiceSpec{ + Selector: labels, + Ports: []api.ServicePort{ + { + Name: "http-portal", + Port: httpPort, + TargetPort: intstr.FromInt(containerPort), + }, + }, + }, + } + + if gitServerSvc, err = f.Client.Services(f.Namespace.Name).Create(gitServerSvc); err != nil { + Failf("unable to create test git server service %s: %v", gitServerSvc.Name, err) + } + + gitVolumeName := "git-volume" + gitVolumeMountPath := "/etc/git-volume" + gitURL := "http://" + gitServerSvc.Spec.ClusterIP + ":" + strconv.Itoa(httpPort) + gitRepo := "test" + + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "pod-secrets-" + string(util.NewUUID()), + }, + Spec: api.PodSpec{ + Volumes: []api.Volume{ + { + Name: volumeName, + VolumeSource: api.VolumeSource{ + Secret: &api.SecretVolumeSource{ + SecretName: name, + }, + }, + }, + { + Name: gitVolumeName, + VolumeSource: api.VolumeSource{ + GitRepo: &api.GitRepoVolumeSource{ + Repository: gitURL, + Directory: gitRepo, + }, + }, + }, + }, + Containers: []api.Container{ + { + Name: "secret-test", + Image: "gcr.io/google_containers/test-webserver", + VolumeMounts: []api.VolumeMount{ + { + Name: volumeName, + MountPath: volumeMountPath, + ReadOnly: true, + }, + { + Name: gitVolumeName, + MountPath: gitVolumeMountPath, + }, + }, + }, + }, + }, + } + + if pod, err = f.Client.Pods(f.Namespace.Name).Create(pod); err != nil { + Failf("unable to create pod %v: %v", pod.Name, err) + } + + defer func() { + By("Cleaning up the secret") + if err := f.Client.Secrets(f.Namespace.Name).Delete(secret.Name); err != nil { + Failf("unable to delete secret %v: %v", secret.Name, err) + } + By("Cleaning up the git server pod") + if err = f.Client.Pods(f.Namespace.Name).Delete(gitServerPod.Name, api.NewDeleteOptions(0)); err != nil { + Failf("unable to delete git server pod %v: %v", gitServerPod.Name, err) + } + By("Cleaning up the git server svc") + if err = f.Client.Services(f.Namespace.Name).Delete(gitServerSvc.Name); err != nil { + Failf("unable to delete git server svc %v: %v", gitServerSvc.Name, err) + } + By("Cleaning up the git vol pod") + if err = f.Client.Pods(f.Namespace.Name).Delete(pod.Name, api.NewDeleteOptions(0)); err != nil { + Failf("unable to delete git vol pod %v: %v", pod.Name, err) + } + }() + + expectNoError(waitForPodRunningInNamespace(f.Client, pod.Name, f.Namespace.Name)) + }) +})