From 56ccd98db8c459a5dce928610770778048d4530e Mon Sep 17 00:00:00 2001 From: Sami Wagiaalla Date: Tue, 10 May 2016 15:36:38 -0400 Subject: [PATCH] Add support for PersistentVolumeClaim in Attacher/Detacher interface - Dereference PVCs in kubelet. - Add getPersistentVolumebySpec to kubelet. - Call getPersistentVolumebySpec from mount External volumes - Add applyPVAnnotations to kubelet. - Delete persistent_claim plugin. --- cmd/kubelet/app/plugins.go | 2 - pkg/kubelet/kubelet_test.go | 160 ++++++++ pkg/kubelet/volumes.go | 78 +++- pkg/volume/persistent_claim/OWNERS | 2 - pkg/volume/persistent_claim/doc.go | 19 - .../persistent_claim/persistent_claim.go | 109 ------ .../persistent_claim/persistent_claim_test.go | 369 ------------------ pkg/volume/testing/testing.go | 2 +- pkg/volume/volume.go | 2 +- 9 files changed, 235 insertions(+), 508 deletions(-) delete mode 100644 pkg/volume/persistent_claim/OWNERS delete mode 100644 pkg/volume/persistent_claim/doc.go delete mode 100644 pkg/volume/persistent_claim/persistent_claim.go delete mode 100644 pkg/volume/persistent_claim/persistent_claim_test.go diff --git a/cmd/kubelet/app/plugins.go b/cmd/kubelet/app/plugins.go index c9388d1278..5e24957f9a 100644 --- a/cmd/kubelet/app/plugins.go +++ b/cmd/kubelet/app/plugins.go @@ -44,7 +44,6 @@ import ( "k8s.io/kubernetes/pkg/volume/host_path" "k8s.io/kubernetes/pkg/volume/iscsi" "k8s.io/kubernetes/pkg/volume/nfs" - "k8s.io/kubernetes/pkg/volume/persistent_claim" "k8s.io/kubernetes/pkg/volume/rbd" "k8s.io/kubernetes/pkg/volume/secret" // Cloud providers @@ -72,7 +71,6 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin { allPlugins = append(allPlugins, secret.ProbeVolumePlugins()...) allPlugins = append(allPlugins, iscsi.ProbeVolumePlugins()...) allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, persistent_claim.ProbeVolumePlugins()...) allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...) allPlugins = append(allPlugins, cephfs.ProbeVolumePlugins()...) diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index ad0fd50bca..8ff72e7565 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -565,6 +565,166 @@ func TestCleanupOrphanedVolumes(t *testing.T) { } } +func TestGetPersistentVolumeBySpec(t *testing.T) { + testKubelet := newTestKubelet(t) + kubelet := testKubelet.kubelet + kubeClient := testKubelet.fakeKubeClient + kubeClient.ReactionChain = fake.NewSimpleClientset(&api.PersistentVolumeClaimList{Items: []api.PersistentVolumeClaim{}}).ReactionChain + + // Test claim does not exist + _, err := kubelet.getPersistentVolumeByClaimName("myclaim", "test") + if err == nil { + t.Errorf("Expected claim to not be found") + } + + // Test claim found not bound. + claim := api.PersistentVolumeClaim{ + ObjectMeta: api.ObjectMeta{ + Name: "myclaim", + Namespace: "test", + }, + } + kubeClient.ReactionChain = fake.NewSimpleClientset(&api.PersistentVolumeClaimList{Items: []api.PersistentVolumeClaim{ + claim, + }}).ReactionChain + + _, err = kubelet.getPersistentVolumeByClaimName("myclaim", "test") + if err == nil { + t.Errorf("Expected a claim not bound error to occur") + } + + // Test claim found, bound but PV does not exist + claim = api.PersistentVolumeClaim{ + ObjectMeta: api.ObjectMeta{ + Name: "myclaim", + Namespace: "test", + UID: types.UID("myclaimUID123"), + }, + Spec: api.PersistentVolumeClaimSpec{ + VolumeName: "myrealvol", + }, + } + kubeClient.ReactionChain = fake.NewSimpleClientset(&api.PersistentVolumeClaimList{Items: []api.PersistentVolumeClaim{ + claim, + }}).ReactionChain + + _, err = kubelet.getPersistentVolumeByClaimName("myclaim", "test") + if err == nil { + t.Errorf("Expected PV not found error to occur") + } + + // Test volume found but not bound. + volume := api.PersistentVolume{ + ObjectMeta: api.ObjectMeta{ + Name: "myrealvol", + }, + } + kubeClient.ReactionChain = fake.NewSimpleClientset( + &api.PersistentVolumeClaimList{Items: []api.PersistentVolumeClaim{ + claim, + }}, + &api.PersistentVolumeList{Items: []api.PersistentVolume{ + volume, + }}, + ).ReactionChain + _, err = kubelet.getPersistentVolumeByClaimName("myclaim", "test") + if err == nil { + t.Errorf("Expected PV not bound error to occur") + } + + // Test volume found and incorrectly bound + volume = api.PersistentVolume{ + ObjectMeta: api.ObjectMeta{ + Name: "myrealvol", + }, + Spec: api.PersistentVolumeSpec{ + ClaimRef: &api.ObjectReference{ + Name: "myclaim", + Namespace: "test", + UID: types.UID("veryWrongUID"), + }, + }, + } + kubeClient.ReactionChain = fake.NewSimpleClientset( + &api.PersistentVolumeClaimList{Items: []api.PersistentVolumeClaim{ + claim, + }}, + &api.PersistentVolumeList{Items: []api.PersistentVolume{ + volume, + }}, + ).ReactionChain + _, err = kubelet.getPersistentVolumeByClaimName("myclaim", "test") + if err == nil { + t.Errorf("Expected wrong UID error to occur") + } + + // Test volume found and correctly bound + volume = api.PersistentVolume{ + ObjectMeta: api.ObjectMeta{ + Name: "myrealvol", + }, + Spec: api.PersistentVolumeSpec{ + ClaimRef: &api.ObjectReference{ + Name: "myclaim", + Namespace: "test", + UID: types.UID("myclaimUID123"), + }, + }, + } + kubeClient.ReactionChain = fake.NewSimpleClientset( + &api.PersistentVolumeClaimList{Items: []api.PersistentVolumeClaim{ + claim, + }}, + &api.PersistentVolumeList{Items: []api.PersistentVolume{ + volume, + }}, + ).ReactionChain + + foundVolume, err := kubelet.getPersistentVolumeByClaimName("myclaim", "test") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if foundVolume.Name != volume.Name { + t.Errorf("Found incorrect volume expected %v, but got %v", volume, foundVolume) + } +} + +func TestApplyPersistentVolumeAnnotations(t *testing.T) { + testKubelet := newTestKubelet(t) + kubelet := testKubelet.kubelet + + pod := &api.Pod{} + + pv := &api.PersistentVolume{ + ObjectMeta: api.ObjectMeta{ + Name: "pv", + Annotations: map[string]string{ + volumeGidAnnotationKey: "12345", + }, + }, + Spec: api.PersistentVolumeSpec{ + PersistentVolumeSource: api.PersistentVolumeSource{ + GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, + }, + ClaimRef: &api.ObjectReference{ + Name: "claim", + UID: types.UID("abc123"), + }, + }, + } + + kubelet.applyPersistentVolumeAnnotations(pv, pod) + + if pod.Spec.SecurityContext == nil { + t.Errorf("Pod SecurityContext was not set") + } + + if pod.Spec.SecurityContext.SupplementalGroups[0] != 12345 { + t.Errorf("Pod's SupplementalGroups list does not contain expect group") + } +} + type stubVolume struct { path string volume.MetricsNil diff --git a/pkg/kubelet/volumes.go b/pkg/kubelet/volumes.go index 71ff3a24a8..80aabab5fc 100644 --- a/pkg/kubelet/volumes.go +++ b/pkg/kubelet/volumes.go @@ -20,6 +20,7 @@ import ( "fmt" "io/ioutil" "path" + "strconv" "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" @@ -34,6 +35,10 @@ import ( "k8s.io/kubernetes/pkg/volume" ) +const ( + volumeGidAnnotationKey = "pv.beta.kubernetes.io/gid" +) + // This just exports required functions from kubelet proper, for use by volume // plugins. type volumeHost struct { @@ -126,8 +131,20 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap, return nil, err } + var volSpec *volume.Spec + if pod.Spec.Volumes[i].VolumeSource.PersistentVolumeClaim != nil { + claimName := pod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName + pv, err := kl.getPersistentVolumeByClaimName(claimName, pod.Namespace) + if err != nil { + glog.Errorf("Could not find persistentVolume for claim %s err %v", claimName, err) + return nil, err + } + kl.applyPersistentVolumeAnnotations(pv, pod) + volSpec = volume.NewSpecFromPersistentVolume(pv, pod.Spec.Volumes[i].PersistentVolumeClaim.ReadOnly) + } else { + volSpec = volume.NewSpecFromVolume(&pod.Spec.Volumes[i]) + } // Try to use a plugin for this volume. - volSpec := volume.NewSpecFromVolume(&pod.Spec.Volumes[i]) mounter, err := kl.newVolumeMounterFromPlugins(volSpec, pod, volume.VolumeOptions{RootContext: rootContext}) if err != nil { glog.Errorf("Could not create volume mounter for pod %s: %v", pod.UID, err) @@ -137,7 +154,7 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap, // some volumes require attachment before mounter's setup. // The plugin can be nil, but non-nil errors are legitimate errors. // For non-nil plugins, Attachment to a node is required before Mounter's setup. - attacher, err := kl.newVolumeAttacherFromPlugins(volSpec, pod, volume.VolumeOptions{RootContext: rootContext}) + attacher, err := kl.newVolumeAttacherFromPlugins(volSpec, pod) if err != nil { glog.Errorf("Could not create volume attacher for pod %s: %v", pod.UID, err) return nil, err @@ -153,7 +170,7 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap, return nil, err } - deviceMountPath := attacher.GetDeviceMountPath(&volumeHost{kl}, volSpec) + deviceMountPath := attacher.GetDeviceMountPath(volSpec) if err = attacher.MountDevice(volSpec, devicePath, deviceMountPath, kl.mounter); err != nil { return nil, err } @@ -163,7 +180,7 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap, if err != nil { return nil, err } - podVolumes[volSpec.Volume.Name] = kubecontainer.VolumeInfo{Mounter: mounter} + podVolumes[pod.Spec.Volumes[i].Name] = kubecontainer.VolumeInfo{Mounter: mounter} } return podVolumes, nil } @@ -270,6 +287,57 @@ func (kl *Kubelet) getPodVolumesFromDisk() map[string]cleaner { return currentVolumes } +func (kl *Kubelet) getPersistentVolumeByClaimName(claimName string, namespace string) (*api.PersistentVolume, error) { + claim, err := kl.kubeClient.Core().PersistentVolumeClaims(namespace).Get(claimName) + if err != nil { + glog.Errorf("Error finding claim: %+v\n", claimName) + return nil, err + } + glog.V(5).Infof("Found claim %v ", claim) + + if claim.Spec.VolumeName == "" { + return nil, fmt.Errorf("The claim %+v is not yet bound to a volume", claimName) + } + + pv, err := kl.kubeClient.Core().PersistentVolumes().Get(claim.Spec.VolumeName) + if err != nil { + glog.Errorf("Error finding persistent volume for claim: %+v\n", claimName) + return nil, err + } + + if pv.Spec.ClaimRef == nil { + return nil, fmt.Errorf("The volume is not yet bound to the claim. Expected to find the bind on volume.Spec.ClaimRef: %+v", pv) + } + + if pv.Spec.ClaimRef.UID != claim.UID { + return nil, fmt.Errorf("Expected volume.Spec.ClaimRef.UID %+v but have %+v", pv.Spec.ClaimRef.UID, claim.UID) + } + + return pv, nil +} + +func (kl *Kubelet) applyPersistentVolumeAnnotations(pv *api.PersistentVolume, pod *api.Pod) error { + // If a GID annotation is provided set the GID attribute. + if volumeGid, ok := pv.Annotations[volumeGidAnnotationKey]; ok { + gid, err := strconv.ParseInt(volumeGid, 10, 64) + if err != nil { + return fmt.Errorf("Invalid value for %s %v", volumeGidAnnotationKey, err) + } + + if pod.Spec.SecurityContext == nil { + pod.Spec.SecurityContext = &api.PodSecurityContext{} + } + for _, existingGid := range pod.Spec.SecurityContext.SupplementalGroups { + if gid == existingGid { + return nil + } + } + pod.Spec.SecurityContext.SupplementalGroups = append(pod.Spec.SecurityContext.SupplementalGroups, gid) + } + + return nil +} + // newVolumeMounterFromPlugins attempts to find a plugin by volume spec, pod // and volume options and then creates a Mounter. // Returns a valid Unmounter or an error. @@ -293,7 +361,7 @@ func (kl *Kubelet) newVolumeMounterFromPlugins(spec *volume.Spec, pod *api.Pod, // - an error if no plugin was found for the volume // or the attacher failed to instantiate // - nil if there is no appropriate attacher for this volume -func (kl *Kubelet) newVolumeAttacherFromPlugins(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Attacher, error) { +func (kl *Kubelet) newVolumeAttacherFromPlugins(spec *volume.Spec, pod *api.Pod) (volume.Attacher, error) { plugin, err := kl.volumePluginMgr.FindAttachablePluginBySpec(spec) if err != nil { return nil, fmt.Errorf("can't use volume plugins for %s: %v", spec.Name(), err) diff --git a/pkg/volume/persistent_claim/OWNERS b/pkg/volume/persistent_claim/OWNERS deleted file mode 100644 index fdc2a40e07..0000000000 --- a/pkg/volume/persistent_claim/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -maintainers: -- childsb diff --git a/pkg/volume/persistent_claim/doc.go b/pkg/volume/persistent_claim/doc.go deleted file mode 100644 index fc1335a8ba..0000000000 --- a/pkg/volume/persistent_claim/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -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 persistent_claim contains the internal representation of persistent -// volume claims. -package persistent_claim diff --git a/pkg/volume/persistent_claim/persistent_claim.go b/pkg/volume/persistent_claim/persistent_claim.go deleted file mode 100644 index ea593bd9a8..0000000000 --- a/pkg/volume/persistent_claim/persistent_claim.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2014 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 persistent_claim - -import ( - "fmt" - "strconv" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/types" - "k8s.io/kubernetes/pkg/volume" -) - -func ProbeVolumePlugins() []volume.VolumePlugin { - return []volume.VolumePlugin{&persistentClaimPlugin{host: nil}} -} - -type persistentClaimPlugin struct { - host volume.VolumeHost - readOnly bool -} - -var _ volume.VolumePlugin = &persistentClaimPlugin{} - -const ( - persistentClaimPluginName = "kubernetes.io/persistent-claim" - volumeGidAnnotationKey = "pv.beta.kubernetes.io/gid" -) - -func (plugin *persistentClaimPlugin) Init(host volume.VolumeHost) error { - plugin.host = host - return nil -} - -func (plugin *persistentClaimPlugin) Name() string { - return persistentClaimPluginName -} - -func (plugin *persistentClaimPlugin) CanSupport(spec *volume.Spec) bool { - return spec.Volume != nil && spec.Volume.PersistentVolumeClaim != nil -} - -func (plugin *persistentClaimPlugin) NewMounter(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Mounter, error) { - claim, err := plugin.host.GetKubeClient().Core().PersistentVolumeClaims(pod.Namespace).Get(spec.Volume.PersistentVolumeClaim.ClaimName) - if err != nil { - glog.Errorf("Error finding claim: %+v\n", spec.Volume.PersistentVolumeClaim.ClaimName) - return nil, err - } - - if claim.Spec.VolumeName == "" { - return nil, fmt.Errorf("The claim %+v is not yet bound to a volume", claim.Name) - } - - pv, err := plugin.host.GetKubeClient().Core().PersistentVolumes().Get(claim.Spec.VolumeName) - if err != nil { - glog.Errorf("Error finding persistent volume for claim: %+v\n", claim.Name) - return nil, err - } - - if pv.Spec.ClaimRef == nil { - glog.Errorf("The volume is not yet bound to the claim. Expected to find the bind on volume.Spec.ClaimRef: %+v", pv) - return nil, err - } - - if pv.Spec.ClaimRef.UID != claim.UID { - glog.Errorf("Expected volume.Spec.ClaimRef.UID %+v but have %+v", pv.Spec.ClaimRef.UID, claim.UID) - return nil, err - } - - // If a GID annotation is provided set the GID attribute. - if volumeGid, ok := pv.Annotations[volumeGidAnnotationKey]; ok { - gid, err := strconv.ParseInt(volumeGid, 10, 64) - if err != nil { - return nil, fmt.Errorf("Invalid value for %s %v", volumeGidAnnotationKey, err) - } - - if pod.Spec.SecurityContext == nil { - pod.Spec.SecurityContext = &api.PodSecurityContext{} - } - pod.Spec.SecurityContext.SupplementalGroups = append(pod.Spec.SecurityContext.SupplementalGroups, gid) - } - - mounter, err := plugin.host.NewWrapperMounter(claim.Spec.VolumeName, *volume.NewSpecFromPersistentVolume(pv, spec.ReadOnly), pod, opts) - if err != nil { - glog.Errorf("Error creating mounter for claim: %+v\n", claim.Name) - return nil, err - } - - return mounter, nil -} - -func (plugin *persistentClaimPlugin) NewUnmounter(_ string, _ types.UID) (volume.Unmounter, error) { - return nil, fmt.Errorf("This will never be called directly. The PV backing this claim has a unmounter. Kubelet uses that unmounter, not this one, when removing orphaned volumes.") -} diff --git a/pkg/volume/persistent_claim/persistent_claim_test.go b/pkg/volume/persistent_claim/persistent_claim_test.go deleted file mode 100644 index bf92258e0f..0000000000 --- a/pkg/volume/persistent_claim/persistent_claim_test.go +++ /dev/null @@ -1,369 +0,0 @@ -/* -Copyright 2014 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 persistent_claim - -import ( - "fmt" - "os" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" - "k8s.io/kubernetes/pkg/types" - utilstrings "k8s.io/kubernetes/pkg/util/strings" - utiltesting "k8s.io/kubernetes/pkg/util/testing" - "k8s.io/kubernetes/pkg/volume" - "k8s.io/kubernetes/pkg/volume/gce_pd" - "k8s.io/kubernetes/pkg/volume/host_path" - volumetest "k8s.io/kubernetes/pkg/volume/testing" -) - -// newTestHost returns the temp directory and the VolumeHost created. -// Please be sure to cleanup the temp directory once done! -func newTestHost(t *testing.T, clientset clientset.Interface) (string, volume.VolumeHost) { - tempDir, err := utiltesting.MkTmpdir("persistent_volume_test.") - if err != nil { - t.Fatalf("can't make a temp rootdir: %v", err) - } - return tempDir, volumetest.NewFakeVolumeHost(tempDir, clientset, testProbeVolumePlugins()) -} - -func TestCanSupport(t *testing.T) { - plugMgr := volume.VolumePluginMgr{} - plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost("/somepath/fake", nil, ProbeVolumePlugins())) - - plug, err := plugMgr.FindPluginByName("kubernetes.io/persistent-claim") - if err != nil { - t.Errorf("Can't find the plugin by name") - } - if plug.Name() != "kubernetes.io/persistent-claim" { - t.Errorf("Wrong name: %s", plug.Name()) - } - if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{}}}}) { - t.Errorf("Expected true") - } - if plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{}}}}) { - t.Errorf("Expected false") - } - if plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{}}}) { - t.Errorf("Expected false") - } -} - -func TestNewMounter(t *testing.T) { - tests := []struct { - pv *api.PersistentVolume - claim *api.PersistentVolumeClaim - plugin volume.VolumePlugin - podVolume api.VolumeSource - testFunc func(mounter volume.Mounter, plugin volume.VolumePlugin, pod *api.Pod) error - expectedFailure bool - }{ - { - pv: &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "pvA", - }, - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, - }, - ClaimRef: &api.ObjectReference{ - Name: "claimA", - }, - }, - }, - claim: &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "claimA", - Namespace: "nsA", - }, - Spec: api.PersistentVolumeClaimSpec{ - VolumeName: "pvA", - }, - Status: api.PersistentVolumeClaimStatus{ - Phase: api.ClaimBound, - }, - }, - podVolume: api.VolumeSource{ - PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ - ReadOnly: false, - ClaimName: "claimA", - }, - }, - plugin: gce_pd.ProbeVolumePlugins()[0], - testFunc: func(mounter volume.Mounter, plugin volume.VolumePlugin, pod *api.Pod) error { - if !strings.Contains(mounter.GetPath(), utilstrings.EscapeQualifiedNameForDisk(plugin.Name())) { - return fmt.Errorf("mounter path expected to contain plugin name. Got: %s", mounter.GetPath()) - } - return nil - }, - expectedFailure: false, - }, - { - pv: &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "pvB", - }, - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/somepath"}, - }, - ClaimRef: &api.ObjectReference{ - Name: "claimB", - }, - }, - }, - claim: &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "claimB", - Namespace: "nsB", - }, - Spec: api.PersistentVolumeClaimSpec{ - VolumeName: "pvA", - }, - }, - podVolume: api.VolumeSource{ - PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ - ReadOnly: false, - ClaimName: "claimB", - }, - }, - plugin: host_path.ProbeVolumePlugins(volume.VolumeConfig{})[0], - testFunc: func(mounter volume.Mounter, plugin volume.VolumePlugin, pod *api.Pod) error { - if mounter.GetPath() != "/somepath" { - return fmt.Errorf("Expected HostPath.Path /somepath, got: %s", mounter.GetPath()) - } - return nil - }, - expectedFailure: false, - }, - { - pv: &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "pvA", - }, - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, - }, - }, - }, - claim: &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "claimA", - Namespace: "nsA", - }, - Spec: api.PersistentVolumeClaimSpec{ - VolumeName: "pvA", - }, - Status: api.PersistentVolumeClaimStatus{ - Phase: api.ClaimBound, - }, - }, - podVolume: api.VolumeSource{ - PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ - ReadOnly: false, - ClaimName: "claimA", - }, - }, - plugin: gce_pd.ProbeVolumePlugins()[0], - testFunc: func(mounter volume.Mounter, plugin volume.VolumePlugin, pod *api.Pod) error { - if mounter != nil { - return fmt.Errorf("Unexpected non-nil mounter: %+v", mounter) - } - return nil - }, - expectedFailure: true, // missing pv.Spec.ClaimRef - }, - { - pv: &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "pvA", - }, - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, - }, - ClaimRef: &api.ObjectReference{ - Name: "claimB", - UID: types.UID("abc123"), - }, - }, - }, - claim: &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "claimA", - Namespace: "nsA", - UID: types.UID("def456"), - }, - Spec: api.PersistentVolumeClaimSpec{ - VolumeName: "pvA", - }, - Status: api.PersistentVolumeClaimStatus{ - Phase: api.ClaimBound, - }, - }, - podVolume: api.VolumeSource{ - PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ - ReadOnly: false, - ClaimName: "claimA", - }, - }, - plugin: gce_pd.ProbeVolumePlugins()[0], - testFunc: func(mounter volume.Mounter, plugin volume.VolumePlugin, pod *api.Pod) error { - if mounter != nil { - return fmt.Errorf("Unexpected non-nil mounter: %+v", mounter) - } - return nil - }, - expectedFailure: true, // mismatched pv.Spec.ClaimRef and pvc - }, - { // Test GID annotation - pv: &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "pv", - Annotations: map[string]string{ - volumeGidAnnotationKey: "12345", - }, - }, - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, - }, - ClaimRef: &api.ObjectReference{ - Name: "claim", - UID: types.UID("abc123"), - }, - }, - }, - claim: &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "claim", - UID: types.UID("abc123"), - }, - Spec: api.PersistentVolumeClaimSpec{ - VolumeName: "pv", - }, - Status: api.PersistentVolumeClaimStatus{ - Phase: api.ClaimBound, - }, - }, - podVolume: api.VolumeSource{ - PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ - ReadOnly: false, - ClaimName: "claim", - }, - }, - plugin: gce_pd.ProbeVolumePlugins()[0], - testFunc: func(mounter volume.Mounter, plugin volume.VolumePlugin, pod *api.Pod) error { - if pod.Spec.SecurityContext == nil { - return fmt.Errorf("Pod SecurityContext was not set") - } - - if pod.Spec.SecurityContext.SupplementalGroups[0] != 12345 { - return fmt.Errorf("Pod's SupplementalGroups list does not contain expect group") - } - - return nil - }, - expectedFailure: false, - }, - } - - for _, item := range tests { - client := fake.NewSimpleClientset(item.pv, item.claim) - - plugMgr := volume.VolumePluginMgr{} - tempDir, vh := newTestHost(t, client) - defer os.RemoveAll(tempDir) - plugMgr.InitPlugins(testProbeVolumePlugins(), vh) - - plug, err := plugMgr.FindPluginByName("kubernetes.io/persistent-claim") - if err != nil { - t.Errorf("Can't find the plugin by name") - } - spec := &volume.Spec{Volume: &api.Volume{VolumeSource: item.podVolume}} - pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} - mounter, err := plug.NewMounter(spec, pod, volume.VolumeOptions{}) - - if !item.expectedFailure { - if err != nil { - t.Errorf("Failed to make a new Mounter: %v", err) - } - if mounter == nil { - t.Errorf("Got a nil Mounter: %v", mounter) - } - } - - if err := item.testFunc(mounter, item.plugin, pod); err != nil { - t.Errorf("Unexpected error %+v", err) - } - } -} - -func TestNewMounterClaimNotBound(t *testing.T) { - pv := &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "pvC", - }, - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, - }, - }, - } - claim := &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "claimC", - Namespace: "nsA", - }, - } - podVolume := api.VolumeSource{ - PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ - ReadOnly: false, - ClaimName: "claimC", - }, - } - client := fake.NewSimpleClientset(pv, claim) - - plugMgr := volume.VolumePluginMgr{} - tempDir, vh := newTestHost(t, client) - defer os.RemoveAll(tempDir) - plugMgr.InitPlugins(testProbeVolumePlugins(), vh) - - plug, err := plugMgr.FindPluginByName("kubernetes.io/persistent-claim") - if err != nil { - t.Errorf("Can't find the plugin by name") - } - spec := &volume.Spec{Volume: &api.Volume{VolumeSource: podVolume}} - pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} - mounter, err := plug.NewMounter(spec, pod, volume.VolumeOptions{}) - if mounter != nil { - t.Errorf("Expected a nil mounter if the claim wasn't bound") - } -} - -func testProbeVolumePlugins() []volume.VolumePlugin { - allPlugins := []volume.VolumePlugin{} - allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...) - allPlugins = append(allPlugins, host_path.ProbeVolumePlugins(volume.VolumeConfig{})...) - allPlugins = append(allPlugins, ProbeVolumePlugins()...) - return allPlugins -} diff --git a/pkg/volume/testing/testing.go b/pkg/volume/testing/testing.go index 0df52df40b..134547808d 100644 --- a/pkg/volume/testing/testing.go +++ b/pkg/volume/testing/testing.go @@ -273,7 +273,7 @@ func (fv *FakeVolume) WaitForAttach(spec *Spec, spectimeout time.Duration) (stri return "", nil } -func (fv *FakeVolume) GetDeviceMountPath(host VolumeHost, spec *Spec) string { +func (fv *FakeVolume) GetDeviceMountPath(spec *Spec) string { fv.GetDeviceMountPathCallCount++ return "" } diff --git a/pkg/volume/volume.go b/pkg/volume/volume.go index be71d78b02..3c58033cc8 100644 --- a/pkg/volume/volume.go +++ b/pkg/volume/volume.go @@ -143,7 +143,7 @@ type Attacher interface { // GetDeviceMountPath returns a path where the device should // be mounted after it is attached. This is a global mount // point which should be bind mounted for individual volumes. - GetDeviceMountPath(host VolumeHost, spec *Spec) string + GetDeviceMountPath(spec *Spec) string // MountDevice mounts the disk to a global path which // individual pods can then bind mount