mirror of https://github.com/k3s-io/k3s.git
Add StorageOS volume plugin
parent
810efa6689
commit
5e2503e71f
|
@ -2410,6 +2410,14 @@
|
|||
"ImportPath": "github.com/square/go-jose/json",
|
||||
"Rev": "789a4c4bd4c118f7564954f441b29c153ccd6a96"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/storageos/go-api",
|
||||
"Rev": "74f9beb613cacf0cc282facc2e1550a3231e126f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/storageos/go-api/types",
|
||||
"Rev": "74f9beb613cacf0cc282facc2e1550a3231e126f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/objx",
|
||||
"Rev": "1a9d0bb9f541897e62256577b352fdbc1fb4fd94"
|
||||
|
|
|
@ -77112,6 +77112,112 @@ SOFTWARE.
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/storageos/go-api licensed under: =
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2017 StorageOS
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Copyright (c) 2013-2017, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
= vendor/github.com/storageos/go-api/LICENCE d8f852a0f38554263e64363f57b07fc4 -
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/storageos/go-api/types licensed under: =
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2017 StorageOS
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Copyright (c) 2013-2017, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
= vendor/github.com/storageos/go-api/LICENCE d8f852a0f38554263e64363f57b07fc4 -
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/stretchr/objx licensed under: =
|
||||
|
||||
|
|
|
@ -49239,6 +49239,10 @@
|
|||
"description": "Name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass.",
|
||||
"type": "string"
|
||||
},
|
||||
"storageos": {
|
||||
"description": "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.StorageOSPersistentVolumeSource"
|
||||
},
|
||||
"vsphereVolume": {
|
||||
"description": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.VsphereVirtualDiskVolumeSource"
|
||||
|
@ -50712,6 +50716,56 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.kubernetes.pkg.api.v1.StorageOSPersistentVolumeSource": {
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"fsType": {
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
||||
"type": "string"
|
||||
},
|
||||
"readOnly": {
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"secretRef": {
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.ObjectReference"
|
||||
},
|
||||
"volumeName": {
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
|
||||
"type": "string"
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.kubernetes.pkg.api.v1.StorageOSVolumeSource": {
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"fsType": {
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
||||
"type": "string"
|
||||
},
|
||||
"readOnly": {
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"secretRef": {
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.LocalObjectReference"
|
||||
},
|
||||
"volumeName": {
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
|
||||
"type": "string"
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.kubernetes.pkg.api.v1.TCPSocketAction": {
|
||||
"description": "TCPSocketAction describes an action based on opening a socket",
|
||||
"required": [
|
||||
|
@ -50893,6 +50947,10 @@
|
|||
"description": "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.SecretVolumeSource"
|
||||
},
|
||||
"storageos": {
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.StorageOSVolumeSource"
|
||||
},
|
||||
"vsphereVolume": {
|
||||
"description": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.VsphereVirtualDiskVolumeSource"
|
||||
|
|
|
@ -3945,6 +3945,10 @@
|
|||
"scaleIO": {
|
||||
"$ref": "v1.ScaleIOVolumeSource",
|
||||
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4808,6 +4812,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSVolumeSource": {
|
||||
"id": "v1.StorageOSVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.LocalObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Container": {
|
||||
"id": "v1.Container",
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
|
|
|
@ -1692,6 +1692,10 @@
|
|||
"scaleIO": {
|
||||
"$ref": "v1.ScaleIOVolumeSource",
|
||||
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2555,6 +2559,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSVolumeSource": {
|
||||
"id": "v1.StorageOSVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.LocalObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Container": {
|
||||
"id": "v1.Container",
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
|
|
|
@ -2773,6 +2773,10 @@
|
|||
"scaleIO": {
|
||||
"$ref": "v1.ScaleIOVolumeSource",
|
||||
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3636,6 +3640,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSVolumeSource": {
|
||||
"id": "v1.StorageOSVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.LocalObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Container": {
|
||||
"id": "v1.Container",
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
|
|
|
@ -7417,6 +7417,10 @@
|
|||
"scaleIO": {
|
||||
"$ref": "v1.ScaleIOVolumeSource",
|
||||
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8280,6 +8284,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSVolumeSource": {
|
||||
"id": "v1.StorageOSVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.LocalObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Container": {
|
||||
"id": "v1.Container",
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
|
|
|
@ -1557,6 +1557,10 @@
|
|||
"scaleIO": {
|
||||
"$ref": "v1.ScaleIOVolumeSource",
|
||||
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2382,6 +2386,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSVolumeSource": {
|
||||
"id": "v1.StorageOSVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.LocalObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.VolumeMount": {
|
||||
"id": "v1.VolumeMount",
|
||||
"description": "VolumeMount describes a mounting of a Volume within a container.",
|
||||
|
|
|
@ -18891,6 +18891,10 @@
|
|||
"$ref": "v1.LocalVolumeSource",
|
||||
"description": "Local represents directly-attached storage with node affinity"
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSPersistentVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md"
|
||||
},
|
||||
"accessModes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
@ -19482,6 +19486,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSPersistentVolumeSource": {
|
||||
"id": "v1.StorageOSPersistentVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.ObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.PersistentVolumeStatus": {
|
||||
"id": "v1.PersistentVolumeStatus",
|
||||
"description": "PersistentVolumeStatus is the current status of a persistent volume.",
|
||||
|
@ -19789,6 +19819,10 @@
|
|||
"scaleIO": {
|
||||
"$ref": "v1.ScaleIOVolumeSource",
|
||||
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20095,6 +20129,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSVolumeSource": {
|
||||
"id": "v1.StorageOSVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.LocalObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Container": {
|
||||
"id": "v1.Container",
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
|
|
|
@ -90,6 +90,7 @@ go_library(
|
|||
"//pkg/volume/quobyte:go_default_library",
|
||||
"//pkg/volume/rbd:go_default_library",
|
||||
"//pkg/volume/scaleio:go_default_library",
|
||||
"//pkg/volume/storageos:go_default_library",
|
||||
"//pkg/volume/vsphere_volume:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
|
|
|
@ -54,6 +54,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/volume/quobyte"
|
||||
"k8s.io/kubernetes/pkg/volume/rbd"
|
||||
"k8s.io/kubernetes/pkg/volume/scaleio"
|
||||
"k8s.io/kubernetes/pkg/volume/storageos"
|
||||
"k8s.io/kubernetes/pkg/volume/vsphere_volume"
|
||||
)
|
||||
|
||||
|
@ -75,6 +76,7 @@ func ProbeAttachableVolumePlugins(config componentconfig.VolumeConfiguration) []
|
|||
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
return allPlugins
|
||||
}
|
||||
|
||||
|
@ -123,6 +125,7 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config componen
|
|||
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
|
||||
if cloud != nil {
|
||||
switch {
|
||||
|
|
|
@ -103,6 +103,7 @@ go_library(
|
|||
"//pkg/volume/rbd:go_default_library",
|
||||
"//pkg/volume/scaleio:go_default_library",
|
||||
"//pkg/volume/secret:go_default_library",
|
||||
"//pkg/volume/storageos:go_default_library",
|
||||
"//pkg/volume/vsphere_volume:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
|
|
|
@ -54,6 +54,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/volume/rbd"
|
||||
"k8s.io/kubernetes/pkg/volume/scaleio"
|
||||
"k8s.io/kubernetes/pkg/volume/secret"
|
||||
"k8s.io/kubernetes/pkg/volume/storageos"
|
||||
"k8s.io/kubernetes/pkg/volume/vsphere_volume"
|
||||
// Cloud providers
|
||||
_ "k8s.io/kubernetes/pkg/cloudprovider/providers"
|
||||
|
@ -97,6 +98,7 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
|
|||
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
return allPlugins
|
||||
}
|
||||
|
||||
|
|
|
@ -3185,6 +3185,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_scaleiovolumesource">v1.ScaleIOVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.</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_storageosvolumesource">v1.StorageOSVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -6547,6 +6554,68 @@ Examples:<br>
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageosvolumesource">v1.StorageOSVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_localobjectreference">v1.LocalObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1beta1_statefulsetupdatestrategy">v1beta1.StatefulSetUpdateStrategy</h3>
|
||||
|
@ -6738,7 +6807,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2017-06-06 19:37:03 UTC
|
||||
Last updated 2017-06-07 15:14:00 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -2700,6 +2700,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_scaleiovolumesource">v1.ScaleIOVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.</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_storageosvolumesource">v1.StorageOSVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -5569,6 +5576,68 @@ Examples:<br>
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageosvolumesource">v1.StorageOSVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_localobjectreference">v1.LocalObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_nodeaffinity">v1.NodeAffinity</h3>
|
||||
|
@ -5719,7 +5788,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2017-06-06 04:25:31 UTC
|
||||
Last updated 2017-06-07 15:14:25 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -2707,6 +2707,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_scaleiovolumesource">v1.ScaleIOVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.</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_storageosvolumesource">v1.StorageOSVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -5472,6 +5479,68 @@ Examples:<br>
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageosvolumesource">v1.StorageOSVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_localobjectreference">v1.LocalObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_objectreference">v1.ObjectReference</h3>
|
||||
|
@ -5815,7 +5884,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2017-06-06 04:25:37 UTC
|
||||
Last updated 2017-06-07 15:14:30 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -3874,6 +3874,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_scaleiovolumesource">v1.ScaleIOVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.</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_storageosvolumesource">v1.StorageOSVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -7806,6 +7813,68 @@ Both these may change in the future. Incoming requests are matched against the h
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageosvolumesource">v1.StorageOSVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_localobjectreference">v1.LocalObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_nodeaffinity">v1.NodeAffinity</h3>
|
||||
|
@ -8155,7 +8224,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-06-06 04:25:47 UTC
|
||||
Last updated 2017-06-07 15:14:38 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -3460,6 +3460,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_scaleiovolumesource">v1.ScaleIOVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.</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_storageosvolumesource">v1.StorageOSVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -3507,6 +3514,68 @@ When an object is created, the system will populate this list with the current s
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageosvolumesource">v1.StorageOSVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_localobjectreference">v1.LocalObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_resourcefieldselector">v1.ResourceFieldSelector</h3>
|
||||
|
@ -3957,7 +4026,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2017-06-06 04:26:13 UTC
|
||||
Last updated 2017-06-07 15:14:57 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -4332,6 +4332,68 @@ The resulting set of endpoints can be viewed as:<br>
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageospersistentvolumesource">v1.StorageOSPersistentVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_objectreference">v1.ObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_volume">v1.Volume</h3>
|
||||
|
@ -4545,6 +4607,13 @@ The resulting set of endpoints can be viewed as:<br>
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_scaleiovolumesource">v1.ScaleIOVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.</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_storageosvolumesource">v1.StorageOSVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -7404,6 +7473,13 @@ Examples:<br>
|
|||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume that is attached to the kubelet’s host machine and mounted into the pod More info: <a href="https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md">https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md</a></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_storageospersistentvolumesource">v1.StorageOSPersistentVolumeSource</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="https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes">https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
|
@ -9431,6 +9507,68 @@ Examples:<br>
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageosvolumesource">v1.StorageOSVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_localobjectreference">v1.LocalObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_objectreference">v1.ObjectReference</h3>
|
||||
|
@ -10112,7 +10250,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2017-06-08 02:30:43 UTC
|
||||
Last updated 2017-06-09 12:11:42 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -94,6 +94,30 @@ parameters:
|
|||
|
||||
For a complete example refer ([Portworx Volume docs](../volumes/portworx/README.md))
|
||||
|
||||
#### StorageOS
|
||||
|
||||
```yaml
|
||||
kind: StorageClass
|
||||
apiVersion: storage.k8s.io/v1
|
||||
metadata:
|
||||
name: sc-fast
|
||||
provisioner: kubernetes.io/storageos
|
||||
parameters:
|
||||
pool: default
|
||||
description: Kubernetes volume
|
||||
fsType: ext4
|
||||
adminSecretNamespace: default
|
||||
adminSecretName: storageos-secret
|
||||
```
|
||||
|
||||
* `pool`: The name of the StorageOS distributed capacity pool to provision the volume from. Uses the `default` pool which is normally present if not specified.
|
||||
* `description`: The description to assign to volumes that were created dynamically. All volume descriptions will be the same for the storage class, but different storage classes can be used to allow descriptions for different use cases. Defaults to `Kubernetes volume`.
|
||||
* `fsType`: The default filesystem type to request. Note that user-defined rules within StorageOS may override this value. Defaults to `ext4`.
|
||||
* `adminSecretNamespace`: The namespace where the API configuration secret is located. Required if adminSecretName set.
|
||||
* `adminSecretName`: The name of the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.
|
||||
|
||||
For a complete example refer to the ([StorageOS example](../../volumes/storageos/README.md))
|
||||
|
||||
#### GLUSTERFS
|
||||
|
||||
```yaml
|
||||
|
|
|
@ -0,0 +1,475 @@
|
|||
# StorageOS Volume
|
||||
|
||||
- [StorageOS](#storageos)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Examples](#examples)
|
||||
- [Pre-provisioned Volumes](#pre-provisioned)
|
||||
- [Pod](#pod)
|
||||
- [Persistent Volumes](#persistent-volumes)
|
||||
- [Dynamic Provisioning](#dynamic-provisioning)
|
||||
- [Storage Class](#storage-class)
|
||||
- [API Configuration](#api-configuration)
|
||||
|
||||
## StorageOS
|
||||
|
||||
[StorageOS](https://www.storageos.com) can be used as a storage provider for your Kubernetes cluster. StorageOS runs as a container within your Kubernetes environment, making local storage accessible from any node within the Kubernetes cluster. Data can be replicated to protect against node failure.
|
||||
|
||||
At its core, StorageOS provides block storage. You may choose the filesystem type to install to make devices usable from within containers.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The StorageOS container must be running on each Kubernetes node that wants to contribute storage or that wants to consume storage. For more information on how you can run StorageOS, consult the [StorageOS documentation](https://docs.storageos.com).
|
||||
|
||||
## API Configuration
|
||||
|
||||
The StorageOS provider has been pre-configured to use the StorageOS API defaults, and no additional configuration is required for testing. If you have changed the API port, or have removed the default account or changed its password (recommended), you must specify the new settings. This is done using Kubernetes [Secrets](../../../docs/user-guide/secrets/).
|
||||
|
||||
API configuration is set by using Kubernetes secrets. The configuration secret supports the following parameters:
|
||||
|
||||
* `apiAddress`: The address of the StorageOS API. This is optional and defaults to `tcp://localhost:5705`, which should be correct if the StorageOS container is running using the default settings.
|
||||
* `apiUsername`: The username to authenticate to the StorageOS API with.
|
||||
* `apiPassword`: The password to authenticate to the StorageOS API with.
|
||||
* `apiVersion`: Optional, string value defaulting to `1`. Only set this if requested in StorageOS documentation.
|
||||
|
||||
Mutiple credentials can be used by creating different secrets.
|
||||
|
||||
For Persistent Volumes, secrets must be created in the Pod namespace. Specify the secret name using the `secretName` parameter when attaching existing volumes in Pods or creating new persistent volumes.
|
||||
|
||||
For dynamically provisioned volumes using storage classes, the secret can be created in any namespace. Note that you would want this to be an admin-controlled namespace with restricted access to users. Specify the secret namespace as parameter `adminSecretNamespace` and name as parameter `adminSecretName` in storage classes.
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: storageos-secret
|
||||
type: "kubernetes.io/storageos"
|
||||
data:
|
||||
apiAddress: dGNwOi8vMTI3LjAuMC4xOjU3MDU=
|
||||
apiUsername: c3RvcmFnZW9z
|
||||
apiPassword: c3RvcmFnZW9z
|
||||
```
|
||||
|
||||
Values for `apiAddress`, `apiUsername` and `apiPassword` can be generated with:
|
||||
|
||||
```bash
|
||||
$ echo -n "tcp://127.0.0.1:5705" | base64
|
||||
dGNwOi8vMTI3LjAuMC4xOjU3MDU=
|
||||
```
|
||||
|
||||
Create the secret:
|
||||
|
||||
```bash
|
||||
$ kubectl create -f storageos-secret.yaml
|
||||
secret "storageos-secret" created
|
||||
```
|
||||
|
||||
Verify the secret:
|
||||
|
||||
```bash
|
||||
$ kubectl describe secret storageos-secret
|
||||
Name: storageos-secret
|
||||
Namespace: default
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
|
||||
Type: kubernetes.io/storageos
|
||||
|
||||
Data
|
||||
====
|
||||
apiAddress: 20 bytes
|
||||
apiPassword: 8 bytes
|
||||
apiUsername: 8 bytes
|
||||
|
||||
```
|
||||
## Examples
|
||||
|
||||
These examples assume you have a running Kubernetes cluster with the StorageOS container running on each node, and that an API configuration secret called `storageos-secret` has been created in the `default` namespace.
|
||||
|
||||
### Pre-provisioned Volumes
|
||||
|
||||
#### Pod
|
||||
|
||||
Pods can be created that access volumes directly.
|
||||
|
||||
1. Create a volume using the StorageOS UI, CLI or API. Consult the [StorageOS documentation](https://docs.storageos.com) for details.
|
||||
1. Create a pod that refers to the new volume. In this case the volume is named `redis-vol01`.
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
name: redis
|
||||
role: master
|
||||
name: test-storageos-redis
|
||||
spec:
|
||||
containers:
|
||||
- name: master
|
||||
image: kubernetes/redis:v1
|
||||
env:
|
||||
- name: MASTER
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
limits:
|
||||
cpu: "0.1"
|
||||
volumeMounts:
|
||||
- mountPath: /redis-master-data
|
||||
name: redis-data
|
||||
volumes:
|
||||
- name: redis-data
|
||||
storageos:
|
||||
# This volume must already exist within StorageOS
|
||||
volumeName: redis-vol01
|
||||
# volumeNamespace is optional, and specifies the volume scope within
|
||||
# StorageOS. If no namespace is provided, it will use the namespace
|
||||
# of the pod. Set to `default` or leave blank if you are not using
|
||||
# namespaces.
|
||||
#volumeNamespace: test-storageos
|
||||
# The filesystem type to format the volume with, if required.
|
||||
fsType: ext4
|
||||
# The secret name for API credentials
|
||||
secretName: storageos-secret
|
||||
```
|
||||
|
||||
[Download example](storageos-pod.yaml?raw=true)
|
||||
|
||||
Create the pod:
|
||||
|
||||
```bash
|
||||
$ kubectl create -f examples/volumes/storageos/storageos-pod.yaml
|
||||
```
|
||||
|
||||
Verify that the pod is running:
|
||||
|
||||
```bash
|
||||
$ kubectl get pods test-storageos-redis
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
test-storageos-redis 1/1 Running 0 30m
|
||||
```
|
||||
|
||||
### Persistent Volumes
|
||||
|
||||
1. Create a volume using the StorageOS UI, CLI or API. Consult the [StorageOS documentation](https://docs.storageos.com) for details.
|
||||
1. Create the persistent volume `redis-vol01`.
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: pv0001
|
||||
spec:
|
||||
capacity:
|
||||
storage: 5Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Delete
|
||||
storageos:
|
||||
# This volume must already exist within StorageOS
|
||||
volumeName: pv0001
|
||||
# volumeNamespace is optional, and specifies the volume scope within
|
||||
# StorageOS. Set to `default` or leave blank if you are not using
|
||||
# namespaces.
|
||||
#volumeNamespace: default
|
||||
# The filesystem type to create on the volume, if required.
|
||||
fsType: ext4
|
||||
# The secret name for API credentials
|
||||
secretName: storageos-secret
|
||||
```
|
||||
|
||||
[Download example](storageos-pv.yaml?raw=true)
|
||||
|
||||
Create the persistent volume:
|
||||
|
||||
```bash
|
||||
$ kubectl create -f examples/volumes/storageos/storageos-pv.yaml
|
||||
```
|
||||
|
||||
Verify that the pv has been created:
|
||||
|
||||
```bash
|
||||
$ kubectl describe pv pv0001
|
||||
Name: pv0001
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
StorageClass: fast
|
||||
Status: Available
|
||||
Claim:
|
||||
Reclaim Policy: Delete
|
||||
Access Modes: RWO
|
||||
Capacity: 5Gi
|
||||
Message:
|
||||
Source:
|
||||
Type: StorageOS (a StorageOS Persistent Disk resource)
|
||||
VolumeName: pv0001
|
||||
VolumeNamespace:
|
||||
FSType: ext4
|
||||
ReadOnly: false
|
||||
Events: <none>
|
||||
```
|
||||
|
||||
1. Create persistent volume claim
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: pvc0001
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
storageClassName: fast
|
||||
```
|
||||
|
||||
[Download example](storageos-pvc.yaml?raw=true)
|
||||
|
||||
Create the persistent volume claim:
|
||||
|
||||
```bash
|
||||
$ kubectl create -f examples/volumes/storageos/storageos-pvc.yaml
|
||||
```
|
||||
|
||||
Verify that the pvc has been created:
|
||||
|
||||
```bash
|
||||
$ kubectl describe pvc pvc0001
|
||||
Name: pvc0001
|
||||
Namespace: default
|
||||
StorageClass: fast
|
||||
Status: Bound
|
||||
Volume: pv0001
|
||||
Labels: <none>
|
||||
Capacity: 5Gi
|
||||
Access Modes: RWO
|
||||
No events.
|
||||
```
|
||||
|
||||
1. Create pod which uses the persistent volume claim
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
name: redis
|
||||
role: master
|
||||
name: test-storageos-redis-pvc
|
||||
spec:
|
||||
containers:
|
||||
- name: master
|
||||
image: kubernetes/redis:v1
|
||||
env:
|
||||
- name: MASTER
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
limits:
|
||||
cpu: "0.1"
|
||||
volumeMounts:
|
||||
- mountPath: /redis-master-data
|
||||
name: redis-data
|
||||
volumes:
|
||||
- name: redis-data
|
||||
persistentVolumeClaim:
|
||||
claimName: pvc0001
|
||||
```
|
||||
|
||||
[Download example](storageos-pvcpod.yaml?raw=true)
|
||||
|
||||
Create the pod:
|
||||
|
||||
```bash
|
||||
$ kubectl create -f examples/volumes/storageos/storageos-pvcpod.yaml
|
||||
```
|
||||
|
||||
Verify that the pod has been created:
|
||||
|
||||
```bash
|
||||
$ kubectl get pods
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
test-storageos-redis-pvc 1/1 Running 0 40s
|
||||
```
|
||||
|
||||
### Dynamic Provisioning
|
||||
|
||||
Dynamic provisioning can be used to auto-create volumes when needed. They require a Storage Class, a Persistent Volume Claim, and a Pod.
|
||||
|
||||
#### Storage Class
|
||||
|
||||
Kubernetes administrators can use storage classes to define different types of storage made available within the cluster. Each storage class definition specifies a provisioner type and any parameters needed to access it, as well as any other configuration.
|
||||
|
||||
StorageOS supports the following storage class parameters:
|
||||
|
||||
* `pool`: The name of the StorageOS distributed capacity pool to provision the volume from. Uses the `default` pool which is normally present if not specified.
|
||||
* `description`: The description to assign to volumes that were created dynamically. All volume descriptions will be the same for the storage class, but different storage classes can be used to allow descriptions for different use cases. Defaults to `Kubernetes volume`.
|
||||
* `fsType`: The default filesystem type to request. Note that user-defined rules within StorageOS may override this value. Defaults to `ext4`.
|
||||
* `adminSecretNamespace`: The namespace where the API configuration secret is located. Required if adminSecretName set.
|
||||
* `adminSecretName`: The name of the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.
|
||||
|
||||
1. Create storage class
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
kind: StorageClass
|
||||
apiVersion: storage.k8s.io/v1
|
||||
metadata:
|
||||
name: sc-fast
|
||||
provisioner: kubernetes.io/storageos
|
||||
parameters:
|
||||
pool: default
|
||||
description: Kubernetes volume
|
||||
fsType: ext4
|
||||
adminSecretNamespace: default
|
||||
adminSecretName: storageos-secret
|
||||
```
|
||||
|
||||
[Download example](storageos-sc.yaml?raw=true)
|
||||
|
||||
Create the storage class:
|
||||
|
||||
```bash
|
||||
$ kubectl create -f examples/volumes/storageos/storageos-sc.yaml
|
||||
```
|
||||
|
||||
Verify the storage class has been created:
|
||||
|
||||
```bash
|
||||
$ kubectl describe storageclass fast
|
||||
Name: fast
|
||||
IsDefaultClass: No
|
||||
Annotations: <none>
|
||||
Provisioner: kubernetes.io/storageos
|
||||
Parameters: description=Kubernetes volume,fsType=ext4,pool=default,secretName=storageos-secret
|
||||
No events.
|
||||
```
|
||||
|
||||
1. Create persistent volume claim
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: fast0001
|
||||
spec:
|
||||
storageClassName: fast
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
```
|
||||
|
||||
Create the persistent volume claim (pvc):
|
||||
|
||||
```bash
|
||||
$ kubectl create -f examples/volumes/storageos/storageos-sc-pvc.yaml
|
||||
```
|
||||
|
||||
Verify the pvc has been created:
|
||||
|
||||
```bash
|
||||
$ kubectl describe pvc fast0001
|
||||
Name: fast0001
|
||||
Namespace: default
|
||||
StorageClass: fast
|
||||
Status: Bound
|
||||
Volume: pvc-480952e7-f8e0-11e6-af8c-08002736b526
|
||||
Labels: <none>
|
||||
Capacity: 5Gi
|
||||
Access Modes: RWO
|
||||
Events:
|
||||
<snip>
|
||||
```
|
||||
|
||||
A new persistent volume will also be created and bound to the pvc:
|
||||
|
||||
```bash
|
||||
$ kubectl describe pv pvc-480952e7-f8e0-11e6-af8c-08002736b526
|
||||
Name: pvc-480952e7-f8e0-11e6-af8c-08002736b526
|
||||
Labels: storageos.driver=filesystem
|
||||
StorageClass: fast
|
||||
Status: Bound
|
||||
Claim: default/fast0001
|
||||
Reclaim Policy: Delete
|
||||
Access Modes: RWO
|
||||
Capacity: 5Gi
|
||||
Message:
|
||||
Source:
|
||||
Type: StorageOS (a StorageOS Persistent Disk resource)
|
||||
VolumeName: pvc-480952e7-f8e0-11e6-af8c-08002736b526
|
||||
Namespace: default
|
||||
FSType: ext4
|
||||
ReadOnly: false
|
||||
No events.
|
||||
```
|
||||
|
||||
1. Create pod which uses the persistent volume claim
|
||||
|
||||
Example spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
name: redis
|
||||
role: master
|
||||
name: test-storageos-redis-sc-pvc
|
||||
spec:
|
||||
containers:
|
||||
- name: master
|
||||
image: kubernetes/redis:v1
|
||||
env:
|
||||
- name: MASTER
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
limits:
|
||||
cpu: "0.1"
|
||||
volumeMounts:
|
||||
- mountPath: /redis-master-data
|
||||
name: redis-data
|
||||
volumes:
|
||||
- name: redis-data
|
||||
persistentVolumeClaim:
|
||||
claimName: fast0001
|
||||
```
|
||||
|
||||
[Download example](storageos-sc-pvcpod.yaml?raw=true)
|
||||
|
||||
Create the pod:
|
||||
|
||||
```bash
|
||||
$ kubectl create -f examples/volumes/storageos/storageos-sc-pvcpod.yaml
|
||||
```
|
||||
|
||||
Verify that the pod has been created:
|
||||
|
||||
```bash
|
||||
$ kubectl get pods
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
test-storageos-redis-sc-pvc 1/1 Running 0 44s
|
||||
```
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||
[]()
|
||||
<!-- END MUNGE: GENERATED_ANALYTICS -->
|
|
@ -0,0 +1,37 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
name: redis
|
||||
role: master
|
||||
name: test-storageos-redis
|
||||
spec:
|
||||
containers:
|
||||
- name: master
|
||||
image: kubernetes/redis:v1
|
||||
env:
|
||||
- name: MASTER
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
limits:
|
||||
cpu: "0.1"
|
||||
volumeMounts:
|
||||
- mountPath: /redis-master-data
|
||||
name: redis-data
|
||||
volumes:
|
||||
- name: redis-data
|
||||
storageos:
|
||||
# This volume must already exist within StorageOS
|
||||
volumeName: redis-vol01
|
||||
# Namespace is optional, and specifies the volume scope within
|
||||
# StorageOS. If no namespace is provided, it will use the namespace
|
||||
# of the pod. Set to `default` or leave blank if you are not using
|
||||
# namespaces.
|
||||
#namespace: test-storageos
|
||||
# The name of the storageos pool to use. Will use `default` if not
|
||||
# specified, which should be available on most StorageOS clusters.
|
||||
pool: default
|
||||
# The filesystem type to create on the volume, if required.
|
||||
fsType: ext4
|
|
@ -0,0 +1,22 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: pv0001
|
||||
spec:
|
||||
capacity:
|
||||
storage: 5Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Delete
|
||||
storageClassName: fast
|
||||
storageos:
|
||||
# This volume must already exist within StorageOS
|
||||
volumeName: pv0001
|
||||
# volumeNamespace is optional, and specifies the volume scope within
|
||||
# StorageOS. Set to `default` or leave blank if you are not using
|
||||
# namespaces.
|
||||
#volumeNamespace: default
|
||||
# The filesystem type to create on the volume, if required.
|
||||
fsType: ext4
|
||||
# The secret name for API credentials
|
||||
secretName: storageos-secret
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: pvc0001
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
storageClassName: fast
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
name: redis
|
||||
role: master
|
||||
name: test-storageos-redis-pvc
|
||||
spec:
|
||||
containers:
|
||||
- name: master
|
||||
image: kubernetes/redis:v1
|
||||
env:
|
||||
- name: MASTER
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
limits:
|
||||
cpu: "0.1"
|
||||
volumeMounts:
|
||||
- mountPath: /redis-master-data
|
||||
name: redis-data
|
||||
volumes:
|
||||
- name: redis-data
|
||||
persistentVolumeClaim:
|
||||
claimName: pvc0001
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: fast0001
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: fast
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
name: redis
|
||||
role: master
|
||||
name: test-storageos-redis-sc-pvc
|
||||
spec:
|
||||
containers:
|
||||
- name: master
|
||||
image: kubernetes/redis:v1
|
||||
env:
|
||||
- name: MASTER
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
limits:
|
||||
cpu: "0.1"
|
||||
volumeMounts:
|
||||
- mountPath: /redis-master-data
|
||||
name: redis-data
|
||||
volumes:
|
||||
- name: redis-data
|
||||
persistentVolumeClaim:
|
||||
claimName: fast0001
|
|
@ -0,0 +1,11 @@
|
|||
kind: StorageClass
|
||||
apiVersion: storage.k8s.io/v1
|
||||
metadata:
|
||||
name: sc-fast
|
||||
provisioner: kubernetes.io/storageos
|
||||
parameters:
|
||||
pool: default
|
||||
description: Kubernetes volume
|
||||
fsType: ext4
|
||||
adminSecretNamespace: default
|
||||
adminSecretName: storageos-secret
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: storageos-secret
|
||||
type: "kubernetes.io/storageos"
|
||||
data:
|
||||
apiAddress: dGNwOi8vMTI3LjAuMC4xOjU3MDU=
|
||||
apiUsername: c3RvcmFnZW9z
|
||||
apiPassword: c3RvcmFnZW9z
|
|
@ -12650,6 +12650,31 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.kubernetes.pkg.api.v1.StorageOSVolumeSource": {
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"fsType": {
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
||||
"type": "string"
|
||||
},
|
||||
"readOnly": {
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"secretRef": {
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.LocalObjectReference"
|
||||
},
|
||||
"volumeName": {
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
|
||||
"type": "string"
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.kubernetes.pkg.api.v1.TCPSocketAction": {
|
||||
"description": "TCPSocketAction describes an action based on opening a socket",
|
||||
"required": [
|
||||
|
@ -12804,6 +12829,10 @@
|
|||
"description": "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.SecretVolumeSource"
|
||||
},
|
||||
"storageos": {
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.StorageOSVolumeSource"
|
||||
},
|
||||
"vsphereVolume": {
|
||||
"description": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine",
|
||||
"$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.VsphereVirtualDiskVolumeSource"
|
||||
|
|
|
@ -5161,6 +5161,10 @@
|
|||
"scaleIO": {
|
||||
"$ref": "v1.ScaleIOVolumeSource",
|
||||
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
|
||||
},
|
||||
"storageos": {
|
||||
"$ref": "v1.StorageOSVolumeSource",
|
||||
"description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -6024,6 +6028,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.StorageOSVolumeSource": {
|
||||
"id": "v1.StorageOSVolumeSource",
|
||||
"description": "Represents a StorageOS persistent volume resource.",
|
||||
"properties": {
|
||||
"volumeName": {
|
||||
"type": "string",
|
||||
"description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace."
|
||||
},
|
||||
"volumeNamespace": {
|
||||
"type": "string",
|
||||
"description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean",
|
||||
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
|
||||
},
|
||||
"secretRef": {
|
||||
"$ref": "v1.LocalObjectReference",
|
||||
"description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Container": {
|
||||
"id": "v1.Container",
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
|
|
|
@ -3548,6 +3548,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_scaleiovolumesource">v1.ScaleIOVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">storageos</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.</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_storageosvolumesource">v1.StorageOSVolumeSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -6972,6 +6979,68 @@ Both these may change in the future. Incoming requests are matched against the h
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_storageosvolumesource">v1.StorageOSVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a StorageOS persistent 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">volumeName</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.</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">volumeNamespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod’s namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.</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">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">readOnly</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.</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_localobjectreference">v1.LocalObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_nodeaffinity">v1.NodeAffinity</h3>
|
||||
|
@ -7225,7 +7294,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-06-04 01:49:56 UTC
|
||||
Last updated 2017-06-07 15:17:51 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -20,34 +20,46 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
func getClaimRefNamespace(pv *api.PersistentVolume) string {
|
||||
if pv.Spec.ClaimRef != nil {
|
||||
return pv.Spec.ClaimRef.Namespace
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// VisitPVSecretNames invokes the visitor function with the name of every secret
|
||||
// referenced by the PV spec. If visitor returns false, visiting is short-circuited.
|
||||
// Returns true if visiting completed, false if visiting was short-circuited.
|
||||
func VisitPVSecretNames(pv *api.PersistentVolume, visitor func(string) bool) bool {
|
||||
func VisitPVSecretNames(pv *api.PersistentVolume, visitor func(string, string) bool) bool {
|
||||
source := &pv.Spec.PersistentVolumeSource
|
||||
switch {
|
||||
case source.AzureFile != nil:
|
||||
if len(source.AzureFile.SecretName) > 0 && !visitor(source.AzureFile.SecretName) {
|
||||
if len(source.AzureFile.SecretName) > 0 && !visitor(getClaimRefNamespace(pv), source.AzureFile.SecretName) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
case source.CephFS != nil:
|
||||
if source.CephFS.SecretRef != nil && !visitor(source.CephFS.SecretRef.Name) {
|
||||
if source.CephFS.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.CephFS.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.FlexVolume != nil:
|
||||
if source.FlexVolume.SecretRef != nil && !visitor(source.FlexVolume.SecretRef.Name) {
|
||||
if source.FlexVolume.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.FlexVolume.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.RBD != nil:
|
||||
if source.RBD.SecretRef != nil && !visitor(source.RBD.SecretRef.Name) {
|
||||
if source.RBD.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.RBD.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.ScaleIO != nil:
|
||||
if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) {
|
||||
if source.ScaleIO.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.ScaleIO.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.ISCSI != nil:
|
||||
if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) {
|
||||
if source.ISCSI.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.ISCSI.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.StorageOS != nil:
|
||||
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Namespace, source.StorageOS.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,11 +54,21 @@ func TestPVSecrets(t *testing.T) {
|
|||
ISCSI: &api.ISCSIVolumeSource{
|
||||
SecretRef: &api.LocalObjectReference{
|
||||
Name: "Spec.PersistentVolumeSource.ISCSI.SecretRef"}}}}},
|
||||
{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
StorageOS: &api.StorageOSPersistentVolumeSource{
|
||||
SecretRef: &api.ObjectReference{
|
||||
Name: "Spec.PersistentVolumeSource.StorageOS.SecretRef",
|
||||
Namespace: "Spec.PersistentVolumeSource.StorageOS.SecretRef"}}}}},
|
||||
}
|
||||
|
||||
extractedNames := sets.NewString()
|
||||
extractedNamespaces := sets.NewString()
|
||||
for _, pv := range pvs {
|
||||
VisitPVSecretNames(pv, func(name string) bool {
|
||||
VisitPVSecretNames(pv, func(namespace, name string) bool {
|
||||
extractedNames.Insert(name)
|
||||
if namespace != "" {
|
||||
extractedNamespaces.Insert(namespace)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
@ -76,6 +86,7 @@ func TestPVSecrets(t *testing.T) {
|
|||
"Spec.PersistentVolumeSource.RBD.SecretRef",
|
||||
"Spec.PersistentVolumeSource.ScaleIO.SecretRef",
|
||||
"Spec.PersistentVolumeSource.ISCSI.SecretRef",
|
||||
"Spec.PersistentVolumeSource.StorageOS.SecretRef",
|
||||
)
|
||||
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.PersistentVolume{}))
|
||||
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
||||
|
@ -96,6 +107,14 @@ func TestPVSecrets(t *testing.T) {
|
|||
t.Logf("Extra secret names:\n%s", strings.Join(extraNames.List(), "\n"))
|
||||
t.Error("Extra secret names extracted. Verify VisitPVSecretNames() is correctly extracting secret names")
|
||||
}
|
||||
|
||||
expectedSecretNamespaces := sets.NewString(
|
||||
"Spec.PersistentVolumeSource.StorageOS.SecretRef",
|
||||
)
|
||||
|
||||
if len(expectedSecretNamespaces.Difference(extractedNamespaces)) > 0 {
|
||||
t.Errorf("Missing expected secret namespace")
|
||||
}
|
||||
}
|
||||
|
||||
// collectSecretPaths traverses the object, computing all the struct paths that lead to fields with "secret" in the name.
|
||||
|
|
|
@ -84,6 +84,10 @@ func VisitPodSecretNames(pod *api.Pod, visitor Visitor) bool {
|
|||
if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.StorageOS != nil:
|
||||
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -89,7 +89,11 @@ func TestPodSecrets(t *testing.T) {
|
|||
VolumeSource: api.VolumeSource{
|
||||
ISCSI: &api.ISCSIVolumeSource{
|
||||
SecretRef: &api.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}},
|
||||
Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}, {
|
||||
VolumeSource: api.VolumeSource{
|
||||
StorageOS: &api.StorageOSVolumeSource{
|
||||
SecretRef: &api.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}},
|
||||
},
|
||||
}
|
||||
extractedNames := sets.NewString()
|
||||
|
@ -119,6 +123,7 @@ func TestPodSecrets(t *testing.T) {
|
|||
"Spec.Volumes[*].VolumeSource.Secret.SecretName",
|
||||
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
|
||||
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
|
||||
"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
|
||||
)
|
||||
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.Pod{}))
|
||||
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
||||
|
|
|
@ -313,6 +313,9 @@ type VolumeSource struct {
|
|||
// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
ScaleIO *ScaleIOVolumeSource
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// +optional
|
||||
StorageOS *StorageOSVolumeSource
|
||||
}
|
||||
|
||||
// Similar to VolumeSource but meant for the administrator who creates PVs.
|
||||
|
@ -384,6 +387,10 @@ type PersistentVolumeSource struct {
|
|||
// Local represents directly-attached storage with node affinity
|
||||
// +optional
|
||||
Local *LocalVolumeSource
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
||||
// +optional
|
||||
StorageOS *StorageOSPersistentVolumeSource
|
||||
}
|
||||
|
||||
type PersistentVolumeClaimVolumeSource struct {
|
||||
|
@ -1149,6 +1156,62 @@ type ScaleIOVolumeSource struct {
|
|||
ReadOnly bool
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
ReadOnly bool
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *LocalObjectReference
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSPersistentVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
ReadOnly bool
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *ObjectReference
|
||||
}
|
||||
|
||||
// Adapts a ConfigMap into a volume.
|
||||
//
|
||||
// The contents of the target ConfigMap's Data field will be presented in a
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2222,6 +2222,11 @@ message PersistentVolumeSource {
|
|||
// Local represents directly-attached storage with node affinity
|
||||
// +optional
|
||||
optional LocalVolumeSource local = 20;
|
||||
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
||||
// +optional
|
||||
optional StorageOSPersistentVolumeSource storageos = 21;
|
||||
}
|
||||
|
||||
// PersistentVolumeSpec is the specification of a persistent volume.
|
||||
|
@ -3760,6 +3765,70 @@ message ServiceStatus {
|
|||
optional LoadBalancerStatus loadBalancer = 1;
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
message StorageOSPersistentVolumeSource {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
optional string volumeName = 1;
|
||||
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
optional string volumeNamespace = 2;
|
||||
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
optional string fsType = 3;
|
||||
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
optional bool readOnly = 4;
|
||||
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
optional ObjectReference secretRef = 5;
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
message StorageOSVolumeSource {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
optional string volumeName = 1;
|
||||
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
optional string volumeNamespace = 2;
|
||||
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
optional string fsType = 3;
|
||||
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
optional bool readOnly = 4;
|
||||
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
optional LocalObjectReference secretRef = 5;
|
||||
}
|
||||
|
||||
// Sysctl defines a kernel parameter to be set
|
||||
message Sysctl {
|
||||
// Name of a property to set
|
||||
|
@ -4011,6 +4080,10 @@ message VolumeSource {
|
|||
// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
optional ScaleIOVolumeSource scaleIO = 25;
|
||||
|
||||
// StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
optional StorageOSVolumeSource storageos = 27;
|
||||
}
|
||||
|
||||
// Represents a vSphere volume resource.
|
||||
|
|
|
@ -174,6 +174,10 @@ func VisitPodSecretNames(pod *v1.Pod, visitor Visitor) bool {
|
|||
if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.StorageOS != nil:
|
||||
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -260,7 +260,11 @@ func TestPodSecrets(t *testing.T) {
|
|||
VolumeSource: v1.VolumeSource{
|
||||
ISCSI: &v1.ISCSIVolumeSource{
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}},
|
||||
Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}, {
|
||||
VolumeSource: v1.VolumeSource{
|
||||
StorageOS: &v1.StorageOSVolumeSource{
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}},
|
||||
},
|
||||
}
|
||||
extractedNames := sets.NewString()
|
||||
|
@ -290,6 +294,7 @@ func TestPodSecrets(t *testing.T) {
|
|||
"Spec.Volumes[*].VolumeSource.Secret.SecretName",
|
||||
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
|
||||
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
|
||||
"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
|
||||
)
|
||||
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&v1.Pod{}))
|
||||
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -348,6 +348,9 @@ type VolumeSource struct {
|
|||
// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
ScaleIO *ScaleIOVolumeSource `json:"scaleIO,omitempty" protobuf:"bytes,25,opt,name=scaleIO"`
|
||||
// StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
StorageOS *StorageOSVolumeSource `json:"storageos,omitempty" protobuf:"bytes,27,opt,name=storageos"`
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
|
||||
|
@ -442,6 +445,10 @@ type PersistentVolumeSource struct {
|
|||
// Local represents directly-attached storage with node affinity
|
||||
// +optional
|
||||
Local *LocalVolumeSource `json:"local,omitempty" protobuf:"bytes,20,opt,name=local"`
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
||||
// +optional
|
||||
StorageOS *StorageOSPersistentVolumeSource `json:"storageos,omitempty" protobuf:"bytes,21,opt,name=storageos"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1228,6 +1235,62 @@ type ScaleIOVolumeSource struct {
|
|||
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,10,opt,name=readOnly"`
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string `json:"volumeName,omitempty" protobuf:"bytes,1,opt,name=volumeName"`
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string `json:"volumeNamespace,omitempty" protobuf:"bytes,2,opt,name=volumeNamespace"`
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string `json:"fsType,omitempty" protobuf:"bytes,3,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,4,opt,name=readOnly"`
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *LocalObjectReference `json:"secretRef,omitempty" protobuf:"bytes,5,opt,name=secretRef"`
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSPersistentVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string `json:"volumeName,omitempty" protobuf:"bytes,1,opt,name=volumeName"`
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string `json:"volumeNamespace,omitempty" protobuf:"bytes,2,opt,name=volumeNamespace"`
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string `json:"fsType,omitempty" protobuf:"bytes,3,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,4,opt,name=readOnly"`
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *ObjectReference `json:"secretRef,omitempty" protobuf:"bytes,5,opt,name=secretRef"`
|
||||
}
|
||||
|
||||
// Adapts a ConfigMap into a volume.
|
||||
//
|
||||
// The contents of the target ConfigMap's Data field will be presented in a
|
||||
|
|
|
@ -1161,6 +1161,7 @@ var map_PersistentVolumeSource = map[string]string{
|
|||
"portworxVolume": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
|
||||
"scaleIO": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.",
|
||||
"local": "Local represents directly-attached storage with node affinity",
|
||||
"storageos": "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md",
|
||||
}
|
||||
|
||||
func (PersistentVolumeSource) SwaggerDoc() map[string]string {
|
||||
|
@ -1875,6 +1876,32 @@ func (ServiceStatus) SwaggerDoc() map[string]string {
|
|||
return map_ServiceStatus
|
||||
}
|
||||
|
||||
var map_StorageOSPersistentVolumeSource = map[string]string{
|
||||
"": "Represents a StorageOS persistent volume resource.",
|
||||
"volumeName": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
|
||||
"volumeNamespace": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
|
||||
"fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
||||
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
|
||||
"secretRef": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.",
|
||||
}
|
||||
|
||||
func (StorageOSPersistentVolumeSource) SwaggerDoc() map[string]string {
|
||||
return map_StorageOSPersistentVolumeSource
|
||||
}
|
||||
|
||||
var map_StorageOSVolumeSource = map[string]string{
|
||||
"": "Represents a StorageOS persistent volume resource.",
|
||||
"volumeName": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
|
||||
"volumeNamespace": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
|
||||
"fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
||||
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
|
||||
"secretRef": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.",
|
||||
}
|
||||
|
||||
func (StorageOSVolumeSource) SwaggerDoc() map[string]string {
|
||||
return map_StorageOSVolumeSource
|
||||
}
|
||||
|
||||
var map_Sysctl = map[string]string{
|
||||
"": "Sysctl defines a kernel parameter to be set",
|
||||
"Name": "Name of a property to set",
|
||||
|
@ -1980,6 +2007,7 @@ var map_VolumeSource = map[string]string{
|
|||
"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",
|
||||
"scaleIO": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.",
|
||||
"storageos": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.",
|
||||
}
|
||||
|
||||
func (VolumeSource) SwaggerDoc() map[string]string {
|
||||
|
|
|
@ -355,6 +355,10 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||
Convert_api_ServiceSpec_To_v1_ServiceSpec,
|
||||
Convert_v1_ServiceStatus_To_api_ServiceStatus,
|
||||
Convert_api_ServiceStatus_To_v1_ServiceStatus,
|
||||
Convert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource,
|
||||
Convert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource,
|
||||
Convert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource,
|
||||
Convert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource,
|
||||
Convert_v1_Sysctl_To_api_Sysctl,
|
||||
Convert_api_Sysctl_To_v1_Sysctl,
|
||||
Convert_v1_TCPSocketAction_To_api_TCPSocketAction,
|
||||
|
@ -3027,6 +3031,7 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *Per
|
|||
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*api.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.Local = (*api.LocalVolumeSource)(unsafe.Pointer(in.Local))
|
||||
out.StorageOS = (*api.StorageOSPersistentVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -3056,6 +3061,7 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
|
|||
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.Local = (*LocalVolumeSource)(unsafe.Pointer(in.Local))
|
||||
out.StorageOS = (*StorageOSPersistentVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -4807,6 +4813,62 @@ func Convert_api_ServiceStatus_To_v1_ServiceStatus(in *api.ServiceStatus, out *S
|
|||
return autoConvert_api_ServiceStatus_To_v1_ServiceStatus(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource(in *StorageOSPersistentVolumeSource, out *api.StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*api.ObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource is an autogenerated conversion function.
|
||||
func Convert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource(in *StorageOSPersistentVolumeSource, out *api.StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource(in *api.StorageOSPersistentVolumeSource, out *StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*ObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource is an autogenerated conversion function.
|
||||
func Convert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource(in *api.StorageOSPersistentVolumeSource, out *StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource(in *StorageOSVolumeSource, out *api.StorageOSVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*api.LocalObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource is an autogenerated conversion function.
|
||||
func Convert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource(in *StorageOSVolumeSource, out *api.StorageOSVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource(in *api.StorageOSVolumeSource, out *StorageOSVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*LocalObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource is an autogenerated conversion function.
|
||||
func Convert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource(in *api.StorageOSVolumeSource, out *StorageOSVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_Sysctl_To_api_Sysctl(in *Sysctl, out *api.Sysctl, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Value = in.Value
|
||||
|
@ -5008,6 +5070,7 @@ func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.
|
|||
out.Projected = (*api.ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
|
||||
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*api.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.StorageOS = (*api.StorageOSVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -5043,6 +5106,7 @@ func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *
|
|||
out.Projected = (*ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
|
||||
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.StorageOS = (*StorageOSVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -195,6 +195,8 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
|||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ServiceProxyOptions, InType: reflect.TypeOf(&ServiceProxyOptions{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ServiceSpec, InType: reflect.TypeOf(&ServiceSpec{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ServiceStatus, InType: reflect.TypeOf(&ServiceStatus{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_StorageOSPersistentVolumeSource, InType: reflect.TypeOf(&StorageOSPersistentVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_StorageOSVolumeSource, InType: reflect.TypeOf(&StorageOSVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Sysctl, InType: reflect.TypeOf(&Sysctl{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_TCPSocketAction, InType: reflect.TypeOf(&TCPSocketAction{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Taint, InType: reflect.TypeOf(&Taint{})},
|
||||
|
@ -2182,6 +2184,13 @@ func DeepCopy_v1_PersistentVolumeSource(in interface{}, out interface{}, c *conv
|
|||
*out = new(LocalVolumeSource)
|
||||
**out = **in
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSPersistentVolumeSource)
|
||||
if err := DeepCopy_v1_StorageOSPersistentVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -3436,6 +3445,36 @@ func DeepCopy_v1_ServiceStatus(in interface{}, out interface{}, c *conversion.Cl
|
|||
}
|
||||
}
|
||||
|
||||
// DeepCopy_v1_StorageOSPersistentVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1_StorageOSPersistentVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSPersistentVolumeSource)
|
||||
out := out.(*StorageOSPersistentVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(ObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_v1_StorageOSVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1_StorageOSVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSVolumeSource)
|
||||
out := out.(*StorageOSVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_v1_Sysctl is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1_Sysctl(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
|
@ -3696,6 +3735,13 @@ func DeepCopy_v1_VolumeSource(in interface{}, out interface{}, c *conversion.Clo
|
|||
return err
|
||||
}
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSVolumeSource)
|
||||
if err := DeepCopy_v1_StorageOSVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -591,6 +591,14 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
|
|||
allErrs = append(allErrs, validateAzureDisk(source.AzureDisk, fldPath.Child("azureDisk"))...)
|
||||
}
|
||||
}
|
||||
if source.StorageOS != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("storageos"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateStorageOSVolumeSource(source.StorageOS, fldPath.Child("storageos"))...)
|
||||
}
|
||||
}
|
||||
if source.Projected != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("projected"), "may not specify more than 1 volume type"))
|
||||
|
@ -1116,6 +1124,45 @@ func validateLocalVolumeSource(ls *api.LocalVolumeSource, fldPath *field.Path) f
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func validateStorageOSVolumeSource(storageos *api.StorageOSVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(storageos.VolumeName) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeName, fldPath.Child("volumeName"))...)
|
||||
}
|
||||
if len(storageos.VolumeNamespace) > 0 {
|
||||
allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeNamespace, fldPath.Child("volumeNamespace"))...)
|
||||
}
|
||||
if storageos.SecretRef != nil {
|
||||
if len(storageos.SecretRef.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateStorageOSPersistentVolumeSource(storageos *api.StorageOSPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(storageos.VolumeName) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeName, fldPath.Child("volumeName"))...)
|
||||
}
|
||||
if len(storageos.VolumeNamespace) > 0 {
|
||||
allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeNamespace, fldPath.Child("volumeNamespace"))...)
|
||||
}
|
||||
if storageos.SecretRef != nil {
|
||||
if len(storageos.SecretRef.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
|
||||
}
|
||||
if len(storageos.SecretRef.Namespace) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "namespace"), ""))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePersistentVolumeName checks that a name is appropriate for a
|
||||
// PersistentVolumeName object.
|
||||
var ValidatePersistentVolumeName = NameIsDNSSubdomain
|
||||
|
@ -1325,6 +1372,14 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
|
|||
}
|
||||
}
|
||||
}
|
||||
if pv.Spec.StorageOS != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(specPath.Child("storageos"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateStorageOSPersistentVolumeSource(pv.Spec.StorageOS, specPath.Child("storageos"))...)
|
||||
}
|
||||
}
|
||||
|
||||
if numVolumes == 0 {
|
||||
allErrs = append(allErrs, field.Required(specPath, "must specify a volume type"))
|
||||
|
|
|
@ -197,6 +197,8 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
|||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ServiceProxyOptions, InType: reflect.TypeOf(&ServiceProxyOptions{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ServiceSpec, InType: reflect.TypeOf(&ServiceSpec{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ServiceStatus, InType: reflect.TypeOf(&ServiceStatus{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_StorageOSPersistentVolumeSource, InType: reflect.TypeOf(&StorageOSPersistentVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_StorageOSVolumeSource, InType: reflect.TypeOf(&StorageOSVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Sysctl, InType: reflect.TypeOf(&Sysctl{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_TCPSocketAction, InType: reflect.TypeOf(&TCPSocketAction{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Taint, InType: reflect.TypeOf(&Taint{})},
|
||||
|
@ -2200,6 +2202,13 @@ func DeepCopy_api_PersistentVolumeSource(in interface{}, out interface{}, c *con
|
|||
*out = new(LocalVolumeSource)
|
||||
**out = **in
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSPersistentVolumeSource)
|
||||
if err := DeepCopy_api_StorageOSPersistentVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -3442,6 +3451,36 @@ func DeepCopy_api_ServiceStatus(in interface{}, out interface{}, c *conversion.C
|
|||
}
|
||||
}
|
||||
|
||||
// DeepCopy_api_StorageOSPersistentVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_StorageOSPersistentVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSPersistentVolumeSource)
|
||||
out := out.(*StorageOSPersistentVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(ObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_api_StorageOSVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_StorageOSVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSVolumeSource)
|
||||
out := out.(*StorageOSVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_api_Sysctl is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_Sysctl(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
|
@ -3702,6 +3741,13 @@ func DeepCopy_api_VolumeSource(in interface{}, out interface{}, c *conversion.Cl
|
|||
return err
|
||||
}
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSVolumeSource)
|
||||
if err := DeepCopy_api_StorageOSVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -945,6 +945,7 @@ var (
|
|||
Quobyte FSType = "quobyte"
|
||||
AzureDisk FSType = "azureDisk"
|
||||
PhotonPersistentDisk FSType = "photonPersistentDisk"
|
||||
StorageOS FSType = "storageos"
|
||||
Projected FSType = "projected"
|
||||
PortworxVolume FSType = "portworxVolume"
|
||||
ScaleIO FSType = "scaleIO"
|
||||
|
|
|
@ -742,6 +742,8 @@ func describeVolumes(volumes []api.Volume, w PrefixWriter, space string) {
|
|||
printScaleIOVolumeSource(volume.VolumeSource.ScaleIO, w)
|
||||
case volume.VolumeSource.CephFS != nil:
|
||||
printCephFSVolumeSource(volume.VolumeSource.CephFS, w)
|
||||
case volume.VolumeSource.StorageOS != nil:
|
||||
printStorageOSVolumeSource(volume.VolumeSource.StorageOS, w)
|
||||
default:
|
||||
w.Write(LEVEL_1, "<unknown>\n")
|
||||
}
|
||||
|
@ -940,6 +942,24 @@ func printCephFSVolumeSource(cephfs *api.CephFSVolumeSource, w PrefixWriter) {
|
|||
cephfs.Monitors, cephfs.Path, cephfs.User, cephfs.SecretFile, cephfs.SecretRef, cephfs.ReadOnly)
|
||||
}
|
||||
|
||||
func printStorageOSVolumeSource(storageos *api.StorageOSVolumeSource, w PrefixWriter) {
|
||||
w.Write(LEVEL_2, "Type:\tStorageOS (a StorageOS Persistent Disk resource)\n"+
|
||||
" VolumeName:\t%v\n"+
|
||||
" VolumeNamespace:\t%v\n"+
|
||||
" FSType:\t%v\n"+
|
||||
" ReadOnly:\t%v\n",
|
||||
storageos.VolumeName, storageos.VolumeNamespace, storageos.FSType, storageos.ReadOnly)
|
||||
}
|
||||
|
||||
func printStorageOSPersistentVolumeSource(storageos *api.StorageOSPersistentVolumeSource, w PrefixWriter) {
|
||||
w.Write(LEVEL_2, "Type:\tStorageOS (a StorageOS Persistent Disk resource)\n"+
|
||||
" VolumeName:\t%v\n"+
|
||||
" VolumeNamespace:\t%v\n"+
|
||||
" FSType:\t%v\n"+
|
||||
" ReadOnly:\t%v\n",
|
||||
storageos.VolumeName, storageos.VolumeNamespace, storageos.FSType, storageos.ReadOnly)
|
||||
}
|
||||
|
||||
type PersistentVolumeDescriber struct {
|
||||
clientset.Interface
|
||||
}
|
||||
|
@ -1013,6 +1033,8 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) (
|
|||
printLocalVolumeSource(pv.Spec.Local, w)
|
||||
case pv.Spec.CephFS != nil:
|
||||
printCephFSVolumeSource(pv.Spec.CephFS, w)
|
||||
case pv.Spec.StorageOS != nil:
|
||||
printStorageOSPersistentVolumeSource(pv.Spec.StorageOS, w)
|
||||
}
|
||||
|
||||
if events != nil {
|
||||
|
|
|
@ -64,6 +64,7 @@ func GetAllFSTypesAsSet() sets.String {
|
|||
string(extensions.Quobyte),
|
||||
string(extensions.AzureDisk),
|
||||
string(extensions.PhotonPersistentDisk),
|
||||
string(extensions.StorageOS),
|
||||
string(extensions.Projected),
|
||||
string(extensions.PortworxVolume),
|
||||
string(extensions.ScaleIO),
|
||||
|
@ -120,6 +121,8 @@ func GetVolumeFSType(v api.Volume) (extensions.FSType, error) {
|
|||
return extensions.AzureDisk, nil
|
||||
case v.PhotonPersistentDisk != nil:
|
||||
return extensions.PhotonPersistentDisk, nil
|
||||
case v.StorageOS != nil:
|
||||
return extensions.StorageOS, nil
|
||||
case v.Projected != nil:
|
||||
return extensions.Projected, nil
|
||||
case v.PortworxVolume != nil:
|
||||
|
|
|
@ -115,6 +115,7 @@ filegroup(
|
|||
"//pkg/volume/rbd:all-srcs",
|
||||
"//pkg/volume/scaleio:all-srcs",
|
||||
"//pkg/volume/secret:all-srcs",
|
||||
"//pkg/volume/storageos:all-srcs",
|
||||
"//pkg/volume/testing:all-srcs",
|
||||
"//pkg/volume/util:all-srcs",
|
||||
"//pkg/volume/validation:all-srcs",
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"storageos.go",
|
||||
"storageos_util.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/client/clientset_generated/clientset: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",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/storageos/go-api:go_default_library",
|
||||
"//vendor/github.com/storageos/go-api/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"storageos_test.go",
|
||||
"storageos_util_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/client/clientset_generated/clientset/fake:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/testing:go_default_library",
|
||||
"//vendor/github.com/storageos/go-api/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
maintainers:
|
||||
- croomes
|
||||
- rusenask
|
||||
- chira001
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
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 storageos contains the internal representation of StorageOS
|
||||
// PersistentDisk volumes.
|
||||
package storageos // import "k8s.io/kubernetes/pkg/volume/storageos"
|
|
@ -0,0 +1,741 @@
|
|||
/*
|
||||
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 storageos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
kstrings "k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
)
|
||||
|
||||
// ProbeVolumePlugins is the primary entrypoint for volume plugins.
|
||||
func ProbeVolumePlugins() []volume.VolumePlugin {
|
||||
return []volume.VolumePlugin{&storageosPlugin{nil}}
|
||||
}
|
||||
|
||||
type storageosPlugin struct {
|
||||
host volume.VolumeHost
|
||||
}
|
||||
|
||||
var _ volume.VolumePlugin = &storageosPlugin{}
|
||||
var _ volume.PersistentVolumePlugin = &storageosPlugin{}
|
||||
var _ volume.DeletableVolumePlugin = &storageosPlugin{}
|
||||
var _ volume.ProvisionableVolumePlugin = &storageosPlugin{}
|
||||
|
||||
const (
|
||||
storageosPluginName = "kubernetes.io/storageos"
|
||||
storageosDevicePath = "/var/lib/storageos/volumes"
|
||||
defaultAPIAddress = "tcp://localhost:5705"
|
||||
defaultAPIUser = "storageos"
|
||||
defaultAPIPassword = "storageos"
|
||||
defaultAPIVersion = "1"
|
||||
defaultFSType = "ext4"
|
||||
defaultNamespace = "default"
|
||||
)
|
||||
|
||||
func getPath(uid types.UID, volNamespace string, volName string, pvName string, host volume.VolumeHost) string {
|
||||
if len(volNamespace) != 0 && len(volName) != 0 && strings.Count(volName, ".") == 0 {
|
||||
return host.GetPodVolumeDir(uid, kstrings.EscapeQualifiedNameForDisk(storageosPluginName), pvName+"."+volNamespace+"."+volName)
|
||||
}
|
||||
return host.GetPodVolumeDir(uid, kstrings.EscapeQualifiedNameForDisk(storageosPluginName), pvName)
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) Init(host volume.VolumeHost) error {
|
||||
plugin.host = host
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) GetPluginName() string {
|
||||
return storageosPluginName
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
||||
volumeSource, _, err := getVolumeSource(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", volumeSource.VolumeNamespace, volumeSource.VolumeName), nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) CanSupport(spec *volume.Spec) bool {
|
||||
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS != nil) ||
|
||||
(spec.Volume != nil && spec.Volume.StorageOS != nil)
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) RequiresRemount() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
|
||||
return []v1.PersistentVolumeAccessMode{
|
||||
v1.ReadWriteOnce,
|
||||
v1.ReadOnlyMany,
|
||||
}
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
|
||||
|
||||
apiCfg, err := getAPICfg(spec, pod, plugin.host.GetKubeClient())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plugin.newMounterInternal(spec, pod, apiCfg, &storageosUtil{}, plugin.host.GetMounter())
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, apiCfg *storageosAPIConfig, manager storageosManager, mounter mount.Interface) (volume.Mounter, error) {
|
||||
|
||||
volName, volNamespace, fsType, readOnly, err := getVolumeInfoFromSpec(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &storageosMounter{
|
||||
storageos: &storageos{
|
||||
podUID: pod.UID,
|
||||
podNamespace: pod.GetNamespace(),
|
||||
pvName: spec.Name(),
|
||||
volName: volName,
|
||||
volNamespace: volNamespace,
|
||||
fsType: fsType,
|
||||
readOnly: readOnly,
|
||||
apiCfg: apiCfg,
|
||||
manager: manager,
|
||||
mounter: mounter,
|
||||
plugin: plugin,
|
||||
MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, volNamespace, volName, spec.Name(), plugin.host)),
|
||||
},
|
||||
devicePath: storageosDevicePath,
|
||||
diskMounter: &mount.SafeFormatAndMount{Interface: mounter, Runner: exec.New()},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) NewUnmounter(pvName string, podUID types.UID) (volume.Unmounter, error) {
|
||||
return plugin.newUnmounterInternal(pvName, podUID, &storageosUtil{}, plugin.host.GetMounter())
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) newUnmounterInternal(pvName string, podUID types.UID, manager storageosManager, mounter mount.Interface) (volume.Unmounter, error) {
|
||||
|
||||
// Parse volume namespace & name from mountpoint if mounted
|
||||
volNamespace, volName, err := getVolumeInfo(pvName, podUID, plugin.host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &storageosUnmounter{
|
||||
storageos: &storageos{
|
||||
podUID: podUID,
|
||||
pvName: pvName,
|
||||
volName: volName,
|
||||
volNamespace: volNamespace,
|
||||
manager: manager,
|
||||
mounter: mounter,
|
||||
plugin: plugin,
|
||||
MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volNamespace, volName, pvName, plugin.host)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
|
||||
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS == nil {
|
||||
return nil, fmt.Errorf("spec.PersistentVolumeSource.StorageOS is nil")
|
||||
}
|
||||
|
||||
class, err := util.GetClassForVolume(plugin.host.GetKubeClient(), spec.PersistentVolume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var adminSecretName, adminSecretNamespace string
|
||||
|
||||
for k, v := range class.Parameters {
|
||||
switch strings.ToLower(k) {
|
||||
case "adminsecretname":
|
||||
adminSecretName = v
|
||||
case "adminsecretnamespace":
|
||||
adminSecretNamespace = v
|
||||
}
|
||||
}
|
||||
|
||||
apiCfg, err := parsePVSecret(adminSecretNamespace, adminSecretName, plugin.host.GetKubeClient())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err)
|
||||
}
|
||||
|
||||
return plugin.newDeleterInternal(spec, apiCfg, &storageosUtil{})
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) newDeleterInternal(spec *volume.Spec, apiCfg *storageosAPIConfig, manager storageosManager) (volume.Deleter, error) {
|
||||
|
||||
return &storageosDeleter{
|
||||
storageosMounter: &storageosMounter{
|
||||
storageos: &storageos{
|
||||
pvName: spec.Name(),
|
||||
volName: spec.PersistentVolume.Spec.StorageOS.VolumeName,
|
||||
volNamespace: spec.PersistentVolume.Spec.StorageOS.VolumeNamespace,
|
||||
apiCfg: apiCfg,
|
||||
manager: manager,
|
||||
plugin: plugin,
|
||||
},
|
||||
},
|
||||
pvUID: spec.PersistentVolume.UID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
|
||||
return plugin.newProvisionerInternal(options, &storageosUtil{})
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) newProvisionerInternal(options volume.VolumeOptions, manager storageosManager) (volume.Provisioner, error) {
|
||||
return &storageosProvisioner{
|
||||
storageosMounter: &storageosMounter{
|
||||
storageos: &storageos{
|
||||
manager: manager,
|
||||
plugin: plugin,
|
||||
},
|
||||
},
|
||||
options: options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
|
||||
volNamespace, volName, err := getVolumeFromRef(volumeName)
|
||||
if err != nil {
|
||||
volNamespace = defaultNamespace
|
||||
volName = volumeName
|
||||
}
|
||||
storageosVolume := &v1.Volume{
|
||||
Name: volumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
StorageOS: &v1.StorageOSVolumeSource{
|
||||
VolumeName: volName,
|
||||
VolumeNamespace: volNamespace,
|
||||
},
|
||||
},
|
||||
}
|
||||
return volume.NewSpecFromVolume(storageosVolume), nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) SupportsMountOption() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) SupportsBulkVolumeVerification() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getVolumeSource(spec *volume.Spec) (*v1.StorageOSVolumeSource, bool, error) {
|
||||
if spec.Volume != nil && spec.Volume.StorageOS != nil {
|
||||
return spec.Volume.StorageOS, spec.Volume.StorageOS.ReadOnly, nil
|
||||
}
|
||||
return nil, false, fmt.Errorf("Spec does not reference a StorageOS volume type")
|
||||
}
|
||||
|
||||
func getPersistentVolumeSource(spec *volume.Spec) (*v1.StorageOSPersistentVolumeSource, bool, error) {
|
||||
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS != nil {
|
||||
return spec.PersistentVolume.Spec.StorageOS, spec.ReadOnly, nil
|
||||
}
|
||||
return nil, false, fmt.Errorf("Spec does not reference a StorageOS persistent volume type")
|
||||
}
|
||||
|
||||
// storageosManager is the abstract interface to StorageOS volume ops.
|
||||
type storageosManager interface {
|
||||
// Connects to the StorageOS API using the supplied configuration.
|
||||
NewAPI(apiCfg *storageosAPIConfig) error
|
||||
// Creates a StorageOS volume.
|
||||
CreateVolume(provisioner *storageosProvisioner) (*storageosVolume, error)
|
||||
// Attaches the disk to the kubelet's host machine.
|
||||
AttachVolume(mounter *storageosMounter) (string, error)
|
||||
// Detaches the disk from the kubelet's host machine.
|
||||
DetachVolume(unmounter *storageosUnmounter, dir string) error
|
||||
// Mounts the disk on the Kubelet's host machine.
|
||||
MountVolume(mounter *storageosMounter, mnt, dir string) error
|
||||
// Unmounts the disk from the Kubelet's host machine.
|
||||
UnmountVolume(unounter *storageosUnmounter) error
|
||||
// Deletes the storageos volume. All data will be lost.
|
||||
DeleteVolume(deleter *storageosDeleter) error
|
||||
}
|
||||
|
||||
// storageos volumes represent a bare host directory mount of an StorageOS export.
|
||||
type storageos struct {
|
||||
podUID types.UID
|
||||
podNamespace string
|
||||
pvName string
|
||||
volName string
|
||||
volNamespace string
|
||||
secretName string
|
||||
readOnly bool
|
||||
description string
|
||||
pool string
|
||||
fsType string
|
||||
sizeGB int
|
||||
labels map[string]string
|
||||
apiCfg *storageosAPIConfig
|
||||
manager storageosManager
|
||||
mounter mount.Interface
|
||||
plugin *storageosPlugin
|
||||
volume.MetricsProvider
|
||||
}
|
||||
|
||||
type storageosMounter struct {
|
||||
*storageos
|
||||
devicePath string
|
||||
// Interface used to mount the file or block device
|
||||
diskMounter *mount.SafeFormatAndMount
|
||||
}
|
||||
|
||||
var _ volume.Mounter = &storageosMounter{}
|
||||
|
||||
func (b *storageosMounter) GetAttributes() volume.Attributes {
|
||||
return volume.Attributes{
|
||||
ReadOnly: b.readOnly,
|
||||
Managed: !b.readOnly,
|
||||
SupportsSELinux: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (b *storageosMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *storageosMounter) SetUp(fsGroup *types.UnixGroupID) error {
|
||||
// Need a namespace to find the volume, try pod's namespace if not set.
|
||||
if b.volNamespace == "" {
|
||||
glog.V(2).Infof("Setting StorageOS volume namespace to pod namespace: %s", b.podNamespace)
|
||||
b.volNamespace = b.podNamespace
|
||||
}
|
||||
|
||||
// Attach the StorageOS volume as a block device
|
||||
devicePath, err := b.manager.AttachVolume(b)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to attach StorageOS volume %s: %s", b.volName, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// Mount the loop device into the plugin's disk global mount dir.
|
||||
globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.podNamespace, b.volName)
|
||||
err = b.manager.MountVolume(b, devicePath, globalPDPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(4).Infof("Successfully mounted StorageOS volume %s into global mount directory", b.volName)
|
||||
|
||||
// Bind mount the volume into the pod
|
||||
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||
}
|
||||
|
||||
// SetUp bind mounts the disk global mount to the give volume path.
|
||||
func (b *storageosMounter) SetUpAt(dir string, fsGroup *types.UnixGroupID) error {
|
||||
notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
glog.V(4).Infof("StorageOS volume set up: %s %v %v", dir, !notMnt, err)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
glog.Errorf("Cannot validate mount point: %s %v", dir, err)
|
||||
return err
|
||||
}
|
||||
if !notMnt {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dir, 0750); err != nil {
|
||||
glog.Errorf("mkdir failed on disk %s (%v)", dir, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform a bind mount to the full path to allow duplicate mounts of the same PD.
|
||||
options := []string{"bind"}
|
||||
if b.readOnly {
|
||||
options = append(options, "ro")
|
||||
}
|
||||
|
||||
globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
|
||||
glog.V(4).Infof("Attempting to bind mount to pod volume at %s", dir)
|
||||
|
||||
err = b.mounter.Mount(globalPDPath, dir, "", options)
|
||||
if err != nil {
|
||||
notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
if mntErr != nil {
|
||||
glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
if !notMnt {
|
||||
if mntErr = b.mounter.Unmount(dir); mntErr != nil {
|
||||
glog.Errorf("Failed to unmount: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
if mntErr != nil {
|
||||
glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
if !notMnt {
|
||||
glog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir)
|
||||
return err
|
||||
}
|
||||
}
|
||||
os.Remove(dir)
|
||||
glog.Errorf("Mount of disk %s failed: %v", dir, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !b.readOnly {
|
||||
volume.SetVolumeOwnership(b, fsGroup)
|
||||
}
|
||||
glog.V(4).Infof("StorageOS volume setup complete on %s", dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeGlobalPDName(host volume.VolumeHost, pvName, volNamespace, volName string) string {
|
||||
return path.Join(host.GetPluginDir(kstrings.EscapeQualifiedNameForDisk(storageosPluginName)), mount.MountsInGlobalPDPath, pvName+"."+volNamespace+"."+volName)
|
||||
}
|
||||
|
||||
// Given the pod id and PV name, finds the volume's namespace and name from the
|
||||
// name or volume mount. We mount as volNamespace.pvName, but k8s will specify
|
||||
// only the pvName to unmount.
|
||||
// Will return empty volNamespace/pvName if the volume is not mounted.
|
||||
func getVolumeInfo(pvName string, podUID types.UID, host volume.VolumeHost) (string, string, error) {
|
||||
if volNamespace, volName, err := getVolumeFromRef(pvName); err == nil {
|
||||
return volNamespace, volName, nil
|
||||
}
|
||||
|
||||
volumeDir := filepath.Dir(host.GetPodVolumeDir(podUID, kstrings.EscapeQualifiedNameForDisk(storageosPluginName), pvName))
|
||||
files, err := ioutil.ReadDir(volumeDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("Could not read mounts from pod volume dir: %s", err)
|
||||
}
|
||||
for _, f := range files {
|
||||
if f.Mode().IsDir() && strings.HasPrefix(f.Name(), pvName+".") {
|
||||
if volNamespace, volName, err := getVolumeFromRef(f.Name()); err == nil {
|
||||
return volNamespace, volName, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("Could not get info from unmounted pv %q at %q", pvName, volumeDir)
|
||||
}
|
||||
|
||||
// Splits the volume ref on "." to return the volNamespace and pvName. Neither
|
||||
// namespaces nor service names allow "." in their names.
|
||||
func getVolumeFromRef(ref string) (volNamespace string, volName string, err error) {
|
||||
refParts := strings.Split(ref, ".")
|
||||
switch len(refParts) {
|
||||
case 2:
|
||||
return refParts[0], refParts[1], nil
|
||||
case 3:
|
||||
return refParts[1], refParts[2], nil
|
||||
}
|
||||
return "", "", fmt.Errorf("ref not in format volNamespace.volName or pvName.volNamespace.volName")
|
||||
}
|
||||
|
||||
// GetPath returns the path to the user specific mount of a StorageOS volume
|
||||
func (storageosVolume *storageos) GetPath() string {
|
||||
return getPath(storageosVolume.podUID, storageosVolume.volNamespace, storageosVolume.volName, storageosVolume.pvName, storageosVolume.plugin.host)
|
||||
}
|
||||
|
||||
type storageosUnmounter struct {
|
||||
*storageos
|
||||
}
|
||||
|
||||
var _ volume.Unmounter = &storageosUnmounter{}
|
||||
|
||||
func (b *storageosUnmounter) GetPath() string {
|
||||
return getPath(b.podUID, b.volNamespace, b.volName, b.pvName, b.plugin.host)
|
||||
}
|
||||
|
||||
// Unmounts the bind mount, and detaches the disk only if the PD
|
||||
// resource was the last reference to that disk on the kubelet.
|
||||
func (b *storageosUnmounter) TearDown() error {
|
||||
if len(b.volNamespace) == 0 || len(b.volName) == 0 {
|
||||
glog.Warningf("volNamespace: %q, volName: %q not set, skipping TearDown", b.volNamespace, b.volName)
|
||||
return fmt.Errorf("pvName not specified for TearDown, waiting for next sync loop")
|
||||
}
|
||||
// Unmount from pod
|
||||
mountPath := b.GetPath()
|
||||
|
||||
err := b.TearDownAt(mountPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Unmount from pod failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Find device name from global mount
|
||||
globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
|
||||
devicePath, _, err := mount.GetDeviceNameFromMount(b.mounter, globalPDPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Detach failed when getting device from global mount: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmount from plugin's disk global mount dir.
|
||||
err = b.TearDownAt(globalPDPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Detach failed during unmount: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Detach loop device
|
||||
err = b.manager.DetachVolume(b, devicePath)
|
||||
if err != nil {
|
||||
glog.Errorf("Detach device %s failed for volume %s: %v", devicePath, b.pvName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Successfully unmounted StorageOS volume %s and detached devices", b.pvName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmounts the bind mount, and detaches the disk only if the PD
|
||||
// resource was the last reference to that disk on the kubelet.
|
||||
func (b *storageosUnmounter) TearDownAt(dir string) error {
|
||||
if err := util.UnmountPath(dir, b.mounter); err != nil {
|
||||
glog.V(4).Infof("Unmounted StorageOS volume %s failed with: %v", b.pvName, err)
|
||||
}
|
||||
if err := b.manager.UnmountVolume(b); err != nil {
|
||||
glog.V(4).Infof("Mount reference for volume %s could not be removed from StorageOS: %v", b.pvName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type storageosDeleter struct {
|
||||
*storageosMounter
|
||||
pvUID types.UID
|
||||
}
|
||||
|
||||
var _ volume.Deleter = &storageosDeleter{}
|
||||
|
||||
func (d *storageosDeleter) GetPath() string {
|
||||
return getPath(d.podUID, d.volNamespace, d.volName, d.pvName, d.plugin.host)
|
||||
}
|
||||
|
||||
func (d *storageosDeleter) Delete() error {
|
||||
return d.manager.DeleteVolume(d)
|
||||
}
|
||||
|
||||
type storageosProvisioner struct {
|
||||
*storageosMounter
|
||||
options volume.VolumeOptions
|
||||
}
|
||||
|
||||
var _ volume.Provisioner = &storageosProvisioner{}
|
||||
|
||||
func (c *storageosProvisioner) Provision() (*v1.PersistentVolume, error) {
|
||||
|
||||
var adminSecretName, adminSecretNamespace string
|
||||
|
||||
// Apply ProvisionerParameters (case-insensitive). We leave validation of
|
||||
// the values to the cloud provider.
|
||||
for k, v := range c.options.Parameters {
|
||||
switch strings.ToLower(k) {
|
||||
case "adminsecretname":
|
||||
adminSecretName = v
|
||||
case "adminsecretnamespace":
|
||||
adminSecretNamespace = v
|
||||
case "volumenamespace":
|
||||
c.volNamespace = v
|
||||
case "description":
|
||||
c.description = v
|
||||
case "pool":
|
||||
c.pool = v
|
||||
case "fstype":
|
||||
c.fsType = v
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, c.plugin.GetPluginName())
|
||||
}
|
||||
}
|
||||
|
||||
// Set from PVC
|
||||
c.podNamespace = c.options.PVC.Namespace
|
||||
c.volName = c.options.PVName
|
||||
if c.volNamespace == "" {
|
||||
c.volNamespace = c.options.PVC.Namespace
|
||||
}
|
||||
c.labels = make(map[string]string)
|
||||
for k, v := range c.options.PVC.Labels {
|
||||
c.labels[k] = v
|
||||
}
|
||||
capacity := c.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
||||
c.sizeGB = int(volume.RoundUpSize(capacity.Value(), 1024*1024*1024))
|
||||
|
||||
apiCfg, err := parsePVSecret(adminSecretNamespace, adminSecretName, c.plugin.host.GetKubeClient())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.apiCfg = apiCfg
|
||||
|
||||
vol, err := c.manager.CreateVolume(c)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to create volume: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if vol.FSType == "" {
|
||||
vol.FSType = defaultFSType
|
||||
}
|
||||
|
||||
pv := &v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: vol.Name,
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{
|
||||
volumehelper.VolumeDynamicallyCreatedByKey: "storageos-dynamic-provisioner",
|
||||
},
|
||||
},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeReclaimPolicy: c.options.PersistentVolumeReclaimPolicy,
|
||||
AccessModes: c.options.PVC.Spec.AccessModes,
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", vol.SizeGB)),
|
||||
},
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
StorageOS: &v1.StorageOSPersistentVolumeSource{
|
||||
VolumeName: vol.Name,
|
||||
VolumeNamespace: vol.Namespace,
|
||||
FSType: vol.FSType,
|
||||
ReadOnly: false,
|
||||
SecretRef: &v1.ObjectReference{
|
||||
Name: adminSecretName,
|
||||
Namespace: adminSecretNamespace,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(c.options.PVC.Spec.AccessModes) == 0 {
|
||||
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
||||
}
|
||||
if len(vol.Labels) != 0 {
|
||||
if pv.Labels == nil {
|
||||
pv.Labels = make(map[string]string)
|
||||
}
|
||||
for k, v := range vol.Labels {
|
||||
pv.Labels[k] = v
|
||||
}
|
||||
}
|
||||
return pv, nil
|
||||
}
|
||||
|
||||
// Returns StorageOS volume name, namespace, fstype and readonly from spec
|
||||
func getVolumeInfoFromSpec(spec *volume.Spec) (string, string, string, bool, error) {
|
||||
if spec.PersistentVolume != nil {
|
||||
source, readOnly, err := getPersistentVolumeSource(spec)
|
||||
if err != nil {
|
||||
return "", "", "", false, err
|
||||
}
|
||||
return source.VolumeName, source.VolumeNamespace, source.FSType, readOnly, nil
|
||||
}
|
||||
|
||||
if spec.Volume != nil {
|
||||
source, readOnly, err := getVolumeSource(spec)
|
||||
if err != nil {
|
||||
return "", "", "", false, err
|
||||
}
|
||||
return source.VolumeName, source.VolumeNamespace, source.FSType, readOnly, nil
|
||||
}
|
||||
return "", "", "", false, fmt.Errorf("spec not Volume or PersistentVolume")
|
||||
}
|
||||
|
||||
// Returns API config if secret set, otherwise empty struct so defaults can be
|
||||
// attempted.
|
||||
func getAPICfg(spec *volume.Spec, pod *v1.Pod, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
|
||||
if spec.PersistentVolume != nil {
|
||||
source, _, err := getPersistentVolumeSource(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if source.SecretRef == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return parsePVSecret(source.SecretRef.Namespace, source.SecretRef.Name, kubeClient)
|
||||
}
|
||||
|
||||
if spec.Volume != nil {
|
||||
source, _, err := getVolumeSource(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if source.SecretRef == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return parsePodSecret(pod, source.SecretRef.Name, kubeClient)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("spec not Volume or PersistentVolume")
|
||||
}
|
||||
|
||||
func parsePodSecret(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
|
||||
secret, err := util.GetSecretForPod(pod, secretName, kubeClient)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
|
||||
return nil, fmt.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
|
||||
}
|
||||
return parseAPIConfig(secret)
|
||||
}
|
||||
|
||||
// Important: Only to be called with data from a PV to avoid secrets being
|
||||
// loaded from a user-suppler namespace.
|
||||
func parsePVSecret(namespace, secretName string, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
|
||||
secret, err := util.GetSecretForPV(namespace, secretName, storageosPluginName, kubeClient)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
|
||||
return nil, fmt.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
|
||||
}
|
||||
return parseAPIConfig(secret)
|
||||
}
|
||||
|
||||
// Parse API configuration from parameters or secret
|
||||
func parseAPIConfig(params map[string]string) (*storageosAPIConfig, error) {
|
||||
|
||||
if len(params) == 0 {
|
||||
return nil, fmt.Errorf("empty API config")
|
||||
}
|
||||
|
||||
c := &storageosAPIConfig{}
|
||||
|
||||
for name, data := range params {
|
||||
switch strings.ToLower(name) {
|
||||
case "apiaddress":
|
||||
c.apiAddr = string(data)
|
||||
case "apiusername":
|
||||
c.apiUser = string(data)
|
||||
case "apipassword":
|
||||
c.apiPass = string(data)
|
||||
case "apiversion":
|
||||
c.apiVersion = string(data)
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
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 storageos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
)
|
||||
|
||||
func TestCanSupport(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("storageos_test")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating 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/storageos")
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
if plug.GetPluginName() != "kubernetes.io/storageos" {
|
||||
t.Errorf("Wrong name: %s", plug.GetPluginName())
|
||||
}
|
||||
if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{StorageOS: &v1.StorageOSVolumeSource{}}}}) {
|
||||
t.Errorf("Expected true")
|
||||
}
|
||||
if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{StorageOS: &v1.StorageOSPersistentVolumeSource{}}}}}) {
|
||||
t.Errorf("Expected true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccessModes(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("storageos_test")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating 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/storageos")
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
if !contains(plug.GetAccessModes(), v1.ReadWriteOnce) || !contains(plug.GetAccessModes(), v1.ReadOnlyMany) {
|
||||
t.Errorf("Expected two AccessModeTypes: %s and %s", v1.ReadWriteOnce, v1.ReadOnlyMany)
|
||||
}
|
||||
}
|
||||
|
||||
type fakePDManager struct {
|
||||
api apiImplementer
|
||||
attachCalled bool
|
||||
detachCalled bool
|
||||
mountCalled bool
|
||||
unmountCalled bool
|
||||
createCalled bool
|
||||
deleteCalled bool
|
||||
}
|
||||
|
||||
func (fake *fakePDManager) NewAPI(apiCfg *storageosAPIConfig) error {
|
||||
fake.api = fakeAPI{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakePDManager) CreateVolume(p *storageosProvisioner) (*storageosVolume, error) {
|
||||
fake.createCalled = true
|
||||
labels := make(map[string]string)
|
||||
labels["fakepdmanager"] = "yes"
|
||||
return &storageosVolume{
|
||||
Name: "test-storageos-name",
|
||||
Namespace: "test-storageos-namespace",
|
||||
Pool: "test-storageos-pool",
|
||||
SizeGB: 100,
|
||||
Labels: labels,
|
||||
FSType: "ext2",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fake *fakePDManager) AttachVolume(b *storageosMounter) (string, error) {
|
||||
fake.attachCalled = true
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (fake *fakePDManager) DetachVolume(b *storageosUnmounter, loopDevice string) error {
|
||||
fake.detachCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakePDManager) MountVolume(b *storageosMounter, mntDevice, deviceMountPath string) error {
|
||||
fake.mountCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakePDManager) UnmountVolume(b *storageosUnmounter) error {
|
||||
fake.unmountCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakePDManager) DeleteVolume(d *storageosDeleter) error {
|
||||
fake.deleteCalled = true
|
||||
if d.volName != "test-storageos-name" {
|
||||
return fmt.Errorf("Deleter got unexpected volume name: %s", d.volName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("storageos_test")
|
||||
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/storageos")
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
secretName := "very-secret"
|
||||
spec := &v1.Volume{
|
||||
Name: "vol1-pvname",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
StorageOS: &v1.StorageOSVolumeSource{
|
||||
VolumeName: "vol1",
|
||||
VolumeNamespace: "ns1",
|
||||
FSType: "ext3",
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: secretName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
client := fake.NewSimpleClientset()
|
||||
|
||||
client.Core().Secrets("default").Create(&v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: "default",
|
||||
},
|
||||
Type: "kubernetes.io/storageos",
|
||||
Data: map[string][]byte{
|
||||
"apiUsername": []byte("storageos"),
|
||||
"apiPassword": []byte("storageos"),
|
||||
"apiAddr": []byte("tcp://localhost:5705"),
|
||||
}})
|
||||
|
||||
plug.(*storageosPlugin).host = volumetest.NewFakeVolumeHost(tmpDir, client, nil)
|
||||
|
||||
// Test Mounter
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid"), Namespace: "default"}}
|
||||
fakeManager := &fakePDManager{}
|
||||
|
||||
apiCfg, err := parsePodSecret(pod, secretName, plug.(*storageosPlugin).host.GetKubeClient())
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't get secret from %v/%v", pod.Namespace, secretName)
|
||||
}
|
||||
|
||||
mounter, err := plug.(*storageosPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), pod, apiCfg, fakeManager, &mount.FakeMounter{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Fatalf("Got a nil Mounter")
|
||||
}
|
||||
|
||||
expectedPath := path.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~storageos/vol1-pvname.ns1.vol1")
|
||||
volPath := mounter.GetPath()
|
||||
if volPath != expectedPath {
|
||||
t.Errorf("Expected path: '%s' got: '%s'", expectedPath, volPath)
|
||||
}
|
||||
|
||||
if err := mounter.SetUp(nil); err != nil {
|
||||
t.Errorf("Expected success, got: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(volPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, volume path not created: %s", volPath)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !fakeManager.attachCalled {
|
||||
t.Errorf("Attach not called")
|
||||
}
|
||||
if !fakeManager.mountCalled {
|
||||
t.Errorf("Mount not called")
|
||||
}
|
||||
|
||||
// Test Unmounter
|
||||
fakeManager = &fakePDManager{}
|
||||
unmounter, err := plug.(*storageosPlugin).newUnmounterInternal("vol1-pvname", types.UID("poduid"), fakeManager, &mount.FakeMounter{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Unmounter: %v", err)
|
||||
}
|
||||
if unmounter == nil {
|
||||
t.Errorf("Got a nil Unmounter")
|
||||
}
|
||||
|
||||
volPath = unmounter.GetPath()
|
||||
if volPath != expectedPath {
|
||||
t.Errorf("Expected path: '%s' got: '%s'", expectedPath, volPath)
|
||||
}
|
||||
|
||||
if err := unmounter.TearDown(); err != nil {
|
||||
t.Errorf("Expected success, got: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(volPath); err == nil {
|
||||
t.Errorf("TearDown() failed, volume path still exists: %s", volPath)
|
||||
} else if !os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
|
||||
if !fakeManager.unmountCalled {
|
||||
t.Errorf("Unmount not called")
|
||||
}
|
||||
if !fakeManager.detachCalled {
|
||||
t.Errorf("Detach not called")
|
||||
}
|
||||
|
||||
// Test Provisioner
|
||||
fakeManager = &fakePDManager{}
|
||||
options := volume.VolumeOptions{
|
||||
PVC: volumetest.CreateTestPVC("100Mi", []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}),
|
||||
// PVName: "test-volume-name",
|
||||
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
|
||||
Parameters: map[string]string{
|
||||
"VolumeNamespace": "test-volume-namespace",
|
||||
"adminSecretName": secretName,
|
||||
},
|
||||
}
|
||||
provisioner, err := plug.(*storageosPlugin).newProvisionerInternal(options, fakeManager)
|
||||
if err != nil {
|
||||
t.Errorf("newProvisionerInternal() failed: %v", err)
|
||||
}
|
||||
|
||||
persistentSpec, err := provisioner.Provision()
|
||||
if err != nil {
|
||||
t.Fatalf("Provision() failed: %v", err)
|
||||
}
|
||||
|
||||
if persistentSpec.Spec.PersistentVolumeSource.StorageOS.VolumeName != "test-storageos-name" {
|
||||
t.Errorf("Provision() returned unexpected volume Name: %s, expected test-storageos-name", persistentSpec.Spec.PersistentVolumeSource.StorageOS.VolumeName)
|
||||
}
|
||||
if persistentSpec.Spec.PersistentVolumeSource.StorageOS.VolumeNamespace != "test-storageos-namespace" {
|
||||
t.Errorf("Provision() returned unexpected volume Namespace: %s", persistentSpec.Spec.PersistentVolumeSource.StorageOS.VolumeNamespace)
|
||||
}
|
||||
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.Spec.PersistentVolumeSource.StorageOS.FSType != "ext2" {
|
||||
t.Errorf("Provision() returned unexpected volume FSType: %s", persistentSpec.Spec.PersistentVolumeSource.StorageOS.FSType)
|
||||
}
|
||||
if persistentSpec.Labels["fakepdmanager"] != "yes" {
|
||||
t.Errorf("Provision() returned unexpected labels: %v", persistentSpec.Labels)
|
||||
}
|
||||
if !fakeManager.createCalled {
|
||||
t.Errorf("Create not called")
|
||||
}
|
||||
|
||||
// Test Deleter
|
||||
fakeManager = &fakePDManager{}
|
||||
volSpec := &volume.Spec{
|
||||
PersistentVolume: persistentSpec,
|
||||
}
|
||||
deleter, err := plug.(*storageosPlugin).newDeleterInternal(volSpec, apiCfg, fakeManager)
|
||||
if err != nil {
|
||||
t.Errorf("newDeleterInternal() failed: %v", err)
|
||||
}
|
||||
|
||||
err = deleter.Delete()
|
||||
if err != nil {
|
||||
t.Errorf("Deleter() failed: %v", err)
|
||||
}
|
||||
if !fakeManager.deleteCalled {
|
||||
t.Errorf("Delete not called")
|
||||
}
|
||||
}
|
||||
|
||||
func contains(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool {
|
||||
for _, m := range modes {
|
||||
if m == mode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestPersistentClaimReadOnlyFlag(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("storageos_test")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
pv := &v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvA",
|
||||
},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
StorageOS: &v1.StorageOSPersistentVolumeSource{VolumeName: "pvA", VolumeNamespace: "vnsA", ReadOnly: false},
|
||||
},
|
||||
ClaimRef: &v1.ObjectReference{
|
||||
Name: "claimA",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
claim := &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "claimA",
|
||||
Namespace: "nsA",
|
||||
},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
VolumeName: "pvA",
|
||||
},
|
||||
Status: v1.PersistentVolumeClaimStatus{
|
||||
Phase: v1.ClaimBound,
|
||||
},
|
||||
}
|
||||
|
||||
client := fake.NewSimpleClientset(pv, claim)
|
||||
plugMgr := volume.VolumePluginMgr{}
|
||||
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil))
|
||||
plug, _ := plugMgr.FindPluginByName(storageosPluginName)
|
||||
|
||||
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
|
||||
spec := volume.NewSpecFromPersistentVolume(pv, true)
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "nsA", UID: types.UID("poduid")}}
|
||||
fakeManager := &fakePDManager{}
|
||||
fakeConfig := &fakeConfig{}
|
||||
apiCfg := fakeConfig.GetAPIConfig()
|
||||
mounter, err := plug.(*storageosPlugin).newMounterInternal(spec, pod, apiCfg, fakeManager, &mount.FakeMounter{})
|
||||
|
||||
if !mounter.GetAttributes().ReadOnly {
|
||||
t.Errorf("Expected true for mounter.IsReadOnly")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
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 storageos
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
|
||||
"github.com/golang/glog"
|
||||
storageosapi "github.com/storageos/go-api"
|
||||
storageostypes "github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
losetupPath = "losetup"
|
||||
|
||||
modeBlock deviceType = iota
|
||||
modeFile
|
||||
modeUnsupported
|
||||
|
||||
ErrDeviceNotFound = "device not found"
|
||||
ErrDeviceNotSupported = "device not supported"
|
||||
ErrNotAvailable = "not available"
|
||||
)
|
||||
|
||||
type deviceType int
|
||||
|
||||
// storageosVolume describes a provisioned volume
|
||||
type storageosVolume struct {
|
||||
ID string
|
||||
Name string
|
||||
Namespace string
|
||||
Description string
|
||||
Pool string
|
||||
SizeGB int
|
||||
Labels map[string]string
|
||||
FSType string
|
||||
}
|
||||
|
||||
type storageosAPIConfig struct {
|
||||
apiAddr string
|
||||
apiUser string
|
||||
apiPass string
|
||||
apiVersion string
|
||||
}
|
||||
|
||||
type apiImplementer interface {
|
||||
Volume(namespace string, ref string) (*storageostypes.Volume, error)
|
||||
VolumeCreate(opts storageostypes.VolumeCreateOptions) (*storageostypes.Volume, error)
|
||||
VolumeMount(opts storageostypes.VolumeMountOptions) error
|
||||
VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error
|
||||
VolumeDelete(opt storageostypes.DeleteOptions) error
|
||||
}
|
||||
|
||||
// storageosUtil is the utility structure to interact with the StorageOS API.
|
||||
type storageosUtil struct {
|
||||
api apiImplementer
|
||||
}
|
||||
|
||||
func (u *storageosUtil) NewAPI(apiCfg *storageosAPIConfig) error {
|
||||
if u.api != nil {
|
||||
return nil
|
||||
}
|
||||
if apiCfg == nil {
|
||||
apiCfg = &storageosAPIConfig{
|
||||
apiAddr: defaultAPIAddress,
|
||||
apiUser: defaultAPIUser,
|
||||
apiPass: defaultAPIPassword,
|
||||
apiVersion: defaultAPIVersion,
|
||||
}
|
||||
glog.V(4).Infof("Using default StorageOS API settings: addr %s, version: %s", apiCfg.apiAddr, defaultAPIVersion)
|
||||
}
|
||||
|
||||
api, err := storageosapi.NewVersionedClient(apiCfg.apiAddr, defaultAPIVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
api.SetAuth(apiCfg.apiUser, apiCfg.apiPass)
|
||||
u.api = api
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a new StorageOS volume and makes it available as a device within
|
||||
// /var/lib/storageos/volumes.
|
||||
func (u *storageosUtil) CreateVolume(p *storageosProvisioner) (*storageosVolume, error) {
|
||||
if err := u.NewAPI(p.apiCfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if p.labels == nil {
|
||||
p.labels = make(map[string]string)
|
||||
}
|
||||
opts := storageostypes.VolumeCreateOptions{
|
||||
Name: p.volName,
|
||||
Size: p.sizeGB,
|
||||
Description: p.description,
|
||||
Pool: p.pool,
|
||||
FSType: p.fsType,
|
||||
Namespace: p.volNamespace,
|
||||
Labels: p.labels,
|
||||
}
|
||||
|
||||
vol, err := u.api.VolumeCreate(opts)
|
||||
if err != nil {
|
||||
glog.Errorf("volume create failed for volume %q (%v)", opts.Name, err)
|
||||
return nil, err
|
||||
}
|
||||
return &storageosVolume{
|
||||
ID: vol.ID,
|
||||
Name: vol.Name,
|
||||
Namespace: vol.Namespace,
|
||||
Description: vol.Description,
|
||||
Pool: vol.Pool,
|
||||
FSType: vol.FSType,
|
||||
SizeGB: int(vol.Size),
|
||||
Labels: vol.Labels,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Attach exposes a volume on the host as a block device. StorageOS uses a
|
||||
// global namespace, so if the volume exists, it should already be available as
|
||||
// a device within `/var/lib/storageos/volumes/<id>`.
|
||||
//
|
||||
// Depending on the host capabilities, the device may be either a block device
|
||||
// or a file device. Block devices can be used directly, but file devices must
|
||||
// be made accessible as a block device before using.
|
||||
func (u *storageosUtil) AttachVolume(b *storageosMounter) (string, error) {
|
||||
if err := u.NewAPI(b.apiCfg); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
vol, err := u.api.Volume(b.volNamespace, b.volName)
|
||||
if err != nil {
|
||||
glog.Warningf("volume retrieve failed for volume %q with namespace %q (%v)", b.volName, b.volNamespace, err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Clear any existing mount reference from the API. These may be leftover
|
||||
// from previous mounts where the unmount operation couldn't get access to
|
||||
// the API credentials.
|
||||
if vol.Mounted {
|
||||
opts := storageostypes.VolumeUnmountOptions{
|
||||
Name: vol.Name,
|
||||
Namespace: vol.Namespace,
|
||||
}
|
||||
if err := u.api.VolumeUnmount(opts); err != nil {
|
||||
glog.Warningf("Couldn't clear existing StorageOS mount reference: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
srcPath := path.Join(b.devicePath, vol.ID)
|
||||
dt, err := pathDeviceType(srcPath)
|
||||
if err != nil {
|
||||
glog.Warningf("volume source path %q for volume %q not ready (%v)", srcPath, b.volName, err)
|
||||
return "", err
|
||||
}
|
||||
switch dt {
|
||||
case modeBlock:
|
||||
return srcPath, nil
|
||||
case modeFile:
|
||||
return attachFileDevice(srcPath)
|
||||
default:
|
||||
return "", fmt.Errorf(ErrDeviceNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
// Detach detaches a volume from the host. This is only needed when NBD is not
|
||||
// enabled and loop devices are used to simulate a block device.
|
||||
func (u *storageosUtil) DetachVolume(b *storageosUnmounter, devicePath string) error {
|
||||
if !isLoopDevice(devicePath) {
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(devicePath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return removeLoopDevice(devicePath)
|
||||
}
|
||||
|
||||
// Mount mounts the volume on the host.
|
||||
func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountPath string) error {
|
||||
notMnt, err := b.mounter.IsLikelyNotMountPoint(deviceMountPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(deviceMountPath, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
notMnt = true
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = os.MkdirAll(deviceMountPath, 0750); err != nil {
|
||||
glog.Errorf("mkdir failed on disk %s (%v)", deviceMountPath, err)
|
||||
return err
|
||||
}
|
||||
options := []string{}
|
||||
if b.readOnly {
|
||||
options = append(options, "ro")
|
||||
}
|
||||
if notMnt {
|
||||
err = b.diskMounter.FormatAndMount(mntDevice, deviceMountPath, b.fsType, options)
|
||||
if err != nil {
|
||||
os.Remove(deviceMountPath)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := u.NewAPI(b.apiCfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := storageostypes.VolumeMountOptions{
|
||||
Name: b.volName,
|
||||
Namespace: b.volNamespace,
|
||||
FsType: b.fsType,
|
||||
Mountpoint: deviceMountPath,
|
||||
Client: b.plugin.host.GetHostName(),
|
||||
}
|
||||
return u.api.VolumeMount(opts)
|
||||
}
|
||||
|
||||
// Unmount removes the mount reference from the volume allowing it to be
|
||||
// re-mounted elsewhere.
|
||||
func (u *storageosUtil) UnmountVolume(b *storageosUnmounter) error {
|
||||
if err := u.NewAPI(b.apiCfg); err != nil {
|
||||
// We can't always get the config we need, so allow the unmount to
|
||||
// succeed even if we can't remove the mount reference from the API.
|
||||
glog.V(4).Infof("Could not remove mount reference in the StorageOS API as no credentials available to the unmount operation")
|
||||
return nil
|
||||
}
|
||||
|
||||
opts := storageostypes.VolumeUnmountOptions{
|
||||
Name: b.volName,
|
||||
Namespace: b.volNamespace,
|
||||
Client: b.plugin.host.GetHostName(),
|
||||
}
|
||||
return u.api.VolumeUnmount(opts)
|
||||
}
|
||||
|
||||
// Deletes a StorageOS volume. Assumes it has already been unmounted and detached.
|
||||
func (u *storageosUtil) DeleteVolume(d *storageosDeleter) error {
|
||||
if err := u.NewAPI(d.apiCfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deletes must be forced as the StorageOS API will not normally delete
|
||||
// volumes that it thinks are mounted. We can't be sure the unmount was
|
||||
// registered via the API so we trust k8s to only delete volumes it knows
|
||||
// are unmounted.
|
||||
opts := storageostypes.DeleteOptions{
|
||||
Name: d.volName,
|
||||
Namespace: d.volNamespace,
|
||||
Force: true,
|
||||
}
|
||||
return u.api.VolumeDelete(opts)
|
||||
}
|
||||
|
||||
// pathMode returns the FileMode for a path.
|
||||
func pathDeviceType(path string) (deviceType, error) {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return modeUnsupported, err
|
||||
}
|
||||
switch mode := fi.Mode(); {
|
||||
case mode&os.ModeDevice != 0:
|
||||
return modeBlock, nil
|
||||
case mode.IsRegular():
|
||||
return modeFile, nil
|
||||
default:
|
||||
return modeUnsupported, nil
|
||||
}
|
||||
}
|
||||
|
||||
// attachFileDevice takes a path to a regular file and makes it available as an
|
||||
// attached block device.
|
||||
func attachFileDevice(path string) (string, error) {
|
||||
blockDevicePath, err := getLoopDevice(path)
|
||||
if err != nil && err.Error() != ErrDeviceNotFound {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If no existing loop device for the path, create one
|
||||
if blockDevicePath == "" {
|
||||
glog.V(4).Infof("Creating device for path: %s", path)
|
||||
blockDevicePath, err = makeLoopDevice(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return blockDevicePath, nil
|
||||
}
|
||||
|
||||
// Returns the full path to the loop device associated with the given path.
|
||||
func getLoopDevice(path string) (string, error) {
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
return "", errors.New(ErrNotAvailable)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("not attachable: %v", err)
|
||||
}
|
||||
|
||||
exec := exec.New()
|
||||
args := []string{"-j", path}
|
||||
out, err := exec.Command(losetupPath, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed device discover command for path %s: %v", path, err)
|
||||
return "", err
|
||||
}
|
||||
return parseLosetupOutputForDevice(out)
|
||||
}
|
||||
|
||||
func makeLoopDevice(path string) (string, error) {
|
||||
exec := exec.New()
|
||||
args := []string{"-f", "--show", path}
|
||||
out, err := exec.Command(losetupPath, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed device create command for path %s: %v", path, err)
|
||||
return "", err
|
||||
}
|
||||
return parseLosetupOutputForDevice(out)
|
||||
}
|
||||
|
||||
func removeLoopDevice(device string) error {
|
||||
exec := exec.New()
|
||||
args := []string{"-d", device}
|
||||
out, err := exec.Command(losetupPath, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
if !strings.Contains(string(out), "No such device or address") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLoopDevice(device string) bool {
|
||||
return strings.HasPrefix(device, "/dev/loop")
|
||||
}
|
||||
|
||||
func parseLosetupOutputForDevice(output []byte) (string, error) {
|
||||
if len(output) == 0 {
|
||||
return "", errors.New(ErrDeviceNotFound)
|
||||
}
|
||||
|
||||
// losetup returns device in the format:
|
||||
// /dev/loop1: [0073]:148662 (/var/lib/storageos/volumes/308f14af-cf0a-08ff-c9c3-b48104318e05)
|
||||
device := strings.TrimSpace(strings.SplitN(string(output), ":", 2)[0])
|
||||
if len(device) == 0 {
|
||||
return "", errors.New(ErrDeviceNotFound)
|
||||
}
|
||||
return device, nil
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
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 storageos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
storageostypes "github.com/storageos/go-api/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"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testApiSecretName = "storageos-api"
|
||||
var testVolName = "storageos-test-vol"
|
||||
var testPVName = "storageos-test-pv"
|
||||
var testNamespace = "storageos-test-namespace"
|
||||
var testSize = 1
|
||||
var testDesc = "testdescription"
|
||||
var testPool = "testpool"
|
||||
var testFSType = "ext2"
|
||||
var testVolUUID = "01c43d34-89f8-83d3-422b-43536a0f25e6"
|
||||
|
||||
type fakeConfig struct {
|
||||
apiAddr string
|
||||
apiUser string
|
||||
apiPass string
|
||||
apiVersion string
|
||||
}
|
||||
|
||||
func (c fakeConfig) GetAPIConfig() *storageosAPIConfig {
|
||||
return &storageosAPIConfig{
|
||||
apiAddr: "http://5.6.7.8:9999",
|
||||
apiUser: "abc",
|
||||
apiPass: "123",
|
||||
apiVersion: "10",
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
util := storageosUtil{}
|
||||
cfg := fakeConfig{}
|
||||
err := util.NewAPI(cfg.GetAPIConfig())
|
||||
if err != nil {
|
||||
t.Fatalf("error getting api config: %v", err)
|
||||
}
|
||||
if util.api == nil {
|
||||
t.Errorf("client() unexpectedly returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
type fakeAPI struct{}
|
||||
|
||||
func (f fakeAPI) Volume(namespace string, ref string) (*storageostypes.Volume, error) {
|
||||
if namespace == testNamespace && ref == testVolName {
|
||||
return &storageostypes.Volume{
|
||||
ID: "01c43d34-89f8-83d3-422b-43536a0f25e6",
|
||||
Name: ref,
|
||||
Pool: "default",
|
||||
Namespace: namespace,
|
||||
Size: 5,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
func (f fakeAPI) VolumeCreate(opts storageostypes.VolumeCreateOptions) (*storageostypes.Volume, error) {
|
||||
|
||||
// Append a label from the api
|
||||
labels := opts.Labels
|
||||
labels["labelfromapi"] = "apilabel"
|
||||
|
||||
return &storageostypes.Volume{
|
||||
ID: testVolUUID,
|
||||
Name: opts.Name,
|
||||
Namespace: opts.Namespace,
|
||||
Description: opts.Description,
|
||||
Pool: opts.Pool,
|
||||
Size: opts.Size,
|
||||
FSType: opts.FSType,
|
||||
Labels: labels,
|
||||
}, nil
|
||||
}
|
||||
func (f fakeAPI) VolumeMount(opts storageostypes.VolumeMountOptions) error {
|
||||
return nil
|
||||
}
|
||||
func (f fakeAPI) VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error {
|
||||
return nil
|
||||
}
|
||||
func (f fakeAPI) VolumeDelete(opts storageostypes.DeleteOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCreateVolume(t *testing.T) {
|
||||
|
||||
tmpDir, err := utiltesting.MkTmpdir("storageos_test")
|
||||
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, _ := plugMgr.FindPluginByName("kubernetes.io/storageos")
|
||||
|
||||
// Use real util with stubbed api
|
||||
util := &storageosUtil{}
|
||||
util.api = fakeAPI{}
|
||||
|
||||
labels := map[string]string{
|
||||
"labelA": "valueA",
|
||||
"labelB": "valueB",
|
||||
}
|
||||
|
||||
options := volume.VolumeOptions{
|
||||
PVName: testPVName,
|
||||
PVC: volumetest.CreateTestPVC(fmt.Sprintf("%dGi", testSize), []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}),
|
||||
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
|
||||
provisioner := &storageosProvisioner{
|
||||
storageosMounter: &storageosMounter{
|
||||
storageos: &storageos{
|
||||
pvName: testPVName,
|
||||
volName: testVolName,
|
||||
volNamespace: testNamespace,
|
||||
sizeGB: testSize,
|
||||
pool: testPool,
|
||||
description: testDesc,
|
||||
fsType: testFSType,
|
||||
labels: labels,
|
||||
manager: util,
|
||||
plugin: plug.(*storageosPlugin),
|
||||
},
|
||||
},
|
||||
options: options,
|
||||
}
|
||||
|
||||
vol, err := util.CreateVolume(provisioner)
|
||||
if err != nil {
|
||||
t.Errorf("CreateVolume() returned error: %v", err)
|
||||
}
|
||||
if vol == nil {
|
||||
t.Fatalf("CreateVolume() vol is empty")
|
||||
}
|
||||
if vol.ID == "" {
|
||||
t.Error("CreateVolume() vol ID is empty")
|
||||
}
|
||||
if vol.Name != testVolName {
|
||||
t.Errorf("CreateVolume() returned unexpected Name %s", vol.Name)
|
||||
}
|
||||
if vol.Namespace != testNamespace {
|
||||
t.Errorf("CreateVolume() returned unexpected Namespace %s", vol.Namespace)
|
||||
}
|
||||
if vol.Pool != testPool {
|
||||
t.Errorf("CreateVolume() returned unexpected Pool %s", vol.Pool)
|
||||
}
|
||||
if vol.FSType != testFSType {
|
||||
t.Errorf("CreateVolume() returned unexpected FSType %s", vol.FSType)
|
||||
}
|
||||
if vol.SizeGB != testSize {
|
||||
t.Errorf("CreateVolume() returned unexpected Size %d", vol.SizeGB)
|
||||
}
|
||||
if len(vol.Labels) == 0 {
|
||||
t.Error("CreateVolume() Labels are empty")
|
||||
} else {
|
||||
for k, v := range labels {
|
||||
var val string
|
||||
var ok bool
|
||||
if val, ok = vol.Labels[k]; !ok {
|
||||
t.Errorf("CreateVolume() Label %s not set", k)
|
||||
}
|
||||
if val != v {
|
||||
t.Errorf("CreateVolume() returned unexpected Label value %s", val)
|
||||
}
|
||||
}
|
||||
var val string
|
||||
var ok bool
|
||||
if val, ok = vol.Labels["labelfromapi"]; !ok {
|
||||
t.Error("CreateVolume() Label from api not set")
|
||||
}
|
||||
if val != "apilabel" {
|
||||
t.Errorf("CreateVolume() returned unexpected Label value %s", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttachVolume(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("storageos_test")
|
||||
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, _ := plugMgr.FindPluginByName("kubernetes.io/storageos")
|
||||
|
||||
// Use real util with stubbed api
|
||||
util := &storageosUtil{}
|
||||
util.api = fakeAPI{}
|
||||
|
||||
mounter := &storageosMounter{
|
||||
storageos: &storageos{
|
||||
volName: testVolName,
|
||||
volNamespace: testNamespace,
|
||||
manager: util,
|
||||
mounter: &mount.FakeMounter{},
|
||||
plugin: plug.(*storageosPlugin),
|
||||
},
|
||||
devicePath: tmpDir,
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Errorf("Got a nil Mounter")
|
||||
}
|
||||
}
|
|
@ -251,9 +251,9 @@ func (g *Graph) AddPV(pv *api.PersistentVolume) {
|
|||
|
||||
// since we don't know the other end of the pvc -> pod -> node chain (or it may not even exist yet), we can't decorate these edges with kubernetes node info
|
||||
g.graph.SetEdge(simple.Edge{F: pvVertex, T: g.getOrCreateVertex_locked(pvcVertexType, pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name)})
|
||||
pvutil.VisitPVSecretNames(pv, func(secret string) bool {
|
||||
pvutil.VisitPVSecretNames(pv, func(namespace, secret string) bool {
|
||||
// This grants access to the named secret in the same namespace as the bound PVC
|
||||
g.graph.SetEdge(simple.Edge{F: g.getOrCreateVertex_locked(secretVertexType, pv.Spec.ClaimRef.Namespace, secret), T: pvVertex})
|
||||
g.graph.SetEdge(simple.Edge{F: g.getOrCreateVertex_locked(secretVertexType, namespace, secret), T: pvVertex})
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
|
|
@ -313,6 +313,9 @@ type VolumeSource struct {
|
|||
// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
ScaleIO *ScaleIOVolumeSource
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// +optional
|
||||
StorageOS *StorageOSVolumeSource
|
||||
}
|
||||
|
||||
// Similar to VolumeSource but meant for the administrator who creates PVs.
|
||||
|
@ -384,6 +387,10 @@ type PersistentVolumeSource struct {
|
|||
// Local represents directly-attached storage with node affinity
|
||||
// +optional
|
||||
Local *LocalVolumeSource
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
||||
// +optional
|
||||
StorageOS *StorageOSPersistentVolumeSource
|
||||
}
|
||||
|
||||
type PersistentVolumeClaimVolumeSource struct {
|
||||
|
@ -1149,6 +1156,62 @@ type ScaleIOVolumeSource struct {
|
|||
ReadOnly bool
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
ReadOnly bool
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *LocalObjectReference
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSPersistentVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
ReadOnly bool
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *ObjectReference
|
||||
}
|
||||
|
||||
// Adapts a ConfigMap into a volume.
|
||||
//
|
||||
// The contents of the target ConfigMap's Data field will be presented in a
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2222,6 +2222,11 @@ message PersistentVolumeSource {
|
|||
// Local represents directly-attached storage with node affinity
|
||||
// +optional
|
||||
optional LocalVolumeSource local = 20;
|
||||
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
||||
// +optional
|
||||
optional StorageOSPersistentVolumeSource storageos = 21;
|
||||
}
|
||||
|
||||
// PersistentVolumeSpec is the specification of a persistent volume.
|
||||
|
@ -3760,6 +3765,70 @@ message ServiceStatus {
|
|||
optional LoadBalancerStatus loadBalancer = 1;
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
message StorageOSPersistentVolumeSource {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
optional string volumeName = 1;
|
||||
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
optional string volumeNamespace = 2;
|
||||
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
optional string fsType = 3;
|
||||
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
optional bool readOnly = 4;
|
||||
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
optional ObjectReference secretRef = 5;
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
message StorageOSVolumeSource {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
optional string volumeName = 1;
|
||||
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
optional string volumeNamespace = 2;
|
||||
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
optional string fsType = 3;
|
||||
|
||||
// Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
optional bool readOnly = 4;
|
||||
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
optional LocalObjectReference secretRef = 5;
|
||||
}
|
||||
|
||||
// Sysctl defines a kernel parameter to be set
|
||||
message Sysctl {
|
||||
// Name of a property to set
|
||||
|
@ -4011,6 +4080,10 @@ message VolumeSource {
|
|||
// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
optional ScaleIOVolumeSource scaleIO = 25;
|
||||
|
||||
// StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
optional StorageOSVolumeSource storageos = 27;
|
||||
}
|
||||
|
||||
// Represents a vSphere volume resource.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -348,6 +348,9 @@ type VolumeSource struct {
|
|||
// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
ScaleIO *ScaleIOVolumeSource `json:"scaleIO,omitempty" protobuf:"bytes,25,opt,name=scaleIO"`
|
||||
// StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
|
||||
// +optional
|
||||
StorageOS *StorageOSVolumeSource `json:"storageos,omitempty" protobuf:"bytes,27,opt,name=storageos"`
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
|
||||
|
@ -442,6 +445,10 @@ type PersistentVolumeSource struct {
|
|||
// Local represents directly-attached storage with node affinity
|
||||
// +optional
|
||||
Local *LocalVolumeSource `json:"local,omitempty" protobuf:"bytes,20,opt,name=local"`
|
||||
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
|
||||
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
|
||||
// +optional
|
||||
StorageOS *StorageOSPersistentVolumeSource `json:"storageos,omitempty" protobuf:"bytes,21,opt,name=storageos"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1228,6 +1235,62 @@ type ScaleIOVolumeSource struct {
|
|||
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,10,opt,name=readOnly"`
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string `json:"volumeName,omitempty" protobuf:"bytes,1,opt,name=volumeName"`
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string `json:"volumeNamespace,omitempty" protobuf:"bytes,2,opt,name=volumeNamespace"`
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string `json:"fsType,omitempty" protobuf:"bytes,3,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,4,opt,name=readOnly"`
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *LocalObjectReference `json:"secretRef,omitempty" protobuf:"bytes,5,opt,name=secretRef"`
|
||||
}
|
||||
|
||||
// Represents a StorageOS persistent volume resource.
|
||||
type StorageOSPersistentVolumeSource struct {
|
||||
// VolumeName is the human-readable name of the StorageOS volume. Volume
|
||||
// names are only unique within a namespace.
|
||||
VolumeName string `json:"volumeName,omitempty" protobuf:"bytes,1,opt,name=volumeName"`
|
||||
// VolumeNamespace specifies the scope of the volume within StorageOS. If no
|
||||
// namespace is specified then the Pod's namespace will be used. This allows the
|
||||
// Kubernetes name scoping to be mirrored within StorageOS for tighter integration.
|
||||
// Set VolumeName to any name to override the default behaviour.
|
||||
// Set to "default" if you are not using namespaces within StorageOS.
|
||||
// Namespaces that do not pre-exist within StorageOS will be created.
|
||||
// +optional
|
||||
VolumeNamespace string `json:"volumeNamespace,omitempty" protobuf:"bytes,2,opt,name=volumeNamespace"`
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// +optional
|
||||
FSType string `json:"fsType,omitempty" protobuf:"bytes,3,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,4,opt,name=readOnly"`
|
||||
// SecretRef specifies the secret to use for obtaining the StorageOS API
|
||||
// credentials. If not specified, default values will be attempted.
|
||||
// +optional
|
||||
SecretRef *ObjectReference `json:"secretRef,omitempty" protobuf:"bytes,5,opt,name=secretRef"`
|
||||
}
|
||||
|
||||
// Adapts a ConfigMap into a volume.
|
||||
//
|
||||
// The contents of the target ConfigMap's Data field will be presented in a
|
||||
|
|
|
@ -1161,6 +1161,7 @@ var map_PersistentVolumeSource = map[string]string{
|
|||
"portworxVolume": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
|
||||
"scaleIO": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.",
|
||||
"local": "Local represents directly-attached storage with node affinity",
|
||||
"storageos": "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md",
|
||||
}
|
||||
|
||||
func (PersistentVolumeSource) SwaggerDoc() map[string]string {
|
||||
|
@ -1875,6 +1876,32 @@ func (ServiceStatus) SwaggerDoc() map[string]string {
|
|||
return map_ServiceStatus
|
||||
}
|
||||
|
||||
var map_StorageOSPersistentVolumeSource = map[string]string{
|
||||
"": "Represents a StorageOS persistent volume resource.",
|
||||
"volumeName": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
|
||||
"volumeNamespace": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
|
||||
"fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
||||
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
|
||||
"secretRef": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.",
|
||||
}
|
||||
|
||||
func (StorageOSPersistentVolumeSource) SwaggerDoc() map[string]string {
|
||||
return map_StorageOSPersistentVolumeSource
|
||||
}
|
||||
|
||||
var map_StorageOSVolumeSource = map[string]string{
|
||||
"": "Represents a StorageOS persistent volume resource.",
|
||||
"volumeName": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
|
||||
"volumeNamespace": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
|
||||
"fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
|
||||
"readOnly": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
|
||||
"secretRef": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.",
|
||||
}
|
||||
|
||||
func (StorageOSVolumeSource) SwaggerDoc() map[string]string {
|
||||
return map_StorageOSVolumeSource
|
||||
}
|
||||
|
||||
var map_Sysctl = map[string]string{
|
||||
"": "Sysctl defines a kernel parameter to be set",
|
||||
"Name": "Name of a property to set",
|
||||
|
@ -1980,6 +2007,7 @@ var map_VolumeSource = map[string]string{
|
|||
"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",
|
||||
"scaleIO": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.",
|
||||
"storageos": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.",
|
||||
}
|
||||
|
||||
func (VolumeSource) SwaggerDoc() map[string]string {
|
||||
|
|
|
@ -355,6 +355,10 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||
Convert_api_ServiceSpec_To_v1_ServiceSpec,
|
||||
Convert_v1_ServiceStatus_To_api_ServiceStatus,
|
||||
Convert_api_ServiceStatus_To_v1_ServiceStatus,
|
||||
Convert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource,
|
||||
Convert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource,
|
||||
Convert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource,
|
||||
Convert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource,
|
||||
Convert_v1_Sysctl_To_api_Sysctl,
|
||||
Convert_api_Sysctl_To_v1_Sysctl,
|
||||
Convert_v1_TCPSocketAction_To_api_TCPSocketAction,
|
||||
|
@ -3027,6 +3031,7 @@ func autoConvert_v1_PersistentVolumeSource_To_api_PersistentVolumeSource(in *Per
|
|||
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*api.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.Local = (*api.LocalVolumeSource)(unsafe.Pointer(in.Local))
|
||||
out.StorageOS = (*api.StorageOSPersistentVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -3056,6 +3061,7 @@ func autoConvert_api_PersistentVolumeSource_To_v1_PersistentVolumeSource(in *api
|
|||
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.Local = (*LocalVolumeSource)(unsafe.Pointer(in.Local))
|
||||
out.StorageOS = (*StorageOSPersistentVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -4807,6 +4813,62 @@ func Convert_api_ServiceStatus_To_v1_ServiceStatus(in *api.ServiceStatus, out *S
|
|||
return autoConvert_api_ServiceStatus_To_v1_ServiceStatus(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource(in *StorageOSPersistentVolumeSource, out *api.StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*api.ObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource is an autogenerated conversion function.
|
||||
func Convert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource(in *StorageOSPersistentVolumeSource, out *api.StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_v1_StorageOSPersistentVolumeSource_To_api_StorageOSPersistentVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource(in *api.StorageOSPersistentVolumeSource, out *StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*ObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource is an autogenerated conversion function.
|
||||
func Convert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource(in *api.StorageOSPersistentVolumeSource, out *StorageOSPersistentVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_api_StorageOSPersistentVolumeSource_To_v1_StorageOSPersistentVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource(in *StorageOSVolumeSource, out *api.StorageOSVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*api.LocalObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource is an autogenerated conversion function.
|
||||
func Convert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource(in *StorageOSVolumeSource, out *api.StorageOSVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_v1_StorageOSVolumeSource_To_api_StorageOSVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource(in *api.StorageOSVolumeSource, out *StorageOSVolumeSource, s conversion.Scope) error {
|
||||
out.VolumeName = in.VolumeName
|
||||
out.VolumeNamespace = in.VolumeNamespace
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.SecretRef = (*LocalObjectReference)(unsafe.Pointer(in.SecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource is an autogenerated conversion function.
|
||||
func Convert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource(in *api.StorageOSVolumeSource, out *StorageOSVolumeSource, s conversion.Scope) error {
|
||||
return autoConvert_api_StorageOSVolumeSource_To_v1_StorageOSVolumeSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_Sysctl_To_api_Sysctl(in *Sysctl, out *api.Sysctl, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Value = in.Value
|
||||
|
@ -5008,6 +5070,7 @@ func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.
|
|||
out.Projected = (*api.ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
|
||||
out.PortworxVolume = (*api.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*api.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.StorageOS = (*api.StorageOSVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -5043,6 +5106,7 @@ func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *
|
|||
out.Projected = (*ProjectedVolumeSource)(unsafe.Pointer(in.Projected))
|
||||
out.PortworxVolume = (*PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
|
||||
out.ScaleIO = (*ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
|
||||
out.StorageOS = (*StorageOSVolumeSource)(unsafe.Pointer(in.StorageOS))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -195,6 +195,8 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
|||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ServiceProxyOptions, InType: reflect.TypeOf(&ServiceProxyOptions{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ServiceSpec, InType: reflect.TypeOf(&ServiceSpec{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ServiceStatus, InType: reflect.TypeOf(&ServiceStatus{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_StorageOSPersistentVolumeSource, InType: reflect.TypeOf(&StorageOSPersistentVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_StorageOSVolumeSource, InType: reflect.TypeOf(&StorageOSVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Sysctl, InType: reflect.TypeOf(&Sysctl{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_TCPSocketAction, InType: reflect.TypeOf(&TCPSocketAction{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Taint, InType: reflect.TypeOf(&Taint{})},
|
||||
|
@ -2182,6 +2184,13 @@ func DeepCopy_v1_PersistentVolumeSource(in interface{}, out interface{}, c *conv
|
|||
*out = new(LocalVolumeSource)
|
||||
**out = **in
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSPersistentVolumeSource)
|
||||
if err := DeepCopy_v1_StorageOSPersistentVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -3436,6 +3445,36 @@ func DeepCopy_v1_ServiceStatus(in interface{}, out interface{}, c *conversion.Cl
|
|||
}
|
||||
}
|
||||
|
||||
// DeepCopy_v1_StorageOSPersistentVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1_StorageOSPersistentVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSPersistentVolumeSource)
|
||||
out := out.(*StorageOSPersistentVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(ObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_v1_StorageOSVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1_StorageOSVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSVolumeSource)
|
||||
out := out.(*StorageOSVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_v1_Sysctl is an autogenerated deepcopy function.
|
||||
func DeepCopy_v1_Sysctl(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
|
@ -3696,6 +3735,13 @@ func DeepCopy_v1_VolumeSource(in interface{}, out interface{}, c *conversion.Clo
|
|||
return err
|
||||
}
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSVolumeSource)
|
||||
if err := DeepCopy_v1_StorageOSVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,6 +197,8 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
|||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ServiceProxyOptions, InType: reflect.TypeOf(&ServiceProxyOptions{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ServiceSpec, InType: reflect.TypeOf(&ServiceSpec{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ServiceStatus, InType: reflect.TypeOf(&ServiceStatus{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_StorageOSPersistentVolumeSource, InType: reflect.TypeOf(&StorageOSPersistentVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_StorageOSVolumeSource, InType: reflect.TypeOf(&StorageOSVolumeSource{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Sysctl, InType: reflect.TypeOf(&Sysctl{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_TCPSocketAction, InType: reflect.TypeOf(&TCPSocketAction{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Taint, InType: reflect.TypeOf(&Taint{})},
|
||||
|
@ -2200,6 +2202,13 @@ func DeepCopy_api_PersistentVolumeSource(in interface{}, out interface{}, c *con
|
|||
*out = new(LocalVolumeSource)
|
||||
**out = **in
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSPersistentVolumeSource)
|
||||
if err := DeepCopy_api_StorageOSPersistentVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -3442,6 +3451,36 @@ func DeepCopy_api_ServiceStatus(in interface{}, out interface{}, c *conversion.C
|
|||
}
|
||||
}
|
||||
|
||||
// DeepCopy_api_StorageOSPersistentVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_StorageOSPersistentVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSPersistentVolumeSource)
|
||||
out := out.(*StorageOSPersistentVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(ObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_api_StorageOSVolumeSource is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_StorageOSVolumeSource(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
in := in.(*StorageOSVolumeSource)
|
||||
out := out.(*StorageOSVolumeSource)
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy_api_Sysctl is an autogenerated deepcopy function.
|
||||
func DeepCopy_api_Sysctl(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
{
|
||||
|
@ -3702,6 +3741,13 @@ func DeepCopy_api_VolumeSource(in interface{}, out interface{}, c *conversion.Cl
|
|||
return err
|
||||
}
|
||||
}
|
||||
if in.StorageOS != nil {
|
||||
in, out := &in.StorageOS, &out.StorageOS
|
||||
*out = new(StorageOSVolumeSource)
|
||||
if err := DeepCopy_api_StorageOSVolumeSource(*in, *out, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -945,6 +945,7 @@ var (
|
|||
Quobyte FSType = "quobyte"
|
||||
AzureDisk FSType = "azureDisk"
|
||||
PhotonPersistentDisk FSType = "photonPersistentDisk"
|
||||
StorageOS FSType = "storageos"
|
||||
Projected FSType = "projected"
|
||||
PortworxVolume FSType = "portworxVolume"
|
||||
ScaleIO FSType = "scaleIO"
|
||||
|
|
|
@ -301,6 +301,7 @@ filegroup(
|
|||
"//vendor/github.com/spf13/pflag:all-srcs",
|
||||
"//vendor/github.com/spf13/viper:all-srcs",
|
||||
"//vendor/github.com/square/go-jose:all-srcs",
|
||||
"//vendor/github.com/storageos/go-api:all-srcs",
|
||||
"//vendor/github.com/stretchr/objx:all-srcs",
|
||||
"//vendor/github.com/stretchr/testify/assert:all-srcs",
|
||||
"//vendor/github.com/stretchr/testify/mock:all-srcs",
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
|
@ -0,0 +1,47 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"client.go",
|
||||
"client_unix.go",
|
||||
"controller.go",
|
||||
"event.go",
|
||||
"namespace.go",
|
||||
"pool.go",
|
||||
"rule.go",
|
||||
"server_version.go",
|
||||
"template.go",
|
||||
"util.go",
|
||||
"validation.go",
|
||||
"volume.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/github.com/gorilla/websocket:go_default_library",
|
||||
"//vendor/github.com/storageos/go-api/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//vendor/github.com/storageos/go-api/types:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -0,0 +1,45 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2015-2017 StorageOS
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Copyright (c) 2013-2017, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,2 @@
|
|||
# StorageOS API client library
|
||||
|
|
@ -0,0 +1,620 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
userAgent = "go-storageosclient"
|
||||
unixProtocol = "unix"
|
||||
namedPipeProtocol = "npipe"
|
||||
DefaultVersionStr = "1"
|
||||
DefaultVersion = 1
|
||||
defaultNamespace = "default"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidEndpoint is returned when the endpoint is not a valid HTTP URL.
|
||||
ErrInvalidEndpoint = errors.New("invalid endpoint")
|
||||
|
||||
// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
|
||||
ErrConnectionRefused = errors.New("cannot connect to StorageOS API endpoint")
|
||||
|
||||
// ErrInactivityTimeout is returned when a streamable call has been inactive for some time.
|
||||
ErrInactivityTimeout = errors.New("inactivity time exceeded timeout")
|
||||
|
||||
// ErrInvalidVersion is returned when a versioned client was requested but no version specified.
|
||||
ErrInvalidVersion = errors.New("invalid version")
|
||||
|
||||
// DefaultHost is the default API host
|
||||
DefaultHost = "tcp://localhost:5705"
|
||||
)
|
||||
|
||||
// APIVersion is an internal representation of a version of the Remote API.
|
||||
type APIVersion int
|
||||
|
||||
// NewAPIVersion returns an instance of APIVersion for the given string.
|
||||
//
|
||||
// The given string must be in the form <major>
|
||||
func NewAPIVersion(input string) (APIVersion, error) {
|
||||
if input == "" {
|
||||
return DefaultVersion, ErrInvalidVersion
|
||||
}
|
||||
version, err := strconv.Atoi(input)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Unable to parse version %q", input)
|
||||
}
|
||||
return APIVersion(version), nil
|
||||
}
|
||||
|
||||
func (version APIVersion) String() string {
|
||||
return fmt.Sprintf("v%d", version)
|
||||
}
|
||||
|
||||
// Client is the basic type of this package. It provides methods for
|
||||
// interaction with the API.
|
||||
type Client struct {
|
||||
SkipServerVersionCheck bool
|
||||
HTTPClient *http.Client
|
||||
TLSConfig *tls.Config
|
||||
Dialer Dialer
|
||||
endpoint string
|
||||
endpointURL *url.URL
|
||||
username string
|
||||
secret string
|
||||
requestedAPIVersion APIVersion
|
||||
serverAPIVersion APIVersion
|
||||
expectedAPIVersion APIVersion
|
||||
nativeHTTPClient *http.Client
|
||||
}
|
||||
|
||||
// ClientVersion returns the API version of the client
|
||||
func (c *Client) ClientVersion() string {
|
||||
return DefaultVersionStr
|
||||
}
|
||||
|
||||
// Dialer is an interface that allows network connections to be dialed
|
||||
// (net.Dialer fulfills this interface) and named pipes (a shim using
|
||||
// winio.DialPipe)
|
||||
type Dialer interface {
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// NewClient returns a Client instance ready for communication with the given
|
||||
// server endpoint. It will use the latest remote API version available in the
|
||||
// server.
|
||||
func NewClient(endpoint string) (*Client, error) {
|
||||
client, err := NewVersionedClient(endpoint, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.SkipServerVersionCheck = true
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewTLSClient returns a Client instance ready for TLS communications with the given
|
||||
// server endpoint, key and certificates . It will use the latest remote API version
|
||||
// available in the server.
|
||||
func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
|
||||
client, err := NewVersionedTLSClient(endpoint, cert, key, ca, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.SkipServerVersionCheck = true
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewVersionedClient returns a Client instance ready for communication with
|
||||
// the given server endpoint, using a specific remote API version.
|
||||
func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
|
||||
u, err := parseEndpoint(endpoint, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Client{
|
||||
HTTPClient: defaultClient(),
|
||||
Dialer: &net.Dialer{},
|
||||
endpoint: endpoint,
|
||||
endpointURL: u,
|
||||
}
|
||||
|
||||
if apiVersionString != "" {
|
||||
version, err := strconv.Atoi(apiVersionString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.requestedAPIVersion = APIVersion(version)
|
||||
}
|
||||
|
||||
c.initializeNativeClient()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewVersionedTLSClient returns a Client instance ready for TLS communications with the givens
|
||||
// server endpoint, key and certificates, using a specific remote API version.
|
||||
func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
|
||||
var certPEMBlock []byte
|
||||
var keyPEMBlock []byte
|
||||
var caPEMCert []byte
|
||||
if _, err := os.Stat(cert); !os.IsNotExist(err) {
|
||||
certPEMBlock, err = ioutil.ReadFile(cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(key); !os.IsNotExist(err) {
|
||||
keyPEMBlock, err = ioutil.ReadFile(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(ca); !os.IsNotExist(err) {
|
||||
caPEMCert, err = ioutil.ReadFile(ca)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, apiVersionString)
|
||||
}
|
||||
|
||||
// NewVersionedTLSClientFromBytes returns a Client instance ready for TLS communications with the givens
|
||||
// server endpoint, key and certificates (passed inline to the function as opposed to being
|
||||
// read from a local file), using a specific remote API version.
|
||||
func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte, apiVersionString string) (*Client, error) {
|
||||
u, err := parseEndpoint(endpoint, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
if certPEMBlock != nil && keyPEMBlock != nil {
|
||||
tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{tlsCert}
|
||||
}
|
||||
if caPEMCert == nil {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
} else {
|
||||
caPool := x509.NewCertPool()
|
||||
if !caPool.AppendCertsFromPEM(caPEMCert) {
|
||||
return nil, errors.New("Could not add RootCA pem")
|
||||
}
|
||||
tlsConfig.RootCAs = caPool
|
||||
}
|
||||
tr := defaultTransport()
|
||||
tr.TLSClientConfig = tlsConfig
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &Client{
|
||||
HTTPClient: &http.Client{Transport: tr},
|
||||
TLSConfig: tlsConfig,
|
||||
Dialer: &net.Dialer{},
|
||||
endpoint: endpoint,
|
||||
endpointURL: u,
|
||||
}
|
||||
|
||||
if apiVersionString != "" {
|
||||
version, err := strconv.Atoi(apiVersionString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.requestedAPIVersion = APIVersion(version)
|
||||
}
|
||||
|
||||
c.initializeNativeClient()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// SetAuth sets the API username and secret to be used for all API requests.
|
||||
// It should not be called concurrently with any other Client methods.
|
||||
func (c *Client) SetAuth(username string, secret string) {
|
||||
if username != "" {
|
||||
c.username = username
|
||||
}
|
||||
if secret != "" {
|
||||
c.secret = secret
|
||||
}
|
||||
}
|
||||
|
||||
// SetTimeout takes a timeout and applies it to both the HTTPClient and
|
||||
// nativeHTTPClient. It should not be called concurrently with any other Client
|
||||
// methods.
|
||||
func (c *Client) SetTimeout(t time.Duration) {
|
||||
if c.HTTPClient != nil {
|
||||
c.HTTPClient.Timeout = t
|
||||
}
|
||||
if c.nativeHTTPClient != nil {
|
||||
c.nativeHTTPClient.Timeout = t
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) checkAPIVersion() error {
|
||||
serverAPIVersionString, err := c.getServerAPIVersionString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.serverAPIVersion, err = NewAPIVersion(serverAPIVersionString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.requestedAPIVersion == 0 {
|
||||
c.expectedAPIVersion = c.serverAPIVersion
|
||||
} else {
|
||||
c.expectedAPIVersion = c.requestedAPIVersion
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Endpoint returns the current endpoint. It's useful for getting the endpoint
|
||||
// when using functions that get this data from the environment (like
|
||||
// NewClientFromEnv.
|
||||
func (c *Client) Endpoint() string {
|
||||
return c.endpoint
|
||||
}
|
||||
|
||||
// Ping pings the API server
|
||||
//
|
||||
// See https://goo.gl/wYfgY1 for more details.
|
||||
func (c *Client) Ping() error {
|
||||
urlpath := "/_ping"
|
||||
resp, err := c.do("GET", urlpath, doOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return newError(resp)
|
||||
}
|
||||
resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) getServerAPIVersionString() (version string, err error) {
|
||||
v, err := c.ServerVersion(context.Background())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return v.APIVersion, nil
|
||||
}
|
||||
|
||||
type doOptions struct {
|
||||
data interface{}
|
||||
fieldSelector string
|
||||
labelSelector string
|
||||
namespace string
|
||||
forceJSON bool
|
||||
force bool
|
||||
values url.Values
|
||||
headers map[string]string
|
||||
unversioned bool
|
||||
context context.Context
|
||||
}
|
||||
|
||||
func (c *Client) do(method, urlpath string, doOptions doOptions) (*http.Response, error) {
|
||||
var params io.Reader
|
||||
if doOptions.data != nil || doOptions.forceJSON {
|
||||
buf, err := json.Marshal(doOptions.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = bytes.NewBuffer(buf)
|
||||
}
|
||||
|
||||
// Prefix the path with the namespace if given. The caller should only set
|
||||
// the namespace if this is desired.
|
||||
if doOptions.namespace != "" {
|
||||
urlpath = "/" + NamespaceAPIPrefix + "/" + doOptions.namespace + "/" + urlpath
|
||||
}
|
||||
|
||||
if !c.SkipServerVersionCheck && !doOptions.unversioned {
|
||||
err := c.checkAPIVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
if doOptions.values != nil {
|
||||
query = doOptions.values
|
||||
}
|
||||
if doOptions.force {
|
||||
query.Add("force", "1")
|
||||
}
|
||||
|
||||
httpClient := c.HTTPClient
|
||||
protocol := c.endpointURL.Scheme
|
||||
var u string
|
||||
switch protocol {
|
||||
case unixProtocol, namedPipeProtocol:
|
||||
httpClient = c.nativeHTTPClient
|
||||
u = c.getFakeNativeURL(urlpath, doOptions.unversioned)
|
||||
default:
|
||||
u = c.getAPIPath(urlpath, query, doOptions.unversioned)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, u, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
if doOptions.data != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else if method == "POST" {
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
}
|
||||
if c.username != "" && c.secret != "" {
|
||||
req.SetBasicAuth(c.username, c.secret)
|
||||
}
|
||||
|
||||
for k, v := range doOptions.headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
ctx := doOptions.context
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, ErrConnectionRefused
|
||||
}
|
||||
return nil, chooseError(ctx, err)
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
||||
return nil, newError(resp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// if error in context, return that instead of generic http error
|
||||
func chooseError(ctx context.Context, err error) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) getURL(path string, unversioned bool) string {
|
||||
|
||||
urlStr := strings.TrimRight(c.endpointURL.String(), "/")
|
||||
path = strings.TrimLeft(path, "/")
|
||||
if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol {
|
||||
urlStr = ""
|
||||
}
|
||||
if unversioned {
|
||||
return fmt.Sprintf("%s/%s", urlStr, path)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s/%s", urlStr, c.requestedAPIVersion, path)
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) getAPIPath(path string, query url.Values, unversioned bool) string {
|
||||
var apiPath string
|
||||
urlStr := strings.TrimRight(c.endpointURL.String(), "/")
|
||||
path = strings.TrimLeft(path, "/")
|
||||
if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol {
|
||||
urlStr = ""
|
||||
}
|
||||
if unversioned {
|
||||
apiPath = fmt.Sprintf("%s/%s", urlStr, path)
|
||||
} else {
|
||||
apiPath = fmt.Sprintf("%s/%s/%s", urlStr, c.requestedAPIVersion, path)
|
||||
}
|
||||
|
||||
if len(query) > 0 {
|
||||
apiPath = apiPath + "?" + query.Encode()
|
||||
}
|
||||
|
||||
return apiPath
|
||||
}
|
||||
|
||||
// getFakeNativeURL returns the URL needed to make an HTTP request over a UNIX
|
||||
// domain socket to the given path.
|
||||
func (c *Client) getFakeNativeURL(path string, unversioned bool) string {
|
||||
u := *c.endpointURL // Copy.
|
||||
|
||||
// Override URL so that net/http will not complain.
|
||||
u.Scheme = "http"
|
||||
u.Host = "unix.sock" // Doesn't matter what this is - it's not used.
|
||||
u.Path = ""
|
||||
urlStr := strings.TrimRight(u.String(), "/")
|
||||
path = strings.TrimLeft(path, "/")
|
||||
if unversioned {
|
||||
return fmt.Sprintf("%s/%s", urlStr, path)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s/%s", urlStr, c.requestedAPIVersion, path)
|
||||
}
|
||||
|
||||
type jsonMessage struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Progress string `json:"progress,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Stream string `json:"stream,omitempty"`
|
||||
}
|
||||
|
||||
func queryString(opts interface{}) string {
|
||||
if opts == nil {
|
||||
return ""
|
||||
}
|
||||
value := reflect.ValueOf(opts)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
if value.Kind() != reflect.Struct {
|
||||
return ""
|
||||
}
|
||||
items := url.Values(map[string][]string{})
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
field := value.Type().Field(i)
|
||||
if field.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
key := field.Tag.Get("qs")
|
||||
if key == "" {
|
||||
key = strings.ToLower(field.Name)
|
||||
} else if key == "-" {
|
||||
continue
|
||||
}
|
||||
addQueryStringValue(items, key, value.Field(i))
|
||||
}
|
||||
return items.Encode()
|
||||
}
|
||||
|
||||
func addQueryStringValue(items url.Values, key string, v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
items.Add(key, "1")
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if v.Int() > 0 {
|
||||
items.Add(key, strconv.FormatInt(v.Int(), 10))
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if v.Float() > 0 {
|
||||
items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
|
||||
}
|
||||
case reflect.String:
|
||||
if v.String() != "" {
|
||||
items.Add(key, v.String())
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if !v.IsNil() {
|
||||
if b, err := json.Marshal(v.Interface()); err == nil {
|
||||
items.Add(key, string(b))
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
if len(v.MapKeys()) > 0 {
|
||||
if b, err := json.Marshal(v.Interface()); err == nil {
|
||||
items.Add(key, string(b))
|
||||
}
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
vLen := v.Len()
|
||||
if vLen > 0 {
|
||||
for i := 0; i < vLen; i++ {
|
||||
addQueryStringValue(items, key, v.Index(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error represents failures in the API. It represents a failure from the API.
|
||||
type Error struct {
|
||||
Status int
|
||||
Message string
|
||||
}
|
||||
|
||||
func newError(resp *http.Response) *Error {
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return &Error{Status: resp.StatusCode, Message: fmt.Sprintf("cannot read body, err: %v", err)}
|
||||
}
|
||||
return &Error{Status: resp.StatusCode, Message: string(data)}
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
|
||||
}
|
||||
|
||||
func parseEndpoint(endpoint string, tls bool) (*url.URL, error) {
|
||||
if endpoint != "" && !strings.Contains(endpoint, "://") {
|
||||
endpoint = "tcp://" + endpoint
|
||||
}
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidEndpoint
|
||||
}
|
||||
if tls && u.Scheme != "unix" {
|
||||
u.Scheme = "https"
|
||||
}
|
||||
switch u.Scheme {
|
||||
case unixProtocol, namedPipeProtocol:
|
||||
return u, nil
|
||||
case "http", "https", "tcp":
|
||||
_, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
if e, ok := err.(*net.AddrError); ok {
|
||||
if e.Err == "missing port in address" {
|
||||
return u, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrInvalidEndpoint
|
||||
}
|
||||
number, err := strconv.ParseInt(port, 10, 64)
|
||||
if err == nil && number > 0 && number < 65536 {
|
||||
if u.Scheme == "tcp" {
|
||||
if tls {
|
||||
u.Scheme = "https"
|
||||
} else {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
return nil, ErrInvalidEndpoint
|
||||
default:
|
||||
return nil, ErrInvalidEndpoint
|
||||
}
|
||||
}
|
||||
|
||||
// defaultTransport returns a new http.Transport with the same default values
|
||||
// as http.DefaultTransport, but with idle connections and keepalives disabled.
|
||||
func defaultTransport() *http.Transport {
|
||||
transport := defaultPooledTransport()
|
||||
transport.DisableKeepAlives = true
|
||||
transport.MaxIdleConnsPerHost = -1
|
||||
return transport
|
||||
}
|
||||
|
||||
// defaultPooledTransport returns a new http.Transport with similar default
|
||||
// values to http.DefaultTransport. Do not use this for transient transports as
|
||||
// it can leak file descriptors over time. Only use this for transports that
|
||||
// will be re-used for the same host(s).
|
||||
func defaultPooledTransport() *http.Transport {
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
DisableKeepAlives: false,
|
||||
MaxIdleConnsPerHost: 1,
|
||||
}
|
||||
return transport
|
||||
}
|
||||
|
||||
// defaultClient returns a new http.Client with similar default values to
|
||||
// http.Client, but with a non-shared Transport, idle connections disabled, and
|
||||
// keepalives disabled.
|
||||
func defaultClient() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: defaultTransport(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// +build !windows
|
||||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// initializeNativeClient initializes the native Unix domain socket client on
|
||||
// Unix-style operating systems
|
||||
func (c *Client) initializeNativeClient() {
|
||||
if c.endpointURL.Scheme != unixProtocol {
|
||||
return
|
||||
}
|
||||
socketPath := c.endpointURL.Path
|
||||
tr := defaultTransport()
|
||||
tr.Dial = func(network, addr string) (net.Conn, error) {
|
||||
return c.Dialer.Dial(unixProtocol, socketPath)
|
||||
}
|
||||
c.nativeHTTPClient = &http.Client{Transport: tr}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// +build windows
|
||||
// Copyright 2016 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
)
|
||||
|
||||
const namedPipeConnectTimeout = 2 * time.Second
|
||||
|
||||
type pipeDialer struct {
|
||||
dialFunc func(network, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
func (p pipeDialer) Dial(network, address string) (net.Conn, error) {
|
||||
return p.dialFunc(network, address)
|
||||
}
|
||||
|
||||
// initializeNativeClient initializes the native Named Pipe client for Windows
|
||||
func (c *Client) initializeNativeClient() {
|
||||
if c.endpointURL.Scheme != namedPipeProtocol {
|
||||
return
|
||||
}
|
||||
namedPipePath := c.endpointURL.Path
|
||||
dialFunc := func(network, addr string) (net.Conn, error) {
|
||||
timeout := namedPipeConnectTimeout
|
||||
return winio.DialPipe(namedPipePath, &timeout)
|
||||
}
|
||||
tr := defaultTransport()
|
||||
tr.Dial = dialFunc
|
||||
c.Dialer = &pipeDialer{dialFunc}
|
||||
c.nativeHTTPClient = &http.Client{Transport: tr}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// ControllerAPIPrefix is a partial path to the HTTP endpoint.
|
||||
ControllerAPIPrefix = "controllers"
|
||||
|
||||
// ErrNoSuchController is the error returned when the controller does not exist.
|
||||
ErrNoSuchController = errors.New("no such controller")
|
||||
|
||||
// ErrControllerInUse is the error returned when the controller requested to be removed is still in use.
|
||||
ErrControllerInUse = errors.New("controller in use and cannot be removed")
|
||||
)
|
||||
|
||||
// ControllerList returns the list of available controllers.
|
||||
func (c *Client) ControllerList(opts types.ListOptions) ([]*types.Controller, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", ControllerAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var controllers []*types.Controller
|
||||
if err := json.NewDecoder(resp.Body).Decode(&controllers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return controllers, nil
|
||||
}
|
||||
|
||||
// Controller returns a controller by its reference.
|
||||
func (c *Client) Controller(ref string) (*types.Controller, error) {
|
||||
|
||||
resp, err := c.do("GET", ControllerAPIPrefix+"/"+ref, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchController
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var controller types.Controller
|
||||
if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controller, nil
|
||||
}
|
||||
|
||||
// ControllerUpdate updates a controller on the server.
|
||||
func (c *Client) ControllerUpdate(opts types.ControllerUpdateOptions) (*types.Controller, error) {
|
||||
ref := opts.Name
|
||||
if IsUUID(opts.ID) {
|
||||
ref = opts.ID
|
||||
}
|
||||
resp, err := c.do("PUT", ControllerAPIPrefix+"/"+ref, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var controller types.Controller
|
||||
if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controller, nil
|
||||
}
|
||||
|
||||
// ControllerDelete removes a controller by its reference.
|
||||
func (c *Client) ControllerDelete(opts types.DeleteOptions) error {
|
||||
deleteOpts := doOptions{
|
||||
namespace: opts.Namespace,
|
||||
force: opts.Force,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("DELETE", ControllerAPIPrefix+"/"+opts.Name, deleteOpts)
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchController
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrControllerInUse
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// EventAPIPrefix is a partial path to the HTTP endpoint.
|
||||
EventAPIPrefix = "event"
|
||||
|
||||
// ErrNoSuchEvent is the error returned when the event does not exist.
|
||||
ErrNoSuchEvent = errors.New("no such event")
|
||||
)
|
||||
|
||||
// EventList returns the list of available events.
|
||||
func (c *Client) EventList(opts types.ListOptions) ([]*types.Event, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("GET", EventAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var events []*types.Event
|
||||
if err := json.NewDecoder(resp.Body).Decode(&events); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
// Events returns a stream of events in the daemon. It's up to the caller to close the stream
|
||||
// by cancelling the context. Once the stream has been completely read an io.EOF error will
|
||||
// be sent over the error channel. If an error is sent all processing will be stopped. It's up
|
||||
// to the caller to reopen the stream in the event of an error by reinvoking this method.
|
||||
func (c *Client) Events(ctx context.Context, opts types.ListOptions) (<-chan types.Request, <-chan error) {
|
||||
|
||||
// listOpts := doOptions{
|
||||
// fieldSelector: opts.FieldSelector,
|
||||
// labelSelector: opts.LabelSelector,
|
||||
// context: ctx,
|
||||
// }
|
||||
|
||||
messages := make(chan types.Request)
|
||||
errs := make(chan error, 1)
|
||||
|
||||
// started := make(chan struct{})
|
||||
ws, _, err := websocket.DefaultDialer.Dial("ws://10.245.103.2:8000/v1/ws/event", nil)
|
||||
if err != nil {
|
||||
// close(started)
|
||||
// errs <- err
|
||||
log.Fatal(err)
|
||||
}
|
||||
// defer ws.Close()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer ws.Close()
|
||||
defer close(done)
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println("read:", err)
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
// log.Printf("recv: %s", message)
|
||||
var request types.Request
|
||||
if err := json.Unmarshal(message, &request); err != nil {
|
||||
log.Printf("decode error: %s", message)
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
messages <- request
|
||||
}
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case t := <-ticker.C:
|
||||
log.Printf("tick: %s\n", t.String())
|
||||
err := ws.WriteMessage(websocket.TextMessage, []byte(t.String()))
|
||||
if err != nil {
|
||||
log.Println("write:", err)
|
||||
return
|
||||
}
|
||||
case <-ctx.Done():
|
||||
log.Println("done")
|
||||
err := ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
if err != nil {
|
||||
log.Println("write close:", err)
|
||||
return
|
||||
}
|
||||
errs <- ctx.Err()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
ws.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// go func() {
|
||||
// defer ws.Close()
|
||||
// defer close(errs)
|
||||
//
|
||||
// // query, err := buildEventsQueryParams(cli.version, options)
|
||||
// // if err != nil {
|
||||
// // close(started)
|
||||
// // errs <- err
|
||||
// // return
|
||||
// // }
|
||||
//
|
||||
// // resp, err := cli.get(ctx, "/events", query, nil)
|
||||
//
|
||||
// // decoder := json.NewDecoder(resp.Body)
|
||||
//
|
||||
// close(started)
|
||||
// for {
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// log.Println("done")
|
||||
// errs <- ctx.Err()
|
||||
// return
|
||||
// default:
|
||||
// log.Println("default")
|
||||
// _, message, err := ws.ReadMessage()
|
||||
// if err != nil {
|
||||
// log.Println("read:", err)
|
||||
// return
|
||||
// }
|
||||
// log.Printf("recv: %s", message)
|
||||
// var event types.Event
|
||||
// if err := json.Unmarshal(message, &event); err != nil {
|
||||
// log.Printf("decode error: %s", message)
|
||||
// errs <- err
|
||||
// return
|
||||
// }
|
||||
// log.Printf("sent: %v", event)
|
||||
// messages <- event
|
||||
//
|
||||
// // select {
|
||||
// // case messages <- event:
|
||||
// // case <-ctx.Done():
|
||||
// // errs <- ctx.Err()
|
||||
// // return
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// <-started
|
||||
log.Println("returning")
|
||||
return messages, errs
|
||||
}
|
||||
|
||||
// Event returns a event by its reference.
|
||||
func (c *Client) Event(ref string) (*types.Event, error) {
|
||||
resp, err := c.do("GET", EventAPIPrefix+"/"+ref, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchEvent
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var event types.Event
|
||||
if err := json.NewDecoder(resp.Body).Decode(&event); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &event, nil
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// NamespaceAPIPrefix is a partial path to the HTTP endpoint.
|
||||
NamespaceAPIPrefix = "namespaces"
|
||||
|
||||
// ErrNoSuchNamespace is the error returned when the namespace does not exist.
|
||||
ErrNoSuchNamespace = errors.New("no such namespace")
|
||||
|
||||
// ErrNamespaceInUse is the error returned when the namespace requested to be removed is still in use.
|
||||
ErrNamespaceInUse = errors.New("namespace in use and cannot be removed")
|
||||
)
|
||||
|
||||
// NamespaceList returns the list of available namespaces.
|
||||
func (c *Client) NamespaceList(opts types.ListOptions) ([]*types.Namespace, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", NamespaceAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var namespaces []*types.Namespace
|
||||
if err := json.NewDecoder(resp.Body).Decode(&namespaces); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return namespaces, nil
|
||||
}
|
||||
|
||||
// Namespace returns a namespace by its reference.
|
||||
func (c *Client) Namespace(ref string) (*types.Namespace, error) {
|
||||
resp, err := c.do("GET", NamespaceAPIPrefix+"/"+ref, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchNamespace
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var namespace types.Namespace
|
||||
if err := json.NewDecoder(resp.Body).Decode(&namespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &namespace, nil
|
||||
}
|
||||
|
||||
// NamespaceCreate creates a namespace on the server and returns the new object.
|
||||
func (c *Client) NamespaceCreate(opts types.NamespaceCreateOptions) (*types.Namespace, error) {
|
||||
resp, err := c.do("POST", NamespaceAPIPrefix, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var namespace types.Namespace
|
||||
if err := json.NewDecoder(resp.Body).Decode(&namespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &namespace, nil
|
||||
}
|
||||
|
||||
// NamespaceUpdate updates a namespace on the server and returns the updated object.
|
||||
func (c *Client) NamespaceUpdate(opts types.NamespaceCreateOptions) (*types.Namespace, error) {
|
||||
resp, err := c.do("PUT", NamespaceAPIPrefix+"/"+opts.Name, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var namespace types.Namespace
|
||||
if err := json.NewDecoder(resp.Body).Decode(&namespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &namespace, nil
|
||||
}
|
||||
|
||||
// NamespaceDelete removes a namespace by its reference.
|
||||
func (c *Client) NamespaceDelete(opts types.DeleteOptions) error {
|
||||
deleteOpts := doOptions{
|
||||
force: opts.Force,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("DELETE", NamespaceAPIPrefix+"/"+opts.Name, deleteOpts)
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchNamespace
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrNamespaceInUse
|
||||
}
|
||||
|
||||
// namespace can't be deleted yet, unless force is supplied
|
||||
if e.Status == http.StatusPreconditionFailed {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// PoolAPIPrefix is a partial path to the HTTP endpoint.
|
||||
PoolAPIPrefix = "pools"
|
||||
|
||||
// ErrNoSuchPool is the error returned when the pool does not exist.
|
||||
ErrNoSuchPool = errors.New("no such pool")
|
||||
|
||||
// ErrPoolInUse is the error returned when the pool requested to be removed is still in use.
|
||||
ErrPoolInUse = errors.New("pool in use and cannot be removed")
|
||||
)
|
||||
|
||||
// PoolList returns the list of available pools.
|
||||
func (c *Client) PoolList(opts types.ListOptions) ([]*types.Pool, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("GET", PoolAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var pools []*types.Pool
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pools); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
// PoolCreate creates a pool on the server and returns the new object.
|
||||
func (c *Client) PoolCreate(opts types.PoolCreateOptions) (*types.Pool, error) {
|
||||
resp, err := c.do("POST", PoolAPIPrefix, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pool types.Pool
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pool); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pool, nil
|
||||
}
|
||||
|
||||
// Pool returns a pool by its reference.
|
||||
func (c *Client) Pool(ref string) (*types.Pool, error) {
|
||||
resp, err := c.do("GET", PoolAPIPrefix+"/"+ref, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchPool
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var pool types.Pool
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pool); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pool, nil
|
||||
}
|
||||
|
||||
// PoolDelete removes a pool by its reference.
|
||||
func (c *Client) PoolDelete(opts types.DeleteOptions) error {
|
||||
deleteOpts := doOptions{
|
||||
force: opts.Force,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("DELETE", PoolAPIPrefix+"/"+opts.Name, deleteOpts)
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchPool
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrPoolInUse
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// RuleAPIPrefix is a partial path to the HTTP endpoint.
|
||||
RuleAPIPrefix = "rules"
|
||||
|
||||
// ErrNoSuchRule is the error returned when the rule does not exist.
|
||||
ErrNoSuchRule = errors.New("no such rule")
|
||||
|
||||
// ErrRuleInUse is the error returned when the rule requested to be removed is still in use.
|
||||
ErrRuleInUse = errors.New("rule in use and cannot be removed")
|
||||
)
|
||||
|
||||
// RuleList returns the list of available rules.
|
||||
func (c *Client) RuleList(opts types.ListOptions) ([]*types.Rule, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", RuleAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var rules []*types.Rule
|
||||
if err := json.NewDecoder(resp.Body).Decode(&rules); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
// Rule returns a rule by its reference.
|
||||
func (c *Client) Rule(namespace string, ref string) (*types.Rule, error) {
|
||||
path, err := namespacedRefPath(namespace, RuleAPIPrefix, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchRule
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var rule types.Rule
|
||||
if err := json.NewDecoder(resp.Body).Decode(&rule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rule, nil
|
||||
}
|
||||
|
||||
// RuleCreate creates a rule on the server and returns the new object.
|
||||
func (c *Client) RuleCreate(opts types.RuleCreateOptions) (*types.Rule, error) {
|
||||
resp, err := c.do("POST", RuleAPIPrefix, doOptions{
|
||||
data: opts,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rule types.Rule
|
||||
if err := json.NewDecoder(resp.Body).Decode(&rule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rule, nil
|
||||
}
|
||||
|
||||
// RuleUpdate updates a rule on the server.
|
||||
func (c *Client) RuleUpdate(opts types.RuleUpdateOptions) (*types.Rule, error) {
|
||||
ref := opts.Name
|
||||
if IsUUID(opts.ID) {
|
||||
ref = opts.ID
|
||||
}
|
||||
fmt.Printf("%#v\n", opts)
|
||||
path, err := namespacedRefPath(opts.Namespace, RuleAPIPrefix, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := c.do("PUT", path, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var rule types.Rule
|
||||
if err := json.NewDecoder(resp.Body).Decode(&rule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rule, nil
|
||||
}
|
||||
|
||||
// RuleDelete removes a rule by its reference.
|
||||
func (c *Client) RuleDelete(opts types.DeleteOptions) error {
|
||||
deleteOpts := doOptions{
|
||||
namespace: opts.Namespace,
|
||||
force: opts.Force,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("DELETE", RuleAPIPrefix+"/"+opts.Name, deleteOpts)
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchRule
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrRuleInUse
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
// ServerVersion returns the server's version and runtime info.
|
||||
func (c *Client) ServerVersion(ctx context.Context) (*types.VersionInfo, error) {
|
||||
|
||||
// Send as unversioned
|
||||
resp, err := c.do("GET", "version", doOptions{context: ctx, unversioned: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, newError(resp)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var version types.VersionInfo
|
||||
if err := json.NewDecoder(resp.Body).Decode(&version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &version, nil
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
layout:
|
||||
models:
|
||||
- name: definition
|
||||
source: asset:model
|
||||
target: "{{ joinFilePath .Target .ModelPackage }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
||||
operations:
|
||||
- name: handler
|
||||
source: asset:serverOperation
|
||||
target: "{{ joinFilePath .Target .APIPackage .Package }}"
|
||||
file_name: "{{ (snakize (pascalize .Name)) }}.go"
|
|
@ -0,0 +1,854 @@
|
|||
|
||||
# A Swagger 2.0 (a.k.a. OpenAPI) definition of the StorageOS API.
|
||||
#
|
||||
# This is used for generating API documentation and the types used by the
|
||||
# client/server. See api/README.md for more information.
|
||||
#
|
||||
# Some style notes:
|
||||
# - This file is used by ReDoc, which allows GitHub Flavored Markdown in
|
||||
# descriptions.
|
||||
# - There is no maximum line length, for ease of editing and pretty diffs.
|
||||
# - operationIds are in the format "NounVerb", with a singular noun.
|
||||
|
||||
swagger: "2.0"
|
||||
schemes:
|
||||
- "http"
|
||||
- "https"
|
||||
produces:
|
||||
- "application/json"
|
||||
- "text/plain"
|
||||
consumes:
|
||||
- "application/json"
|
||||
- "text/plain"
|
||||
basePath: "/v1"
|
||||
info:
|
||||
title: "StorageOS API"
|
||||
version: "0.7"
|
||||
x-logo:
|
||||
url: "http://storageos.wpengine.com/wp-content/uploads/2017/03/cropped-logo-1.png"
|
||||
description: |
|
||||
The StorageOS API is an HTTP API used for managing volumes and StorageOS services. It is the API that the StorageOS UI, CLI and platform integrations use to communicate with the StorageOS backend.
|
||||
|
||||
# Errors
|
||||
|
||||
The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format:
|
||||
```
|
||||
{
|
||||
"message": "page not found"
|
||||
}
|
||||
```
|
||||
# The tags on paths define the menu sections in the ReDoc documentation, so
|
||||
# the usage of tags must make sense for that:
|
||||
# - They should be singular, not plural.
|
||||
# - There should not be too many tags, or the menu becomes unwieldy. For
|
||||
# example, it is preferable to add a path to the "System" tag instead of
|
||||
# creating a tag with a single path in it.
|
||||
# - The order of tags in this list defines the order in the menu.
|
||||
tags:
|
||||
# Primary objects
|
||||
- name: "Volume"
|
||||
x-displayName: "Volumes"
|
||||
description: |
|
||||
Create and manage volumes.
|
||||
- name: "Pool"
|
||||
x-displayName: "Pools"
|
||||
description: |
|
||||
Create and manage distributed capacity pools.
|
||||
|
||||
definitions:
|
||||
|
||||
ErrorResponse:
|
||||
description: "Represents an error."
|
||||
type: "object"
|
||||
required: ["message"]
|
||||
properties:
|
||||
message:
|
||||
description: "The error message."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
example:
|
||||
message: "Something went wrong."
|
||||
|
||||
Deployment:
|
||||
type: "object"
|
||||
description: "Volume master or replica deployment details."
|
||||
properties:
|
||||
ID:
|
||||
type: "string"
|
||||
readOnly: true
|
||||
Controller:
|
||||
type: "string"
|
||||
readOnly: true
|
||||
Inode:
|
||||
type: "integer"
|
||||
format: "uint32"
|
||||
readOnly: true
|
||||
Status:
|
||||
type: "string"
|
||||
readOnly: true
|
||||
Health:
|
||||
type: "string"
|
||||
readOnly: true
|
||||
CreatedAt:
|
||||
type: "string"
|
||||
format: "datetime"
|
||||
readOnly: true
|
||||
|
||||
VolumeCreateOptions:
|
||||
type: "object"
|
||||
description: "Parameters available for creating new volumes."
|
||||
required: [Name]
|
||||
properties:
|
||||
Name:
|
||||
description: "Volume name."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Description:
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
description: "Volume description."
|
||||
Size:
|
||||
type: "integer"
|
||||
description: "Size in GB (if 0 or not specified, then defaults to 10 GB)."
|
||||
x-nullable: false
|
||||
Pool:
|
||||
type: "string"
|
||||
description: "Name of capacity pool to provision the volume in, or the name of the current pool."
|
||||
Labels:
|
||||
type: "object"
|
||||
description: "User-defined key/value metadata."
|
||||
x-nullable: false
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
VolumeUpdateOptions:
|
||||
type: "object"
|
||||
description: "Parameters available for updating existing volumes."
|
||||
properties:
|
||||
Description:
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
description: "Volume description."
|
||||
Size:
|
||||
type: "integer"
|
||||
description: "Size in GB."
|
||||
x-nullable: false
|
||||
Labels:
|
||||
type: "object"
|
||||
description: "User-defined key/value metadata."
|
||||
x-nullable: false
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
# VolumeMountOptions:
|
||||
# type: "object"
|
||||
# description: "Parameters available for mounting volumes."
|
||||
# properties:
|
||||
# ID:
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# description: "Volume unique ID."
|
||||
# Name:
|
||||
# description: "Volume name."
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# Namespace:
|
||||
# description: "The object scope, such as for teams and projects."
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# Client:
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# description: "Hostname of the client performing the mount."
|
||||
#
|
||||
# VolumeUnmountOptions:
|
||||
# type: "object"
|
||||
# description: "Parameters available for unmounting volumes."
|
||||
# properties:
|
||||
# ID:
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# description: "Volume unique ID."
|
||||
# Name:
|
||||
# description: "Volume name."
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# Namespace:
|
||||
# description: "The object scope, such as for teams and projects."
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# Client:
|
||||
# type: "string"
|
||||
# x-nullable: false
|
||||
# description: "Hostname of the client performing the unmount."
|
||||
|
||||
# ListOptions:
|
||||
# type: "object"
|
||||
# description: "Parameters for finding volumes."
|
||||
# properties:
|
||||
# LabelSelector:
|
||||
# description: "A selector to restrict the list of returned objects by their labels. Defaults to everything."
|
||||
# type: "string"
|
||||
# FieldSelector:
|
||||
# type: "string"
|
||||
# description: "A selector to restrict the list of returned objects by their fields. Defaults to everything."
|
||||
# TimeoutSeconds:
|
||||
# type: "integer"
|
||||
# description: "Timeout for the list call."
|
||||
# Namespace:
|
||||
# type: "string"
|
||||
# description: "Object name and auth scope, such as for teams and projects"
|
||||
|
||||
Volume:
|
||||
type: "object"
|
||||
description: "A storage volume."
|
||||
required: [Name, Size]
|
||||
properties:
|
||||
ID:
|
||||
description: "Volume unique ID."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
Name:
|
||||
description: "Volume name."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Description:
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
description: "Volume description."
|
||||
Size:
|
||||
type: integer
|
||||
description: "Size in GB."
|
||||
x-nullable: false
|
||||
Pool:
|
||||
type: "string"
|
||||
description: "Name of capacity pool to provision the volume in, or the name of the current pool."
|
||||
Labels:
|
||||
type: "object"
|
||||
description: "User-defined key/value metadata."
|
||||
x-nullable: false
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
Master:
|
||||
$ref: "#/definitions/Deployment"
|
||||
Replicas:
|
||||
type: "array"
|
||||
description: "Volume deployment information for the replica volumes."
|
||||
items:
|
||||
$ref: "#/definitions/Deployment"
|
||||
readOnly: true
|
||||
Status:
|
||||
type: "string"
|
||||
description: "Short status, one of: pending, evaluating, deploying, active, unavailable, failed, updating, deleting."
|
||||
readOnly: true
|
||||
StatusMessage:
|
||||
type: "string"
|
||||
description: "Status message explaining current status."
|
||||
readOnly: true
|
||||
Health:
|
||||
type: "string"
|
||||
description: "Volume health, one of: healthy, degraded or dead."
|
||||
readOnly: true
|
||||
Inode:
|
||||
type: "integer"
|
||||
format: "uint32"
|
||||
description: "Block device inode."
|
||||
readOnly: true
|
||||
Deleted:
|
||||
type: "boolean"
|
||||
description: "Flag indicating if the volume has been deleted and is waiting for scrubbing."
|
||||
readOnly: true
|
||||
Mounted:
|
||||
type: "boolean"
|
||||
description: "Flag indicating if the volume is mounted and in use."
|
||||
readOnly: true
|
||||
MountedBy:
|
||||
type: "string"
|
||||
description: "Reference to the node that has the volume mounted."
|
||||
readOnly: true
|
||||
Mountpoint:
|
||||
type: "string"
|
||||
description: "Mountpoint where the volume was mounted."
|
||||
readOnly: true
|
||||
MountedAt:
|
||||
type: "string"
|
||||
format: "dateTime"
|
||||
description: "When the volume was mounted."
|
||||
readOnly: true
|
||||
CreatedBy:
|
||||
type: "string"
|
||||
description: "User that created the volume."
|
||||
readOnly: true
|
||||
CreatedAt:
|
||||
type: "string"
|
||||
format: "dateTime"
|
||||
description: "When the volume was created."
|
||||
readOnly: true
|
||||
example:
|
||||
Name: vol01
|
||||
Size: 5
|
||||
Labels:
|
||||
com.example.some-label: "some-value"
|
||||
com.example.some-other-label: "some-other-value"
|
||||
|
||||
PoolCreateOptions:
|
||||
type: "object"
|
||||
description: "Parameters available for creating new pools."
|
||||
required: [Name]
|
||||
properties:
|
||||
Name:
|
||||
description: "Pool name."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Description:
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
description: "Pool description."
|
||||
Default:
|
||||
type: "boolean"
|
||||
description: "Default determines whether this pool is the default if a volume is provisioned without a pool specified. There can only be one default pool."
|
||||
x-nullable: false
|
||||
DefaultDriver:
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
description: "DefaultDriver specifies the storage driver to use by default if there are multiple drivers in the pool and no driver was specified in the provisioning request or assigned by rules. If no driver was specified and no default set, driver weight is used to determine the default."
|
||||
ControllerNames:
|
||||
type: "array"
|
||||
description: "ControllerNames is a list of controller names that are participating in the storage pool."
|
||||
items:
|
||||
type: "string"
|
||||
DriverNames:
|
||||
type: "array"
|
||||
description: "DriverNames is a list of backend storage drivers that are available in the storage pool."
|
||||
items:
|
||||
type: "string"
|
||||
Active:
|
||||
type: "boolean"
|
||||
x-nullable: false
|
||||
description: "Flag describing whether rule is active."
|
||||
default: false
|
||||
Labels:
|
||||
type: "object"
|
||||
description: "Labels define a list of labels that describe the pool."
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
Pool:
|
||||
type: "object"
|
||||
description: |
|
||||
Pools are used to define distributed capacity that can be used to provision
|
||||
volumes from. Typically, each server that makes storage available will be
|
||||
added to one or more pools.
|
||||
|
||||
Capacity drivers are also added to the pool to determine which backend
|
||||
storage driver to use. Currently this is limited to a single type of
|
||||
driver per pool, but in the future we will allow multiple, allowing for
|
||||
dynamic tiering and snapshots from one driver type to another.
|
||||
required: [Name]
|
||||
properties:
|
||||
ID:
|
||||
description: "Pool unique ID."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
Name:
|
||||
description: "Pool name."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Description:
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
description: "Pool description."
|
||||
Default:
|
||||
type: "boolean"
|
||||
x-nullable: false
|
||||
description: |
|
||||
Default determines whether this pool is the default if a volume is
|
||||
provisioned without a pool specified. There can only be one default
|
||||
pool.
|
||||
DefaultDriver:
|
||||
type: "string"
|
||||
description: |
|
||||
DefaultDriver specifies the storage driver to use by default if there
|
||||
are multiple drivers in the pool and no driver was specified in the
|
||||
provisioning request or assigned by rules. If no driver was specified
|
||||
and no default set, driver weight is used to determine the default.
|
||||
ControllerNames:
|
||||
type: "array"
|
||||
description: "ControllerNames is a list of controller names that are participating in the storage pool."
|
||||
items:
|
||||
type: "string"
|
||||
DriverNames:
|
||||
type: "array"
|
||||
description: "DriverNames is a list of backend storage drivers that are available in the storage pool."
|
||||
items:
|
||||
type: "string"
|
||||
DriverInstances:
|
||||
$ref: "#/definitions/DriverInstances"
|
||||
Active:
|
||||
type: "boolean"
|
||||
x-nullable: false
|
||||
description: "Flag describing whether rule is active."
|
||||
default: false
|
||||
CapacityStats:
|
||||
$ref: "#/definitions/CapacityStats"
|
||||
Labels:
|
||||
type: "object"
|
||||
description: "Labels define a list of labels that describe the pool."
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
Rule:
|
||||
type: "object"
|
||||
description: "A policy rule."
|
||||
required: [Name]
|
||||
properties:
|
||||
ID:
|
||||
description: "Rule unique ID."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
Name:
|
||||
description: "Rule name."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Description:
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
description: "Rule description."
|
||||
Active:
|
||||
type: "boolean"
|
||||
x-nullable: false
|
||||
description: "Flag describing whether rule is active."
|
||||
default: false
|
||||
Weight:
|
||||
type: "integer"
|
||||
x-nullable: false
|
||||
description: |
|
||||
"Weight is used to determine order during rule processing. Rules with heavier weights are processed later."
|
||||
default: 0
|
||||
Operator:
|
||||
type: "string"
|
||||
description: "Operator is used to compare objects or labels."
|
||||
enum:
|
||||
- "!"
|
||||
- "="
|
||||
- "=="
|
||||
- "in"
|
||||
- "!="
|
||||
- "notin"
|
||||
- "exists"
|
||||
- "gt"
|
||||
- "lt"
|
||||
RuleAction:
|
||||
type: "string"
|
||||
description: "RuleAction controls whether the action is to add or remove a label from the matching object(s)."
|
||||
enum:
|
||||
- "add"
|
||||
- "remove"
|
||||
default: "add"
|
||||
Selectors:
|
||||
type: "object"
|
||||
description: "Selectors defines the list of labels that should trigger a rule."
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
Labels:
|
||||
type: "object"
|
||||
description: "Labels define the list of labels that will be added or removed from the matching object(s).."
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
CapacityStats:
|
||||
type: "object"
|
||||
description: "CapacityStats is used to report capacity statistics on pools and controllers."
|
||||
properties:
|
||||
TotalCapacityBytes:
|
||||
description: "TotalCapacityBytes is the object's total capacity in bytes."
|
||||
type: "integer"
|
||||
readOnly: true
|
||||
AvailableCapacityBytes:
|
||||
description: "AvailableCapacityBytes is the object's available capacity in bytes."
|
||||
type: "integer"
|
||||
readOnly: true
|
||||
ProvisionedCapacityBytes:
|
||||
description: "ProvisionedCapacityBytes is the object's provisioned capacity in bytes."
|
||||
type: "integer"
|
||||
readOnly: true
|
||||
|
||||
DriverInstances:
|
||||
type: "object"
|
||||
description: "DriverInstances shows the internal configuration and state of each driver on all the nodes in the pool. Data within DriverInstances can not be modified directly."
|
||||
properties:
|
||||
ID:
|
||||
description: "Instance unique ID."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
Name:
|
||||
description: "Instance name."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
Description:
|
||||
description: "Instance description."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
Active:
|
||||
description: "Flag describing whether the template is active."
|
||||
type: "boolean"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
Config:
|
||||
description: "Config is JSON struct that is passed directly to the driver. There is no specific format, and the driver is responsible for validation."
|
||||
type: "object"
|
||||
readOnly: true
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
Labels:
|
||||
description: "Labels define a list of labels that describe the driver instance. These are inherited from the pool when the driver instance is created."
|
||||
type: "object"
|
||||
readOnly: true
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
ControllerName:
|
||||
description: "ControllerName specifies the controller that this instance is running on."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
PoolID:
|
||||
description: "PoolID refers to the pool that this driver instance relates to."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
DriverName:
|
||||
description: "DriverName specifies which capacity driver this is an instance of."
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
readOnly: true
|
||||
CapacityStats:
|
||||
$ref: "#/definitions/CapacityStats"
|
||||
|
||||
parameters:
|
||||
Name:
|
||||
name: "name"
|
||||
in: "path"
|
||||
type: "string"
|
||||
description: "Volume name or ID."
|
||||
required: true
|
||||
Namespace:
|
||||
name: "namespace"
|
||||
in: "path"
|
||||
type: "string"
|
||||
description: "Object name and auth scope, such as for teams and projects."
|
||||
required: true
|
||||
NamespaceQuery:
|
||||
name: "namespace"
|
||||
in: "query"
|
||||
type: "string"
|
||||
description: "Object name and auth scope, such as for teams and projects."
|
||||
default: "default"
|
||||
LabelSelector:
|
||||
name: "labelSelector"
|
||||
in: "query"
|
||||
description: "A selector to restrict the list of returned objects by their labels. Defaults to everything."
|
||||
type: "string"
|
||||
FieldSelector:
|
||||
name: "fieldSelector"
|
||||
in: "query"
|
||||
type: "string"
|
||||
description: "A selector to restrict the list of returned objects by their fields. Defaults to everything."
|
||||
TimeoutSeconds:
|
||||
name: "timeoutSeconds"
|
||||
in: "query"
|
||||
type: "integer"
|
||||
description: "Timeout for the list call."
|
||||
|
||||
paths:
|
||||
/namespaces/{namespace}/volumes:
|
||||
get:
|
||||
summary: "List volumes"
|
||||
description: "List of volumes that match the query."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Volume"]
|
||||
parameters:
|
||||
- name: "namespace"
|
||||
in: "path"
|
||||
required: true
|
||||
description: "The object scope, such as for teams and projects."
|
||||
type: "string"
|
||||
- $ref: "#/parameters/LabelSelector"
|
||||
- $ref: "#/parameters/FieldSelector"
|
||||
- $ref: "#/parameters/TimeoutSeconds"
|
||||
- $ref: "#/parameters/NamespaceQuery"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
schema:
|
||||
type: "array"
|
||||
items:
|
||||
$ref: "#/definitions/Volume"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
post:
|
||||
summary: "Create volume"
|
||||
description: "Provisions a new volume."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Volume"]
|
||||
parameters:
|
||||
- $ref: "#/parameters/Namespace"
|
||||
- name: "VolumeCreateOptions"
|
||||
in: "body"
|
||||
schema:
|
||||
$ref: "#/definitions/VolumeCreateOptions"
|
||||
responses:
|
||||
201:
|
||||
description: "Volume created successfully"
|
||||
schema:
|
||||
$ref: "#/definitions/Volume"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
409:
|
||||
description: "Volume with name already exists"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
/namespaces/{namespace}/volumes/{name}:
|
||||
get:
|
||||
summary: "Get a volume"
|
||||
description: "Gets a volume by name or ID. Returns to whole volume object."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Volume"]
|
||||
parameters:
|
||||
- $ref: "#/parameters/Name"
|
||||
- $ref: "#/parameters/Namespace"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
schema:
|
||||
$ref: "#/definitions/Volume"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
404:
|
||||
description: "Not found"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
407:
|
||||
description: "Volume already exists"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
put:
|
||||
summary: "Update volume"
|
||||
description: "Updates an existing volume."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Volume"]
|
||||
parameters:
|
||||
- $ref: "#/parameters/Name"
|
||||
- $ref: "#/parameters/Namespace"
|
||||
- name: "VolumeUpdateOptions"
|
||||
in: "body"
|
||||
schema:
|
||||
$ref: "#/definitions/VolumeUpdateOptions"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
schema:
|
||||
$ref: "#/definitions/Volume"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
delete:
|
||||
summary: "Delete volume"
|
||||
description: "Deletes a volume."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Volume"]
|
||||
parameters:
|
||||
- name: "namespace"
|
||||
in: "path"
|
||||
required: true
|
||||
description: "The object scope, such as for teams and projects."
|
||||
type: "string"
|
||||
- name: "name"
|
||||
in: "path"
|
||||
required: true
|
||||
description: "Volume name or ID."
|
||||
type: "string"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
407:
|
||||
description: "Volume in use"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
/namespaces/{namespace}/volumes/{name}/mount:
|
||||
post:
|
||||
summary: "Mount volume"
|
||||
description: "Updates the mount reference for the volume."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Volume"]
|
||||
parameters:
|
||||
- $ref: "#/parameters/Name"
|
||||
- $ref: "#/parameters/Namespace"
|
||||
- name: "client"
|
||||
in: "body"
|
||||
description: "Hostname of the client mounting the volume"
|
||||
required: true
|
||||
schema:
|
||||
type: "string"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
schema:
|
||||
$ref: "#/definitions/Volume"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
407:
|
||||
description: "Volume already mounted"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
/namespaces/{namespace}/volumes/{name}/unmount:
|
||||
post:
|
||||
summary: "Mount volume"
|
||||
description: "Updates the mount reference for the volume."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Volume"]
|
||||
parameters:
|
||||
- $ref: "#/parameters/Name"
|
||||
- $ref: "#/parameters/Namespace"
|
||||
- name: "client"
|
||||
in: "body"
|
||||
description: "Hostname of the client mounting the volume"
|
||||
required: true
|
||||
schema:
|
||||
type: "string"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
schema:
|
||||
$ref: "#/definitions/Volume"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
407:
|
||||
description: "Volume not mounted"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
/pools:
|
||||
get:
|
||||
summary: "List pools"
|
||||
description: "List of pools that match the query."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Pool"]
|
||||
#parameters:
|
||||
# - $ref: "#/parameters/LabelSelector"
|
||||
# - $ref: "#/parameters/FieldSelector"
|
||||
# - $ref: "#/parameters/TimeoutSeconds"
|
||||
# - $ref: "#/parameters/NamespaceQuery"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
schema:
|
||||
type: "array"
|
||||
items:
|
||||
$ref: "#/definitions/Pool"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
post:
|
||||
summary: "Create pool"
|
||||
description: "Provisions a new pool."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Pool"]
|
||||
parameters:
|
||||
- name: "PoolCreateOptions"
|
||||
in: "body"
|
||||
schema:
|
||||
$ref: "#/definitions/PoolCreateOptions"
|
||||
responses:
|
||||
201:
|
||||
description: "Pool created successfully"
|
||||
schema:
|
||||
$ref: "#/definitions/Pool"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
409:
|
||||
description: "Pool with name already exists"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
/pools/{name}:
|
||||
get:
|
||||
summary: "Get a pool"
|
||||
description: "Gets a pool by name or ID. Returns to whole pool object."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Pool"]
|
||||
parameters:
|
||||
- $ref: "#/parameters/Name"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
schema:
|
||||
$ref: "#/definitions/Pool"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
404:
|
||||
description: "Not found"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
407:
|
||||
description: "Pool already exists"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
delete:
|
||||
summary: "Delete pool"
|
||||
description: "Deletes a pool."
|
||||
produces:
|
||||
- "application/json"
|
||||
tags: ["Pool"]
|
||||
parameters:
|
||||
- name: "name"
|
||||
in: "path"
|
||||
required: true
|
||||
description: "Pool name or ID."
|
||||
type: "string"
|
||||
responses:
|
||||
200:
|
||||
description: "Success"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
407:
|
||||
description: "Pool in use"
|
||||
500:
|
||||
description: "Server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
|
@ -0,0 +1,90 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// TemplateAPIPrefix is a partial path to the HTTP endpoint.
|
||||
TemplateAPIPrefix = "/templates"
|
||||
|
||||
// ErrNoSuchTemplate is the error returned when the template does not exist.
|
||||
ErrNoSuchTemplate = errors.New("no such template")
|
||||
|
||||
// ErrTemplateInUse is the error returned when the template requested to be removed is still in use.
|
||||
ErrTemplateInUse = errors.New("template in use and cannot be removed")
|
||||
)
|
||||
|
||||
// TemplateList returns the list of available templates.
|
||||
func (c *Client) TemplateList(opts types.ListOptions) ([]types.Template, error) {
|
||||
path := TemplateAPIPrefix + "?" + queryString(opts)
|
||||
resp, err := c.do("GET", path, doOptions{context: opts.Context})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var templates []types.Template
|
||||
if err := json.NewDecoder(resp.Body).Decode(&templates); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return templates, nil
|
||||
}
|
||||
|
||||
// TemplateCreate creates a template on the server and returns the new object.
|
||||
func (c *Client) TemplateCreate(opts types.TemplateCreateOptions) (string, error) {
|
||||
resp, err := c.do("POST", TemplateAPIPrefix, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
out, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.Unquote(string(out))
|
||||
}
|
||||
|
||||
// Template returns a template by its reference.
|
||||
func (c *Client) Template(ref string) (*types.Template, error) {
|
||||
resp, err := c.do("GET", TemplateAPIPrefix+"/"+ref, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchTemplate
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var template types.Template
|
||||
if err := json.NewDecoder(resp.Body).Decode(&template); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &template, nil
|
||||
}
|
||||
|
||||
// TemplateDelete removes a template by its reference.
|
||||
func (c *Client) TemplateDelete(ref string) error {
|
||||
resp, err := c.do("DELETE", TemplateAPIPrefix+"/"+ref, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchTemplate
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrTemplateInUse
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,769 @@
|
|||
docs.storageos.com - API webpage
|
||||
================================
|
||||
|
||||
TODO: Openind paragraph of text has multiple typos and grammar errors. Need to fix this. [DONE]
|
||||
TODO: hard to see the text for the example JSON post messages on the RHS. Possibly a Safari thing?!
|
||||
TODO: some of the JSON examples on the RHS of the webpage look wrong?! Safari issue?
|
||||
TODO: docs should use all lowercase for consistency?
|
||||
|
||||
VOLUMES
|
||||
=======
|
||||
|
||||
List volumes
|
||||
============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - no provisioned volumes.
|
||||
- No auth username and password supplied.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
<--
|
||||
401 UNAUTHORIZED
|
||||
Unauthorized
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - no provisioned volumes.
|
||||
- bad namespace used.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/defaultxxx/volumes
|
||||
<--
|
||||
404 NOT FOUND
|
||||
JSON: "message": "Not found"
|
||||
Unauthorized
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - no provisioned volumes.
|
||||
- Dropped the {namespace} path parameter.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/volumes
|
||||
<--
|
||||
404 NOT FOUND
|
||||
Not found
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - no provisioned volumes.
|
||||
- One volume has already been created successfully.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/volumes
|
||||
<--
|
||||
JSON: valid volume/inode state data (note: this time the master data is correctly set i.e. has non-zero data)
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster - single provisioned volumes.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/volumes
|
||||
<--
|
||||
JSON: valid volume/inode state data.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster - multiple provisioned volumes.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/volumes
|
||||
<--
|
||||
JSON: an array of valid volume/inode state data for multiple volumes.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster - multiple provisioned volumes.
|
||||
- labelSelector
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/volumes?labelSelector=com.example.some-label
|
||||
<--
|
||||
JSON: an array of valid volume/inode state data for multiple volumes.
|
||||
-FAILED- doesn't appear to filter.
|
||||
XXX:
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster - multiple provisioned volumes.
|
||||
- fieldSelector
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/namespaces/volumes?fieldSelector=<????>
|
||||
<--
|
||||
JSON: an array of valid volume/inode state data for multiple volumes.
|
||||
-UNTESTED- don't know what to put for fieldSelector's value??
|
||||
XXX:
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Create volumes
|
||||
==============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - no provisioned volumes.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "redis-vol01",
|
||||
"size": 1
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data (note: master data is all zerod at this point)
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - with a single volume already provisioned.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "redis-vol02",
|
||||
"size": 2
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data (note: master data is all zerod at this point)
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - with two volumes already provisioned.
|
||||
- Now trying to provision a third with bad JSON body -- using CAPITAL first letters for Name and Size.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"Name": "redis-vol03",
|
||||
"Size": 3
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data (note: master data is all zerod at this point)
|
||||
-EXPECTED- WORKS?! This implies that the JSON keys are non-case sensitive.
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - with two volumes already provisioned.
|
||||
- Now trying to provision a third with bad JSON body -- using all CAPITALS for NAME and SIZE.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"NAME": "redis-vol03",
|
||||
"SIZE": 3
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data (note: master data is all zerod at this point)
|
||||
-EXPECTED- WORKS?! This implies that the JSON keys are non-case sensitive.
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - with two volumes already provisioned.
|
||||
- Now trying to provision a volume with bad JSON body -- missing size parameter.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "redis-vol05",
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data (note: master data is all zerod at this point)
|
||||
-EXPECTED- Size defaults to 10
|
||||
TODO: update documentation to reflect this. [DONE]
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - with two volumes already provisioned.
|
||||
- Now trying to provision a volume with bad JSON body -- with size (0) parameter.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "redis-vol05",
|
||||
"size" 0
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data (note: master data is all zerod at this point)
|
||||
-EXPECTED- Size defaults to 10
|
||||
TODO: update documentation to reflect this. [DONE]
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - with two volumes already provisioned.
|
||||
- Now trying to provision a volume with bad JSON body -- empty JSON object.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
}
|
||||
<--
|
||||
volume name not valid
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - with two volumes already provisioned.
|
||||
- Now trying to provision a volume with no JSON body -- empty
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
<--
|
||||
JSON: "message": "EOF"
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster.
|
||||
- Now trying to provision a volume with no JSON name field
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"size": 2
|
||||
}
|
||||
<--
|
||||
volume name not valid
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster.
|
||||
- Now trying to provision a volume with no JSON name field
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"size": 2
|
||||
}
|
||||
<--
|
||||
volume name not valid
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster.
|
||||
- Now trying to provision a volume with same name as one that has already been provisioned.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "myvol1",
|
||||
"size": 5
|
||||
}
|
||||
<--
|
||||
volume with name 'myvol1' already exists
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster.
|
||||
- Now trying to provision a volume with pool name.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "myvol1",
|
||||
"size": 5,
|
||||
"pool": "mypool1"
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data with correct pool field.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- 3 node cluster.
|
||||
- Now trying to provision a volume with optional labels.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"Name": "vol01",
|
||||
"Size": 5,
|
||||
"Labels": {
|
||||
"com.example.some-label": "some-value",
|
||||
"com.example.some-other-label": "some-other-value"
|
||||
}
|
||||
}
|
||||
<--
|
||||
JSON: valid volume/inode state data with correct labels.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Get volumes
|
||||
===========
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Get by name.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
|
||||
<--
|
||||
JSON: correct volume/inode state data.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Get by id.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/270c1fc2-c578-77f8-2d7c-1515e626b6c3
|
||||
<--
|
||||
JSON: correct volume/inode state data.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Passing bad name/id.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/this-volume-does-not-exist
|
||||
<--
|
||||
Not Found
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Update volumes
|
||||
==============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Update a volume
|
||||
-->
|
||||
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
|
||||
JSON:
|
||||
{
|
||||
"Description": "string",
|
||||
"Size": 5,
|
||||
"Labels": {
|
||||
"property1": "string",
|
||||
"property2": "string"
|
||||
}
|
||||
}
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Update a volume, trying with bad JSON, missing opening curly brace!
|
||||
-->
|
||||
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
|
||||
JSON:
|
||||
"Description": "string",
|
||||
"Size": 5,
|
||||
"Labels": {
|
||||
"property1": "string",
|
||||
"property2": "string"
|
||||
}
|
||||
}
|
||||
<--
|
||||
400 BAD REQUEST
|
||||
Request decode failed: json: cannot unmarshal string into Go value of type types.Volume
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Update a volume, trying with size (0) and same property1 and new property3.
|
||||
-->
|
||||
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
|
||||
JSON:
|
||||
{
|
||||
"Description": "string",
|
||||
"Size": 0,
|
||||
"Labels": {
|
||||
"property1": "string",
|
||||
"property3": "string3"
|
||||
}
|
||||
}
|
||||
<--
|
||||
200 OK
|
||||
-NOT EXPECTED-
|
||||
The old labels are completely overwritten anew (hence the previous property2 label is not present). I assume this is the desired behaviour?!
|
||||
TODO: However size is now zero?! Check this is ok! Probably not; since the Create volume API defaults to 10 when 0 is passed or omitted.
|
||||
XXX:
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Update a volume, trying with omitting size parameter
|
||||
-->
|
||||
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
|
||||
JSON:
|
||||
{
|
||||
"Description": "string",
|
||||
"Labels": {
|
||||
"property1": "string",
|
||||
"property3": "string3"
|
||||
}
|
||||
}
|
||||
<--
|
||||
200 OK
|
||||
-NOT EXPECTED-
|
||||
XXX: size is now zero when not passing size in JSON body of PUT request.
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Update a volume, trying with omitting description parameter
|
||||
-->
|
||||
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
|
||||
JSON:
|
||||
{
|
||||
"Labels": {
|
||||
"property1": "string",
|
||||
"property3": "string3"
|
||||
}
|
||||
}
|
||||
<--
|
||||
200 OK
|
||||
-NOT EXPECTED-
|
||||
XXX: description string is empty i.e. "" when not passing description in JSON body of PUT request.
|
||||
The above implies that the update volume PUT request receiving side interprets missing update parameters as their null-value counterparts. So it's is not possible to update just specific parameters.
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Delete volumes
|
||||
==============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Delete a volume. But not specifying a name in the path.
|
||||
-->
|
||||
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes
|
||||
<--
|
||||
404 NOT FOUND
|
||||
404 page not found
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Delete a volume by specifying the volume's name.
|
||||
-->
|
||||
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol05
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
TODO: But when doing a GET ~volumes/redis-vol05 it is still present So DELETE volumes doesn't appear to be working.
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Delete a volume by specifying the volume's id.
|
||||
-->
|
||||
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/5233930b-b77f-2863-0895-b1eb5d73ec45
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
TODO: But when doing a GET ~volumes/5233930b-b77f-2863-0895-b1eb5d73ec45 it is still present So DELETE volumes doesn't appear to be working.
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Trying to delete a mounted volume
|
||||
-->
|
||||
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
|
||||
<--
|
||||
412 PRECONDITION FAILED
|
||||
cannot delete mounted volume
|
||||
-EXPECTED-
|
||||
TODO: seems correct, this hints that the mount is working which is in conflict with my observation below for MOUNT and UNMOUNT. Q. Is it checking the mount status via the OS, or by some other means i.e. locally cached value?
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Trying to delete a unmounted volume
|
||||
-->
|
||||
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Mount volumes
|
||||
=============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Mount a volume. But not specifying a name in the path.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol02/mount
|
||||
JSON:
|
||||
{
|
||||
"client": "storageos-1-68228"
|
||||
}
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
NOTE: in the JSON response, no_of_mounts does increase correctly by one each time. And mounted is set to true correctly.
|
||||
TODO: although this worked, the documentation doesn't give the proper JSON request body.
|
||||
TODO: no_of_mounts is still 0 in /var/lib/storageos/state/inode/178101 (should increase by 1 for every mount.)
|
||||
BUG^
|
||||
TODO: Also not sure if really mounted this volume, since running the storageos cli e.g.:
|
||||
$ ./storageos volume ls
|
||||
The MOUNTED BY column is always empty ?? Either cli doesn't show this info, yet. Or the volume isn't mounted.
|
||||
===============================================================================
|
||||
|
||||
|
||||
Unmount volumes
|
||||
===============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Mount a volume. But not specifying a name in the path.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol02/unmount
|
||||
JSON:
|
||||
{
|
||||
"can-be-anything": "storageos-1-68228"
|
||||
}
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
NOTE: in the JSON response, mounted is set back to false correctly.
|
||||
TODO: although this worked, the documentation doesn't give the proper JSON request body. The
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
POOLS
|
||||
=====
|
||||
|
||||
List pools
|
||||
==========
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster.
|
||||
- No auth username and password supplied.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/pools
|
||||
<--
|
||||
401 UNAUTHORIZED
|
||||
Unauthorized
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster. No pools.
|
||||
-->
|
||||
GET: 172.28.128.3:5705/v1/pools
|
||||
<--
|
||||
200 OK
|
||||
JSON: an array of pools.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Create pools
|
||||
============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "mypool1"
|
||||
}
|
||||
<--
|
||||
201 CREATED
|
||||
JSON: valid pool data.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster.
|
||||
- Trying to create a pool with the same name as another already created.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "mypool1"
|
||||
}
|
||||
<--
|
||||
409 CONFLICT
|
||||
Pool with name 'mypool1' already exists
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster.
|
||||
- Trying to create a pool with the same name as another already created.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "mypool2",
|
||||
"description": "hello world!"
|
||||
}
|
||||
<--
|
||||
201 CREATED
|
||||
JSON: valid pool data.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster.
|
||||
- Trying to create a pool with the defaultDriver string set.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/volumes
|
||||
BODY (JSON):
|
||||
{
|
||||
"name": "mypool6",
|
||||
"description": "hello world again!",
|
||||
"default": true,
|
||||
"defautDriver": "I'm the default driver :)"
|
||||
}
|
||||
<--
|
||||
201 CREATED
|
||||
JSON: Is correct for the most part, but defaultDriver is an empty string??
|
||||
-NOT EXPECTED-
|
||||
XXX
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Get Pools
|
||||
=========
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Get by name.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/pools/mypool1
|
||||
<--
|
||||
200 OK
|
||||
JSON: correct pool data.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Get by id.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/pools/ea477d68-8193-1179-d889-aa6ea8797082
|
||||
<--
|
||||
200 OK
|
||||
JSON: correct pool data.
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned volumes.
|
||||
- Get by name. Try passing invalid name.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/pools/mypool1xxx
|
||||
<--
|
||||
404 NOT FOUND
|
||||
Not Found
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Delete Pools
|
||||
============
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned pools.
|
||||
- Trying to delete without specifying name etc. in the path.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/pools
|
||||
<--
|
||||
404 NOT FOUND
|
||||
404 page not found
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned pools.
|
||||
- Delete by name.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/pools/mypool2
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
||||
|
||||
SCENARIO:
|
||||
- Fresh start - 3 node cluster - N provisioned pools.
|
||||
- Delete by id.
|
||||
-->
|
||||
POST: 172.28.128.3:5705/v1/pools/9a10bbfe-eaaa-af3c-2a9b-d78e0790efb4
|
||||
<--
|
||||
200 OK
|
||||
-EXPECTED-
|
||||
|
||||
===============================================================================
|
|
@ -0,0 +1,49 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"auth.go",
|
||||
"capacity_stats.go",
|
||||
"controller.go",
|
||||
"controller_update_options.go",
|
||||
"delete_options.go",
|
||||
"deployment.go",
|
||||
"driver_instance.go",
|
||||
"error_response.go",
|
||||
"events.go",
|
||||
"list_options.go",
|
||||
"namespace.go",
|
||||
"operator.go",
|
||||
"pool.go",
|
||||
"pool_create_options.go",
|
||||
"rule.go",
|
||||
"template.go",
|
||||
"template_create_options.go",
|
||||
"version.go",
|
||||
"volume.go",
|
||||
"volume_create_options.go",
|
||||
"volume_update_options.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -0,0 +1,14 @@
|
|||
package types
|
||||
|
||||
// AuthConfig contains authorization information for connecting to a Registry
|
||||
type AuthConfig struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
ServerAddress string `json:"serveraddress,omitempty"`
|
||||
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `json:"identitytoken,omitempty"`
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package types
|
||||
|
||||
// ErrCapacityStatsUnchanged can be used when comparing stats
|
||||
const ErrCapacityStatsUnchanged = "no changes"
|
||||
|
||||
// CapacityStats is used to report capacity statistics on pools and controllers.
|
||||
type CapacityStats struct {
|
||||
|
||||
// TotalCapacityBytes is the object's total capacity in bytes.
|
||||
TotalCapacityBytes uint64 `json:"totalCapacityBytes"`
|
||||
|
||||
// AvailableCapacityBytes is the object's available capacity in bytes.
|
||||
AvailableCapacityBytes uint64 `json:"availableCapacityBytes"`
|
||||
|
||||
// ProvisionedCapacityBytes is the object's provisioned capacity in bytes.
|
||||
ProvisionedCapacityBytes uint64 `json:"provisionedCapacityBytes"`
|
||||
}
|
||||
|
||||
// IsEqual checks if capacity values are the same
|
||||
func (c CapacityStats) IsEqual(n CapacityStats) bool {
|
||||
if c == n {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package types
|
||||
|
||||
// Versions and Prefixes used in API and KV URLs
|
||||
import "time"
|
||||
|
||||
const (
|
||||
ControllerAPIPrefix = "controller"
|
||||
ControllerDefaultPort = "3260"
|
||||
ControllerScanAPIPrefix = "config/scan"
|
||||
)
|
||||
|
||||
// ControllerCurrent - current controller
|
||||
var ControllerCurrent = ""
|
||||
|
||||
// Controller status phases
|
||||
const (
|
||||
ControllerStatusPending = "pending"
|
||||
ControllerStatusEvaluating = "evaluating"
|
||||
ControllerStatusDeploying = "deploying"
|
||||
ControllerStatusActive = "active"
|
||||
ControllerStatusFailed = "failed"
|
||||
ControllerStatusDeleting = "deleting"
|
||||
|
||||
ControllerHealthStarting = "starting"
|
||||
ControllerHealthOK = "healthy"
|
||||
ControllerHealthDegraded = "degraded"
|
||||
ControllerHealthOffline = "offline"
|
||||
)
|
||||
|
||||
// Errors for controller related things
|
||||
const (
|
||||
ErrControllerHostIDAllocation string = "error, could not allocate hostid"
|
||||
ErrControllerIDNotSet = "error, controller ID not set"
|
||||
ErrControllerNotFound = "controller not found"
|
||||
)
|
||||
|
||||
// Controller is used to represent a storage node in a cluster
|
||||
type Controller struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
HostID uint16 `json:"hostID"`
|
||||
Scheduler bool `json:"scheduler"`
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
APIPort int `json:"apiPort"`
|
||||
NatsPort int `json:"natsPort"`
|
||||
NatsClusterPort int `json:"natsClusterPort"`
|
||||
SerfPort int `json:"serfPort"`
|
||||
DFSPort int `json:"dfsPort"`
|
||||
Description string `json:"description"`
|
||||
ControllerGroups []string `json:"controllerGroups"`
|
||||
Tags []string `json:"tags"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
VolumeStats VolumeStats `json:"volumeStats"`
|
||||
PoolStats map[string]DriverStats `json:"poolStats"`
|
||||
|
||||
// health is updated by the
|
||||
Health string `json:"health"`
|
||||
HealthUpdatedAt time.Time `json:"healthUpdatedAt"`
|
||||
VersionInfo map[string]VersionInfo `json:"versionInfo"`
|
||||
Version string `json:"version"`
|
||||
|
||||
// high level stats that combine info from all driver instances
|
||||
CapacityStats CapacityStats `json:"capacityStats"`
|
||||
}
|
||||
|
||||
// DriverStats is used to report stats for all drivers in a pool.
|
||||
type DriverStats map[string]CapacityStats
|
||||
|
||||
// VolumeStats - volume stats (volume counts, looking forward to capacity)
|
||||
type VolumeStats struct {
|
||||
MasterVolumeCount int `json:"masterVolumeCount"`
|
||||
ReplicaVolumeCount int `json:"replicaVolumeCount"`
|
||||
VirtualVolumeCount int `json:"virtualVolumeCount"`
|
||||
}
|
24
vendor/github.com/storageos/go-api/types/controller_update_options.go
generated
vendored
Normal file
24
vendor/github.com/storageos/go-api/types/controller_update_options.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// ControllerUpdateOptions are available parameters for updating existing controllers.
|
||||
type ControllerUpdateOptions struct {
|
||||
|
||||
// Controller unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Controller name.
|
||||
// Read Only: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Description of the controller.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Labels are user-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// DeleteOptions are available parameters for deleting existing volumes.
|
||||
type DeleteOptions struct {
|
||||
|
||||
// Volume unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Volume name.
|
||||
// Read Only: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// Force will cause the volume to be deleted even if it's in use.
|
||||
Force bool `json:"force"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// Deployment Volume master or replica deployment details.
|
||||
// swagger:model Deployment
|
||||
type Deployment struct {
|
||||
|
||||
// Deployment unique ID
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Inode number
|
||||
// Read Only: true
|
||||
Inode uint32 `json:"inode"`
|
||||
|
||||
// Controller ID
|
||||
// Read Only: true
|
||||
Controller string `json:"controller"`
|
||||
|
||||
// Health
|
||||
// Read Only: true
|
||||
Health string `json:"health"`
|
||||
|
||||
// Status
|
||||
// Read Only: true
|
||||
Status string `json:"status"`
|
||||
|
||||
// Created at
|
||||
// Read Only: true
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package types
|
||||
|
||||
import "encoding/gob"
|
||||
|
||||
// DriverInstance is used to define an instance of a storage capacity driver.
|
||||
type DriverInstance struct {
|
||||
|
||||
// Instance unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Instance name.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Instance description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Flag describing whether the template is active.
|
||||
// Default: false
|
||||
Active bool `json:"active"`
|
||||
|
||||
// Config is JSON struct that is passed directly to the driver. There is no
|
||||
// specific format, and the driver is responsible for validation.
|
||||
Config interface{} `json:"config"`
|
||||
|
||||
// Labels define a list of labels that describe the driver instance. These
|
||||
// are inherited from the pool when the driver instance is created.
|
||||
Labels []string `json:"labels"`
|
||||
|
||||
// ControllerName specifies the controller that this instance is running on.
|
||||
ControllerName string `json:"controllerName"`
|
||||
|
||||
// PoolID refers to the pool that this driver instance relates to.
|
||||
PoolID string `json:"poolID"`
|
||||
|
||||
// DriverName specifies which capacity driver this is an instance of.
|
||||
DriverName string `json:"driverName"`
|
||||
|
||||
// CapacityStats tracks that capacity usage of this driver instance on the
|
||||
// current controller.
|
||||
CapacityStats CapacityStats `json:"capacityStats"`
|
||||
}
|
||||
|
||||
// DriverInstances is a collection of Driver instance objects.
|
||||
type DriverInstances []*DriverInstance
|
||||
|
||||
func init() {
|
||||
gob.Register(DriverInstance{})
|
||||
gob.Register([]interface{}{})
|
||||
}
|
||||
|
||||
// Find an instance matching the parameters.
|
||||
func (i *DriverInstances) Find(pool string, driver string, controller string) *DriverInstance {
|
||||
|
||||
for _, inst := range *i {
|
||||
if inst.PoolID == pool && inst.DriverName == driver && inst.ControllerName == controller {
|
||||
return inst
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add a new instance to the list of instances.
|
||||
func (i *DriverInstances) Add(new *DriverInstance) {
|
||||
|
||||
for _, inst := range *i {
|
||||
// Skip if it already exists
|
||||
if inst.PoolID == new.PoolID && inst.DriverName == new.DriverName && inst.ControllerName == new.ControllerName {
|
||||
return
|
||||
}
|
||||
}
|
||||
*i = append(*i, new)
|
||||
}
|
||||
|
||||
// Remove an instance to the list of instances.
|
||||
func (i *DriverInstances) Remove(id string) {
|
||||
|
||||
// TODO: not working
|
||||
// for ndx, inst := range *i {
|
||||
// if inst.ID == id {
|
||||
// // splice out the item to remove
|
||||
// *i = append(*i[:ndx], *i[ndx+1:]...)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package types
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// ErrorResponse Represents an error.
|
||||
// swagger:model ErrorResponse
|
||||
type ErrorResponse struct {
|
||||
|
||||
// The error message.
|
||||
// Required: true
|
||||
Message string `json:"message"`
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// EventType describes the type of event
|
||||
type EventType string
|
||||
|
||||
// EventTypes are added to events to assist with type assertions
|
||||
const (
|
||||
RequestType EventType = "request"
|
||||
ResponseType = "response"
|
||||
HeartbeatType = "heartbeat"
|
||||
BackupType = "backup"
|
||||
)
|
||||
|
||||
// Event describes the fields that all events should implement. Event is
|
||||
// intended to be inherherited in more specific Event types.
|
||||
type Event struct {
|
||||
ID string `json:"id"`
|
||||
// Parent is used to specify parent event
|
||||
Parent string `json:"parent"`
|
||||
EventType EventType `json:"eventType"`
|
||||
Action string `json:"action"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Log []string `json:"log"`
|
||||
ProgressPercent int `json:"progressPercent"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
|
||||
Target string `json:"target"`
|
||||
ActionPayload interface{} `json:"actionPayload"`
|
||||
|
||||
// payload can be encoded into bytes as well
|
||||
ActionPayloadBytes []byte `json:"actionPayloadBts"`
|
||||
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
// retry related value
|
||||
Retry bool `json:"retry"`
|
||||
RetriedAt time.Time `json:"retriedAt"`
|
||||
Attempts int `json:"attempts"`
|
||||
|
||||
// optional parameter
|
||||
Deadline time.Time `json:"deadline"`
|
||||
|
||||
// optional events to dispatch
|
||||
Rollback []*Request `json:"rollback"`
|
||||
RollbackDone bool `json:"rollbackDone"`
|
||||
|
||||
Subject string `json:"subject"` // or "queue"
|
||||
|
||||
// controller ID which created this event
|
||||
OriginController string `json:"originController"`
|
||||
}
|
||||
|
||||
// Request is the message structure used for sending request events
|
||||
type Request struct {
|
||||
Event
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// ListOptions are optional parameters for finding and listing most objects.
|
||||
type ListOptions struct {
|
||||
|
||||
// FieldSelector restricts the list of returned objects by their fields. Defaults to everything.
|
||||
FieldSelector string
|
||||
|
||||
// LabelSelector restricts the list of returned objects by their labels. Defaults to everything.
|
||||
LabelSelector string
|
||||
|
||||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Namespace is used to as a container to isolate namespace and rule obects.
|
||||
type Namespace struct {
|
||||
|
||||
// Namespace unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Namespace name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// The optional DisplayName is how the project is displayed in the web console (defaults to name).
|
||||
DisplayName string `json:"displayName"`
|
||||
|
||||
// Namespcae description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// User-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// When the namespace was created.
|
||||
// Read Only: true
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
// User that created the namespace.
|
||||
// Read Only: true
|
||||
CreatedBy string `json:"createdBy"`
|
||||
|
||||
// When the namespace was created.
|
||||
// Read Only: true
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// NamespaceCreateOptions are available parameters for creating new namespaces.
|
||||
type NamespaceCreateOptions struct {
|
||||
|
||||
// Name is the name of the namespace to create.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// The optional DisplayName is how the project is displayed in the web console (defaults to name).
|
||||
DisplayName string `json:"displayName"`
|
||||
|
||||
// Description describes the namespace.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Labels are user-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue