cleanup: Remove storageos volume plugins from k8s codebase
This commit is contained in:
parent
442574f3a7
commit
d52cdeae79
49
LICENSES/vendor/github.com/storageos/go-api/LICENSE
generated
vendored
49
LICENSES/vendor/github.com/storageos/go-api/LICENSE
generated
vendored
@ -1,49 +0,0 @@
|
||||
= vendor/github.com/storageos/go-api licensed under: =
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2018 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 10d8703157b5fd9422b1813bae555905
|
@ -39,7 +39,6 @@ import (
|
||||
"k8s.io/kubernetes/pkg/volume/iscsi"
|
||||
"k8s.io/kubernetes/pkg/volume/local"
|
||||
"k8s.io/kubernetes/pkg/volume/nfs"
|
||||
"k8s.io/kubernetes/pkg/volume/storageos"
|
||||
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
@ -59,7 +58,6 @@ func ProbeAttachableVolumePlugins() ([]volume.VolumePlugin, error) {
|
||||
if err != nil {
|
||||
return allPlugins, err
|
||||
}
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, iscsi.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
||||
@ -82,7 +80,6 @@ func ProbeExpandableVolumePlugins(config persistentvolumeconfig.VolumeConfigurat
|
||||
return allPlugins, err
|
||||
}
|
||||
allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
|
||||
return allPlugins, nil
|
||||
}
|
||||
@ -131,7 +128,6 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config persiste
|
||||
}
|
||||
|
||||
allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
|
||||
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
||||
|
@ -38,7 +38,6 @@ import (
|
||||
"k8s.io/kubernetes/pkg/volume/nfs"
|
||||
"k8s.io/kubernetes/pkg/volume/projected"
|
||||
"k8s.io/kubernetes/pkg/volume/secret"
|
||||
"k8s.io/kubernetes/pkg/volume/storageos"
|
||||
|
||||
// Cloud providers
|
||||
_ "k8s.io/kubernetes/pkg/cloudprovider/providers"
|
||||
@ -72,7 +71,6 @@ func ProbeVolumePlugins(featureGate featuregate.FeatureGate) ([]volume.VolumePlu
|
||||
allPlugins = append(allPlugins, configmap.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, projected.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
||||
return allPlugins, nil
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@ -65,7 +65,6 @@ require (
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/storageos/go-api v2.2.0+incompatible
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
github.com/vmware/govmomi v0.20.3
|
||||
@ -489,7 +488,6 @@ replace (
|
||||
github.com/spf13/cobra => github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/pflag => github.com/spf13/pflag v1.0.5
|
||||
github.com/stoewer/go-strcase => github.com/stoewer/go-strcase v1.2.0
|
||||
github.com/storageos/go-api => github.com/storageos/go-api v2.2.0+incompatible
|
||||
github.com/stretchr/objx => github.com/stretchr/objx v0.2.0
|
||||
github.com/stretchr/testify => github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/gocapability => github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
|
2
go.sum
2
go.sum
@ -390,8 +390,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/storageos/go-api v2.2.0+incompatible h1:U0SablXoZIg06gvSlg8BCdzq1C/SkHVygOVX95Z2MU0=
|
||||
github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
|
@ -54,7 +54,6 @@ import (
|
||||
"k8s.io/kubernetes/pkg/volume/projected"
|
||||
"k8s.io/kubernetes/pkg/volume/rbd"
|
||||
"k8s.io/kubernetes/pkg/volume/secret"
|
||||
"k8s.io/kubernetes/pkg/volume/storageos"
|
||||
"k8s.io/kubernetes/pkg/volume/util/hostutil"
|
||||
"k8s.io/kubernetes/pkg/volume/util/subpath"
|
||||
"k8s.io/kubernetes/test/utils"
|
||||
@ -83,7 +82,6 @@ func volumePlugins() []volume.VolumePlugin {
|
||||
allPlugins = append(allPlugins, projected.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, local.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
|
||||
return allPlugins
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
/*
|
||||
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"
|
@ -1,762 +0,0 @@
|
||||
/*
|
||||
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/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/mount-utils"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
utilstrings "k8s.io/utils/strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
)
|
||||
|
||||
// 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"
|
||||
defaultDeviceDir = "/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, utilstrings.EscapeQualifiedName(storageosPluginName), pvName+"."+volNamespace+"."+volName)
|
||||
}
|
||||
return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(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(spec *volume.Spec) 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{host: plugin.host}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, apiCfg *storageosAPIConfig, manager storageosManager, mounter mount.Interface, exec utilexec.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,
|
||||
exec: exec,
|
||||
plugin: plugin,
|
||||
MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, volNamespace, volName, spec.Name(), plugin.host)),
|
||||
},
|
||||
diskMounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec},
|
||||
mountOptions: util.MountOptionFromSpec(spec),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) NewUnmounter(pvName string, podUID types.UID) (volume.Unmounter, error) {
|
||||
return plugin.newUnmounterInternal(pvName, podUID, &storageosUtil{host: plugin.host}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
|
||||
}
|
||||
|
||||
func (plugin *storageosPlugin) newUnmounterInternal(pvName string, podUID types.UID, manager storageosManager, mounter mount.Interface, exec utilexec.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,
|
||||
exec: exec,
|
||||
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{host: plugin.host})
|
||||
}
|
||||
|
||||
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{host: plugin.host})
|
||||
}
|
||||
|
||||
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 true
|
||||
}
|
||||
|
||||
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)
|
||||
// Attaches the device to the host at a mount path.
|
||||
AttachDevice(mounter *storageosMounter, deviceMountPath 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
|
||||
// Gets the node's device path.
|
||||
DeviceDir(mounter *storageosMounter) string
|
||||
}
|
||||
|
||||
// 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
|
||||
readOnly bool
|
||||
description string
|
||||
pool string
|
||||
fsType string
|
||||
sizeGB int
|
||||
labels map[string]string
|
||||
apiCfg *storageosAPIConfig
|
||||
manager storageosManager
|
||||
mounter mount.Interface
|
||||
exec utilexec.Interface
|
||||
plugin *storageosPlugin
|
||||
volume.MetricsProvider
|
||||
}
|
||||
|
||||
type storageosMounter struct {
|
||||
*storageos
|
||||
|
||||
// The directory containing the StorageOS devices
|
||||
deviceDir string
|
||||
|
||||
// Interface used to mount the file or block device
|
||||
diskMounter *mount.SafeFormatAndMount
|
||||
mountOptions []string
|
||||
}
|
||||
|
||||
var _ volume.Mounter = &storageosMounter{}
|
||||
|
||||
func (b *storageosMounter) GetAttributes() volume.Attributes {
|
||||
return volume.Attributes{
|
||||
ReadOnly: b.readOnly,
|
||||
Managed: !b.readOnly,
|
||||
SELinuxRelabel: true,
|
||||
}
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *storageosMounter) SetUp(mounterArgs volume.MounterArgs) error {
|
||||
// Need a namespace to find the volume, try pod's namespace if not set.
|
||||
if b.volNamespace == "" {
|
||||
klog.V(2).Infof("Setting StorageOS volume namespace to pod namespace: %s", b.podNamespace)
|
||||
b.volNamespace = b.podNamespace
|
||||
}
|
||||
|
||||
targetPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
|
||||
|
||||
// Attach the device to the host.
|
||||
if err := b.manager.AttachDevice(b, targetPath); err != nil {
|
||||
klog.Errorf("Failed to attach device at %s: %s", targetPath, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// Attach the StorageOS volume as a block device
|
||||
devicePath, err := b.manager.AttachVolume(b)
|
||||
if err != nil {
|
||||
klog.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.
|
||||
err = b.manager.MountVolume(b, devicePath, targetPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
klog.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(), mounterArgs)
|
||||
}
|
||||
|
||||
// SetUp bind mounts the disk global mount to the give volume path.
|
||||
func (b *storageosMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
|
||||
notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
klog.V(4).Infof("StorageOS volume set up: %s %v %v", dir, !notMnt, err)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
klog.Errorf("Cannot validate mount point: %s %v", dir, err)
|
||||
return err
|
||||
}
|
||||
if !notMnt {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dir, 0750); err != nil {
|
||||
klog.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")
|
||||
}
|
||||
mountOptions := util.JoinMountOptions(b.mountOptions, options)
|
||||
|
||||
globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
|
||||
klog.V(4).Infof("Attempting to bind mount to pod volume at %s", dir)
|
||||
|
||||
err = b.mounter.MountSensitiveWithoutSystemd(globalPDPath, dir, "", mountOptions, nil)
|
||||
if err != nil {
|
||||
notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
if mntErr != nil {
|
||||
klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
if !notMnt {
|
||||
if mntErr = b.mounter.Unmount(dir); mntErr != nil {
|
||||
klog.Errorf("Failed to unmount: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
|
||||
if mntErr != nil {
|
||||
klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
|
||||
return err
|
||||
}
|
||||
if !notMnt {
|
||||
klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir)
|
||||
return err
|
||||
}
|
||||
}
|
||||
os.Remove(dir)
|
||||
klog.Errorf("Mount of disk %s failed: %v", dir, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !b.readOnly {
|
||||
volume.SetVolumeOwnership(b, mounterArgs.FsGroup, mounterArgs.FSGroupChangePolicy, util.FSGroupCompleteHook(b.plugin, nil))
|
||||
}
|
||||
klog.V(4).Infof("StorageOS volume setup complete on %s", dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeGlobalPDName(host volume.VolumeHost, pvName, volNamespace, volName string) string {
|
||||
return filepath.Join(host.GetPluginDir(utilstrings.EscapeQualifiedName(storageosPluginName)), util.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, utilstrings.EscapeQualifiedName(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 {
|
||||
klog.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 {
|
||||
klog.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 {
|
||||
klog.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 {
|
||||
klog.Errorf("Detach failed during unmount: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Detach loop device
|
||||
err = b.manager.DetachVolume(b, devicePath)
|
||||
if err != nil {
|
||||
klog.Errorf("Detach device %s failed for volume %s: %v", devicePath, b.pvName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
klog.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 := mount.CleanupMountPoint(dir, b.mounter, false); err != nil {
|
||||
klog.V(4).Infof("Unmounted StorageOS volume %s failed with: %v", b.pvName, err)
|
||||
}
|
||||
if err := b.manager.UnmountVolume(b); err != nil {
|
||||
klog.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(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
|
||||
if !util.ContainsAllAccessModes(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) {
|
||||
return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes())
|
||||
}
|
||||
if util.CheckPersistentVolumeClaimModeBlock(c.options.PVC) {
|
||||
return nil, fmt.Errorf("%s does not support block volume provisioning", c.plugin.GetPluginName())
|
||||
}
|
||||
|
||||
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)]
|
||||
var err error
|
||||
c.sizeGB, err = volumehelpers.RoundUpToGiBInt(capacity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 {
|
||||
klog.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{
|
||||
util.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,
|
||||
},
|
||||
},
|
||||
},
|
||||
MountOptions: c.options.MountOptions,
|
||||
},
|
||||
}
|
||||
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 {
|
||||
klog.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 {
|
||||
klog.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
|
||||
}
|
@ -1,384 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/mount-utils"
|
||||
"k8s.io/utils/exec/testing"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"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(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil))
|
||||
|
||||
plug, err := plugMgr.FindPluginByName("kubernetes.io/storageos")
|
||||
if err != nil {
|
||||
t.Fatal("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(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil))
|
||||
|
||||
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/storageos")
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
if !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteOnce) || !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadOnlyMany) {
|
||||
t.Errorf("Expected two AccessModeTypes: %s and %s", v1.ReadWriteOnce, v1.ReadOnlyMany)
|
||||
}
|
||||
}
|
||||
|
||||
type fakePDManager struct {
|
||||
api apiImplementer
|
||||
attachCalled bool
|
||||
attachDeviceCalled 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) AttachDevice(b *storageosMounter, dir string) error {
|
||||
fake.attachDeviceCalled = 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 (fake *fakePDManager) DeviceDir(mounter *storageosMounter) string {
|
||||
return defaultDeviceDir
|
||||
}
|
||||
|
||||
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(), nil /* prober */, volumetest.NewFakeVolumeHost(t, 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.CoreV1().Secrets("default").Create(context.TODO(), &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"),
|
||||
}}, metav1.CreateOptions{})
|
||||
|
||||
plug.(*storageosPlugin).host = volumetest.NewFakeVolumeHost(t, 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.NewFakeMounter(nil), &testingexec.FakeExec{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Fatalf("Got a nil Mounter")
|
||||
}
|
||||
|
||||
expectedPath := filepath.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(volume.MounterArgs{}); 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.attachDeviceCalled {
|
||||
t.Errorf("AttachDevice not called")
|
||||
}
|
||||
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.NewFakeMounter(nil), &testingexec.FakeExec{})
|
||||
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("TearDown() failed: %v", err)
|
||||
}
|
||||
|
||||
if !fakeManager.unmountCalled {
|
||||
t.Errorf("Unmount not called")
|
||||
}
|
||||
if !fakeManager.detachCalled {
|
||||
t.Errorf("Detach not called")
|
||||
}
|
||||
|
||||
// Test Provisioner
|
||||
fakeManager = &fakePDManager{}
|
||||
mountOptions := []string{"sync", "noatime"}
|
||||
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,
|
||||
"adminsecretnamespace": "default",
|
||||
},
|
||||
MountOptions: mountOptions,
|
||||
}
|
||||
provisioner, err := plug.(*storageosPlugin).newProvisionerInternal(options, fakeManager)
|
||||
if err != nil {
|
||||
t.Errorf("newProvisionerInternal() failed: %v", err)
|
||||
}
|
||||
|
||||
persistentSpec, err := provisioner.Provision(nil, nil)
|
||||
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 len(persistentSpec.Spec.MountOptions) != 2 {
|
||||
t.Errorf("Provision() returned unexpected volume mount options: %v", persistentSpec.Spec.MountOptions)
|
||||
}
|
||||
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 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(), nil /* prober */, volumetest.NewFakeVolumeHost(t, 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{}
|
||||
apiCfg := GetAPIConfig()
|
||||
mounter, err := plug.(*storageosPlugin).newMounterInternal(spec, pod, apiCfg, fakeManager, mount.NewFakeMounter(nil), &testingexec.FakeExec{})
|
||||
if err != nil {
|
||||
t.Fatalf("error creating a new internal mounter:%v", err)
|
||||
}
|
||||
if !mounter.GetAttributes().ReadOnly {
|
||||
t.Errorf("Expected true for mounter.IsReadOnly")
|
||||
}
|
||||
}
|
@ -1,436 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
storageosapi "github.com/storageos/go-api"
|
||||
storageostypes "github.com/storageos/go-api/types"
|
||||
"k8s.io/klog/v2"
|
||||
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
losetupPath = "losetup"
|
||||
|
||||
modeBlock deviceType = iota
|
||||
modeFile
|
||||
modeUnsupported
|
||||
|
||||
//ErrDeviceNotFound defines "device not found"
|
||||
ErrDeviceNotFound = "device not found"
|
||||
//ErrDeviceNotSupported defines "device not supported"
|
||||
ErrDeviceNotSupported = "device not supported"
|
||||
//ErrNotAvailable defines "not available"
|
||||
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
|
||||
Node(ref string) (*storageostypes.Node, error)
|
||||
}
|
||||
|
||||
// storageosUtil is the utility structure to interact with the StorageOS API.
|
||||
type storageosUtil struct {
|
||||
api apiImplementer
|
||||
host volume.VolumeHost
|
||||
}
|
||||
|
||||
func (u *storageosUtil) NewAPI(apiCfg *storageosAPIConfig) error {
|
||||
if u.api != nil {
|
||||
return nil
|
||||
}
|
||||
if u.host == nil {
|
||||
return errors.New("host must not be nil")
|
||||
}
|
||||
if apiCfg == nil {
|
||||
apiCfg = &storageosAPIConfig{
|
||||
apiAddr: defaultAPIAddress,
|
||||
apiUser: defaultAPIUser,
|
||||
apiPass: defaultAPIPassword,
|
||||
apiVersion: defaultAPIVersion,
|
||||
}
|
||||
klog.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)
|
||||
if err := api.SetDialContext(proxyutil.NewFilteredDialContext(api.GetDialContext(), nil, u.host.GetFilteredDialOptions())); err != nil {
|
||||
return fmt.Errorf("failed to set DialContext in storageos client: %v", err)
|
||||
}
|
||||
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) {
|
||||
|
||||
klog.V(4).Infof("creating StorageOS volume %q with namespace %q", p.volName, p.volNamespace)
|
||||
|
||||
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 {
|
||||
// don't log error details from client calls in events
|
||||
klog.V(4).Infof("volume create failed for volume %q (%v)", opts.Name, err)
|
||||
return nil, errors.New("volume create failed: see kube-controller-manager.log for details")
|
||||
}
|
||||
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) {
|
||||
|
||||
klog.V(4).Infof("attaching StorageOS volume %q with namespace %q", b.volName, b.volNamespace)
|
||||
|
||||
if err := u.NewAPI(b.apiCfg); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Get the node's device path from the API, falling back to the default if
|
||||
// not set on the node.
|
||||
if b.deviceDir == "" {
|
||||
b.deviceDir = u.DeviceDir(b)
|
||||
}
|
||||
|
||||
vol, err := u.api.Volume(b.volNamespace, b.volName)
|
||||
if err != nil {
|
||||
klog.Warningf("volume retrieve failed for volume %q with namespace %q (%v)", b.volName, b.volNamespace, err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
srcPath := filepath.Join(b.deviceDir, vol.ID)
|
||||
dt, err := pathDeviceType(srcPath)
|
||||
if err != nil {
|
||||
klog.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, b.exec)
|
||||
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 {
|
||||
|
||||
klog.V(4).Infof("detaching StorageOS volume %q with namespace %q", b.volName, b.volNamespace)
|
||||
|
||||
if !isLoopDevice(devicePath) {
|
||||
return nil
|
||||
}
|
||||
if _, err := os.Stat(devicePath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return removeLoopDevice(devicePath, b.exec)
|
||||
}
|
||||
|
||||
// AttachDevice attaches the volume device to the host at a given mount path.
|
||||
func (u *storageosUtil) AttachDevice(b *storageosMounter, deviceMountPath string) error {
|
||||
|
||||
klog.V(4).Infof("attaching StorageOS device for volume %q with namespace %q", b.volName, b.volNamespace)
|
||||
|
||||
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(),
|
||||
}
|
||||
if err := u.api.VolumeMount(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mount mounts the volume on the host.
|
||||
func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountPath string) error {
|
||||
|
||||
klog.V(4).Infof("mounting StorageOS volume %q with namespace %q", b.volName, b.volNamespace)
|
||||
|
||||
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 {
|
||||
klog.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
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmount removes the mount reference from the volume allowing it to be
|
||||
// re-mounted elsewhere.
|
||||
func (u *storageosUtil) UnmountVolume(b *storageosUnmounter) error {
|
||||
|
||||
klog.V(4).Infof("clearing StorageOS mount reference for volume %q with namespace %q", b.volName, b.volNamespace)
|
||||
|
||||
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.
|
||||
klog.Warningf("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,
|
||||
}
|
||||
if err := u.api.VolumeDelete(opts); err != nil {
|
||||
// don't log error details from client calls in events
|
||||
klog.V(4).Infof("volume deleted failed for volume %q in namespace %q: %v", d.volName, d.volNamespace, err)
|
||||
return errors.New("volume delete failed: see kube-controller-manager.log for details")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the node's device path from the API, falling back to the default if not
|
||||
// specified.
|
||||
func (u *storageosUtil) DeviceDir(b *storageosMounter) string {
|
||||
|
||||
ctrl, err := u.api.Node(b.plugin.host.GetHostName())
|
||||
if err != nil {
|
||||
klog.Warningf("node device path lookup failed: %v", err)
|
||||
return defaultDeviceDir
|
||||
}
|
||||
if ctrl == nil || ctrl.DeviceDir == "" {
|
||||
klog.Warningf("node device path not set, using default: %s", defaultDeviceDir)
|
||||
return defaultDeviceDir
|
||||
}
|
||||
return ctrl.DeviceDir
|
||||
}
|
||||
|
||||
// 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, exec utilexec.Interface) (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 == "" {
|
||||
klog.V(4).Infof("Creating device for path: %s", path)
|
||||
blockDevicePath, err = makeLoopDevice(path, exec)
|
||||
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)
|
||||
}
|
||||
|
||||
return getLoopDeviceFromSysfs(path)
|
||||
}
|
||||
|
||||
func makeLoopDevice(path string, exec utilexec.Interface) (string, error) {
|
||||
args := []string{"-f", "-P", path}
|
||||
out, err := exec.Command(losetupPath, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Failed device create command for path %s: %v %s", path, err, out)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return getLoopDeviceFromSysfs(path)
|
||||
}
|
||||
|
||||
func removeLoopDevice(device string, exec utilexec.Interface) error {
|
||||
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")
|
||||
}
|
||||
|
||||
// getLoopDeviceFromSysfs finds the backing file for a loop
|
||||
// device from sysfs via "/sys/block/loop*/loop/backing_file".
|
||||
func getLoopDeviceFromSysfs(path string) (string, error) {
|
||||
// If the file is a symlink.
|
||||
realPath, err := filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return "", errors.New(ErrDeviceNotFound)
|
||||
}
|
||||
|
||||
devices, err := filepath.Glob("/sys/block/loop*")
|
||||
if err != nil {
|
||||
return "", errors.New(ErrDeviceNotFound)
|
||||
}
|
||||
|
||||
for _, device := range devices {
|
||||
backingFile := fmt.Sprintf("%s/loop/backing_file", device)
|
||||
|
||||
// The contents of this file is the absolute path of "path".
|
||||
data, err := ioutil.ReadFile(backingFile)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Return the first match.
|
||||
backingFilePath := strings.TrimSpace(string(data))
|
||||
if backingFilePath == path || backingFilePath == realPath {
|
||||
return fmt.Sprintf("/dev/%s", filepath.Base(device)), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New(ErrDeviceNotFound)
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"testing"
|
||||
|
||||
storageostypes "github.com/storageos/go-api/types"
|
||||
"k8s.io/mount-utils"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
func GetAPIConfig() *storageosAPIConfig {
|
||||
return &storageosAPIConfig{
|
||||
apiAddr: "http://5.6.7.8:9999",
|
||||
apiUser: "abc",
|
||||
apiPass: "123",
|
||||
apiVersion: "10",
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("storageos_test")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpdir: %v", err)
|
||||
}
|
||||
util := storageosUtil{host: volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil)}
|
||||
err = util.NewAPI(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 (f fakeAPI) Node(ref string) (*storageostypes.Node, error) {
|
||||
return &storageostypes.Node{}, 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(), nil /* prober */, volumetest.NewFakeVolumeHost(t, 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 {
|
||||
var val string
|
||||
var ok bool
|
||||
for k, v := range labels {
|
||||
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)
|
||||
}
|
||||
}
|
||||
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(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil))
|
||||
plug, _ := plugMgr.FindPluginByName("kubernetes.io/storageos")
|
||||
|
||||
// Use real util with stubbed api
|
||||
util := &storageosUtil{}
|
||||
util.api = fakeAPI{}
|
||||
|
||||
_ = &storageosMounter{
|
||||
storageos: &storageos{
|
||||
volName: testVolName,
|
||||
volNamespace: testNamespace,
|
||||
manager: util,
|
||||
mounter: mount.NewFakeMounter(nil),
|
||||
plugin: plug.(*storageosPlugin),
|
||||
},
|
||||
deviceDir: tmpDir,
|
||||
}
|
||||
}
|
24
vendor/github.com/storageos/go-api/.gitignore
generated
vendored
24
vendor/github.com/storageos/go-api/.gitignore
generated
vendored
@ -1,24 +0,0 @@
|
||||
# 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
|
45
vendor/github.com/storageos/go-api/LICENCE
generated
vendored
45
vendor/github.com/storageos/go-api/LICENCE
generated
vendored
@ -1,45 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2018 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.
|
5
vendor/github.com/storageos/go-api/README.md
generated
vendored
5
vendor/github.com/storageos/go-api/README.md
generated
vendored
@ -1,5 +0,0 @@
|
||||
# StorageOS API client library
|
||||
|
||||
## Swagger Spec
|
||||
Swagger specification for this repo is available in the [StorageOS public documentation](https://github.com/storageos/storageos.github.io/blob/master/swagger.yaml).
|
||||
|
612
vendor/github.com/storageos/go-api/client.go
generated
vendored
612
vendor/github.com/storageos/go-api/client.go
generated
vendored
@ -1,612 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/storageos/go-api/netutil"
|
||||
"github.com/storageos/go-api/serror"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultUserAgent is the default User-Agent header to include in HTTP requests.
|
||||
DefaultUserAgent = "go-storageosclient"
|
||||
// DefaultVersionStr is the string value of the default API version.
|
||||
DefaultVersionStr = "1"
|
||||
// DefaultVersion is the default API version.
|
||||
DefaultVersion = 1
|
||||
)
|
||||
|
||||
var (
|
||||
// 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")
|
||||
|
||||
// ErrProxyNotSupported is returned when a client is unable to set a proxy for http requests.
|
||||
ErrProxyNotSupported = errors.New("client does not support http proxy")
|
||||
|
||||
// ErrDialerNotSupported is returned when a client is unable to set a DialContext for http requests.
|
||||
ErrDialerNotSupported = errors.New("client does not support setting DialContext")
|
||||
|
||||
// DefaultPort is the default API port.
|
||||
DefaultPort = "5705"
|
||||
|
||||
// DataplaneHealthPort is the the port used by the dataplane health-check service.
|
||||
DataplaneHealthPort = "5704"
|
||||
|
||||
// DefaultHost is the default API host.
|
||||
DefaultHost = "http://localhost:" + DefaultPort
|
||||
)
|
||||
|
||||
// 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 {
|
||||
httpClient *http.Client
|
||||
|
||||
addresses []string
|
||||
username string
|
||||
secret string
|
||||
userAgent string
|
||||
|
||||
configLock *sync.RWMutex // Lock for config changes
|
||||
addressLock *sync.Mutex // Lock used to copy/update the address slice
|
||||
|
||||
requestedAPIVersion APIVersion
|
||||
serverAPIVersion APIVersion
|
||||
expectedAPIVersion APIVersion
|
||||
|
||||
SkipServerVersionCheck bool
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
type dialContext = func(ctx context.Context, 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(nodes string) (*Client, error) {
|
||||
client, err := NewVersionedClient(nodes, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.SkipServerVersionCheck = true
|
||||
client.userAgent = DefaultUserAgent
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewVersionedClient returns a Client instance ready for communication with
|
||||
// the given server endpoint, using a specific remote API version.
|
||||
func NewVersionedClient(nodestring string, apiVersionString string) (*Client, error) {
|
||||
nodes := strings.Split(nodestring, ",")
|
||||
addresses, err := netutil.AddressesFromNodes(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(addresses) > 1 {
|
||||
// Shuffle returned addresses in attempt to spread the load
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
rnd.Shuffle(len(addresses), func(i, j int) {
|
||||
addresses[i], addresses[j] = addresses[j], addresses[i]
|
||||
})
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
httpClient: defaultClient(),
|
||||
addresses: addresses,
|
||||
configLock: &sync.RWMutex{},
|
||||
addressLock: &sync.Mutex{},
|
||||
}
|
||||
|
||||
if apiVersionString != "" {
|
||||
version, err := strconv.Atoi(apiVersionString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.requestedAPIVersion = APIVersion(version)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// SetUserAgent sets the client useragent.
|
||||
func (c *Client) SetUserAgent(useragent string) {
|
||||
c.configLock.Lock()
|
||||
defer c.configLock.Unlock()
|
||||
c.userAgent = useragent
|
||||
}
|
||||
|
||||
// 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) {
|
||||
c.configLock.Lock()
|
||||
defer c.configLock.Unlock()
|
||||
if username != "" {
|
||||
c.username = username
|
||||
}
|
||||
if secret != "" {
|
||||
c.secret = secret
|
||||
}
|
||||
}
|
||||
|
||||
// SetProxy will set the proxy URL for both the HTTPClient.
|
||||
// If the transport method does not support usage
|
||||
// of proxies, an error will be returned.
|
||||
func (c *Client) SetProxy(proxy *url.URL) error {
|
||||
c.configLock.Lock()
|
||||
defer c.configLock.Unlock()
|
||||
|
||||
if client := c.httpClient; client != nil {
|
||||
transport, supported := client.Transport.(*http.Transport)
|
||||
if !supported {
|
||||
return ErrProxyNotSupported
|
||||
}
|
||||
transport.Proxy = http.ProxyURL(proxy)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
c.configLock.Lock()
|
||||
defer c.configLock.Unlock()
|
||||
if c.httpClient != nil {
|
||||
c.httpClient.Timeout = t
|
||||
}
|
||||
}
|
||||
|
||||
// GetDialContext returns the current DialContext function, or nil if there is none.
|
||||
func (c *Client) GetDialContext() dialContext {
|
||||
c.configLock.RLock()
|
||||
defer c.configLock.RUnlock()
|
||||
|
||||
if c.httpClient == nil {
|
||||
return nil
|
||||
}
|
||||
transport, supported := c.httpClient.Transport.(*http.Transport)
|
||||
if !supported {
|
||||
return nil
|
||||
}
|
||||
return transport.DialContext
|
||||
}
|
||||
|
||||
// SetDialContext uses the given dial function to establish TCP connections in the HTTPClient.
|
||||
func (c *Client) SetDialContext(dial dialContext) error {
|
||||
c.configLock.Lock()
|
||||
defer c.configLock.Unlock()
|
||||
|
||||
if client := c.httpClient; client != nil {
|
||||
transport, supported := client.Transport.(*http.Transport)
|
||||
if !supported {
|
||||
return ErrDialerNotSupported
|
||||
}
|
||||
transport.DialContext = dial
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) checkAPIVersion() error {
|
||||
serverAPIVersionString, err := c.getServerAPIVersionString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.serverAPIVersion, err = NewAPIVersion(serverAPIVersionString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.configLock.Lock()
|
||||
defer c.configLock.Unlock()
|
||||
if c.requestedAPIVersion == 0 {
|
||||
c.expectedAPIVersion = c.serverAPIVersion
|
||||
} else {
|
||||
c.expectedAPIVersion = c.requestedAPIVersion
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
return resp.Body.Close()
|
||||
}
|
||||
|
||||
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 {
|
||||
context context.Context
|
||||
data interface{}
|
||||
|
||||
values url.Values
|
||||
headers map[string]string
|
||||
|
||||
fieldSelector string
|
||||
labelSelector string
|
||||
namespace string
|
||||
|
||||
forceJSON bool
|
||||
force bool
|
||||
unversioned bool
|
||||
|
||||
retryOn []int // http.status codes
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// Obtain a reader lock to prevent the http client from being
|
||||
// modified underneath us during a do().
|
||||
c.configLock.RLock()
|
||||
defer c.configLock.RUnlock() // This defer matches both the initial and the above lock
|
||||
|
||||
httpClient := c.httpClient
|
||||
endpoint := c.getAPIPath(urlpath, query, doOptions.unversioned)
|
||||
|
||||
// The doOptions Context is shared for every attempted request in the do.
|
||||
ctx := doOptions.context
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
var failedAddresses = map[string]struct{}{}
|
||||
|
||||
c.addressLock.Lock()
|
||||
var addresses = make([]string, len(c.addresses))
|
||||
copy(addresses, c.addresses)
|
||||
c.addressLock.Unlock()
|
||||
|
||||
for _, address := range addresses {
|
||||
target := address + endpoint
|
||||
|
||||
req, err := http.NewRequest(method, target, params)
|
||||
if err != nil {
|
||||
// Probably should not try and continue if we're unable
|
||||
// to create the request.
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("User-Agent", c.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)
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
|
||||
// If it is a custom error, return it. It probably knows more than us
|
||||
if serror.IsStorageOSError(err) {
|
||||
switch serror.ErrorKind(err) {
|
||||
case serror.APIUncontactable:
|
||||
// If API isn't contactable we should try the next address
|
||||
failedAddresses[address] = struct{}{}
|
||||
continue
|
||||
case serror.InvalidHostConfig:
|
||||
// If invalid host or unknown error, we should report back
|
||||
fallthrough
|
||||
case serror.UnknownError:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
if _, ok := err.(net.Error); ok {
|
||||
// Be optimistic and try the next endpoint
|
||||
failedAddresses[address] = struct{}{}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var shouldretry bool
|
||||
if doOptions.retryOn != nil {
|
||||
for _, code := range doOptions.retryOn {
|
||||
if resp.StatusCode == code {
|
||||
failedAddresses[address] = struct{}{}
|
||||
shouldretry = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If we get to the point of response, we should move any failed
|
||||
// addresses to the back.
|
||||
failed := len(failedAddresses)
|
||||
if failed > 0 {
|
||||
// Copy addresses we think are okay into the head of the list
|
||||
newOrder := make([]string, 0, len(addresses)-failed)
|
||||
|
||||
for _, addr := range addresses {
|
||||
if _, exists := failedAddresses[addr]; !exists {
|
||||
newOrder = append(newOrder, addr)
|
||||
}
|
||||
}
|
||||
for addr := range failedAddresses {
|
||||
newOrder = append(newOrder, addr)
|
||||
}
|
||||
|
||||
c.addressLock.Lock()
|
||||
// Bring in the new order
|
||||
c.addresses = newOrder
|
||||
c.addressLock.Unlock()
|
||||
}
|
||||
|
||||
if shouldretry {
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
||||
return nil, newError(resp) // These status codes are likely to be fatal
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
return nil, netutil.ErrAllFailed(addresses)
|
||||
}
|
||||
|
||||
func (c *Client) getAPIPath(path string, query url.Values, unversioned bool) string {
|
||||
var apiPath = strings.TrimLeft(path, "/")
|
||||
|
||||
if !unversioned {
|
||||
apiPath = fmt.Sprintf("/%s/%s", c.requestedAPIVersion, apiPath)
|
||||
} else {
|
||||
apiPath = fmt.Sprintf("/%s", apiPath)
|
||||
}
|
||||
|
||||
if len(query) > 0 {
|
||||
apiPath = apiPath + "?" + query.Encode()
|
||||
}
|
||||
|
||||
return apiPath
|
||||
}
|
||||
|
||||
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 {
|
||||
type jsonError struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
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)}
|
||||
}
|
||||
|
||||
// attempt to unmarshal the error if in json format
|
||||
jerr := &jsonError{}
|
||||
err = json.Unmarshal(data, jerr)
|
||||
if err != nil {
|
||||
return &Error{Status: resp.StatusCode, Message: string(data)} // Failed, just return string
|
||||
}
|
||||
|
||||
return &Error{Status: resp.StatusCode, Message: jerr.Message}
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
var niceStatus string
|
||||
|
||||
switch e.Status {
|
||||
case 400, 500:
|
||||
niceStatus = "Server failed to process your request. Was the data correct?"
|
||||
case 401:
|
||||
niceStatus = "Unauthenticated access of secure endpoint, please retry after authentication"
|
||||
case 403:
|
||||
niceStatus = "Forbidden request. Your user cannot perform this action"
|
||||
case 404:
|
||||
niceStatus = "Requested object not found. Does this item exist?"
|
||||
}
|
||||
|
||||
if niceStatus != "" {
|
||||
return fmt.Sprintf("API error (%s): %s", niceStatus, e.Message)
|
||||
}
|
||||
return fmt.Sprintf("API error (%s): %s", http.StatusText(e.Status), e.Message)
|
||||
}
|
||||
|
||||
// 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(dialer Dialer) *http.Transport {
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: dialer.Dial,
|
||||
TLSHandshakeTimeout: 5 * 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.
|
||||
// If a custom dialer is not provided, one with sane defaults will be created.
|
||||
func defaultClient() *http.Client {
|
||||
dialer := &net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 5 * time.Second,
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Transport: defaultPooledTransport(dialer),
|
||||
}
|
||||
}
|
48
vendor/github.com/storageos/go-api/cluster.go
generated
vendored
48
vendor/github.com/storageos/go-api/cluster.go
generated
vendored
@ -1,48 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// ClusterMaintenanceAPIPrefix is a path to the HTTP endpoint for managing
|
||||
// the cluster maintenance mode.
|
||||
ClusterMaintenanceAPIPrefix = "cluster/maintenance"
|
||||
)
|
||||
|
||||
// Maintenance returns the maintenance status of the cluster
|
||||
func (c *Client) Maintenance() (*types.Maintenance, error) {
|
||||
resp, err := c.do("GET", ClusterMaintenanceAPIPrefix, doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
res := &types.Maintenance{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// EnableMaintenance enables maintenance mode in the cluster
|
||||
func (c *Client) EnableMaintenance() error {
|
||||
resp, err := c.do("POST", ClusterMaintenanceAPIPrefix, doOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableMaintenance disables maintenance mode in the cluster
|
||||
func (c *Client) DisableMaintenance() error {
|
||||
resp, err := c.do("DELETE", ClusterMaintenanceAPIPrefix, doOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
91
vendor/github.com/storageos/go-api/health.go
generated
vendored
91
vendor/github.com/storageos/go-api/health.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// HealthAPIPrefix is a partial path to the HTTP endpoint.
|
||||
HealthAPIPrefix = "health"
|
||||
)
|
||||
|
||||
func (c *Client) ClusterHealth(ctx context.Context) ([]*types.ClusterHealthNode, error) {
|
||||
status := []*types.ClusterHealthNode{}
|
||||
url := fmt.Sprintf("/cluster/%s", HealthAPIPrefix)
|
||||
|
||||
resp, err := c.do("GET", url, doOptions{context: ctx, retryOn: []int{http.StatusNotFound}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// CPHealth returns the health of the control plane server at a given url.
|
||||
func (c *Client) CPHealth(ctx context.Context, hostname string) (*types.CPHealthStatus, error) {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/v1/%s", hostname, DefaultPort, HealthAPIPrefix)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", c.userAgent)
|
||||
if c.username != "" && c.secret != "" {
|
||||
req.SetBasicAuth(c.username, c.secret)
|
||||
}
|
||||
|
||||
c.configLock.RLock()
|
||||
resp, err := c.httpClient.Do(req.WithContext(ctx))
|
||||
c.configLock.RUnlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status *types.CPHealthStatus
|
||||
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// DPHealth returns the health of the data plane server at a given url.
|
||||
func (c *Client) DPHealth(ctx context.Context, hostname string) (*types.DPHealthStatus, error) {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/v1/%s", hostname, DataplaneHealthPort, HealthAPIPrefix)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", c.userAgent)
|
||||
if c.username != "" && c.secret != "" {
|
||||
req.SetBasicAuth(c.username, c.secret)
|
||||
}
|
||||
|
||||
c.configLock.RLock()
|
||||
resp, err := c.httpClient.Do(req.WithContext(ctx))
|
||||
c.configLock.RUnlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status *types.DPHealthStatus
|
||||
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
43
vendor/github.com/storageos/go-api/licence.go
generated
vendored
43
vendor/github.com/storageos/go-api/licence.go
generated
vendored
@ -1,43 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// licenceAPIPrefix is a partial path to the HTTP endpoint.
|
||||
licenceAPIPrefix = "licencing"
|
||||
)
|
||||
|
||||
// Licence returns the current licence on the server.
|
||||
func (c *Client) Licence() (*types.Licence, error) {
|
||||
resp, err := c.do("GET", licenceAPIPrefix, doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
licence := &types.Licence{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&licence); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return licence, nil
|
||||
}
|
||||
|
||||
// LicenceApply applies a licence on the server.
|
||||
func (c *Client) LicenceApply(licenceKey string) error {
|
||||
_, err := c.do("POST", licenceAPIPrefix, doOptions{
|
||||
data: &types.LicenceKeyContainer{Key: licenceKey},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// LicenceDelete removes the current licence.
|
||||
func (c *Client) LicenceDelete() error {
|
||||
resp, err := c.do("DELETE", licenceAPIPrefix, doOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return resp.Body.Close()
|
||||
}
|
62
vendor/github.com/storageos/go-api/logger.go
generated
vendored
62
vendor/github.com/storageos/go-api/logger.go
generated
vendored
@ -1,62 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// LoggerAPIPrefix is a partial path to the HTTP endpoint.
|
||||
LoggerAPIPrefix = "logs"
|
||||
)
|
||||
|
||||
// LoggerConfig returns every cluster node's logging configuration.
|
||||
func (c *Client) LoggerConfig(opts types.ListOptions) ([]*types.Logger, error) {
|
||||
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", LoggerAPIPrefix+"/cluster/config", listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var loggers []*types.Logger
|
||||
if err := json.NewDecoder(resp.Body).Decode(&loggers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loggers, nil
|
||||
|
||||
}
|
||||
|
||||
// LoggerUpdate patches updates to logging configuration. Fields to update must
|
||||
// be listed in the Fields value, and if a list of Nodes is given it will only
|
||||
// apply to the nodes listed. Returns the updated configuration.
|
||||
func (c *Client) LoggerUpdate(opts types.LoggerUpdateOptions) ([]*types.Logger, error) {
|
||||
|
||||
resp, err := c.do("PATCH", LoggerAPIPrefix+"/cluster/config", doOptions{
|
||||
data: opts,
|
||||
context: context.Background(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var loggers []*types.Logger
|
||||
if err := json.NewDecoder(resp.Body).Decode(&loggers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loggers, nil
|
||||
}
|
47
vendor/github.com/storageos/go-api/login.go
generated
vendored
47
vendor/github.com/storageos/go-api/login.go
generated
vendored
@ -1,47 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// LoginAPIPrefix is a partial path to the HTTP endpoint.
|
||||
LoginAPIPrefix = "auth/login"
|
||||
// ErrLoginFailed is the error returned on an unsuccessful login.
|
||||
ErrLoginFailed = errors.New("failed to get token from API endpoint")
|
||||
)
|
||||
|
||||
// Login attemps to get a token from the API
|
||||
func (c *Client) Login() (token string, err error) {
|
||||
resp, err := c.do("POST", LoginAPIPrefix, doOptions{data: struct {
|
||||
User string `json:"username"`
|
||||
Pass string `json:"password"`
|
||||
}{c.username, c.secret}})
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*Error); ok {
|
||||
return "", ErrLoginFailed
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", ErrLoginFailed
|
||||
}
|
||||
|
||||
unmarsh := struct {
|
||||
Token string `json:"token"`
|
||||
}{}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&unmarsh); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if unmarsh.Token == "" {
|
||||
return "", ErrLoginFailed
|
||||
}
|
||||
|
||||
return unmarsh.Token, nil
|
||||
}
|
125
vendor/github.com/storageos/go-api/namespace.go
generated
vendored
125
vendor/github.com/storageos/go-api/namespace.go
generated
vendored
@ -1,125 +0,0 @@
|
||||
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
|
||||
}
|
31
vendor/github.com/storageos/go-api/netutil/errors.go
generated
vendored
31
vendor/github.com/storageos/go-api/netutil/errors.go
generated
vendored
@ -1,31 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/storageos/go-api/serror"
|
||||
)
|
||||
|
||||
// ErrAllFailed produces a typed StorageOS error which should be used to indicate that
|
||||
// the API is not contactable for all of the supplied node addresses.
|
||||
func ErrAllFailed(addrs []string) error {
|
||||
msg := fmt.Sprintf("failed to dial all known cluster members, (%s)", strings.Join(addrs, ","))
|
||||
help := "ensure that the value of $STORAGEOS_HOST (or the -H flag) is correct, and that there are healthy StorageOS nodes in this cluster"
|
||||
|
||||
return serror.NewTypedStorageOSError(serror.APIUncontactable, nil, msg, help)
|
||||
}
|
||||
|
||||
func newInvalidNodeError(err error) error {
|
||||
msg := fmt.Sprintf("invalid node format: %s", err)
|
||||
help := "please check the format of $STORAGEOS_HOST (or the -H flag) complies with the StorageOS JOIN format"
|
||||
|
||||
return serror.NewTypedStorageOSError(serror.InvalidHostConfig, err, msg, help)
|
||||
}
|
||||
|
||||
var (
|
||||
errUnsupportedScheme = errors.New("unsupported URL scheme")
|
||||
errInvalidHostName = errors.New("invalid hostname")
|
||||
errInvalidPortNumber = errors.New("invalid port number")
|
||||
)
|
5
vendor/github.com/storageos/go-api/netutil/netutil.go
generated
vendored
5
vendor/github.com/storageos/go-api/netutil/netutil.go
generated
vendored
@ -1,5 +0,0 @@
|
||||
// Package netutil provides network related errors and helper functions.
|
||||
package netutil
|
||||
|
||||
// DefaultDialPort is the default port which the API is contacted on.
|
||||
const DefaultDialPort = "5705"
|
84
vendor/github.com/storageos/go-api/netutil/parsers.go
generated
vendored
84
vendor/github.com/storageos/go-api/netutil/parsers.go
generated
vendored
@ -1,84 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
httpScheme = "http"
|
||||
httpsScheme = "https"
|
||||
tcpScheme = "tcp"
|
||||
)
|
||||
|
||||
// AddressesFromNodes takes a list of node hosts and attempts to return a list of hosts in host:port
|
||||
// format along with any error encountered.
|
||||
//
|
||||
// The function accepts node hosts in URL, ip, ip:port, resolvable-name and resolvable-name:port
|
||||
// formats and will append the default port value if needed. For hosts where the scheme has been omitted,
|
||||
// the scheme for the first host will be used. If the first host has no scheme, it will default to http.
|
||||
func AddressesFromNodes(nodes []string) ([]string, error) {
|
||||
var addresses []string
|
||||
|
||||
var scheme string
|
||||
|
||||
for _, node := range nodes {
|
||||
address := node
|
||||
// If no scheme present, set the first scheme
|
||||
if !strings.Contains(address, "://") {
|
||||
if scheme == "" {
|
||||
scheme = httpScheme
|
||||
}
|
||||
address = strings.Join([]string{scheme, address}, "://")
|
||||
}
|
||||
|
||||
url, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return nil, newInvalidNodeError(err)
|
||||
}
|
||||
|
||||
switch url.Scheme {
|
||||
case tcpScheme:
|
||||
url.Scheme = httpScheme
|
||||
fallthrough
|
||||
case httpScheme, httpsScheme:
|
||||
if scheme == "" {
|
||||
scheme = url.Scheme
|
||||
}
|
||||
default:
|
||||
return nil, newInvalidNodeError(errUnsupportedScheme)
|
||||
}
|
||||
|
||||
host := url.Hostname()
|
||||
if host == "" {
|
||||
return nil, newInvalidNodeError(errInvalidHostName)
|
||||
}
|
||||
|
||||
// Given input like "http://localhost:8080:8383", url.Parse() will
|
||||
// return host as "localhost:8000", which isn't a vaild DNS name.
|
||||
if strings.Contains(host, ":") {
|
||||
return nil, newInvalidNodeError(errInvalidHostName)
|
||||
}
|
||||
|
||||
port := url.Port()
|
||||
if port == "" {
|
||||
port = DefaultDialPort
|
||||
}
|
||||
if !validPort(port) {
|
||||
return nil, newInvalidNodeError(errInvalidPortNumber)
|
||||
}
|
||||
|
||||
addresses = append(addresses, strings.TrimRight(url.String(), "/"))
|
||||
}
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func validPort(port string) bool {
|
||||
intPort, err := strconv.Atoi(port)
|
||||
|
||||
return (err == nil) &&
|
||||
(intPort > 0) &&
|
||||
(intPort <= 65535)
|
||||
}
|
35
vendor/github.com/storageos/go-api/network_diagnostics.go
generated
vendored
35
vendor/github.com/storageos/go-api/network_diagnostics.go
generated
vendored
@ -1,35 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// NetworkDiagnosticsAPIPrefix is a partial path to the HTTP endpoint for
|
||||
// the node connectivity diagnostics report.
|
||||
NetworkDiagnosticsAPIPrefix = "diagnostics/network"
|
||||
)
|
||||
|
||||
// NetworkDiagnostics returns a collection of network connectivity reports. If
|
||||
// a reference to a node is given, it will only check connectivity from that
|
||||
// node. Otherwise, connectivity between all cluster nodes will be returned.
|
||||
func (c *Client) NetworkDiagnostics(ref string) (types.ConnectivityResults, error) {
|
||||
resp, err := c.do("GET", path.Join(NetworkDiagnosticsAPIPrefix, ref), doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchNode
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var results types.ConnectivityResults
|
||||
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
110
vendor/github.com/storageos/go-api/node.go
generated
vendored
110
vendor/github.com/storageos/go-api/node.go
generated
vendored
@ -1,110 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// NodeAPIPrefix is a partial path to the HTTP endpoint.
|
||||
NodeAPIPrefix = "nodes"
|
||||
|
||||
// ErrNoSuchNode is the error returned when the node does not exist.
|
||||
ErrNoSuchNode = errors.New("no such node")
|
||||
|
||||
// ErrNodeInUse is the error returned when the node requested to be removed is still in use.
|
||||
ErrNodeInUse = errors.New("node in use and cannot be removed")
|
||||
)
|
||||
|
||||
// NodeList returns the list of available nodes.
|
||||
func (c *Client) NodeList(opts types.ListOptions) ([]*types.Node, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", NodeAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var nodes []*types.Node
|
||||
if err := json.NewDecoder(resp.Body).Decode(&nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// Node returns a node by its reference.
|
||||
func (c *Client) Node(ref string) (*types.Node, error) {
|
||||
|
||||
resp, err := c.do("GET", NodeAPIPrefix+"/"+ref, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchNode
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var node types.Node
|
||||
if err := json.NewDecoder(resp.Body).Decode(&node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &node, nil
|
||||
}
|
||||
|
||||
// NodeUpdate updates a node on the server.
|
||||
func (c *Client) NodeUpdate(opts types.NodeUpdateOptions) (*types.Node, error) {
|
||||
ref := opts.Name
|
||||
if IsUUID(opts.ID) {
|
||||
ref = opts.ID
|
||||
}
|
||||
resp, err := c.do("PUT", NodeAPIPrefix+"/"+ref, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var node types.Node
|
||||
if err := json.NewDecoder(resp.Body).Decode(&node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &node, nil
|
||||
}
|
||||
|
||||
// NodeDelete removes a node by its reference.
|
||||
func (c *Client) NodeDelete(opts types.DeleteOptions) error {
|
||||
deleteOpts := doOptions{
|
||||
namespace: opts.Namespace,
|
||||
force: opts.Force,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("DELETE", NodeAPIPrefix+"/"+opts.Name, deleteOpts)
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchNode
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrNodeInUse
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
102
vendor/github.com/storageos/go-api/policy.go
generated
vendored
102
vendor/github.com/storageos/go-api/policy.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// PolicyAPIPrefix is a partial path to the HTTP endpoint.
|
||||
PolicyAPIPrefix = "policies"
|
||||
// ErrNoSuchPolicy is the error returned when the policy does not exist.
|
||||
ErrNoSuchPolicy = errors.New("no such policy")
|
||||
)
|
||||
|
||||
// nopMarshaler is an alias to a []byte that implements json.Marshaler
|
||||
// it bypasses the base64 encoded string representation that json will give byte slices.
|
||||
// It should only be used to wrap []byte types containing pre-rendered valid json that will later
|
||||
// (out of the caller's control) be run through json.Marshal
|
||||
type nopMarshaler []byte
|
||||
|
||||
func (n *nopMarshaler) MarshalJSON() ([]byte, error) {
|
||||
return *n, nil
|
||||
}
|
||||
|
||||
// PolicyCreate creates a policy on the server.
|
||||
func (c *Client) PolicyCreate(ctx context.Context, jsonl []byte) error {
|
||||
nopm := nopMarshaler(jsonl)
|
||||
_, err := c.do("POST", PolicyAPIPrefix, doOptions{
|
||||
data: &nopm,
|
||||
context: ctx,
|
||||
headers: map[string]string{"Content-Type": "application/x-jsonlines"},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Policy returns a policy on the server by ID.
|
||||
func (c *Client) Policy(id string) (*types.Policy, error) {
|
||||
path := fmt.Sprintf("%s/%s", PolicyAPIPrefix, id)
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchPolicy
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var policy *types.Policy
|
||||
if err := json.NewDecoder(resp.Body).Decode(&policy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// PolicyList returns the list of policies on the server.
|
||||
func (c *Client) PolicyList(opts types.ListOptions) (types.PolicySet, 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", PolicyAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var policies types.PolicySet
|
||||
if err := json.NewDecoder(resp.Body).Decode(&policies); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// PolicyDelete deletes a policy on the server by ID.
|
||||
func (c *Client) PolicyDelete(opts types.DeleteOptions) error {
|
||||
resp, err := c.do("DELETE", PolicyAPIPrefix+"/"+opts.Name, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchPolicy
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
117
vendor/github.com/storageos/go-api/pool.go
generated
vendored
117
vendor/github.com/storageos/go-api/pool.go
generated
vendored
@ -1,117 +0,0 @@
|
||||
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.PoolOptions) (*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
|
||||
}
|
||||
|
||||
// PoolUpdate - update pool
|
||||
func (c *Client) PoolUpdate(opts types.PoolOptions) (*types.Pool, error) {
|
||||
ref := opts.Name
|
||||
if IsUUID(opts.ID) {
|
||||
ref = opts.ID
|
||||
}
|
||||
|
||||
resp, err := c.do("PUT", PoolAPIPrefix+"/"+ref, 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 err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
141
vendor/github.com/storageos/go-api/rule.go
generated
vendored
141
vendor/github.com/storageos/go-api/rule.go
generated
vendored
@ -1,141 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"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) {
|
||||
path, err := namespacedPath(opts.Namespace, RuleAPIPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.do("POST", path, 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
|
||||
}
|
||||
|
||||
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 err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
11
vendor/github.com/storageos/go-api/serror/error_kind.go
generated
vendored
11
vendor/github.com/storageos/go-api/serror/error_kind.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
package serror
|
||||
|
||||
//go:generate stringer -type=StorageOSErrorKind error_kind.go
|
||||
type StorageOSErrorKind int
|
||||
|
||||
// Known error kinds
|
||||
const (
|
||||
UnknownError StorageOSErrorKind = iota
|
||||
APIUncontactable
|
||||
InvalidHostConfig
|
||||
)
|
37
vendor/github.com/storageos/go-api/serror/kind_lookup_map.go
generated
vendored
37
vendor/github.com/storageos/go-api/serror/kind_lookup_map.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
package serror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var kindLookupMap map[string]StorageOSErrorKind
|
||||
|
||||
func init() {
|
||||
kindLookupMap = make(map[string]StorageOSErrorKind)
|
||||
|
||||
// Populate the lookup map with all the known constants
|
||||
for i := StorageOSErrorKind(0); !strings.HasPrefix(i.String(), "StorageOSErrorKind("); i++ {
|
||||
kindLookupMap[i.String()] = i
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StorageOSErrorKind) UnmarshalJSON(b []byte) error {
|
||||
str := ""
|
||||
if err := json.Unmarshal(b, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, ok := kindLookupMap[str]
|
||||
if !ok {
|
||||
return fmt.Errorf("Failed to unmarshal ErrorKind %s", s)
|
||||
}
|
||||
|
||||
*s = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StorageOSErrorKind) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(s.String())
|
||||
}
|
34
vendor/github.com/storageos/go-api/serror/storageos_error.go
generated
vendored
34
vendor/github.com/storageos/go-api/serror/storageos_error.go
generated
vendored
@ -1,34 +0,0 @@
|
||||
package serror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type StorageOSError interface {
|
||||
// embedding error provides compatibility with standard error handling code
|
||||
error
|
||||
|
||||
// Encoding/decoding methods to help errors traverse API boundaries
|
||||
json.Marshaler
|
||||
json.Unmarshaler
|
||||
|
||||
Err() error // Returns the underlying error that caused this event
|
||||
String() string // A short string representing the error (for logging etc)
|
||||
Help() string // A larger string that should provide informative debug instruction to users
|
||||
Kind() StorageOSErrorKind // A type representing a set of known error conditions, helpful to switch on
|
||||
Extra() map[string]string // A container for error specific information
|
||||
|
||||
// TODO: should we include callstack traces here? We could have a debug mode for it.
|
||||
}
|
||||
|
||||
func ErrorKind(err error) StorageOSErrorKind {
|
||||
if serr, ok := err.(StorageOSError); ok {
|
||||
return serr.Kind()
|
||||
}
|
||||
return UnknownError
|
||||
}
|
||||
|
||||
func IsStorageOSError(err error) bool {
|
||||
_, ok := err.(StorageOSError)
|
||||
return ok
|
||||
}
|
16
vendor/github.com/storageos/go-api/serror/storageoserrorkind_string.go
generated
vendored
16
vendor/github.com/storageos/go-api/serror/storageoserrorkind_string.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
// Code generated by "stringer -type=StorageOSErrorKind error_kind.go"; DO NOT EDIT.
|
||||
|
||||
package serror
|
||||
|
||||
import "strconv"
|
||||
|
||||
const _StorageOSErrorKind_name = "UnknownErrorAPIUncontactableInvalidHostConfig"
|
||||
|
||||
var _StorageOSErrorKind_index = [...]uint8{0, 12, 28, 45}
|
||||
|
||||
func (i StorageOSErrorKind) String() string {
|
||||
if i < 0 || i >= StorageOSErrorKind(len(_StorageOSErrorKind_index)-1) {
|
||||
return "StorageOSErrorKind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _StorageOSErrorKind_name[_StorageOSErrorKind_index[i]:_StorageOSErrorKind_index[i+1]]
|
||||
}
|
64
vendor/github.com/storageos/go-api/serror/typed_error.go
generated
vendored
64
vendor/github.com/storageos/go-api/serror/typed_error.go
generated
vendored
@ -1,64 +0,0 @@
|
||||
package serror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func NewTypedStorageOSError(kind StorageOSErrorKind, err error, msg string, help string) StorageOSError {
|
||||
return &typedStorageOSError{
|
||||
internal: &internal_TypedStorageOSError{
|
||||
ErrorKind: &kind,
|
||||
Cause: err,
|
||||
ErrMessage: msg,
|
||||
HelpMessage: help,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewUntypedStorageOSError(err error, msg string, help string) StorageOSError {
|
||||
var kind StorageOSErrorKind = UnknownError
|
||||
|
||||
return &typedStorageOSError{
|
||||
internal: &internal_TypedStorageOSError{
|
||||
ErrorKind: &kind,
|
||||
Cause: err,
|
||||
ErrMessage: msg,
|
||||
HelpMessage: help,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type internal_TypedStorageOSError struct {
|
||||
ErrorKind *StorageOSErrorKind `json:"error_kind"`
|
||||
Cause error `json:"caused_by"`
|
||||
ErrMessage string `json:"error_message"`
|
||||
HelpMessage string `json:"help_message"`
|
||||
ExtraMap map[string]string `json:"extra"`
|
||||
}
|
||||
|
||||
type typedStorageOSError struct {
|
||||
internal *internal_TypedStorageOSError
|
||||
}
|
||||
|
||||
func (t *typedStorageOSError) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.internal)
|
||||
}
|
||||
|
||||
func (t *typedStorageOSError) UnmarshalJSON(d []byte) error {
|
||||
internal := &internal_TypedStorageOSError{}
|
||||
|
||||
err := json.Unmarshal(d, internal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.internal = internal
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *typedStorageOSError) Error() string { return t.String() }
|
||||
func (t *typedStorageOSError) Err() error { return t.internal.Cause }
|
||||
func (t *typedStorageOSError) String() string { return t.internal.ErrMessage }
|
||||
func (t *typedStorageOSError) Help() string { return t.internal.HelpMessage }
|
||||
func (t *typedStorageOSError) Kind() StorageOSErrorKind { return *t.internal.ErrorKind }
|
||||
func (t *typedStorageOSError) Extra() map[string]string { return t.internal.ExtraMap }
|
28
vendor/github.com/storageos/go-api/server_version.go
generated
vendored
28
vendor/github.com/storageos/go-api/server_version.go
generated
vendored
@ -1,28 +0,0 @@
|
||||
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
|
||||
}
|
90
vendor/github.com/storageos/go-api/template.go
generated
vendored
90
vendor/github.com/storageos/go-api/template.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
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
|
||||
}
|
14
vendor/github.com/storageos/go-api/types/auth.go
generated
vendored
14
vendor/github.com/storageos/go-api/types/auth.go
generated
vendored
@ -1,14 +0,0 @@
|
||||
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"`
|
||||
}
|
25
vendor/github.com/storageos/go-api/types/capacity_stats.go
generated
vendored
25
vendor/github.com/storageos/go-api/types/capacity_stats.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
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
|
||||
}
|
10
vendor/github.com/storageos/go-api/types/cluster.go
generated
vendored
10
vendor/github.com/storageos/go-api/types/cluster.go
generated
vendored
@ -1,10 +0,0 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// Maintenance is used to place the cluster in maintenance mode.
|
||||
type Maintenance struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
41
vendor/github.com/storageos/go-api/types/connectivity.go
generated
vendored
41
vendor/github.com/storageos/go-api/types/connectivity.go
generated
vendored
@ -1,41 +0,0 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// ConnectivityResult capture's a node connectivity report to a given target.
|
||||
type ConnectivityResult struct {
|
||||
// Label is a human-readable reference for the service being tested.
|
||||
Label string `json:"label"`
|
||||
|
||||
// Address is the host:port of the service being tested.
|
||||
Address string `json:"address"`
|
||||
|
||||
// Source is a human-readable reference for the source host where the tests
|
||||
// were run from.
|
||||
Source string `json:"source"`
|
||||
|
||||
// LatencyNS is the duration in nanoseconds that the check took to complete.
|
||||
// Will also be set on unsuccessful attempts.
|
||||
LatencyNS time.Duration `json:"latency_ns"`
|
||||
|
||||
// Error is set if the test returned an error.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// IsOK returns true iff no error
|
||||
func (r ConnectivityResult) IsOK() bool {
|
||||
return len(r.Error) == 0
|
||||
}
|
||||
|
||||
// ConnectivityResults is a collection of connectivty reports.
|
||||
type ConnectivityResults []ConnectivityResult
|
||||
|
||||
// IsOK returns true iff no error in any result.
|
||||
func (r ConnectivityResults) IsOK() bool {
|
||||
for _, result := range r {
|
||||
if !result.IsOK() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
24
vendor/github.com/storageos/go-api/types/delete_options.go
generated
vendored
24
vendor/github.com/storageos/go-api/types/delete_options.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
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:"-"`
|
||||
}
|
36
vendor/github.com/storageos/go-api/types/deployment.go
generated
vendored
36
vendor/github.com/storageos/go-api/types/deployment.go
generated
vendored
@ -1,36 +0,0 @@
|
||||
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"`
|
||||
|
||||
// Node ID
|
||||
// Read Only: true
|
||||
Node string `json:"node"`
|
||||
|
||||
// Node name
|
||||
// Read Only: true
|
||||
NodeName string `json:"nodeName"`
|
||||
|
||||
// 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"`
|
||||
}
|
86
vendor/github.com/storageos/go-api/types/driver_instance.go
generated
vendored
86
vendor/github.com/storageos/go-api/types/driver_instance.go
generated
vendored
@ -1,86 +0,0 @@
|
||||
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
|
||||
// }
|
||||
// }
|
||||
}
|
13
vendor/github.com/storageos/go-api/types/error_response.go
generated
vendored
13
vendor/github.com/storageos/go-api/types/error_response.go
generated
vendored
@ -1,13 +0,0 @@
|
||||
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"`
|
||||
}
|
60
vendor/github.com/storageos/go-api/types/events.go
generated
vendored
60
vendor/github.com/storageos/go-api/types/events.go
generated
vendored
@ -1,60 +0,0 @@
|
||||
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
|
||||
}
|
148
vendor/github.com/storageos/go-api/types/health.go
generated
vendored
148
vendor/github.com/storageos/go-api/types/health.go
generated
vendored
@ -1,148 +0,0 @@
|
||||
package types
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type SubModuleStatus struct {
|
||||
Status string `json:"status"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
ChangedAt string `json:"changedAt"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type NamedSubModuleStatus struct {
|
||||
Name string
|
||||
SubModuleStatus
|
||||
}
|
||||
|
||||
type ClusterHealthNode struct {
|
||||
NodeID string `json:"nodeID"`
|
||||
NodeName string `json:"nodeName"`
|
||||
Submodules struct {
|
||||
DirectFSInitiator SubModuleStatus `json:"directfs_initiator"`
|
||||
Director SubModuleStatus `json:"director"`
|
||||
KV SubModuleStatus `json:"kv"`
|
||||
KVWrite SubModuleStatus `json:"kv_write"`
|
||||
NATS SubModuleStatus `json:"nats"`
|
||||
Presentation SubModuleStatus `json:"presentation"`
|
||||
RDB SubModuleStatus `json:"rdb"`
|
||||
} `json:"submodules"`
|
||||
}
|
||||
|
||||
type CPHealthStatus struct {
|
||||
KV SubModuleStatus
|
||||
KVWrite SubModuleStatus
|
||||
NATS SubModuleStatus
|
||||
Scheduler SubModuleStatus
|
||||
}
|
||||
|
||||
func (c *CPHealthStatus) ToNamedSubmodules() []NamedSubModuleStatus {
|
||||
return []NamedSubModuleStatus{
|
||||
{Name: "nats", SubModuleStatus: c.NATS},
|
||||
{Name: "kv", SubModuleStatus: c.KV},
|
||||
{Name: "kv_write", SubModuleStatus: c.KVWrite},
|
||||
{Name: "scheduler", SubModuleStatus: c.Scheduler},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CPHealthStatus) UnmarshalJSON(data []byte) error {
|
||||
unmarsh := struct {
|
||||
Submodules struct {
|
||||
KV SubModuleStatus `json:"kv"`
|
||||
KVWrite SubModuleStatus `json:"kv_write"`
|
||||
NATS SubModuleStatus `json:"nats"`
|
||||
Scheduler SubModuleStatus `json:"scheduler"`
|
||||
} `json:"submodules"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(data, &unmarsh); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.KV = unmarsh.Submodules.KV
|
||||
c.KVWrite = unmarsh.Submodules.KVWrite
|
||||
c.NATS = unmarsh.Submodules.NATS
|
||||
c.Scheduler = unmarsh.Submodules.Scheduler
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DPHealthStatus struct {
|
||||
DirectFSClient SubModuleStatus
|
||||
DirectFSServer SubModuleStatus
|
||||
Director SubModuleStatus
|
||||
FSDriver SubModuleStatus
|
||||
FS SubModuleStatus
|
||||
}
|
||||
|
||||
func (d *DPHealthStatus) ToNamedSubmodules() []NamedSubModuleStatus {
|
||||
return []NamedSubModuleStatus{
|
||||
{Name: "dfs_client", SubModuleStatus: d.DirectFSClient},
|
||||
{Name: "dfs_server", SubModuleStatus: d.DirectFSServer},
|
||||
{Name: "director", SubModuleStatus: d.Director},
|
||||
{Name: "fs_driver", SubModuleStatus: d.FSDriver},
|
||||
{Name: "fs", SubModuleStatus: d.FS},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DPHealthStatus) UnmarshalJSON(data []byte) error {
|
||||
unmarsh := struct {
|
||||
Submodules struct {
|
||||
DirectFSClient SubModuleStatus `json:"directfs-client"`
|
||||
DirectFSServer SubModuleStatus `json:"directfs-server"`
|
||||
Director SubModuleStatus `json:"director"`
|
||||
FSDriver SubModuleStatus `json:"filesystem-driver"`
|
||||
FS SubModuleStatus `json:"fs"`
|
||||
} `json:"submodules"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(data, &unmarsh); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.DirectFSClient = unmarsh.Submodules.DirectFSClient
|
||||
d.DirectFSServer = unmarsh.Submodules.DirectFSServer
|
||||
d.Director = unmarsh.Submodules.Director
|
||||
d.FSDriver = unmarsh.Submodules.FSDriver
|
||||
d.FS = unmarsh.Submodules.FS
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HealthStatus is the health status json object.
|
||||
type HealthStatus struct {
|
||||
Submodules HealthSubmodules `json:"submodules"`
|
||||
}
|
||||
|
||||
// HealthSubmodules is the "submodules" attribuet of HealthStatus.
|
||||
type HealthSubmodules struct {
|
||||
KV SubModuleStatus `json:"kv,omitempty"`
|
||||
KVWrite SubModuleStatus `json:"kv_write,omitempty"`
|
||||
NATS SubModuleStatus `json:"nats,omitempty"`
|
||||
Scheduler SubModuleStatus `json:"scheduler,omitempty"`
|
||||
DirectFSClient SubModuleStatus `json:"directfs_initiator,omitempty"`
|
||||
DirectFSServer SubModuleStatus `json:"directfs_responder,omitempty"`
|
||||
Director SubModuleStatus `json:"director,omitempty"`
|
||||
FSDriver SubModuleStatus `json:"rdb,omitempty"`
|
||||
FS SubModuleStatus `json:"presentation,omitempty"`
|
||||
}
|
||||
|
||||
// ToCPHealthStatus returns only CPHealthStatus from the HealthStatus.
|
||||
func (h *HealthStatus) ToCPHealthStatus() *CPHealthStatus {
|
||||
return &CPHealthStatus{
|
||||
KV: h.Submodules.KV,
|
||||
KVWrite: h.Submodules.KVWrite,
|
||||
NATS: h.Submodules.KVWrite,
|
||||
Scheduler: h.Submodules.Scheduler,
|
||||
}
|
||||
}
|
||||
|
||||
// ToDPHealthStatus returns only DPHealthStatus from the HealthStatus.
|
||||
func (h *HealthStatus) ToDPHealthStatus() *DPHealthStatus {
|
||||
return &DPHealthStatus{
|
||||
DirectFSClient: h.Submodules.DirectFSClient,
|
||||
DirectFSServer: h.Submodules.DirectFSServer,
|
||||
Director: h.Submodules.Director,
|
||||
FSDriver: h.Submodules.FSDriver,
|
||||
FS: h.Submodules.FS,
|
||||
}
|
||||
}
|
35
vendor/github.com/storageos/go-api/types/licence.go
generated
vendored
35
vendor/github.com/storageos/go-api/types/licence.go
generated
vendored
@ -1,35 +0,0 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
|
||||
// FeatureType store features types
|
||||
type FeatureType string
|
||||
|
||||
const (
|
||||
// HA means High Availability
|
||||
HA = FeatureType("HA")
|
||||
// DEV means developer licence
|
||||
DEV = FeatureType("DEV")
|
||||
// TRIAL means trial licence
|
||||
TRIAL = FeatureType("TRIAL")
|
||||
)
|
||||
|
||||
// Licence holds the information to be encoded in the licence key. It needs to be synced across
|
||||
// the django server running on portal-API as well as the corresponding decoding package on the
|
||||
// storageOS control plane
|
||||
type Licence struct {
|
||||
ArrayUUID string `json:"arrayUUID,omitempty"`
|
||||
ClusterID string `json:"clusterID,omitempty"`
|
||||
CustomerID string `json:"customerID"`
|
||||
CustomerName string `json:"customerName"`
|
||||
Storage int `json:"storage"`
|
||||
ValidUntil time.Time `json:"validUntil"`
|
||||
LicenceType string `json:"licenceType"`
|
||||
Features map[FeatureType]bool `json:"features"`
|
||||
Unregistered bool `json:"unregistered"`
|
||||
}
|
||||
|
||||
// LicenceKeyContainer - stores a licence key
|
||||
type LicenceKeyContainer struct {
|
||||
Key string `json:"key"`
|
||||
}
|
19
vendor/github.com/storageos/go-api/types/list_options.go
generated
vendored
19
vendor/github.com/storageos/go-api/types/list_options.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
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
|
||||
}
|
40
vendor/github.com/storageos/go-api/types/logger.go
generated
vendored
40
vendor/github.com/storageos/go-api/types/logger.go
generated
vendored
@ -1,40 +0,0 @@
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// Logger is the runtime configuration of the node's logging services.
|
||||
// swagger:model Logger
|
||||
type Logger struct {
|
||||
|
||||
// Node name
|
||||
Node string `json:"node"`
|
||||
|
||||
// Log level
|
||||
Level string `json:"level"`
|
||||
|
||||
// Log filter
|
||||
Filter string `json:"filter"`
|
||||
|
||||
// Log filters by category
|
||||
// Read Only: true
|
||||
Categories map[string]string `json:"categories"`
|
||||
}
|
||||
|
||||
// LoggerUpdateOptions are the available parameters for updating loggers.
|
||||
type LoggerUpdateOptions struct {
|
||||
|
||||
// Log level
|
||||
Level string `json:"level"`
|
||||
|
||||
// Log filter
|
||||
Filter string `json:"filter"`
|
||||
|
||||
// List of nodes to update. All if not set.
|
||||
Nodes []string `json:"nodes"`
|
||||
|
||||
// List of fields to update. Must be set.
|
||||
Fields []string `json:"fields"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
59
vendor/github.com/storageos/go-api/types/namespace.go
generated
vendored
59
vendor/github.com/storageos/go-api/types/namespace.go
generated
vendored
@ -1,59 +0,0 @@
|
||||
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:"-"`
|
||||
}
|
104
vendor/github.com/storageos/go-api/types/node.go
generated
vendored
104
vendor/github.com/storageos/go-api/types/node.go
generated
vendored
@ -1,104 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Node represents a StorageOS cluster node.
|
||||
type Node struct {
|
||||
NodeConfig
|
||||
|
||||
HostID uint32 `json:"hostID"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
|
||||
Health string `json:"health"`
|
||||
HealthUpdatedAt time.Time `json:"healthUpdatedAt"`
|
||||
|
||||
VersionInfo map[string]VersionInfo `json:"versionInfo"`
|
||||
Version string `json:"version"`
|
||||
Revision string // the GitCommit this maps to
|
||||
|
||||
Scheduler bool `json:"scheduler"`
|
||||
|
||||
Cordon bool `json:"cordon"`
|
||||
Drain bool `json:"drain"`
|
||||
|
||||
VolumeStats VolumeStats `json:"volumeStats"`
|
||||
|
||||
// PoolStats map[string]map[string]CapacityStats `json:"poolStats"`
|
||||
|
||||
CapacityStats CapacityStats `json:"capacityStats"`
|
||||
}
|
||||
|
||||
// NodeConfig is a read-only representation of the node's configuration, set at
|
||||
// start time by environment variables passed to the container or using defaults.
|
||||
type NodeConfig struct {
|
||||
// UUID is the unique identifier of the node. It cannot be changed once set.
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// Hostname of the node.
|
||||
Hostname string `json:"hostname"`
|
||||
|
||||
// Address is is used for communication between nodes.
|
||||
// Nodes will fail to start if the address they first registered with
|
||||
// changes. This protects against the container being re-scheduled on a
|
||||
// different host. Nodes will typically use the host server's ip address,
|
||||
// running the docker container in -net host mode.
|
||||
Address string `json:"address"`
|
||||
|
||||
// KvAddr is the address of the KV store to use for storing configuration.
|
||||
// It can include the address or FQDN with optional port. Defaults to
|
||||
// Address/ADVERTISE_IP.
|
||||
KvAddr string `json:"kvAddr"`
|
||||
|
||||
// Port allocations
|
||||
APIPort int `json:"apiPort"`
|
||||
NatsPort int `json:"natsPort"`
|
||||
NatsClusterPort int `json:"natsClusterPort"`
|
||||
SerfPort int `json:"serfPort"`
|
||||
DFSPort int `json:"dfsPort"`
|
||||
KVPeerPort int `json:"kvPeerPort"`
|
||||
KVClientPort int `json:"kvClientPort"`
|
||||
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
LogLevel string `json:"logLevel"` // the level of the logs to outout
|
||||
LogFormat string `json:"logFormat"` // either text or json
|
||||
LogFilter string `json:"logFilter"` // used to discard messages based on the message's category
|
||||
|
||||
// BindAddr is used to control the default address StorageOS binds to. This
|
||||
// should always be set to 0.0.0.0 (all interfaces).
|
||||
BindAddr string `json:"bindAddr"`
|
||||
|
||||
// DeviceDir is where the volumes are exported. This directory must be
|
||||
// shared into the container using the rshared volume mount option.
|
||||
DeviceDir string `json:"deviceDir"`
|
||||
|
||||
// Join existing cluster
|
||||
Join string `json:"join"`
|
||||
|
||||
// Backend selects the KV backend, either embedded (testing only) or etcd.
|
||||
Backend string `json:"kvBackend"`
|
||||
|
||||
// EnableDebug is used to enable various debugging features. Used by http
|
||||
// to enable debug endpoints and as a shortcut to enable debug logging.
|
||||
EnableDebug bool `json:"debug"`
|
||||
|
||||
// Devices specify all devices that are available on the node.
|
||||
Devices []Device `json:"devices"`
|
||||
}
|
||||
|
||||
// Device - device type
|
||||
type Device struct {
|
||||
ID string
|
||||
Labels map[string]string `json:"labels"`
|
||||
Status string `json:"status"`
|
||||
Identifier string `json:"identifier"`
|
||||
Class string `json:"class"`
|
||||
CapacityStats CapacityStats `json:"capacityStats"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
28
vendor/github.com/storageos/go-api/types/node_update_options.go
generated
vendored
28
vendor/github.com/storageos/go-api/types/node_update_options.go
generated
vendored
@ -1,28 +0,0 @@
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// NodeUpdateOptions are available parameters for updating existing nodes.
|
||||
type NodeUpdateOptions struct {
|
||||
|
||||
// Node unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Node name.
|
||||
// Read Only: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Description of the node.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Labels are user-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// Cordon marks the node as unschedulable if true
|
||||
Cordon bool `json:"cordon"`
|
||||
Drain bool `json:"drain"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
19
vendor/github.com/storageos/go-api/types/operator.go
generated
vendored
19
vendor/github.com/storageos/go-api/types/operator.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
package types
|
||||
|
||||
// Operator represents a key/field's relationship to value(s).
|
||||
// See labels.Requirement and fields.Requirement for more details.
|
||||
type Operator string
|
||||
|
||||
// Valid operators
|
||||
const (
|
||||
None Operator = ""
|
||||
DoesNotExist Operator = "!"
|
||||
Equals Operator = "="
|
||||
DoubleEquals Operator = "=="
|
||||
In Operator = "in"
|
||||
NotEquals Operator = "!="
|
||||
NotIn Operator = "notin"
|
||||
Exists Operator = "exists"
|
||||
GreaterThan Operator = "gt"
|
||||
LessThan Operator = "lt"
|
||||
)
|
45
vendor/github.com/storageos/go-api/types/policy.go
generated
vendored
45
vendor/github.com/storageos/go-api/types/policy.go
generated
vendored
@ -1,45 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Policy struct {
|
||||
Spec struct {
|
||||
User string `json:"user,omitempty"`
|
||||
Group string `json:"group,omitempty"`
|
||||
Readonly bool `json:"readonly,omitempty"`
|
||||
APIGroup string `json:"apiGroup,omitempty"`
|
||||
Resource string `json:"resource,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
NonResourcePath string `json:"nonResourcePath,omitempty"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
|
||||
// PolicyWithId is used as an internal type to render table formated versions of the json response
|
||||
type PolicyWithID struct {
|
||||
Policy
|
||||
ID string
|
||||
}
|
||||
|
||||
// MarshalJSON returns a marshaled copy of the internal policy object, so it is still valid to use
|
||||
// with the REST API
|
||||
func (p *PolicyWithID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(p.Policy)
|
||||
}
|
||||
|
||||
// PolicySet is a representation of the data structure returned from the REST API
|
||||
type PolicySet map[string]Policy
|
||||
|
||||
func (p PolicySet) GetPoliciesWithID() []*PolicyWithID {
|
||||
rtn := make([]*PolicyWithID, 0, len(p))
|
||||
|
||||
for k, v := range p {
|
||||
rtn = append(rtn, &PolicyWithID{
|
||||
Policy: v,
|
||||
ID: k,
|
||||
})
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
39
vendor/github.com/storageos/go-api/types/pool.go
generated
vendored
39
vendor/github.com/storageos/go-api/types/pool.go
generated
vendored
@ -1,39 +0,0 @@
|
||||
package types
|
||||
|
||||
// Pool is used to define a capacity pool.
|
||||
type Pool struct {
|
||||
|
||||
// Pool unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Pool name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Pool description.
|
||||
Description string `json:"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.
|
||||
Default bool `json:"default"`
|
||||
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// DeviceSelector - specifies a selector to filter node devices based on their labels.
|
||||
// Only devices from nodes that are in the 'NodeNames' list can be selected
|
||||
DeviceSelector string `json:"deviceSelector"`
|
||||
|
||||
// Populated by the system. Read-only.
|
||||
CapacityStats CapacityStats `json:"capacityStats"`
|
||||
|
||||
// This field is computed based on NodeSelector value
|
||||
// Populated by the system. Read-only.
|
||||
Nodes []*Node `json:"nodes"`
|
||||
|
||||
// Labels define a list of labels that describe the pool.
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// Pools is a collection of Pool objects
|
||||
type Pools []*Pool
|
28
vendor/github.com/storageos/go-api/types/pool_options.go
generated
vendored
28
vendor/github.com/storageos/go-api/types/pool_options.go
generated
vendored
@ -1,28 +0,0 @@
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// PoolOptions are available parameters for creating or updating pools.
|
||||
type PoolOptions struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
// Pool description.
|
||||
Description string `json:"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.
|
||||
Default bool `json:"default"`
|
||||
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// DeviceSelector - specifies a selector to filter node devices based on their labels.
|
||||
// Only devices from nodes that are in the 'NodeNames' list can be selected
|
||||
DeviceSelector string `json:"deviceSelector"`
|
||||
|
||||
// Labels define a list of labels that describe the pool.
|
||||
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:"-"`
|
||||
}
|
125
vendor/github.com/storageos/go-api/types/rule.go
generated
vendored
125
vendor/github.com/storageos/go-api/types/rule.go
generated
vendored
@ -1,125 +0,0 @@
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// Rule is used to define a rule
|
||||
type Rule struct {
|
||||
|
||||
// Rule unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Rule name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace is the object name and authentication scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// Rule description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Flag describing whether the rule is active.
|
||||
// Default: false
|
||||
Active bool `json:"active"`
|
||||
|
||||
// Weight is used to determine order during rule processing. Rules with
|
||||
// heavier weights are processed later.
|
||||
// default: 0
|
||||
Weight int `json:"weight"`
|
||||
|
||||
// RuleAction controls whether the action is to add or remove a label from the
|
||||
// matching object(s).
|
||||
RuleAction string `json:"action"`
|
||||
|
||||
// Selectors defines the list of labels that should trigger a rule.
|
||||
Selector string `json:"selector"`
|
||||
|
||||
// Labels define the list of labels that will be added or removed from the
|
||||
// matching object(s).
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// Rules is a collection of Rules.
|
||||
type Rules []*Rule
|
||||
|
||||
// RuleCreateOptions are available parameters for creating new rules.
|
||||
type RuleCreateOptions struct {
|
||||
|
||||
// Rule name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace is the object name and authentication scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// Rule description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Flag describing whether the rule is active.
|
||||
// Default: false
|
||||
Active bool `json:"active"`
|
||||
|
||||
// Weight is used to determine order during rule processing. Rules with
|
||||
// heavier weights are processed later.
|
||||
// default: 0
|
||||
Weight int `json:"weight"`
|
||||
|
||||
// RuleAction controls whether the action is to add or remove a label from the
|
||||
// matching object(s).
|
||||
RuleAction string `json:"action"`
|
||||
|
||||
// Selectors defines the list of labels that should trigger a rule.
|
||||
Selector string `json:"selector"`
|
||||
|
||||
// Labels define the list of labels that will be added or removed from the
|
||||
// matching object(s).
|
||||
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:"-"`
|
||||
}
|
||||
|
||||
// RuleUpdateOptions are available parameters for creating new rules.
|
||||
type RuleUpdateOptions struct {
|
||||
|
||||
// Rule unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Rule name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace is the object name and authentication scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// Rule description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Flag describing whether the rule is active.
|
||||
// Default: false
|
||||
Active bool `json:"active"`
|
||||
|
||||
// Weight is used to determine order during rule processing. Rules with
|
||||
// heavier weights are processed later.
|
||||
// default: 0
|
||||
Weight int `json:"weight"`
|
||||
|
||||
// Operator is used to compare objects or labels.
|
||||
Operator string `json:"operator"`
|
||||
|
||||
// RuleAction controls whether the action is to add or remove a label from the
|
||||
// matching object(s).
|
||||
RuleAction string `json:"action"`
|
||||
|
||||
// Selectors defines the list of labels that should trigger a rule.
|
||||
Selector string `json:"selector"`
|
||||
|
||||
// Labels define the list of labels that will be added or removed from the
|
||||
// matching object(s).
|
||||
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:"-"`
|
||||
}
|
53
vendor/github.com/storageos/go-api/types/template.go
generated
vendored
53
vendor/github.com/storageos/go-api/types/template.go
generated
vendored
@ -1,53 +0,0 @@
|
||||
package types
|
||||
|
||||
// Template is used to define an auto-naming rule.
|
||||
type Template struct {
|
||||
|
||||
// Template unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Template name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Template description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Template format. This is used for pattern matching against labels.
|
||||
Format string `json:"format"`
|
||||
|
||||
// Autoincrement defines whether there is a dynamic numeric component in the
|
||||
// template that must auto-increment when objects with the same name already
|
||||
// exists.
|
||||
AutoIncrement bool `json:"autoIncrement"`
|
||||
|
||||
// Padding determines whether a dynamic numeric component in the name should
|
||||
// be padded.
|
||||
// default: false
|
||||
Padding bool `json:"padding"`
|
||||
|
||||
// PaddingLength sets the length of the padding. A Padding length of 3 would
|
||||
// set name similar to `abc001` for the first item. Ignored if Padding set to
|
||||
// `false`.
|
||||
PaddingLength int `json:"paddingLength"`
|
||||
|
||||
// Flag describing whether the template is active.
|
||||
// Default: false
|
||||
Active bool `json:"active"`
|
||||
|
||||
// Weight is used to determine order during template processing. Templates
|
||||
// with heavier weights are processed later.
|
||||
// default: 0
|
||||
Weight int `json:"weight"`
|
||||
|
||||
// ObjectTypes defines the type names that the template can be applied to.
|
||||
ObjectTypes []string `json:"objectTypes"`
|
||||
|
||||
// Labels define a list of the labels that the object must have in order for
|
||||
// the template to be applied.
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// Templates is a collection of Template objects
|
||||
type Templates []*Template
|
51
vendor/github.com/storageos/go-api/types/template_create_options.go
generated
vendored
51
vendor/github.com/storageos/go-api/types/template_create_options.go
generated
vendored
@ -1,51 +0,0 @@
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// TemplateCreateOptions are available parameters for creating new templates.
|
||||
type TemplateCreateOptions struct {
|
||||
|
||||
// Template name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Template description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Template format. This is used for pattern matching against labels.
|
||||
Format string `json:"format"`
|
||||
|
||||
// Autoincrement defines whether there is a dynamic numeric component in the
|
||||
// template that must auto-increment when objects with the same name already
|
||||
// exists.
|
||||
AutoIncrement bool `json:"autoIncrement"`
|
||||
|
||||
// Padding determines whether a dynamic numeric component in the name should
|
||||
// be padded.
|
||||
// default: false
|
||||
Padding bool `json:"padding"`
|
||||
|
||||
// PaddingLength sets the length of the padding. A Padding length of 3 would
|
||||
// set name similar to `abc001` for the first item. Ignored if Padding set to
|
||||
// `false`.
|
||||
PaddingLength int `json:"paddingLength"`
|
||||
|
||||
// Flag describing whether the template is active.
|
||||
// Default: false
|
||||
Active bool `json:"active"`
|
||||
|
||||
// Weight is used to determine order during template processing. Templates
|
||||
// with heavier weights are processed later.
|
||||
// default: 0
|
||||
Weight int `json:"weight"`
|
||||
|
||||
// ObjectTypes defines the type names that the template can be applied to.
|
||||
ObjectTypes []string `json:"objectTypes"`
|
||||
|
||||
// Labels define a list of the labels that the object must have in order for
|
||||
// the template to be applied.
|
||||
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:"-"`
|
||||
}
|
79
vendor/github.com/storageos/go-api/types/user.go
generated
vendored
79
vendor/github.com/storageos/go-api/types/user.go
generated
vendored
@ -1,79 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UUID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Groups []string `json:"groups"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
func (u *User) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
UUID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Groups string `json:"groups"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Role string `json:"role"`
|
||||
}{
|
||||
UUID: u.UUID,
|
||||
Username: u.Username,
|
||||
Groups: strings.Join(u.Groups, ","),
|
||||
Password: u.Password,
|
||||
Role: u.Role,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (u *User) UnmarshalJSON(data []byte) error {
|
||||
temp := &struct {
|
||||
UUID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Groups string `json:"groups"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(data, temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.UUID = temp.UUID
|
||||
u.Username = temp.Username
|
||||
u.Password = temp.Password
|
||||
u.Role = temp.Role
|
||||
u.Groups = strings.Split(temp.Groups, ",")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UserCreateOptions struct {
|
||||
Username string `json:"username"`
|
||||
Groups []string `json:"groups"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
||||
|
||||
func (u UserCreateOptions) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
Username string `json:"username"`
|
||||
Groups string `json:"groups"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
}{
|
||||
Username: u.Username,
|
||||
Groups: strings.Join(u.Groups, ","),
|
||||
Password: u.Password,
|
||||
Role: u.Role,
|
||||
})
|
||||
|
||||
}
|
26
vendor/github.com/storageos/go-api/types/version.go
generated
vendored
26
vendor/github.com/storageos/go-api/types/version.go
generated
vendored
@ -1,26 +0,0 @@
|
||||
package types
|
||||
|
||||
// VersionInfo describes version and runtime info.
|
||||
type VersionInfo struct {
|
||||
Name string `json:"name"`
|
||||
BuildDate string `json:"buildDate"`
|
||||
Revision string `json:"revision"`
|
||||
Version string `json:"version"`
|
||||
APIVersion string `json:"apiVersion"`
|
||||
GoVersion string `json:"goVersion"`
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
KernelVersion string `json:"kernelVersion"`
|
||||
Experimental bool `json:"experimental"`
|
||||
}
|
||||
|
||||
type VersionResponse struct {
|
||||
Client *VersionInfo
|
||||
Server *VersionInfo
|
||||
}
|
||||
|
||||
// ServerOK returns true when the client could connect to the docker server
|
||||
// and parse the information received. It returns false otherwise.
|
||||
func (v VersionResponse) ServerOK() bool {
|
||||
return v.Server != nil
|
||||
}
|
143
vendor/github.com/storageos/go-api/types/volume.go
generated
vendored
143
vendor/github.com/storageos/go-api/types/volume.go
generated
vendored
@ -1,143 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultNamespace is used when a namespace hasn't been specified.
|
||||
const DefaultNamespace = "default"
|
||||
|
||||
// Volume represents storage volume.
|
||||
// swagger:model Volume
|
||||
type Volume struct {
|
||||
|
||||
// Volume unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Block device inode.
|
||||
// Read Only: true
|
||||
Inode uint32 `json:"inode"`
|
||||
|
||||
// Volume name.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Size in GB.
|
||||
// Required: true
|
||||
Size int `json:"size"`
|
||||
|
||||
// Name of capacity pool to provision the volume in, or the name of the current pool.
|
||||
Pool string `json:"pool"`
|
||||
|
||||
// Filesystem type to mount. May be set on create, or set by rules to influence client.
|
||||
FSType string `json:"fsType"`
|
||||
|
||||
// Volume description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// User-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// Namespace is the object name and authentication scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// node selector (where volumes should land)
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// Volume deployment information for the master volume.
|
||||
// Read Only: true
|
||||
Master *Deployment `json:"master,omitempty"`
|
||||
|
||||
// Flag indicating if the volume is mounted and in use.
|
||||
// Read Only: true
|
||||
Mounted bool `json:"mounted"`
|
||||
|
||||
// MountDevice, where the device is located
|
||||
MountDevice string `json:"mountDevice"`
|
||||
|
||||
// Mountpoint, where the volume is mounted
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
|
||||
// When the volume was mounted.
|
||||
// Read Only: true
|
||||
MountedAt time.Time `json:"mountedAt,omitempty"`
|
||||
|
||||
// Reference to the node that has the volume mounted.
|
||||
// Read Only: true
|
||||
MountedBy string `json:"mountedBy,omitempty"`
|
||||
|
||||
// Volume deployment information for the replica volumes.
|
||||
// Read Only: true
|
||||
Replicas []*Deployment `json:"replicas"`
|
||||
|
||||
// Volume health, one of: healthy, degraded or dead.
|
||||
// Read Only: true
|
||||
Health string `json:"health"`
|
||||
|
||||
// Short status, one of: pending, evaluating, deploying, active, unavailable, failed, updating, deleting.
|
||||
// Read Only: true
|
||||
Status string `json:"status"`
|
||||
|
||||
// Status message explaining current status.
|
||||
// Read Only: true
|
||||
StatusMessage string `json:"statusMessage"`
|
||||
|
||||
// mkfs performed on new volumes
|
||||
MkfsDone bool `json:"mkfsDone"`
|
||||
MkfsDoneAt time.Time `json:"mkfsDoneAt"`
|
||||
|
||||
// When the volume was created.
|
||||
// Read Only: true
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
// User that created the volume.
|
||||
// Read Only: true
|
||||
CreatedBy string `json:"createdBy"`
|
||||
}
|
||||
|
||||
// VolumeMountOptions - used by clients to inform of volume mount operations.
|
||||
type VolumeMountOptions struct {
|
||||
|
||||
// Volume unique ID.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Name is the name of the volume to mount.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Mountpoint, where the volume is mounted
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
|
||||
// Filesystem type, optional but expected when mounting raw volume
|
||||
FsType string `json:"fsType"`
|
||||
|
||||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// The hostname of the client mounting the volume.
|
||||
Client string `json:"client"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
||||
|
||||
// VolumeUnmountOptions - used by clients to inform of volume mount operations.
|
||||
type VolumeUnmountOptions struct {
|
||||
|
||||
// Volume unique ID.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Name is the name of the volume to unmount.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// The hostname of the client unmounting the volume. Must match the hostname
|
||||
// of the client that registered the mount operation.
|
||||
Client string `json:"client"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
36
vendor/github.com/storageos/go-api/types/volume_create_options.go
generated
vendored
36
vendor/github.com/storageos/go-api/types/volume_create_options.go
generated
vendored
@ -1,36 +0,0 @@
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// VolumeCreateOptions are available parameters for creating new volumes.
|
||||
type VolumeCreateOptions struct {
|
||||
|
||||
// Name is the name of the volume to create.
|
||||
// Required: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Description describes the volume.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Size in GB.
|
||||
// Required: true
|
||||
Size int `json:"size"`
|
||||
|
||||
// Pool is the name or id of capacity pool to provision the volume in.
|
||||
Pool string `json:"pool"`
|
||||
|
||||
// Filesystem type to mount. May be set on create, or set by rules to influence client.
|
||||
FSType string `json:"fsType"`
|
||||
|
||||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// node selector (where volumes should land)
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// 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:"-"`
|
||||
}
|
8
vendor/github.com/storageos/go-api/types/volume_stats.go
generated
vendored
8
vendor/github.com/storageos/go-api/types/volume_stats.go
generated
vendored
@ -1,8 +0,0 @@
|
||||
package types
|
||||
|
||||
// VolumeStats - volume stats (volume counts, looking forward to capacity)
|
||||
type VolumeStats struct {
|
||||
MasterVolumeCount int `json:"masterVolumeCount"`
|
||||
ReplicaVolumeCount int `json:"replicaVolumeCount"`
|
||||
VirtualVolumeCount int `json:"virtualVolumeCount"`
|
||||
}
|
34
vendor/github.com/storageos/go-api/types/volume_update_options.go
generated
vendored
34
vendor/github.com/storageos/go-api/types/volume_update_options.go
generated
vendored
@ -1,34 +0,0 @@
|
||||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// VolumeUpdateOptions are available parameters for updating existing volumes.
|
||||
type VolumeUpdateOptions struct {
|
||||
|
||||
// Volume unique ID.
|
||||
// Read Only: true
|
||||
ID string `json:"id"`
|
||||
|
||||
// Volume name.
|
||||
// Read Only: true
|
||||
Name string `json:"name"`
|
||||
|
||||
// Description describes the volume.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Size in GB.
|
||||
// Required: true
|
||||
Size int `json:"size"`
|
||||
|
||||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// node selector (where volumes should land)
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// 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:"-"`
|
||||
}
|
118
vendor/github.com/storageos/go-api/user.go
generated
vendored
118
vendor/github.com/storageos/go-api/user.go
generated
vendored
@ -1,118 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// UserAPIPrefix is a partial path to the HTTP endpoint.
|
||||
UserAPIPrefix = "users"
|
||||
|
||||
// ErrNoSuchUser is the error returned when the user does not exist.
|
||||
ErrNoSuchUser = errors.New("no such user")
|
||||
)
|
||||
|
||||
// UserList returns the list of available users.
|
||||
func (c *Client) UserList(opts types.ListOptions) ([]*types.User, 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", UserAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
users := make([]*types.User, 0)
|
||||
if err := json.NewDecoder(resp.Body).Decode(&users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// User returns a user by its username/id.
|
||||
func (c *Client) User(username string) (*types.User, error) {
|
||||
path := fmt.Sprintf("%s/%s", UserAPIPrefix, username)
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchUser
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var user *types.User
|
||||
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UserCreate creates a user on the server.
|
||||
func (c *Client) UserCreate(opts types.UserCreateOptions) error {
|
||||
_, err := c.do("POST", UserAPIPrefix, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// UserUpdate updates a user on the server.
|
||||
func (c *Client) UserUpdate(ctx context.Context, user *types.User) error {
|
||||
var ref string
|
||||
switch {
|
||||
case user.UUID != "":
|
||||
ref = user.UUID
|
||||
case user.Username != "":
|
||||
ref = user.Username
|
||||
default:
|
||||
return ErrNoSuchUser
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s", UserAPIPrefix, ref)
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: user,
|
||||
context: ctx,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchUser
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UserDelete removes a user by its reference.
|
||||
func (c *Client) UserDelete(opts types.DeleteOptions) error {
|
||||
resp, err := c.do("DELETE", UserAPIPrefix+"/"+opts.Name, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchUser
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
16
vendor/github.com/storageos/go-api/util.go
generated
vendored
16
vendor/github.com/storageos/go-api/util.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseRef is a helper to split out the namespace and name from a path
|
||||
// reference.
|
||||
func ParseRef(ref string) (namespace string, name string, err error) {
|
||||
parts := strings.Split(ref, "/")
|
||||
if len(parts) != 2 {
|
||||
return "", "", fmt.Errorf("Name must be prefixed with <namespace>/")
|
||||
}
|
||||
return parts[0], parts[1], nil
|
||||
}
|
76
vendor/github.com/storageos/go-api/validation.go
generated
vendored
76
vendor/github.com/storageos/go-api/validation.go
generated
vendored
@ -1,76 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
// IDFormat are the characters allowed to represent an ID.
|
||||
IDFormat = `[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`
|
||||
|
||||
// NameFormat are the characters allowed to represent a name.
|
||||
NameFormat = `[a-zA-Z0-9][a-zA-Z0-9~_.-]+`
|
||||
)
|
||||
|
||||
var (
|
||||
// IDPattern is a regular expression to validate a unique id against the
|
||||
// collection of restricted characters.
|
||||
IDPattern = regexp.MustCompile(`^` + IDFormat + `$`)
|
||||
|
||||
// NamePattern is a regular expression to validate names against the
|
||||
// collection of restricted characters.
|
||||
NamePattern = regexp.MustCompile(`^` + NameFormat + `$`)
|
||||
|
||||
// ErrNoRef is given when the reference given is invalid.
|
||||
ErrNoRef = errors.New("no ref provided or incorrect format")
|
||||
// ErrNoNamespace is given when the namespace given is invalid.
|
||||
ErrNoNamespace = errors.New("no namespace provided or incorrect format")
|
||||
)
|
||||
|
||||
// ValidateNamespaceAndRef returns true if both the namespace and ref are valid.
|
||||
func ValidateNamespaceAndRef(namespace, ref string) error {
|
||||
if !IsUUID(ref) && !IsName(ref) {
|
||||
return ErrNoRef
|
||||
}
|
||||
if !IsName(namespace) {
|
||||
return ErrNoNamespace
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateNamespace returns true if the namespace uses a valid name.
|
||||
func ValidateNamespace(namespace string) error {
|
||||
if !IsName(namespace) {
|
||||
return ErrNoNamespace
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsUUID returns true if the string input is a valid UUID string.
|
||||
func IsUUID(s string) bool {
|
||||
return IDPattern.MatchString(s)
|
||||
}
|
||||
|
||||
// IsName returns true if the string input is a valid Name string.
|
||||
func IsName(s string) bool {
|
||||
return NamePattern.MatchString(s)
|
||||
}
|
||||
|
||||
// namespacedPath checks for valid input and returns api path for a namespaced
|
||||
// objectType. Use namespacedRefPath for objects.
|
||||
func namespacedPath(namespace, objectType string) (string, error) {
|
||||
if err := ValidateNamespace(namespace); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "/namespaces/" + namespace + "/" + objectType, nil
|
||||
}
|
||||
|
||||
// namespacedRefPath checks for valid input and returns api path for a single
|
||||
// namespaced object. Use namespacedPath for objects type path.
|
||||
func namespacedRefPath(namespace, objectType, ref string) (string, error) {
|
||||
if err := ValidateNamespaceAndRef(namespace, ref); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "/namespaces/" + namespace + "/" + objectType + "/" + ref, nil
|
||||
}
|
197
vendor/github.com/storageos/go-api/volume.go
generated
vendored
197
vendor/github.com/storageos/go-api/volume.go
generated
vendored
@ -1,197 +0,0 @@
|
||||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// VolumeAPIPrefix is a partial path to the HTTP endpoint.
|
||||
VolumeAPIPrefix = "volumes"
|
||||
|
||||
// ErrNoSuchVolume is the error returned when the volume does not exist.
|
||||
ErrNoSuchVolume = errors.New("no such volume")
|
||||
|
||||
// ErrVolumeInUse is the error returned when the volume requested to be removed is still in use.
|
||||
ErrVolumeInUse = errors.New("volume in use and cannot be removed")
|
||||
)
|
||||
|
||||
// VolumeList returns the list of available volumes.
|
||||
func (c *Client) VolumeList(opts types.ListOptions) ([]*types.Volume, 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", VolumeAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var volumes []*types.Volume
|
||||
if err := json.NewDecoder(resp.Body).Decode(&volumes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
// Volume returns a volume by its reference.
|
||||
func (c *Client) Volume(namespace string, ref string) (*types.Volume, error) {
|
||||
path, err := namespacedRefPath(namespace, VolumeAPIPrefix, 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, ErrNoSuchVolume
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var volume types.Volume
|
||||
if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// VolumeCreate creates a volume on the server and returns the new object.
|
||||
func (c *Client) VolumeCreate(opts types.VolumeCreateOptions) (*types.Volume, error) {
|
||||
path, err := namespacedPath(opts.Namespace, VolumeAPIPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var volume types.Volume
|
||||
if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// VolumeUpdate updates a volume on the server.
|
||||
func (c *Client) VolumeUpdate(opts types.VolumeUpdateOptions) (*types.Volume, error) {
|
||||
ref := opts.Name
|
||||
if IsUUID(opts.ID) {
|
||||
ref = opts.ID
|
||||
}
|
||||
path, err := namespacedRefPath(opts.Namespace, VolumeAPIPrefix, 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 volume types.Volume
|
||||
if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// VolumeDelete removes a volume by its reference.
|
||||
func (c *Client) VolumeDelete(opts types.DeleteOptions) error {
|
||||
deleteOpts := doOptions{
|
||||
namespace: opts.Namespace,
|
||||
force: opts.Force,
|
||||
context: opts.Context,
|
||||
}
|
||||
resp, err := c.do("DELETE", VolumeAPIPrefix+"/"+opts.Name, deleteOpts)
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchVolume
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrVolumeInUse
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// VolumeMount updates the volume with the client that mounted it.
|
||||
func (c *Client) VolumeMount(opts types.VolumeMountOptions) error {
|
||||
ref := opts.Name
|
||||
if IsUUID(opts.ID) {
|
||||
ref = opts.ID
|
||||
}
|
||||
path, err := namespacedRefPath(opts.Namespace, VolumeAPIPrefix, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := c.do("POST", path+"/mount", doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchVolume
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrVolumeInUse
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// VolumeUnmount removes the client from the mount reference.
|
||||
func (c *Client) VolumeUnmount(opts types.VolumeUnmountOptions) error {
|
||||
ref := opts.Name
|
||||
if IsUUID(opts.ID) {
|
||||
ref = opts.ID
|
||||
}
|
||||
path, err := namespacedRefPath(opts.Namespace, VolumeAPIPrefix, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := c.do("POST", path+"/unmount", doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchVolume
|
||||
}
|
||||
if e.Status == http.StatusConflict {
|
||||
return ErrVolumeInUse
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
7
vendor/modules.txt
vendored
7
vendor/modules.txt
vendored
@ -762,12 +762,6 @@ github.com/spf13/pflag
|
||||
# github.com/stoewer/go-strcase v1.2.0 => github.com/stoewer/go-strcase v1.2.0
|
||||
## explicit; go 1.11
|
||||
github.com/stoewer/go-strcase
|
||||
# github.com/storageos/go-api v2.2.0+incompatible => github.com/storageos/go-api v2.2.0+incompatible
|
||||
## explicit
|
||||
github.com/storageos/go-api
|
||||
github.com/storageos/go-api/netutil
|
||||
github.com/storageos/go-api/serror
|
||||
github.com/storageos/go-api/types
|
||||
# github.com/stretchr/objx v0.2.0 => github.com/stretchr/objx v0.2.0
|
||||
## explicit; go 1.12
|
||||
github.com/stretchr/objx
|
||||
@ -2780,7 +2774,6 @@ sigs.k8s.io/yaml
|
||||
# github.com/spf13/cobra => github.com/spf13/cobra v1.4.0
|
||||
# github.com/spf13/pflag => github.com/spf13/pflag v1.0.5
|
||||
# github.com/stoewer/go-strcase => github.com/stoewer/go-strcase v1.2.0
|
||||
# github.com/storageos/go-api => github.com/storageos/go-api v2.2.0+incompatible
|
||||
# github.com/stretchr/objx => github.com/stretchr/objx v0.2.0
|
||||
# github.com/stretchr/testify => github.com/stretchr/testify v1.7.0
|
||||
# github.com/syndtr/gocapability => github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
|
Loading…
Reference in New Issue
Block a user