support Azure data disk volume

Signed-off-by: Huamin Chen <hchen@redhat.com>
pull/6/head
Huamin Chen 2016-07-27 14:07:34 -04:00
parent 88c977c34a
commit dea4b0226d
36 changed files with 51872 additions and 48054 deletions

View File

@ -1425,6 +1425,10 @@
"quobyte": {
"$ref": "v1.QuobyteVolumeSource",
"description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime"
},
"azureDisk": {
"$ref": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod."
}
}
},
@ -2023,6 +2027,40 @@
}
}
},
"v1.AzureDiskVolumeSource": {
"id": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
"required": [
"diskName",
"diskURI"
],
"properties": {
"diskName": {
"type": "string",
"description": "The Name of the data disk in the blob storage"
},
"diskURI": {
"type": "string",
"description": "The URI the data disk in the blob storage"
},
"cachingMode": {
"$ref": "v1.AzureDataDiskCachingMode",
"description": "Host Caching mode: None, Read Only, Read Write."
},
"fsType": {
"type": "string",
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.AzureDataDiskCachingMode": {
"id": "v1.AzureDataDiskCachingMode",
"properties": {}
},
"v1.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",

View File

@ -1430,6 +1430,10 @@
"quobyte": {
"$ref": "v1.QuobyteVolumeSource",
"description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime"
},
"azureDisk": {
"$ref": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod."
}
}
},
@ -2028,6 +2032,40 @@
}
}
},
"v1.AzureDiskVolumeSource": {
"id": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
"required": [
"diskName",
"diskURI"
],
"properties": {
"diskName": {
"type": "string",
"description": "The Name of the data disk in the blob storage"
},
"diskURI": {
"type": "string",
"description": "The URI the data disk in the blob storage"
},
"cachingMode": {
"$ref": "v1.AzureDataDiskCachingMode",
"description": "Host Caching mode: None, Read Only, Read Write."
},
"fsType": {
"type": "string",
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.AzureDataDiskCachingMode": {
"id": "v1.AzureDataDiskCachingMode",
"properties": {}
},
"v1.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",

View File

@ -8594,6 +8594,10 @@
"quobyte": {
"$ref": "v1.QuobyteVolumeSource",
"description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime"
},
"azureDisk": {
"$ref": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod."
}
}
},
@ -9192,6 +9196,40 @@
}
}
},
"v1.AzureDiskVolumeSource": {
"id": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
"required": [
"diskName",
"diskURI"
],
"properties": {
"diskName": {
"type": "string",
"description": "The Name of the data disk in the blob storage"
},
"diskURI": {
"type": "string",
"description": "The URI the data disk in the blob storage"
},
"cachingMode": {
"$ref": "v1.AzureDataDiskCachingMode",
"description": "Host Caching mode: None, Read Only, Read Write."
},
"fsType": {
"type": "string",
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.AzureDataDiskCachingMode": {
"id": "v1.AzureDataDiskCachingMode",
"properties": {}
},
"v1.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",

View File

@ -17317,6 +17317,10 @@
"$ref": "v1.QuobyteVolumeSource",
"description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime"
},
"azureDisk": {
"$ref": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod."
},
"accessModes": {
"type": "array",
"items": {
@ -17732,6 +17736,40 @@
}
}
},
"v1.AzureDiskVolumeSource": {
"id": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
"required": [
"diskName",
"diskURI"
],
"properties": {
"diskName": {
"type": "string",
"description": "The Name of the data disk in the blob storage"
},
"diskURI": {
"type": "string",
"description": "The URI the data disk in the blob storage"
},
"cachingMode": {
"$ref": "v1.AzureDataDiskCachingMode",
"description": "Host Caching mode: None, Read Only, Read Write."
},
"fsType": {
"type": "string",
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.AzureDataDiskCachingMode": {
"id": "v1.AzureDataDiskCachingMode",
"properties": {}
},
"v1.PersistentVolumeStatus": {
"id": "v1.PersistentVolumeStatus",
"description": "PersistentVolumeStatus is the current status of a persistent volume.",
@ -17986,6 +18024,10 @@
"quobyte": {
"$ref": "v1.QuobyteVolumeSource",
"description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime"
},
"azureDisk": {
"$ref": "v1.AzureDiskVolumeSource",
"description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod."
}
}
},

View File

@ -37,6 +37,7 @@ import (
"k8s.io/kubernetes/pkg/util/io"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/aws_ebs"
"k8s.io/kubernetes/pkg/volume/azure_dd"
"k8s.io/kubernetes/pkg/volume/cinder"
"k8s.io/kubernetes/pkg/volume/flexvolume"
"k8s.io/kubernetes/pkg/volume/gce_pd"
@ -60,6 +61,7 @@ func ProbeAttachableVolumePlugins(config componentconfig.VolumeConfiguration) []
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins(config.FlexVolumePluginDir)...)
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
return allPlugins
}

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_dd"
"k8s.io/kubernetes/pkg/volume/azure_file"
"k8s.io/kubernetes/pkg/volume/cephfs"
"k8s.io/kubernetes/pkg/volume/cinder"
@ -84,6 +85,7 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, configmap.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
return allPlugins
}

View File

@ -2963,6 +2963,68 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azurediskvolumesource">v1.AzureDiskVolumeSource</h3>
<div class="paragraph">
<p>AzureDisk represents an Azure Data Disk 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">diskName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Name of the data disk in the blob storage</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">diskURI</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The URI the data disk in the blob storage</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">cachingMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host Caching mode: None, Read Only, Read Write.</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_azuredatadiskcachingmode">v1.AzureDataDiskCachingMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">fsType</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</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">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_vspherevirtualdiskvolumesource">v1.VsphereVirtualDiskVolumeSource</h3>
@ -3313,6 +3375,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_quobytevolumesource">v1.QuobyteVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">azureDisk</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AzureDisk represents an Azure Data Disk 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_azurediskvolumesource">v1.AzureDiskVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -4119,6 +4188,10 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azuredatadiskcachingmode">v1.AzureDataDiskCachingMode</h3>
</div>
<div class="sect2">
<h3 id="_v1_rbdvolumesource">v1.RBDVolumeSource</h3>
@ -4214,7 +4287,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-08-23 04:34:09 UTC
Last updated 2016-08-23 13:11:42 UTC
</div>
</div>
</body>

View File

@ -2789,6 +2789,68 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azurediskvolumesource">v1.AzureDiskVolumeSource</h3>
<div class="paragraph">
<p>AzureDisk represents an Azure Data Disk 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">diskName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Name of the data disk in the blob storage</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">diskURI</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The URI the data disk in the blob storage</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">cachingMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host Caching mode: None, Read Only, Read Write.</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_azuredatadiskcachingmode">v1.AzureDataDiskCachingMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">fsType</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</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">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_keytopath">v1.KeyToPath</h3>
@ -3118,6 +3180,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_quobytevolumesource">v1.QuobyteVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">azureDisk</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AzureDisk represents an Azure Data Disk 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_azurediskvolumesource">v1.AzureDiskVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -6597,6 +6666,10 @@ Both these may change in the future. Incoming requests are matched against the h
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azuredatadiskcachingmode">v1.AzureDataDiskCachingMode</h3>
</div>
<div class="sect2">
<h3 id="_any">any</h3>
@ -6609,7 +6682,7 @@ Both these may change in the future. Incoming requests are matched against the h
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-08-23 04:33:54 UTC
Last updated 2016-08-23 13:11:31 UTC
</div>
</div>
</body>

View File

@ -3158,6 +3158,68 @@ The resulting set of endpoints can be viewed as:<br>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azurediskvolumesource">v1.AzureDiskVolumeSource</h3>
<div class="paragraph">
<p>AzureDisk represents an Azure Data Disk 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">diskName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Name of the data disk in the blob storage</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">diskURI</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The URI the data disk in the blob storage</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">cachingMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host Caching mode: None, Read Only, Read Write.</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_azuredatadiskcachingmode">v1.AzureDataDiskCachingMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">fsType</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</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">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_keytopath">v1.KeyToPath</h3>
@ -3725,6 +3787,13 @@ The resulting set of endpoints can be viewed as:<br>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_quobytevolumesource">v1.QuobyteVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">azureDisk</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AzureDisk represents an Azure Data Disk 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_azurediskvolumesource">v1.AzureDiskVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -5985,6 +6054,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">azureDisk</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AzureDisk represents an Azure Data Disk 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_azurediskvolumesource">v1.AzureDiskVolumeSource</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>
@ -8342,6 +8418,10 @@ The resulting set of endpoints can be viewed as:<br>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_azuredatadiskcachingmode">v1.AzureDataDiskCachingMode</h3>
</div>
<div class="sect2">
<h3 id="_any">any</h3>
@ -8354,7 +8434,7 @@ The resulting set of endpoints can be viewed as:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-08-23 04:33:41 UTC
Last updated 2016-08-23 13:11:18 UTC
</div>
</div>
</body>

View File

@ -404,6 +404,9 @@ func TestExampleObjectSchemas(t *testing.T) {
"../examples/volumes/azure_file": {
"azure": &api.Pod{},
},
"../examples/volumes/azure_disk": {
"azure": &api.Pod{},
},
}
capabilities.SetForTests(capabilities.Capabilities{

View File

@ -0,0 +1,51 @@
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
<!-- BEGIN STRIP_FOR_RELEASE -->
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/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?
On Azure VM, create a Pod using the volume spec based on [azure](azure.yaml).
In the pod, you need to provide the following information:
- *diskName*: (required) the name of the VHD blob object.
- *diskURI*: (required) the URI of the vhd blob object.
- *cachingMode*: (optional) disk caching mode. Must be one of None, ReadOnly, or ReadWrite. Default is None.
- *fsType*: (optional) the filesytem type to mount. Default is ext4.
- *readOnly*: (optional) whether the filesystem is used as readOnly. Default is false.
Launch the Pod:
```console
# kubectl create -f examples/volumes/azure_disk/azure.yaml
```
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/examples/volumes/azure_disk/README.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: azure
spec:
containers:
- image: kubernetes/pause
name: azure
volumeMounts:
- name: azure
mountPath: /mnt/azure
volumes:
- name: azure
azureDisk:
diskName: test.vhd
diskURI: https://someaccount.blob.microsoft.net/vhds/test.vhd

View File

@ -416,6 +416,20 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source)
types := []api.PersistentVolumeClaimPhase{api.ClaimBound, api.ClaimPending, api.ClaimLost}
pvc.Status.Phase = types[c.Rand.Intn(len(types))]
},
func(obj *api.AzureDiskVolumeSource, c fuzz.Continue) {
if obj.CachingMode == nil {
obj.CachingMode = new(api.AzureDataDiskCachingMode)
*obj.CachingMode = api.AzureDataDiskCachingNone
}
if obj.FSType == nil {
obj.FSType = new(string)
*obj.FSType = "ext4"
}
if obj.ReadOnly == nil {
obj.ReadOnly = new(bool)
*obj.ReadOnly = false
}
},
func(s *api.NamespaceSpec, c fuzz.Continue) {
s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
},

File diff suppressed because it is too large Load Diff

View File

@ -241,6 +241,8 @@ type VolumeSource struct {
ConfigMap *ConfigMapVolumeSource `json:"configMap,omitempty"`
// VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine
VsphereVolume *VsphereVirtualDiskVolumeSource `json:"vsphereVolume,omitempty"`
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
AzureDisk *AzureDiskVolumeSource `json:"azureDisk,omitempty"`
}
// Similar to VolumeSource but meant for the administrator who creates PVs.
@ -283,6 +285,8 @@ type PersistentVolumeSource struct {
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
// VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine
VsphereVolume *VsphereVirtualDiskVolumeSource `json:"vsphereVolume,omitempty"`
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
AzureDisk *AzureDiskVolumeSource `json:"azureDisk,omitempty"`
}
type PersistentVolumeClaimVolumeSource struct {
@ -793,6 +797,31 @@ type VsphereVirtualDiskVolumeSource struct {
FSType string `json:"fsType,omitempty"`
}
type AzureDataDiskCachingMode string
const (
AzureDataDiskCachingNone AzureDataDiskCachingMode = "None"
AzureDataDiskCachingReadOnly AzureDataDiskCachingMode = "ReadOnly"
AzureDataDiskCachingReadWrite AzureDataDiskCachingMode = "ReadWrite"
)
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
type AzureDiskVolumeSource struct {
// The Name of the data disk in the blob storage
DiskName string `json:"diskName"`
// The URI the the data disk in the blob storage
DataDiskURI string `json:"diskURI"`
// Host Caching mode: None, Read Only, Read Write.
CachingMode *AzureDataDiskCachingMode `json:"cachingMode,omitempty"`
// Filesystem type to mount.
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
FSType *string `json:"fsType,omitempty"`
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
ReadOnly *bool `json:"readOnly,omitempty"`
}
// Adapts a ConfigMap into a volume.
//
// The contents of the target ConfigMap's Data field will be presented in a

View File

@ -218,6 +218,20 @@ func SetDefaults_ISCSIVolumeSource(obj *ISCSIVolumeSource) {
obj.ISCSIInterface = "default"
}
}
func SetDefaults_AzureDiskVolumeSource(obj *AzureDiskVolumeSource) {
if obj.CachingMode == nil {
obj.CachingMode = new(AzureDataDiskCachingMode)
*obj.CachingMode = AzureDataDiskCachingNone
}
if obj.FSType == nil {
obj.FSType = new(string)
*obj.FSType = "ext4"
}
if obj.ReadOnly == nil {
obj.ReadOnly = new(bool)
*obj.ReadOnly = false
}
}
func SetDefaults_Endpoints(obj *Endpoints) {
for i := range obj.Subsets {
ss := &obj.Subsets[i]

File diff suppressed because it is too large Load Diff

View File

@ -89,6 +89,27 @@ message AvoidPods {
repeated PreferAvoidPodsEntry preferAvoidPods = 1;
}
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
message AzureDiskVolumeSource {
// The Name of the data disk in the blob storage
optional string diskName = 1;
// The URI the data disk in the blob storage
optional string diskURI = 2;
// Host Caching mode: None, Read Only, Read Write.
optional string cachingMode = 3;
// Filesystem type to mount.
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
optional string fsType = 4;
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
optional bool readOnly = 5;
}
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
message AzureFileVolumeSource {
// the name of secret that contains Azure Storage Account Name and Key
@ -1757,6 +1778,9 @@ message PersistentVolumeSource {
// Quobyte represents a Quobyte mount on the host that shares a pod's lifetime
optional QuobyteVolumeSource quobyte = 15;
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
optional AzureDiskVolumeSource azureDisk = 16;
}
// PersistentVolumeSpec is the specification of a persistent volume.
@ -3036,6 +3060,9 @@ message VolumeSource {
// Quobyte represents a Quobyte mount on the host that shares a pod's lifetime
optional QuobyteVolumeSource quobyte = 21;
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
optional AzureDiskVolumeSource azureDisk = 22;
}
// Represents a vSphere volume resource.

File diff suppressed because it is too large Load Diff

View File

@ -280,6 +280,8 @@ type VolumeSource struct {
VsphereVolume *VsphereVirtualDiskVolumeSource `json:"vsphereVolume,omitempty" protobuf:"bytes,20,opt,name=vsphereVolume"`
// Quobyte represents a Quobyte mount on the host that shares a pod's lifetime
Quobyte *QuobyteVolumeSource `json:"quobyte,omitempty" protobuf:"bytes,21,opt,name=quobyte"`
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
AzureDisk *AzureDiskVolumeSource `json:"azureDisk,omitempty" protobuf:"bytes,22,opt,name=azureDisk"`
}
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
@ -344,6 +346,8 @@ type PersistentVolumeSource struct {
VsphereVolume *VsphereVirtualDiskVolumeSource `json:"vsphereVolume,omitempty" protobuf:"bytes,14,opt,name=vsphereVolume"`
// Quobyte represents a Quobyte mount on the host that shares a pod's lifetime
Quobyte *QuobyteVolumeSource `json:"quobyte,omitempty" protobuf:"bytes,15,opt,name=quobyte"`
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
AzureDisk *AzureDiskVolumeSource `json:"azureDisk,omitempty" protobuf:"bytes,16,opt,name=azureDisk"`
}
// +genclient=true
@ -891,6 +895,30 @@ type VsphereVirtualDiskVolumeSource struct {
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
FSType string `json:"fsType,omitempty" protobuf:"bytes,2,opt,name=fsType"`
}
type AzureDataDiskCachingMode string
const (
AzureDataDiskCachingNone AzureDataDiskCachingMode = "None"
AzureDataDiskCachingReadOnly AzureDataDiskCachingMode = "ReadOnly"
AzureDataDiskCachingReadWrite AzureDataDiskCachingMode = "ReadWrite"
)
// AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
type AzureDiskVolumeSource struct {
// The Name of the data disk in the blob storage
DiskName string `json:"diskName" protobuf:"bytes,1,opt,name=diskName"`
// The URI the data disk in the blob storage
DataDiskURI string `json:"diskURI" protobuf:"bytes,2,opt,name=diskURI"`
// Host Caching mode: None, Read Only, Read Write.
CachingMode *AzureDataDiskCachingMode `json:"cachingMode,omitempty" protobuf:"bytes,3,opt,name=cachingMode,casttype=AzureDataDiskCachingMode"`
// Filesystem type to mount.
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
FSType *string `json:"fsType,omitempty" protobuf:"bytes,4,opt,name=fsType"`
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
ReadOnly *bool `json:"readOnly,omitempty" protobuf:"varint,5,opt,name=readOnly"`
}
// Adapts a ConfigMap into a volume.
//

View File

@ -69,6 +69,19 @@ func (AvoidPods) SwaggerDoc() map[string]string {
return map_AvoidPods
}
var map_AzureDiskVolumeSource = map[string]string{
"": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
"diskName": "The Name of the data disk in the blob storage",
"diskURI": "The URI the data disk in the blob storage",
"cachingMode": "Host Caching mode: None, Read Only, Read Write.",
"fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
}
func (AzureDiskVolumeSource) SwaggerDoc() map[string]string {
return map_AzureDiskVolumeSource
}
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",
@ -1083,6 +1096,7 @@ var map_PersistentVolumeSource = map[string]string{
"azureFile": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
"vsphereVolume": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine",
"quobyte": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime",
"azureDisk": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
}
func (PersistentVolumeSource) SwaggerDoc() map[string]string {
@ -1767,6 +1781,7 @@ var map_VolumeSource = map[string]string{
"configMap": "ConfigMap represents a configMap that should populate this volume",
"vsphereVolume": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine",
"quobyte": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime",
"azureDisk": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
}
func (VolumeSource) SwaggerDoc() map[string]string {

View File

@ -44,6 +44,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_api_AttachedVolume_To_v1_AttachedVolume,
Convert_v1_AvoidPods_To_api_AvoidPods,
Convert_api_AvoidPods_To_v1_AvoidPods,
Convert_v1_AzureDiskVolumeSource_To_api_AzureDiskVolumeSource,
Convert_api_AzureDiskVolumeSource_To_v1_AzureDiskVolumeSource,
Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource,
Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource,
Convert_v1_Binding_To_api_Binding,
@ -497,6 +499,45 @@ func Convert_api_AvoidPods_To_v1_AvoidPods(in *api.AvoidPods, out *AvoidPods, s
return autoConvert_api_AvoidPods_To_v1_AvoidPods(in, out, s)
}
func autoConvert_v1_AzureDiskVolumeSource_To_api_AzureDiskVolumeSource(in *AzureDiskVolumeSource, out *api.AzureDiskVolumeSource, s conversion.Scope) error {
SetDefaults_AzureDiskVolumeSource(in)
out.DiskName = in.DiskName
out.DataDiskURI = in.DataDiskURI
if in.CachingMode != nil {
in, out := &in.CachingMode, &out.CachingMode
*out = new(api.AzureDataDiskCachingMode)
**out = api.AzureDataDiskCachingMode(**in)
} else {
out.CachingMode = nil
}
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_v1_AzureDiskVolumeSource_To_api_AzureDiskVolumeSource(in *AzureDiskVolumeSource, out *api.AzureDiskVolumeSource, s conversion.Scope) error {
return autoConvert_v1_AzureDiskVolumeSource_To_api_AzureDiskVolumeSource(in, out, s)
}
func autoConvert_api_AzureDiskVolumeSource_To_v1_AzureDiskVolumeSource(in *api.AzureDiskVolumeSource, out *AzureDiskVolumeSource, s conversion.Scope) error {
out.DiskName = in.DiskName
out.DataDiskURI = in.DataDiskURI
if in.CachingMode != nil {
in, out := &in.CachingMode, &out.CachingMode
*out = new(AzureDataDiskCachingMode)
**out = AzureDataDiskCachingMode(**in)
} else {
out.CachingMode = nil
}
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_api_AzureDiskVolumeSource_To_v1_AzureDiskVolumeSource(in *api.AzureDiskVolumeSource, out *AzureDiskVolumeSource, s conversion.Scope) error {
return autoConvert_api_AzureDiskVolumeSource_To_v1_AzureDiskVolumeSource(in, out, s)
}
func autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *AzureFileVolumeSource, out *api.AzureFileVolumeSource, s conversion.Scope) error {
out.SecretName = in.SecretName
out.ShareName = in.ShareName
@ -4210,6 +4251,15 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *Per
} else {
out.Quobyte = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(api.AzureDiskVolumeSource)
if err := Convert_v1_AzureDiskVolumeSource_To_api_AzureDiskVolumeSource(*in, *out, s); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}
@ -4353,6 +4403,15 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
} else {
out.VsphereVolume = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(AzureDiskVolumeSource)
if err := Convert_api_AzureDiskVolumeSource_To_v1_AzureDiskVolumeSource(*in, *out, s); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}
@ -6803,6 +6862,15 @@ func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.
} else {
out.Quobyte = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(api.AzureDiskVolumeSource)
if err := Convert_v1_AzureDiskVolumeSource_To_api_AzureDiskVolumeSource(*in, *out, s); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}
@ -7000,6 +7068,15 @@ func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *
} else {
out.VsphereVolume = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(AzureDiskVolumeSource)
if err := Convert_api_AzureDiskVolumeSource_To_v1_AzureDiskVolumeSource(*in, *out, s); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}

View File

@ -40,6 +40,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Affinity, InType: reflect.TypeOf(&Affinity{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_AttachedVolume, InType: reflect.TypeOf(&AttachedVolume{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_AvoidPods, InType: reflect.TypeOf(&AvoidPods{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_AzureDiskVolumeSource, InType: reflect.TypeOf(&AzureDiskVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_AzureFileVolumeSource, InType: reflect.TypeOf(&AzureFileVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Binding, InType: reflect.TypeOf(&Binding{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Capabilities, InType: reflect.TypeOf(&Capabilities{})},
@ -268,6 +269,37 @@ func DeepCopy_v1_AvoidPods(in interface{}, out interface{}, c *conversion.Cloner
}
}
func DeepCopy_v1_AzureDiskVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AzureDiskVolumeSource)
out := out.(*AzureDiskVolumeSource)
out.DiskName = in.DiskName
out.DataDiskURI = in.DataDiskURI
if in.CachingMode != nil {
in, out := &in.CachingMode, &out.CachingMode
*out = new(AzureDataDiskCachingMode)
**out = **in
} else {
out.CachingMode = nil
}
if in.FSType != nil {
in, out := &in.FSType, &out.FSType
*out = new(string)
**out = **in
} else {
out.FSType = nil
}
if in.ReadOnly != nil {
in, out := &in.ReadOnly, &out.ReadOnly
*out = new(bool)
**out = **in
} else {
out.ReadOnly = nil
}
return nil
}
}
func DeepCopy_v1_AzureFileVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AzureFileVolumeSource)
@ -2188,6 +2220,15 @@ func DeepCopy_v1_PersistentVolumeSource(in interface{}, out interface{}, c *conv
} else {
out.Quobyte = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(AzureDiskVolumeSource)
if err := DeepCopy_v1_AzureDiskVolumeSource(*in, *out, c); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}
}
@ -3626,6 +3667,15 @@ func DeepCopy_v1_VolumeSource(in interface{}, out interface{}, c *conversion.Clo
} else {
out.Quobyte = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(AzureDiskVolumeSource)
if err := DeepCopy_v1_AzureDiskVolumeSource(*in, *out, c); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}
}

View File

@ -631,6 +631,11 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
allErrs = append(allErrs, validateVsphereVolumeSource(source.VsphereVolume, fldPath.Child("vsphereVolume"))...)
}
}
if source.AzureDisk != nil {
numVolumes++
allErrs = append(allErrs, validateAzureDisk(source.AzureDisk, fldPath.Child("azureDisk"))...)
}
if numVolumes == 0 {
allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
}
@ -941,6 +946,22 @@ func validateAzureFile(azure *api.AzureFileVolumeSource, fldPath *field.Path) fi
return allErrs
}
var supportedCachingModes = sets.NewString(string(api.AzureDataDiskCachingNone), string(api.AzureDataDiskCachingReadOnly), string(api.AzureDataDiskCachingReadWrite))
func validateAzureDisk(azure *api.AzureDiskVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if azure.DiskName == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("diskName"), ""))
}
if azure.DataDiskURI == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("diskURI"), ""))
}
if azure.CachingMode != nil && !supportedCachingModes.Has(string(*azure.CachingMode)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("cachingMode"), *azure.CachingMode, supportedCachingModes.List()))
}
return allErrs
}
func validateVsphereVolumeSource(cd *api.VsphereVirtualDiskVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(cd.VolumePath) == 0 {
@ -1093,6 +1114,11 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
allErrs = append(allErrs, validateVsphereVolumeSource(pv.Spec.VsphereVolume, specPath.Child("vsphereVolume"))...)
}
}
if pv.Spec.AzureDisk != nil {
numVolumes++
allErrs = append(allErrs, validateAzureDisk(pv.Spec.AzureDisk, specPath.Child("azureDisk"))...)
}
if numVolumes == 0 {
allErrs = append(allErrs, field.Required(specPath, "must specify a volume type"))
}

View File

@ -2063,6 +2063,47 @@ func TestValidateVolumes(t *testing.T) {
errtype: field.ErrorTypeRequired,
errfield: "quobyte.volume",
},
// AzureDisk
{
name: "valid AzureDisk",
vol: api.Volume{
Name: "azure-disk",
VolumeSource: api.VolumeSource{
AzureDisk: &api.AzureDiskVolumeSource{
DiskName: "foo",
DataDiskURI: "https://blob/vhds/bar.vhd",
},
},
},
},
{
name: "AzureDisk empty disk name",
vol: api.Volume{
Name: "azure-disk",
VolumeSource: api.VolumeSource{
AzureDisk: &api.AzureDiskVolumeSource{
DiskName: "",
DataDiskURI: "https://blob/vhds/bar.vhd",
},
},
},
errtype: field.ErrorTypeRequired,
errfield: "azureDisk.diskName",
},
{
name: "AzureDisk empty disk uri",
vol: api.Volume{
Name: "azure-disk",
VolumeSource: api.VolumeSource{
AzureDisk: &api.AzureDiskVolumeSource{
DiskName: "foo",
DataDiskURI: "",
},
},
},
errtype: field.ErrorTypeRequired,
errfield: "azureDisk.diskURI",
},
}
for i, tc := range testCases {

View File

@ -42,6 +42,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Affinity, InType: reflect.TypeOf(&Affinity{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_AttachedVolume, InType: reflect.TypeOf(&AttachedVolume{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_AvoidPods, InType: reflect.TypeOf(&AvoidPods{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_AzureDiskVolumeSource, InType: reflect.TypeOf(&AzureDiskVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_AzureFileVolumeSource, InType: reflect.TypeOf(&AzureFileVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Binding, InType: reflect.TypeOf(&Binding{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Capabilities, InType: reflect.TypeOf(&Capabilities{})},
@ -272,6 +273,37 @@ func DeepCopy_api_AvoidPods(in interface{}, out interface{}, c *conversion.Clone
}
}
func DeepCopy_api_AzureDiskVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AzureDiskVolumeSource)
out := out.(*AzureDiskVolumeSource)
out.DiskName = in.DiskName
out.DataDiskURI = in.DataDiskURI
if in.CachingMode != nil {
in, out := &in.CachingMode, &out.CachingMode
*out = new(AzureDataDiskCachingMode)
**out = **in
} else {
out.CachingMode = nil
}
if in.FSType != nil {
in, out := &in.FSType, &out.FSType
*out = new(string)
**out = **in
} else {
out.FSType = nil
}
if in.ReadOnly != nil {
in, out := &in.ReadOnly, &out.ReadOnly
*out = new(bool)
**out = **in
} else {
out.ReadOnly = nil
}
return nil
}
}
func DeepCopy_api_AzureFileVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*AzureFileVolumeSource)
@ -2246,6 +2278,15 @@ func DeepCopy_api_PersistentVolumeSource(in interface{}, out interface{}, c *con
} else {
out.VsphereVolume = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(AzureDiskVolumeSource)
if err := DeepCopy_api_AzureDiskVolumeSource(*in, *out, c); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}
}
@ -3661,6 +3702,15 @@ func DeepCopy_api_VolumeSource(in interface{}, out interface{}, c *conversion.Cl
} else {
out.VsphereVolume = nil
}
if in.AzureDisk != nil {
in, out := &in.AzureDisk, &out.AzureDisk
*out = new(AzureDiskVolumeSource)
if err := DeepCopy_api_AzureDiskVolumeSource(*in, *out, c); err != nil {
return err
}
} else {
out.AzureDisk = nil
}
return nil
}
}

View File

@ -718,6 +718,7 @@ var (
ConfigMap FSType = "configMap"
VsphereVolume FSType = "vsphereVolume"
Quobyte FSType = "quobyte"
AzureDisk FSType = "azureDisk"
All FSType = "*"
)

View File

@ -996,6 +996,7 @@ var (
FC FSType = "fc"
ConfigMap FSType = "configMap"
Quobyte FSType = "quobyte"
AzureDisk FSType = "azureDisk"
All FSType = "*"
)

View File

@ -0,0 +1,153 @@
/*
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
import (
"fmt"
"strings"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/cloudprovider"
)
const (
maxLUN = 64 // max number of LUNs per VM
)
// AttachDisk attaches a vhd to vm
// the vhd must exist, can be identified by diskName, diskURI, and lun.
func (az *Cloud) AttachDisk(diskName, diskURI, vmName string, lun int32, cachingMode compute.CachingTypes) error {
vm, exists, err := az.getVirtualMachine(vmName)
if err != nil {
return err
} else if !exists {
return cloudprovider.InstanceNotFound
}
disks := *vm.Properties.StorageProfile.DataDisks
disks = append(disks,
compute.DataDisk{
Name: &diskName,
Vhd: &compute.VirtualHardDisk{
URI: &diskURI,
},
Lun: &lun,
Caching: cachingMode,
CreateOption: "attach",
})
newVM := compute.VirtualMachine{
Location: vm.Location,
Properties: &compute.VirtualMachineProperties{
StorageProfile: &compute.StorageProfile{
DataDisks: &disks,
},
},
}
_, err = az.VirtualMachinesClient.CreateOrUpdate(az.ResourceGroup, vmName, newVM, nil)
if err != nil {
glog.Errorf("azure attach failed, err: %v", err)
detail := err.Error()
if strings.Contains(detail, "Code=\"AcquireDiskLeaseFailed\"") {
// if lease cannot be acquired, immediately detach the disk and return the original error
glog.Infof("failed to acquire disk lease, try detach")
az.DetachDiskByName(diskName, diskURI, vmName)
}
} else {
glog.V(4).Infof("azure attach succeeded")
}
return err
}
// DetachDiskByName detaches a vhd from host
// the vhd can be identified by diskName or diskURI
func (az *Cloud) DetachDiskByName(diskName, diskURI, vmName string) error {
vm, exists, err := az.getVirtualMachine(vmName)
if err != nil || !exists {
// if host doesn't exist, no need to detach
glog.Warningf("cannot find node %s, skip detaching disk %s", vmName, diskName)
return nil
}
disks := *vm.Properties.StorageProfile.DataDisks
for i, disk := range disks {
if (disk.Name != nil && diskName != "" && *disk.Name == diskName) || (disk.Vhd.URI != nil && diskURI != "" && *disk.Vhd.URI == diskURI) {
// found the disk
glog.V(4).Infof("detach disk: name %q uri %q", diskName, diskURI)
disks = append(disks[:i], disks[i+1:]...)
break
}
}
newVM := compute.VirtualMachine{
Location: vm.Location,
Properties: &compute.VirtualMachineProperties{
StorageProfile: &compute.StorageProfile{
DataDisks: &disks,
},
},
}
_, err = az.VirtualMachinesClient.CreateOrUpdate(az.ResourceGroup, vmName, newVM, nil)
if err != nil {
glog.Errorf("azure disk detach failed, err: %v", err)
} else {
glog.V(4).Infof("azure disk detach succeeded")
}
return err
}
// GetDiskLun finds the lun on the host that the vhd is attached to, given a vhd's diskName and diskURI
func (az *Cloud) GetDiskLun(diskName, diskURI, vmName string) (int32, error) {
vm, exists, err := az.getVirtualMachine(vmName)
if err != nil {
return -1, err
} else if !exists {
return -1, cloudprovider.InstanceNotFound
}
disks := *vm.Properties.StorageProfile.DataDisks
for _, disk := range disks {
if disk.Lun != nil && (disk.Name != nil && diskName != "" && *disk.Name == diskName) || (disk.Vhd.URI != nil && diskURI != "" && *disk.Vhd.URI == diskURI) {
// found the disk
glog.V(4).Infof("find disk: lun %d name %q uri %q", *disk.Lun, diskName, diskURI)
return *disk.Lun, nil
}
}
return -1, fmt.Errorf("Cannot find Lun for disk %s", diskName)
}
// GetNextDiskLun searches all vhd attachment on the host and find unused lun
// return -1 if all luns are used
func (az *Cloud) GetNextDiskLun(vmName string) (int32, error) {
vm, exists, err := az.getVirtualMachine(vmName)
if err != nil {
return -1, err
} else if !exists {
return -1, cloudprovider.InstanceNotFound
}
used := make([]bool, maxLUN)
disks := *vm.Properties.StorageProfile.DataDisks
for _, disk := range disks {
if disk.Lun != nil {
used[*disk.Lun] = true
}
}
for k, v := range used {
if !v {
return int32(k), nil
}
}
return -1, fmt.Errorf("All Luns are used")
}

View File

@ -608,6 +608,8 @@ func describeVolumes(volumes []api.Volume, out io.Writer, space string) {
printQuobyteVolumeSource(volume.VolumeSource.Quobyte, out)
case volume.VolumeSource.DownwardAPI != nil:
printDownwardAPIVolumeSource(volume.VolumeSource.DownwardAPI, out)
case volume.VolumeSource.AzureDisk != nil:
printAzureDiskVolumeSource(volume.VolumeSource.AzureDisk, out)
default:
fmt.Fprintf(out, " <unknown>\n")
}
@ -726,6 +728,16 @@ func printDownwardAPIVolumeSource(d *api.DownwardAPIVolumeSource, out io.Writer)
}
}
func printAzureDiskVolumeSource(d *api.AzureDiskVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tAzureDisk (an Azure Data Disk mount on the host and bind mount to the pod)\n"+
" DiskName:\t%v\n"+
" DiskURI:\t%v\n"+
" FSType:\t%v\n"+
" CachingMode:\t%v\n"+
" ReadOnly:\t%v\n",
d.DiskName, d.DataDiskURI, *d.FSType, *d.CachingMode, *d.ReadOnly)
}
type PersistentVolumeDescriber struct {
client.Interface
}

View File

@ -59,7 +59,8 @@ func GetAllFSTypesAsSet() sets.String {
string(extensions.FC),
string(extensions.ConfigMap),
string(extensions.VsphereVolume),
string(extensions.Quobyte))
string(extensions.Quobyte),
string(extensions.AzureDisk))
return fstypes
}
@ -108,6 +109,8 @@ func GetVolumeFSType(v api.Volume) (extensions.FSType, error) {
return extensions.VsphereVolume, nil
case v.Quobyte != nil:
return extensions.Quobyte, nil
case v.AzureDisk != nil:
return extensions.AzureDisk, nil
}
return "", fmt.Errorf("unknown volume type for volume: %#v", v)

View File

@ -0,0 +1,261 @@
/*
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_dd
import (
"fmt"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/keymutex"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
)
type azureDiskAttacher struct {
host volume.VolumeHost
azureProvider azureCloudProvider
}
var _ volume.Attacher = &azureDiskAttacher{}
var _ volume.AttachableVolumePlugin = &azureDataDiskPlugin{}
const (
checkSleepDuration = time.Second
)
// acquire lock to get an lun number
var getLunMutex = keymutex.NewKeyMutex()
// NewAttacher initializes an Attacher
func (plugin *azureDataDiskPlugin) NewAttacher() (volume.Attacher, error) {
azure, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
if err != nil {
glog.V(4).Infof("failed to get azure provider")
return nil, err
}
return &azureDiskAttacher{
host: plugin.host,
azureProvider: azure,
}, nil
}
// Attach attaches a volume.Spec to a Azure VM referenced by hostname, returning the disk's LUN
func (attacher *azureDiskAttacher) Attach(spec *volume.Spec, hostName string) (string, error) {
volumeSource, err := getVolumeSource(spec)
if err != nil {
glog.Warningf("failed to get azure disk spec")
return "", err
}
instanceid, err := attacher.azureProvider.InstanceID(hostName)
if err != nil {
glog.Warningf("failed to get azure instance id")
return "", fmt.Errorf("failed to get azure instance id for host %q", hostName)
}
if ind := strings.LastIndex(instanceid, "/"); ind >= 0 {
instanceid = instanceid[(ind + 1):]
}
lun, err := attacher.azureProvider.GetDiskLun(volumeSource.DiskName, volumeSource.DataDiskURI, instanceid)
if err == cloudprovider.InstanceNotFound {
// Log error and continue with attach
glog.Warningf(
"Error checking if volume is already attached to current node (%q). Will continue and try attach anyway. err=%v",
instanceid, err)
}
if err == nil {
// Volume is already attached to node.
glog.V(4).Infof("Attach operation is successful. volume %q is already attached to node %q at lun %d.", volumeSource.DiskName, instanceid, lun)
} else {
getLunMutex.LockKey(instanceid)
defer getLunMutex.UnlockKey(instanceid)
lun, err = attacher.azureProvider.GetNextDiskLun(instanceid)
if err != nil {
glog.Warningf("no LUN available for instance %q", instanceid)
return "", fmt.Errorf("all LUNs are used, cannot attach volume %q to instance %q", volumeSource.DiskName, instanceid)
}
err = attacher.azureProvider.AttachDisk(volumeSource.DiskName, volumeSource.DataDiskURI, instanceid, lun, compute.CachingTypes(*volumeSource.CachingMode))
if err == nil {
glog.V(4).Infof("Attach operation successful: volume %q attached to node %q.", volumeSource.DataDiskURI, instanceid)
} else {
glog.V(2).Infof("Attach volume %q to instance %q failed with %v", volumeSource.DataDiskURI, instanceid, err)
return "", fmt.Errorf("Attach volume %q to instance %q failed with %v", volumeSource.DiskName, instanceid, err)
}
}
return strconv.Itoa(int(lun)), err
}
// WaitForAttach runs on the node to detect if the volume (referenced by LUN) is attached. If attached, the device path is returned
func (attacher *azureDiskAttacher) WaitForAttach(spec *volume.Spec, lunStr string, timeout time.Duration) (string, error) {
volumeSource, err := getVolumeSource(spec)
if err != nil {
return "", err
}
if len(lunStr) == 0 {
return "", fmt.Errorf("WaitForAttach failed for Azure disk %q: lun is empty.", volumeSource.DiskName)
}
lun, err := strconv.Atoi(lunStr)
if err != nil {
return "", fmt.Errorf("WaitForAttach: wrong lun %q, err: %v", lunStr, err)
}
scsiHostRescan(&osIOHandler{})
exe := exec.New()
devicePath := ""
err = wait.Poll(checkSleepDuration, timeout, func() (bool, error) {
glog.V(4).Infof("Checking Azure disk %q(lun %s) is attached.", volumeSource.DiskName, lunStr)
if devicePath, err = findDiskByLun(lun, &osIOHandler{}, exe); err == nil {
glog.V(4).Infof("Successfully found attached Azure disk %q(lun %s, device path %s).", volumeSource.DiskName, lunStr, devicePath)
return true, nil
} else {
//Log error, if any, and continue checking periodically
glog.V(4).Infof("Error Stat Azure disk (%q) is attached: %v", volumeSource.DiskName, err)
return false, nil
}
})
return devicePath, err
}
// GetDeviceMountPath finds the volume's mount path on the node
func (attacher *azureDiskAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
volumeSource, err := getVolumeSource(spec)
if err != nil {
return "", err
}
return makeGlobalPDPath(attacher.host, volumeSource.DiskName), nil
}
// MountDevice runs mount command on the node to mount the volume
func (attacher *azureDiskAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
mounter := attacher.host.GetMounter()
notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
return err
}
notMnt = true
} else {
return err
}
}
volumeSource, err := getVolumeSource(spec)
if err != nil {
return err
}
options := []string{}
if spec.ReadOnly {
options = append(options, "ro")
}
if notMnt {
diskMounter := &mount.SafeFormatAndMount{Interface: mounter, Runner: exec.New()}
err = diskMounter.FormatAndMount(devicePath, deviceMountPath, *volumeSource.FSType, options)
if err != nil {
os.Remove(deviceMountPath)
return err
}
}
return nil
}
type azureDiskDetacher struct {
mounter mount.Interface
azureProvider azureCloudProvider
}
var _ volume.Detacher = &azureDiskDetacher{}
// NewDetacher initializes a volume Detacher
func (plugin *azureDataDiskPlugin) NewDetacher() (volume.Detacher, error) {
azure, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
if err != nil {
return nil, err
}
return &azureDiskDetacher{
mounter: plugin.host.GetMounter(),
azureProvider: azure,
}, nil
}
// Detach detaches disk from Azure VM.
func (detacher *azureDiskDetacher) Detach(diskName string, hostName string) error {
if diskName == "" {
return fmt.Errorf("invalid disk to detach: %q", diskName)
}
instanceid, err := detacher.azureProvider.InstanceID(hostName)
if err != nil {
glog.Warningf("no instance id for host %q, skip detaching", hostName)
return nil
}
if ind := strings.LastIndex(instanceid, "/"); ind >= 0 {
instanceid = instanceid[(ind + 1):]
}
glog.V(4).Infof("detach %v from host %q", diskName, instanceid)
err = detacher.azureProvider.DetachDiskByName(diskName, "" /* diskURI */, instanceid)
if err != nil {
glog.Errorf("failed to detach azure disk %q, err %v", diskName, err)
}
return err
}
// WaitForDetach detects if the disk is detached on the node
func (detacher *azureDiskDetacher) WaitForDetach(devicePath string, timeout time.Duration) error {
return wait.Poll(checkSleepDuration, timeout, func() (bool, error) {
glog.V(4).Infof("Checking device %q is detached.", devicePath)
if pathExists, err := util.PathExists(devicePath); err != nil {
return false, fmt.Errorf("Error checking if device path exists: %v", err)
} else if !pathExists {
return true, nil
} else {
return false, nil
}
})
}
// UnmountDevice unmounts the volume on the node
func (detacher *azureDiskDetacher) UnmountDevice(deviceMountPath string) error {
volume := path.Base(deviceMountPath)
if err := util.UnmountPath(deviceMountPath, detacher.mounter); err != nil {
glog.Errorf("Error unmounting %q: %v", volume, err)
return err
} else {
return nil
}
}

View File

@ -0,0 +1,359 @@
/*
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_dd
import (
"fmt"
"os"
"path"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/keymutex"
"k8s.io/kubernetes/pkg/util/mount"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
)
// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
return []volume.VolumePlugin{&azureDataDiskPlugin{}}
}
type azureDataDiskPlugin struct {
host volume.VolumeHost
volumeLocks keymutex.KeyMutex
}
// Abstract interface to disk operations.
// azure cloud provider should implement it
type azureCloudProvider interface {
// Attaches the disk to the host machine.
AttachDisk(diskName, diskUri, vmName string, lun int32, cachingMode compute.CachingTypes) error
// Detaches the disk, identified by disk name or uri, from the host machine.
DetachDiskByName(diskName, diskUri, vmName string) error
// Get the LUN number of the disk that is attached to the host
GetDiskLun(diskName, diskUri, vmName string) (int32, error)
// Get the next available LUN number to attach a new VHD
GetNextDiskLun(vmName string) (int32, error)
// InstanceID returns the cloud provider ID of the specified instance.
InstanceID(name string) (string, error)
}
var _ volume.VolumePlugin = &azureDataDiskPlugin{}
var _ volume.PersistentVolumePlugin = &azureDataDiskPlugin{}
const (
azureDataDiskPluginName = "kubernetes.io/azure-disk"
)
func (plugin *azureDataDiskPlugin) Init(host volume.VolumeHost) error {
plugin.host = host
plugin.volumeLocks = keymutex.NewKeyMutex()
return nil
}
func (plugin *azureDataDiskPlugin) GetPluginName() string {
return azureDataDiskPluginName
}
func (plugin *azureDataDiskPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
volumeSource, err := getVolumeSource(spec)
if err != nil {
return "", err
}
return volumeSource.DiskName, nil
}
func (plugin *azureDataDiskPlugin) CanSupport(spec *volume.Spec) bool {
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureDisk != nil) ||
(spec.Volume != nil && spec.Volume.AzureDisk != nil)
}
func (plugin *azureDataDiskPlugin) RequiresRemount() bool {
return false
}
func (plugin *azureDataDiskPlugin) GetAccessModes() []api.PersistentVolumeAccessMode {
return []api.PersistentVolumeAccessMode{
api.ReadWriteOnce,
}
}
func (plugin *azureDataDiskPlugin) NewMounter(spec *volume.Spec, pod *api.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
return plugin.newMounterInternal(spec, pod.UID, plugin.host.GetMounter())
}
func (plugin *azureDataDiskPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, mounter mount.Interface) (volume.Mounter, error) {
// azures used directly in a pod have a ReadOnly flag set by the pod author.
// azures used as a PersistentVolume gets the ReadOnly flag indirectly through the persistent-claim volume used to mount the PV
azure, err := getVolumeSource(spec)
if err != nil {
return nil, err
}
fsType := *azure.FSType
diskName := azure.DiskName
diskUri := azure.DataDiskURI
cachingMode := *azure.CachingMode
return &azureDiskMounter{
azureDisk: &azureDisk{
podUID: podUID,
volName: spec.Name(),
diskName: diskName,
diskUri: diskUri,
cachingMode: cachingMode,
mounter: mounter,
plugin: plugin,
},
fsType: fsType,
readOnly: *azure.ReadOnly,
diskMounter: &mount.SafeFormatAndMount{Interface: plugin.host.GetMounter(), Runner: exec.New()}}, nil
}
func (plugin *azureDataDiskPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter())
}
func (plugin *azureDataDiskPlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Unmounter, error) {
return &azureDiskUnmounter{
&azureDisk{
podUID: podUID,
volName: volName,
mounter: mounter,
plugin: plugin,
},
}, nil
}
func (plugin *azureDataDiskPlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter()
pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
if err != nil {
return nil, err
}
azVolume := &api.Volume{
Name: volName,
VolumeSource: api.VolumeSource{
AzureDisk: &api.AzureDiskVolumeSource{
DiskName: sourceName,
},
},
}
return volume.NewSpecFromVolume(azVolume), nil
}
func (plugin *azureDataDiskPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
mounter := plugin.host.GetMounter()
return mount.GetMountRefs(mounter, deviceMountPath)
}
type azureDisk struct {
volName string
podUID types.UID
diskName string
diskUri string
cachingMode api.AzureDataDiskCachingMode
mounter mount.Interface
plugin *azureDataDiskPlugin
volume.MetricsNil
}
type azureDiskMounter struct {
*azureDisk
// Filesystem type, optional.
fsType string
// Specifies whether the disk will be attached as read-only.
readOnly bool
// diskMounter provides the interface that is used to mount the actual block device.
diskMounter *mount.SafeFormatAndMount
}
var _ volume.Mounter = &azureDiskMounter{}
func (b *azureDiskMounter) GetAttributes() volume.Attributes {
return volume.Attributes{
ReadOnly: b.readOnly,
Managed: !b.readOnly,
SupportsSELinux: true,
}
}
// SetUp attaches the disk and bind mounts to the volume path.
func (b *azureDiskMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup)
}
// SetUpAt attaches the disk and bind mounts to the volume path.
func (b *azureDiskMounter) SetUpAt(dir string, fsGroup *int64) error {
b.plugin.volumeLocks.LockKey(b.diskName)
defer b.plugin.volumeLocks.UnlockKey(b.diskName)
// TODO: handle failed mounts here.
notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
glog.V(4).Infof("DataDisk set up: %s %v %v", dir, !notMnt, err)
if err != nil && !os.IsNotExist(err) {
glog.Errorf("IsLikelyNotMountPoint failed: %v", err)
return err
}
if !notMnt {
glog.V(4).Infof("%s is a mount point", dir)
return nil
}
globalPDPath := makeGlobalPDPath(b.plugin.host, b.diskName)
if err := os.MkdirAll(dir, 0750); err != nil {
glog.V(4).Infof("Could not create directory %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")
}
err = b.mounter.Mount(globalPDPath, 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)
return err
}
if !b.readOnly {
volume.SetVolumeOwnership(b, fsGroup)
}
glog.V(3).Infof("Azure disk volume %s mounted to %s", b.diskName, dir)
return nil
}
func makeGlobalPDPath(host volume.VolumeHost, volume string) string {
return path.Join(host.GetPluginDir(azureDataDiskPluginName), "mounts", volume)
}
func (azure *azureDisk) GetPath() string {
name := azureDataDiskPluginName
return azure.plugin.host.GetPodVolumeDir(azure.podUID, utilstrings.EscapeQualifiedNameForDisk(name), azure.volName)
}
type azureDiskUnmounter struct {
*azureDisk
}
var _ volume.Unmounter = &azureDiskUnmounter{}
// 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 *azureDiskUnmounter) TearDown() error {
return c.TearDownAt(c.GetPath())
}
// 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 *azureDiskUnmounter) TearDownAt(dir string) error {
notMnt, err := c.mounter.IsLikelyNotMountPoint(dir)
if err != nil {
glog.Errorf("Error checking if mountpoint %s: %v", dir, err)
return err
}
if notMnt {
glog.V(2).Info("Not mountpoint, deleting")
return os.Remove(dir)
}
// lock the volume (and thus wait for any concurrrent SetUpAt to finish)
c.plugin.volumeLocks.LockKey(c.diskName)
defer c.plugin.volumeLocks.UnlockKey(c.diskName)
refs, err := mount.GetMountRefs(c.mounter, dir)
if err != nil {
glog.Errorf("Error getting mountrefs for %s: %v", dir, err)
return err
}
if len(refs) == 0 {
glog.Errorf("Did not find pod-mount for %s during tear down", dir)
return fmt.Errorf("%s is not mounted", dir)
}
c.diskName = path.Base(refs[0])
glog.V(4).Infof("Found volume %s mounted to %s", c.diskName, dir)
// Unmount the bind-mount inside this pod
if err := c.mounter.Unmount(dir); err != nil {
glog.Errorf("Error unmounting dir %s %v", dir, err)
return err
}
notMnt, mntErr := c.mounter.IsLikelyNotMountPoint(dir)
if mntErr != nil {
glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
return err
}
if notMnt {
if err := os.Remove(dir); err != nil {
glog.Errorf("Error removing mountpoint %s %v", dir, err)
return err
}
}
return nil
}
func getVolumeSource(spec *volume.Spec) (*api.AzureDiskVolumeSource, error) {
if spec.Volume != nil && spec.Volume.AzureDisk != nil {
return spec.Volume.AzureDisk, nil
}
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureDisk != nil {
return spec.PersistentVolume.Spec.AzureDisk, nil
}
return nil, fmt.Errorf("Spec does not reference an Azure disk volume type")
}
// 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,169 @@
/*
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 azure_dd
import (
"fmt"
"os"
"path"
"testing"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
utiltesting "k8s.io/kubernetes/pkg/util/testing"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
)
func TestCanSupport(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("azure_dd")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil, "" /* rootContext */))
plug, err := plugMgr.FindPluginByName(azureDataDiskPluginName)
if err != nil {
t.Errorf("Can't find the plugin by name")
}
if plug.GetPluginName() != azureDataDiskPluginName {
t.Errorf("Wrong name: %s", plug.GetPluginName())
}
if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{AzureDisk: &api.AzureDiskVolumeSource{}}}}) {
t.Errorf("Expected true")
}
if !plug.CanSupport(&volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{AzureDisk: &api.AzureDiskVolumeSource{}}}}}) {
t.Errorf("Expected true")
}
}
const (
fakeDiskName = "foo"
fakeDiskUri = "https://azure/vhds/bar.vhd"
fakeLun = 2
)
type fakeAzureProvider struct {
}
func (fake *fakeAzureProvider) AttachDisk(diskName, diskUri, vmName string, lun int32, cachingMode compute.CachingTypes) error {
if diskName != fakeDiskName || diskUri != fakeDiskUri || lun != fakeLun {
return fmt.Errorf("wrong disk")
}
return nil
}
func (fake *fakeAzureProvider) DetachDiskByName(diskName, diskUri, vmName string) error {
if diskName != fakeDiskName || diskUri != fakeDiskUri {
return fmt.Errorf("wrong disk")
}
return nil
}
func (fake *fakeAzureProvider) GetDiskLun(diskName, diskUri, vmName string) (int32, error) {
return int32(fakeLun), nil
}
func (fake *fakeAzureProvider) GetNextDiskLun(vmName string) (int32, error) {
return fakeLun, nil
}
func (fake *fakeAzureProvider) InstanceID(name string) (string, error) {
return "localhost", nil
}
func TestPlugin(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("azure_ddTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil, "" /* rootContext */))
plug, err := plugMgr.FindPluginByName(azureDataDiskPluginName)
if err != nil {
t.Errorf("Can't find the plugin by name")
}
fs := "ext4"
ro := false
caching := api.AzureDataDiskCachingNone
spec := &api.Volume{
Name: "vol1",
VolumeSource: api.VolumeSource{
AzureDisk: &api.AzureDiskVolumeSource{
DiskName: fakeDiskName,
DataDiskURI: fakeDiskUri,
FSType: &fs,
CachingMode: &caching,
ReadOnly: &ro,
},
},
}
mounter, err := plug.(*azureDataDiskPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), &mount.FakeMounter{})
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-disk/vol1")
path := mounter.GetPath()
if path != volPath {
t.Errorf("Got unexpected path: %s, should be %s", path, volPath)
}
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.(*azureDataDiskPlugin).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("SetUp() failed: %v", err)
}
}

View File

@ -0,0 +1,117 @@
/*
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_dd
import (
"io/ioutil"
"os"
"path"
"regexp"
"strconv"
"strings"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/util/exec"
)
type ioHandler interface {
ReadDir(dirname string) ([]os.FileInfo, error)
WriteFile(filename string, data []byte, perm os.FileMode) error
}
type osIOHandler struct{}
func (handler *osIOHandler) ReadDir(dirname string) ([]os.FileInfo, error) {
return ioutil.ReadDir(dirname)
}
func (handler *osIOHandler) WriteFile(filename string, data []byte, perm os.FileMode) error {
return ioutil.WriteFile(filename, data, perm)
}
// given a LUN find the VHD device path like /dev/sdb
// VHD disks under sysfs are like /sys/bus/scsi/devices/3:0:1:0
// return empty string if no disk is found
func findDiskByLun(lun int, io ioHandler, exe exec.Interface) (string, error) {
var err error
sys_path := "/sys/bus/scsi/devices"
if dirs, err := io.ReadDir(sys_path); err == nil {
for _, f := range dirs {
name := f.Name()
// look for path like /sys/bus/scsi/devices/3:0:1:0
arr := strings.Split(name, ":")
if len(arr) < 4 {
continue
}
target, err := strconv.Atoi(arr[0])
if err != nil {
glog.Errorf("failed to parse target from %v (%v), err %v", arr[0], name, err)
continue
}
// as observed, targets 0-3 are used by OS disks. Skip them
if target > 3 {
l, err := strconv.Atoi(arr[3])
if err != nil {
// unknown path format, continue to read the next one
glog.Errorf("failed to parse lun from %v (%v), err %v", arr[3], name, err)
continue
}
if lun == l {
// find the matching LUN
// read vendor and model to ensure it is a VHD disk
vendor := path.Join(sys_path, name, "vendor")
model := path.Join(sys_path, name, "model")
out, err := exe.Command("cat", vendor, model).CombinedOutput()
if err != nil {
glog.Errorf("failed to cat device vendor and model, err: %v", err)
continue
}
matched, err := regexp.MatchString("^MSFT[ ]{0,}\nVIRTUAL DISK[ ]{0,}\n$", strings.ToUpper(string(out)))
if err != nil || !matched {
glog.V(4).Infof("doesn't match VHD, output %v, error %v", string(out), err)
continue
}
// find it!
dir := path.Join(sys_path, name, "block")
dev, err := io.ReadDir(dir)
if err != nil {
glog.Errorf("failed to read %s", dir)
} else {
return "/dev/" + dev[0].Name(), nil
}
}
}
}
}
return "", err
}
// rescan scsi bus
func scsiHostRescan(io ioHandler) {
scsi_path := "/sys/class/scsi_host/"
if dirs, err := io.ReadDir(scsi_path); err == nil {
for _, f := range dirs {
name := scsi_path + f.Name() + "/scan"
data := []byte("- - -")
if err = io.WriteFile(name, data, 0666); err != nil {
glog.Errorf("failed to rescan scsi host %s", name)
}
}
} else {
glog.Errorf("failed to read %s, err %v", scsi_path, err)
}
}

View File

@ -0,0 +1,116 @@
/*
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_dd
import (
"fmt"
"os"
"testing"
"time"
"k8s.io/kubernetes/pkg/util/exec"
)
type fakeFileInfo struct {
name string
}
func (fi *fakeFileInfo) Name() string {
return fi.name
}
func (fi *fakeFileInfo) Size() int64 {
return 0
}
func (fi *fakeFileInfo) Mode() os.FileMode {
return 777
}
func (fi *fakeFileInfo) ModTime() time.Time {
return time.Now()
}
func (fi *fakeFileInfo) IsDir() bool {
return false
}
func (fi *fakeFileInfo) Sys() interface{} {
return nil
}
var (
lun = 1
lunStr = "1"
diskPath = "4:0:0:" + lunStr
devName = "sda"
)
type fakeIOHandler struct{}
func (handler *fakeIOHandler) ReadDir(dirname string) ([]os.FileInfo, error) {
switch dirname {
case "/sys/bus/scsi/devices":
f1 := &fakeFileInfo{
name: "3:0:0:1",
}
f2 := &fakeFileInfo{
name: "4:0:0:0",
}
f3 := &fakeFileInfo{
name: diskPath,
}
f4 := &fakeFileInfo{
name: "host1",
}
f5 := &fakeFileInfo{
name: "target2:0:0",
}
return []os.FileInfo{f1, f2, f3, f4, f5}, nil
case "/sys/bus/scsi/devices/" + diskPath + "/block":
n := &fakeFileInfo{
name: devName,
}
return []os.FileInfo{n}, nil
}
return nil, fmt.Errorf("bad dir")
}
func (handler *fakeIOHandler) WriteFile(filename string, data []byte, perm os.FileMode) error {
return nil
}
func TestIoHandler(t *testing.T) {
var fcmd exec.FakeCmd
fcmd = exec.FakeCmd{
CombinedOutputScript: []exec.FakeCombinedOutputAction{
// cat
func() ([]byte, error) {
return []byte("Msft \nVirtual Disk \n"), nil
},
},
}
fake := exec.FakeExec{
CommandScript: []exec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
disk, err := findDiskByLun(lun, &fakeIOHandler{}, &fake)
// if no disk matches lun, exit
if disk != "/dev/"+devName || err != nil {
t.Errorf("no data disk disk found: disk %v err %v", disk, err)
}
}