From 282404cbc94af7c24ca54b023c6fbc61f8302a6c Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Mon, 14 Aug 2017 12:16:25 +0200 Subject: [PATCH] Add Exec interface to VolumeHost This exec should be used by volume plugins to execute mount utilities. It will eventually execute things in mount containers. --- .../attachdetach/attach_detach_controller.go | 4 ++ .../volume/persistentvolume/volume_host.go | 4 ++ pkg/kubelet/volume_host.go | 4 ++ pkg/util/mount/BUILD | 1 + pkg/util/mount/exec.go | 50 +++++++++++++++++++ pkg/util/mount/mount.go | 10 ++++ pkg/volume/plugins.go | 3 ++ pkg/volume/testing/testing.go | 6 +++ 8 files changed, 82 insertions(+) create mode 100644 pkg/util/mount/exec.go diff --git a/pkg/controller/volume/attachdetach/attach_detach_controller.go b/pkg/controller/volume/attachdetach/attach_detach_controller.go index 6c206e504e..29231260c6 100644 --- a/pkg/controller/volume/attachdetach/attach_detach_controller.go +++ b/pkg/controller/volume/attachdetach/attach_detach_controller.go @@ -571,6 +571,10 @@ func (adc *attachDetachController) GetConfigMapFunc() func(namespace, name strin } } +func (adc *attachDetachController) GetExec(pluginName string) mount.Exec { + return mount.NewOsExec() +} + func (adc *attachDetachController) addNodeToDswp(node *v1.Node, nodeName types.NodeName) { if _, exists := node.Annotations[volumehelper.ControllerManagedAttachAnnotation]; exists { keepTerminatedPodVolumes := false diff --git a/pkg/controller/volume/persistentvolume/volume_host.go b/pkg/controller/volume/persistentvolume/volume_host.go index e4c402f006..6c91a8817d 100644 --- a/pkg/controller/volume/persistentvolume/volume_host.go +++ b/pkg/controller/volume/persistentvolume/volume_host.go @@ -93,6 +93,10 @@ func (adc *PersistentVolumeController) GetConfigMapFunc() func(namespace, name s } } +func (adc *PersistentVolumeController) GetExec(pluginName string) mount.Exec { + return mount.NewOsExec() +} + func (ctrl *PersistentVolumeController) GetNodeLabels() (map[string]string, error) { return nil, fmt.Errorf("GetNodeLabels() unsupported in PersistentVolumeController") } diff --git a/pkg/kubelet/volume_host.go b/pkg/kubelet/volume_host.go index 51dd94f023..2ab22d5773 100644 --- a/pkg/kubelet/volume_host.go +++ b/pkg/kubelet/volume_host.go @@ -156,3 +156,7 @@ func (kvh *kubeletVolumeHost) GetNodeLabels() (map[string]string, error) { } return node.Labels, nil } + +func (kvh *kubeletVolumeHost) GetExec(pluginName string) mount.Exec { + return mount.NewOsExec() +} diff --git a/pkg/util/mount/BUILD b/pkg/util/mount/BUILD index 66aa2cbb6d..6087221857 100644 --- a/pkg/util/mount/BUILD +++ b/pkg/util/mount/BUILD @@ -10,6 +10,7 @@ go_library( name = "go_default_library", srcs = [ "doc.go", + "exec.go", "fake.go", "mount.go", "mount_unsupported.go", diff --git a/pkg/util/mount/exec.go b/pkg/util/mount/exec.go new file mode 100644 index 0000000000..716cda0a0c --- /dev/null +++ b/pkg/util/mount/exec.go @@ -0,0 +1,50 @@ +/* +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 mount + +import "k8s.io/utils/exec" + +func NewOsExec() Exec { + return &osExec{} +} + +// Real implementation of Exec interface that uses simple util.Exec +type osExec struct{} + +var _ Exec = &osExec{} + +func (e *osExec) Run(cmd string, args ...string) ([]byte, error) { + exe := exec.New() + return exe.Command(cmd, args...).CombinedOutput() +} + +func NewFakeExec(run runHook) *FakeExec { + return &FakeExec{runHook: run} +} + +// Fake for testing. +type FakeExec struct { + runHook runHook +} +type runHook func(cmd string, args ...string) ([]byte, error) + +func (f *FakeExec) Run(cmd string, args ...string) ([]byte, error) { + if f.runHook != nil { + return f.runHook(cmd, args...) + } + return nil, nil +} diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index eab2889951..7552bbb094 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -70,6 +70,16 @@ type Interface interface { GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) } +// Exec executes command where mount utilities are. This can be either the host, +// container where kubelet runs or even a remote pod with mount utilities. +// Usual pkg/util/exec interface is not used because kubelet.RunInContainer does +// not provide stdin/stdout/stderr streams. +type Exec interface { + // Run executes a command and returns its stdout + stderr combined in one + // stream. + Run(cmd string, args ...string) ([]byte, error) +} + // Compile-time check to ensure all Mounter implementations satisfy // the mount interface var _ Interface = &Mounter{} diff --git a/pkg/volume/plugins.go b/pkg/volume/plugins.go index 39c67366ff..1521a0afc7 100644 --- a/pkg/volume/plugins.go +++ b/pkg/volume/plugins.go @@ -244,6 +244,9 @@ type VolumeHost interface { // Returns a function that returns a configmap. GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) + // Returns an interface that should be used to execute any utilities in volume plugins + GetExec(pluginName string) mount.Exec + // Returns the labels on the node GetNodeLabels() (map[string]string, error) } diff --git a/pkg/volume/testing/testing.go b/pkg/volume/testing/testing.go index 9ccd840c89..bc96bd6ecd 100644 --- a/pkg/volume/testing/testing.go +++ b/pkg/volume/testing/testing.go @@ -49,6 +49,7 @@ type fakeVolumeHost struct { pluginMgr VolumePluginMgr cloud cloudprovider.Interface mounter mount.Interface + exec mount.Exec writer io.Writer } @@ -64,6 +65,7 @@ func newFakeVolumeHost(rootDir string, kubeClient clientset.Interface, plugins [ host := &fakeVolumeHost{rootDir: rootDir, kubeClient: kubeClient, cloud: cloud} host.mounter = &mount.FakeMounter{} host.writer = &io.StdWriter{} + host.exec = mount.NewFakeExec(nil) host.pluginMgr.InitPlugins(plugins, host) return host } @@ -142,6 +144,10 @@ func (f *fakeVolumeHost) GetSecretFunc() func(namespace, name string) (*v1.Secre } } +func (f *fakeVolumeHost) GetExec(pluginName string) mount.Exec { + return f.exec +} + func (f *fakeVolumeHost) GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) { return func(namespace, name string) (*v1.ConfigMap, error) { return f.kubeClient.Core().ConfigMaps(namespace).Get(name, metav1.GetOptions{})