diff --git a/cmd/kube-controller-manager/app/BUILD b/cmd/kube-controller-manager/app/BUILD index 56e95cab5c..bbf16bde3e 100644 --- a/cmd/kube-controller-manager/app/BUILD +++ b/cmd/kube-controller-manager/app/BUILD @@ -83,6 +83,7 @@ go_library( "//pkg/volume/gce_pd:go_default_library", "//pkg/volume/glusterfs:go_default_library", "//pkg/volume/host_path:go_default_library", + "//pkg/volume/local:go_default_library", "//pkg/volume/nfs:go_default_library", "//pkg/volume/photon_pd:go_default_library", "//pkg/volume/portworx:go_default_library", diff --git a/cmd/kube-controller-manager/app/plugins.go b/cmd/kube-controller-manager/app/plugins.go index 38f126f6b8..45dbec4f2a 100644 --- a/cmd/kube-controller-manager/app/plugins.go +++ b/cmd/kube-controller-manager/app/plugins.go @@ -47,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/volume/gce_pd" "k8s.io/kubernetes/pkg/volume/glusterfs" "k8s.io/kubernetes/pkg/volume/host_path" + "k8s.io/kubernetes/pkg/volume/local" "k8s.io/kubernetes/pkg/volume/nfs" "k8s.io/kubernetes/pkg/volume/photon_pd" "k8s.io/kubernetes/pkg/volume/portworx" @@ -121,6 +122,7 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config componen allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...) allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...) allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...) + allPlugins = append(allPlugins, local.ProbeVolumePlugins()...) if cloud != nil { switch { diff --git a/cmd/kubelet/app/BUILD b/cmd/kubelet/app/BUILD index c29b3791ea..0202f3d5b0 100644 --- a/cmd/kubelet/app/BUILD +++ b/cmd/kubelet/app/BUILD @@ -92,6 +92,7 @@ go_library( "//pkg/volume/glusterfs:go_default_library", "//pkg/volume/host_path:go_default_library", "//pkg/volume/iscsi:go_default_library", + "//pkg/volume/local:go_default_library", "//pkg/volume/nfs:go_default_library", "//pkg/volume/photon_pd:go_default_library", "//pkg/volume/portworx:go_default_library", diff --git a/cmd/kubelet/app/plugins.go b/cmd/kubelet/app/plugins.go index 10be1927d7..be45e063d4 100644 --- a/cmd/kubelet/app/plugins.go +++ b/cmd/kubelet/app/plugins.go @@ -45,6 +45,7 @@ import ( "k8s.io/kubernetes/pkg/volume/glusterfs" "k8s.io/kubernetes/pkg/volume/host_path" "k8s.io/kubernetes/pkg/volume/iscsi" + "k8s.io/kubernetes/pkg/volume/local" "k8s.io/kubernetes/pkg/volume/nfs" "k8s.io/kubernetes/pkg/volume/photon_pd" "k8s.io/kubernetes/pkg/volume/portworx" @@ -95,6 +96,7 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin { allPlugins = append(allPlugins, projected.ProbeVolumePlugins()...) allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...) allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...) + allPlugins = append(allPlugins, local.ProbeVolumePlugins()...) return allPlugins } diff --git a/pkg/volume/BUILD b/pkg/volume/BUILD index 923204d37d..5ecc5b254a 100644 --- a/pkg/volume/BUILD +++ b/pkg/volume/BUILD @@ -106,6 +106,7 @@ filegroup( "//pkg/volume/glusterfs:all-srcs", "//pkg/volume/host_path:all-srcs", "//pkg/volume/iscsi:all-srcs", + "//pkg/volume/local:all-srcs", "//pkg/volume/nfs:all-srcs", "//pkg/volume/photon_pd:all-srcs", "//pkg/volume/portworx:all-srcs", diff --git a/pkg/volume/local/BUILD b/pkg/volume/local/BUILD new file mode 100644 index 0000000000..e6172e5ce3 --- /dev/null +++ b/pkg/volume/local/BUILD @@ -0,0 +1,56 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "local.go", + ], + tags = ["automanaged"], + deps = [ + "//pkg/api/v1:go_default_library", + "//pkg/util/mount:go_default_library", + "//pkg/util/strings:go_default_library", + "//pkg/volume:go_default_library", + "//pkg/volume/util:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["local_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//pkg/api/v1:go_default_library", + "//pkg/volume:go_default_library", + "//pkg/volume/testing:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/client-go/util/testing:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/volume/local/OWNERS b/pkg/volume/local/OWNERS new file mode 100644 index 0000000000..2737b7296e --- /dev/null +++ b/pkg/volume/local/OWNERS @@ -0,0 +1,21 @@ +approvers: +- saad-ali +- thockin +- vishh +- msau42 +- jingxu97 +- jsafrane +reviewers: +- thockin +- smarterclayton +- deads2k +- brendandburns +- derekwaynecarr +- pmorie +- saad-ali +- justinsb +- jsafrane +- rootfs +- jingxu97 +- msau42 +- vishh diff --git a/pkg/volume/local/doc.go b/pkg/volume/local/doc.go new file mode 100644 index 0000000000..6817475508 --- /dev/null +++ b/pkg/volume/local/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 local contains the internal representation of local volumes +package local // import "k8s.io/kubernetes/pkg/volume/local" diff --git a/pkg/volume/local/local.go b/pkg/volume/local/local.go new file mode 100644 index 0000000000..aeee2339e5 --- /dev/null +++ b/pkg/volume/local/local.go @@ -0,0 +1,267 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 local + +import ( + "fmt" + "os" + + "github.com/golang/glog" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/util/mount" + "k8s.io/kubernetes/pkg/util/strings" + "k8s.io/kubernetes/pkg/volume" + "k8s.io/kubernetes/pkg/volume/util" +) + +// This is the primary entrypoint for volume plugins. +func ProbeVolumePlugins() []volume.VolumePlugin { + return []volume.VolumePlugin{&localVolumePlugin{}} +} + +type localVolumePlugin struct { + host volume.VolumeHost +} + +var _ volume.VolumePlugin = &localVolumePlugin{} +var _ volume.PersistentVolumePlugin = &localVolumePlugin{} + +const ( + localVolumePluginName = "kubernetes.io/local-volume" +) + +func (plugin *localVolumePlugin) Init(host volume.VolumeHost) error { + plugin.host = host + return nil +} + +func (plugin *localVolumePlugin) GetPluginName() string { + return localVolumePluginName +} + +func (plugin *localVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) { + // This volume is only supported as a PersistentVolumeSource, so the PV name is unique + return spec.Name(), nil +} + +func (plugin *localVolumePlugin) CanSupport(spec *volume.Spec) bool { + // This volume is only supported as a PersistentVolumeSource + return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil) +} + +func (plugin *localVolumePlugin) RequiresRemount() bool { + return false +} + +func (plugin *localVolumePlugin) SupportsMountOption() bool { + return false +} + +func (plugin *localVolumePlugin) SupportsBulkVolumeVerification() bool { + return false +} + +func (plugin *localVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode { + // The current meaning of AccessMode is how many nodes can attach to it, not how many pods can mount it + return []v1.PersistentVolumeAccessMode{ + v1.ReadWriteOnce, + } +} + +func getVolumeSource(spec *volume.Spec) (*v1.LocalVolumeSource, bool, error) { + if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil { + return spec.PersistentVolume.Spec.Local, spec.ReadOnly, nil + } + + return nil, false, fmt.Errorf("Spec does not reference a Local volume type") +} + +func (plugin *localVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { + volumeSource, readOnly, err := getVolumeSource(spec) + if err != nil { + return nil, err + } + + return &localVolumeMounter{ + localVolume: &localVolume{ + podUID: pod.UID, + volName: spec.Name(), + mounter: plugin.host.GetMounter(), + plugin: plugin, + globalPath: volumeSource.Path, + }, + readOnly: readOnly, + }, nil + +} + +func (plugin *localVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) { + return &localVolumeUnmounter{ + localVolume: &localVolume{ + podUID: podUID, + volName: volName, + mounter: plugin.host.GetMounter(), + plugin: plugin, + }, + }, nil +} + +// TODO: check if no path and no topology constraints are ok +func (plugin *localVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) { + localVolume := &v1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: volumeName, + }, + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeSource: v1.PersistentVolumeSource{ + Local: &v1.LocalVolumeSource{ + Path: "", + }, + }, + }, + } + return volume.NewSpecFromPersistentVolume(localVolume, false), nil +} + +// Local volumes represent a local directory on a node. +// The directory at the globalPath will be bind-mounted to the pod's directory +type localVolume struct { + volName string + podUID types.UID + // Global path to the volume + globalPath string + // Mounter interface that provides system calls to mount the global path to the pod local path. + mounter mount.Interface + plugin *localVolumePlugin + // TODO: add metrics + volume.MetricsNil +} + +func (l *localVolume) GetPath() string { + return l.plugin.host.GetPodVolumeDir(l.podUID, strings.EscapeQualifiedNameForDisk(localVolumePluginName), l.volName) +} + +type localVolumeMounter struct { + *localVolume + readOnly bool +} + +var _ volume.Mounter = &localVolumeMounter{} + +func (m *localVolumeMounter) GetAttributes() volume.Attributes { + return volume.Attributes{ + ReadOnly: m.readOnly, + Managed: !m.readOnly, + SupportsSELinux: true, + } +} + +// CanMount checks prior to mount operations to verify that the required components (binaries, etc.) +// to mount the volume are available on the underlying node. +// If not, it returns an error +func (m *localVolumeMounter) CanMount() error { + return nil +} + +// SetUp bind mounts the directory to the volume path +func (m *localVolumeMounter) SetUp(fsGroup *types.UnixGroupID) error { + return m.SetUpAt(m.GetPath(), fsGroup) +} + +// SetUpAt bind mounts the directory to the volume path and sets up volume ownership +func (m *localVolumeMounter) SetUpAt(dir string, fsGroup *types.UnixGroupID) error { + if m.globalPath == "" { + err := fmt.Errorf("LocalVolume volume %q path is empty", m.volName) + return err + } + + notMnt, err := m.mounter.IsLikelyNotMountPoint(dir) + glog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly) + if err != nil && !os.IsNotExist(err) { + glog.Errorf("cannot validate mount point: %s %v", dir, err) + return err + } + if !notMnt { + return nil + } + + if err := os.MkdirAll(dir, 0750); err != nil { + glog.Errorf("mkdir failed on disk %s (%v)", dir, err) + return err + } + + // Perform a bind mount to the full path to allow duplicate mounts of the same volume. + options := []string{"bind"} + if m.readOnly { + options = append(options, "ro") + } + + glog.V(4).Infof("attempting to mount %s", dir) + err = m.mounter.Mount(m.globalPath, dir, "", options) + if err != nil { + notMnt, mntErr := m.mounter.IsLikelyNotMountPoint(dir) + if mntErr != nil { + glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr) + return err + } + if !notMnt { + if mntErr = m.mounter.Unmount(dir); mntErr != nil { + glog.Errorf("Failed to unmount: %v", mntErr) + return err + } + notMnt, mntErr := m.mounter.IsLikelyNotMountPoint(dir) + if mntErr != nil { + glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr) + return err + } + if !notMnt { + // This is very odd, we don't expect it. We'll try again next sync loop. + glog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir) + return err + } + } + os.Remove(dir) + glog.Errorf("Mount of volume %s failed: %v", dir, err) + return err + } + + if !m.readOnly { + // TODO: how to prevent multiple mounts with conflicting fsGroup? + return volume.SetVolumeOwnership(m, fsGroup) + } + return nil +} + +type localVolumeUnmounter struct { + *localVolume +} + +var _ volume.Unmounter = &localVolumeUnmounter{} + +// TearDown unmounts the bind mount +func (u *localVolumeUnmounter) TearDown() error { + return u.TearDownAt(u.GetPath()) +} + +// TearDownAt unmounts the bind mount +func (u *localVolumeUnmounter) TearDownAt(dir string) error { + glog.V(4).Infof("Unmounting volume %q at path %q\n", u.volName, dir) + return util.UnmountPath(dir, u.mounter) +} diff --git a/pkg/volume/local/local_test.go b/pkg/volume/local/local_test.go new file mode 100644 index 0000000000..b417eff3ea --- /dev/null +++ b/pkg/volume/local/local_test.go @@ -0,0 +1,288 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 local + +import ( + "os" + "path" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + utiltesting "k8s.io/client-go/util/testing" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/volume" + volumetest "k8s.io/kubernetes/pkg/volume/testing" +) + +const ( + testPVName = "pvA" + testMountPath = "pods/poduid/volumes/kubernetes.io~local-volume/pvA" + testNodeName = "fakeNodeName" +) + +func getPlugin(t *testing.T) (string, volume.VolumePlugin) { + tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") + if err != nil { + t.Fatalf("can't make a temp dir: %v", err) + } + + plugMgr := volume.VolumePluginMgr{} + plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) + + plug, err := plugMgr.FindPluginByName(localVolumePluginName) + if err != nil { + os.RemoveAll(tmpDir) + t.Fatalf("Can't find the plugin by name") + } + if plug.GetPluginName() != localVolumePluginName { + t.Errorf("Wrong name: %s", plug.GetPluginName()) + } + return tmpDir, plug +} + +func getPersistentPlugin(t *testing.T) (string, volume.PersistentVolumePlugin) { + tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") + if err != nil { + t.Fatalf("can't make a temp dir: %v", err) + } + + plugMgr := volume.VolumePluginMgr{} + plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) + + plug, err := plugMgr.FindPersistentPluginByName(localVolumePluginName) + if err != nil { + os.RemoveAll(tmpDir) + t.Fatalf("Can't find the plugin by name") + } + if plug.GetPluginName() != localVolumePluginName { + t.Errorf("Wrong name: %s", plug.GetPluginName()) + } + return tmpDir, plug +} + +func getTestVolume(readOnly bool) *volume.Spec { + pv := &v1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: testPVName, + }, + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeSource: v1.PersistentVolumeSource{ + Local: &v1.LocalVolumeSource{ + Path: "/test-vol", + }, + }, + }, + } + return volume.NewSpecFromPersistentVolume(pv, readOnly) +} + +func contains(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool { + for _, m := range modes { + if m == mode { + return true + } + } + return false +} + +func TestCanSupport(t *testing.T) { + tmpDir, plug := getPlugin(t) + defer os.RemoveAll(tmpDir) + + if !plug.CanSupport(getTestVolume(false)) { + t.Errorf("Expected true") + } +} + +func TestGetAccessModes(t *testing.T) { + tmpDir, plug := getPersistentPlugin(t) + defer os.RemoveAll(tmpDir) + + modes := plug.GetAccessModes() + if !contains(modes, v1.ReadWriteOnce) { + t.Errorf("Expected AccessModeType %q", v1.ReadWriteOnce) + } + + if contains(modes, v1.ReadWriteMany) { + t.Errorf("Found AccessModeType %q, expected not", v1.ReadWriteMany) + } + if contains(modes, v1.ReadOnlyMany) { + t.Errorf("Found AccessModeType %q, expected not", v1.ReadOnlyMany) + } +} + +func TestGetVolumeName(t *testing.T) { + tmpDir, plug := getPersistentPlugin(t) + defer os.RemoveAll(tmpDir) + + volName, err := plug.GetVolumeName(getTestVolume(false)) + if err != nil { + t.Errorf("Failed to get volume name: %v", err) + } + if volName != testPVName { + t.Errorf("Expected volume name %q, got %q", testPVName, volName) + } +} + +func TestMountUnmount(t *testing.T) { + tmpDir, plug := getPlugin(t) + defer os.RemoveAll(tmpDir) + + pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} + mounter, err := plug.NewMounter(getTestVolume(false), pod, volume.VolumeOptions{}) + if err != nil { + t.Errorf("Failed to make a new Mounter: %v", err) + } + if mounter == nil { + t.Fatalf("Got a nil Mounter") + } + + volPath := path.Join(tmpDir, testMountPath) + path := mounter.GetPath() + if path != volPath { + t.Errorf("Got unexpected path: %s", path) + } + + if err := mounter.SetUp(nil); err != nil { + t.Errorf("Expected success, got: %v", err) + } + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + t.Errorf("SetUp() failed, volume path not created: %s", path) + } else { + t.Errorf("SetUp() failed: %v", err) + } + } + + unmounter, err := plug.NewUnmounter(testPVName, pod.UID) + if err != nil { + t.Errorf("Failed to make a new Unmounter: %v", err) + } + if unmounter == nil { + t.Fatalf("Got a nil Unmounter") + } + + if err := unmounter.TearDown(); err != nil { + t.Errorf("Expected success, got: %v", err) + } + if _, err := os.Stat(path); err == nil { + t.Errorf("TearDown() failed, volume path still exists: %s", path) + } else if !os.IsNotExist(err) { + t.Errorf("SetUp() failed: %v", err) + } +} + +func TestConstructVolumeSpec(t *testing.T) { + tmpDir, plug := getPlugin(t) + defer os.RemoveAll(tmpDir) + + volPath := path.Join(tmpDir, testMountPath) + spec, err := plug.ConstructVolumeSpec(testPVName, volPath) + if err != nil { + t.Errorf("ConstructVolumeSpec() failed: %v", err) + } + if spec == nil { + t.Fatalf("ConstructVolumeSpec() returned nil") + } + + volName := spec.Name() + if volName != testPVName { + t.Errorf("Expected volume name %q, got %q", testPVName, volName) + } + + if spec.Volume != nil { + t.Errorf("Volume object returned, expected nil") + } + + pv := spec.PersistentVolume + if pv == nil { + t.Fatalf("PersistentVolume object nil") + } + + ls := pv.Spec.PersistentVolumeSource.Local + if ls == nil { + t.Fatalf("LocalVolumeSource object nil") + } +} + +func TestPersistentClaimReadOnlyFlag(t *testing.T) { + tmpDir, plug := getPlugin(t) + defer os.RemoveAll(tmpDir) + + // Read only == true + pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} + mounter, err := plug.NewMounter(getTestVolume(true), pod, volume.VolumeOptions{}) + if err != nil { + t.Errorf("Failed to make a new Mounter: %v", err) + } + if mounter == nil { + t.Fatalf("Got a nil Mounter") + } + if !mounter.GetAttributes().ReadOnly { + t.Errorf("Expected true for mounter.IsReadOnly") + } + + // Read only == false + mounter, err = plug.NewMounter(getTestVolume(false), pod, volume.VolumeOptions{}) + if err != nil { + t.Errorf("Failed to make a new Mounter: %v", err) + } + if mounter == nil { + t.Fatalf("Got a nil Mounter") + } + if mounter.GetAttributes().ReadOnly { + t.Errorf("Expected false for mounter.IsReadOnly") + } +} + +func TestUnsupportedPlugins(t *testing.T) { + tmpDir, err := utiltesting.MkTmpdir("localVolumeTest") + if err != nil { + t.Fatalf("can't make a temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + plugMgr := volume.VolumePluginMgr{} + plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) + spec := getTestVolume(false) + + recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec) + if err == nil && recyclePlug != nil { + t.Errorf("Recyclable plugin found, expected none") + } + + deletePlug, err := plugMgr.FindDeletablePluginByName(localVolumePluginName) + if err == nil && deletePlug != nil { + t.Errorf("Deletable plugin found, expected none") + } + + attachPlug, err := plugMgr.FindAttachablePluginByName(localVolumePluginName) + if err == nil && attachPlug != nil { + t.Errorf("Attachable plugin found, expected none") + } + + createPlug, err := plugMgr.FindCreatablePluginBySpec(spec) + if err == nil && createPlug != nil { + t.Errorf("Creatable plugin found, expected none") + } + + provisionPlug, err := plugMgr.FindProvisionablePluginByName(localVolumePluginName) + if err == nil && provisionPlug != nil { + t.Errorf("Provisionable plugin found, expected none") + } +} diff --git a/pkg/volume/util/BUILD b/pkg/volume/util/BUILD index a70b367a07..c2ff2f5669 100644 --- a/pkg/volume/util/BUILD +++ b/pkg/volume/util/BUILD @@ -44,6 +44,9 @@ go_test( library = ":go_default_library", tags = ["automanaged"], deps = [ + "//pkg/api/v1:go_default_library", + "//pkg/api/v1/helper:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/client-go/util/testing:go_default_library", ], diff --git a/pkg/volume/util/operationexecutor/BUILD b/pkg/volume/util/operationexecutor/BUILD index 5bee309d25..9817a5f606 100644 --- a/pkg/volume/util/operationexecutor/BUILD +++ b/pkg/volume/util/operationexecutor/BUILD @@ -22,6 +22,7 @@ go_library( "//pkg/kubelet/events:go_default_library", "//pkg/util/mount:go_default_library", "//pkg/volume:go_default_library", + "//pkg/volume/util:go_default_library", "//pkg/volume/util/nestedpendingoperations:go_default_library", "//pkg/volume/util/types:go_default_library", "//pkg/volume/util/volumehelper:go_default_library", diff --git a/plugin/pkg/scheduler/algorithm/predicates/BUILD b/plugin/pkg/scheduler/algorithm/predicates/BUILD index 77880a3316..969b312ba3 100644 --- a/plugin/pkg/scheduler/algorithm/predicates/BUILD +++ b/plugin/pkg/scheduler/algorithm/predicates/BUILD @@ -22,6 +22,7 @@ go_library( "//pkg/api/v1/helper:go_default_library", "//pkg/api/v1/helper/qos:go_default_library", "//pkg/client/listers/core/v1:go_default_library", + "//pkg/volume/util:go_default_library", "//plugin/pkg/scheduler/algorithm:go_default_library", "//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library", "//plugin/pkg/scheduler/schedulercache:go_default_library", @@ -31,6 +32,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//vendor/k8s.io/client-go/util/workqueue:go_default_library", + "//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset:go_default_library", ], )