Merge pull request #18232 from pwittrock/fsacct

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2015-12-11 00:48:09 -08:00
commit 23baca87b5
28 changed files with 494 additions and 33 deletions

View File

@ -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 {

View File

@ -514,6 +514,7 @@ func TestGetPodVolumesFromDisk(t *testing.T) {
type stubVolume struct {
path string
volume.MetricsNil
}
func (f *stubVolume) GetPath() string {
@ -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{

View File

@ -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) {

View File

@ -143,6 +143,7 @@ type cephfs struct {
readonly bool
mounter mount.Interface
plugin *cephfsPlugin
volume.MetricsNil
}
type cephfsBuilder struct {

View File

@ -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) {

View File

@ -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.

View File

@ -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
}
@ -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
}
@ -133,6 +135,7 @@ type emptyDir struct {
mountDetector mountDetector
plugin *emptyDirPlugin
rootContext string
volume.MetricsProvider
}
func (ed *emptyDir) GetAttributes() volume.Attributes {
@ -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.

View File

@ -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 {
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")
}
}

View File

@ -149,6 +149,7 @@ type fcDisk struct {
manager diskManager
// io handler interface
io ioHandler
volume.MetricsNil
}
func (fc *fcDisk) GetPath() string {

View File

@ -111,6 +111,7 @@ type flockerBuilder struct {
exe exec.Interface
opts volume.VolumeOptions
readOnly bool
volume.MetricsNil
}
func (b flockerBuilder) GetAttributes() volume.Attributes {

View File

@ -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) {

View File

@ -89,6 +89,7 @@ type gitRepoVolume struct {
volName string
podUID types.UID
plugin *gitRepoPlugin
volume.MetricsNil
}
var _ volume.Volume = &gitRepoVolume{}

View File

@ -142,6 +142,7 @@ type glusterfs struct {
pod *api.Pod
mounter mount.Interface
plugin *glusterfsPlugin
volume.MetricsNil
}
type glusterfsBuilder struct {

View File

@ -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) {
@ -130,12 +135,14 @@ 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
}
@ -143,7 +150,8 @@ func newDeleter(spec *volume.Spec, host volume.VolumeHost) (volume.Deleter, erro
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) {
@ -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 {
@ -214,6 +223,7 @@ type hostPathRecycler struct {
host volume.VolumeHost
config volume.VolumeConfig
timeout int64
volume.MetricsProvider
}
func (r *hostPathRecycler) GetPath() string {
@ -280,6 +290,7 @@ type hostPathDeleter struct {
name string
path string
host volume.VolumeHost
volume.MetricsProvider
}
func (r *hostPathDeleter) GetPath() string {

View File

@ -18,6 +18,7 @@ package host_path
import (
"fmt"
"io/ioutil"
"os"
"testing"
@ -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{}
@ -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")
}
}

View File

@ -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 {

91
pkg/volume/metrics_du.go Normal file
View File

@ -0,0 +1,91 @@
/*
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 volume
import (
"errors"
"fmt"
"os/exec"
"strings"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/volume/util"
)
var _ MetricsProvider = &metricsDu{}
// metricsDu represents a MetricsProvider that calculates the used and available
// Volume space by executing the "du" command and gathering filesystem info for the Volume path.
type metricsDu struct {
// the directory path the volume is mounted to.
path string
}
// NewMetricsDu creates a new metricsDu with the Volume path.
func NewMetricsDu(path string) MetricsProvider {
return &metricsDu{path}
}
// See MetricsProvider.GetMetrics
// GetMetrics calculates the volume usage and device free space by executing "du"
// and gathering filesystem info for the Volume path.
func (md *metricsDu) GetMetrics() (*Metrics, error) {
metrics := &Metrics{}
if md.path == "" {
return metrics, errors.New("no path defined for disk usage metrics.")
}
err := md.runDu(metrics)
if err != nil {
return metrics, err
}
err = md.getFsInfo(metrics)
if err != nil {
return metrics, err
}
return metrics, nil
}
// runDu executes the "du" command and writes the results to metrics.Used
func (md *metricsDu) runDu(metrics *Metrics) error {
// Uses the same niceness level as cadvisor.fs does when running du
// Uses -B 1 to always scale to a blocksize of 1 byte
out, err := exec.Command("nice", "-n", "19", "du", "-s", "-B", "1", md.path).CombinedOutput()
if err != nil {
return fmt.Errorf("failed command 'du' on %s with error %v", md.path, err)
}
used, err := resource.ParseQuantity(strings.Fields(string(out))[0])
if err != nil {
return fmt.Errorf("failed to parse 'du' output %s due to error %v", out, err)
}
used.Format = resource.BinarySI
metrics.Used = used
return nil
}
// getFsInfo writes metrics.Capacity and metrics.Available from the filesystem info
func (md *metricsDu) getFsInfo(metrics *Metrics) error {
available, capacity, err := util.FsInfo(md.path)
if err != nil {
return fmt.Errorf("Failed to get FsInfo due to error %v", err)
}
metrics.Available = resource.NewQuantity(available, resource.BinarySI)
metrics.Capacity = resource.NewQuantity(capacity, resource.BinarySI)
return nil
}

View File

@ -0,0 +1,90 @@
/*
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 volume
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
// TestMetricsDuGetCapacity tests that MetricsDu can read disk usage
// for path
func TestMetricsDuGetCapacity(t *testing.T) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "metrics_du_test")
if err != nil {
t.Fatalf("Can't make a tmp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
metrics := NewMetricsDu(tmpDir)
actual, err := metrics.GetMetrics()
if err != nil {
t.Errorf("Unexpected error when calling GetMetrics %v", err)
}
if actual.Used.Value() != 4096 {
t.Errorf("Expected Used %d for empty directory to be 4096.", actual.Used.Value())
}
// TODO(pwittroc): Figure out a way to test these values for correctness, maybe by formatting and mounting a file
// as a filesystem
if actual.Capacity.Value() <= 0 {
t.Errorf("Expected Capacity %d to be greater than 0.", actual.Capacity.Value())
}
if actual.Available.Value() <= 0 {
t.Errorf("Expected Available %d to be greater than 0.", actual.Available.Value())
}
// Write a file and expect Used to increase
ioutil.WriteFile(filepath.Join(tmpDir, "f1"), []byte("Hello World"), os.ModeTemporary)
actual, err = metrics.GetMetrics()
if err != nil {
t.Errorf("Unexpected error when calling GetMetrics %v", err)
}
if actual.Used.Value() != 8192 {
t.Errorf("Unexpected Used for directory with file. Expected 8192, was %d.", actual.Used.Value())
}
}
// TestMetricsDuRequireInit tests that if MetricsDu is not initialized with a path, GetMetrics
// returns an error
func TestMetricsDuRequirePath(t *testing.T) {
metrics := &metricsDu{}
actual, err := metrics.GetMetrics()
expected := &Metrics{}
if *actual != *expected {
t.Errorf("Expected empty Metrics from uninitialized MetricsDu, actual %v", *actual)
}
if err == nil {
t.Errorf("Expected error when calling GetMetrics on uninitialized MetricsDu, actual nil")
}
}
// TestMetricsDuRealDirectory tests that if MetricsDu is initialized to a non-existent path, GetMetrics
// returns an error
func TestMetricsDuRequireRealDirectory(t *testing.T) {
metrics := NewMetricsDu("/not/a/real/directory")
actual, err := metrics.GetMetrics()
expected := &Metrics{}
if *actual != *expected {
t.Errorf("Expected empty Metrics from incorrectly initialized MetricsDu, actual %v", *actual)
}
if err == nil {
t.Errorf("Expected error when calling GetMetrics on incorrectly initialized MetricsDu, actual nil")
}
}

31
pkg/volume/metrics_nil.go Normal file
View File

@ -0,0 +1,31 @@
/*
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 volume
import "errors"
var _ MetricsProvider = &MetricsNil{}
// MetricsNil represents a MetricsProvider that does not support returning
// Metrics. It serves as a placeholder for Volumes that do not yet support metrics.
type MetricsNil struct{}
// See MetricsProvider.GetMetrics
// GetMetrics returns an empty Metrics and an error.
func (*MetricsNil) GetMetrics() (*Metrics, error) {
return &Metrics{}, errors.New("metrics are not supported for MetricsNil Volumes")
}

View File

@ -0,0 +1,33 @@
/*
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 volume
import (
"testing"
)
func TestMetricsNilGetCapacity(t *testing.T) {
metrics := &MetricsNil{}
actual, err := metrics.GetMetrics()
expected := &Metrics{}
if *actual != *expected {
t.Errorf("Expected empty Metrics, actual %v", *actual)
}
if err == nil {
t.Errorf("Expected error when calling GetMetrics, actual nil")
}
}

View File

@ -131,6 +131,7 @@ type nfs struct {
plugin *nfsPlugin
// decouple creating recyclers by deferring to a function. Allows for easier testing.
newRecyclerFunc func(spec *volume.Spec, host volume.VolumeHost, volumeConfig volume.VolumeConfig) (volume.Recycler, error)
volume.MetricsNil
}
func (nfsVolume *nfs) GetPath() string {
@ -271,6 +272,7 @@ type nfsRecycler struct {
host volume.VolumeHost
config volume.VolumeConfig
timeout int64
volume.MetricsNil
}
func (r *nfsRecycler) GetPath() string {

View File

@ -92,6 +92,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 {

View File

@ -172,6 +172,7 @@ type rbd struct {
mounter *mount.SafeFormatAndMount
// Utility interface that provides API calls to the provider to attach/detach disks.
manager diskManager
volume.MetricsNil
}
func (rbd *rbd) GetPath() string {

View File

@ -61,14 +61,14 @@ func (plugin *secretPlugin) CanSupport(spec *volume.Spec) bool {
func (plugin *secretPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) {
return &secretVolumeBuilder{
secretVolume: &secretVolume{spec.Name(), pod.UID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter()},
secretVolume: &secretVolume{spec.Name(), pod.UID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}},
secretName: spec.Volume.Secret.SecretName,
pod: *pod,
opts: &opts}, nil
}
func (plugin *secretPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) {
return &secretVolumeCleaner{&secretVolume{volName, podUID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter()}}, nil
return &secretVolumeCleaner{&secretVolume{volName, podUID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}}}, nil
}
type secretVolume struct {
@ -77,6 +77,7 @@ type secretVolume struct {
plugin *secretPlugin
mounter mount.Interface
writer ioutil.Writer
volume.MetricsNil
}
var _ volume.Volume = &secretVolume{}

View File

@ -136,19 +136,19 @@ func (plugin *FakeVolumePlugin) CanSupport(spec *Spec) bool {
}
func (plugin *FakeVolumePlugin) NewBuilder(spec *Spec, pod *api.Pod, opts VolumeOptions) (Builder, error) {
return &FakeVolume{pod.UID, spec.Name(), plugin}, nil
return &FakeVolume{pod.UID, spec.Name(), plugin, MetricsNil{}}, nil
}
func (plugin *FakeVolumePlugin) NewCleaner(volName string, podUID types.UID) (Cleaner, error) {
return &FakeVolume{podUID, volName, plugin}, nil
return &FakeVolume{podUID, volName, plugin, MetricsNil{}}, nil
}
func (plugin *FakeVolumePlugin) NewRecycler(spec *Spec) (Recycler, error) {
return &fakeRecycler{"/attributesTransferredFromSpec"}, nil
return &fakeRecycler{"/attributesTransferredFromSpec", MetricsNil{}}, nil
}
func (plugin *FakeVolumePlugin) NewDeleter(spec *Spec) (Deleter, error) {
return &FakeDeleter{"/attributesTransferredFromSpec"}, nil
return &FakeDeleter{"/attributesTransferredFromSpec", MetricsNil{}}, nil
}
func (plugin *FakeVolumePlugin) GetAccessModes() []api.PersistentVolumeAccessMode {
@ -159,6 +159,7 @@ type FakeVolume struct {
PodUID types.UID
VolName string
Plugin *FakeVolumePlugin
MetricsNil
}
func (_ *FakeVolume) GetAttributes() Attributes {
@ -192,6 +193,7 @@ func (fv *FakeVolume) TearDownAt(dir string) error {
type fakeRecycler struct {
path string
MetricsNil
}
func (fr *fakeRecycler) Recycle() error {
@ -214,6 +216,7 @@ func NewFakeRecycler(spec *Spec, host VolumeHost, config VolumeConfig) (Recycler
type FakeDeleter struct {
path string
MetricsNil
}
func (fd *FakeDeleter) Delete() error {

View File

@ -0,0 +1,41 @@
// +build linux
/*
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 util
import (
"syscall"
)
// FSInfo linux returns (available bytes, byte capacity, error) for the filesystem that
// path resides upon.
func FsInfo(path string) (int64, int64, error) {
statfs := &syscall.Statfs_t{}
err := syscall.Statfs(path, statfs)
if err != nil {
return 0, 0, err
}
// Available is blocks available * fragment size
available := int64(statfs.Bavail) * int64(statfs.Frsize)
// Capacity is total block count * fragment size
capacity := int64(statfs.Blocks) * int64(statfs.Frsize)
return available, capacity, nil
}

View File

@ -0,0 +1,28 @@
// +build !linux
/*
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 util
import (
"errors"
)
// FSInfo unsupported returns 0 values for available and capacity and an error.
func FsInfo(path string) (int64, int64, error) {
return 0, 0, errors.New("FsInfo not supported for this build.")
}

View File

@ -19,6 +19,7 @@ package volume
import (
"io/ioutil"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"os"
"path"
)
@ -28,6 +29,32 @@ import (
type Volume interface {
// GetPath returns the directory path the volume is mounted to.
GetPath() string
// MetricsProvider embeds methods for exposing metrics (e.g. used,available space).
MetricsProvider
}
// MetricsProvider exposes metrics (e.g. used,available space) related to a Volume.
type MetricsProvider interface {
// GetMetrics returns the Metrics for the Volume. Maybe expensive for some implementations.
GetMetrics() (*Metrics, error)
}
// Metrics represents the used and available bytes of the Volume.
type Metrics struct {
// Used represents the total bytes used by the Volume.
// Note: For block devices this maybe more than the total size of the files.
Used *resource.Quantity
// Capacity represents the total capacity (bytes) of the volume's underlying storage.
// For Volumes that share a filesystem with the host (e.g. emptydir, hostpath) this is the size
// of the underlying storage, and will not equal Used + Available as the fs is shared.
Capacity *resource.Quantity
// Available represents the storage space available (bytes) for the Volume.
// For Volumes that share a filesystem with the host (e.g. emptydir, hostpath), this is the available
// space on the underlying storage, and is shared with host processes and other Volumes.
Available *resource.Quantity
}
// Attributes represents the attributes of this builder.