vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

64
vendor/k8s.io/kubernetes/pkg/volume/azure_file/BUILD generated vendored Normal file
View File

@ -0,0 +1,64 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"azure_file.go",
"azure_provision.go",
"azure_util.go",
"doc.go",
],
importpath = "k8s.io/kubernetes/pkg/volume/azure_file",
deps = [
"//pkg/cloudprovider:go_default_library",
"//pkg/cloudprovider/providers/azure: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/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors: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",
],
)
go_test(
name = "go_default_test",
srcs = ["azure_file_test.go"],
importpath = "k8s.io/kubernetes/pkg/volume/azure_file",
library = ":go_default_library",
deps = [
"//pkg/cloudprovider/providers/azure:go_default_library",
"//pkg/cloudprovider/providers/fake:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/testing: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/kubernetes/fake: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/azure_file/OWNERS generated vendored Normal file
View File

@ -0,0 +1,11 @@
approvers:
- brendandburns
- rootfs
reviewers:
- rootfs
- brendandburns
- saad-ali
- jsafrane
- jingxu97
- msau42
- andyzhangx

View File

@ -0,0 +1,332 @@
/*
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 azure_file
import (
"fmt"
"os"
"runtime"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
kstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
"k8s.io/kubernetes/pkg/volume/util"
)
// ProbeVolumePlugins is the primary endpoint for volume plugins
func ProbeVolumePlugins() []volume.VolumePlugin {
return []volume.VolumePlugin{&azureFilePlugin{nil}}
}
type azureFilePlugin struct {
host volume.VolumeHost
}
var _ volume.VolumePlugin = &azureFilePlugin{}
var _ volume.PersistentVolumePlugin = &azureFilePlugin{}
const (
azureFilePluginName = "kubernetes.io/azure-file"
)
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
return host.GetPodVolumeDir(uid, kstrings.EscapeQualifiedNameForDisk(azureFilePluginName), volName)
}
func (plugin *azureFilePlugin) Init(host volume.VolumeHost) error {
plugin.host = host
return nil
}
func (plugin *azureFilePlugin) GetPluginName() string {
return azureFilePluginName
}
func (plugin *azureFilePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
share, _, err := getVolumeSource(spec)
if err != nil {
return "", err
}
return share, nil
}
func (plugin *azureFilePlugin) CanSupport(spec *volume.Spec) bool {
//TODO: check if mount.cifs is there
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureFile != nil) ||
(spec.Volume != nil && spec.Volume.AzureFile != nil)
}
func (plugin *azureFilePlugin) RequiresRemount() bool {
return false
}
func (plugin *azureFilePlugin) SupportsMountOption() bool {
return true
}
func (plugin *azureFilePlugin) SupportsBulkVolumeVerification() bool {
return false
}
func (plugin *azureFilePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
return []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
v1.ReadOnlyMany,
v1.ReadWriteMany,
}
}
func (plugin *azureFilePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
return plugin.newMounterInternal(spec, pod, &azureSvc{}, plugin.host.GetMounter(plugin.GetPluginName()))
}
func (plugin *azureFilePlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, util azureUtil, mounter mount.Interface) (volume.Mounter, error) {
share, readOnly, err := getVolumeSource(spec)
if err != nil {
return nil, err
}
secretName, secretNamespace, err := getSecretNameAndNamespace(spec, pod.Namespace)
return &azureFileMounter{
azureFile: &azureFile{
volName: spec.Name(),
mounter: mounter,
pod: pod,
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, spec.Name(), plugin.host)),
},
util: util,
secretNamespace: secretNamespace,
secretName: secretName,
shareName: share,
readOnly: readOnly,
mountOptions: volume.MountOptionFromSpec(spec),
}, nil
}
func (plugin *azureFilePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()))
}
func (plugin *azureFilePlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Unmounter, error) {
return &azureFileUnmounter{&azureFile{
volName: volName,
mounter: mounter,
pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: podUID}},
plugin: plugin,
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
}}, nil
}
func (plugin *azureFilePlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
azureVolume := &v1.Volume{
Name: volName,
VolumeSource: v1.VolumeSource{
AzureFile: &v1.AzureFileVolumeSource{
SecretName: volName,
ShareName: volName,
},
},
}
return volume.NewSpecFromVolume(azureVolume), nil
}
// azureFile volumes represent mount of an AzureFile share.
type azureFile struct {
volName string
podUID types.UID
pod *v1.Pod
mounter mount.Interface
plugin *azureFilePlugin
volume.MetricsProvider
}
func (azureFileVolume *azureFile) GetPath() string {
return getPath(azureFileVolume.pod.UID, azureFileVolume.volName, azureFileVolume.plugin.host)
}
type azureFileMounter struct {
*azureFile
util azureUtil
secretName string
secretNamespace string
shareName string
readOnly bool
mountOptions []string
}
var _ volume.Mounter = &azureFileMounter{}
func (b *azureFileMounter) GetAttributes() volume.Attributes {
return volume.Attributes{
ReadOnly: b.readOnly,
Managed: !b.readOnly,
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 *azureFileMounter) CanMount() error {
return nil
}
// SetUp attaches the disk and bind mounts to the volume path.
func (b *azureFileMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup)
}
func (b *azureFileMounter) SetUpAt(dir string, fsGroup *int64) error {
notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
glog.V(4).Infof("AzureFile mount set up: %s %v %v", dir, !notMnt, err)
if err != nil && !os.IsNotExist(err) {
return err
}
if !notMnt {
return nil
}
var accountKey, accountName string
if accountName, accountKey, err = b.util.GetAzureCredentials(b.plugin.host, b.secretNamespace, b.secretName); err != nil {
return err
}
mountOptions := []string{}
source := ""
osSeparator := string(os.PathSeparator)
source = fmt.Sprintf("%s%s%s.file.%s%s%s", osSeparator, osSeparator, accountName, getStorageEndpointSuffix(b.plugin.host.GetCloudProvider()), osSeparator, b.shareName)
if runtime.GOOS == "windows" {
mountOptions = []string{fmt.Sprintf("AZURE\\%s", accountName), accountKey}
} else {
os.MkdirAll(dir, 0700)
// parameters suggested by https://azure.microsoft.com/en-us/documentation/articles/storage-how-to-use-files-linux/
options := []string{fmt.Sprintf("username=%s,password=%s", accountName, accountKey)}
if b.readOnly {
options = append(options, "ro")
}
mountOptions = volume.JoinMountOptions(b.mountOptions, options)
mountOptions = appendDefaultMountOptions(mountOptions)
}
err = b.mounter.Mount(source, dir, "cifs", mountOptions)
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)
return err
}
return nil
}
var _ volume.Unmounter = &azureFileUnmounter{}
type azureFileUnmounter struct {
*azureFile
}
func (c *azureFileUnmounter) TearDown() error {
return c.TearDownAt(c.GetPath())
}
func (c *azureFileUnmounter) TearDownAt(dir string) error {
return util.UnmountPath(dir, c.mounter)
}
func getVolumeSource(spec *volume.Spec) (string, bool, error) {
if spec.Volume != nil && spec.Volume.AzureFile != nil {
share := spec.Volume.AzureFile.ShareName
readOnly := spec.Volume.AzureFile.ReadOnly
return share, readOnly, nil
} else if spec.PersistentVolume != nil &&
spec.PersistentVolume.Spec.AzureFile != nil {
share := spec.PersistentVolume.Spec.AzureFile.ShareName
readOnly := spec.ReadOnly
return share, readOnly, nil
}
return "", false, fmt.Errorf("Spec does not reference an AzureFile volume type")
}
func getSecretNameAndNamespace(spec *volume.Spec, defaultNamespace string) (string, string, error) {
secretName := ""
secretNamespace := ""
if spec.Volume != nil && spec.Volume.AzureFile != nil {
secretName = spec.Volume.AzureFile.SecretName
secretNamespace = defaultNamespace
} else if spec.PersistentVolume != nil &&
spec.PersistentVolume.Spec.AzureFile != nil {
secretNamespace = defaultNamespace
if spec.PersistentVolume.Spec.AzureFile.SecretNamespace != nil {
secretNamespace = *spec.PersistentVolume.Spec.AzureFile.SecretNamespace
}
secretName = spec.PersistentVolume.Spec.AzureFile.SecretName
} else {
return "", "", fmt.Errorf("Spec does not reference an AzureFile volume type")
}
if len(secretNamespace) == 0 {
return "", "", fmt.Errorf("invalid Azure volume: nil namespace")
}
return secretName, secretNamespace, nil
}
func getAzureCloud(cloudProvider cloudprovider.Interface) (*azure.Cloud, error) {
azure, ok := cloudProvider.(*azure.Cloud)
if !ok || azure == nil {
return nil, fmt.Errorf("Failed to get Azure Cloud Provider. GetCloudProvider returned %v instead", cloudProvider)
}
return azure, nil
}
func getStorageEndpointSuffix(cloudprovider cloudprovider.Interface) string {
const publicCloudStorageEndpointSuffix = "core.windows.net"
azure, err := getAzureCloud(cloudprovider)
if err != nil {
glog.Warningf("No Azure cloud provider found. Using the Azure public cloud endpoint: %s", publicCloudStorageEndpointSuffix)
return publicCloudStorageEndpointSuffix
}
return azure.Environment.StorageEndpointSuffix
}

View File

@ -0,0 +1,397 @@
/*
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 azure_file
import (
"fmt"
"io/ioutil"
"os"
"path"
"reflect"
"strings"
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
)
func TestCanSupport(t *testing.T) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "azureFileTest")
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/azure-file")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
if plug.GetPluginName() != "kubernetes.io/azure-file" {
t.Errorf("Wrong name: %s", plug.GetPluginName())
}
if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{AzureFile: &v1.AzureFileVolumeSource{}}}}) {
t.Errorf("Expected true")
}
if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{AzureFile: &v1.AzureFilePersistentVolumeSource{}}}}}) {
t.Errorf("Expected true")
}
}
func TestGetAccessModes(t *testing.T) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "azureFileTest")
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.FindPersistentPluginByName("kubernetes.io/azure-file")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
if !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteOnce) || !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadOnlyMany) || !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteMany) {
t.Errorf("Expected three AccessModeTypes: %s, %s, and %s", v1.ReadWriteOnce, v1.ReadOnlyMany, v1.ReadWriteMany)
}
}
func getAzureTestCloud(t *testing.T) *azure.Cloud {
config := `{
"aadClientId": "--aad-client-id--",
"aadClientSecret": "--aad-client-secret--"
}`
configReader := strings.NewReader(config)
cloud, err := azure.NewCloud(configReader)
if err != nil {
t.Error(err)
}
azureCloud, ok := cloud.(*azure.Cloud)
if !ok {
t.Error("NewCloud returned incorrect type")
}
return azureCloud
}
func getTestTempDir(t *testing.T) string {
tmpDir, err := ioutil.TempDir(os.TempDir(), "azurefileTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
return tmpDir
}
func TestPluginAzureCloudProvider(t *testing.T) {
tmpDir := getTestTempDir(t)
defer os.RemoveAll(tmpDir)
testPlugin(t, tmpDir, volumetest.NewFakeVolumeHostWithCloudProvider(tmpDir, nil, nil, getAzureTestCloud(t)))
}
func TestPluginWithoutCloudProvider(t *testing.T) {
tmpDir := getTestTempDir(t)
defer os.RemoveAll(tmpDir)
testPlugin(t, tmpDir, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
}
func TestPluginWithOtherCloudProvider(t *testing.T) {
tmpDir := getTestTempDir(t)
defer os.RemoveAll(tmpDir)
cloud := &fakecloud.FakeCloud{}
testPlugin(t, tmpDir, volumetest.NewFakeVolumeHostWithCloudProvider(tmpDir, nil, nil, cloud))
}
func testPlugin(t *testing.T, tmpDir string, volumeHost volume.VolumeHost) {
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumeHost)
plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
spec := &v1.Volume{
Name: "vol1",
VolumeSource: v1.VolumeSource{
AzureFile: &v1.AzureFileVolumeSource{
SecretName: "secret",
ShareName: "share",
},
},
}
fake := &mount.FakeMounter{}
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
mounter, err := plug.(*azureFilePlugin).newMounterInternal(volume.NewSpecFromVolume(spec), pod, &fakeAzureSvc{}, fake)
if err != nil {
t.Errorf("Failed to make a new Mounter: %v", err)
}
if mounter == nil {
t.Errorf("Got a nil Mounter")
}
volPath := path.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~azure-file/vol1")
path := mounter.GetPath()
if path != volPath {
t.Errorf("Got unexpected path: %s", path)
}
if err := mounter.SetUp(nil); err != nil {
t.Errorf("Expected success, got: %v", err)
}
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
t.Errorf("SetUp() failed, volume path not created: %s", path)
} else {
t.Errorf("SetUp() failed: %v", err)
}
}
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
t.Errorf("SetUp() failed, volume path not created: %s", path)
} else {
t.Errorf("SetUp() failed: %v", err)
}
}
unmounter, err := plug.(*azureFilePlugin).newUnmounterInternal("vol1", types.UID("poduid"), &mount.FakeMounter{})
if err != nil {
t.Errorf("Failed to make a new Unmounter: %v", err)
}
if unmounter == nil {
t.Errorf("Got a nil Unmounter")
}
if err := unmounter.TearDown(); err != nil {
t.Errorf("Expected success, got: %v", err)
}
if _, err := os.Stat(path); err == nil {
t.Errorf("TearDown() failed, volume path still exists: %s", path)
} else if !os.IsNotExist(err) {
t.Errorf("TearDown() failed: %v", err)
}
}
func TestPersistentClaimReadOnlyFlag(t *testing.T) {
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvA",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFilePersistentVolumeSource{},
},
ClaimRef: &v1.ObjectReference{
Name: "claimA",
},
},
}
claim := &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "claimA",
Namespace: "nsA",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "pvA",
},
Status: v1.PersistentVolumeClaimStatus{
Phase: v1.ClaimBound,
},
}
client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost("/tmp/fake", client, nil))
plug, _ := plugMgr.FindPluginByName(azureFilePluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
spec := volume.NewSpecFromPersistentVolume(pv, true)
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{})
if mounter == nil {
t.Fatalf("Got a nil Mounter")
}
if !mounter.GetAttributes().ReadOnly {
t.Errorf("Expected true for mounter.IsReadOnly")
}
}
type fakeAzureSvc struct{}
func (s *fakeAzureSvc) GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName string) (string, string, error) {
return "name", "key", nil
}
func (s *fakeAzureSvc) SetAzureCredentials(host volume.VolumeHost, nameSpace, accountName, accountKey string) (string, error) {
return "secret", nil
}
func TestMounterAndUnmounterTypeAssert(t *testing.T) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "azurefileTest")
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/azure-file")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
spec := &v1.Volume{
Name: "vol1",
VolumeSource: v1.VolumeSource{
AzureFile: &v1.AzureFileVolumeSource{
SecretName: "secret",
ShareName: "share",
},
},
}
fake := &mount.FakeMounter{}
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
mounter, err := plug.(*azureFilePlugin).newMounterInternal(volume.NewSpecFromVolume(spec), pod, &fakeAzureSvc{}, fake)
if _, ok := mounter.(volume.Unmounter); ok {
t.Errorf("Volume Mounter can be type-assert to Unmounter")
}
unmounter, err := plug.(*azureFilePlugin).newUnmounterInternal("vol1", types.UID("poduid"), &mount.FakeMounter{})
if _, ok := unmounter.(volume.Mounter); ok {
t.Errorf("Volume Unmounter can be type-assert to Mounter")
}
}
type testcase struct {
name string
defaultNs string
spec *volume.Spec
// Expected return of the test
expectedName string
expectedNs string
expectedError error
}
func TestGetSecretNameAndNamespaceForPV(t *testing.T) {
secretNs := "ns"
tests := []testcase{
{
name: "persistent volume source",
defaultNs: "default",
spec: &volume.Spec{
PersistentVolume: &v1.PersistentVolume{
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFilePersistentVolumeSource{
ShareName: "share",
SecretName: "name",
SecretNamespace: &secretNs,
},
},
},
},
},
expectedName: "name",
expectedNs: "ns",
expectedError: nil,
},
{
name: "persistent volume source without namespace",
defaultNs: "default",
spec: &volume.Spec{
PersistentVolume: &v1.PersistentVolume{
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFilePersistentVolumeSource{
ShareName: "share",
SecretName: "name",
},
},
},
},
},
expectedName: "name",
expectedNs: "default",
expectedError: nil,
},
{
name: "pod volume source",
defaultNs: "default",
spec: &volume.Spec{
Volume: &v1.Volume{
VolumeSource: v1.VolumeSource{
AzureFile: &v1.AzureFileVolumeSource{
ShareName: "share",
SecretName: "name",
},
},
},
},
expectedName: "name",
expectedNs: "default",
expectedError: nil,
},
}
for _, testcase := range tests {
resultName, resultNs, err := getSecretNameAndNamespace(testcase.spec, testcase.defaultNs)
if err != testcase.expectedError || resultName != testcase.expectedName || resultNs != testcase.expectedNs {
t.Errorf("%s failed: expected err=%v ns=%q name=%q, got %v/%q/%q", testcase.name, testcase.expectedError, testcase.expectedNs, testcase.expectedName,
err, resultNs, resultName)
}
}
}
func TestAppendDefaultMountOptions(t *testing.T) {
tests := []struct {
options []string
expected []string
}{
{
options: []string{"dir_mode=0777"},
expected: []string{"dir_mode=0777", fmt.Sprintf("%s=%s", fileMode, defaultFileMode), fmt.Sprintf("%s=%s", vers, defaultVers)},
},
{
options: []string{"file_mode=0777"},
expected: []string{"file_mode=0777", fmt.Sprintf("%s=%s", dirMode, defaultDirMode), fmt.Sprintf("%s=%s", vers, defaultVers)},
},
{
options: []string{"vers=2.1"},
expected: []string{"vers=2.1", fmt.Sprintf("%s=%s", fileMode, defaultFileMode), fmt.Sprintf("%s=%s", dirMode, defaultDirMode)},
},
{
options: []string{""},
expected: []string{"", fmt.Sprintf("%s=%s", fileMode, defaultFileMode), fmt.Sprintf("%s=%s", dirMode, defaultDirMode), fmt.Sprintf("%s=%s", vers, defaultVers)},
},
{
options: []string{"file_mode=0777", "dir_mode=0777"},
expected: []string{"file_mode=0777", "dir_mode=0777", fmt.Sprintf("%s=%s", vers, defaultVers)},
},
}
for _, test := range tests {
result := appendDefaultMountOptions(test.options)
if !reflect.DeepEqual(result, test.expected) {
t.Errorf("input: %q, appendDefaultMountOptions result: %q, expected: %q", test.options, result, test.expected)
}
}
}

View File

@ -0,0 +1,213 @@
/*
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 azure_file
import (
"fmt"
"strings"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
)
var _ volume.DeletableVolumePlugin = &azureFilePlugin{}
var _ volume.ProvisionableVolumePlugin = &azureFilePlugin{}
// Abstract interface to file share operations.
// azure cloud provider should implement it
type azureCloudProvider interface {
// create a file share
CreateFileShare(name, storageAccount, storageType, location string, requestGB int) (string, string, error)
// delete a file share
DeleteFileShare(accountName, key, name string) error
}
type azureFileDeleter struct {
*azureFile
accountName, accountKey, shareName string
azureProvider azureCloudProvider
}
func (plugin *azureFilePlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
azure, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
if err != nil {
glog.V(4).Infof("failed to get azure provider")
return nil, err
}
return plugin.newDeleterInternal(spec, &azureSvc{}, azure)
}
func (plugin *azureFilePlugin) newDeleterInternal(spec *volume.Spec, util azureUtil, azure azureCloudProvider) (volume.Deleter, error) {
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureFile == nil {
return nil, fmt.Errorf("invalid PV spec")
}
secretName, secretNamespace, err := getSecretNameAndNamespace(spec, spec.PersistentVolume.Spec.ClaimRef.Namespace)
if err != nil {
return nil, err
}
shareName := spec.PersistentVolume.Spec.AzureFile.ShareName
if accountName, accountKey, err := util.GetAzureCredentials(plugin.host, secretNamespace, secretName); err != nil {
return nil, err
} else {
return &azureFileDeleter{
azureFile: &azureFile{
volName: spec.Name(),
plugin: plugin,
},
shareName: shareName,
accountName: accountName,
accountKey: accountKey,
azureProvider: azure,
}, nil
}
}
func (plugin *azureFilePlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
azure, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
if err != nil {
glog.V(4).Infof("failed to get azure provider")
return nil, err
}
if len(options.PVC.Spec.AccessModes) == 0 {
options.PVC.Spec.AccessModes = plugin.GetAccessModes()
}
return plugin.newProvisionerInternal(options, azure)
}
func (plugin *azureFilePlugin) newProvisionerInternal(options volume.VolumeOptions, azure azureCloudProvider) (volume.Provisioner, error) {
return &azureFileProvisioner{
azureFile: &azureFile{
plugin: plugin,
},
azureProvider: azure,
util: &azureSvc{},
options: options,
}, nil
}
var _ volume.Deleter = &azureFileDeleter{}
func (f *azureFileDeleter) GetPath() string {
name := azureFilePluginName
return f.plugin.host.GetPodVolumeDir(f.podUID, utilstrings.EscapeQualifiedNameForDisk(name), f.volName)
}
func (f *azureFileDeleter) Delete() error {
glog.V(4).Infof("deleting volume %s", f.shareName)
return f.azureProvider.DeleteFileShare(f.accountName, f.accountKey, f.shareName)
}
type azureFileProvisioner struct {
*azureFile
azureProvider azureCloudProvider
util azureUtil
options volume.VolumeOptions
}
var _ volume.Provisioner = &azureFileProvisioner{}
func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) {
if !volume.AccessModesContainedInAll(a.plugin.GetAccessModes(), a.options.PVC.Spec.AccessModes) {
return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", a.options.PVC.Spec.AccessModes, a.plugin.GetAccessModes())
}
var sku, location, account string
// File share name has a length limit of 63, and it cannot contain two consecutive '-'s.
name := volume.GenerateVolumeName(a.options.ClusterName, a.options.PVName, 63)
name = strings.Replace(name, "--", "-", -1)
capacity := a.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
requestBytes := capacity.Value()
requestGB := int(volume.RoundUpSize(requestBytes, 1024*1024*1024))
secretNamespace := a.options.PVC.Namespace
// Apply ProvisionerParameters (case-insensitive). We leave validation of
// the values to the cloud provider.
for k, v := range a.options.Parameters {
switch strings.ToLower(k) {
case "skuname":
sku = v
case "location":
location = v
case "storageaccount":
account = v
case "secretnamespace":
secretNamespace = v
default:
return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, a.plugin.GetPluginName())
}
}
// TODO: implement c.options.ProvisionerSelector parsing
if a.options.PVC.Spec.Selector != nil {
return nil, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on Azure file")
}
account, key, err := a.azureProvider.CreateFileShare(name, account, sku, location, requestGB)
if err != nil {
return nil, err
}
// create a secret for storage account and key
secretName, err := a.util.SetAzureCredentials(a.plugin.host, secretNamespace, account, key)
if err != nil {
return nil, err
}
// create PV
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: a.options.PVName,
Labels: map[string]string{},
Annotations: map[string]string{
volumehelper.VolumeDynamicallyCreatedByKey: "azure-file-dynamic-provisioner",
},
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: a.options.PersistentVolumeReclaimPolicy,
AccessModes: a.options.PVC.Spec.AccessModes,
Capacity: v1.ResourceList{
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", requestGB)),
},
PersistentVolumeSource: v1.PersistentVolumeSource{
AzureFile: &v1.AzureFilePersistentVolumeSource{
SecretName: secretName,
ShareName: name,
SecretNamespace: &secretNamespace,
},
},
MountOptions: a.options.MountOptions,
},
}
return pv, nil
}
// Return cloud provider
func getAzureCloudProvider(cloudProvider cloudprovider.Interface) (azureCloudProvider, error) {
azureCloudProvider, ok := cloudProvider.(*azure.Cloud)
if !ok || azureCloudProvider == nil {
return nil, fmt.Errorf("Failed to get Azure Cloud Provider. GetCloudProvider returned %v instead", cloudProvider)
}
return azureCloudProvider, nil
}

View File

@ -0,0 +1,129 @@
/*
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 azure_file
import (
"fmt"
"strings"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/volume"
)
const (
fileMode = "file_mode"
dirMode = "dir_mode"
vers = "vers"
defaultFileMode = "0755"
defaultDirMode = "0755"
defaultVers = "3.0"
)
// Abstract interface to azure file operations.
type azureUtil interface {
GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName string) (string, string, error)
SetAzureCredentials(host volume.VolumeHost, nameSpace, accountName, accountKey string) (string, error)
}
type azureSvc struct{}
func (s *azureSvc) GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName string) (string, string, error) {
var accountKey, accountName string
kubeClient := host.GetKubeClient()
if kubeClient == nil {
return "", "", fmt.Errorf("Cannot get kube client")
}
keys, err := kubeClient.Core().Secrets(nameSpace).Get(secretName, metav1.GetOptions{})
if err != nil {
return "", "", fmt.Errorf("Couldn't get secret %v/%v", nameSpace, secretName)
}
for name, data := range keys.Data {
if name == "azurestorageaccountname" {
accountName = string(data)
}
if name == "azurestorageaccountkey" {
accountKey = string(data)
}
}
if accountName == "" || accountKey == "" {
return "", "", fmt.Errorf("Invalid %v/%v, couldn't extract azurestorageaccountname or azurestorageaccountkey", nameSpace, secretName)
}
return accountName, accountKey, nil
}
func (s *azureSvc) SetAzureCredentials(host volume.VolumeHost, nameSpace, accountName, accountKey string) (string, error) {
kubeClient := host.GetKubeClient()
if kubeClient == nil {
return "", fmt.Errorf("Cannot get kube client")
}
secretName := "azure-storage-account-" + accountName + "-secret"
secret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: nameSpace,
Name: secretName,
},
Data: map[string][]byte{
"azurestorageaccountname": []byte(accountName),
"azurestorageaccountkey": []byte(accountKey),
},
Type: "Opaque",
}
_, err := kubeClient.Core().Secrets(nameSpace).Create(secret)
if errors.IsAlreadyExists(err) {
err = nil
}
if err != nil {
return "", fmt.Errorf("Couldn't create secret %v", err)
}
return secretName, err
}
// check whether mountOptions contain file_mode and dir_mode, if not, append default mode
func appendDefaultMountOptions(mountOptions []string) []string {
fileModeFlag := false
dirModeFlag := false
versFlag := false
for _, mountOption := range mountOptions {
if strings.HasPrefix(mountOption, fileMode) {
fileModeFlag = true
}
if strings.HasPrefix(mountOption, dirMode) {
dirModeFlag = true
}
if strings.HasPrefix(mountOption, vers) {
versFlag = true
}
}
allMountOptions := mountOptions
if !fileModeFlag {
allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", fileMode, defaultFileMode))
}
if !dirModeFlag {
allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", dirMode, defaultDirMode))
}
if !versFlag {
allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", vers, defaultVers))
}
return allMountOptions
}

19
vendor/k8s.io/kubernetes/pkg/volume/azure_file/doc.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
/*
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 azure_file contains the internal representation of
// Azure File Service Volume
package azure_file // import "k8s.io/kubernetes/pkg/volume/azure_file"