Commit 28df55fc authored by Aditya Dani's avatar Aditya Dani

Portworx Volume Driver in Kubernetes

- Add a new type PortworxVolumeSource - Implement the kubernetes volume plugin for Portworx Volumes under pkg/volume/portworx - The Portworx Volume Driver uses the libopenstorage/openstorage specifications and apis for volume operations. Changes for k8s configuration and examples for portworx volumes. - Add PortworxVolume hooks in kubectl, kube-controller-manager and validation. - Add a README for PortworxVolume usage as PVs, PVCs and StorageClass. - Add example spec files Handle code review comments. - Modified READMEs to incorporate to suggestions. - Add a test for ReadWriteMany access mode. - Use util.UnmountPath in TearDown. - Add ReadOnly flag to PortworxVolumeSource - Use hostname:port instead of unix sockets - Delete the mount dir in TearDown. - Fix link issue in persistentvolumes README - In unit test check for mountpath after Setup is done. - Add PVC Claim Name as a Portworx Volume Label Generated code and documentation. - Updated swagger spec - Updated api-reference docs - Updated generated code under pkg/api/v1 Godeps update for Portworx Volume Driver - Adds github.com/libopenstorage/openstorage - Adds go.pedge.io/pb/go/google/protobuf - Updates Godep Licenses
parent dba0af36
......@@ -1664,6 +1664,30 @@
"Rev": "f7ee69f31298ecbe5d2b349c711e2547a617d398"
},
{
"ImportPath": "github.com/libopenstorage/openstorage/api",
"Rev": "6e787003b91ddba85f108b8aede075b1af0d3606"
},
{
"ImportPath": "github.com/libopenstorage/openstorage/api/client",
"Rev": "6e787003b91ddba85f108b8aede075b1af0d3606"
},
{
"ImportPath": "github.com/libopenstorage/openstorage/api/client/volume",
"Rev": "6e787003b91ddba85f108b8aede075b1af0d3606"
},
{
"ImportPath": "github.com/libopenstorage/openstorage/api/spec",
"Rev": "6e787003b91ddba85f108b8aede075b1af0d3606"
},
{
"ImportPath": "github.com/libopenstorage/openstorage/pkg/units",
"Rev": "6e787003b91ddba85f108b8aede075b1af0d3606"
},
{
"ImportPath": "github.com/libopenstorage/openstorage/volume",
"Rev": "6e787003b91ddba85f108b8aede075b1af0d3606"
},
{
"ImportPath": "github.com/lpabon/godbc",
"Comment": "v1.0-1-g9577782",
"Rev": "9577782540c1398b710ddae1b86268ba03a19b0c"
......@@ -2394,6 +2418,10 @@
"Rev": "5292687f5379e01054407da44d7c4590a61fd3de"
},
{
"ImportPath": "go.pedge.io/pb/go/google/protobuf",
"Rev": "f3c84f58974dc53d460d0855337cad85843bf0df"
},
{
"ImportPath": "go4.org/errorutil",
"Rev": "03efcb870d84809319ea509714dd6d19a1498483"
},
......
......@@ -37838,6 +37838,10 @@
"description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PhotonPersistentDiskVolumeSource"
},
"portworxVolume": {
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PortworxVolumeSource"
},
"quobyte": {
"description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.QuobyteVolumeSource"
......@@ -38325,6 +38329,26 @@
}
}
},
"io.k8s.kubernetes.pkg.api.v1.PortworxVolumeSource": {
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"fsType": {
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.",
"type": "string"
},
"readOnly": {
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
"type": "boolean"
},
"volumeID": {
"description": "VolumeID uniquely identifies a Portworx volume",
"type": "string"
}
}
},
"io.k8s.kubernetes.pkg.api.v1.PreferredSchedulingTerm": {
"description": "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).",
"required": [
......@@ -39378,6 +39402,10 @@
"description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PhotonPersistentDiskVolumeSource"
},
"portworxVolume": {
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PortworxVolumeSource"
},
"projected": {
"description": "Items for all in one resources secrets, configmaps, and downward API",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.ProjectedVolumeSource"
......@@ -1484,6 +1484,10 @@
"projected": {
"$ref": "v1.ProjectedVolumeSource",
"description": "Items for all in one resources secrets, configmaps, and downward API"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
}
}
},
......@@ -2243,6 +2247,27 @@
}
}
},
"v1.PortworxVolumeSource": {
"id": "v1.PortworxVolumeSource",
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"volumeID": {
"type": "string",
"description": "VolumeID uniquely identifies a Portworx volume"
},
"fsType": {
"type": "string",
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". 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.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",
......
......@@ -1489,6 +1489,10 @@
"projected": {
"$ref": "v1.ProjectedVolumeSource",
"description": "Items for all in one resources secrets, configmaps, and downward API"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
}
}
},
......@@ -2248,6 +2252,27 @@
}
}
},
"v1.PortworxVolumeSource": {
"id": "v1.PortworxVolumeSource",
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"volumeID": {
"type": "string",
"description": "VolumeID uniquely identifies a Portworx volume"
},
"fsType": {
"type": "string",
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". 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.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",
......
......@@ -6905,6 +6905,10 @@
"projected": {
"$ref": "v1.ProjectedVolumeSource",
"description": "Items for all in one resources secrets, configmaps, and downward API"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
}
}
},
......@@ -7664,6 +7668,27 @@
}
}
},
"v1.PortworxVolumeSource": {
"id": "v1.PortworxVolumeSource",
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"volumeID": {
"type": "string",
"description": "VolumeID uniquely identifies a Portworx volume"
},
"fsType": {
"type": "string",
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". 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.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",
......
......@@ -17885,6 +17885,10 @@
"$ref": "v1.PhotonPersistentDiskVolumeSource",
"description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
},
"accessModes": {
"type": "array",
"items": {
......@@ -18363,6 +18367,27 @@
}
}
},
"v1.PortworxVolumeSource": {
"id": "v1.PortworxVolumeSource",
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"volumeID": {
"type": "string",
"description": "VolumeID uniquely identifies a Portworx volume"
},
"fsType": {
"type": "string",
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". 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.PersistentVolumeStatus": {
"id": "v1.PersistentVolumeStatus",
"description": "PersistentVolumeStatus is the current status of a persistent volume.",
......@@ -18655,6 +18680,10 @@
"projected": {
"$ref": "v1.ProjectedVolumeSource",
"description": "Items for all in one resources secrets, configmaps, and downward API"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
}
}
},
......
......@@ -84,6 +84,7 @@ go_library(
"//pkg/volume/host_path:go_default_library",
"//pkg/volume/nfs:go_default_library",
"//pkg/volume/photon_pd:go_default_library",
"//pkg/volume/portworx:go_default_library",
"//pkg/volume/quobyte:go_default_library",
"//pkg/volume/rbd:go_default_library",
"//pkg/volume/vsphere_volume:go_default_library",
......
......@@ -50,6 +50,7 @@ import (
"k8s.io/kubernetes/pkg/volume/host_path"
"k8s.io/kubernetes/pkg/volume/nfs"
"k8s.io/kubernetes/pkg/volume/photon_pd"
"k8s.io/kubernetes/pkg/volume/portworx"
"k8s.io/kubernetes/pkg/volume/quobyte"
"k8s.io/kubernetes/pkg/volume/rbd"
"k8s.io/kubernetes/pkg/volume/vsphere_volume"
......@@ -68,6 +69,7 @@ func ProbeAttachableVolumePlugins(config componentconfig.VolumeConfiguration) []
allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins(config.FlexVolumePluginDir)...)
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...)
......@@ -115,6 +117,7 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config componen
allPlugins = append(allPlugins, quobyte.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
if cloud != nil {
switch {
......
......@@ -90,6 +90,7 @@ go_library(
"//pkg/volume/iscsi:go_default_library",
"//pkg/volume/nfs:go_default_library",
"//pkg/volume/photon_pd:go_default_library",
"//pkg/volume/portworx:go_default_library",
"//pkg/volume/projected:go_default_library",
"//pkg/volume/quobyte:go_default_library",
"//pkg/volume/rbd:go_default_library",
......
......@@ -46,6 +46,7 @@ import (
"k8s.io/kubernetes/pkg/volume/iscsi"
"k8s.io/kubernetes/pkg/volume/nfs"
"k8s.io/kubernetes/pkg/volume/photon_pd"
"k8s.io/kubernetes/pkg/volume/portworx"
"k8s.io/kubernetes/pkg/volume/projected"
"k8s.io/kubernetes/pkg/volume/quobyte"
"k8s.io/kubernetes/pkg/volume/rbd"
......@@ -90,6 +91,7 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, projected.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
return allPlugins
}
......
......@@ -1760,6 +1760,54 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_portworxvolumesource">v1.PortworxVolumeSource</h3>
<div class="paragraph">
<p>PortworxVolumeSource represents a Portworx volume resource.</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">volumeID</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeID uniquely identifies a Portworx volume</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">fsType</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". 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_localobjectreference">v1.LocalObjectReference</h3>
<div class="paragraph">
<p>LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.</p>
......@@ -4244,6 +4292,13 @@ The StatefulSet guarantees that a given network identity will always map to the
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_projectedvolumesource">v1.ProjectedVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">portworxVolume</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PortworxVolume represents a portworx volume attached and mounted on kubelets host machine</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_portworxvolumesource">v1.PortworxVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
......@@ -5396,7 +5451,7 @@ Examples:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-02-23 20:32:09 UTC
Last updated 2017-02-27 07:15:52 UTC
</div>
</div>
</body>
......
......@@ -1657,6 +1657,54 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_portworxvolumesource">v1.PortworxVolumeSource</h3>
<div class="paragraph">
<p>PortworxVolumeSource represents a Portworx volume resource.</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">volumeID</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeID uniquely identifies a Portworx volume</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">fsType</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". 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_localobjectreference">v1.LocalObjectReference</h3>
<div class="paragraph">
<p>LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.</p>
......@@ -4224,6 +4272,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_projectedvolumesource">v1.ProjectedVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">portworxVolume</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PortworxVolume represents a portworx volume attached and mounted on kubelets host machine</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_portworxvolumesource">v1.PortworxVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
......@@ -5321,7 +5376,7 @@ Examples:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-02-23 20:32:42 UTC
Last updated 2017-02-27 07:16:35 UTC
</div>
</div>
</body>
......
......@@ -1765,6 +1765,54 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_portworxvolumesource">v1.PortworxVolumeSource</h3>
<div class="paragraph">
<p>PortworxVolumeSource represents a Portworx volume resource.</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">volumeID</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeID uniquely identifies a Portworx volume</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">fsType</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". 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_capabilities">v1.Capabilities</h3>
<div class="paragraph">
<p>Adds and removes POSIX capabilities from running containers.</p>
......@@ -3637,6 +3685,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_projectedvolumesource">v1.ProjectedVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">portworxVolume</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PortworxVolume represents a portworx volume attached and mounted on kubelets host machine</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_portworxvolumesource">v1.PortworxVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
......@@ -7726,7 +7781,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 2017-02-27 08:17:02 UTC
Last updated 2017-02-27 20:18:57 UTC
</div>
</div>
</body>
......
......@@ -1932,6 +1932,54 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_portworxvolumesource">v1.PortworxVolumeSource</h3>
<div class="paragraph">
<p>PortworxVolumeSource represents a Portworx volume resource.</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">volumeID</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeID uniquely identifies a Portworx volume</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">fsType</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". 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_podtemplatelist">v1.PodTemplateList</h3>
<div class="paragraph">
<p>PodTemplateList is a list of PodTemplates.</p>
......@@ -4350,6 +4398,13 @@ The resulting set of endpoints can be viewed as:<br>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_projectedvolumesource">v1.ProjectedVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">portworxVolume</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PortworxVolume represents a portworx volume attached and mounted on kubelets host machine</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_portworxvolumesource">v1.PortworxVolumeSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
......@@ -7017,6 +7072,13 @@ Examples:<br>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">portworxVolume</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PortworxVolume represents a portworx volume attached and mounted on kubelets host machine</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_portworxvolumesource">v1.PortworxVolumeSource</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://kubernetes.io/docs/user-guide/persistent-volumes#access-modes">http://kubernetes.io/docs/user-guide/persistent-volumes#access-modes</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
......@@ -9652,7 +9714,7 @@ Examples:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-02-23 20:32:02 UTC
Last updated 2017-02-27 07:15:44 UTC
</div>
</div>
</body>
......
......@@ -63,6 +63,30 @@ parameters:
* `diskformat`: `thin`, `zeroedthick` and `eagerzeroedthick`. See vSphere docs for details. Default: `"thin"`.
#### Portworx Volume
```yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume
parameters:
repl: "1"
snap_interval: "70"
io_priority: "high"
```
* `fs`: filesystem to be laid out: [none/xfs/ext4] (default: `ext4`)
* `block_size`: block size in Kbytes (default: `32`)
* `repl`: replication factor [1..3] (default: `1`)
* `io_priority`: IO Priority: [high/medium/low] (default: `low`)
* `snap_interval`: snapshot interval in minutes, 0 disables snaps (default: `0`)
* `aggregation_level`: specifies the number of chunks the volume would be distributed into, 0 indicates a non-aggregated volume (default: `0`)
* `ephemeral`: ephemeral storage [true/false] (default `false`)
For a complete example refer ([Portworx Volume docs](../../volumes/portworx/README.md))
#### GLUSTERFS
......
# Portworx Volume
- [Portworx](#portworx)
- [Prerequisites](#prerequisites)
- [Examples](#examples)
- [Using Pre-provisioned Portworx Volumes](#pre-provisioned)
- [Running Pod](#running-pod)
- [Persistent Volumes](#persistent-volumes)
- [Using Dynamic Provisioning](#dynamic-provisioning)
- [Storage Class](#storage-class)
## Portworx
[Portworx](http://www.portworx.com) can be used as a storage provider for your Kubernetes cluster. Portworx pools your servers capacity and turns your servers
or cloud instances into converged, highly available compute and storage nodes
## Prerequisites
- A Portworx instance running on all of your Kubernetes nodes. For
more information on how you can install Portworx can be found [here](http://docs.portworx.com)
## Examples
The following examples assumes that you already have a running Kubernetes cluster with Portworx installed on all nodes.
### Using Pre-provisioned Portworx Volumes
Create a Volume using Portworx CLI.
On one of the Kubernetes nodes with Portworx installed run the following command
```shell
/opt/pwx/bin/pxctl volume create <vol-id> --size <size> --fs <fs-type>
```
#### Running Pods
Create Pod which uses Portworx Volumes
Example spec:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: test-portworx-volume-pod
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-portworx-volume
name: test-volume
volumes:
- name: test-volume
# This Portworx volume must already exist.
portworxVolume:
volumeID: "<vol-id>"
fsType: "<fs-type>"
```
[Download example](portworx-volume-pod.yaml?raw=true)
Make sure to replace <vol-id> and <fs-type> in the above spec with
the ones that you used while creating the volume.
Create the Pod.
``` bash
$ kubectl create -f examples/volumes/portworx/portworx-volume-pod.yaml
```
Verify that pod is running:
```bash
$ kubectl.sh get pods
NAME READY STATUS RESTARTS AGE
test-portworx-volume-pod 1/1 Running 0 16s
```
#### Persistent Volumes
1. Create Persistent Volume.
Example spec:
```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: <vol-id>
spec:
capacity:
storage: <size>Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
portworxVolume:
volumeID: "<vol-id>"
fsType: "<fs-type>"
```
Make sure to replace <vol-id>, <size> and <fs-type> in the above spec with
the ones that you used while creating the volume.
[Download example](portworx-volume-pv.yaml?raw=true)
Creating the persistent volume:
``` bash
$ kubectl create -f examples/volumes/portworx/portworx-volume-pv.yaml
```
Verifying persistent volume is created:
``` bash
$ kubectl describe pv pv0001
Name: pv0001
Labels: <none>
StorageClass:
Status: Available
Claim:
Reclaim Policy: Retain
Access Modes: RWO
Capacity: 2Gi
Message:
Source:
Type: PortworxVolume (a Portworx Persistent Volume resource)
VolumeID: pv0001
FSType: ext4
No events.
```
2. Create Persistent Volume Claim.
Example spec:
```yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc0001
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: <size>Gi
```
[Download example](portworx-volume-pvc.yaml?raw=true)
Creating the persistent volume claim:
``` bash
$ kubectl create -f examples/volumes/portworx/portworx-volume-pvc.yaml
```
Verifying persistent volume claim is created:
``` bash
$ kubectl describe pvc pvc0001
Name: pvc0001
Namespace: default
Status: Bound
Volume: pv0001
Labels: <none>
Capacity: 2Gi
Access Modes: RWO
No events.
```
3. Create Pod which uses Persistent Volume Claim.
See example:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: pvpod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/test-webserver
volumeMounts:
- name: test-volume
mountPath: /test-portworx-volume
volumes:
- name: test-volume
persistentVolumeClaim:
claimName: pvc0001
```
[Download example](portworx-volume-pvcpod.yaml?raw=true)
Creating the pod:
``` bash
$ kubectl create -f examples/volumes/portworx/portworx-volume-pvcpod.yaml
```
Verifying pod is created:
``` bash
$ kubectl get pod pvpod
NAME READY STATUS RESTARTS AGE
pvpod 1/1 Running 0 48m
```
### Using Dynamic Provisioning
Using Dynamic Provisioning and Storage Classes you don't need to
create Portworx volumes out of band and they will be created automatically.
#### Storage Class
Using Storage Classes objects an admin can define the different classes of Portworx Volumes
that are offered in a cluster. Following are the different parameters that can be used to define a Portworx
Storage Class
* `fs`: filesystem to be laid out: none|xfs|ext4 (default: `ext4`)
* `block_size`: block size in Kbytes (default: `32`)
* `repl`: replication factor [1..3] (default: `1`)
* `io_priority`: IO Priority: [high|medium|low] (default: `low`)
* `snap_interval`: snapshot interval in minutes, 0 disables snaps (default: `0`)
* `aggregation_level`: specifies the number of replication sets the volume can be aggregated from (default: `1`)
* `ephemeral`: ephemeral storage [true|false] (default `false`)
1. Create Storage Class.
See example:
```yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume
parameters:
repl: "1"
snap_interval: "70"
io_priority: "high"
```
[Download example](portworx-volume-sc-high.yaml?raw=true)
Creating the storageclass:
``` bash
$ kubectl create -f examples/volumes/portworx/portworx-volume-sc-high.yaml
```
Verifying storage class is created:
``` bash
$ kubectl describe storageclass portworx-io-priority-high
Name: portworx-io-priority-high
IsDefaultClass: No
Annotations: <none>
Provisioner: kubernetes.io/portworx-volume
Parameters: io_priority=high,repl=1,snapshot_interval=70
No events.
```
2. Create Persistent Volume Claim.
See example:
```yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvcsc001
annotations:
volume.beta.kubernetes.io/storage-class: portworx-io-priority-high
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
```
[Download example](portworx-volume-pvcsc.yaml?raw=true)
Creating the persistent volume claim:
``` bash
$ kubectl create -f examples/volumes/portworx/portworx-volume-pvcsc.yaml
```
Verifying persistent volume claim is created:
``` bash
$ kubectl describe pvc pvcsc001
Name: pvcsc001
Namespace: default
StorageClass: portworx-io-priority-high
Status: Bound
Volume: pvc-e5578707-c626-11e6-baf6-08002729a32b
Labels: <none>
Capacity: 2Gi
Access Modes: RWO
No Events
```
Persistent Volume is automatically created and is bounded to this pvc.
Verifying persistent volume claim is created:
``` bash
$ kubectl describe pv pvc-e5578707-c626-11e6-baf6-08002729a32b
Name: pvc-e5578707-c626-11e6-baf6-08002729a32b
Labels: <none>
StorageClass: portworx-io-priority-high
Status: Bound
Claim: default/pvcsc001
Reclaim Policy: Delete
Access Modes: RWO
Capacity: 2Gi
Message:
Source:
Type: PortworxVolume (a Portworx Persistent Volume resource)
VolumeID: 374093969022973811
No events.
```
3. Create Pod which uses Persistent Volume Claim with storage class.
See example:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: pvpod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/test-webserver
volumeMounts:
- name: test-volume
mountPath: /test-portworx-volume
volumes:
- name: test-volume
persistentVolumeClaim:
claimName: pvcsc001
```
[Download example](portworx-volume-pvcscpod.yaml?raw=true)
Creating the pod:
``` bash
$ kubectl create -f examples/volumes/portworx/portworx-volume-pvcscpod.yaml
```
Verifying pod is created:
``` bash
$ kubectl get pod pvpod
NAME READY STATUS RESTARTS AGE
pvpod 1/1 Running 0 48m
```
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/examples/volumes/portworx/README.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->
apiVersion: v1
kind: Pod
metadata:
name: test-portworx-volume-pod
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-portworx-volume
name: test-volume
volumes:
- name: test-volume
# This Portworx volume must already exist.
portworxVolume:
volumeID: "vol1"
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0001
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
portworxVolume:
volumeID: "pv0001"
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc0001
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
\ No newline at end of file
apiVersion: v1
kind: Pod
metadata:
name: pvpod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/test-webserver
volumeMounts:
- name: test-volume
mountPath: /test-portworx-volume
volumes:
- name: test-volume
persistentVolumeClaim:
claimName: pvc0001
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvcsc001
annotations:
volume.beta.kubernetes.io/storage-class: portworx-io-priority-high
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
apiVersion: v1
kind: Pod
metadata:
name: pvpod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/test-webserver
volumeMounts:
- name: test-volume
mountPath: /test-portworx-volume
volumes:
- name: test-volume
persistentVolumeClaim:
claimName: pvcsc001
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume
parameters:
repl: "1"
snap_interval: "70"
io_priority: "high"
......@@ -12636,6 +12636,26 @@
}
}
},
"io.k8s.kubernetes.pkg.api.v1.PortworxVolumeSource": {
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"fsType": {
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.",
"type": "string"
},
"readOnly": {
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
"type": "boolean"
},
"volumeID": {
"description": "VolumeID uniquely identifies a Portworx volume",
"type": "string"
}
}
},
"io.k8s.kubernetes.pkg.api.v1.PreferredSchedulingTerm": {
"description": "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).",
"required": [
......@@ -13323,6 +13343,10 @@
"description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PhotonPersistentDiskVolumeSource"
},
"portworxVolume": {
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PortworxVolumeSource"
},
"projected": {
"description": "Items for all in one resources secrets, configmaps, and downward API",
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.ProjectedVolumeSource"
......
......@@ -296,6 +296,9 @@ type VolumeSource struct {
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource
// Items for all in one resources secrets, configmaps, and downward API
Projected *ProjectedVolumeSource
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource
}
// Similar to VolumeSource but meant for the administrator who creates PVs.
......@@ -358,6 +361,9 @@ type PersistentVolumeSource struct {
AzureDisk *AzureDiskVolumeSource
// PhotonPersistentDisk represents a Photon Controller persistent disk attached and mounted on kubelets host machine
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource
}
type PersistentVolumeClaimVolumeSource struct {
......@@ -1001,6 +1007,21 @@ type PhotonPersistentDiskVolumeSource struct {
FSType string
}
// PortworxVolumeSource represents a Portworx volume resource.
type PortworxVolumeSource struct {
// VolumeID uniquely identifies a Portworx volume
VolumeID string
// FSType represents the filesystem type to mount
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified.
// +optional
FSType string
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool
}
type AzureDataDiskCachingMode string
const (
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2128,6 +2128,10 @@ message PersistentVolumeSource {
// PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine
optional PhotonPersistentDiskVolumeSource photonPersistentDisk = 17;
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
optional PortworxVolumeSource portworxVolume = 18;
}
// PersistentVolumeSpec is the specification of a persistent volume.
......@@ -2790,6 +2794,22 @@ message PodTemplateSpec {
optional PodSpec spec = 2;
}
// PortworxVolumeSource represents a Portworx volume resource.
message PortworxVolumeSource {
// VolumeID uniquely identifies a Portworx volume
optional string volumeID = 1;
// FSType represents the filesystem type to mount
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified.
optional string fsType = 2;
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
optional bool readOnly = 3;
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
// +k8s:openapi-gen=false
message Preconditions {
......@@ -3811,7 +3831,11 @@ message VolumeSource {
optional PhotonPersistentDiskVolumeSource photonPersistentDisk = 23;
// Items for all in one resources secrets, configmaps, and downward API
optional ProjectedVolumeSource projected = 24;
optional ProjectedVolumeSource projected = 25;
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
optional PortworxVolumeSource portworxVolume = 24;
}
// Represents a vSphere volume resource.
......
......@@ -328,6 +328,9 @@ type VolumeSource struct {
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource `json:"photonPersistentDisk,omitempty" protobuf:"bytes,23,opt,name=photonPersistentDisk"`
// Items for all in one resources secrets, configmaps, and downward API
Projected *ProjectedVolumeSource `json:"projected,omitempty"`
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource `json:"portworxVolume,omitempty" protobuf:"bytes,24,opt,name=portworxVolume"`
}
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
......@@ -413,6 +416,9 @@ type PersistentVolumeSource struct {
AzureDisk *AzureDiskVolumeSource `json:"azureDisk,omitempty" protobuf:"bytes,16,opt,name=azureDisk"`
// PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource `json:"photonPersistentDisk,omitempty" protobuf:"bytes,17,opt,name=photonPersistentDisk"`
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource `json:"portworxVolume,omitempty" protobuf:"bytes,18,opt,name=portworxVolume"`
}
// +genclient=true
......@@ -1107,6 +1113,20 @@ type AzureDiskVolumeSource struct {
ReadOnly *bool `json:"readOnly,omitempty" protobuf:"varint,5,opt,name=readOnly"`
}
// PortworxVolumeSource represents a Portworx volume resource.
type PortworxVolumeSource struct {
// VolumeID uniquely identifies a Portworx volume
VolumeID string `json:"volumeID" protobuf:"bytes,1,opt,name=volumeID"`
// FSType represents the filesystem type to mount
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified.
FSType string `json:"fsType,omitempty" protobuf:"bytes,2,opt,name=fsType"`
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,3,opt,name=readOnly"`
}
// Adapts a ConfigMap into a volume.
//
// The contents of the target ConfigMap's Data field will be presented in a
......
......@@ -1122,6 +1122,7 @@ var map_PersistentVolumeSource = map[string]string{
"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.",
"photonPersistentDisk": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine",
"portworxVolume": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
}
func (PersistentVolumeSource) SwaggerDoc() map[string]string {
......@@ -1399,6 +1400,17 @@ func (PodTemplateSpec) SwaggerDoc() map[string]string {
return map_PodTemplateSpec
}
var map_PortworxVolumeSource = map[string]string{
"": "PortworxVolumeSource represents a Portworx volume resource.",
"volumeID": "VolumeID uniquely identifies a Portworx volume",
"fsType": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.",
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
}
func (PortworxVolumeSource) SwaggerDoc() map[string]string {
return map_PortworxVolumeSource
}
var map_Preconditions = map[string]string{
"": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.",
"uid": "Specifies the target UID.",
......@@ -1897,6 +1909,7 @@ var map_VolumeSource = map[string]string{
"azureDisk": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
"photonPersistentDisk": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine",
"projected": "Items for all in one resources secrets, configmaps, and downward API",
"portworxVolume": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
}
func (VolumeSource) SwaggerDoc() map[string]string {
......
......@@ -275,6 +275,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_api_PodTemplateList_To_v1_PodTemplateList,
Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec,
Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec,
Convert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource,
Convert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource,
Convert_v1_Preconditions_To_api_Preconditions,
Convert_api_Preconditions_To_v1_Preconditions,
Convert_v1_PreferAvoidPodsEntry_To_api_PreferAvoidPodsEntry,
......@@ -2706,6 +2708,7 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *Per
out.Quobyte = (*api.QuobyteVolumeSource)(unsafe.Pointer(in.Quobyte))
out.AzureDisk = (*api.AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*api.PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......@@ -2731,6 +2734,7 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
out.VsphereVolume = (*VsphereVirtualDiskVolumeSource)(unsafe.Pointer(in.VsphereVolume))
out.AzureDisk = (*AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......@@ -3341,6 +3345,28 @@ func autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in *api.PodTemplateSp
return nil
}
func autoConvert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource(in *PortworxVolumeSource, out *api.PortworxVolumeSource, s conversion.Scope) error {
out.VolumeID = in.VolumeID
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource(in *PortworxVolumeSource, out *api.PortworxVolumeSource, s conversion.Scope) error {
return autoConvert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource(in, out, s)
}
func autoConvert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource(in *api.PortworxVolumeSource, out *PortworxVolumeSource, s conversion.Scope) error {
out.VolumeID = in.VolumeID
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource(in *api.PortworxVolumeSource, out *PortworxVolumeSource, s conversion.Scope) error {
return autoConvert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource(in, out, s)
}
func autoConvert_v1_Preconditions_To_api_Preconditions(in *Preconditions, out *api.Preconditions, s conversion.Scope) error {
out.UID = (*types.UID)(unsafe.Pointer(in.UID))
return nil
......@@ -4458,6 +4484,7 @@ func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.
out.AzureDisk = (*api.AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*api.PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.Projected = (*api.ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......@@ -4490,6 +4517,7 @@ func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *
out.AzureDisk = (*AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.Projected = (*ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......
......@@ -155,6 +155,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PodTemplate, InType: reflect.TypeOf(&PodTemplate{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PodTemplateList, InType: reflect.TypeOf(&PodTemplateList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PodTemplateSpec, InType: reflect.TypeOf(&PodTemplateSpec{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PortworxVolumeSource, InType: reflect.TypeOf(&PortworxVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Preconditions, InType: reflect.TypeOf(&Preconditions{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PreferAvoidPodsEntry, InType: reflect.TypeOf(&PreferAvoidPodsEntry{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PreferredSchedulingTerm, InType: reflect.TypeOf(&PreferredSchedulingTerm{})},
......@@ -2015,6 +2016,11 @@ func DeepCopy_v1_PersistentVolumeSource(in interface{}, out interface{}, c *conv
*out = new(PhotonPersistentDiskVolumeSource)
**out = **in
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......@@ -2513,6 +2519,15 @@ func DeepCopy_v1_PodTemplateSpec(in interface{}, out interface{}, c *conversion.
}
}
func DeepCopy_v1_PortworxVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PortworxVolumeSource)
out := out.(*PortworxVolumeSource)
*out = *in
return nil
}
}
func DeepCopy_v1_Preconditions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Preconditions)
......@@ -3420,6 +3435,11 @@ func DeepCopy_v1_VolumeSource(in interface{}, out interface{}, c *conversion.Clo
return err
}
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......
......@@ -511,6 +511,14 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
allErrs = append(allErrs, validatePhotonPersistentDiskVolumeSource(source.PhotonPersistentDisk, fldPath.Child("photonPersistentDisk"))...)
}
}
if source.PortworxVolume != nil {
if numVolumes > 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("portworxVolume"), "may not specify more than 1 volume type"))
} else {
numVolumes++
allErrs = append(allErrs, validatePortworxVolumeSource(source.PortworxVolume, fldPath.Child("portworxVolume"))...)
}
}
if source.AzureDisk != nil {
numVolumes++
allErrs = append(allErrs, validateAzureDisk(source.AzureDisk, fldPath.Child("azureDisk"))...)
......@@ -979,6 +987,14 @@ func validatePhotonPersistentDiskVolumeSource(cd *api.PhotonPersistentDiskVolume
return allErrs
}
func validatePortworxVolumeSource(pwx *api.PortworxVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(pwx.VolumeID) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
}
return allErrs
}
// ValidatePersistentVolumeName checks that a name is appropriate for a
// PersistentVolumeName object.
var ValidatePersistentVolumeName = NameIsDNSSubdomain
......@@ -1138,6 +1154,14 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
allErrs = append(allErrs, validatePhotonPersistentDiskVolumeSource(pv.Spec.PhotonPersistentDisk, specPath.Child("photonPersistentDisk"))...)
}
}
if pv.Spec.PortworxVolume != nil {
if numVolumes > 0 {
allErrs = append(allErrs, field.Forbidden(specPath.Child("portworxVolume"), "may not specify more than 1 volume type"))
} else {
numVolumes++
allErrs = append(allErrs, validatePortworxVolumeSource(pv.Spec.PortworxVolume, specPath.Child("portworxVolume"))...)
}
}
if pv.Spec.AzureDisk != nil {
numVolumes++
allErrs = append(allErrs, validateAzureDisk(pv.Spec.AzureDisk, specPath.Child("azureDisk"))...)
......
......@@ -158,6 +158,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PodTemplate, InType: reflect.TypeOf(&PodTemplate{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PodTemplateList, InType: reflect.TypeOf(&PodTemplateList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PodTemplateSpec, InType: reflect.TypeOf(&PodTemplateSpec{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PortworxVolumeSource, InType: reflect.TypeOf(&PortworxVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Preconditions, InType: reflect.TypeOf(&Preconditions{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PreferAvoidPodsEntry, InType: reflect.TypeOf(&PreferAvoidPodsEntry{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PreferredSchedulingTerm, InType: reflect.TypeOf(&PreferredSchedulingTerm{})},
......@@ -2059,6 +2060,11 @@ func DeepCopy_api_PersistentVolumeSource(in interface{}, out interface{}, c *con
*out = new(PhotonPersistentDiskVolumeSource)
**out = **in
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......@@ -2557,6 +2563,15 @@ func DeepCopy_api_PodTemplateSpec(in interface{}, out interface{}, c *conversion
}
}
func DeepCopy_api_PortworxVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PortworxVolumeSource)
out := out.(*PortworxVolumeSource)
*out = *in
return nil
}
}
func DeepCopy_api_Preconditions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Preconditions)
......@@ -3447,6 +3462,11 @@ func DeepCopy_api_VolumeSource(in interface{}, out interface{}, c *conversion.Cl
return err
}
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......
......@@ -917,6 +917,7 @@ var (
AzureDisk FSType = "azureDisk"
PhotonPersistentDisk FSType = "photonPersistentDisk"
Projected FSType = "projected"
PortworxVolume FSType = "portworxVolume"
All FSType = "*"
)
......
......@@ -623,6 +623,8 @@ func describeVolumes(volumes []api.Volume, w *PrefixWriter, space string) {
printCinderVolumeSource(volume.VolumeSource.Cinder, w)
case volume.VolumeSource.PhotonPersistentDisk != nil:
printPhotonPersistentDiskVolumeSource(volume.VolumeSource.PhotonPersistentDisk, w)
case volume.VolumeSource.PortworxVolume != nil:
printPortworxVolumeSource(volume.VolumeSource.PortworxVolume, w)
default:
w.Write(LEVEL_1, "<unknown>\n")
}
......@@ -696,6 +698,12 @@ func printQuobyteVolumeSource(quobyte *api.QuobyteVolumeSource, w *PrefixWriter)
quobyte.Registry, quobyte.Volume, quobyte.ReadOnly)
}
func printPortworxVolumeSource(pwxVolume *api.PortworxVolumeSource, w *PrefixWriter) {
w.Write(LEVEL_2, "Type:\tPortworxVolume (a Portworx Volume resource)\n"+
" VolumeID:\t%v\n",
pwxVolume.VolumeID)
}
func printISCSIVolumeSource(iscsi *api.ISCSIVolumeSource, w *PrefixWriter) {
w.Write(LEVEL_2, "Type:\tISCSI (an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod)\n"+
" TargetPortal:\t%v\n"+
......@@ -841,6 +849,8 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
printAzureDiskVolumeSource(pv.Spec.AzureDisk, w)
case pv.Spec.PhotonPersistentDisk != nil:
printPhotonPersistentDiskVolumeSource(pv.Spec.PhotonPersistentDisk, w)
case pv.Spec.PortworxVolume != nil:
printPortworxVolumeSource(pv.Spec.PortworxVolume, w)
}
if events != nil {
......
......@@ -63,6 +63,7 @@ func GetAllFSTypesAsSet() sets.String {
string(extensions.AzureDisk),
string(extensions.PhotonPersistentDisk),
string(extensions.Projected),
string(extensions.PortworxVolume),
)
return fstypes
}
......@@ -118,6 +119,8 @@ func GetVolumeFSType(v api.Volume) (extensions.FSType, error) {
return extensions.PhotonPersistentDisk, nil
case v.Projected != nil:
return extensions.Projected, nil
case v.PortworxVolume != nil:
return extensions.PortworxVolume, nil
}
return "", fmt.Errorf("unknown volume type for volume: %#v", v)
......
......@@ -108,6 +108,7 @@ filegroup(
"//pkg/volume/iscsi:all-srcs",
"//pkg/volume/nfs:all-srcs",
"//pkg/volume/photon_pd:all-srcs",
"//pkg/volume/portworx:all-srcs",
"//pkg/volume/projected:all-srcs",
"//pkg/volume/quobyte:all-srcs",
"//pkg/volume/rbd:all-srcs",
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["portworx_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/testing:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/client-go/util/testing",
],
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"portworx.go",
"portworx_util.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/util/strings:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/libopenstorage/openstorage/api",
"//vendor:github.com/libopenstorage/openstorage/api/client",
"//vendor:github.com/libopenstorage/openstorage/api/client/volume",
"//vendor:github.com/libopenstorage/openstorage/api/spec",
"//vendor:github.com/libopenstorage/openstorage/volume",
"//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/types",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
maintainers:
- adityadani
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package portworx contains the internal representation of Portworx
// Block Device volumes.
package portworx
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package portworx
import (
"fmt"
"os"
"path"
"testing"
"k8s.io/apimachinery/pkg/types"
utiltesting "k8s.io/client-go/util/testing"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
)
const (
PortworxTestVolume = "portworx-test-vol"
)
func TestCanSupport(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("portworxVolumeTest")
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))
plug, err := plugMgr.FindPluginByName("kubernetes.io/portworx-volume")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
if plug.GetPluginName() != "kubernetes.io/portworx-volume" {
t.Errorf("Wrong name: %s", plug.GetPluginName())
}
if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{PortworxVolume: &v1.PortworxVolumeSource{}}}}) {
t.Errorf("Expected true")
}
if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{PortworxVolume: &v1.PortworxVolumeSource{}}}}}) {
t.Errorf("Expected true")
}
}
func TestGetAccessModes(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("portworxVolumeTest")
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))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/portworx-volume")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
if !contains(plug.GetAccessModes(), v1.ReadWriteOnce) {
t.Errorf("Expected to support AccessModeTypes: %s", v1.ReadWriteOnce)
}
if !contains(plug.GetAccessModes(), v1.ReadWriteMany) {
t.Errorf("Expected to support AccessModeTypes: %s", v1.ReadWriteMany)
}
if contains(plug.GetAccessModes(), v1.ReadOnlyMany) {
t.Errorf("Expected not to support AccessModeTypes: %s", v1.ReadOnlyMany)
}
}
func contains(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool {
for _, m := range modes {
if m == mode {
return true
}
}
return false
}
type fakePortworxManager struct {
attachCalled bool
mountCalled bool
}
func (fake *fakePortworxManager) AttachVolume(b *portworxVolumeMounter) (string, error) {
fake.attachCalled = true
return "", nil
}
func (fake *fakePortworxManager) DetachVolume(c *portworxVolumeUnmounter) error {
return nil
}
func (fake *fakePortworxManager) MountVolume(b *portworxVolumeMounter, mountPath string) error {
fake.mountCalled = true
return nil
}
func (fake *fakePortworxManager) UnmountVolume(c *portworxVolumeUnmounter, mountPath string) error {
return nil
}
func (fake *fakePortworxManager) CreateVolume(c *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int, labels map[string]string, err error) {
labels = make(map[string]string)
labels["fakeportworxmanager"] = "yes"
return PortworxTestVolume, 100, labels, nil
}
func (fake *fakePortworxManager) DeleteVolume(cd *portworxVolumeDeleter) error {
if cd.volumeID != PortworxTestVolume {
return fmt.Errorf("Deleter got unexpected volume name: %s", cd.volumeID)
}
return nil
}
func TestPlugin(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("portworxVolumeTest")
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))
plug, err := plugMgr.FindPluginByName("kubernetes.io/portworx-volume")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
spec := &v1.Volume{
Name: "vol1",
VolumeSource: v1.VolumeSource{
PortworxVolume: &v1.PortworxVolumeSource{
VolumeID: PortworxTestVolume,
FSType: "ext4",
},
},
}
fakeManager := &fakePortworxManager{}
// Test Mounter
fakeMounter := &mount.FakeMounter{}
mounter, err := plug.(*portworxVolumePlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), fakeManager, fakeMounter)
if err != nil {
t.Errorf("Failed to make a new Mounter: %v", err)
}
if mounter == nil {
t.Errorf("Got a nil Mounter")
}
volPath := path.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~portworx-volume/vol1")
path := mounter.GetPath()
if path != volPath {
t.Errorf("Got unexpected path: %s", path)
}
if err := mounter.SetUp(nil); err != nil {
t.Errorf("Expected success, got: %v", err)
}
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
t.Errorf("SetUp() failed, volume path not created: %s", path)
} else {
t.Errorf("SetUp() failed: %v", err)
}
}
if !fakeManager.attachCalled {
t.Errorf("Attach watch not called")
}
if !fakeManager.mountCalled {
t.Errorf("Mount watch not called")
}
// Test Unmounter
fakeManager = &fakePortworxManager{}
unmounter, err := plug.(*portworxVolumePlugin).newUnmounterInternal("vol1", types.UID("poduid"), fakeManager, 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)
}
// Test Provisioner
options := volume.VolumeOptions{
PVC: volumetest.CreateTestPVC("100Gi", []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}),
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
}
provisioner, err := plug.(*portworxVolumePlugin).newProvisionerInternal(options, &fakePortworxManager{})
persistentSpec, err := provisioner.Provision()
if err != nil {
t.Errorf("Provision() failed: %v", err)
}
if persistentSpec.Spec.PersistentVolumeSource.PortworxVolume.VolumeID != PortworxTestVolume {
t.Errorf("Provision() returned unexpected volume ID: %s", persistentSpec.Spec.PersistentVolumeSource.PortworxVolume.VolumeID)
}
cap := persistentSpec.Spec.Capacity[v1.ResourceStorage]
size := cap.Value()
if size != 100*1024*1024*1024 {
t.Errorf("Provision() returned unexpected volume size: %v", size)
}
if persistentSpec.Labels["fakeportworxmanager"] != "yes" {
t.Errorf("Provision() returned unexpected labels: %v", persistentSpec.Labels)
}
// Test Deleter
volSpec := &volume.Spec{
PersistentVolume: persistentSpec,
}
deleter, err := plug.(*portworxVolumePlugin).newDeleterInternal(volSpec, &fakePortworxManager{})
err = deleter.Delete()
if err != nil {
t.Errorf("Deleter() failed: %v", err)
}
}
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package portworx
import (
"github.com/golang/glog"
osdapi "github.com/libopenstorage/openstorage/api"
osdclient "github.com/libopenstorage/openstorage/api/client"
volumeclient "github.com/libopenstorage/openstorage/api/client/volume"
osdspec "github.com/libopenstorage/openstorage/api/spec"
osdvolume "github.com/libopenstorage/openstorage/volume"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/volume"
)
const (
osdMgmtPort = "9001"
osdDriverVersion = "v1"
pxdDriverName = "pxd"
pwxSockName = "pwx"
pvcClaimLabel = "pvc"
)
type PortworxVolumeUtil struct {
portworxClient *osdclient.Client
}
// CreateVolume creates a Portworx volume.
func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (string, int, map[string]string, error) {
hostname := p.plugin.host.GetHostName()
client, err := util.osdClient(hostname)
if err != nil {
return "", 0, nil, err
}
capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
// Portworx Volumes are specified in GB
requestGB := int(volume.RoundUpSize(capacity.Value(), 1024*1024*1024))
specHandler := osdspec.NewSpecHandler()
spec, err := specHandler.SpecFromOpts(p.options.Parameters)
if err != nil {
return "", 0, nil, err
}
spec.Size = uint64(requestGB * 1024 * 1024 * 1024)
source := osdapi.Source{}
locator := osdapi.VolumeLocator{
Name: p.options.PVName,
}
// Add claim Name as a part of Portworx Volume Labels
locator.VolumeLabels = make(map[string]string)
locator.VolumeLabels[pvcClaimLabel] = p.options.PVC.Name
volumeID, err := client.Create(&locator, &source, spec)
if err != nil {
glog.V(2).Infof("Error creating Portworx Volume : %v", err)
}
return volumeID, requestGB, nil, err
}
// DeleteVolume deletes a Portworx volume
func (util *PortworxVolumeUtil) DeleteVolume(d *portworxVolumeDeleter) error {
hostname := d.plugin.host.GetHostName()
client, err := util.osdClient(hostname)
if err != nil {
return err
}
err = client.Delete(d.volumeID)
if err != nil {
glog.V(2).Infof("Error deleting Portworx Volume (%v): %v", d.volName, err)
return err
}
return nil
}
// AttachVolume attaches a Portworx Volume
func (util *PortworxVolumeUtil) AttachVolume(m *portworxVolumeMounter) (string, error) {
hostname := m.plugin.host.GetHostName()
client, err := util.osdClient(hostname)
if err != nil {
return "", err
}
devicePath, err := client.Attach(m.volName)
if err != nil {
glog.V(2).Infof("Error attaching Portworx Volume (%v): %v", m.volName, err)
return "", err
}
return devicePath, nil
}
// DetachVolume detaches a Portworx Volume
func (util *PortworxVolumeUtil) DetachVolume(u *portworxVolumeUnmounter) error {
hostname := u.plugin.host.GetHostName()
client, err := util.osdClient(hostname)
if err != nil {
return err
}
err = client.Detach(u.volName)
if err != nil {
glog.V(2).Infof("Error detaching Portworx Volume (%v): %v", u.volName, err)
return err
}
return nil
}
// MountVolume mounts a Portworx Volume on the specified mountPath
func (util *PortworxVolumeUtil) MountVolume(m *portworxVolumeMounter, mountPath string) error {
hostname := m.plugin.host.GetHostName()
client, err := util.osdClient(hostname)
if err != nil {
return err
}
err = client.Mount(m.volName, mountPath)
if err != nil {
glog.V(2).Infof("Error mounting Portworx Volume (%v) on Path (%v): %v", m.volName, mountPath, err)
return err
}
return nil
}
// UnmountVolume unmounts a Portworx Volume
func (util *PortworxVolumeUtil) UnmountVolume(u *portworxVolumeUnmounter, mountPath string) error {
hostname := u.plugin.host.GetHostName()
client, err := util.osdClient(hostname)
if err != nil {
return err
}
err = client.Unmount(u.volName, mountPath)
if err != nil {
glog.V(2).Infof("Error unmounting Portworx Volume (%v) on Path (%v): %v", u.volName, mountPath, err)
return err
}
return nil
}
func (util *PortworxVolumeUtil) osdClient(hostname string) (osdvolume.VolumeDriver, error) {
osdEndpoint := "http://" + hostname + ":" + osdMgmtPort
if util.portworxClient == nil {
driverClient, err := volumeclient.NewDriverClient(osdEndpoint, pxdDriverName, osdDriverVersion)
if err != nil {
return nil, err
}
util.portworxClient = driverClient
}
return volumeclient.VolumeDriver(util.portworxClient), nil
}
......@@ -296,6 +296,9 @@ type VolumeSource struct {
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource
// Items for all in one resources secrets, configmaps, and downward API
Projected *ProjectedVolumeSource
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource
}
// Similar to VolumeSource but meant for the administrator who creates PVs.
......@@ -358,6 +361,9 @@ type PersistentVolumeSource struct {
AzureDisk *AzureDiskVolumeSource
// PhotonPersistentDisk represents a Photon Controller persistent disk attached and mounted on kubelets host machine
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource
}
type PersistentVolumeClaimVolumeSource struct {
......@@ -1001,6 +1007,21 @@ type PhotonPersistentDiskVolumeSource struct {
FSType string
}
// PortworxVolumeSource represents a Portworx volume resource.
type PortworxVolumeSource struct {
// VolumeID uniquely identifies a Portworx volume
VolumeID string
// FSType represents the filesystem type to mount
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified.
// +optional
FSType string
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool
}
type AzureDataDiskCachingMode string
const (
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2128,6 +2128,10 @@ message PersistentVolumeSource {
// PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine
optional PhotonPersistentDiskVolumeSource photonPersistentDisk = 17;
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
optional PortworxVolumeSource portworxVolume = 18;
}
// PersistentVolumeSpec is the specification of a persistent volume.
......@@ -2790,6 +2794,22 @@ message PodTemplateSpec {
optional PodSpec spec = 2;
}
// PortworxVolumeSource represents a Portworx volume resource.
message PortworxVolumeSource {
// VolumeID uniquely identifies a Portworx volume
optional string volumeID = 1;
// FSType represents the filesystem type to mount
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified.
optional string fsType = 2;
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
optional bool readOnly = 3;
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
// +k8s:openapi-gen=false
message Preconditions {
......@@ -3811,7 +3831,11 @@ message VolumeSource {
optional PhotonPersistentDiskVolumeSource photonPersistentDisk = 23;
// Items for all in one resources secrets, configmaps, and downward API
optional ProjectedVolumeSource projected = 24;
optional ProjectedVolumeSource projected = 25;
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
optional PortworxVolumeSource portworxVolume = 24;
}
// Represents a vSphere volume resource.
......
......@@ -328,6 +328,9 @@ type VolumeSource struct {
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource `json:"photonPersistentDisk,omitempty" protobuf:"bytes,23,opt,name=photonPersistentDisk"`
// Items for all in one resources secrets, configmaps, and downward API
Projected *ProjectedVolumeSource `json:"projected,omitempty"`
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource `json:"portworxVolume,omitempty" protobuf:"bytes,24,opt,name=portworxVolume"`
}
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
......@@ -413,6 +416,9 @@ type PersistentVolumeSource struct {
AzureDisk *AzureDiskVolumeSource `json:"azureDisk,omitempty" protobuf:"bytes,16,opt,name=azureDisk"`
// PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource `json:"photonPersistentDisk,omitempty" protobuf:"bytes,17,opt,name=photonPersistentDisk"`
// PortworxVolume represents a portworx volume attached and mounted on kubelets host machine
// +optional
PortworxVolume *PortworxVolumeSource `json:"portworxVolume,omitempty" protobuf:"bytes,18,opt,name=portworxVolume"`
}
// +genclient=true
......@@ -1107,6 +1113,20 @@ type AzureDiskVolumeSource struct {
ReadOnly *bool `json:"readOnly,omitempty" protobuf:"varint,5,opt,name=readOnly"`
}
// PortworxVolumeSource represents a Portworx volume resource.
type PortworxVolumeSource struct {
// VolumeID uniquely identifies a Portworx volume
VolumeID string `json:"volumeID" protobuf:"bytes,1,opt,name=volumeID"`
// FSType represents the filesystem type to mount
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified.
FSType string `json:"fsType,omitempty" protobuf:"bytes,2,opt,name=fsType"`
// Defaults to false (read/write). ReadOnly here will force
// the ReadOnly setting in VolumeMounts.
// +optional
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,3,opt,name=readOnly"`
}
// Adapts a ConfigMap into a volume.
//
// The contents of the target ConfigMap's Data field will be presented in a
......
......@@ -1122,6 +1122,7 @@ var map_PersistentVolumeSource = map[string]string{
"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.",
"photonPersistentDisk": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine",
"portworxVolume": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
}
func (PersistentVolumeSource) SwaggerDoc() map[string]string {
......@@ -1399,6 +1400,17 @@ func (PodTemplateSpec) SwaggerDoc() map[string]string {
return map_PodTemplateSpec
}
var map_PortworxVolumeSource = map[string]string{
"": "PortworxVolumeSource represents a Portworx volume resource.",
"volumeID": "VolumeID uniquely identifies a Portworx volume",
"fsType": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.",
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
}
func (PortworxVolumeSource) SwaggerDoc() map[string]string {
return map_PortworxVolumeSource
}
var map_Preconditions = map[string]string{
"": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.",
"uid": "Specifies the target UID.",
......@@ -1897,6 +1909,7 @@ var map_VolumeSource = map[string]string{
"azureDisk": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
"photonPersistentDisk": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine",
"projected": "Items for all in one resources secrets, configmaps, and downward API",
"portworxVolume": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
}
func (VolumeSource) SwaggerDoc() map[string]string {
......
......@@ -275,6 +275,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_api_PodTemplateList_To_v1_PodTemplateList,
Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec,
Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec,
Convert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource,
Convert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource,
Convert_v1_Preconditions_To_api_Preconditions,
Convert_api_Preconditions_To_v1_Preconditions,
Convert_v1_PreferAvoidPodsEntry_To_api_PreferAvoidPodsEntry,
......@@ -2706,6 +2708,7 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *Per
out.Quobyte = (*api.QuobyteVolumeSource)(unsafe.Pointer(in.Quobyte))
out.AzureDisk = (*api.AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*api.PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......@@ -2731,6 +2734,7 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
out.VsphereVolume = (*VsphereVirtualDiskVolumeSource)(unsafe.Pointer(in.VsphereVolume))
out.AzureDisk = (*AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......@@ -3341,6 +3345,28 @@ func autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in *api.PodTemplateSp
return nil
}
func autoConvert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource(in *PortworxVolumeSource, out *api.PortworxVolumeSource, s conversion.Scope) error {
out.VolumeID = in.VolumeID
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource(in *PortworxVolumeSource, out *api.PortworxVolumeSource, s conversion.Scope) error {
return autoConvert_v1_PortworxVolumeSource_To_api_PortworxVolumeSource(in, out, s)
}
func autoConvert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource(in *api.PortworxVolumeSource, out *PortworxVolumeSource, s conversion.Scope) error {
out.VolumeID = in.VolumeID
out.FSType = in.FSType
out.ReadOnly = in.ReadOnly
return nil
}
func Convert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource(in *api.PortworxVolumeSource, out *PortworxVolumeSource, s conversion.Scope) error {
return autoConvert_api_PortworxVolumeSource_To_v1_PortworxVolumeSource(in, out, s)
}
func autoConvert_v1_Preconditions_To_api_Preconditions(in *Preconditions, out *api.Preconditions, s conversion.Scope) error {
out.UID = (*types.UID)(unsafe.Pointer(in.UID))
return nil
......@@ -4458,6 +4484,7 @@ func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.
out.AzureDisk = (*api.AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*api.PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.Projected = (*api.ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......@@ -4490,6 +4517,7 @@ func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *
out.AzureDisk = (*AzureDiskVolumeSource)(unsafe.Pointer(in.AzureDisk))
out.PhotonPersistentDisk = (*PhotonPersistentDiskVolumeSource)(unsafe.Pointer(in.PhotonPersistentDisk))
out.Projected = (*ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
return nil
}
......
......@@ -155,6 +155,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PodTemplate, InType: reflect.TypeOf(&PodTemplate{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PodTemplateList, InType: reflect.TypeOf(&PodTemplateList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PodTemplateSpec, InType: reflect.TypeOf(&PodTemplateSpec{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PortworxVolumeSource, InType: reflect.TypeOf(&PortworxVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Preconditions, InType: reflect.TypeOf(&Preconditions{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PreferAvoidPodsEntry, InType: reflect.TypeOf(&PreferAvoidPodsEntry{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_PreferredSchedulingTerm, InType: reflect.TypeOf(&PreferredSchedulingTerm{})},
......@@ -2015,6 +2016,11 @@ func DeepCopy_v1_PersistentVolumeSource(in interface{}, out interface{}, c *conv
*out = new(PhotonPersistentDiskVolumeSource)
**out = **in
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......@@ -2513,6 +2519,15 @@ func DeepCopy_v1_PodTemplateSpec(in interface{}, out interface{}, c *conversion.
}
}
func DeepCopy_v1_PortworxVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PortworxVolumeSource)
out := out.(*PortworxVolumeSource)
*out = *in
return nil
}
}
func DeepCopy_v1_Preconditions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Preconditions)
......@@ -3420,6 +3435,11 @@ func DeepCopy_v1_VolumeSource(in interface{}, out interface{}, c *conversion.Clo
return err
}
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......
......@@ -158,6 +158,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PodTemplate, InType: reflect.TypeOf(&PodTemplate{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PodTemplateList, InType: reflect.TypeOf(&PodTemplateList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PodTemplateSpec, InType: reflect.TypeOf(&PodTemplateSpec{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PortworxVolumeSource, InType: reflect.TypeOf(&PortworxVolumeSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Preconditions, InType: reflect.TypeOf(&Preconditions{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PreferAvoidPodsEntry, InType: reflect.TypeOf(&PreferAvoidPodsEntry{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_PreferredSchedulingTerm, InType: reflect.TypeOf(&PreferredSchedulingTerm{})},
......@@ -2059,6 +2060,11 @@ func DeepCopy_api_PersistentVolumeSource(in interface{}, out interface{}, c *con
*out = new(PhotonPersistentDiskVolumeSource)
**out = **in
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......@@ -2557,6 +2563,15 @@ func DeepCopy_api_PodTemplateSpec(in interface{}, out interface{}, c *conversion
}
}
func DeepCopy_api_PortworxVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*PortworxVolumeSource)
out := out.(*PortworxVolumeSource)
*out = *in
return nil
}
}
func DeepCopy_api_Preconditions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Preconditions)
......@@ -3447,6 +3462,11 @@ func DeepCopy_api_VolumeSource(in interface{}, out interface{}, c *conversion.Cl
return err
}
}
if in.PortworxVolume != nil {
in, out := &in.PortworxVolume, &out.PortworxVolume
*out = new(PortworxVolumeSource)
**out = **in
}
return nil
}
}
......
......@@ -917,6 +917,7 @@ var (
AzureDisk FSType = "azureDisk"
PhotonPersistentDisk FSType = "photonPersistentDisk"
Projected FSType = "projected"
PortworxVolume FSType = "portworxVolume"
All FSType = "*"
)
......
......@@ -16604,3 +16604,86 @@ go_library(
],
tags = ["automanaged"],
)
go_library(
name = "github.com/libopenstorage/openstorage/api",
srcs = [
"github.com/libopenstorage/openstorage/api/api.go",
"github.com/libopenstorage/openstorage/api/api.pb.go",
"github.com/libopenstorage/openstorage/api/status.go",
],
tags = ["automanaged"],
deps = [
"//vendor:github.com/golang/protobuf/proto",
"//vendor:go.pedge.io/pb/go/google/protobuf",
],
)
go_library(
name = "github.com/libopenstorage/openstorage/api/client",
srcs = [
"github.com/libopenstorage/openstorage/api/client/client.go",
"github.com/libopenstorage/openstorage/api/client/request.go",
],
tags = ["automanaged"],
)
go_library(
name = "github.com/libopenstorage/openstorage/api/client/volume",
srcs = [
"github.com/libopenstorage/openstorage/api/client/volume/client.go",
"github.com/libopenstorage/openstorage/api/client/volume/volume.go",
],
tags = ["automanaged"],
deps = [
"//vendor:github.com/libopenstorage/openstorage/api",
"//vendor:github.com/libopenstorage/openstorage/api/client",
"//vendor:github.com/libopenstorage/openstorage/volume",
],
)
go_library(
name = "github.com/libopenstorage/openstorage/api/spec",
srcs = ["github.com/libopenstorage/openstorage/api/spec/spec_handler.go"],
tags = ["automanaged"],
deps = [
"//vendor:github.com/libopenstorage/openstorage/api",
"//vendor:github.com/libopenstorage/openstorage/pkg/units",
],
)
go_library(
name = "github.com/libopenstorage/openstorage/pkg/units",
srcs = ["github.com/libopenstorage/openstorage/pkg/units/units.go"],
tags = ["automanaged"],
)
go_library(
name = "github.com/libopenstorage/openstorage/volume",
srcs = [
"github.com/libopenstorage/openstorage/volume/volume.go",
"github.com/libopenstorage/openstorage/volume/volume_driver_registry.go",
"github.com/libopenstorage/openstorage/volume/volume_not_supported.go",
],
tags = ["automanaged"],
deps = ["//vendor:github.com/libopenstorage/openstorage/api"],
)
go_library(
name = "go.pedge.io/pb/go/google/protobuf",
srcs = [
"go.pedge.io/pb/go/google/protobuf/any.pb.go",
"go.pedge.io/pb/go/google/protobuf/api.pb.go",
"go.pedge.io/pb/go/google/protobuf/duration.pb.go",
"go.pedge.io/pb/go/google/protobuf/empty.pb.go",
"go.pedge.io/pb/go/google/protobuf/field_mask.pb.go",
"go.pedge.io/pb/go/google/protobuf/protobuf.gen.go",
"go.pedge.io/pb/go/google/protobuf/source_context.pb.go",
"go.pedge.io/pb/go/google/protobuf/struct.pb.go",
"go.pedge.io/pb/go/google/protobuf/timestamp.pb.go",
"go.pedge.io/pb/go/google/protobuf/type.pb.go",
"go.pedge.io/pb/go/google/protobuf/wrappers.pb.go",
],
tags = ["automanaged"],
deps = ["//vendor:github.com/golang/protobuf/proto"],
)
package api
import (
"fmt"
"strconv"
"strings"
"time"
)
// Strings for VolumeSpec
const (
Name = "name"
SpecEphemeral = "ephemeral"
SpecShared = "shared"
SpecSize = "size"
SpecScale = "scale"
SpecFilesystem = "fs"
SpecBlockSize = "block_size"
SpecHaLevel = "repl"
SpecPriority = "io_priority"
SpecSnapshotInterval = "snap_interval"
SpecAggregationLevel = "aggregation_level"
SpecDedupe = "dedupe"
SpecPassphrase = "passphrase"
)
// OptionKey specifies a set of recognized query params.
const (
// OptName query parameter used to lookup volume by name.
OptName = "Name"
// OptVolumeID query parameter used to lookup volume by ID.
OptVolumeID = "VolumeID"
// OptLabel query parameter used to lookup volume by set of labels.
OptLabel = "Label"
// OptConfigLabel query parameter used to lookup volume by set of labels.
OptConfigLabel = "ConfigLabel"
// OptCumulative query parameter used to request cumulative stats.
OptCumulative = "Cumulative"
)
// Api client-server Constants
const (
OsdVolumePath = "osd-volumes"
OsdSnapshotPath = "osd-snapshot"
)
// Node describes the state of a node.
// It includes the current physical state (CPU, memory, storage, network usage) as
// well as the containers running on the system.
type Node struct {
Id string
Cpu float64 // percentage.
MemTotal uint64
MemUsed uint64
MemFree uint64
Avgload int
Status Status
GenNumber uint64
Disks map[string]StorageResource
MgmtIp string
DataIp string
Timestamp time.Time
StartTime time.Time
Hostname string
NodeData map[string]interface{}
// User defined labels for node. Key Value pairs
NodeLabels map[string]string
}
// Cluster represents the state of the cluster.
type Cluster struct {
Status Status
// Id is the ID of the cluster.
Id string
// NodeId is the ID of the node on which this cluster object
// is initialized
NodeId string
// Nodes is an array of all the nodes in the cluster.
Nodes []Node
}
// StatPoint represents the basic structure of a single Stat reported
// TODO: This is the first step to introduce stats in openstorage.
// Follow up task is to introduce an API for logging stats
type StatPoint struct {
// Name of the Stat
Name string
// Tags for the Stat
Tags map[string]string
// Fields and values of the stat
Fields map[string]interface{}
// Timestamp in Unix format
Timestamp int64
}
func DriverTypeSimpleValueOf(s string) (DriverType, error) {
obj, err := simpleValueOf("driver_type", DriverType_value, s)
return DriverType(obj), err
}
func (x DriverType) SimpleString() string {
return simpleString("driver_type", DriverType_name, int32(x))
}
func FSTypeSimpleValueOf(s string) (FSType, error) {
obj, err := simpleValueOf("fs_type", FSType_value, s)
return FSType(obj), err
}
func (x FSType) SimpleString() string {
return simpleString("fs_type", FSType_name, int32(x))
}
func CosTypeSimpleValueOf(s string) (CosType, error) {
obj, err := simpleValueOf("cos_type", CosType_value, s)
return CosType(obj), err
}
func (x CosType) SimpleString() string {
return simpleString("cos_type", CosType_name, int32(x))
}
func GraphDriverChangeTypeSimpleValueOf(s string) (GraphDriverChangeType, error) {
obj, err := simpleValueOf("graph_driver_change_type", GraphDriverChangeType_value, s)
return GraphDriverChangeType(obj), err
}
func (x GraphDriverChangeType) SimpleString() string {
return simpleString("graph_driver_change_type", GraphDriverChangeType_name, int32(x))
}
func VolumeActionParamSimpleValueOf(s string) (VolumeActionParam, error) {
obj, err := simpleValueOf("volume_action_param", VolumeActionParam_value, s)
return VolumeActionParam(obj), err
}
func (x VolumeActionParam) SimpleString() string {
return simpleString("volume_action_param", VolumeActionParam_name, int32(x))
}
func VolumeStateSimpleValueOf(s string) (VolumeState, error) {
obj, err := simpleValueOf("volume_state", VolumeState_value, s)
return VolumeState(obj), err
}
func (x VolumeState) SimpleString() string {
return simpleString("volume_state", VolumeState_name, int32(x))
}
func VolumeStatusSimpleValueOf(s string) (VolumeStatus, error) {
obj, err := simpleValueOf("volume_status", VolumeStatus_value, s)
return VolumeStatus(obj), err
}
func (x VolumeStatus) SimpleString() string {
return simpleString("volume_status", VolumeStatus_name, int32(x))
}
func simpleValueOf(typeString string, valueMap map[string]int32, s string) (int32, error) {
obj, ok := valueMap[strings.ToUpper(fmt.Sprintf("%s_%s", typeString, s))]
if !ok {
return 0, fmt.Errorf("no openstorage.%s for %s", strings.ToUpper(typeString), s)
}
return obj, nil
}
func simpleString(typeString string, nameMap map[int32]string, v int32) string {
s, ok := nameMap[v]
if !ok {
return strconv.Itoa(int(v))
}
return strings.TrimPrefix(strings.ToLower(s), fmt.Sprintf("%s_", strings.ToLower(typeString)))
}
func toSec(ms uint64) uint64 {
return ms / 1000
}
func (v *Stats) WriteThroughput() uint64 {
if v.IntervalMs == 0 {
return 0
}
return (v.WriteBytes) / toSec(v.IntervalMs)
}
func (v *Stats) ReadThroughput() uint64 {
if v.IntervalMs == 0 {
return 0
}
return (v.ReadBytes) / toSec(v.IntervalMs)
}
func (v *Stats) Latency() uint64 {
ops := v.Writes + v.Reads
if ops == 0 {
return 0
}
return (uint64)((v.IoMs * 1000) / (v.Writes + v.Reads))
}
func (v *Stats) Iops() uint64 {
if v.IntervalMs == 0 {
return 0
}
return (v.Writes + v.Reads) / toSec(v.IntervalMs)
}
package client
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"sync"
"time"
)
var (
httpCache = make(map[string]*http.Client)
cacheLock sync.Mutex
)
// NewClient returns a new REST client for specified server.
func NewClient(host string, version string) (*Client, error) {
baseURL, err := url.Parse(host)
if err != nil {
return nil, err
}
if baseURL.Path == "" {
baseURL.Path = "/"
}
unix2HTTP(baseURL)
c := &Client{
base: baseURL,
version: version,
httpClient: getHttpClient(host),
}
return c, nil
}
func GetUnixServerPath(socketName string, paths ...string) string {
serverPath := "unix://"
for _, path := range paths {
serverPath = serverPath + path
}
serverPath = serverPath + socketName + ".sock"
return serverPath
}
// Client is an HTTP REST wrapper. Use one of Get/Post/Put/Delete to get a request
// object.
type Client struct {
base *url.URL
version string
httpClient *http.Client
}
// Status sends a Status request at the /status REST endpoint.
func (c *Client) Status() (*Status, error) {
status := &Status{}
err := c.Get().UsePath("/status").Do().Unmarshal(status)
return status, err
}
// Version send a request at the /versions REST endpoint.
func (c *Client) Versions(endpoint string) ([]string, error) {
versions := []string{}
err := c.Get().Resource(endpoint + "/versions").Do().Unmarshal(&versions)
return versions, err
}
// Get returns a Request object setup for GET call.
func (c *Client) Get() *Request {
return NewRequest(c.httpClient, c.base, "GET", c.version)
}
// Post returns a Request object setup for POST call.
func (c *Client) Post() *Request {
return NewRequest(c.httpClient, c.base, "POST", c.version)
}
// Put returns a Request object setup for PUT call.
func (c *Client) Put() *Request {
return NewRequest(c.httpClient, c.base, "PUT", c.version)
}
// Put returns a Request object setup for DELETE call.
func (c *Client) Delete() *Request {
return NewRequest(c.httpClient, c.base, "DELETE", c.version)
}
func unix2HTTP(u *url.URL) {
if u.Scheme == "unix" {
// Override the main URL object so the HTTP lib won't complain
u.Scheme = "http"
u.Host = "unix.sock"
u.Path = ""
}
}
func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client {
httpTransport := &http.Transport{
TLSClientConfig: tlsConfig,
}
switch u.Scheme {
case "unix":
socketPath := u.Path
unixDial := func(proto, addr string) (net.Conn, error) {
ret, err := net.DialTimeout("unix", socketPath, timeout)
return ret, err
}
httpTransport.Dial = unixDial
unix2HTTP(u)
default:
httpTransport.Dial = func(proto, addr string) (net.Conn, error) {
return net.DialTimeout(proto, addr, timeout)
}
}
return &http.Client{Transport: httpTransport}
}
func getHttpClient(host string) *http.Client {
c, ok := httpCache[host]
if !ok {
cacheLock.Lock()
defer cacheLock.Unlock()
c, ok = httpCache[host]
if !ok {
u, err := url.Parse(host)
if err != nil {
// TODO(pedge): clean up
fmt.Println("Failed to parse into url", host)
return nil
}
if u.Path == "" {
u.Path = "/"
}
c = newHTTPClient(u, nil, 10*time.Second)
httpCache[host] = c
}
}
return c
}
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path"
"strconv"
"strings"
"time"
)
// Request is contructed iteratively by the client and finally dispatched.
// A REST endpoint is accessed with the following convention:
// base_url/<version>/<resource>/[<instance>]
type Request struct {
client *http.Client
version string
verb string
path string
base *url.URL
params url.Values
headers http.Header
resource string
instance string
err error
body []byte
req *http.Request
resp *http.Response
timeout time.Duration
}
// Response is a representation of HTTP response received from the server.
type Response struct {
status string
statusCode int
err error
body []byte
}
// Status upon error, attempts to parse the body of a response into a meaningful status.
type Status struct {
Message string
ErrorCode int
}
// NewRequest instance
func NewRequest(client *http.Client, base *url.URL, verb string, version string) *Request {
return &Request{
client: client,
verb: verb,
base: base,
path: base.Path,
version: version,
}
}
func checkExists(mustExist string, before string) error {
if len(mustExist) == 0 {
return fmt.Errorf("%q should be set before setting %q", mustExist, before)
}
return nil
}
func checkSet(name string, s *string, newval string) error {
if len(*s) != 0 {
return fmt.Errorf("%q already set to %q, cannot change to %q",
name, *s, newval)
}
*s = newval
return nil
}
// Resource specifies the resource to be accessed.
func (r *Request) Resource(resource string) *Request {
if r.err == nil {
r.err = checkSet("resource", &r.resource, resource)
}
return r
}
// Instance specifies the instance of the resource to be accessed.
func (r *Request) Instance(instance string) *Request {
if r.err == nil {
r.err = checkExists("resource", "instance")
if r.err == nil {
r.err = checkSet("instance", &r.instance, instance)
}
}
return r
}
// UsePath use the specified path and don't build up a request.
func (r *Request) UsePath(path string) *Request {
if r.err == nil {
r.err = checkSet("path", &r.path, path)
}
return r
}
// QueryOption adds specified options to query.
func (r *Request) QueryOption(key string, value string) *Request {
if r.err != nil {
return r
}
if r.params == nil {
r.params = make(url.Values)
}
r.params.Add(string(key), value)
return r
}
// QueryOptionLabel adds specified label to query.
func (r *Request) QueryOptionLabel(key string, labels map[string]string) *Request {
if r.err != nil {
return r
}
if b, err := json.Marshal(labels); err != nil {
r.err = err
} else {
if r.params == nil {
r.params = make(url.Values)
}
r.params.Add(string(key), string(b))
}
return r
}
// SetHeader adds specified header values to query.
func (r *Request) SetHeader(key, value string) *Request {
if r.headers == nil {
r.headers = http.Header{}
}
r.headers.Set(key, value)
return r
}
// Timeout makes the request use the given duration as a timeout. Sets the "timeout"
// parameter.
func (r *Request) Timeout(d time.Duration) *Request {
if r.err != nil {
return r
}
r.timeout = d
return r
}
// Body sets the request Body.
func (r *Request) Body(v interface{}) *Request {
var err error
if r.err != nil {
return r
}
r.body, err = json.Marshal(v)
if err != nil {
r.err = err
return r
}
return r
}
// URL returns the current working URL.
func (r *Request) URL() *url.URL {
u := *r.base
p := r.path
if len(r.version) != 0 {
p = path.Join(p, strings.ToLower(r.version))
}
if len(r.resource) != 0 {
p = path.Join(p, strings.ToLower(r.resource))
if len(r.instance) != 0 {
p = path.Join(p, r.instance)
}
}
u.Path = p
query := url.Values{}
for key, values := range r.params {
for _, value := range values {
query.Add(key, value)
}
}
if r.timeout != 0 {
query.Set("timeout", r.timeout.String())
}
u.RawQuery = query.Encode()
return &u
}
// headerVal for key as an int. Return false if header is not present or valid.
func headerVal(key string, resp *http.Response) (int, bool) {
if h := resp.Header.Get(key); len(h) > 0 {
if i, err := strconv.Atoi(h); err == nil {
return i, true
}
}
return 0, false
}
func parseHTTPStatus(resp *http.Response, body []byte) error {
var (
status *Status
err error
)
httpOK := resp.StatusCode >= http.StatusOK && resp.StatusCode <= http.StatusPartialContent
hasStatus := false
if body != nil {
err = json.Unmarshal(body, status)
if err == nil && status.Message != "" {
hasStatus = true
}
}
// If the status is NG, return an error regardless of HTTP status.
if hasStatus && status.ErrorCode != 0 {
return fmt.Errorf("Error %v : %v", status.ErrorCode, status.Message)
}
// Status is good and HTTP status is good, everything is good
if httpOK {
return nil
}
// If HTTP status is NG, return an error.
return fmt.Errorf("HTTP error %d", resp.StatusCode)
}
// Do executes the request and returns a Response.
func (r *Request) Do() *Response {
var (
err error
req *http.Request
resp *http.Response
url string
body []byte
)
if r.err != nil {
return &Response{err: r.err}
}
url = r.URL().String()
req, err = http.NewRequest(r.verb, url, bytes.NewBuffer(r.body))
if err != nil {
return &Response{err: err}
}
if r.headers == nil {
r.headers = http.Header{}
}
req.Header = r.headers
req.Header.Set("Content-Type", "application/json")
resp, err = r.client.Do(req)
if err != nil {
return &Response{err: err}
}
if resp.Body != nil {
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
}
if err != nil {
return &Response{err: err}
}
return &Response{
status: resp.Status,
statusCode: resp.StatusCode,
body: body,
err: parseHTTPStatus(resp, body),
}
}
// Body return http body, valid only if there is no error
func (r Response) Body() ([]byte, error) {
return r.body, r.err
}
// StatusCode HTTP status code returned.
func (r Response) StatusCode() int {
return r.statusCode
}
// Unmarshal result into obj
func (r Response) Unmarshal(v interface{}) error {
if r.err != nil {
return r.err
}
return json.Unmarshal(r.body, v)
}
// Error executing the request.
func (r Response) Error() error {
return r.err
}
func (r Response) FormatError() error {
if len(r.body) == 0 {
return fmt.Errorf("Error: %v", r.err)
} else {
return fmt.Errorf("HTTP-%d: %s", r.statusCode, string(r.body))
}
}
package volume
import (
"fmt"
"github.com/libopenstorage/openstorage/api/client"
"github.com/libopenstorage/openstorage/volume"
"github.com/libopenstorage/openstorage/api"
)
// VolumeDriver returns a REST wrapper for the VolumeDriver interface.
func VolumeDriver(c *client.Client) volume.VolumeDriver {
return newVolumeClient(c)
}
// NewDriver returns a new REST client of the supplied version for specified driver.
// host: REST endpoint [http://<ip>:<port> OR unix://<path-to-unix-socket>]. default: [unix:///var/lib/osd/<driverName>.sock]
// version: Volume API version
func NewDriverClient(host, driverName, version string) (*client.Client, error) {
if driverName == "" {
return nil, fmt.Errorf("Driver Name cannot be empty")
}
if host == "" {
host = client.GetUnixServerPath(driverName, volume.DriverAPIBase)
}
if version == "" {
// Set the default version
version = volume.APIVersion
}
return client.NewClient(host, version)
}
// GetSupportedDriverVersions returns a list of supported versions
// for the provided driver. It uses the given server endpoint or the
// standard unix domain socket
func GetSupportedDriverVersions(driverName, host string) ([]string, error) {
// Get a client handler
if host == "" {
host = client.GetUnixServerPath(driverName, volume.DriverAPIBase)
}
client, err := client.NewClient(host, "")
if err != nil {
return []string{}, err
}
versions, err := client.Versions(api.OsdVolumePath)
if err != nil {
return []string{}, err
}
return versions, nil
}
package spec
import (
"fmt"
"regexp"
"strconv"
"github.com/libopenstorage/openstorage/api"
"github.com/libopenstorage/openstorage/pkg/units"
)
// SpecHandler provides conversion function from what gets passed in over the
// plugin API to an api.VolumeSpec object.
type SpecHandler interface {
// SpecFromString parses options from the name.
// If the scheduler was unable to pass in the volume spec via the API,
// the spec can be passed in via the name in the format:
// "key=value;key=value;name=volname"
// If the spec was parsed, it returns:
// (true, parsed_spec, parsed_name)
// If the input string didn't contain the string, it returns:
// (false, DefaultSpec(), inputString)
SpecFromString(inputString string) (bool, *api.VolumeSpec, string)
// SpecFromOpts parses in docker options passed in the the docker run
// command of the form --opt name=value
// If the options are validated then it returns:
// (resultant_VolumeSpec, nil)
// If the options have invalid values then it returns:
// (nil, error)
SpecFromOpts(opts map[string]string) (*api.VolumeSpec, error)
// Returns a default VolumeSpec if no docker options or string encoding
// was provided.
DefaultSpec() *api.VolumeSpec
}
var (
nameRegex = regexp.MustCompile(api.Name + "=([0-9A-Za-z]+),?")
sizeRegex = regexp.MustCompile(api.SpecSize + "=([0-9A-Za-z]+),?")
scaleRegex = regexp.MustCompile(api.SpecScale + "=([0-9A-Za-z]+),?")
fsRegex = regexp.MustCompile(api.SpecFilesystem + "=([0-9A-Za-z]+),?")
bsRegex = regexp.MustCompile(api.SpecBlockSize + "=([0-9]+),?")
haRegex = regexp.MustCompile(api.SpecHaLevel + "=([0-9]+),?")
cosRegex = regexp.MustCompile(api.SpecPriority + "=([A-Za-z]+),?")
sharedRegex = regexp.MustCompile(api.SpecShared + "=([A-Za-z]+),?")
passphraseRegex = regexp.MustCompile(api.SpecPassphrase + "=([0-9A-Za-z_@./#&+-]+),?")
)
type specHandler struct {
}
func NewSpecHandler() SpecHandler {
return &specHandler{}
}
func (d *specHandler) cosLevel(cos string) (uint32, error) {
switch cos {
case "high", "3":
return uint32(api.CosType_HIGH), nil
case "medium", "2":
return uint32(api.CosType_MEDIUM), nil
case "low", "1", "":
return uint32(api.CosType_LOW), nil
}
return uint32(api.CosType_LOW),
fmt.Errorf("Cos must be one of %q | %q | %q", "high", "medium", "low")
}
func (d *specHandler) getVal(r *regexp.Regexp, str string) (bool, string) {
found := r.FindString(str)
if found == "" {
return false, ""
}
submatches := r.FindStringSubmatch(str)
if len(submatches) < 2 {
return false, ""
}
val := submatches[1]
return true, val
}
func (d *specHandler) DefaultSpec() *api.VolumeSpec {
return &api.VolumeSpec{
VolumeLabels: make(map[string]string),
Format: api.FSType_FS_TYPE_EXT4,
HaLevel: 1,
}
}
func (d *specHandler) SpecFromOpts(
opts map[string]string,
) (*api.VolumeSpec, error) {
spec := d.DefaultSpec()
for k, v := range opts {
switch k {
case api.SpecEphemeral:
spec.Ephemeral, _ = strconv.ParseBool(v)
case api.SpecSize:
if size, err := units.Parse(v); err != nil {
return nil, err
} else {
spec.Size = uint64(size)
}
case api.SpecFilesystem:
if value, err := api.FSTypeSimpleValueOf(v); err != nil {
return nil, err
} else {
spec.Format = value
}
case api.SpecBlockSize:
if blockSize, err := units.Parse(v); err != nil {
return nil, err
} else {
spec.BlockSize = blockSize
}
case api.SpecHaLevel:
haLevel, _ := strconv.ParseInt(v, 10, 64)
spec.HaLevel = haLevel
case api.SpecPriority:
cos, _ := api.CosTypeSimpleValueOf(v)
spec.Cos = cos
case api.SpecDedupe:
spec.Dedupe, _ = strconv.ParseBool(v)
case api.SpecSnapshotInterval:
snapshotInterval, _ := strconv.ParseUint(v, 10, 32)
spec.SnapshotInterval = uint32(snapshotInterval)
case api.SpecAggregationLevel:
aggregationLevel, _ := strconv.ParseUint(v, 10, 32)
spec.AggregationLevel = uint32(aggregationLevel)
case api.SpecShared:
if shared, err := strconv.ParseBool(v); err != nil {
return nil, err
} else {
spec.Shared = shared
}
case api.SpecPassphrase:
spec.Encrypted = true
spec.Passphrase = v
default:
spec.VolumeLabels[k] = v
}
}
return spec, nil
}
func (d *specHandler) SpecFromString(
str string,
) (bool, *api.VolumeSpec, string) {
// If we can't parse the name, the rest of the spec is invalid.
ok, name := d.getVal(nameRegex, str)
if !ok {
return false, d.DefaultSpec(), str
}
opts := make(map[string]string)
if ok, sz := d.getVal(sizeRegex, str); ok {
opts[api.SpecSize] = sz
}
if ok, scale := d.getVal(scaleRegex, str); ok {
opts[api.SpecScale] = scale
}
if ok, fs := d.getVal(fsRegex, str); ok {
opts[api.SpecFilesystem] = fs
}
if ok, bs := d.getVal(bsRegex, str); ok {
opts[api.SpecBlockSize] = bs
}
if ok, ha := d.getVal(haRegex, str); ok {
opts[api.SpecHaLevel] = ha
}
if ok, priority := d.getVal(cosRegex, str); ok {
opts[api.SpecPriority] = priority
}
if ok, shared := d.getVal(sharedRegex, str); ok {
opts[api.SpecShared] = shared
}
if ok, passphrase := d.getVal(passphraseRegex, str); ok {
opts[api.SpecPassphrase] = passphrase
}
spec, err := d.SpecFromOpts(opts)
if err != nil {
return false, d.DefaultSpec(), name
}
return true, spec, name
}
package api
type StatusKind int32
const (
// StatusSeverityLow indicates an OK status
StatusSeverityLow StatusKind = iota
// StatusSeverityMedium indicates a status which is in transition from OK to BAD or vice versa
StatusSeverityMedium
// StatusSeverityHigh indicates a BAD status
StatusSeverityHigh
)
var statusToStatusKind = map[Status]StatusKind{
Status_STATUS_NONE: StatusSeverityHigh,
Status_STATUS_INIT: StatusSeverityMedium,
Status_STATUS_OK: StatusSeverityLow,
Status_STATUS_OFFLINE: StatusSeverityHigh,
Status_STATUS_ERROR: StatusSeverityHigh,
Status_STATUS_NOT_IN_QUORUM: StatusSeverityHigh,
Status_STATUS_DECOMMISSION: StatusSeverityHigh,
Status_STATUS_MAINTENANCE: StatusSeverityHigh,
Status_STATUS_STORAGE_DOWN: StatusSeverityHigh,
Status_STATUS_STORAGE_DEGRADED: StatusSeverityHigh,
Status_STATUS_NEEDS_REBOOT: StatusSeverityHigh,
Status_STATUS_STORAGE_REBALANCE: StatusSeverityMedium,
Status_STATUS_STORAGE_DRIVE_REPLACE: StatusSeverityMedium,
// Add statuses before MAX
Status_STATUS_MAX: StatusSeverityHigh,
}
func StatusSimpleValueOf(s string) (Status, error) {
obj, err := simpleValueOf("status", Status_value, s)
return Status(obj), err
}
func (x Status) SimpleString() string {
return simpleString("status", Status_name, int32(x))
}
func (x Status) StatusKind() StatusKind {
statusType, _ := statusToStatusKind[x]
return statusType
}
// StatusKindMapLength used only for unit testing
func StatusKindMapLength() int {
return len(statusToStatusKind)
}
package units
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
const (
_ = iota
// KiB 1024 bytes
KiB = 1 << (10 * iota)
// MiB 1024 KiB
MiB
// GiB 1024 MiB
GiB
// TiB 1024 GiB
TiB
// PiB 1024 TiB
PiB
)
const (
// KB 1000 bytes
KB = 1000
// MB 1000 KB
MB = KB * 1000
// GB 1000 MB
GB = MB * 1000
// TB 1000 GB
TB = GB * 1000
// PB 1000 TB
PB = TB * 1000
)
var (
unitMap = map[string]int64{
"B": 1,
"b": 1,
"KB": KB,
"kb": KB,
"MB": MB,
"mb": MB,
"GB": GB,
"gb": GB,
"TB": TB,
"tb": TB,
"PB": PB,
"pb": PB,
"K": KiB,
"k": KiB,
"M": MiB,
"m": MiB,
"G": GiB,
"g": GiB,
"T": TiB,
"t": TiB,
"P": PiB,
"p": PiB,
"KiB": KiB,
"MiB": MiB,
"GiB": GiB,
"TiB": TiB,
"PiB": PiB,
"Mi": MiB,
"Gi": GiB,
"Ti": TiB,
"Pi": PiB,
}
)
var unitPattern = regexp.MustCompile(
"([0-9]+)(.[0-9]+)*\\s*(B|b|K|k|M|m|G|g|T|t|P|p|KB|kb|KiB|MB|mb|MiB|Mi|GB|gb|GiB|Gi|TB|tb|TiB|Ti|PB|pb|PiB|Pi|)")
var BadUnit = errors.New("Bad unit")
func String(b uint64) string {
if b > PiB {
return fmt.Sprintf("%.2f PiB", float64(b)/float64(PiB))
}
if b > TiB {
return fmt.Sprintf("%.2f TiB", float64(b)/float64(TiB))
}
if b > GiB {
return fmt.Sprintf("%.1f TiB", float64(b)/float64(GiB))
}
if b > MiB {
return fmt.Sprintf("%v MiB", b/MiB)
}
if b > KiB {
return fmt.Sprintf("%v KiB", b/KiB)
}
return fmt.Sprintf("%v bytes", b)
}
func Parse(bUnit string) (int64, error) {
ustring := strings.TrimSpace(bUnit)
unitPattern.Longest()
if !unitPattern.MatchString(ustring) {
return -1, fmt.Errorf("Unit parse error: %s", bUnit)
}
matches := unitPattern.FindStringSubmatch(ustring)
if len(matches) == 0 || len(matches) > 4 {
return -1, fmt.Errorf(
"Unit parse error: invalid count of fields (%v)",
len(matches))
}
if len(matches) == 1 {
return strconv.ParseInt(ustring, 10, 64)
}
shift := 0
if len(matches) == 4 {
shift = 1
}
if len(matches) == 2 {
return -1, fmt.Errorf("Unit parse error: invalid fields %v",
matches)
}
if ustring != matches[0] {
return -1, fmt.Errorf("Unit parse error: invalid fields %v",
matches)
}
multiplier, ok := unitMap[matches[2+shift]]
if !ok {
multiplier = unitMap["G"]
}
base, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {
return -1, fmt.Errorf("Invalid number")
}
return base * multiplier, nil
}
## Volume Drivers
Volume drivers implement the [Volume Plugin Interface](https://docs.docker.com/engine/extend/plugins_volume/).
This provides an interface to register a volume driver and advertise the driver to Docker. Registering a driver with this volume interface will cause Docker to be able to communicate with the driver to create and assign volumes to a container.
A volume spec is needed to create a volume. A volume spec looks like:
```
// VolumeSpec has the properties needed to create a volume.
type VolumeSpec struct {
// Ephemeral storage
Ephemeral bool
// Thin provisioned volume size in bytes
Size uint64
// Format disk with this FileSystem
Format Filesystem
// BlockSize for file system
BlockSize int
// HA Level specifies the number of nodes that are
// allowed to fail, and yet data is availabel.
// A value of 0 implies that data is not erasure coded,
// a failure of a node will lead to data loss.
HALevel int
// This disk's CoS
Cos VolumeCos
// Perform dedupe on this disk
Dedupe bool
// SnapshotInterval in minutes, set to 0 to disable Snapshots
SnapshotInterval int
// Volume configuration labels
ConfigLabels Labels
}
```
Various volume driver implementations can be found in the `drivers` directory.
### Block Drivers
Block drivers operate at the block layer. They provide raw volumes formatted with a user specified filesystem. This volume is then mounted into the container at a path specified using the `docker run -v` option.
### File Drivers
File drivers operate at the filesystem layer.
package volume
import (
"errors"
"github.com/libopenstorage/openstorage/api"
)
var (
ErrAlreadyShutdown = errors.New("VolumeDriverProvider already shutdown")
ErrExist = errors.New("Driver already exists")
ErrDriverNotFound = errors.New("Driver implementation not found")
ErrDriverInitializing = errors.New("Driver is initializing")
ErrEnoEnt = errors.New("Volume does not exist.")
ErrEnomem = errors.New("Out of memory.")
ErrEinval = errors.New("Invalid argument")
ErrVolDetached = errors.New("Volume is detached")
ErrVolAttached = errors.New("Volume is attached")
ErrVolAttachedOnRemoteNode = errors.New("Volume is attached on another node")
ErrVolAttachedScale = errors.New("Volume is attached but can be scaled")
ErrVolHasSnaps = errors.New("Volume has snapshots associated")
ErrNotSupported = errors.New("Operation not supported")
)
// Constants used by the VolumeDriver
const (
APIVersion = "v1"
PluginAPIBase = "/run/docker/plugins/"
DriverAPIBase = "/var/lib/osd/driver/"
MountBase = "/var/lib/osd/mounts/"
VolumeBase = "/var/lib/osd/"
)
type Store interface {
// Lock volume specified by volumeID.
Lock(volumeID string) (interface{}, error)
// Lock volume with token obtained from call to Lock.
Unlock(token interface{}) error
// CreateVol returns error if volume with the same ID already existe.
CreateVol(vol *api.Volume) error
// GetVol from volumeID.
GetVol(volumeID string) (*api.Volume, error)
// UpdateVol with vol
UpdateVol(vol *api.Volume) error
// DeleteVol. Returns error if volume does not exist.
DeleteVol(volumeID string) error
}
// VolumeDriver is the main interface to be implemented by any storage driver.
// Every driver must at minimum implement the ProtoDriver sub interface.
type VolumeDriver interface {
IODriver
ProtoDriver
BlockDriver
Enumerator
}
// IODriver interfaces applicable to object store interfaces.
type IODriver interface {
// Read sz bytes from specified volume at specified offset.
// Return number of bytes read and error.
Read(volumeID string, buf []byte, sz uint64, offset int64) (int64, error)
// Write sz bytes from specified volume at specified offset.
// Return number of bytes written and error.
Write(volumeID string, buf []byte, sz uint64, offset int64) (int64, error)
// Flush writes to stable storage.
// Return error.
Flush(volumeID string) error
}
type SnapshotDriver interface {
// Snapshot create volume snapshot.
// Errors ErrEnoEnt may be returned
Snapshot(volumeID string, readonly bool, locator *api.VolumeLocator) (string, error)
}
// ProtoDriver must be implemented by all volume drivers. It specifies the
// most basic functionality, such as creating and deleting volumes.
type ProtoDriver interface {
SnapshotDriver
// Name returns the name of the driver.
Name() string
// Type of this driver
Type() api.DriverType
// Create a new Vol for the specific volume spec.
// It returns a system generated VolumeID that uniquely identifies the volume
Create(locator *api.VolumeLocator, Source *api.Source, spec *api.VolumeSpec) (string, error)
// Delete volume.
// Errors ErrEnoEnt, ErrVolHasSnaps may be returned.
Delete(volumeID string) error
// Mount volume at specified path
// Errors ErrEnoEnt, ErrVolDetached may be returned.
Mount(volumeID string, mountPath string) error
// MountedAt return volume mounted at specified mountpath.
MountedAt(mountPath string) string
// Unmount volume at specified path
// Errors ErrEnoEnt, ErrVolDetached may be returned.
Unmount(volumeID string, mountPath string) error
// Update not all fields of the spec are supported, ErrNotSupported will be thrown for unsupported
// updates.
Set(volumeID string, locator *api.VolumeLocator, spec *api.VolumeSpec) error
// Stats for specified volume.
// cumulative stats are /proc/diskstats style stats.
// nonCumulative stats are stats for specific duration.
// Errors ErrEnoEnt may be returned
Stats(volumeID string, cumulative bool) (*api.Stats, error)
// Alerts on this volume.
// Errors ErrEnoEnt may be returned
Alerts(volumeID string) (*api.Alerts, error)
// GetActiveRequests get active requests
GetActiveRequests() (*api.ActiveRequests, error)
// Status returns a set of key-value pairs which give low
// level diagnostic status about this driver.
Status() [][2]string
// Shutdown and cleanup.
Shutdown()
}
// Enumerator provides a set of interfaces to get details on a set of volumes.
type Enumerator interface {
// Inspect specified volumes.
// Returns slice of volumes that were found.
Inspect(volumeIDs []string) ([]*api.Volume, error)
// Enumerate volumes that map to the volumeLocator. Locator fields may be regexp.
// If locator fields are left blank, this will return all volumes.
Enumerate(locator *api.VolumeLocator, labels map[string]string) ([]*api.Volume, error)
// Enumerate snaps for specified volumes
SnapEnumerate(volID []string, snapLabels map[string]string) ([]*api.Volume, error)
}
type StoreEnumerator interface {
Store
Enumerator
}
// BlockDriver needs to be implemented by block volume drivers. Filesystem volume
// drivers can ignore this interface and include the builtin DefaultBlockDriver.
type BlockDriver interface {
// Attach map device to the host.
// On success the devicePath specifies location where the device is exported
// Errors ErrEnoEnt, ErrVolAttached may be returned.
Attach(volumeID string) (string, error)
// Detach device from the host.
// Errors ErrEnoEnt, ErrVolDetached may be returned.
Detach(volumeID string) error
}
// VolumeDriverProvider provides VolumeDrivers.
type VolumeDriverProvider interface {
// Get gets the VolumeDriver for the given name.
// If a VolumeDriver was not created for the given name, the error ErrDriverNotFound is returned.
Get(name string) (VolumeDriver, error)
// Shutdown shuts down all volume drivers.
Shutdown() error
}
// VolumeDriverRegistry registers VolumeDrivers.
type VolumeDriverRegistry interface {
VolumeDriverProvider
// New creates the VolumeDriver for the given name.
// If a VolumeDriver was already created for the given name, the error ErrExist is returned.
Register(name string, params map[string]string) error
// Add inserts a new VolumeDriver provider with a well known name.
Add(name string, init func(map[string]string) (VolumeDriver, error)) error
}
// VolumeDriverRegistry constructs a new VolumeDriverRegistry.
func NewVolumeDriverRegistry(nameToInitFunc map[string]func(map[string]string) (VolumeDriver, error)) VolumeDriverRegistry {
return newVolumeDriverRegistry(nameToInitFunc)
}
package volume
import "sync"
type volumeDriverRegistry struct {
nameToInitFunc map[string]func(map[string]string) (VolumeDriver, error)
nameToVolumeDriver map[string]VolumeDriver
lock *sync.RWMutex
isShutdown bool
}
func newVolumeDriverRegistry(nameToInitFunc map[string]func(map[string]string) (VolumeDriver, error)) *volumeDriverRegistry {
return &volumeDriverRegistry{
nameToInitFunc,
make(map[string]VolumeDriver),
&sync.RWMutex{},
false,
}
}
func (v *volumeDriverRegistry) Get(name string) (VolumeDriver, error) {
v.lock.RLock()
defer v.lock.RUnlock()
if v.isShutdown {
return nil, ErrAlreadyShutdown
}
volumeDriver, ok := v.nameToVolumeDriver[name]
if !ok {
return nil, ErrDriverNotFound
}
return volumeDriver, nil
}
func (v *volumeDriverRegistry) Add(name string, init func(map[string]string) (VolumeDriver, error)) error {
v.nameToInitFunc[name] = init
return nil
}
func (v *volumeDriverRegistry) Register(name string, params map[string]string) error {
initFunc, ok := v.nameToInitFunc[name]
if !ok {
return ErrNotSupported
}
v.lock.Lock()
defer v.lock.Unlock()
if v.isShutdown {
return ErrAlreadyShutdown
}
if _, ok := v.nameToVolumeDriver[name]; ok {
return ErrExist
}
volumeDriver, err := initFunc(params)
if err != nil {
return err
}
v.nameToVolumeDriver[name] = volumeDriver
return nil
}
func (v *volumeDriverRegistry) Shutdown() error {
v.lock.Lock()
if v.isShutdown {
return ErrAlreadyShutdown
}
for _, volumeDriver := range v.nameToVolumeDriver {
volumeDriver.Shutdown()
}
v.isShutdown = true
return nil
}
package volume
import (
"github.com/libopenstorage/openstorage/api"
)
var (
// BlockNotSupported is a default (null) block driver implementation. This can be
// used by drivers that do not want to (or care about) implementing the attach,
// format and detach interfaces.
BlockNotSupported = &blockNotSupported{}
SnapshotNotSupported = &snapshotNotSupported{}
IONotSupported = &ioNotSupported{}
)
type blockNotSupported struct{}
func (b *blockNotSupported) Attach(volumeID string) (string, error) {
return "", ErrNotSupported
}
func (b *blockNotSupported) Detach(volumeID string) error {
return ErrNotSupported
}
type snapshotNotSupported struct{}
func (s *snapshotNotSupported) Snapshot(volumeID string, readonly bool, locator *api.VolumeLocator) (string, error) {
return "", ErrNotSupported
}
type ioNotSupported struct{}
func (i *ioNotSupported) Read(volumeID string, buffer []byte, size uint64, offset int64) (int64, error) {
return 0, ErrNotSupported
}
func (i *ioNotSupported) Write(volumeID string, buffer []byte, size uint64, offset int64) (int64, error) {
return 0, ErrNotSupported
}
func (i *ioNotSupported) Flush(volumeID string) error {
return ErrNotSupported
}
The MIT License (MIT)
Copyright (c) 2015 Peter Edge
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
// Code generated by protoc-gen-go.
// source: google/protobuf/any.proto
// DO NOT EDIT!
package google_protobuf
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// `Any` contains an arbitrary serialized message along with a URL
// that describes the type of the serialized message.
//
//
// JSON
// ====
// The JSON representation of an `Any` value uses the regular
// representation of the deserialized, embedded message, with an
// additional field `@type` which contains the type URL. Example:
//
// package google.profile;
// message Person {
// string first_name = 1;
// string last_name = 2;
// }
//
// {
// "@type": "type.googleapis.com/google.profile.Person",
// "firstName": <string>,
// "lastName": <string>
// }
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
// `value` which holds the custom JSON in addition to the `@type`
// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
// "value": "1.212s"
// }
//
type Any struct {
// A URL/resource name whose content describes the type of the
// serialized message.
//
// For URLs which use the schema `http`, `https`, or no schema, the
// following restrictions and interpretations apply:
//
// * If no schema is provided, `https` is assumed.
// * The last segment of the URL's path must represent the fully
// qualified name of the type (as in `path/google.protobuf.Duration`).
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
// lookup. Therefore, binary compatibility needs to be preserved
// on changes to types. (Use versioned type names to manage
// breaking changes.)
//
// Schemas other than `http`, `https` (or the empty schema) might be
// used with implementation specific semantics.
//
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl" json:"type_url,omitempty"`
// Must be valid serialized data of the above specified type.
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
}
func (m *Any) Reset() { *m = Any{} }
func (m *Any) String() string { return proto.CompactTextString(m) }
func (*Any) ProtoMessage() {}
func (*Any) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (*Any) XXX_WellKnownType() string { return "Any" }
func init() {
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
}
func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 160 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f,
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4,
0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x25, 0x33, 0x2e, 0x66, 0xc7, 0xbc, 0x4a,
0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2, 0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46,
0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48, 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7,
0x34, 0x55, 0x82, 0x09, 0x28, 0xce, 0x13, 0x04, 0xe1, 0x38, 0x79, 0x73, 0x09, 0x27, 0xe7, 0xe7,
0xea, 0xa1, 0x19, 0xe7, 0xc4, 0x01, 0x34, 0x2c, 0x00, 0xc4, 0x09, 0x60, 0x5c, 0xc0, 0xc8, 0xb8,
0x88, 0x89, 0xd9, 0x3d, 0xc0, 0x69, 0x15, 0x93, 0x9c, 0x3b, 0x44, 0x59, 0x00, 0x54, 0x99, 0x5e,
0x78, 0x6a, 0x4e, 0x8e, 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0xd0, 0x92, 0xe2, 0x24, 0x36, 0xb0,
0x7e, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x49, 0xa8, 0x53, 0xb9, 0x00, 0x00, 0x00,
}
// Code generated by protoc-gen-go.
// source: google/protobuf/duration.proto
// DO NOT EDIT!
package google_protobuf
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// A Duration represents a signed, fixed-length span of time represented
// as a count of seconds and fractions of seconds at nanosecond
// resolution. It is independent of any calendar and concepts like "day"
// or "month". It is related to Timestamp in that the difference between
// two Timestamp values is a Duration and it can be added or subtracted
// from a Timestamp. Range is approximately +-10,000 years.
//
// Example 1: Compute Duration from two Timestamps in pseudo code.
//
// Timestamp start = ...;
// Timestamp end = ...;
// Duration duration = ...;
//
// duration.seconds = end.seconds - start.seconds;
// duration.nanos = end.nanos - start.nanos;
//
// if (duration.seconds < 0 && duration.nanos > 0) {
// duration.seconds += 1;
// duration.nanos -= 1000000000;
// } else if (durations.seconds > 0 && duration.nanos < 0) {
// duration.seconds -= 1;
// duration.nanos += 1000000000;
// }
//
// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
//
// Timestamp start = ...;
// Duration duration = ...;
// Timestamp end = ...;
//
// end.seconds = start.seconds + duration.seconds;
// end.nanos = start.nanos + duration.nanos;
//
// if (end.nanos < 0) {
// end.seconds -= 1;
// end.nanos += 1000000000;
// } else if (end.nanos >= 1000000000) {
// end.seconds += 1;
// end.nanos -= 1000000000;
// }
//
type Duration struct {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive.
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
// Signed fractions of a second at nanosecond resolution of the span
// of time. Durations less than one second are represented with a 0
// `seconds` field and a positive or negative `nanos` field. For durations
// of one second or more, a non-zero value for the `nanos` field must be
// of the same sign as the `seconds` field. Must be from -999,999,999
// to +999,999,999 inclusive.
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
}
func (m *Duration) Reset() { *m = Duration{} }
func (m *Duration) String() string { return proto.CompactTextString(m) }
func (*Duration) ProtoMessage() {}
func (*Duration) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} }
func (*Duration) XXX_WellKnownType() string { return "Duration" }
func init() {
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
}
func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 161 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f,
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a,
0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56,
0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5,
0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e,
0x7e, 0xb1, 0x04, 0x13, 0x50, 0x9c, 0x35, 0x08, 0xc2, 0x71, 0x0a, 0xe0, 0x12, 0x4e, 0xce, 0xcf,
0xd5, 0x43, 0x33, 0xd2, 0x89, 0x17, 0x66, 0x60, 0x00, 0x48, 0x24, 0x80, 0x71, 0x01, 0x23, 0xe3,
0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10, 0xb5, 0x01, 0x50, 0xb5, 0x7a,
0xe1, 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c,
0x60, 0x43, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x5b, 0xa9, 0x05, 0xfd, 0xc7, 0x00, 0x00,
0x00,
}
// Code generated by protoc-gen-go.
// source: google/protobuf/empty.proto
// DO NOT EDIT!
package google_protobuf
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// A generic empty message that you can re-use to avoid defining duplicated
// empty messages in your APIs. A typical example is to use it as the request
// or the response type of an API method. For instance:
//
// service Foo {
// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
// }
//
// The JSON representation for `Empty` is empty JSON object `{}`.
type Empty struct {
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} }
func (*Empty) XXX_WellKnownType() string { return "Empty" }
func init() {
proto.RegisterType((*Empty)(nil), "google.protobuf.Empty")
}
func init() { proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor3) }
var fileDescriptor3 = []byte{
// 124 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xcf, 0xcf, 0x4f,
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcd, 0x2d, 0x28,
0xa9, 0xd4, 0x03, 0x73, 0x85, 0xf8, 0x21, 0x92, 0x7a, 0x30, 0x49, 0x25, 0x76, 0x2e, 0x56, 0x57,
0x90, 0xbc, 0x53, 0x00, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xbc, 0x13, 0x17, 0x58, 0x36,
0x00, 0xc4, 0x0d, 0x60, 0x5c, 0xc0, 0xc8, 0xf8, 0x83, 0x91, 0x71, 0x11, 0x13, 0xb3, 0x7b, 0x80,
0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xda, 0x00, 0xa8, 0x5a, 0xbd, 0xf0, 0xd4, 0x9c, 0x1c, 0xef,
0xbc, 0xfc, 0xf2, 0xbc, 0x90, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0xb0, 0x21, 0xc6, 0x80, 0x00,
0x00, 0x00, 0xff, 0xff, 0xac, 0xca, 0x5b, 0xd0, 0x91, 0x00, 0x00, 0x00,
}
// Code generated by protoc-gen-go.
// source: google/protobuf/field_mask.proto
// DO NOT EDIT!
package google_protobuf
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// `FieldMask` represents a set of symbolic field paths, for example:
//
// paths: "f.a"
// paths: "f.b.d"
//
// Here `f` represents a field in some root message, `a` and `b`
// fields in the message found in `f`, and `d` a field found in the
// message in `f.b`.
//
// Field masks are used to specify a subset of fields that should be
// returned by a get operation or modified by an update operation.
// Field masks also have a custom JSON encoding (see below).
//
// # Field Masks in Projections
//
// When used in the context of a projection, a response message or
// sub-message is filtered by the API to only contain those fields as
// specified in the mask. For example, if the mask in the previous
// example is applied to a response message as follows:
//
// f {
// a : 22
// b {
// d : 1
// x : 2
// }
// y : 13
// }
// z: 8
//
// The result will not contain specific values for fields x,y and z
// (their value will be set to the default, and omitted in proto text
// output):
//
//
// f {
// a : 22
// b {
// d : 1
// }
// }
//
// A repeated field is not allowed except at the last position of a
// field mask.
//
// If a FieldMask object is not present in a get operation, the
// operation applies to all fields (as if a FieldMask of all fields
// had been specified).
//
// Note that a field mask does not necessarily applies to the
// top-level response message. In case of a REST get operation, the
// field mask applies directly to the response, but in case of a REST
// list operation, the mask instead applies to each individual message
// in the returned resource list. In case of a REST custom method,
// other definitions may be used. Where the mask applies will be
// clearly documented together with its declaration in the API. In
// any case, the effect on the returned resource/resources is required
// behavior for APIs.
//
// # Field Masks in Update Operations
//
// A field mask in update operations specifies which fields of the
// targeted resource are going to be updated. The API is required
// to only change the values of the fields as specified in the mask
// and leave the others untouched. If a resource is passed in to
// describe the updated values, the API ignores the values of all
// fields not covered by the mask.
//
// In order to reset a field's value to the default, the field must
// be in the mask and set to the default value in the provided resource.
// Hence, in order to reset all fields of a resource, provide a default
// instance of the resource and set all fields in the mask, or do
// not provide a mask as described below.
//
// If a field mask is not present on update, the operation applies to
// all fields (as if a field mask of all fields has been specified).
// Note that in the presence of schema evolution, this may mean that
// fields the client does not know and has therefore not filled into
// the request will be reset to their default. If this is unwanted
// behavior, a specific service may require a client to always specify
// a field mask, producing an error if not.
//
// As with get operations, the location of the resource which
// describes the updated values in the request message depends on the
// operation kind. In any case, the effect of the field mask is
// required to be honored by the API.
//
// ## Considerations for HTTP REST
//
// The HTTP kind of an update operation which uses a field mask must
// be set to PATCH instead of PUT in order to satisfy HTTP semantics
// (PUT must only be used for full updates).
//
// # JSON Encoding of Field Masks
//
// In JSON, a field mask is encoded as a single string where paths are
// separated by a comma. Fields name in each path are converted
// to/from lower-camel naming conventions.
//
// As an example, consider the following message declarations:
//
// message Profile {
// User user = 1;
// Photo photo = 2;
// }
// message User {
// string display_name = 1;
// string address = 2;
// }
//
// In proto a field mask for `Profile` may look as such:
//
// mask {
// paths: "user.display_name"
// paths: "photo"
// }
//
// In JSON, the same mask is represented as below:
//
// {
// mask: "user.displayName,photo"
// }
//
type FieldMask struct {
// The set of field mask paths.
Paths []string `protobuf:"bytes,1,rep,name=paths" json:"paths,omitempty"`
}
func (m *FieldMask) Reset() { *m = FieldMask{} }
func (m *FieldMask) String() string { return proto.CompactTextString(m) }
func (*FieldMask) ProtoMessage() {}
func (*FieldMask) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{0} }
func init() {
proto.RegisterType((*FieldMask)(nil), "google.protobuf.FieldMask")
}
func init() { proto.RegisterFile("google/protobuf/field_mask.proto", fileDescriptor4) }
var fileDescriptor4 = []byte{
// 147 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xcf, 0xcf, 0x4f,
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcb, 0x4c, 0xcd,
0x49, 0x89, 0xcf, 0x4d, 0x2c, 0xce, 0xd6, 0x03, 0x8b, 0x09, 0xf1, 0x43, 0x54, 0xe8, 0xc1, 0x54,
0x28, 0x29, 0x72, 0x71, 0xba, 0x81, 0x14, 0xf9, 0x02, 0xd5, 0x08, 0x89, 0x70, 0xb1, 0x16, 0x24,
0x96, 0x64, 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x06, 0x41, 0x38, 0x4e, 0x81, 0x5c, 0xc2,
0xc9, 0xf9, 0xb9, 0x7a, 0x68, 0x3a, 0x9d, 0xf8, 0xe0, 0xfa, 0x02, 0x40, 0x42, 0x01, 0x8c, 0x0b,
0x18, 0x19, 0x17, 0x31, 0x31, 0xbb, 0x07, 0x38, 0xad, 0x62, 0x92, 0x73, 0x87, 0x28, 0x0e, 0x80,
0x2a, 0xd6, 0x0b, 0x4f, 0xcd, 0xc9, 0xf1, 0xce, 0xcb, 0x2f, 0xcf, 0x0b, 0xa9, 0x2c, 0x48, 0x2d,
0x4e, 0x62, 0x03, 0x9b, 0x62, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x21, 0xb1, 0xe8, 0x01, 0xb1,
0x00, 0x00, 0x00,
}
package google_protobuf
import (
"time"
)
var (
// EmptyInstance is an instance of Empty.
EmptyInstance = &Empty{}
)
// Now returns the current time as a protobuf Timestamp.
func Now() *Timestamp {
return TimeToProto(time.Now().UTC())
}
// TimeToProto converts a go Time to a protobuf Timestamp.
func TimeToProto(t time.Time) *Timestamp {
return &Timestamp{
Seconds: t.UnixNano() / int64(time.Second),
Nanos: int32(t.UnixNano() % int64(time.Second)),
}
}
// GoTime converts a protobuf Timestamp to a go Time.
func (t *Timestamp) GoTime() time.Time {
if t == nil {
return time.Unix(0, 0).UTC()
}
return time.Unix(
t.Seconds,
int64(t.Nanos),
).UTC()
}
// Before returns true if t is before j.
func (t *Timestamp) Before(j *Timestamp) bool {
if j == nil {
return false
}
if t == nil {
return true
}
if t.Seconds < j.Seconds {
return true
}
if t.Seconds > j.Seconds {
return false
}
return t.Nanos < j.Nanos
}
// DurationToProto converts a go Duration to a protobuf Duration.
func DurationToProto(d time.Duration) *Duration {
return &Duration{
Seconds: int64(d) / int64(time.Second),
Nanos: int32(int64(d) % int64(time.Second)),
}
}
// GoDuration converts a protobuf Duration to a go Duration.
func (d *Duration) GoDuration() time.Duration {
if d == nil {
return 0
}
return time.Duration((d.Seconds * int64(time.Second)) + int64(d.Nanos))
}
// Code generated by protoc-gen-go.
// source: google/protobuf/source_context.proto
// DO NOT EDIT!
package google_protobuf
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// `SourceContext` represents information about the source of a
// protobuf element, like the file in which it is defined.
type SourceContext struct {
// The path-qualified name of the .proto file that contained the associated
// protobuf element. For example: `"google/protobuf/source.proto"`.
FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName" json:"file_name,omitempty"`
}
func (m *SourceContext) Reset() { *m = SourceContext{} }
func (m *SourceContext) String() string { return proto.CompactTextString(m) }
func (*SourceContext) ProtoMessage() {}
func (*SourceContext) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{0} }
func init() {
proto.RegisterType((*SourceContext)(nil), "google.protobuf.SourceContext")
}
func init() { proto.RegisterFile("google/protobuf/source_context.proto", fileDescriptor5) }
var fileDescriptor5 = []byte{
// 159 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x49, 0xcf, 0xcf, 0x4f,
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xce, 0x2f, 0x2d,
0x4a, 0x4e, 0x8d, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xad, 0x28, 0xd1, 0x03, 0x8b, 0x0b, 0xf1, 0x43,
0x54, 0xe9, 0xc1, 0x54, 0x29, 0xe9, 0x70, 0xf1, 0x06, 0x83, 0x15, 0x3a, 0x43, 0xd4, 0x09, 0x49,
0x73, 0x71, 0xa6, 0x65, 0xe6, 0xa4, 0xc6, 0xe7, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a,
0x70, 0x06, 0x71, 0x80, 0x04, 0xfc, 0x80, 0x7c, 0xa7, 0x50, 0x2e, 0xe1, 0xe4, 0xfc, 0x5c, 0x3d,
0x34, 0x43, 0x9c, 0x84, 0x50, 0x8c, 0x08, 0x00, 0x09, 0x07, 0x30, 0x2e, 0x60, 0x64, 0x5c, 0xc4,
0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x21, 0x00, 0xaa, 0x41, 0x2f, 0x3c,
0x35, 0x27, 0xc7, 0x3b, 0x2f, 0xbf, 0x3c, 0x2f, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x6c,
0x92, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x80, 0x1f, 0xc2, 0xc4, 0x00, 0x00, 0x00,
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment