Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Volume Accounting Interface methods #18232

Merged
merged 1 commit into from
Dec 11, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ func newMockRecycler(spec *volume.Spec, host volume.VolumeHost, config volume.Vo
type mockRecycler struct {
path string
host volume.VolumeHost
volume.MetricsNil
}

func (r *mockRecycler) GetPath() string {
Expand Down
7 changes: 4 additions & 3 deletions pkg/kubelet/kubelet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ func TestGetPodVolumesFromDisk(t *testing.T) {

type stubVolume struct {
path string
volume.MetricsNil
}

func (f *stubVolume) GetPath() string {
Expand Down Expand Up @@ -559,9 +560,9 @@ func TestMakeVolumeMounts(t *testing.T) {
}

podVolumes := kubecontainer.VolumeMap{
"disk": kubecontainer.VolumeInfo{Builder: &stubVolume{"/mnt/disk"}},
"disk4": kubecontainer.VolumeInfo{Builder: &stubVolume{"/mnt/host"}},
"disk5": kubecontainer.VolumeInfo{Builder: &stubVolume{"/var/lib/kubelet/podID/volumes/empty/disk5"}},
"disk": kubecontainer.VolumeInfo{Builder: &stubVolume{path: "/mnt/disk"}},
"disk4": kubecontainer.VolumeInfo{Builder: &stubVolume{path: "/mnt/host"}},
"disk5": kubecontainer.VolumeInfo{Builder: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}},
}

pod := api.Pod{
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/aws_ebs/aws_ebs.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ type awsElasticBlockStore struct {
// Mounter interface that provides system calls to mount the global path to the pod local path.
mounter mount.Interface
plugin *awsElasticBlockStorePlugin
volume.MetricsNil
}

func detachDiskLogError(ebs *awsElasticBlockStore) {
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/cephfs/cephfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ type cephfs struct {
readonly bool
mounter mount.Interface
plugin *cephfsPlugin
volume.MetricsNil
}

type cephfsBuilder struct {
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/cinder/cinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ type cinderVolume struct {
// diskMounter provides the interface that is used to mount the actual block device.
blockDeviceMounter mount.Interface
plugin *cinderPlugin
volume.MetricsNil
}

func detachDiskLogError(cd *cinderVolume) {
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/downwardapi/downwardapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type downwardAPIVolume struct {
pod *api.Pod
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.
Expand Down
35 changes: 21 additions & 14 deletions pkg/volume/empty_dir/empty_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,14 @@ func (plugin *emptyDirPlugin) newBuilderInternal(spec *volume.Spec, pod *api.Pod
medium = spec.Volume.EmptyDir.Medium
}
return &emptyDir{
pod: pod,
volName: spec.Name(),
medium: medium,
mounter: mounter,
mountDetector: mountDetector,
plugin: plugin,
rootContext: opts.RootContext,
pod: pod,
volName: spec.Name(),
medium: medium,
mounter: mounter,
mountDetector: mountDetector,
plugin: plugin,
rootContext: opts.RootContext,
MetricsProvider: volume.NewMetricsDu(GetPath(pod.UID, spec.Name(), plugin.host)),
}, nil
}

Expand All @@ -96,12 +97,13 @@ func (plugin *emptyDirPlugin) NewCleaner(volName string, podUID types.UID) (volu

func (plugin *emptyDirPlugin) newCleanerInternal(volName string, podUID types.UID, mounter mount.Interface, mountDetector mountDetector) (volume.Cleaner, error) {
ed := &emptyDir{
pod: &api.Pod{ObjectMeta: api.ObjectMeta{UID: podUID}},
volName: volName,
medium: api.StorageMediumDefault, // might be changed later
mounter: mounter,
mountDetector: mountDetector,
plugin: plugin,
pod: &api.Pod{ObjectMeta: api.ObjectMeta{UID: podUID}},
volName: volName,
medium: api.StorageMediumDefault, // might be changed later
mounter: mounter,
mountDetector: mountDetector,
plugin: plugin,
MetricsProvider: volume.NewMetricsDu(GetPath(podUID, volName, plugin.host)),
}
return ed, nil
}
Expand Down Expand Up @@ -133,6 +135,7 @@ type emptyDir struct {
mountDetector mountDetector
plugin *emptyDirPlugin
rootContext string
volume.MetricsProvider
}

func (ed *emptyDir) GetAttributes() volume.Attributes {
Expand Down Expand Up @@ -265,8 +268,12 @@ func (ed *emptyDir) setupDir(dir string) error {
}

func (ed *emptyDir) GetPath() string {
return GetPath(ed.pod.UID, ed.volName, ed.plugin.host)
}

func GetPath(uid types.UID, volName string, host volume.VolumeHost) string {
name := emptyDirPluginName
return ed.plugin.host.GetPodVolumeDir(ed.pod.UID, util.EscapeQualifiedNameForDisk(name), ed.volName)
return host.GetPodVolumeDir(uid, util.EscapeQualifiedNameForDisk(name), volName)
}

// TearDown simply discards everything in the directory.
Expand Down
39 changes: 39 additions & 0 deletions pkg/volume/empty_dir/empty_dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,42 @@ func TestPluginBackCompat(t *testing.T) {
t.Errorf("Got unexpected path: %s", volPath)
}
}

// TestMetrics tests that MetricProvider methods return sane values.
func TestMetrics(t *testing.T) {
// Create an empty temp directory for the volume
tmpDir, err := ioutil.TempDir(os.TempDir(), "empty_dir_test")
if err != nil {
t.Fatalf("Can't make a tmp dir: %v", err)
}
defer os.RemoveAll(tmpDir)

plug := makePluginUnderTest(t, "kubernetes.io/empty-dir", tmpDir)

spec := &api.Volume{
Name: "vol1",
}
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
builder, err := plug.NewBuilder(volume.NewSpecFromVolume(spec), pod, volume.VolumeOptions{RootContext: ""})
if err != nil {
t.Errorf("Failed to make a new Builder: %v", err)
}

// Need to create the subdirectory
os.MkdirAll(builder.GetPath(), 0755)

// TODO(pwittroc): Move this into a reusable testing utility
metrics, err := builder.GetMetrics()
if err != nil {
t.Errorf("Unexpected error when calling GetMetrics %v", err)
}
if metrics.Used.Value() != 4096 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is very fs type dependent. In tmpfs, directory size is 0, and thus we'd expect != 4096 is correct. A fs can also have block size more than 4096, see stat(2) . The formula should probably be metrics.Used.Value() == st.st_blksize * st.st_blocks

t.Errorf("Expected Used %d to be 4096", metrics.Used.Value())
}
if metrics.Capacity.Value() <= 0 {
t.Errorf("Expected Capacity to be greater than 0")
}
if metrics.Available.Value() <= 0 {
t.Errorf("Expected Available to be greater than 0")
}
}
1 change: 1 addition & 0 deletions pkg/volume/fc/fc.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ type fcDisk struct {
manager diskManager
// io handler interface
io ioHandler
volume.MetricsNil
}

func (fc *fcDisk) GetPath() string {
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/flocker/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ type flockerBuilder struct {
exe exec.Interface
opts volume.VolumeOptions
readOnly bool
volume.MetricsNil
}

func (b flockerBuilder) GetAttributes() volume.Attributes {
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/gce_pd/gce_pd.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ type gcePersistentDisk struct {
// Mounter interface that provides system calls to mount the global path to the pod local path.
mounter mount.Interface
plugin *gcePersistentDiskPlugin
volume.MetricsNil
}

func detachDiskLogError(pd *gcePersistentDisk) {
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/git_repo/git_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type gitRepoVolume struct {
volName string
podUID types.UID
plugin *gitRepoPlugin
volume.MetricsNil
}

var _ volume.Volume = &gitRepoVolume{}
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/glusterfs/glusterfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ type glusterfs struct {
pod *api.Pod
mounter mount.Interface
plugin *glusterfsPlugin
volume.MetricsNil
}

type glusterfsBuilder struct {
Expand Down
29 changes: 20 additions & 9 deletions pkg/volume/host_path/host_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,25 @@ func (plugin *hostPathPlugin) GetAccessModes() []api.PersistentVolumeAccessMode

func (plugin *hostPathPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, _ volume.VolumeOptions) (volume.Builder, error) {
if spec.Volume != nil && spec.Volume.HostPath != nil {
path := spec.Volume.HostPath.Path
return &hostPathBuilder{
hostPath: &hostPath{path: spec.Volume.HostPath.Path},
hostPath: &hostPath{path: path, MetricsProvider: volume.NewMetricsDu(path)},
readOnly: false,
}, nil
} else {
path := spec.PersistentVolume.Spec.HostPath.Path
return &hostPathBuilder{
hostPath: &hostPath{path: spec.PersistentVolume.Spec.HostPath.Path},
hostPath: &hostPath{path: path, MetricsProvider: volume.NewMetricsDu(path)},
readOnly: spec.ReadOnly,
}, nil
}
}

func (plugin *hostPathPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) {
return &hostPathCleaner{&hostPath{""}}, nil
return &hostPathCleaner{&hostPath{
path: "",
MetricsProvider: volume.NewMetricsDu(""),
}}, nil
}

func (plugin *hostPathPlugin) NewRecycler(spec *volume.Spec) (volume.Recycler, error) {
Expand All @@ -130,20 +135,23 @@ func newRecycler(spec *volume.Spec, host volume.VolumeHost, config volume.Volume
if spec.PersistentVolume == nil || spec.PersistentVolume.Spec.HostPath == nil {
return nil, fmt.Errorf("spec.PersistentVolumeSource.HostPath is nil")
}
path := spec.PersistentVolume.Spec.HostPath.Path
return &hostPathRecycler{
name: spec.Name(),
path: spec.PersistentVolume.Spec.HostPath.Path,
host: host,
config: config,
timeout: volume.CalculateTimeoutForVolume(config.RecyclerMinimumTimeout, config.RecyclerTimeoutIncrement, spec.PersistentVolume),
name: spec.Name(),
path: path,
host: host,
config: config,
timeout: volume.CalculateTimeoutForVolume(config.RecyclerMinimumTimeout, config.RecyclerTimeoutIncrement, spec.PersistentVolume),
MetricsProvider: volume.NewMetricsDu(path),
}, nil
}

func newDeleter(spec *volume.Spec, host volume.VolumeHost) (volume.Deleter, error) {
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.HostPath == nil {
return nil, fmt.Errorf("spec.PersistentVolumeSource.HostPath is nil")
}
return &hostPathDeleter{spec.Name(), spec.PersistentVolume.Spec.HostPath.Path, host}, nil
path := spec.PersistentVolume.Spec.HostPath.Path
return &hostPathDeleter{spec.Name(), path, host, volume.NewMetricsDu(path)}, nil
}

func newCreater(options volume.VolumeOptions, host volume.VolumeHost) (volume.Creater, error) {
Expand All @@ -154,6 +162,7 @@ func newCreater(options volume.VolumeOptions, host volume.VolumeHost) (volume.Cr
// The direct at the specified path will be directly exposed to the container.
type hostPath struct {
path string
volume.MetricsProvider
}

func (hp *hostPath) GetPath() string {
Expand Down Expand Up @@ -214,6 +223,7 @@ type hostPathRecycler struct {
host volume.VolumeHost
config volume.VolumeConfig
timeout int64
volume.MetricsProvider
}

func (r *hostPathRecycler) GetPath() string {
Expand Down Expand Up @@ -280,6 +290,7 @@ type hostPathDeleter struct {
name string
path string
host volume.VolumeHost
volume.MetricsProvider
}

func (r *hostPathDeleter) GetPath() string {
Expand Down
45 changes: 44 additions & 1 deletion pkg/volume/host_path/host_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package host_path

import (
"fmt"
"io/ioutil"
"os"
"testing"

Expand Down Expand Up @@ -92,7 +93,7 @@ func TestDeleter(t *testing.T) {
defer os.RemoveAll(tempPath)
err := os.MkdirAll(tempPath, 0750)
if err != nil {
t.Fatal("Failed to create tmp directory for deleter: %v", err)
t.Fatalf("Failed to create tmp directory for deleter: %v", err)
}

plugMgr := volume.VolumePluginMgr{}
Expand Down Expand Up @@ -272,3 +273,45 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
t.Errorf("Expected true for builder.IsReadOnly")
}
}

// TestMetrics tests that MetricProvider methods return sane values.
func TestMetrics(t *testing.T) {
// Create an empty temp directory for the volume
tmpDir, err := ioutil.TempDir(os.TempDir(), "host_path_test")
if err != nil {
t.Fatalf("Can't make a tmp dir: %v", err)
}
defer os.RemoveAll(tmpDir)

plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volume.NewFakeVolumeHost(tmpDir, nil, nil))

plug, err := plugMgr.FindPluginByName("kubernetes.io/host-path")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
spec := &api.Volume{
Name: "vol1",
VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: tmpDir}},
}
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
builder, err := plug.NewBuilder(volume.NewSpecFromVolume(spec), pod, volume.VolumeOptions{})
if err != nil {
t.Errorf("Failed to make a new Builder: %v", err)
}

// TODO(pwittroc): Move this into a reusable testing utility
metrics, err := builder.GetMetrics()
if err != nil {
t.Errorf("Unexpected error when calling GetMetrics %v", err)
}
if metrics.Used.Value() != 4096 {
t.Errorf("Expected Used %d to be 4096", metrics.Used)
}
if metrics.Capacity.Value() <= 0 {
t.Errorf("Expected Capacity to be greater than 0")
}
if metrics.Available.Value() <= 0 {
t.Errorf("Expected Available to be greater than 0")
}
}
1 change: 1 addition & 0 deletions pkg/volume/iscsi/iscsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ type iscsiDisk struct {
plugin *iscsiPlugin
// Utility interface that provides API calls to the provider to attach/detach disks.
manager diskManager
volume.MetricsNil
}

func (iscsi *iscsiDisk) GetPath() string {
Expand Down