support Azure File Service volume

Signed-off-by: Huamin Chen <hchen@redhat.com>
pull/6/head
Huamin Chen 2015-11-13 11:47:04 -05:00
parent 242000d790
commit d7e4b826b9
26 changed files with 41638 additions and 39532 deletions

View File

@ -14914,6 +14914,10 @@
"$ref": "v1.FlexVolumeSource",
"description": "FlexVolume represents a generic volume resource that is provisioned/attached using a exec based plugin. This is an alpha feature and may change in future."
},
"azureFile": {
"$ref": "v1.AzureFileVolumeSource",
"description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod."
},
"accessModes": {
"type": "array",
"items": {
@ -15268,6 +15272,28 @@
}
}
},
"v1.AzureFileVolumeSource": {
"id": "v1.AzureFileVolumeSource",
"description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
"required": [
"secretName",
"shareName"
],
"properties": {
"secretName": {
"type": "string",
"description": "the name of secret that contains Azure Storage Account Name and Key"
},
"shareName": {
"type": "string",
"description": "Share Name"
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.PersistentVolumeStatus": {
"id": "v1.PersistentVolumeStatus",
"description": "PersistentVolumeStatus is the current status of a persistent volume.",
@ -15498,6 +15524,10 @@
"fc": {
"$ref": "v1.FCVolumeSource",
"description": "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod."
},
"azureFile": {
"$ref": "v1.AzureFileVolumeSource",
"description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod."
}
}
},

View File

@ -4333,6 +4333,10 @@
"fc": {
"$ref": "v1.FCVolumeSource",
"description": "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod."
},
"azureFile": {
"$ref": "v1.AzureFileVolumeSource",
"description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod."
}
}
},
@ -4779,6 +4783,28 @@
}
}
},
"v1.AzureFileVolumeSource": {
"id": "v1.AzureFileVolumeSource",
"description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
"required": [
"secretName",
"shareName"
],
"properties": {
"secretName": {
"type": "string",
"description": "the name of secret that contains Azure Storage Account Name and Key"
},
"shareName": {
"type": "string",
"description": "Share Name"
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",

View File

@ -29,6 +29,7 @@ import (
// Volume plugins
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/aws_ebs"
"k8s.io/kubernetes/pkg/volume/azure_file"
"k8s.io/kubernetes/pkg/volume/cephfs"
"k8s.io/kubernetes/pkg/volume/cinder"
"k8s.io/kubernetes/pkg/volume/downwardapi"
@ -78,7 +79,7 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins(pluginDir)...)
allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...)
return allPlugins
}

View File

@ -2364,6 +2364,54 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azurefilevolumesource">v1.AzureFileVolumeSource</h3>
<div class="paragraph">
<p>AzureFile represents an Azure File Service mount on the host and bind mount to the pod.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">secretName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">the name of secret that contains Azure Storage Account Name and Key</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">shareName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Share Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_hostpathvolumesource">v1.HostPathVolumeSource</h3>
@ -3668,6 +3716,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_fcvolumesource">v1.FCVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">azureFile</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AzureFile represents an Azure File Service mount on the host and bind mount to the pod.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_azurefilevolumesource">v1.AzureFileVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -4772,7 +4827,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-02-08 13:55:53 UTC
Last updated 2016-02-09 01:06:27 UTC
</div>
</div>
</body>

View File

@ -1891,6 +1891,54 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azurefilevolumesource">v1.AzureFileVolumeSource</h3>
<div class="paragraph">
<p>AzureFile represents an Azure File Service mount on the host and bind mount to the pod.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">secretName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">the name of secret that contains Azure Storage Account Name and Key</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">shareName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Share Name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_iscsivolumesource">v1.ISCSIVolumeSource</h3>
@ -3235,6 +3283,13 @@ The resulting set of endpoints can be viewed as:<br>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_fcvolumesource">v1.FCVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">azureFile</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AzureFile represents an Azure File Service mount on the host and bind mount to the pod.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_azurefilevolumesource">v1.AzureFileVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -5180,6 +5235,13 @@ The resulting set of endpoints can be viewed as:<br>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">azureFile</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AzureFile represents an Azure File Service mount on the host and bind mount to the pod.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_azurefilevolumesource">v1.AzureFileVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">accessModes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AccessModes contains all ways the volume can be mounted. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes">http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
@ -7316,7 +7378,7 @@ The resulting set of endpoints can be viewed as:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-02-02 15:03:04 UTC
Last updated 2016-02-08 13:47:29 UTC
</div>
</div>
</body>

View File

@ -67,6 +67,7 @@ Familiarity with [pods](pods.md) is suggested.
- [persistentVolumeClaim](#persistentvolumeclaim)
- [downwardAPI](#downwardapi)
- [FlexVolume](#flexvolume)
- [AzureFileVolume](#azurefilevolume)
- [Resources](#resources)
<!-- END MUNGE: GENERATED_TOC -->
@ -124,6 +125,7 @@ Kubernetes supports several types of Volumes:
* `secret`
* `persistentVolumeClaim`
* `downwardAPI`
* `azureFileVolume`
We welcome additional contributions.
@ -427,6 +429,13 @@ an alpha feature and may change in future.
More details are in [here](../../examples/flexvolume/README.md)
### AzureFileVolume
A `AzureFileVolume` is used to mount a Microsoft Azure File Volume (SMB 2.1 and 3.0)
into a Pod.
More details can be found [here](../../examples/azure_file/README.md)
## Resources
The storage media (Disk, SSD, etc) of an `emptyDir` volume is determined by the
@ -440,7 +449,6 @@ request a certain amount of space using a [resource](compute-resources.md)
specification, and to select the type of media to use, for clusters that have
several media types.
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/volumes.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->

View File

@ -0,0 +1,64 @@
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
<!-- BEGIN STRIP_FOR_RELEASE -->
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>
If you are using a released version of Kubernetes, you should
refer to the docs that go with that version.
Documentation for other releases can be found at
[releases.k8s.io](http://releases.k8s.io).
</strong>
--
<!-- END STRIP_FOR_RELEASE -->
<!-- END MUNGE: UNVERSIONED_WARNING -->
# How to Use it?
Install *cifs-utils* on the Kubernetes host. For example, on Fedora based Linux
# yum -y install cifs-utils
Note, as explained in [Azure File Storage for Linux](https://azure.microsoft.com/en-us/documentation/articles/storage-how-to-use-files-linux/), the Linux hosts and the file share must be in the same Azure region.
Obtain an Microsoft Azure storage account and create a [secret](secret/azure-secret.yaml) that contains the base64 encoded Azure Storage account name and key. In the secret file, base64-encode Azure Storage account name and pair it with name *azurestorageaccountname*, and base64-encode Azure Storage access key and pair it with name *azurestorageaccountkey*.
Then create a Pod using the volume spec based on [azure](azure.yaml).
In the pod, you need to provide the following information:
- *secretName*: the name of the secret that contains both Azure storage account name and key.
- *shareName*: The share name to be used.
- *readOnly*: Whether the filesystem is used as readOnly.
Create the secret:
```console
# kubectl create -f examples/azure_file/secret/azure-secret.yaml
```
You should see the account name and key from `kubectl get secret`
Then create the Pod:
```console
# kubectl create -f examples/azure_file/azure.yaml
```
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/examples/azure_file/README.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->

View File

@ -0,0 +1,17 @@
apiVersion: v1
kind: Pod
metadata:
name: azure
spec:
containers:
- image: kubernetes/pause
name: azure
volumeMounts:
- name: azure
mountPath: /mnt/azure
volumes:
- name: azure
azureFile:
secretName: azure-secret
shareName: k8stest
readOnly: false

View File

@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: azure-secret
type: Opaque
data:
azurestorageaccountname: azhzdGVzdA==
azurestorageaccountkey: eElGMXpKYm5ub2pGTE1Ta0JwNTBteDAyckhzTUsyc2pVN21GdDRMMTNob0I3ZHJBYUo4akQ2K0E0NDNqSm9nVjd5MkZVT2hRQ1dQbU02WWFOSHk3cWc9PQ==

View File

@ -402,6 +402,9 @@ func TestExampleObjectSchemas(t *testing.T) {
"redis-service": &api.Service{},
"job": &extensions.Job{},
},
"../examples/azure_file": {
"azure": &api.Pod{},
},
}
capabilities.SetForTests(capabilities.Capabilities{

View File

@ -33,6 +33,7 @@ func init() {
if err := Scheme.AddGeneratedDeepCopyFuncs(
DeepCopy_api_AWSElasticBlockStoreVolumeSource,
DeepCopy_api_Affinity,
DeepCopy_api_AzureFileVolumeSource,
DeepCopy_api_Binding,
DeepCopy_api_Capabilities,
DeepCopy_api_CephFSVolumeSource,
@ -203,6 +204,13 @@ func DeepCopy_api_Affinity(in Affinity, out *Affinity, c *conversion.Cloner) err
return nil
}
func DeepCopy_api_AzureFileVolumeSource(in AzureFileVolumeSource, out *AzureFileVolumeSource, c *conversion.Cloner) error {
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
return nil
}
func DeepCopy_api_Binding(in Binding, out *Binding, c *conversion.Cloner) error {
if err := DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
@ -1828,6 +1836,15 @@ func DeepCopy_api_PersistentVolumeSource(in PersistentVolumeSource, out *Persist
} else {
out.Flocker = nil
}
if in.AzureFile != nil {
in, out := in.AzureFile, &out.AzureFile
*out = new(AzureFileVolumeSource)
if err := DeepCopy_api_AzureFileVolumeSource(*in, *out, c); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -2912,6 +2929,15 @@ func DeepCopy_api_VolumeSource(in VolumeSource, out *VolumeSource, c *conversion
} else {
out.FC = nil
}
if in.AzureFile != nil {
in, out := in.AzureFile, &out.AzureFile
*out = new(AzureFileVolumeSource)
if err := DeepCopy_api_AzureFileVolumeSource(*in, *out, c); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -215,6 +215,8 @@ type VolumeSource struct {
DownwardAPI *DownwardAPIVolumeSource `json:"downwardAPI,omitempty"`
// FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.
FC *FCVolumeSource `json:"fc,omitempty"`
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
}
// Similar to VolumeSource but meant for the administrator who creates PVs.
@ -251,6 +253,8 @@ type PersistentVolumeSource struct {
FC *FCVolumeSource `json:"fc,omitempty"`
// Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running
Flocker *FlockerVolumeSource `json:"flocker,omitempty"`
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
}
type PersistentVolumeClaimVolumeSource struct {
@ -672,6 +676,17 @@ type DownwardAPIVolumeFile struct {
FieldRef ObjectFieldSelector `json:"fieldRef"`
}
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
type AzureFileVolumeSource struct {
// the name of secret that contains Azure Storage Account Name and Key
SecretName string `json:"secretName"`
// Share Name
ShareName string `json:"shareName"`
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
ReadOnly bool `json:"readOnly,omitempty"`
}
// ContainerPort represents a network port in a single container
type ContainerPort struct {
// Optional: If specified, this must be an IANA_SVC_NAME Each named port

View File

@ -43,6 +43,20 @@ func Convert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolu
return autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in, out, s)
}
func autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in *api.AzureFileVolumeSource, out *AzureFileVolumeSource, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.AzureFileVolumeSource))(in)
}
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in *api.AzureFileVolumeSource, out *AzureFileVolumeSource, s conversion.Scope) error {
return autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in, out, s)
}
func autoConvert_api_Binding_To_v1_Binding(in *api.Binding, out *Binding, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.Binding))(in)
@ -1970,6 +1984,15 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
} else {
out.Flocker = nil
}
// unable to generate simple pointer conversion for api.AzureFileVolumeSource -> v1.AzureFileVolumeSource
if in.AzureFile != nil {
out.AzureFile = new(AzureFileVolumeSource)
if err := Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -3196,6 +3219,15 @@ func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *
} else {
out.FC = nil
}
// unable to generate simple pointer conversion for api.AzureFileVolumeSource -> v1.AzureFileVolumeSource
if in.AzureFile != nil {
out.AzureFile = new(AzureFileVolumeSource)
if err := Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -3231,6 +3263,20 @@ func Convert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolu
return autoConvert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource(in, out, s)
}
func autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *AzureFileVolumeSource, out *api.AzureFileVolumeSource, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*AzureFileVolumeSource))(in)
}
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *AzureFileVolumeSource, out *api.AzureFileVolumeSource, s conversion.Scope) error {
return autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in, out, s)
}
func autoConvert_v1_Binding_To_api_Binding(in *Binding, out *api.Binding, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*Binding))(in)
@ -5099,6 +5145,15 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *Per
} else {
out.FlexVolume = nil
}
// unable to generate simple pointer conversion for v1.AzureFileVolumeSource -> api.AzureFileVolumeSource
if in.AzureFile != nil {
out.AzureFile = new(api.AzureFileVolumeSource)
if err := Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -6274,6 +6329,15 @@ func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.
} else {
out.FC = nil
}
// unable to generate simple pointer conversion for v1.AzureFileVolumeSource -> api.AzureFileVolumeSource
if in.AzureFile != nil {
out.AzureFile = new(api.AzureFileVolumeSource)
if err := Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -6284,6 +6348,7 @@ func Convert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.Volu
func init() {
err := api.Scheme.AddGeneratedConversionFuncs(
autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource,
autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource,
autoConvert_api_Binding_To_v1_Binding,
autoConvert_api_Capabilities_To_v1_Capabilities,
autoConvert_api_CephFSVolumeSource_To_v1_CephFSVolumeSource,
@ -6410,6 +6475,7 @@ func init() {
autoConvert_api_Volume_To_v1_Volume,
autoConvert_unversioned_ExportOptions_To_v1_ExportOptions,
autoConvert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource,
autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource,
autoConvert_v1_Binding_To_api_Binding,
autoConvert_v1_Capabilities_To_api_Capabilities,
autoConvert_v1_CephFSVolumeSource_To_api_CephFSVolumeSource,

View File

@ -73,6 +73,13 @@ func deepCopy_v1_AWSElasticBlockStoreVolumeSource(in AWSElasticBlockStoreVolumeS
return nil
}
func deepCopy_v1_AzureFileVolumeSource(in AzureFileVolumeSource, out *AzureFileVolumeSource, c *conversion.Cloner) error {
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
return nil
}
func deepCopy_v1_Binding(in Binding, out *Binding, c *conversion.Cloner) error {
if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
@ -1490,6 +1497,14 @@ func deepCopy_v1_PersistentVolumeSource(in PersistentVolumeSource, out *Persiste
} else {
out.FlexVolume = nil
}
if in.AzureFile != nil {
out.AzureFile = new(AzureFileVolumeSource)
if err := deepCopy_v1_AzureFileVolumeSource(*in.AzureFile, out.AzureFile, c); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -2514,6 +2529,14 @@ func deepCopy_v1_VolumeSource(in VolumeSource, out *VolumeSource, c *conversion.
} else {
out.FC = nil
}
if in.AzureFile != nil {
out.AzureFile = new(AzureFileVolumeSource)
if err := deepCopy_v1_AzureFileVolumeSource(*in.AzureFile, out.AzureFile, c); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -2550,6 +2573,7 @@ func init() {
deepCopy_unversioned_Time,
deepCopy_unversioned_TypeMeta,
deepCopy_v1_AWSElasticBlockStoreVolumeSource,
deepCopy_v1_AzureFileVolumeSource,
deepCopy_v1_Binding,
deepCopy_v1_Capabilities,
deepCopy_v1_CephFSVolumeSource,

File diff suppressed because it is too large Load Diff

View File

@ -261,6 +261,8 @@ type VolumeSource struct {
DownwardAPI *DownwardAPIVolumeSource `json:"downwardAPI,omitempty"`
// FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.
FC *FCVolumeSource `json:"fc,omitempty"`
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
}
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
@ -319,6 +321,8 @@ type PersistentVolumeSource struct {
// provisioned/attached using a exec based plugin. This is an
// alpha feature and may change in future.
FlexVolume *FlexVolumeSource `json:"flexVolume,omitempty"`
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
}
// PersistentVolume (PV) is a storage resource provisioned by an administrator.
@ -791,6 +795,17 @@ type FCVolumeSource struct {
ReadOnly bool `json:"readOnly,omitempty"`
}
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
type AzureFileVolumeSource struct {
// the name of secret that contains Azure Storage Account Name and Key
SecretName string `json:"secretName"`
// Share Name
ShareName string `json:"shareName"`
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
ReadOnly bool `json:"readOnly,omitempty"`
}
// ContainerPort represents a network port in a single container.
type ContainerPort struct {
// If specified, this must be an IANA_SVC_NAME and unique within the pod. Each

View File

@ -48,6 +48,17 @@ func (Affinity) SwaggerDoc() map[string]string {
return map_Affinity
}
var map_AzureFileVolumeSource = map[string]string{
"": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
"secretName": "the name of secret that contains Azure Storage Account Name and Key",
"shareName": "Share Name",
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
}
func (AzureFileVolumeSource) SwaggerDoc() map[string]string {
return map_AzureFileVolumeSource
}
var map_Binding = map[string]string{
"": "Binding ties one object to another. For example, a pod is bound to a node by a scheduler.",
"metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
@ -990,6 +1001,7 @@ var map_PersistentVolumeSource = map[string]string{
"fc": "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.",
"flocker": "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running",
"flexVolume": "FlexVolume represents a generic volume resource that is provisioned/attached using a exec based plugin. This is an alpha feature and may change in future.",
"azureFile": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
}
func (PersistentVolumeSource) SwaggerDoc() map[string]string {
@ -1541,6 +1553,7 @@ var map_VolumeSource = map[string]string{
"flocker": "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running",
"downwardAPI": "DownwardAPI represents downward API about the pod that should populate this volume",
"fc": "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.",
"azureFile": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
}
func (VolumeSource) SwaggerDoc() map[string]string {

View File

@ -494,6 +494,10 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
numVolumes++
allErrs = append(allErrs, validateFlexVolumeSource(source.FlexVolume, fldPath.Child("flexVolume"))...)
}
if source.AzureFile != nil {
numVolumes++
allErrs = append(allErrs, validateAzureFile(source.AzureFile, fldPath.Child("azureFile"))...)
}
if numVolumes == 0 {
allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
}
@ -718,6 +722,17 @@ func validateFlexVolumeSource(fv *api.FlexVolumeSource, fldPath *field.Path) fie
return allErrs
}
func validateAzureFile(azure *api.AzureFileVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if azure.SecretName == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
}
if azure.ShareName == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("shareName"), ""))
}
return allErrs
}
func ValidatePersistentVolumeName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
@ -842,6 +857,10 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
numVolumes++
allErrs = append(allErrs, validateFlexVolumeSource(pv.Spec.FlexVolume, specPath.Child("flexVolume"))...)
}
if pv.Spec.AzureFile != nil {
numVolumes++
allErrs = append(allErrs, validateAzureFile(pv.Spec.AzureFile, specPath.Child("azureFile"))...)
}
if numVolumes == 0 {
allErrs = append(allErrs, field.Required(specPath, "must specify a volume type"))
}

View File

@ -509,6 +509,7 @@ func TestValidateVolumes(t *testing.T) {
}}}},
{Name: "fc", VolumeSource: api.VolumeSource{FC: &api.FCVolumeSource{[]string{"some_wwn"}, &lun, "ext4", false}}},
{Name: "flexvolume", VolumeSource: api.VolumeSource{FlexVolume: &api.FlexVolumeSource{Driver: "kubernetes.io/blue", FSType: "ext4"}}},
{Name: "azure", VolumeSource: api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{"key", "share", false}}},
}
names, errs := validateVolumes(successCase, field.NewPath("field"))
if len(errs) != 0 {
@ -557,6 +558,8 @@ func TestValidateVolumes(t *testing.T) {
zeroWWN := api.VolumeSource{FC: &api.FCVolumeSource{[]string{}, &lun, "ext4", false}}
emptyLun := api.VolumeSource{FC: &api.FCVolumeSource{[]string{"wwn"}, nil, "ext4", false}}
slashInName := api.VolumeSource{Flocker: &api.FlockerVolumeSource{DatasetName: "foo/bar"}}
emptyAzureSecret := api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{"", "share", false}}
emptyAzureShare := api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{"name", "", false}}
errorCases := map[string]struct {
V []api.Volume
T field.ErrorType
@ -678,6 +681,16 @@ func TestValidateVolumes(t *testing.T) {
field.ErrorTypeInvalid,
"gitRepo.directory", "",
},
"empty secret": {
[]api.Volume{{Name: "emptyaccount", VolumeSource: emptyAzureSecret}},
field.ErrorTypeRequired,
"azureFile.secretName", "",
},
"empty share": {
[]api.Volume{{Name: "emptyaccount", VolumeSource: emptyAzureShare}},
field.ErrorTypeRequired,
"azureFile.shareName", "",
},
}
for k, v := range errorCases {
_, errs := validateVolumes(v.V, field.NewPath("field"))

View File

@ -44,6 +44,20 @@ func Convert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolu
return autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in, out, s)
}
func autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in *api.AzureFileVolumeSource, out *v1.AzureFileVolumeSource, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.AzureFileVolumeSource))(in)
}
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in *api.AzureFileVolumeSource, out *v1.AzureFileVolumeSource, s conversion.Scope) error {
return autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in, out, s)
}
func autoConvert_api_Capabilities_To_v1_Capabilities(in *api.Capabilities, out *v1.Capabilities, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.Capabilities))(in)
@ -1257,6 +1271,15 @@ func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *
} else {
out.FC = nil
}
// unable to generate simple pointer conversion for api.AzureFileVolumeSource -> v1.AzureFileVolumeSource
if in.AzureFile != nil {
out.AzureFile = new(v1.AzureFileVolumeSource)
if err := Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -1329,6 +1352,20 @@ func Convert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolu
return autoConvert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource(in, out, s)
}
func autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *v1.AzureFileVolumeSource, out *api.AzureFileVolumeSource, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*v1.AzureFileVolumeSource))(in)
}
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *v1.AzureFileVolumeSource, out *api.AzureFileVolumeSource, s conversion.Scope) error {
return autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in, out, s)
}
func autoConvert_v1_Capabilities_To_api_Capabilities(in *v1.Capabilities, out *api.Capabilities, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*v1.Capabilities))(in)
@ -2518,6 +2555,15 @@ func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *v1.VolumeSource, out *a
} else {
out.FC = nil
}
// unable to generate simple pointer conversion for v1.AzureFileVolumeSource -> api.AzureFileVolumeSource
if in.AzureFile != nil {
out.AzureFile = new(api.AzureFileVolumeSource)
if err := Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -5213,6 +5259,7 @@ func Convert_v1beta1_ThirdPartyResourceList_To_extensions_ThirdPartyResourceList
func init() {
err := api.Scheme.AddGeneratedConversionFuncs(
autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource,
autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource,
autoConvert_api_Capabilities_To_v1_Capabilities,
autoConvert_api_CephFSVolumeSource_To_v1_CephFSVolumeSource,
autoConvert_api_CinderVolumeSource_To_v1_CinderVolumeSource,
@ -5320,6 +5367,7 @@ func init() {
autoConvert_unversioned_LabelSelectorRequirement_To_v1beta1_LabelSelectorRequirement,
autoConvert_unversioned_LabelSelector_To_v1beta1_LabelSelector,
autoConvert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource,
autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource,
autoConvert_v1_Capabilities_To_api_Capabilities,
autoConvert_v1_CephFSVolumeSource_To_api_CephFSVolumeSource,
autoConvert_v1_CinderVolumeSource_To_api_CinderVolumeSource,

View File

@ -73,6 +73,13 @@ func deepCopy_v1_AWSElasticBlockStoreVolumeSource(in v1.AWSElasticBlockStoreVolu
return nil
}
func deepCopy_v1_AzureFileVolumeSource(in v1.AzureFileVolumeSource, out *v1.AzureFileVolumeSource, c *conversion.Cloner) error {
out.SecretName = in.SecretName
out.ShareName = in.ShareName
out.ReadOnly = in.ReadOnly
return nil
}
func deepCopy_v1_Capabilities(in v1.Capabilities, out *v1.Capabilities, c *conversion.Cloner) error {
if in.Add != nil {
out.Add = make([]v1.Capability, len(in.Add))
@ -964,6 +971,14 @@ func deepCopy_v1_VolumeSource(in v1.VolumeSource, out *v1.VolumeSource, c *conve
} else {
out.FC = nil
}
if in.AzureFile != nil {
out.AzureFile = new(v1.AzureFileVolumeSource)
if err := deepCopy_v1_AzureFileVolumeSource(*in.AzureFile, out.AzureFile, c); err != nil {
return err
}
} else {
out.AzureFile = nil
}
return nil
}
@ -1981,6 +1996,7 @@ func init() {
deepCopy_unversioned_Time,
deepCopy_unversioned_TypeMeta,
deepCopy_v1_AWSElasticBlockStoreVolumeSource,
deepCopy_v1_AzureFileVolumeSource,
deepCopy_v1_Capabilities,
deepCopy_v1_CephFSVolumeSource,
deepCopy_v1_CinderVolumeSource,

View File

@ -0,0 +1,234 @@
/*
Copyright 2016 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 azure_file
import (
"fmt"
"os"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
"github.com/golang/glog"
)
// This is the primary entrypoint 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 (plugin *azureFilePlugin) Init(host volume.VolumeHost) error {
plugin.host = host
return nil
}
func (plugin *azureFilePlugin) Name() string {
return azureFilePluginName
}
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) GetAccessModes() []api.PersistentVolumeAccessMode {
return []api.PersistentVolumeAccessMode{
api.ReadWriteOnce,
api.ReadOnlyMany,
api.ReadWriteMany,
}
}
func (plugin *azureFilePlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, _ volume.VolumeOptions) (volume.Builder, error) {
return plugin.newBuilderInternal(spec, pod, &azureSvc{}, plugin.host.GetMounter())
}
func (plugin *azureFilePlugin) newBuilderInternal(spec *volume.Spec, pod *api.Pod, util azureUtil, mounter mount.Interface) (volume.Builder, error) {
var source *api.AzureFileVolumeSource
var readOnly bool
if spec.Volume != nil && spec.Volume.AzureFile != nil {
source = spec.Volume.AzureFile
readOnly = spec.Volume.AzureFile.ReadOnly
} else {
source = spec.PersistentVolume.Spec.AzureFile
readOnly = spec.ReadOnly
}
return &azureFileBuilder{
azureFile: &azureFile{
volName: spec.Name(),
mounter: mounter,
pod: pod,
plugin: plugin,
},
util: util,
secretName: source.SecretName,
shareName: source.ShareName,
readOnly: readOnly,
}, nil
}
func (plugin *azureFilePlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) {
return plugin.newCleanerInternal(volName, podUID, plugin.host.GetMounter())
}
func (plugin *azureFilePlugin) newCleanerInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Cleaner, error) {
return &azureFileCleaner{&azureFile{
volName: volName,
mounter: mounter,
pod: &api.Pod{ObjectMeta: api.ObjectMeta{UID: podUID}},
plugin: plugin,
}}, nil
}
// azureFile volumes represent mount of an AzureFile share.
type azureFile struct {
volName string
pod *api.Pod
mounter mount.Interface
plugin *azureFilePlugin
volume.MetricsNil
}
func (azureFileVolume *azureFile) GetPath() string {
name := azureFilePluginName
return azureFileVolume.plugin.host.GetPodVolumeDir(azureFileVolume.pod.UID, strings.EscapeQualifiedNameForDisk(name), azureFileVolume.volName)
}
type azureFileBuilder struct {
*azureFile
util azureUtil
secretName string
shareName string
readOnly bool
}
var _ volume.Builder = &azureFileBuilder{}
func (b *azureFileBuilder) GetAttributes() volume.Attributes {
return volume.Attributes{
ReadOnly: b.readOnly,
Managed: !b.readOnly,
SupportsSELinux: false,
}
}
// SetUp attaches the disk and bind mounts to the volume path.
func (b *azureFileBuilder) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup)
}
func (b *azureFileBuilder) 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.pod.Namespace, b.secretName, b.shareName); err != nil {
return err
}
os.MkdirAll(dir, 0750)
source := fmt.Sprintf("//%s.file.core.windows.net/%s", accountName, b.shareName)
// parameters suggested by https://azure.microsoft.com/en-us/documentation/articles/storage-how-to-use-files-linux/
options := []string{fmt.Sprintf("vers=3.0,username=%s,password=%s,dir_mode=0777,file_mode=0777", accountName, accountKey)}
if b.readOnly {
options = append(options, "ro")
}
err = b.mounter.Mount(source, dir, "cifs", 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)
return err
}
return nil
}
var _ volume.Cleaner = &azureFileCleaner{}
type azureFileCleaner struct {
*azureFile
}
func (c *azureFileCleaner) TearDown() error {
return c.TearDownAt(c.GetPath())
}
func (c *azureFileCleaner) TearDownAt(dir string) error {
notMnt, err := c.mounter.IsLikelyNotMountPoint(dir)
if err != nil {
glog.Errorf("Error checking IsLikelyNotMountPoint: %v", err)
return err
}
if notMnt {
return os.Remove(dir)
}
if err := c.mounter.Unmount(dir); err != nil {
glog.Errorf("Unmounting failed: %v", err)
return err
}
notMnt, mntErr := c.mounter.IsLikelyNotMountPoint(dir)
if mntErr != nil {
glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
return mntErr
}
if notMnt {
if err := os.Remove(dir); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,239 @@
/*
Copyright 2016 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 azure_file
import (
"io/ioutil"
"os"
"path"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/testing/fake"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
)
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(), volume.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.Name() != "kubernetes.io/azure-file" {
t.Errorf("Wrong name: %s", plug.Name())
}
if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{}}}}) {
t.Errorf("Expected true")
}
if !plug.CanSupport(&volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{AzureFile: &api.AzureFileVolumeSource{}}}}}) {
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(), volume.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 !contains(plug.GetAccessModes(), api.ReadWriteOnce) || !contains(plug.GetAccessModes(), api.ReadOnlyMany) || !contains(plug.GetAccessModes(), api.ReadWriteMany) {
t.Errorf("Expected three AccessModeTypes: %s, %s, and %s", api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany)
}
}
func contains(modes []api.PersistentVolumeAccessMode, mode api.PersistentVolumeAccessMode) bool {
for _, m := range modes {
if m == mode {
return true
}
}
return false
}
func TestPlugin(t *testing.T) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "azurefileTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v")
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volume.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 := &api.Volume{
Name: "vol1",
VolumeSource: api.VolumeSource{
AzureFile: &api.AzureFileVolumeSource{
SecretName: "secret",
ShareName: "share",
},
},
}
fake := &mount.FakeMounter{}
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
builder, err := plug.(*azureFilePlugin).newBuilderInternal(volume.NewSpecFromVolume(spec), pod, &fakeAzureSvc{}, fake)
if err != nil {
t.Errorf("Failed to make a new Builder: %v", err)
}
if builder == nil {
t.Errorf("Got a nil Builder")
}
volPath := path.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~azure-file/vol1")
path := builder.GetPath()
if path != volPath {
t.Errorf("Got unexpected path: %s", path)
}
if err := builder.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)
}
}
cleaner, err := plug.(*azureFilePlugin).newCleanerInternal("vol1", types.UID("poduid"), &mount.FakeMounter{})
if err != nil {
t.Errorf("Failed to make a new Cleaner: %v", err)
}
if cleaner == nil {
t.Errorf("Got a nil Cleaner")
}
if err := cleaner.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("SetUp() failed: %v", err)
}
}
func TestPersistentClaimReadOnlyFlag(t *testing.T) {
pv := &api.PersistentVolume{
ObjectMeta: api.ObjectMeta{
Name: "pvA",
},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
AzureFile: &api.AzureFileVolumeSource{},
},
ClaimRef: &api.ObjectReference{
Name: "claimA",
},
},
}
claim := &api.PersistentVolumeClaim{
ObjectMeta: api.ObjectMeta{
Name: "claimA",
Namespace: "nsA",
},
Spec: api.PersistentVolumeClaimSpec{
VolumeName: "pvA",
},
Status: api.PersistentVolumeClaimStatus{
Phase: api.ClaimBound,
},
}
client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil))
plug, _ := plugMgr.FindPluginByName(azureFilePluginName)
// readOnly bool is supplied by persistent-claim volume source when its builder creates other volumes
spec := volume.NewSpecFromPersistentVolume(pv, true)
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
builder, _ := plug.NewBuilder(spec, pod, volume.VolumeOptions{})
if !builder.GetAttributes().ReadOnly {
t.Errorf("Expected true for builder.IsReadOnly")
}
}
type fakeAzureSvc struct{}
func (s *fakeAzureSvc) GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName, shareName string) (string, string, error) {
return "name", "key", nil
}
func TestBuilderAndCleanerTypeAssert(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(), volume.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 := &api.Volume{
Name: "vol1",
VolumeSource: api.VolumeSource{
AzureFile: &api.AzureFileVolumeSource{
SecretName: "secret",
ShareName: "share",
},
},
}
fake := &mount.FakeMounter{}
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}}
builder, err := plug.(*azureFilePlugin).newBuilderInternal(volume.NewSpecFromVolume(spec), pod, &fakeAzureSvc{}, fake)
if _, ok := builder.(volume.Cleaner); ok {
t.Errorf("Volume Builder can be type-assert to Cleaner")
}
cleaner, err := plug.(*azureFilePlugin).newCleanerInternal("vol1", types.UID("poduid"), &mount.FakeMounter{})
if _, ok := cleaner.(volume.Builder); ok {
t.Errorf("Volume Cleaner can be type-assert to Builder")
}
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2016 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 azure_file
import (
"fmt"
"k8s.io/kubernetes/pkg/volume"
)
// Abstract interface to azure file operations.
type azureUtil interface {
GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName, shareName string) (string, string, error)
}
type azureSvc struct{}
func (s *azureSvc) GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName, shareName 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)
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
}

View File

@ -0,0 +1,19 @@
/*
Copyright 2016 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 azure_file contains the internal representation of
// Azure File Service Volume
package azure_file