diff --git a/pkg/volume/aws_ebs/aws_ebs.go b/pkg/volume/aws_ebs/aws_ebs.go index 84cc8d1bb63..b8b60a7c33c 100644 --- a/pkg/volume/aws_ebs/aws_ebs.go +++ b/pkg/volume/aws_ebs/aws_ebs.go @@ -240,6 +240,10 @@ func (ebs *awsElasticBlockStore) SetUpAt(dir string) error { return nil } +func (pd *awsElasticBlockStore) IsReadOnly() bool { + return pd.readOnly +} + func makeGlobalPDPath(host volume.VolumeHost, volumeID string) string { // Clean up the URI to be more fs-friendly name := volumeID diff --git a/pkg/volume/aws_ebs/aws_ebs_test.go b/pkg/volume/aws_ebs/aws_ebs_test.go index a5017a0a896..2e7727c6919 100644 --- a/pkg/volume/aws_ebs/aws_ebs_test.go +++ b/pkg/volume/aws_ebs/aws_ebs_test.go @@ -21,6 +21,8 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" "github.com/GoogleCloudPlatform/kubernetes/pkg/types" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume" @@ -157,3 +159,50 @@ func TestPlugin(t *testing.T) { t.Errorf("SetUp() failed: %v", err) } } + +func TestPersistentClaimReadOnlyFlag(t *testing.T) { + pv := &api.PersistentVolume{ + ObjectMeta: api.ObjectMeta{ + Name: "pvA", + }, + Spec: api.PersistentVolumeSpec{ + PersistentVolumeSource: api.PersistentVolumeSource{ + AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{}, + }, + 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, + }, + } + + o := testclient.NewObjects(api.Scheme, api.Scheme) + o.Add(pv) + o.Add(claim) + client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)} + + plugMgr := volume.VolumePluginMgr{} + plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) + plug, _ := plugMgr.FindPluginByName(awsElasticBlockStorePluginName) + spec := volume.NewSpecFromPersistentVolume(pv, false) + + pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} + builder, _ := plug.NewBuilder(spec, pod, volume.VolumeOptions{}, nil) + + if builder.IsReadOnly() { + t.Errorf("Expected false for builder.IsReadOnly") + } + +} diff --git a/pkg/volume/empty_dir/empty_dir.go b/pkg/volume/empty_dir/empty_dir.go index 8bcf325e13e..6a189a62841 100644 --- a/pkg/volume/empty_dir/empty_dir.go +++ b/pkg/volume/empty_dir/empty_dir.go @@ -143,6 +143,10 @@ func (ed *emptyDir) SetUpAt(dir string) error { } } +func (ed *emptyDir) IsReadOnly() bool { + return false +} + func (ed *emptyDir) setupDefault(dir string) error { return os.MkdirAll(dir, 0750) } diff --git a/pkg/volume/gce_pd/gce_pd.go b/pkg/volume/gce_pd/gce_pd.go index 7489065d8e5..8969893592a 100644 --- a/pkg/volume/gce_pd/gce_pd.go +++ b/pkg/volume/gce_pd/gce_pd.go @@ -229,6 +229,10 @@ func (b *gcePersistentDiskBuilder) SetUpAt(dir string) error { return nil } +func (pd *gcePersistentDisk) IsReadOnly() bool { + return pd.readOnly +} + func makeGlobalPDName(host volume.VolumeHost, devName string) string { return path.Join(host.GetPluginDir(gcePersistentDiskPluginName), "mounts", devName) } diff --git a/pkg/volume/git_repo/git_repo.go b/pkg/volume/git_repo/git_repo.go index 92627a9c7f1..135110b8aa8 100644 --- a/pkg/volume/git_repo/git_repo.go +++ b/pkg/volume/git_repo/git_repo.go @@ -118,6 +118,10 @@ func (b *gitRepoVolumeBuilder) SetUp() error { return b.SetUpAt(b.GetPath()) } +func (gr *gitRepo) IsReadOnly() bool { + return false +} + // This is the spec for the volume that this plugin wraps. var wrappedVolumeSpec = &volume.Spec{ Name: "not-used", diff --git a/pkg/volume/glusterfs/glusterfs.go b/pkg/volume/glusterfs/glusterfs.go index 8df45a7cbb7..3d9626c66d9 100644 --- a/pkg/volume/glusterfs/glusterfs.go +++ b/pkg/volume/glusterfs/glusterfs.go @@ -99,7 +99,7 @@ func (plugin *glusterfsPlugin) newBuilderInternal(spec *volume.Spec, ep *api.End }, hosts: ep, path: source.Path, - readonly: readOnly, + readOnly: readOnly, exe: exe}, nil } @@ -128,7 +128,8 @@ type glusterfsBuilder struct { *glusterfs hosts *api.Endpoints path string - readonly bool + readOnly bool + mounter mount.Interface exe exec.Interface } @@ -161,6 +162,10 @@ func (b *glusterfsBuilder) SetUpAt(dir string) error { return err } +func (glusterfsVolume *glusterfs) IsReadOnly() bool { + return glusterfsVolume.readOnly +} + func (glusterfsVolume *glusterfs) GetPath() string { name := glusterfsPluginName return glusterfsVolume.plugin.host.GetPodVolumeDir(glusterfsVolume.pod.UID, util.EscapeQualifiedNameForDisk(name), glusterfsVolume.volName) @@ -212,7 +217,7 @@ func (b *glusterfsBuilder) setUpAtInternal(dir string) error { var errs error options := []string{} - if b.readonly { + if glusterfsVolume.readOnly { options = append(options, "ro") } diff --git a/pkg/volume/host_path/host_path.go b/pkg/volume/host_path/host_path.go index d369e3c0b79..3768740509c 100644 --- a/pkg/volume/host_path/host_path.go +++ b/pkg/volume/host_path/host_path.go @@ -119,6 +119,14 @@ func (b *hostPathBuilder) SetUpAt(dir string) error { return fmt.Errorf("SetUpAt() does not make sense for host paths") } +func (b *hostPathBuilder) IsReadOnly() bool { + return false +} + +func (b *hostPathBuilder) GetPath() string { + return b.path +} + type hostPathCleaner struct { *hostPath } diff --git a/pkg/volume/iscsi/iscsi.go b/pkg/volume/iscsi/iscsi.go index 587b9e3865d..05fdccd380d 100644 --- a/pkg/volume/iscsi/iscsi.go +++ b/pkg/volume/iscsi/iscsi.go @@ -183,6 +183,10 @@ type iscsiDiskCleaner struct { var _ volume.Cleaner = &iscsiDiskCleaner{} +func (b *iscsiDiskBuilder) IsReadOnly() bool { + return b.readOnly +} + // Unmounts the bind mount, and detaches the disk only if the disk // resource was the last reference to that disk on the kubelet. func (c *iscsiDiskCleaner) TearDown() error { diff --git a/pkg/volume/nfs/nfs.go b/pkg/volume/nfs/nfs.go index 8776200b268..54151817dbd 100644 --- a/pkg/volume/nfs/nfs.go +++ b/pkg/volume/nfs/nfs.go @@ -188,6 +188,15 @@ type nfsCleaner struct { *nfs } +func (nfsVolume *nfs) IsReadOnly() bool { + return nfsVolume.readOnly +} + +func (nfsVolume *nfs) GetPath() string { + name := nfsPluginName + return nfsVolume.plugin.host.GetPodVolumeDir(nfsVolume.pod.UID, util.EscapeQualifiedNameForDisk(name), nfsVolume.volName) +} + var _ volume.Cleaner = &nfsCleaner{} func (c *nfsCleaner) TearDown() error { diff --git a/pkg/volume/persistent_claim/persistent_claim.go b/pkg/volume/persistent_claim/persistent_claim.go index 737f6b8443b..55d48ac4be5 100644 --- a/pkg/volume/persistent_claim/persistent_claim.go +++ b/pkg/volume/persistent_claim/persistent_claim.go @@ -26,11 +26,12 @@ import ( ) func ProbeVolumePlugins() []volume.VolumePlugin { - return []volume.VolumePlugin{&persistentClaimPlugin{nil}} + return []volume.VolumePlugin{&persistentClaimPlugin{host: nil}} } type persistentClaimPlugin struct { - host volume.VolumeHost + host volume.VolumeHost + readOnly bool } var _ volume.VolumePlugin = &persistentClaimPlugin{} @@ -52,6 +53,7 @@ func (plugin *persistentClaimPlugin) CanSupport(spec *volume.Spec) bool { } func (plugin *persistentClaimPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions, mounter mount.Interface) (volume.Builder, error) { + plugin.readOnly = spec.ReadOnly claim, err := plugin.host.GetKubeClient().PersistentVolumeClaims(pod.Namespace).Get(spec.VolumeSource.PersistentVolumeClaim.ClaimName) if err != nil { glog.Errorf("Error finding claim: %+v\n", spec.VolumeSource.PersistentVolumeClaim.ClaimName) @@ -87,6 +89,10 @@ func (plugin *persistentClaimPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, return builder, nil } +func (plugin *persistentClaimPlugin) IsReadOnly() bool { + return plugin.readOnly +} + func (plugin *persistentClaimPlugin) NewCleaner(_ string, _ types.UID, _ mount.Interface) (volume.Cleaner, error) { return nil, fmt.Errorf("This will never be called directly. The PV backing this claim has a cleaner. Kubelet uses that cleaner, not this one, when removing orphaned volumes.") } diff --git a/pkg/volume/rbd/rbd.go b/pkg/volume/rbd/rbd.go index e5eac04c82f..e3a32967140 100644 --- a/pkg/volume/rbd/rbd.go +++ b/pkg/volume/rbd/rbd.go @@ -216,6 +216,10 @@ type rbdCleaner struct { var _ volume.Cleaner = &rbdCleaner{} +func (b *rbd) IsReadOnly() bool { + return b.ReadOnly +} + // Unmounts the bind mount, and detaches the disk only if the disk // resource was the last reference to that disk on the kubelet. func (c *rbdCleaner) TearDown() error { diff --git a/pkg/volume/secret/secret.go b/pkg/volume/secret/secret.go index 26a1b0eba94..83a4fff64d3 100644 --- a/pkg/volume/secret/secret.go +++ b/pkg/volume/secret/secret.go @@ -168,6 +168,10 @@ func (b *secretVolumeBuilder) SetUpAt(dir string) error { return nil } +func (sv *secretVolume) IsReadOnly() bool { + return false +} + func totalSecretBytes(secret *api.Secret) int { totalSize := 0 for _, bytes := range secret.Data { diff --git a/pkg/volume/testing.go b/pkg/volume/testing.go index 3ac6d9736e9..fa2f532ae55 100644 --- a/pkg/volume/testing.go +++ b/pkg/volume/testing.go @@ -127,6 +127,10 @@ func (fv *FakeVolume) SetUpAt(dir string) error { return os.MkdirAll(dir, 0750) } +func (fv *FakeVolume) IsReadOnly() bool { + return false +} + func (fv *FakeVolume) GetPath() string { return path.Join(fv.Plugin.Host.GetPodVolumeDir(fv.PodUID, util.EscapeQualifiedNameForDisk(fv.Plugin.PluginName), fv.VolName)) } diff --git a/pkg/volume/volume.go b/pkg/volume/volume.go index ee668400a87..ff2bf29b944 100644 --- a/pkg/volume/volume.go +++ b/pkg/volume/volume.go @@ -41,6 +41,9 @@ type Builder interface { // directory path, which may or may not exist yet. This may be called // more than once, so implementations must be idempotent. SetUpAt(dir string) error + // IsReadOnly is a flag that gives the builder's ReadOnly attribute. + // All persistent volumes have a private readOnly flag in their builders. + IsReadOnly() bool } // Cleaner interface provides methods to cleanup/unmount the volumes. diff --git a/pkg/volumeclaimbinder/persistent_volume_recycler.go b/pkg/volumeclaimbinder/persistent_volume_recycler.go index c1b6bff0f22..a65131aa8b7 100644 --- a/pkg/volumeclaimbinder/persistent_volume_recycler.go +++ b/pkg/volumeclaimbinder/persistent_volume_recycler.go @@ -123,7 +123,7 @@ func (recycler *PersistentVolumeRecycler) handleRecycle(pv *api.PersistentVolume currentPhase := pv.Status.Phase nextPhase := currentPhase - spec := volume.NewSpecFromPersistentVolume(pv) + spec := volume.NewSpecFromPersistentVolume(pv, false) plugin, err := recycler.pluginMgr.FindRecyclablePluginBySpec(spec) if err != nil { return fmt.Errorf("Could not find recyclable volume plugin for spec: %+v", err)