mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 02:43:36 +00:00
vendor files
This commit is contained in:
68
vendor/k8s.io/kubernetes/pkg/volume/flocker/BUILD
generated
vendored
Normal file
68
vendor/k8s.io/kubernetes/pkg/volume/flocker/BUILD
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"flocker.go",
|
||||
"flocker_util.go",
|
||||
"flocker_volume.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/volume/flocker",
|
||||
deps = [
|
||||
"//pkg/util/env: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",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//vendor/github.com/clusterhq/flocker-go:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource: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/apimachinery/pkg/util/rand:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"flocker_test.go",
|
||||
"flocker_util_test.go",
|
||||
"flocker_volume_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/volume/flocker",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/testing:go_default_library",
|
||||
"//vendor/github.com/clusterhq/flocker-go:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1: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"],
|
||||
)
|
11
vendor/k8s.io/kubernetes/pkg/volume/flocker/OWNERS
generated
vendored
Normal file
11
vendor/k8s.io/kubernetes/pkg/volume/flocker/OWNERS
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
approvers:
|
||||
- rootfs
|
||||
- saad-ali
|
||||
reviewers:
|
||||
- agonzalezro
|
||||
- simonswine
|
||||
- saad-ali
|
||||
- jsafrane
|
||||
- rootfs
|
||||
- jingxu97
|
||||
- msau42
|
18
vendor/k8s.io/kubernetes/pkg/volume/flocker/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/volume/flocker/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2015 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 flocker contains the internal representation of Flocker volumes
|
||||
package flocker // import "k8s.io/kubernetes/pkg/volume/flocker"
|
465
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker.go
generated
vendored
Normal file
465
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker.go
generated
vendored
Normal file
@ -0,0 +1,465 @@
|
||||
/*
|
||||
Copyright 2015 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 flocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/env"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
|
||||
flockerapi "github.com/clusterhq/flocker-go"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
)
|
||||
|
||||
// This is the primary entrypoint for volume plugins.
|
||||
func ProbeVolumePlugins() []volume.VolumePlugin {
|
||||
return []volume.VolumePlugin{&flockerPlugin{nil}}
|
||||
}
|
||||
|
||||
type flockerPlugin struct {
|
||||
host volume.VolumeHost
|
||||
}
|
||||
|
||||
type flockerVolume struct {
|
||||
volName string
|
||||
podUID types.UID
|
||||
// dataset metadata name deprecated
|
||||
datasetName string
|
||||
// dataset uuid
|
||||
datasetUUID string
|
||||
//pod *v1.Pod
|
||||
flockerClient flockerapi.Clientable
|
||||
manager volumeManager
|
||||
plugin *flockerPlugin
|
||||
mounter mount.Interface
|
||||
volume.MetricsProvider
|
||||
}
|
||||
|
||||
var _ volume.VolumePlugin = &flockerPlugin{}
|
||||
var _ volume.PersistentVolumePlugin = &flockerPlugin{}
|
||||
var _ volume.DeletableVolumePlugin = &flockerPlugin{}
|
||||
var _ volume.ProvisionableVolumePlugin = &flockerPlugin{}
|
||||
|
||||
const (
|
||||
flockerPluginName = "kubernetes.io/flocker"
|
||||
|
||||
defaultHost = "localhost"
|
||||
defaultPort = 4523
|
||||
defaultCACertFile = "/etc/flocker/cluster.crt"
|
||||
defaultClientKeyFile = "/etc/flocker/apiuser.key"
|
||||
defaultClientCertFile = "/etc/flocker/apiuser.crt"
|
||||
defaultMountPath = "/flocker"
|
||||
|
||||
timeoutWaitingForVolume = 2 * time.Minute
|
||||
tickerWaitingForVolume = 5 * time.Second
|
||||
)
|
||||
|
||||
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
|
||||
return host.GetPodVolumeDir(uid, strings.EscapeQualifiedNameForDisk(flockerPluginName), volName)
|
||||
}
|
||||
|
||||
func makeGlobalFlockerPath(datasetUUID string) string {
|
||||
return path.Join(defaultMountPath, datasetUUID)
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) Init(host volume.VolumeHost) error {
|
||||
p.host = host
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) GetPluginName() string {
|
||||
return flockerPluginName
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
||||
volumeSource, _, err := getVolumeSource(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return volumeSource.DatasetName, nil
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) CanSupport(spec *volume.Spec) bool {
|
||||
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Flocker != nil) ||
|
||||
(spec.Volume != nil && spec.Volume.Flocker != nil)
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) RequiresRemount() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) SupportsMountOption() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) SupportsBulkVolumeVerification() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
|
||||
return []v1.PersistentVolumeAccessMode{
|
||||
v1.ReadWriteOnce,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) getFlockerVolumeSource(spec *volume.Spec) (*v1.FlockerVolumeSource, bool) {
|
||||
// AFAIK this will always be r/w, but perhaps for the future it will be needed
|
||||
readOnly := false
|
||||
|
||||
if spec.Volume != nil && spec.Volume.Flocker != nil {
|
||||
return spec.Volume.Flocker, readOnly
|
||||
}
|
||||
return spec.PersistentVolume.Spec.Flocker, readOnly
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
|
||||
// Inject real implementations here, test through the internal function.
|
||||
return plugin.newMounterInternal(spec, pod.UID, &FlockerUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager volumeManager, mounter mount.Interface) (volume.Mounter, error) {
|
||||
volumeSource, readOnly, err := getVolumeSource(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
datasetName := volumeSource.DatasetName
|
||||
datasetUUID := volumeSource.DatasetUUID
|
||||
|
||||
return &flockerVolumeMounter{
|
||||
flockerVolume: &flockerVolume{
|
||||
podUID: podUID,
|
||||
volName: spec.Name(),
|
||||
datasetName: datasetName,
|
||||
datasetUUID: datasetUUID,
|
||||
mounter: mounter,
|
||||
manager: manager,
|
||||
plugin: plugin,
|
||||
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, spec.Name(), plugin.host)),
|
||||
},
|
||||
readOnly: readOnly}, nil
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
|
||||
// Inject real implementations here, test through the internal function.
|
||||
return p.newUnmounterInternal(volName, podUID, &FlockerUtil{}, p.host.GetMounter(p.GetPluginName()))
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) newUnmounterInternal(volName string, podUID types.UID, manager volumeManager, mounter mount.Interface) (volume.Unmounter, error) {
|
||||
return &flockerVolumeUnmounter{&flockerVolume{
|
||||
podUID: podUID,
|
||||
volName: volName,
|
||||
manager: manager,
|
||||
mounter: mounter,
|
||||
plugin: p,
|
||||
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, p.host)),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (p *flockerPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
|
||||
flockerVolume := &v1.Volume{
|
||||
Name: volumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{
|
||||
DatasetName: volumeName,
|
||||
},
|
||||
},
|
||||
}
|
||||
return volume.NewSpecFromVolume(flockerVolume), nil
|
||||
}
|
||||
|
||||
func (b *flockerVolume) GetDatasetUUID() (datasetUUID string, err error) {
|
||||
|
||||
// return UUID if set
|
||||
if len(b.datasetUUID) > 0 {
|
||||
return b.datasetUUID, nil
|
||||
}
|
||||
|
||||
if b.flockerClient == nil {
|
||||
return "", fmt.Errorf("Flocker client is not initialized")
|
||||
}
|
||||
|
||||
// lookup in flocker API otherwise
|
||||
return b.flockerClient.GetDatasetID(b.datasetName)
|
||||
}
|
||||
|
||||
type flockerVolumeMounter struct {
|
||||
*flockerVolume
|
||||
readOnly bool
|
||||
}
|
||||
|
||||
func (b *flockerVolumeMounter) GetAttributes() volume.Attributes {
|
||||
return volume.Attributes{
|
||||
ReadOnly: b.readOnly,
|
||||
Managed: false,
|
||||
SupportsSELinux: false,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (b *flockerVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *flockerVolumeMounter) GetPath() string {
|
||||
return getPath(b.podUID, b.volName, b.plugin.host)
|
||||
}
|
||||
|
||||
// SetUp bind mounts the disk global mount to the volume path.
|
||||
func (b *flockerVolumeMounter) SetUp(fsGroup *int64) error {
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
||||
// newFlockerClient uses environment variables and pod attributes to return a
|
||||
// flocker client capable of talking with the Flocker control service.
|
||||
func (p *flockerPlugin) newFlockerClient(hostIP string) (*flockerapi.Client, error) {
|
||||
host := env.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_HOST", defaultHost)
|
||||
port, err := env.GetEnvAsIntOrFallback("FLOCKER_CONTROL_SERVICE_PORT", defaultPort)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
caCertPath := env.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CA_FILE", defaultCACertFile)
|
||||
keyPath := env.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CLIENT_KEY_FILE", defaultClientKeyFile)
|
||||
certPath := env.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CLIENT_CERT_FILE", defaultClientCertFile)
|
||||
|
||||
c, err := flockerapi.NewClient(host, port, hostIP, caCertPath, keyPath, certPath)
|
||||
return c, err
|
||||
}
|
||||
|
||||
func (b *flockerVolumeMounter) newFlockerClient() (*flockerapi.Client, error) {
|
||||
|
||||
hostIP, err := b.plugin.host.GetHostIP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.plugin.newFlockerClient(hostIP.String())
|
||||
}
|
||||
|
||||
/*
|
||||
SetUpAt will setup a Flocker volume following this flow of calls to the Flocker
|
||||
control service:
|
||||
|
||||
1. Get the dataset id for the given volume name/dir
|
||||
2. It should already be there, if it's not the user needs to manually create it
|
||||
3. Check the current Primary UUID
|
||||
4. If it doesn't match with the Primary UUID that we got on 2, then we will
|
||||
need to update the Primary UUID for this volume.
|
||||
5. Wait until the Primary UUID was updated or timeout.
|
||||
*/
|
||||
func (b *flockerVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
||||
var err error
|
||||
if b.flockerClient == nil {
|
||||
b.flockerClient, err = b.newFlockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
datasetUUID, err := b.GetDatasetUUID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("The datasetUUID for volume with datasetName='%s' can not be found using flocker: %s", b.datasetName, err)
|
||||
}
|
||||
|
||||
datasetState, err := b.flockerClient.GetDatasetState(datasetUUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("The datasetState for volume with datasetUUID='%s' could not determinted uusing flocker: %s", datasetUUID, err)
|
||||
}
|
||||
|
||||
primaryUUID, err := b.flockerClient.GetPrimaryUUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if datasetState.Primary != primaryUUID {
|
||||
if err := b.updateDatasetPrimary(datasetUUID, primaryUUID); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := b.flockerClient.GetDatasetState(datasetUUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("The volume with datasetUUID='%s' migrated unsuccessfully.", datasetUUID)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle failed mounts here.
|
||||
notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
glog.V(4).Infof("flockerVolume set up: %s %v %v, datasetUUID %v readOnly %v", dir, !notMnt, err, datasetUUID, b.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 PD.
|
||||
options := []string{"bind"}
|
||||
if b.readOnly {
|
||||
options = append(options, "ro")
|
||||
}
|
||||
|
||||
globalFlockerPath := makeGlobalFlockerPath(datasetUUID)
|
||||
glog.V(4).Infof("attempting to mount %s", dir)
|
||||
|
||||
err = b.mounter.Mount(globalFlockerPath, dir, "", options)
|
||||
if err != nil {
|
||||
notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
if mntErr != nil {
|
||||
glog.Errorf("isLikelyNotMountPoint check failed: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
if !notMnt {
|
||||
if mntErr = b.mounter.Unmount(dir); mntErr != nil {
|
||||
glog.Errorf("failed to unmount: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
notMnt, mntErr := b.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 disk %s failed: %v", dir, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !b.readOnly {
|
||||
volume.SetVolumeOwnership(b, fsGroup)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("successfully mounted %s", dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateDatasetPrimary will update the primary in Flocker and wait for it to
|
||||
// be ready. If it never gets to ready state it will timeout and error.
|
||||
func (b *flockerVolumeMounter) updateDatasetPrimary(datasetUUID string, primaryUUID string) error {
|
||||
// We need to update the primary and wait for it to be ready
|
||||
_, err := b.flockerClient.UpdatePrimaryForDataset(primaryUUID, datasetUUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeoutChan := time.NewTimer(timeoutWaitingForVolume)
|
||||
defer timeoutChan.Stop()
|
||||
tickChan := time.NewTicker(tickerWaitingForVolume)
|
||||
defer tickChan.Stop()
|
||||
|
||||
for {
|
||||
if s, err := b.flockerClient.GetDatasetState(datasetUUID); err == nil && s.Primary == primaryUUID {
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case <-timeoutChan.C:
|
||||
return fmt.Errorf(
|
||||
"Timed out waiting for the datasetUUID: '%s' to be moved to the primary: '%s'\n%v",
|
||||
datasetUUID, primaryUUID, err,
|
||||
)
|
||||
case <-tickChan.C:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getVolumeSource(spec *volume.Spec) (*v1.FlockerVolumeSource, bool, error) {
|
||||
if spec.Volume != nil && spec.Volume.Flocker != nil {
|
||||
return spec.Volume.Flocker, spec.ReadOnly, nil
|
||||
} else if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.Flocker != nil {
|
||||
return spec.PersistentVolume.Spec.Flocker, spec.ReadOnly, nil
|
||||
}
|
||||
|
||||
return nil, false, fmt.Errorf("Spec does not reference a Flocker volume type")
|
||||
}
|
||||
|
||||
type flockerVolumeUnmounter struct {
|
||||
*flockerVolume
|
||||
}
|
||||
|
||||
var _ volume.Unmounter = &flockerVolumeUnmounter{}
|
||||
|
||||
func (c *flockerVolumeUnmounter) GetPath() string {
|
||||
return getPath(c.podUID, c.volName, c.plugin.host)
|
||||
}
|
||||
|
||||
// Unmounts the bind mount, and detaches the disk only if the PD
|
||||
// resource was the last reference to that disk on the kubelet.
|
||||
func (c *flockerVolumeUnmounter) TearDown() error {
|
||||
return c.TearDownAt(c.GetPath())
|
||||
}
|
||||
|
||||
// TearDownAt unmounts the bind mount
|
||||
func (c *flockerVolumeUnmounter) TearDownAt(dir string) error {
|
||||
return util.UnmountPath(dir, c.mounter)
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
|
||||
return plugin.newDeleterInternal(spec, &FlockerUtil{})
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) newDeleterInternal(spec *volume.Spec, manager volumeManager) (volume.Deleter, error) {
|
||||
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Flocker == nil {
|
||||
return nil, fmt.Errorf("spec.PersistentVolumeSource.Flocker is nil")
|
||||
}
|
||||
return &flockerVolumeDeleter{
|
||||
flockerVolume: &flockerVolume{
|
||||
volName: spec.Name(),
|
||||
datasetName: spec.PersistentVolume.Spec.Flocker.DatasetName,
|
||||
datasetUUID: spec.PersistentVolume.Spec.Flocker.DatasetUUID,
|
||||
manager: manager,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
|
||||
return plugin.newProvisionerInternal(options, &FlockerUtil{})
|
||||
}
|
||||
|
||||
func (plugin *flockerPlugin) newProvisionerInternal(options volume.VolumeOptions, manager volumeManager) (volume.Provisioner, error) {
|
||||
return &flockerVolumeProvisioner{
|
||||
flockerVolume: &flockerVolume{
|
||||
manager: manager,
|
||||
plugin: plugin,
|
||||
},
|
||||
options: options,
|
||||
}, nil
|
||||
}
|
363
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_test.go
generated
vendored
Normal file
363
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_test.go
generated
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
/*
|
||||
Copyright 2015 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 flocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
|
||||
flockerapi "github.com/clusterhq/flocker-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const pluginName = "kubernetes.io/flocker"
|
||||
const datasetOneID = "11111111-1111-1111-1111-111111111100"
|
||||
const nodeOneID = "11111111-1111-1111-1111-111111111111"
|
||||
const nodeTwoID = "22222222-2222-2222-2222-222222222222"
|
||||
|
||||
var _ flockerapi.Clientable = &fakeFlockerClient{}
|
||||
|
||||
type fakeFlockerClient struct {
|
||||
DatasetID string
|
||||
Primary string
|
||||
Deleted bool
|
||||
Metadata map[string]string
|
||||
Nodes []flockerapi.NodeState
|
||||
Error error
|
||||
}
|
||||
|
||||
func newFakeFlockerClient() *fakeFlockerClient {
|
||||
return &fakeFlockerClient{
|
||||
DatasetID: datasetOneID,
|
||||
Primary: nodeOneID,
|
||||
Deleted: false,
|
||||
Metadata: map[string]string{"Name": "dataset-one"},
|
||||
Nodes: []flockerapi.NodeState{
|
||||
{
|
||||
Host: "1.2.3.4",
|
||||
UUID: nodeOneID,
|
||||
},
|
||||
{
|
||||
Host: "4.5.6.7",
|
||||
UUID: nodeTwoID,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeFlockerClient) CreateDataset(options *flockerapi.CreateDatasetOptions) (*flockerapi.DatasetState, error) {
|
||||
|
||||
if c.Error != nil {
|
||||
return nil, c.Error
|
||||
}
|
||||
|
||||
return &flockerapi.DatasetState{
|
||||
DatasetID: c.DatasetID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *fakeFlockerClient) DeleteDataset(datasetID string) error {
|
||||
c.DatasetID = datasetID
|
||||
c.Deleted = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeFlockerClient) GetDatasetState(datasetID string) (*flockerapi.DatasetState, error) {
|
||||
return &flockerapi.DatasetState{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeFlockerClient) GetDatasetID(metaName string) (datasetID string, err error) {
|
||||
if val, ok := c.Metadata["Name"]; !ok {
|
||||
return val, nil
|
||||
}
|
||||
return "", fmt.Errorf("No dataset with metadata X found")
|
||||
}
|
||||
|
||||
func (c *fakeFlockerClient) GetPrimaryUUID() (primaryUUID string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *fakeFlockerClient) ListNodes() (nodes []flockerapi.NodeState, err error) {
|
||||
return c.Nodes, nil
|
||||
}
|
||||
|
||||
func (c *fakeFlockerClient) UpdatePrimaryForDataset(primaryUUID, datasetID string) (*flockerapi.DatasetState, error) {
|
||||
return &flockerapi.DatasetState{}, nil
|
||||
}
|
||||
|
||||
type fakeFlockerUtil struct {
|
||||
}
|
||||
|
||||
func (fake *fakeFlockerUtil) CreateVolume(c *flockerVolumeProvisioner) (datasetUUID string, volumeSizeGB int, labels map[string]string, err error) {
|
||||
labels = make(map[string]string)
|
||||
labels["fakeflockerutil"] = "yes"
|
||||
return "test-flocker-volume-uuid", 3, labels, nil
|
||||
}
|
||||
|
||||
func (fake *fakeFlockerUtil) DeleteVolume(cd *flockerVolumeDeleter) error {
|
||||
if cd.datasetUUID != "test-flocker-volume-uuid" {
|
||||
return fmt.Errorf("Deleter got unexpected datasetUUID: %s", cd.datasetUUID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newInitializedVolumePlugMgr(t *testing.T) (*volume.VolumePluginMgr, string) {
|
||||
plugMgr := &volume.VolumePluginMgr{}
|
||||
dir, err := utiltesting.MkTmpdir("flocker")
|
||||
assert.NoError(t, err)
|
||||
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(dir, nil, nil))
|
||||
return plugMgr, dir
|
||||
}
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("flockerTest")
|
||||
if err != nil {
|
||||
t.Fatalf("can't make a temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
plugMgr := volume.VolumePluginMgr{}
|
||||
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
|
||||
|
||||
plug, err := plugMgr.FindPluginByName("kubernetes.io/flocker")
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
spec := &v1.Volume{
|
||||
Name: "vol1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{
|
||||
DatasetUUID: "uuid1",
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeManager := &fakeFlockerUtil{}
|
||||
fakeMounter := &mount.FakeMounter{}
|
||||
mounter, err := plug.(*flockerPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), fakeManager, fakeMounter)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Errorf("Got a nil Mounter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetByName(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
plugMgr, dir := newInitializedVolumePlugMgr(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(pluginName)
|
||||
assert.NotNil(plug, "Can't find the plugin by name")
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestCanSupport(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
plugMgr, dir := newInitializedVolumePlugMgr(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(pluginName)
|
||||
assert.NoError(err)
|
||||
|
||||
specs := map[*volume.Spec]bool{
|
||||
{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{},
|
||||
},
|
||||
},
|
||||
}: true,
|
||||
{
|
||||
PersistentVolume: &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}: true,
|
||||
{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{},
|
||||
},
|
||||
}: false,
|
||||
}
|
||||
|
||||
for spec, expected := range specs {
|
||||
actual := plug.CanSupport(spec)
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFlockerVolumeSource(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
p := flockerPlugin{}
|
||||
|
||||
spec := &volume.Spec{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{},
|
||||
},
|
||||
},
|
||||
}
|
||||
vs, ro := p.getFlockerVolumeSource(spec)
|
||||
assert.False(ro)
|
||||
assert.Equal(spec.Volume.Flocker, vs)
|
||||
|
||||
spec = &volume.Spec{
|
||||
PersistentVolume: &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
vs, ro = p.getFlockerVolumeSource(spec)
|
||||
assert.False(ro)
|
||||
assert.Equal(spec.PersistentVolume.Spec.Flocker, vs)
|
||||
}
|
||||
|
||||
func TestNewMounterDatasetName(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
plugMgr, dir := newInitializedVolumePlugMgr(t)
|
||||
defer os.RemoveAll(dir)
|
||||
plug, err := plugMgr.FindPluginByName(pluginName)
|
||||
assert.NoError(err)
|
||||
|
||||
spec := &volume.Spec{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{
|
||||
DatasetName: "something",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = plug.NewMounter(spec, &v1.Pod{}, volume.VolumeOptions{})
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestNewMounterDatasetUUID(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
plugMgr, dir := newInitializedVolumePlugMgr(t)
|
||||
defer os.RemoveAll(dir)
|
||||
plug, err := plugMgr.FindPluginByName(pluginName)
|
||||
assert.NoError(err)
|
||||
|
||||
spec := &volume.Spec{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{
|
||||
DatasetUUID: "uuid1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mounter, err := plug.NewMounter(spec, &v1.Pod{}, volume.VolumeOptions{})
|
||||
assert.NoError(err)
|
||||
assert.NotNil(mounter, "got a nil mounter")
|
||||
|
||||
}
|
||||
|
||||
func TestNewUnmounter(t *testing.T) {
|
||||
t.Skip("broken")
|
||||
assert := assert.New(t)
|
||||
|
||||
p := flockerPlugin{}
|
||||
|
||||
unmounter, err := p.NewUnmounter("", types.UID(""))
|
||||
assert.Nil(unmounter)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestIsReadOnly(t *testing.T) {
|
||||
b := &flockerVolumeMounter{readOnly: true}
|
||||
assert.True(t, b.GetAttributes().ReadOnly)
|
||||
}
|
||||
|
||||
type mockFlockerClient struct {
|
||||
datasetID, primaryUUID, path string
|
||||
datasetState *flockerapi.DatasetState
|
||||
}
|
||||
|
||||
func newMockFlockerClient(mockDatasetID, mockPrimaryUUID, mockPath string) *mockFlockerClient {
|
||||
return &mockFlockerClient{
|
||||
datasetID: mockDatasetID,
|
||||
primaryUUID: mockPrimaryUUID,
|
||||
path: mockPath,
|
||||
datasetState: &flockerapi.DatasetState{
|
||||
Path: mockPath,
|
||||
DatasetID: mockDatasetID,
|
||||
Primary: mockPrimaryUUID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m mockFlockerClient) CreateDataset(metaName string) (*flockerapi.DatasetState, error) {
|
||||
return m.datasetState, nil
|
||||
}
|
||||
func (m mockFlockerClient) GetDatasetState(datasetID string) (*flockerapi.DatasetState, error) {
|
||||
return m.datasetState, nil
|
||||
}
|
||||
func (m mockFlockerClient) GetDatasetID(metaName string) (string, error) {
|
||||
return m.datasetID, nil
|
||||
}
|
||||
func (m mockFlockerClient) GetPrimaryUUID() (string, error) {
|
||||
return m.primaryUUID, nil
|
||||
}
|
||||
func (m mockFlockerClient) UpdatePrimaryForDataset(primaryUUID, datasetID string) (*flockerapi.DatasetState, error) {
|
||||
return m.datasetState, nil
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: reenable after refactor
|
||||
func TestSetUpAtInternal(t *testing.T) {
|
||||
const dir = "dir"
|
||||
mockPath := "expected-to-be-set-properly" // package var
|
||||
expectedPath := mockPath
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
plugMgr, rootDir := newInitializedVolumePlugMgr(t)
|
||||
if rootDir != "" {
|
||||
defer os.RemoveAll(rootDir)
|
||||
}
|
||||
plug, err := plugMgr.FindPluginByName(flockerPluginName)
|
||||
assert.NoError(err)
|
||||
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
|
||||
b := flockerVolumeMounter{flockerVolume: &flockerVolume{pod: pod, plugin: plug.(*flockerPlugin)}}
|
||||
b.client = newMockFlockerClient("dataset-id", "primary-uid", mockPath)
|
||||
|
||||
assert.NoError(b.SetUpAt(dir, nil))
|
||||
assert.Equal(expectedPath, b.flocker.path)
|
||||
}
|
||||
*/
|
96
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_util.go
generated
vendored
Normal file
96
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_util.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
Copyright 2016 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 flocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
|
||||
flockerapi "github.com/clusterhq/flocker-go"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type FlockerUtil struct{}
|
||||
|
||||
func (util *FlockerUtil) DeleteVolume(d *flockerVolumeDeleter) error {
|
||||
var err error
|
||||
|
||||
if d.flockerClient == nil {
|
||||
d.flockerClient, err = d.plugin.newFlockerClient("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
datasetUUID, err := d.GetDatasetUUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.flockerClient.DeleteDataset(datasetUUID)
|
||||
}
|
||||
|
||||
func (util *FlockerUtil) CreateVolume(c *flockerVolumeProvisioner) (datasetUUID string, volumeSizeGB int, labels map[string]string, err error) {
|
||||
|
||||
if c.flockerClient == nil {
|
||||
c.flockerClient, err = c.plugin.newFlockerClient("")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nodes, err := c.flockerClient.ListNodes()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(nodes) < 1 {
|
||||
err = fmt.Errorf("no nodes found inside the flocker cluster to provision a dataset")
|
||||
return
|
||||
}
|
||||
|
||||
// select random node
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
node := nodes[rand.Intn(len(nodes))]
|
||||
glog.V(2).Infof("selected flocker node with UUID '%s' to provision dataset", node.UUID)
|
||||
|
||||
capacity := c.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
||||
requestBytes := capacity.Value()
|
||||
volumeSizeGB = int(volume.RoundUpSize(requestBytes, 1024*1024*1024))
|
||||
|
||||
createOptions := &flockerapi.CreateDatasetOptions{
|
||||
MaximumSize: requestBytes,
|
||||
Metadata: map[string]string{
|
||||
"type": "k8s-dynamic-prov",
|
||||
"pvc": c.options.PVC.Name,
|
||||
},
|
||||
Primary: node.UUID,
|
||||
}
|
||||
|
||||
datasetState, err := c.flockerClient.CreateDataset(createOptions)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
datasetUUID = datasetState.DatasetID
|
||||
|
||||
glog.V(2).Infof("successfully created Flocker dataset with UUID '%s'", datasetUUID)
|
||||
|
||||
return
|
||||
}
|
58
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_util_test.go
generated
vendored
Normal file
58
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_util_test.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2015 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 flocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFlockerUtil_CreateVolume(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// test CreateVolume happy path
|
||||
pvc := volumetest.CreateTestPVC("3Gi", []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce})
|
||||
options := volume.VolumeOptions{
|
||||
PVC: pvc,
|
||||
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
|
||||
fakeFlockerClient := newFakeFlockerClient()
|
||||
dir, p := newTestableProvisioner(assert, options)
|
||||
provisioner := p.(*flockerVolumeProvisioner)
|
||||
defer os.RemoveAll(dir)
|
||||
provisioner.flockerClient = fakeFlockerClient
|
||||
|
||||
flockerUtil := &FlockerUtil{}
|
||||
|
||||
datasetID, size, _, err := flockerUtil.CreateVolume(provisioner)
|
||||
assert.NoError(err)
|
||||
assert.Equal(datasetOneID, datasetID)
|
||||
assert.Equal(3, size)
|
||||
|
||||
// test error during CreateVolume
|
||||
fakeFlockerClient.Error = fmt.Errorf("Do not feel like provisioning")
|
||||
_, _, _, err = flockerUtil.CreateVolume(provisioner)
|
||||
assert.Equal(fakeFlockerClient.Error.Error(), err.Error())
|
||||
}
|
110
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_volume.go
generated
vendored
Normal file
110
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_volume.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2015 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 flocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
)
|
||||
|
||||
type volumeManager interface {
|
||||
// Creates a volume
|
||||
CreateVolume(provisioner *flockerVolumeProvisioner) (datasetUUID string, volumeSizeGB int, labels map[string]string, err error)
|
||||
// Deletes a volume
|
||||
DeleteVolume(deleter *flockerVolumeDeleter) error
|
||||
}
|
||||
|
||||
type flockerVolumeDeleter struct {
|
||||
*flockerVolume
|
||||
}
|
||||
|
||||
var _ volume.Deleter = &flockerVolumeDeleter{}
|
||||
|
||||
func (b *flockerVolumeDeleter) GetPath() string {
|
||||
return getPath(b.podUID, b.volName, b.plugin.host)
|
||||
}
|
||||
|
||||
func (d *flockerVolumeDeleter) Delete() error {
|
||||
return d.manager.DeleteVolume(d)
|
||||
}
|
||||
|
||||
type flockerVolumeProvisioner struct {
|
||||
*flockerVolume
|
||||
options volume.VolumeOptions
|
||||
}
|
||||
|
||||
var _ volume.Provisioner = &flockerVolumeProvisioner{}
|
||||
|
||||
func (c *flockerVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
||||
if !volume.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) {
|
||||
return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes())
|
||||
}
|
||||
|
||||
if len(c.options.Parameters) > 0 {
|
||||
return nil, fmt.Errorf("Provisioning failed: Specified at least one unsupported parameter")
|
||||
}
|
||||
|
||||
if c.options.PVC.Spec.Selector != nil {
|
||||
return nil, fmt.Errorf("Provisioning failed: Specified unsupported selector")
|
||||
}
|
||||
|
||||
datasetUUID, sizeGB, labels, err := c.manager.CreateVolume(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pv := &v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: c.options.PVName,
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{
|
||||
volumehelper.VolumeDynamicallyCreatedByKey: "flocker-dynamic-provisioner",
|
||||
},
|
||||
},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeReclaimPolicy: c.options.PersistentVolumeReclaimPolicy,
|
||||
AccessModes: c.options.PVC.Spec.AccessModes,
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
|
||||
},
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
Flocker: &v1.FlockerVolumeSource{
|
||||
DatasetUUID: datasetUUID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(c.options.PVC.Spec.AccessModes) == 0 {
|
||||
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
||||
}
|
||||
|
||||
if len(labels) != 0 {
|
||||
if pv.Labels == nil {
|
||||
pv.Labels = make(map[string]string)
|
||||
}
|
||||
for k, v := range labels {
|
||||
pv.Labels[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return pv, nil
|
||||
}
|
102
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_volume_test.go
generated
vendored
Normal file
102
vendor/k8s.io/kubernetes/pkg/volume/flocker/flocker_volume_test.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2015 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 flocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func newTestableProvisioner(assert *assert.Assertions, options volume.VolumeOptions) (string, volume.Provisioner) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("flockervolumeTest")
|
||||
assert.NoError(err, fmt.Sprintf("can't make a temp dir: %v", err))
|
||||
|
||||
plugMgr := volume.VolumePluginMgr{}
|
||||
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(pluginName)
|
||||
assert.NoError(err, "Can't find the plugin by name")
|
||||
|
||||
provisioner, err := plug.(*flockerPlugin).newProvisionerInternal(options, &fakeFlockerUtil{})
|
||||
assert.NoError(err, fmt.Sprintf("Can't create new provisioner:%v", err))
|
||||
return tmpDir, provisioner
|
||||
}
|
||||
|
||||
func TestProvision(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
pvc := volumetest.CreateTestPVC("3Gi", []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce})
|
||||
options := volume.VolumeOptions{
|
||||
PVC: pvc,
|
||||
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
|
||||
dir, provisioner := newTestableProvisioner(assert, options)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
persistentSpec, err := provisioner.Provision()
|
||||
assert.NoError(err, "Provision() failed: ", err)
|
||||
|
||||
cap := persistentSpec.Spec.Capacity[v1.ResourceStorage]
|
||||
|
||||
assert.Equal(int64(3*1024*1024*1024), cap.Value())
|
||||
|
||||
assert.Equal(
|
||||
"test-flocker-volume-uuid",
|
||||
persistentSpec.Spec.PersistentVolumeSource.Flocker.DatasetUUID,
|
||||
)
|
||||
|
||||
assert.Equal(
|
||||
map[string]string{"fakeflockerutil": "yes"},
|
||||
persistentSpec.Labels,
|
||||
)
|
||||
|
||||
// parameters are not supported
|
||||
options = volume.VolumeOptions{
|
||||
PVC: pvc,
|
||||
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
|
||||
Parameters: map[string]string{
|
||||
"not-supported-params": "test123",
|
||||
},
|
||||
}
|
||||
|
||||
dir, provisioner = newTestableProvisioner(assert, options)
|
||||
defer os.RemoveAll(dir)
|
||||
persistentSpec, err = provisioner.Provision()
|
||||
assert.Error(err, "Provision() did not fail with Parameters specified")
|
||||
|
||||
// selectors are not supported
|
||||
pvc.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"key": "value"}}
|
||||
options = volume.VolumeOptions{
|
||||
PVC: pvc,
|
||||
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
|
||||
dir, provisioner = newTestableProvisioner(assert, options)
|
||||
defer os.RemoveAll(dir)
|
||||
persistentSpec, err = provisioner.Provision()
|
||||
assert.Error(err, "Provision() did not fail with Selector specified")
|
||||
}
|
Reference in New Issue
Block a user