Add StorageOS volume plugin

This commit is contained in:
Simon Croome
2017-02-24 15:47:40 +00:00
parent 810efa6689
commit 5e2503e71f
113 changed files with 15630 additions and 3205 deletions

24
vendor/github.com/storageos/go-api/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

47
vendor/github.com/storageos/go-api/BUILD generated vendored Normal file
View File

@@ -0,0 +1,47 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"client.go",
"client_unix.go",
"controller.go",
"event.go",
"namespace.go",
"pool.go",
"rule.go",
"server_version.go",
"template.go",
"util.go",
"validation.go",
"volume.go",
],
tags = ["automanaged"],
deps = [
"//vendor/github.com/gorilla/websocket:go_default_library",
"//vendor/github.com/storageos/go-api/types:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/storageos/go-api/types:all-srcs",
],
tags = ["automanaged"],
)

45
vendor/github.com/storageos/go-api/LICENCE generated vendored Normal file
View File

@@ -0,0 +1,45 @@
MIT License
Copyright (c) 2015-2017 StorageOS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright (c) 2013-2017, go-dockerclient authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2
vendor/github.com/storageos/go-api/README.md generated vendored Normal file
View File

@@ -0,0 +1,2 @@
# StorageOS API client library

620
vendor/github.com/storageos/go-api/client.go generated vendored Normal file
View File

@@ -0,0 +1,620 @@
package storageos
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"reflect"
"strconv"
"strings"
"time"
)
const (
userAgent = "go-storageosclient"
unixProtocol = "unix"
namedPipeProtocol = "npipe"
DefaultVersionStr = "1"
DefaultVersion = 1
defaultNamespace = "default"
)
var (
// ErrInvalidEndpoint is returned when the endpoint is not a valid HTTP URL.
ErrInvalidEndpoint = errors.New("invalid endpoint")
// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
ErrConnectionRefused = errors.New("cannot connect to StorageOS API endpoint")
// ErrInactivityTimeout is returned when a streamable call has been inactive for some time.
ErrInactivityTimeout = errors.New("inactivity time exceeded timeout")
// ErrInvalidVersion is returned when a versioned client was requested but no version specified.
ErrInvalidVersion = errors.New("invalid version")
// DefaultHost is the default API host
DefaultHost = "tcp://localhost:5705"
)
// APIVersion is an internal representation of a version of the Remote API.
type APIVersion int
// NewAPIVersion returns an instance of APIVersion for the given string.
//
// The given string must be in the form <major>
func NewAPIVersion(input string) (APIVersion, error) {
if input == "" {
return DefaultVersion, ErrInvalidVersion
}
version, err := strconv.Atoi(input)
if err != nil {
return 0, fmt.Errorf("Unable to parse version %q", input)
}
return APIVersion(version), nil
}
func (version APIVersion) String() string {
return fmt.Sprintf("v%d", version)
}
// Client is the basic type of this package. It provides methods for
// interaction with the API.
type Client struct {
SkipServerVersionCheck bool
HTTPClient *http.Client
TLSConfig *tls.Config
Dialer Dialer
endpoint string
endpointURL *url.URL
username string
secret string
requestedAPIVersion APIVersion
serverAPIVersion APIVersion
expectedAPIVersion APIVersion
nativeHTTPClient *http.Client
}
// ClientVersion returns the API version of the client
func (c *Client) ClientVersion() string {
return DefaultVersionStr
}
// Dialer is an interface that allows network connections to be dialed
// (net.Dialer fulfills this interface) and named pipes (a shim using
// winio.DialPipe)
type Dialer interface {
Dial(network, address string) (net.Conn, error)
}
// NewClient returns a Client instance ready for communication with the given
// server endpoint. It will use the latest remote API version available in the
// server.
func NewClient(endpoint string) (*Client, error) {
client, err := NewVersionedClient(endpoint, "")
if err != nil {
return nil, err
}
client.SkipServerVersionCheck = true
return client, nil
}
// NewTLSClient returns a Client instance ready for TLS communications with the given
// server endpoint, key and certificates . It will use the latest remote API version
// available in the server.
func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
client, err := NewVersionedTLSClient(endpoint, cert, key, ca, "")
if err != nil {
return nil, err
}
client.SkipServerVersionCheck = true
return client, nil
}
// NewVersionedClient returns a Client instance ready for communication with
// the given server endpoint, using a specific remote API version.
func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
u, err := parseEndpoint(endpoint, false)
if err != nil {
return nil, err
}
c := &Client{
HTTPClient: defaultClient(),
Dialer: &net.Dialer{},
endpoint: endpoint,
endpointURL: u,
}
if apiVersionString != "" {
version, err := strconv.Atoi(apiVersionString)
if err != nil {
return nil, err
}
c.requestedAPIVersion = APIVersion(version)
}
c.initializeNativeClient()
return c, nil
}
// NewVersionedTLSClient returns a Client instance ready for TLS communications with the givens
// server endpoint, key and certificates, using a specific remote API version.
func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
var certPEMBlock []byte
var keyPEMBlock []byte
var caPEMCert []byte
if _, err := os.Stat(cert); !os.IsNotExist(err) {
certPEMBlock, err = ioutil.ReadFile(cert)
if err != nil {
return nil, err
}
}
if _, err := os.Stat(key); !os.IsNotExist(err) {
keyPEMBlock, err = ioutil.ReadFile(key)
if err != nil {
return nil, err
}
}
if _, err := os.Stat(ca); !os.IsNotExist(err) {
caPEMCert, err = ioutil.ReadFile(ca)
if err != nil {
return nil, err
}
}
return NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, apiVersionString)
}
// NewVersionedTLSClientFromBytes returns a Client instance ready for TLS communications with the givens
// server endpoint, key and certificates (passed inline to the function as opposed to being
// read from a local file), using a specific remote API version.
func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte, apiVersionString string) (*Client, error) {
u, err := parseEndpoint(endpoint, true)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{}
if certPEMBlock != nil && keyPEMBlock != nil {
tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{tlsCert}
}
if caPEMCert == nil {
tlsConfig.InsecureSkipVerify = true
} else {
caPool := x509.NewCertPool()
if !caPool.AppendCertsFromPEM(caPEMCert) {
return nil, errors.New("Could not add RootCA pem")
}
tlsConfig.RootCAs = caPool
}
tr := defaultTransport()
tr.TLSClientConfig = tlsConfig
if err != nil {
return nil, err
}
c := &Client{
HTTPClient: &http.Client{Transport: tr},
TLSConfig: tlsConfig,
Dialer: &net.Dialer{},
endpoint: endpoint,
endpointURL: u,
}
if apiVersionString != "" {
version, err := strconv.Atoi(apiVersionString)
if err != nil {
return nil, err
}
c.requestedAPIVersion = APIVersion(version)
}
c.initializeNativeClient()
return c, nil
}
// SetAuth sets the API username and secret to be used for all API requests.
// It should not be called concurrently with any other Client methods.
func (c *Client) SetAuth(username string, secret string) {
if username != "" {
c.username = username
}
if secret != "" {
c.secret = secret
}
}
// SetTimeout takes a timeout and applies it to both the HTTPClient and
// nativeHTTPClient. It should not be called concurrently with any other Client
// methods.
func (c *Client) SetTimeout(t time.Duration) {
if c.HTTPClient != nil {
c.HTTPClient.Timeout = t
}
if c.nativeHTTPClient != nil {
c.nativeHTTPClient.Timeout = t
}
}
func (c *Client) checkAPIVersion() error {
serverAPIVersionString, err := c.getServerAPIVersionString()
if err != nil {
return err
}
c.serverAPIVersion, err = NewAPIVersion(serverAPIVersionString)
if err != nil {
return err
}
if c.requestedAPIVersion == 0 {
c.expectedAPIVersion = c.serverAPIVersion
} else {
c.expectedAPIVersion = c.requestedAPIVersion
}
return nil
}
// Endpoint returns the current endpoint. It's useful for getting the endpoint
// when using functions that get this data from the environment (like
// NewClientFromEnv.
func (c *Client) Endpoint() string {
return c.endpoint
}
// Ping pings the API server
//
// See https://goo.gl/wYfgY1 for more details.
func (c *Client) Ping() error {
urlpath := "/_ping"
resp, err := c.do("GET", urlpath, doOptions{})
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return newError(resp)
}
resp.Body.Close()
return nil
}
func (c *Client) getServerAPIVersionString() (version string, err error) {
v, err := c.ServerVersion(context.Background())
if err != nil {
return "", err
}
return v.APIVersion, nil
}
type doOptions struct {
data interface{}
fieldSelector string
labelSelector string
namespace string
forceJSON bool
force bool
values url.Values
headers map[string]string
unversioned bool
context context.Context
}
func (c *Client) do(method, urlpath string, doOptions doOptions) (*http.Response, error) {
var params io.Reader
if doOptions.data != nil || doOptions.forceJSON {
buf, err := json.Marshal(doOptions.data)
if err != nil {
return nil, err
}
params = bytes.NewBuffer(buf)
}
// Prefix the path with the namespace if given. The caller should only set
// the namespace if this is desired.
if doOptions.namespace != "" {
urlpath = "/" + NamespaceAPIPrefix + "/" + doOptions.namespace + "/" + urlpath
}
if !c.SkipServerVersionCheck && !doOptions.unversioned {
err := c.checkAPIVersion()
if err != nil {
return nil, err
}
}
query := url.Values{}
if doOptions.values != nil {
query = doOptions.values
}
if doOptions.force {
query.Add("force", "1")
}
httpClient := c.HTTPClient
protocol := c.endpointURL.Scheme
var u string
switch protocol {
case unixProtocol, namedPipeProtocol:
httpClient = c.nativeHTTPClient
u = c.getFakeNativeURL(urlpath, doOptions.unversioned)
default:
u = c.getAPIPath(urlpath, query, doOptions.unversioned)
}
req, err := http.NewRequest(method, u, params)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", userAgent)
if doOptions.data != nil {
req.Header.Set("Content-Type", "application/json")
} else if method == "POST" {
req.Header.Set("Content-Type", "plain/text")
}
if c.username != "" && c.secret != "" {
req.SetBasicAuth(c.username, c.secret)
}
for k, v := range doOptions.headers {
req.Header.Set(k, v)
}
ctx := doOptions.context
if ctx == nil {
ctx = context.Background()
}
resp, err := httpClient.Do(req.WithContext(ctx))
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return nil, ErrConnectionRefused
}
return nil, chooseError(ctx, err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
return nil, newError(resp)
}
return resp, nil
}
// if error in context, return that instead of generic http error
func chooseError(ctx context.Context, err error) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return err
}
}
func (c *Client) getURL(path string, unversioned bool) string {
urlStr := strings.TrimRight(c.endpointURL.String(), "/")
path = strings.TrimLeft(path, "/")
if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol {
urlStr = ""
}
if unversioned {
return fmt.Sprintf("%s/%s", urlStr, path)
}
return fmt.Sprintf("%s/%s/%s", urlStr, c.requestedAPIVersion, path)
}
func (c *Client) getAPIPath(path string, query url.Values, unversioned bool) string {
var apiPath string
urlStr := strings.TrimRight(c.endpointURL.String(), "/")
path = strings.TrimLeft(path, "/")
if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol {
urlStr = ""
}
if unversioned {
apiPath = fmt.Sprintf("%s/%s", urlStr, path)
} else {
apiPath = fmt.Sprintf("%s/%s/%s", urlStr, c.requestedAPIVersion, path)
}
if len(query) > 0 {
apiPath = apiPath + "?" + query.Encode()
}
return apiPath
}
// getFakeNativeURL returns the URL needed to make an HTTP request over a UNIX
// domain socket to the given path.
func (c *Client) getFakeNativeURL(path string, unversioned bool) string {
u := *c.endpointURL // Copy.
// Override URL so that net/http will not complain.
u.Scheme = "http"
u.Host = "unix.sock" // Doesn't matter what this is - it's not used.
u.Path = ""
urlStr := strings.TrimRight(u.String(), "/")
path = strings.TrimLeft(path, "/")
if unversioned {
return fmt.Sprintf("%s/%s", urlStr, path)
}
return fmt.Sprintf("%s/%s/%s", urlStr, c.requestedAPIVersion, path)
}
type jsonMessage struct {
Status string `json:"status,omitempty"`
Progress string `json:"progress,omitempty"`
Error string `json:"error,omitempty"`
Stream string `json:"stream,omitempty"`
}
func queryString(opts interface{}) string {
if opts == nil {
return ""
}
value := reflect.ValueOf(opts)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
if value.Kind() != reflect.Struct {
return ""
}
items := url.Values(map[string][]string{})
for i := 0; i < value.NumField(); i++ {
field := value.Type().Field(i)
if field.PkgPath != "" {
continue
}
key := field.Tag.Get("qs")
if key == "" {
key = strings.ToLower(field.Name)
} else if key == "-" {
continue
}
addQueryStringValue(items, key, value.Field(i))
}
return items.Encode()
}
func addQueryStringValue(items url.Values, key string, v reflect.Value) {
switch v.Kind() {
case reflect.Bool:
if v.Bool() {
items.Add(key, "1")
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if v.Int() > 0 {
items.Add(key, strconv.FormatInt(v.Int(), 10))
}
case reflect.Float32, reflect.Float64:
if v.Float() > 0 {
items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
}
case reflect.String:
if v.String() != "" {
items.Add(key, v.String())
}
case reflect.Ptr:
if !v.IsNil() {
if b, err := json.Marshal(v.Interface()); err == nil {
items.Add(key, string(b))
}
}
case reflect.Map:
if len(v.MapKeys()) > 0 {
if b, err := json.Marshal(v.Interface()); err == nil {
items.Add(key, string(b))
}
}
case reflect.Array, reflect.Slice:
vLen := v.Len()
if vLen > 0 {
for i := 0; i < vLen; i++ {
addQueryStringValue(items, key, v.Index(i))
}
}
}
}
// Error represents failures in the API. It represents a failure from the API.
type Error struct {
Status int
Message string
}
func newError(resp *http.Response) *Error {
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return &Error{Status: resp.StatusCode, Message: fmt.Sprintf("cannot read body, err: %v", err)}
}
return &Error{Status: resp.StatusCode, Message: string(data)}
}
func (e *Error) Error() string {
return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
}
func parseEndpoint(endpoint string, tls bool) (*url.URL, error) {
if endpoint != "" && !strings.Contains(endpoint, "://") {
endpoint = "tcp://" + endpoint
}
u, err := url.Parse(endpoint)
if err != nil {
return nil, ErrInvalidEndpoint
}
if tls && u.Scheme != "unix" {
u.Scheme = "https"
}
switch u.Scheme {
case unixProtocol, namedPipeProtocol:
return u, nil
case "http", "https", "tcp":
_, port, err := net.SplitHostPort(u.Host)
if err != nil {
if e, ok := err.(*net.AddrError); ok {
if e.Err == "missing port in address" {
return u, nil
}
}
return nil, ErrInvalidEndpoint
}
number, err := strconv.ParseInt(port, 10, 64)
if err == nil && number > 0 && number < 65536 {
if u.Scheme == "tcp" {
if tls {
u.Scheme = "https"
} else {
u.Scheme = "http"
}
}
return u, nil
}
return nil, ErrInvalidEndpoint
default:
return nil, ErrInvalidEndpoint
}
}
// defaultTransport returns a new http.Transport with the same default values
// as http.DefaultTransport, but with idle connections and keepalives disabled.
func defaultTransport() *http.Transport {
transport := defaultPooledTransport()
transport.DisableKeepAlives = true
transport.MaxIdleConnsPerHost = -1
return transport
}
// defaultPooledTransport returns a new http.Transport with similar default
// values to http.DefaultTransport. Do not use this for transient transports as
// it can leak file descriptors over time. Only use this for transports that
// will be re-used for the same host(s).
func defaultPooledTransport() *http.Transport {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: false,
MaxIdleConnsPerHost: 1,
}
return transport
}
// defaultClient returns a new http.Client with similar default values to
// http.Client, but with a non-shared Transport, idle connections disabled, and
// keepalives disabled.
func defaultClient() *http.Client {
return &http.Client{
Transport: defaultTransport(),
}
}

25
vendor/github.com/storageos/go-api/client_unix.go generated vendored Normal file
View File

@@ -0,0 +1,25 @@
// +build !windows
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package storageos
import (
"net"
"net/http"
)
// initializeNativeClient initializes the native Unix domain socket client on
// Unix-style operating systems
func (c *Client) initializeNativeClient() {
if c.endpointURL.Scheme != unixProtocol {
return
}
socketPath := c.endpointURL.Path
tr := defaultTransport()
tr.Dial = func(network, addr string) (net.Conn, error) {
return c.Dialer.Dial(unixProtocol, socketPath)
}
c.nativeHTTPClient = &http.Client{Transport: tr}
}

40
vendor/github.com/storageos/go-api/client_windows.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
// +build windows
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package storageos
import (
"net"
"net/http"
"time"
"github.com/Microsoft/go-winio"
)
const namedPipeConnectTimeout = 2 * time.Second
type pipeDialer struct {
dialFunc func(network, addr string) (net.Conn, error)
}
func (p pipeDialer) Dial(network, address string) (net.Conn, error) {
return p.dialFunc(network, address)
}
// initializeNativeClient initializes the native Named Pipe client for Windows
func (c *Client) initializeNativeClient() {
if c.endpointURL.Scheme != namedPipeProtocol {
return
}
namedPipePath := c.endpointURL.Path
dialFunc := func(network, addr string) (net.Conn, error) {
timeout := namedPipeConnectTimeout
return winio.DialPipe(namedPipePath, &timeout)
}
tr := defaultTransport()
tr.Dial = dialFunc
c.Dialer = &pipeDialer{dialFunc}
c.nativeHTTPClient = &http.Client{Transport: tr}
}

111
vendor/github.com/storageos/go-api/controller.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
package storageos
import (
"encoding/json"
"errors"
"net/http"
"net/url"
"github.com/storageos/go-api/types"
)
var (
// ControllerAPIPrefix is a partial path to the HTTP endpoint.
ControllerAPIPrefix = "controllers"
// ErrNoSuchController is the error returned when the controller does not exist.
ErrNoSuchController = errors.New("no such controller")
// ErrControllerInUse is the error returned when the controller requested to be removed is still in use.
ErrControllerInUse = errors.New("controller in use and cannot be removed")
)
// ControllerList returns the list of available controllers.
func (c *Client) ControllerList(opts types.ListOptions) ([]*types.Controller, error) {
listOpts := doOptions{
fieldSelector: opts.FieldSelector,
labelSelector: opts.LabelSelector,
namespace: opts.Namespace,
context: opts.Context,
}
if opts.LabelSelector != "" {
query := url.Values{}
query.Add("labelSelector", opts.LabelSelector)
listOpts.values = query
}
resp, err := c.do("GET", ControllerAPIPrefix, listOpts)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var controllers []*types.Controller
if err := json.NewDecoder(resp.Body).Decode(&controllers); err != nil {
return nil, err
}
return controllers, nil
}
// Controller returns a controller by its reference.
func (c *Client) Controller(ref string) (*types.Controller, error) {
resp, err := c.do("GET", ControllerAPIPrefix+"/"+ref, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchController
}
return nil, err
}
defer resp.Body.Close()
var controller types.Controller
if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil {
return nil, err
}
return &controller, nil
}
// ControllerUpdate updates a controller on the server.
func (c *Client) ControllerUpdate(opts types.ControllerUpdateOptions) (*types.Controller, error) {
ref := opts.Name
if IsUUID(opts.ID) {
ref = opts.ID
}
resp, err := c.do("PUT", ControllerAPIPrefix+"/"+ref, doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var controller types.Controller
if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil {
return nil, err
}
return &controller, nil
}
// ControllerDelete removes a controller by its reference.
func (c *Client) ControllerDelete(opts types.DeleteOptions) error {
deleteOpts := doOptions{
namespace: opts.Namespace,
force: opts.Force,
context: opts.Context,
}
resp, err := c.do("DELETE", ControllerAPIPrefix+"/"+opts.Name, deleteOpts)
if err != nil {
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
return ErrNoSuchController
}
if e.Status == http.StatusConflict {
return ErrControllerInUse
}
}
return err
}
defer resp.Body.Close()
return nil
}

189
vendor/github.com/storageos/go-api/event.go generated vendored Normal file
View File

@@ -0,0 +1,189 @@
package storageos
import (
"context"
"encoding/json"
"errors"
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
"github.com/storageos/go-api/types"
)
var (
// EventAPIPrefix is a partial path to the HTTP endpoint.
EventAPIPrefix = "event"
// ErrNoSuchEvent is the error returned when the event does not exist.
ErrNoSuchEvent = errors.New("no such event")
)
// EventList returns the list of available events.
func (c *Client) EventList(opts types.ListOptions) ([]*types.Event, error) {
listOpts := doOptions{
fieldSelector: opts.FieldSelector,
labelSelector: opts.LabelSelector,
context: opts.Context,
}
resp, err := c.do("GET", EventAPIPrefix, listOpts)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var events []*types.Event
if err := json.NewDecoder(resp.Body).Decode(&events); err != nil {
return nil, err
}
return events, nil
}
// Events returns a stream of events in the daemon. It's up to the caller to close the stream
// by cancelling the context. Once the stream has been completely read an io.EOF error will
// be sent over the error channel. If an error is sent all processing will be stopped. It's up
// to the caller to reopen the stream in the event of an error by reinvoking this method.
func (c *Client) Events(ctx context.Context, opts types.ListOptions) (<-chan types.Request, <-chan error) {
// listOpts := doOptions{
// fieldSelector: opts.FieldSelector,
// labelSelector: opts.LabelSelector,
// context: ctx,
// }
messages := make(chan types.Request)
errs := make(chan error, 1)
// started := make(chan struct{})
ws, _, err := websocket.DefaultDialer.Dial("ws://10.245.103.2:8000/v1/ws/event", nil)
if err != nil {
// close(started)
// errs <- err
log.Fatal(err)
}
// defer ws.Close()
done := make(chan struct{})
go func() {
defer ws.Close()
defer close(done)
for {
_, message, err := ws.ReadMessage()
if err != nil {
log.Println("read:", err)
errs <- err
return
}
// log.Printf("recv: %s", message)
var request types.Request
if err := json.Unmarshal(message, &request); err != nil {
log.Printf("decode error: %s", message)
errs <- err
return
}
messages <- request
}
}()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
go func() {
for {
select {
case t := <-ticker.C:
log.Printf("tick: %s\n", t.String())
err := ws.WriteMessage(websocket.TextMessage, []byte(t.String()))
if err != nil {
log.Println("write:", err)
return
}
case <-ctx.Done():
log.Println("done")
err := ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("write close:", err)
return
}
errs <- ctx.Err()
select {
case <-done:
case <-time.After(time.Second):
}
ws.Close()
return
}
}
}()
// go func() {
// defer ws.Close()
// defer close(errs)
//
// // query, err := buildEventsQueryParams(cli.version, options)
// // if err != nil {
// // close(started)
// // errs <- err
// // return
// // }
//
// // resp, err := cli.get(ctx, "/events", query, nil)
//
// // decoder := json.NewDecoder(resp.Body)
//
// close(started)
// for {
// select {
// case <-ctx.Done():
// log.Println("done")
// errs <- ctx.Err()
// return
// default:
// log.Println("default")
// _, message, err := ws.ReadMessage()
// if err != nil {
// log.Println("read:", err)
// return
// }
// log.Printf("recv: %s", message)
// var event types.Event
// if err := json.Unmarshal(message, &event); err != nil {
// log.Printf("decode error: %s", message)
// errs <- err
// return
// }
// log.Printf("sent: %v", event)
// messages <- event
//
// // select {
// // case messages <- event:
// // case <-ctx.Done():
// // errs <- ctx.Err()
// // return
// // }
// }
// }
// }()
// <-started
log.Println("returning")
return messages, errs
}
// Event returns a event by its reference.
func (c *Client) Event(ref string) (*types.Event, error) {
resp, err := c.do("GET", EventAPIPrefix+"/"+ref, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchEvent
}
return nil, err
}
defer resp.Body.Close()
var event types.Event
if err := json.NewDecoder(resp.Body).Decode(&event); err != nil {
return nil, err
}
return &event, nil
}

125
vendor/github.com/storageos/go-api/namespace.go generated vendored Normal file
View File

@@ -0,0 +1,125 @@
package storageos
import (
"encoding/json"
"errors"
"net/http"
"net/url"
"github.com/storageos/go-api/types"
)
var (
// NamespaceAPIPrefix is a partial path to the HTTP endpoint.
NamespaceAPIPrefix = "namespaces"
// ErrNoSuchNamespace is the error returned when the namespace does not exist.
ErrNoSuchNamespace = errors.New("no such namespace")
// ErrNamespaceInUse is the error returned when the namespace requested to be removed is still in use.
ErrNamespaceInUse = errors.New("namespace in use and cannot be removed")
)
// NamespaceList returns the list of available namespaces.
func (c *Client) NamespaceList(opts types.ListOptions) ([]*types.Namespace, error) {
listOpts := doOptions{
fieldSelector: opts.FieldSelector,
labelSelector: opts.LabelSelector,
namespace: opts.Namespace,
context: opts.Context,
}
if opts.LabelSelector != "" {
query := url.Values{}
query.Add("labelSelector", opts.LabelSelector)
listOpts.values = query
}
resp, err := c.do("GET", NamespaceAPIPrefix, listOpts)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var namespaces []*types.Namespace
if err := json.NewDecoder(resp.Body).Decode(&namespaces); err != nil {
return nil, err
}
return namespaces, nil
}
// Namespace returns a namespace by its reference.
func (c *Client) Namespace(ref string) (*types.Namespace, error) {
resp, err := c.do("GET", NamespaceAPIPrefix+"/"+ref, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchNamespace
}
return nil, err
}
defer resp.Body.Close()
var namespace types.Namespace
if err := json.NewDecoder(resp.Body).Decode(&namespace); err != nil {
return nil, err
}
return &namespace, nil
}
// NamespaceCreate creates a namespace on the server and returns the new object.
func (c *Client) NamespaceCreate(opts types.NamespaceCreateOptions) (*types.Namespace, error) {
resp, err := c.do("POST", NamespaceAPIPrefix, doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
return nil, err
}
var namespace types.Namespace
if err := json.NewDecoder(resp.Body).Decode(&namespace); err != nil {
return nil, err
}
return &namespace, nil
}
// NamespaceUpdate updates a namespace on the server and returns the updated object.
func (c *Client) NamespaceUpdate(opts types.NamespaceCreateOptions) (*types.Namespace, error) {
resp, err := c.do("PUT", NamespaceAPIPrefix+"/"+opts.Name, doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
return nil, err
}
var namespace types.Namespace
if err := json.NewDecoder(resp.Body).Decode(&namespace); err != nil {
return nil, err
}
return &namespace, nil
}
// NamespaceDelete removes a namespace by its reference.
func (c *Client) NamespaceDelete(opts types.DeleteOptions) error {
deleteOpts := doOptions{
force: opts.Force,
context: opts.Context,
}
resp, err := c.do("DELETE", NamespaceAPIPrefix+"/"+opts.Name, deleteOpts)
if err != nil {
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
return ErrNoSuchNamespace
}
if e.Status == http.StatusConflict {
return ErrNamespaceInUse
}
// namespace can't be deleted yet, unless force is supplied
if e.Status == http.StatusPreconditionFailed {
return err
}
}
return err
}
defer resp.Body.Close()
return nil
}

96
vendor/github.com/storageos/go-api/pool.go generated vendored Normal file
View File

@@ -0,0 +1,96 @@
package storageos
import (
"encoding/json"
"errors"
"net/http"
"github.com/storageos/go-api/types"
)
var (
// PoolAPIPrefix is a partial path to the HTTP endpoint.
PoolAPIPrefix = "pools"
// ErrNoSuchPool is the error returned when the pool does not exist.
ErrNoSuchPool = errors.New("no such pool")
// ErrPoolInUse is the error returned when the pool requested to be removed is still in use.
ErrPoolInUse = errors.New("pool in use and cannot be removed")
)
// PoolList returns the list of available pools.
func (c *Client) PoolList(opts types.ListOptions) ([]*types.Pool, error) {
listOpts := doOptions{
fieldSelector: opts.FieldSelector,
labelSelector: opts.LabelSelector,
namespace: opts.Namespace,
context: opts.Context,
}
resp, err := c.do("GET", PoolAPIPrefix, listOpts)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var pools []*types.Pool
if err := json.NewDecoder(resp.Body).Decode(&pools); err != nil {
return nil, err
}
return pools, nil
}
// PoolCreate creates a pool on the server and returns the new object.
func (c *Client) PoolCreate(opts types.PoolCreateOptions) (*types.Pool, error) {
resp, err := c.do("POST", PoolAPIPrefix, doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
return nil, err
}
var pool types.Pool
if err := json.NewDecoder(resp.Body).Decode(&pool); err != nil {
return nil, err
}
return &pool, nil
}
// Pool returns a pool by its reference.
func (c *Client) Pool(ref string) (*types.Pool, error) {
resp, err := c.do("GET", PoolAPIPrefix+"/"+ref, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchPool
}
return nil, err
}
defer resp.Body.Close()
var pool types.Pool
if err := json.NewDecoder(resp.Body).Decode(&pool); err != nil {
return nil, err
}
return &pool, nil
}
// PoolDelete removes a pool by its reference.
func (c *Client) PoolDelete(opts types.DeleteOptions) error {
deleteOpts := doOptions{
force: opts.Force,
context: opts.Context,
}
resp, err := c.do("DELETE", PoolAPIPrefix+"/"+opts.Name, deleteOpts)
if err != nil {
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
return ErrNoSuchPool
}
if e.Status == http.StatusConflict {
return ErrPoolInUse
}
}
return nil
}
defer resp.Body.Close()
return nil
}

137
vendor/github.com/storageos/go-api/rule.go generated vendored Normal file
View File

@@ -0,0 +1,137 @@
package storageos
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"github.com/storageos/go-api/types"
)
var (
// RuleAPIPrefix is a partial path to the HTTP endpoint.
RuleAPIPrefix = "rules"
// ErrNoSuchRule is the error returned when the rule does not exist.
ErrNoSuchRule = errors.New("no such rule")
// ErrRuleInUse is the error returned when the rule requested to be removed is still in use.
ErrRuleInUse = errors.New("rule in use and cannot be removed")
)
// RuleList returns the list of available rules.
func (c *Client) RuleList(opts types.ListOptions) ([]*types.Rule, error) {
listOpts := doOptions{
fieldSelector: opts.FieldSelector,
labelSelector: opts.LabelSelector,
namespace: opts.Namespace,
context: opts.Context,
}
if opts.LabelSelector != "" {
query := url.Values{}
query.Add("labelSelector", opts.LabelSelector)
listOpts.values = query
}
resp, err := c.do("GET", RuleAPIPrefix, listOpts)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var rules []*types.Rule
if err := json.NewDecoder(resp.Body).Decode(&rules); err != nil {
return nil, err
}
return rules, nil
}
// Rule returns a rule by its reference.
func (c *Client) Rule(namespace string, ref string) (*types.Rule, error) {
path, err := namespacedRefPath(namespace, RuleAPIPrefix, ref)
if err != nil {
return nil, err
}
resp, err := c.do("GET", path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchRule
}
return nil, err
}
defer resp.Body.Close()
var rule types.Rule
if err := json.NewDecoder(resp.Body).Decode(&rule); err != nil {
return nil, err
}
return &rule, nil
}
// RuleCreate creates a rule on the server and returns the new object.
func (c *Client) RuleCreate(opts types.RuleCreateOptions) (*types.Rule, error) {
resp, err := c.do("POST", RuleAPIPrefix, doOptions{
data: opts,
namespace: opts.Namespace,
context: opts.Context,
})
if err != nil {
return nil, err
}
var rule types.Rule
if err := json.NewDecoder(resp.Body).Decode(&rule); err != nil {
return nil, err
}
return &rule, nil
}
// RuleUpdate updates a rule on the server.
func (c *Client) RuleUpdate(opts types.RuleUpdateOptions) (*types.Rule, error) {
ref := opts.Name
if IsUUID(opts.ID) {
ref = opts.ID
}
fmt.Printf("%#v\n", opts)
path, err := namespacedRefPath(opts.Namespace, RuleAPIPrefix, ref)
if err != nil {
return nil, err
}
resp, err := c.do("PUT", path, doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var rule types.Rule
if err := json.NewDecoder(resp.Body).Decode(&rule); err != nil {
return nil, err
}
return &rule, nil
}
// RuleDelete removes a rule by its reference.
func (c *Client) RuleDelete(opts types.DeleteOptions) error {
deleteOpts := doOptions{
namespace: opts.Namespace,
force: opts.Force,
context: opts.Context,
}
resp, err := c.do("DELETE", RuleAPIPrefix+"/"+opts.Name, deleteOpts)
if err != nil {
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
return ErrNoSuchRule
}
if e.Status == http.StatusConflict {
return ErrRuleInUse
}
}
return nil
}
defer resp.Body.Close()
return nil
}

28
vendor/github.com/storageos/go-api/server_version.go generated vendored Normal file
View File

@@ -0,0 +1,28 @@
package storageos
import (
"context"
"encoding/json"
"net/http"
"github.com/storageos/go-api/types"
)
// ServerVersion returns the server's version and runtime info.
func (c *Client) ServerVersion(ctx context.Context) (*types.VersionInfo, error) {
// Send as unversioned
resp, err := c.do("GET", "version", doOptions{context: ctx, unversioned: true})
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, newError(resp)
}
defer resp.Body.Close()
var version types.VersionInfo
if err := json.NewDecoder(resp.Body).Decode(&version); err != nil {
return nil, err
}
return &version, nil
}

12
vendor/github.com/storageos/go-api/swagger-gen.yaml generated vendored Normal file
View File

@@ -0,0 +1,12 @@
layout:
models:
- name: definition
source: asset:model
target: "{{ joinFilePath .Target .ModelPackage }}"
file_name: "{{ (snakize (pascalize .Name)) }}.go"
operations:
- name: handler
source: asset:serverOperation
target: "{{ joinFilePath .Target .APIPackage .Package }}"
file_name: "{{ (snakize (pascalize .Name)) }}.go"

854
vendor/github.com/storageos/go-api/swagger.yaml generated vendored Normal file
View File

@@ -0,0 +1,854 @@
# A Swagger 2.0 (a.k.a. OpenAPI) definition of the StorageOS API.
#
# This is used for generating API documentation and the types used by the
# client/server. See api/README.md for more information.
#
# Some style notes:
# - This file is used by ReDoc, which allows GitHub Flavored Markdown in
# descriptions.
# - There is no maximum line length, for ease of editing and pretty diffs.
# - operationIds are in the format "NounVerb", with a singular noun.
swagger: "2.0"
schemes:
- "http"
- "https"
produces:
- "application/json"
- "text/plain"
consumes:
- "application/json"
- "text/plain"
basePath: "/v1"
info:
title: "StorageOS API"
version: "0.7"
x-logo:
url: "http://storageos.wpengine.com/wp-content/uploads/2017/03/cropped-logo-1.png"
description: |
The StorageOS API is an HTTP API used for managing volumes and StorageOS services. It is the API that the StorageOS UI, CLI and platform integrations use to communicate with the StorageOS backend.
# Errors
The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format:
```
{
"message": "page not found"
}
```
# The tags on paths define the menu sections in the ReDoc documentation, so
# the usage of tags must make sense for that:
# - They should be singular, not plural.
# - There should not be too many tags, or the menu becomes unwieldy. For
# example, it is preferable to add a path to the "System" tag instead of
# creating a tag with a single path in it.
# - The order of tags in this list defines the order in the menu.
tags:
# Primary objects
- name: "Volume"
x-displayName: "Volumes"
description: |
Create and manage volumes.
- name: "Pool"
x-displayName: "Pools"
description: |
Create and manage distributed capacity pools.
definitions:
ErrorResponse:
description: "Represents an error."
type: "object"
required: ["message"]
properties:
message:
description: "The error message."
type: "string"
x-nullable: false
example:
message: "Something went wrong."
Deployment:
type: "object"
description: "Volume master or replica deployment details."
properties:
ID:
type: "string"
readOnly: true
Controller:
type: "string"
readOnly: true
Inode:
type: "integer"
format: "uint32"
readOnly: true
Status:
type: "string"
readOnly: true
Health:
type: "string"
readOnly: true
CreatedAt:
type: "string"
format: "datetime"
readOnly: true
VolumeCreateOptions:
type: "object"
description: "Parameters available for creating new volumes."
required: [Name]
properties:
Name:
description: "Volume name."
type: "string"
x-nullable: false
Description:
type: "string"
x-nullable: false
description: "Volume description."
Size:
type: "integer"
description: "Size in GB (if 0 or not specified, then defaults to 10 GB)."
x-nullable: false
Pool:
type: "string"
description: "Name of capacity pool to provision the volume in, or the name of the current pool."
Labels:
type: "object"
description: "User-defined key/value metadata."
x-nullable: false
additionalProperties:
type: "string"
VolumeUpdateOptions:
type: "object"
description: "Parameters available for updating existing volumes."
properties:
Description:
type: "string"
x-nullable: false
description: "Volume description."
Size:
type: "integer"
description: "Size in GB."
x-nullable: false
Labels:
type: "object"
description: "User-defined key/value metadata."
x-nullable: false
additionalProperties:
type: "string"
# VolumeMountOptions:
# type: "object"
# description: "Parameters available for mounting volumes."
# properties:
# ID:
# type: "string"
# x-nullable: false
# description: "Volume unique ID."
# Name:
# description: "Volume name."
# type: "string"
# x-nullable: false
# Namespace:
# description: "The object scope, such as for teams and projects."
# type: "string"
# x-nullable: false
# Client:
# type: "string"
# x-nullable: false
# description: "Hostname of the client performing the mount."
#
# VolumeUnmountOptions:
# type: "object"
# description: "Parameters available for unmounting volumes."
# properties:
# ID:
# type: "string"
# x-nullable: false
# description: "Volume unique ID."
# Name:
# description: "Volume name."
# type: "string"
# x-nullable: false
# Namespace:
# description: "The object scope, such as for teams and projects."
# type: "string"
# x-nullable: false
# Client:
# type: "string"
# x-nullable: false
# description: "Hostname of the client performing the unmount."
# ListOptions:
# type: "object"
# description: "Parameters for finding volumes."
# properties:
# LabelSelector:
# description: "A selector to restrict the list of returned objects by their labels. Defaults to everything."
# type: "string"
# FieldSelector:
# type: "string"
# description: "A selector to restrict the list of returned objects by their fields. Defaults to everything."
# TimeoutSeconds:
# type: "integer"
# description: "Timeout for the list call."
# Namespace:
# type: "string"
# description: "Object name and auth scope, such as for teams and projects"
Volume:
type: "object"
description: "A storage volume."
required: [Name, Size]
properties:
ID:
description: "Volume unique ID."
type: "string"
x-nullable: false
readOnly: true
Name:
description: "Volume name."
type: "string"
x-nullable: false
Description:
type: "string"
x-nullable: false
description: "Volume description."
Size:
type: integer
description: "Size in GB."
x-nullable: false
Pool:
type: "string"
description: "Name of capacity pool to provision the volume in, or the name of the current pool."
Labels:
type: "object"
description: "User-defined key/value metadata."
x-nullable: false
additionalProperties:
type: "string"
Master:
$ref: "#/definitions/Deployment"
Replicas:
type: "array"
description: "Volume deployment information for the replica volumes."
items:
$ref: "#/definitions/Deployment"
readOnly: true
Status:
type: "string"
description: "Short status, one of: pending, evaluating, deploying, active, unavailable, failed, updating, deleting."
readOnly: true
StatusMessage:
type: "string"
description: "Status message explaining current status."
readOnly: true
Health:
type: "string"
description: "Volume health, one of: healthy, degraded or dead."
readOnly: true
Inode:
type: "integer"
format: "uint32"
description: "Block device inode."
readOnly: true
Deleted:
type: "boolean"
description: "Flag indicating if the volume has been deleted and is waiting for scrubbing."
readOnly: true
Mounted:
type: "boolean"
description: "Flag indicating if the volume is mounted and in use."
readOnly: true
MountedBy:
type: "string"
description: "Reference to the node that has the volume mounted."
readOnly: true
Mountpoint:
type: "string"
description: "Mountpoint where the volume was mounted."
readOnly: true
MountedAt:
type: "string"
format: "dateTime"
description: "When the volume was mounted."
readOnly: true
CreatedBy:
type: "string"
description: "User that created the volume."
readOnly: true
CreatedAt:
type: "string"
format: "dateTime"
description: "When the volume was created."
readOnly: true
example:
Name: vol01
Size: 5
Labels:
com.example.some-label: "some-value"
com.example.some-other-label: "some-other-value"
PoolCreateOptions:
type: "object"
description: "Parameters available for creating new pools."
required: [Name]
properties:
Name:
description: "Pool name."
type: "string"
x-nullable: false
Description:
type: "string"
x-nullable: false
description: "Pool description."
Default:
type: "boolean"
description: "Default determines whether this pool is the default if a volume is provisioned without a pool specified. There can only be one default pool."
x-nullable: false
DefaultDriver:
type: "string"
x-nullable: false
description: "DefaultDriver specifies the storage driver to use by default if there are multiple drivers in the pool and no driver was specified in the provisioning request or assigned by rules. If no driver was specified and no default set, driver weight is used to determine the default."
ControllerNames:
type: "array"
description: "ControllerNames is a list of controller names that are participating in the storage pool."
items:
type: "string"
DriverNames:
type: "array"
description: "DriverNames is a list of backend storage drivers that are available in the storage pool."
items:
type: "string"
Active:
type: "boolean"
x-nullable: false
description: "Flag describing whether rule is active."
default: false
Labels:
type: "object"
description: "Labels define a list of labels that describe the pool."
additionalProperties:
type: "string"
Pool:
type: "object"
description: |
Pools are used to define distributed capacity that can be used to provision
volumes from. Typically, each server that makes storage available will be
added to one or more pools.
Capacity drivers are also added to the pool to determine which backend
storage driver to use. Currently this is limited to a single type of
driver per pool, but in the future we will allow multiple, allowing for
dynamic tiering and snapshots from one driver type to another.
required: [Name]
properties:
ID:
description: "Pool unique ID."
type: "string"
x-nullable: false
readOnly: true
Name:
description: "Pool name."
type: "string"
x-nullable: false
Description:
type: "string"
x-nullable: false
description: "Pool description."
Default:
type: "boolean"
x-nullable: false
description: |
Default determines whether this pool is the default if a volume is
provisioned without a pool specified. There can only be one default
pool.
DefaultDriver:
type: "string"
description: |
DefaultDriver specifies the storage driver to use by default if there
are multiple drivers in the pool and no driver was specified in the
provisioning request or assigned by rules. If no driver was specified
and no default set, driver weight is used to determine the default.
ControllerNames:
type: "array"
description: "ControllerNames is a list of controller names that are participating in the storage pool."
items:
type: "string"
DriverNames:
type: "array"
description: "DriverNames is a list of backend storage drivers that are available in the storage pool."
items:
type: "string"
DriverInstances:
$ref: "#/definitions/DriverInstances"
Active:
type: "boolean"
x-nullable: false
description: "Flag describing whether rule is active."
default: false
CapacityStats:
$ref: "#/definitions/CapacityStats"
Labels:
type: "object"
description: "Labels define a list of labels that describe the pool."
additionalProperties:
type: "string"
Rule:
type: "object"
description: "A policy rule."
required: [Name]
properties:
ID:
description: "Rule unique ID."
type: "string"
x-nullable: false
readOnly: true
Name:
description: "Rule name."
type: "string"
x-nullable: false
Description:
type: "string"
x-nullable: false
description: "Rule description."
Active:
type: "boolean"
x-nullable: false
description: "Flag describing whether rule is active."
default: false
Weight:
type: "integer"
x-nullable: false
description: |
"Weight is used to determine order during rule processing. Rules with heavier weights are processed later."
default: 0
Operator:
type: "string"
description: "Operator is used to compare objects or labels."
enum:
- "!"
- "="
- "=="
- "in"
- "!="
- "notin"
- "exists"
- "gt"
- "lt"
RuleAction:
type: "string"
description: "RuleAction controls whether the action is to add or remove a label from the matching object(s)."
enum:
- "add"
- "remove"
default: "add"
Selectors:
type: "object"
description: "Selectors defines the list of labels that should trigger a rule."
additionalProperties:
type: "string"
Labels:
type: "object"
description: "Labels define the list of labels that will be added or removed from the matching object(s).."
additionalProperties:
type: "string"
CapacityStats:
type: "object"
description: "CapacityStats is used to report capacity statistics on pools and controllers."
properties:
TotalCapacityBytes:
description: "TotalCapacityBytes is the object's total capacity in bytes."
type: "integer"
readOnly: true
AvailableCapacityBytes:
description: "AvailableCapacityBytes is the object's available capacity in bytes."
type: "integer"
readOnly: true
ProvisionedCapacityBytes:
description: "ProvisionedCapacityBytes is the object's provisioned capacity in bytes."
type: "integer"
readOnly: true
DriverInstances:
type: "object"
description: "DriverInstances shows the internal configuration and state of each driver on all the nodes in the pool. Data within DriverInstances can not be modified directly."
properties:
ID:
description: "Instance unique ID."
type: "string"
x-nullable: false
readOnly: true
Name:
description: "Instance name."
type: "string"
x-nullable: false
readOnly: true
Description:
description: "Instance description."
type: "string"
x-nullable: false
readOnly: true
Active:
description: "Flag describing whether the template is active."
type: "boolean"
x-nullable: false
readOnly: true
Config:
description: "Config is JSON struct that is passed directly to the driver. There is no specific format, and the driver is responsible for validation."
type: "object"
readOnly: true
additionalProperties:
type: "string"
Labels:
description: "Labels define a list of labels that describe the driver instance. These are inherited from the pool when the driver instance is created."
type: "object"
readOnly: true
additionalProperties:
type: "string"
ControllerName:
description: "ControllerName specifies the controller that this instance is running on."
type: "string"
x-nullable: false
readOnly: true
PoolID:
description: "PoolID refers to the pool that this driver instance relates to."
type: "string"
x-nullable: false
readOnly: true
DriverName:
description: "DriverName specifies which capacity driver this is an instance of."
type: "string"
x-nullable: false
readOnly: true
CapacityStats:
$ref: "#/definitions/CapacityStats"
parameters:
Name:
name: "name"
in: "path"
type: "string"
description: "Volume name or ID."
required: true
Namespace:
name: "namespace"
in: "path"
type: "string"
description: "Object name and auth scope, such as for teams and projects."
required: true
NamespaceQuery:
name: "namespace"
in: "query"
type: "string"
description: "Object name and auth scope, such as for teams and projects."
default: "default"
LabelSelector:
name: "labelSelector"
in: "query"
description: "A selector to restrict the list of returned objects by their labels. Defaults to everything."
type: "string"
FieldSelector:
name: "fieldSelector"
in: "query"
type: "string"
description: "A selector to restrict the list of returned objects by their fields. Defaults to everything."
TimeoutSeconds:
name: "timeoutSeconds"
in: "query"
type: "integer"
description: "Timeout for the list call."
paths:
/namespaces/{namespace}/volumes:
get:
summary: "List volumes"
description: "List of volumes that match the query."
produces:
- "application/json"
tags: ["Volume"]
parameters:
- name: "namespace"
in: "path"
required: true
description: "The object scope, such as for teams and projects."
type: "string"
- $ref: "#/parameters/LabelSelector"
- $ref: "#/parameters/FieldSelector"
- $ref: "#/parameters/TimeoutSeconds"
- $ref: "#/parameters/NamespaceQuery"
responses:
200:
description: "Success"
schema:
type: "array"
items:
$ref: "#/definitions/Volume"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
post:
summary: "Create volume"
description: "Provisions a new volume."
produces:
- "application/json"
tags: ["Volume"]
parameters:
- $ref: "#/parameters/Namespace"
- name: "VolumeCreateOptions"
in: "body"
schema:
$ref: "#/definitions/VolumeCreateOptions"
responses:
201:
description: "Volume created successfully"
schema:
$ref: "#/definitions/Volume"
401:
description: "Unauthorized"
409:
description: "Volume with name already exists"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
/namespaces/{namespace}/volumes/{name}:
get:
summary: "Get a volume"
description: "Gets a volume by name or ID. Returns to whole volume object."
produces:
- "application/json"
tags: ["Volume"]
parameters:
- $ref: "#/parameters/Name"
- $ref: "#/parameters/Namespace"
responses:
200:
description: "Success"
schema:
$ref: "#/definitions/Volume"
401:
description: "Unauthorized"
404:
description: "Not found"
schema:
$ref: "#/definitions/ErrorResponse"
407:
description: "Volume already exists"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
put:
summary: "Update volume"
description: "Updates an existing volume."
produces:
- "application/json"
tags: ["Volume"]
parameters:
- $ref: "#/parameters/Name"
- $ref: "#/parameters/Namespace"
- name: "VolumeUpdateOptions"
in: "body"
schema:
$ref: "#/definitions/VolumeUpdateOptions"
responses:
200:
description: "Success"
schema:
$ref: "#/definitions/Volume"
401:
description: "Unauthorized"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
delete:
summary: "Delete volume"
description: "Deletes a volume."
produces:
- "application/json"
tags: ["Volume"]
parameters:
- name: "namespace"
in: "path"
required: true
description: "The object scope, such as for teams and projects."
type: "string"
- name: "name"
in: "path"
required: true
description: "Volume name or ID."
type: "string"
responses:
200:
description: "Success"
401:
description: "Unauthorized"
407:
description: "Volume in use"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
/namespaces/{namespace}/volumes/{name}/mount:
post:
summary: "Mount volume"
description: "Updates the mount reference for the volume."
produces:
- "application/json"
tags: ["Volume"]
parameters:
- $ref: "#/parameters/Name"
- $ref: "#/parameters/Namespace"
- name: "client"
in: "body"
description: "Hostname of the client mounting the volume"
required: true
schema:
type: "string"
responses:
200:
description: "Success"
schema:
$ref: "#/definitions/Volume"
401:
description: "Unauthorized"
407:
description: "Volume already mounted"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
/namespaces/{namespace}/volumes/{name}/unmount:
post:
summary: "Mount volume"
description: "Updates the mount reference for the volume."
produces:
- "application/json"
tags: ["Volume"]
parameters:
- $ref: "#/parameters/Name"
- $ref: "#/parameters/Namespace"
- name: "client"
in: "body"
description: "Hostname of the client mounting the volume"
required: true
schema:
type: "string"
responses:
200:
description: "Success"
schema:
$ref: "#/definitions/Volume"
401:
description: "Unauthorized"
407:
description: "Volume not mounted"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
/pools:
get:
summary: "List pools"
description: "List of pools that match the query."
produces:
- "application/json"
tags: ["Pool"]
#parameters:
# - $ref: "#/parameters/LabelSelector"
# - $ref: "#/parameters/FieldSelector"
# - $ref: "#/parameters/TimeoutSeconds"
# - $ref: "#/parameters/NamespaceQuery"
responses:
200:
description: "Success"
schema:
type: "array"
items:
$ref: "#/definitions/Pool"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
post:
summary: "Create pool"
description: "Provisions a new pool."
produces:
- "application/json"
tags: ["Pool"]
parameters:
- name: "PoolCreateOptions"
in: "body"
schema:
$ref: "#/definitions/PoolCreateOptions"
responses:
201:
description: "Pool created successfully"
schema:
$ref: "#/definitions/Pool"
401:
description: "Unauthorized"
409:
description: "Pool with name already exists"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
/pools/{name}:
get:
summary: "Get a pool"
description: "Gets a pool by name or ID. Returns to whole pool object."
produces:
- "application/json"
tags: ["Pool"]
parameters:
- $ref: "#/parameters/Name"
responses:
200:
description: "Success"
schema:
$ref: "#/definitions/Pool"
401:
description: "Unauthorized"
404:
description: "Not found"
schema:
$ref: "#/definitions/ErrorResponse"
407:
description: "Pool already exists"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"
delete:
summary: "Delete pool"
description: "Deletes a pool."
produces:
- "application/json"
tags: ["Pool"]
parameters:
- name: "name"
in: "path"
required: true
description: "Pool name or ID."
type: "string"
responses:
200:
description: "Success"
401:
description: "Unauthorized"
407:
description: "Pool in use"
500:
description: "Server error"
schema:
$ref: "#/definitions/ErrorResponse"

90
vendor/github.com/storageos/go-api/template.go generated vendored Normal file
View File

@@ -0,0 +1,90 @@
package storageos
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"strconv"
"github.com/storageos/go-api/types"
)
var (
// TemplateAPIPrefix is a partial path to the HTTP endpoint.
TemplateAPIPrefix = "/templates"
// ErrNoSuchTemplate is the error returned when the template does not exist.
ErrNoSuchTemplate = errors.New("no such template")
// ErrTemplateInUse is the error returned when the template requested to be removed is still in use.
ErrTemplateInUse = errors.New("template in use and cannot be removed")
)
// TemplateList returns the list of available templates.
func (c *Client) TemplateList(opts types.ListOptions) ([]types.Template, error) {
path := TemplateAPIPrefix + "?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var templates []types.Template
if err := json.NewDecoder(resp.Body).Decode(&templates); err != nil {
return nil, err
}
return templates, nil
}
// TemplateCreate creates a template on the server and returns the new object.
func (c *Client) TemplateCreate(opts types.TemplateCreateOptions) (string, error) {
resp, err := c.do("POST", TemplateAPIPrefix, doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
return "", err
}
defer resp.Body.Close()
out, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return strconv.Unquote(string(out))
}
// Template returns a template by its reference.
func (c *Client) Template(ref string) (*types.Template, error) {
resp, err := c.do("GET", TemplateAPIPrefix+"/"+ref, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchTemplate
}
return nil, err
}
defer resp.Body.Close()
var template types.Template
if err := json.NewDecoder(resp.Body).Decode(&template); err != nil {
return nil, err
}
return &template, nil
}
// TemplateDelete removes a template by its reference.
func (c *Client) TemplateDelete(ref string) error {
resp, err := c.do("DELETE", TemplateAPIPrefix+"/"+ref, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
return ErrNoSuchTemplate
}
if e.Status == http.StatusConflict {
return ErrTemplateInUse
}
}
return nil
}
defer resp.Body.Close()
return nil
}

769
vendor/github.com/storageos/go-api/testing_notes.txt generated vendored Normal file
View File

@@ -0,0 +1,769 @@
docs.storageos.com - API webpage
================================
TODO: Openind paragraph of text has multiple typos and grammar errors. Need to fix this. [DONE]
TODO: hard to see the text for the example JSON post messages on the RHS. Possibly a Safari thing?!
TODO: some of the JSON examples on the RHS of the webpage look wrong?! Safari issue?
TODO: docs should use all lowercase for consistency?
VOLUMES
=======
List volumes
============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - no provisioned volumes.
- No auth username and password supplied.
-->
GET: 172.28.128.3:5705/v1/namespaces/default/volumes
<--
401 UNAUTHORIZED
Unauthorized
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - no provisioned volumes.
- bad namespace used.
-->
GET: 172.28.128.3:5705/v1/namespaces/defaultxxx/volumes
<--
404 NOT FOUND
JSON: "message": "Not found"
Unauthorized
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - no provisioned volumes.
- Dropped the {namespace} path parameter.
-->
GET: 172.28.128.3:5705/v1/namespaces/volumes
<--
404 NOT FOUND
Not found
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - no provisioned volumes.
- One volume has already been created successfully.
-->
GET: 172.28.128.3:5705/v1/namespaces/volumes
<--
JSON: valid volume/inode state data (note: this time the master data is correctly set i.e. has non-zero data)
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster - single provisioned volumes.
-->
GET: 172.28.128.3:5705/v1/namespaces/volumes
<--
JSON: valid volume/inode state data.
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster - multiple provisioned volumes.
-->
GET: 172.28.128.3:5705/v1/namespaces/volumes
<--
JSON: an array of valid volume/inode state data for multiple volumes.
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster - multiple provisioned volumes.
- labelSelector
-->
GET: 172.28.128.3:5705/v1/namespaces/volumes?labelSelector=com.example.some-label
<--
JSON: an array of valid volume/inode state data for multiple volumes.
-FAILED- doesn't appear to filter.
XXX:
===============================================================================
SCENARIO:
- 3 node cluster - multiple provisioned volumes.
- fieldSelector
-->
GET: 172.28.128.3:5705/v1/namespaces/volumes?fieldSelector=<????>
<--
JSON: an array of valid volume/inode state data for multiple volumes.
-UNTESTED- don't know what to put for fieldSelector's value??
XXX:
===============================================================================
Create volumes
==============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - no provisioned volumes.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"name": "redis-vol01",
"size": 1
}
<--
JSON: valid volume/inode state data (note: master data is all zerod at this point)
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - with a single volume already provisioned.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"name": "redis-vol02",
"size": 2
}
<--
JSON: valid volume/inode state data (note: master data is all zerod at this point)
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - with two volumes already provisioned.
- Now trying to provision a third with bad JSON body -- using CAPITAL first letters for Name and Size.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"Name": "redis-vol03",
"Size": 3
}
<--
JSON: valid volume/inode state data (note: master data is all zerod at this point)
-EXPECTED- WORKS?! This implies that the JSON keys are non-case sensitive.
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - with two volumes already provisioned.
- Now trying to provision a third with bad JSON body -- using all CAPITALS for NAME and SIZE.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"NAME": "redis-vol03",
"SIZE": 3
}
<--
JSON: valid volume/inode state data (note: master data is all zerod at this point)
-EXPECTED- WORKS?! This implies that the JSON keys are non-case sensitive.
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - with two volumes already provisioned.
- Now trying to provision a volume with bad JSON body -- missing size parameter.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"name": "redis-vol05",
}
<--
JSON: valid volume/inode state data (note: master data is all zerod at this point)
-EXPECTED- Size defaults to 10
TODO: update documentation to reflect this. [DONE]
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - with two volumes already provisioned.
- Now trying to provision a volume with bad JSON body -- with size (0) parameter.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"name": "redis-vol05",
"size" 0
}
<--
JSON: valid volume/inode state data (note: master data is all zerod at this point)
-EXPECTED- Size defaults to 10
TODO: update documentation to reflect this. [DONE]
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - with two volumes already provisioned.
- Now trying to provision a volume with bad JSON body -- empty JSON object.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
}
<--
volume name not valid
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - with two volumes already provisioned.
- Now trying to provision a volume with no JSON body -- empty
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
<--
JSON: "message": "EOF"
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster.
- Now trying to provision a volume with no JSON name field
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"size": 2
}
<--
volume name not valid
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster.
- Now trying to provision a volume with no JSON name field
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"size": 2
}
<--
volume name not valid
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster.
- Now trying to provision a volume with same name as one that has already been provisioned.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"name": "myvol1",
"size": 5
}
<--
volume with name 'myvol1' already exists
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster.
- Now trying to provision a volume with pool name.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"name": "myvol1",
"size": 5,
"pool": "mypool1"
}
<--
JSON: valid volume/inode state data with correct pool field.
-EXPECTED-
===============================================================================
SCENARIO:
- 3 node cluster.
- Now trying to provision a volume with optional labels.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes
BODY (JSON):
{
"Name": "vol01",
"Size": 5,
"Labels": {
"com.example.some-label": "some-value",
"com.example.some-other-label": "some-other-value"
}
}
<--
JSON: valid volume/inode state data with correct labels.
-EXPECTED-
===============================================================================
Get volumes
===========
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Get by name.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
<--
JSON: correct volume/inode state data.
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Get by id.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/270c1fc2-c578-77f8-2d7c-1515e626b6c3
<--
JSON: correct volume/inode state data.
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Passing bad name/id.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/this-volume-does-not-exist
<--
Not Found
-EXPECTED-
===============================================================================
Update volumes
==============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Update a volume
-->
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
JSON:
{
"Description": "string",
"Size": 5,
"Labels": {
"property1": "string",
"property2": "string"
}
}
<--
200 OK
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Update a volume, trying with bad JSON, missing opening curly brace!
-->
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
JSON:
"Description": "string",
"Size": 5,
"Labels": {
"property1": "string",
"property2": "string"
}
}
<--
400 BAD REQUEST
Request decode failed: json: cannot unmarshal string into Go value of type types.Volume
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Update a volume, trying with size (0) and same property1 and new property3.
-->
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03
JSON:
{
"Description": "string",
"Size": 0,
"Labels": {
"property1": "string",
"property3": "string3"
}
}
<--
200 OK
-NOT EXPECTED-
The old labels are completely overwritten anew (hence the previous property2 label is not present). I assume this is the desired behaviour?!
TODO: However size is now zero?! Check this is ok! Probably not; since the Create volume API defaults to 10 when 0 is passed or omitted.
XXX:
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Update a volume, trying with omitting size parameter
-->
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
JSON:
{
"Description": "string",
"Labels": {
"property1": "string",
"property3": "string3"
}
}
<--
200 OK
-NOT EXPECTED-
XXX: size is now zero when not passing size in JSON body of PUT request.
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Update a volume, trying with omitting description parameter
-->
PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
JSON:
{
"Labels": {
"property1": "string",
"property3": "string3"
}
}
<--
200 OK
-NOT EXPECTED-
XXX: description string is empty i.e. "" when not passing description in JSON body of PUT request.
The above implies that the update volume PUT request receiving side interprets missing update parameters as their null-value counterparts. So it's is not possible to update just specific parameters.
===============================================================================
Delete volumes
==============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Delete a volume. But not specifying a name in the path.
-->
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes
<--
404 NOT FOUND
404 page not found
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Delete a volume by specifying the volume's name.
-->
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol05
<--
200 OK
-EXPECTED-
TODO: But when doing a GET ~volumes/redis-vol05 it is still present So DELETE volumes doesn't appear to be working.
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Delete a volume by specifying the volume's id.
-->
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/5233930b-b77f-2863-0895-b1eb5d73ec45
<--
200 OK
-EXPECTED-
TODO: But when doing a GET ~volumes/5233930b-b77f-2863-0895-b1eb5d73ec45 it is still present So DELETE volumes doesn't appear to be working.
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Trying to delete a mounted volume
-->
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
<--
412 PRECONDITION FAILED
cannot delete mounted volume
-EXPECTED-
TODO: seems correct, this hints that the mount is working which is in conflict with my observation below for MOUNT and UNMOUNT. Q. Is it checking the mount status via the OS, or by some other means i.e. locally cached value?
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Trying to delete a unmounted volume
-->
DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz
<--
200 OK
-EXPECTED-
===============================================================================
Mount volumes
=============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Mount a volume. But not specifying a name in the path.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol02/mount
JSON:
{
"client": "storageos-1-68228"
}
<--
200 OK
-EXPECTED-
NOTE: in the JSON response, no_of_mounts does increase correctly by one each time. And mounted is set to true correctly.
TODO: although this worked, the documentation doesn't give the proper JSON request body.
TODO: no_of_mounts is still 0 in /var/lib/storageos/state/inode/178101 (should increase by 1 for every mount.)
BUG^
TODO: Also not sure if really mounted this volume, since running the storageos cli e.g.:
$ ./storageos volume ls
The MOUNTED BY column is always empty ?? Either cli doesn't show this info, yet. Or the volume isn't mounted.
===============================================================================
Unmount volumes
===============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Mount a volume. But not specifying a name in the path.
-->
POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol02/unmount
JSON:
{
"can-be-anything": "storageos-1-68228"
}
<--
200 OK
-EXPECTED-
NOTE: in the JSON response, mounted is set back to false correctly.
TODO: although this worked, the documentation doesn't give the proper JSON request body. The
===============================================================================
POOLS
=====
List pools
==========
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster.
- No auth username and password supplied.
-->
GET: 172.28.128.3:5705/v1/pools
<--
401 UNAUTHORIZED
Unauthorized
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster. No pools.
-->
GET: 172.28.128.3:5705/v1/pools
<--
200 OK
JSON: an array of pools.
-EXPECTED-
===============================================================================
Create pools
============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster.
-->
POST: 172.28.128.3:5705/v1/volumes
BODY (JSON):
{
"name": "mypool1"
}
<--
201 CREATED
JSON: valid pool data.
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster.
- Trying to create a pool with the same name as another already created.
-->
POST: 172.28.128.3:5705/v1/volumes
BODY (JSON):
{
"name": "mypool1"
}
<--
409 CONFLICT
Pool with name 'mypool1' already exists
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster.
- Trying to create a pool with the same name as another already created.
-->
POST: 172.28.128.3:5705/v1/volumes
BODY (JSON):
{
"name": "mypool2",
"description": "hello world!"
}
<--
201 CREATED
JSON: valid pool data.
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster.
- Trying to create a pool with the defaultDriver string set.
-->
POST: 172.28.128.3:5705/v1/volumes
BODY (JSON):
{
"name": "mypool6",
"description": "hello world again!",
"default": true,
"defautDriver": "I'm the default driver :)"
}
<--
201 CREATED
JSON: Is correct for the most part, but defaultDriver is an empty string??
-NOT EXPECTED-
XXX
===============================================================================
Get Pools
=========
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Get by name.
-->
POST: 172.28.128.3:5705/v1/pools/mypool1
<--
200 OK
JSON: correct pool data.
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Get by id.
-->
POST: 172.28.128.3:5705/v1/pools/ea477d68-8193-1179-d889-aa6ea8797082
<--
200 OK
JSON: correct pool data.
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned volumes.
- Get by name. Try passing invalid name.
-->
POST: 172.28.128.3:5705/v1/pools/mypool1xxx
<--
404 NOT FOUND
Not Found
-EXPECTED-
===============================================================================
Delete Pools
============
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned pools.
- Trying to delete without specifying name etc. in the path.
-->
POST: 172.28.128.3:5705/v1/pools
<--
404 NOT FOUND
404 page not found
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned pools.
- Delete by name.
-->
POST: 172.28.128.3:5705/v1/pools/mypool2
<--
200 OK
-EXPECTED-
===============================================================================
SCENARIO:
- Fresh start - 3 node cluster - N provisioned pools.
- Delete by id.
-->
POST: 172.28.128.3:5705/v1/pools/9a10bbfe-eaaa-af3c-2a9b-d78e0790efb4
<--
200 OK
-EXPECTED-
===============================================================================

49
vendor/github.com/storageos/go-api/types/BUILD generated vendored Normal file
View File

@@ -0,0 +1,49 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"auth.go",
"capacity_stats.go",
"controller.go",
"controller_update_options.go",
"delete_options.go",
"deployment.go",
"driver_instance.go",
"error_response.go",
"events.go",
"list_options.go",
"namespace.go",
"operator.go",
"pool.go",
"pool_create_options.go",
"rule.go",
"template.go",
"template_create_options.go",
"version.go",
"volume.go",
"volume_create_options.go",
"volume_update_options.go",
],
tags = ["automanaged"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

14
vendor/github.com/storageos/go-api/types/auth.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
package types
// AuthConfig contains authorization information for connecting to a Registry
type AuthConfig struct {
Name string `json:"name,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Auth string `json:"auth,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`
// IdentityToken is used to authenticate the user and get
// an access token for the registry.
IdentityToken string `json:"identitytoken,omitempty"`
}

View File

@@ -0,0 +1,25 @@
package types
// ErrCapacityStatsUnchanged can be used when comparing stats
const ErrCapacityStatsUnchanged = "no changes"
// CapacityStats is used to report capacity statistics on pools and controllers.
type CapacityStats struct {
// TotalCapacityBytes is the object's total capacity in bytes.
TotalCapacityBytes uint64 `json:"totalCapacityBytes"`
// AvailableCapacityBytes is the object's available capacity in bytes.
AvailableCapacityBytes uint64 `json:"availableCapacityBytes"`
// ProvisionedCapacityBytes is the object's provisioned capacity in bytes.
ProvisionedCapacityBytes uint64 `json:"provisionedCapacityBytes"`
}
// IsEqual checks if capacity values are the same
func (c CapacityStats) IsEqual(n CapacityStats) bool {
if c == n {
return true
}
return false
}

74
vendor/github.com/storageos/go-api/types/controller.go generated vendored Normal file
View File

@@ -0,0 +1,74 @@
package types
// Versions and Prefixes used in API and KV URLs
import "time"
const (
ControllerAPIPrefix = "controller"
ControllerDefaultPort = "3260"
ControllerScanAPIPrefix = "config/scan"
)
// ControllerCurrent - current controller
var ControllerCurrent = ""
// Controller status phases
const (
ControllerStatusPending = "pending"
ControllerStatusEvaluating = "evaluating"
ControllerStatusDeploying = "deploying"
ControllerStatusActive = "active"
ControllerStatusFailed = "failed"
ControllerStatusDeleting = "deleting"
ControllerHealthStarting = "starting"
ControllerHealthOK = "healthy"
ControllerHealthDegraded = "degraded"
ControllerHealthOffline = "offline"
)
// Errors for controller related things
const (
ErrControllerHostIDAllocation string = "error, could not allocate hostid"
ErrControllerIDNotSet = "error, controller ID not set"
ErrControllerNotFound = "controller not found"
)
// Controller is used to represent a storage node in a cluster
type Controller struct {
ID string `json:"id,omitempty"`
HostID uint16 `json:"hostID"`
Scheduler bool `json:"scheduler"`
Name string `json:"name"`
Address string `json:"address"`
APIPort int `json:"apiPort"`
NatsPort int `json:"natsPort"`
NatsClusterPort int `json:"natsClusterPort"`
SerfPort int `json:"serfPort"`
DFSPort int `json:"dfsPort"`
Description string `json:"description"`
ControllerGroups []string `json:"controllerGroups"`
Tags []string `json:"tags"`
Labels map[string]string `json:"labels"`
VolumeStats VolumeStats `json:"volumeStats"`
PoolStats map[string]DriverStats `json:"poolStats"`
// health is updated by the
Health string `json:"health"`
HealthUpdatedAt time.Time `json:"healthUpdatedAt"`
VersionInfo map[string]VersionInfo `json:"versionInfo"`
Version string `json:"version"`
// high level stats that combine info from all driver instances
CapacityStats CapacityStats `json:"capacityStats"`
}
// DriverStats is used to report stats for all drivers in a pool.
type DriverStats map[string]CapacityStats
// VolumeStats - volume stats (volume counts, looking forward to capacity)
type VolumeStats struct {
MasterVolumeCount int `json:"masterVolumeCount"`
ReplicaVolumeCount int `json:"replicaVolumeCount"`
VirtualVolumeCount int `json:"virtualVolumeCount"`
}

View File

@@ -0,0 +1,24 @@
package types
import "context"
// ControllerUpdateOptions are available parameters for updating existing controllers.
type ControllerUpdateOptions struct {
// Controller unique ID.
// Read Only: true
ID string `json:"id"`
// Controller name.
// Read Only: true
Name string `json:"name"`
// Description of the controller.
Description string `json:"description"`
// Labels are user-defined key/value metadata.
Labels map[string]string `json:"labels"`
// Context can be set with a timeout or can be used to cancel a request.
Context context.Context `json:"-"`
}

View File

@@ -0,0 +1,24 @@
package types
import "context"
// DeleteOptions are available parameters for deleting existing volumes.
type DeleteOptions struct {
// Volume unique ID.
// Read Only: true
ID string `json:"id"`
// Volume name.
// Read Only: true
Name string `json:"name"`
// Namespace is the object scope, such as for teams and projects.
Namespace string `json:"namespace"`
// Force will cause the volume to be deleted even if it's in use.
Force bool `json:"force"`
// Context can be set with a timeout or can be used to cancel a request.
Context context.Context `json:"-"`
}

32
vendor/github.com/storageos/go-api/types/deployment.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package types
import "time"
// Deployment Volume master or replica deployment details.
// swagger:model Deployment
type Deployment struct {
// Deployment unique ID
// Read Only: true
ID string `json:"id"`
// Inode number
// Read Only: true
Inode uint32 `json:"inode"`
// Controller ID
// Read Only: true
Controller string `json:"controller"`
// Health
// Read Only: true
Health string `json:"health"`
// Status
// Read Only: true
Status string `json:"status"`
// Created at
// Read Only: true
CreatedAt time.Time `json:"createdAt"`
}

View File

@@ -0,0 +1,86 @@
package types
import "encoding/gob"
// DriverInstance is used to define an instance of a storage capacity driver.
type DriverInstance struct {
// Instance unique ID.
// Read Only: true
ID string `json:"id"`
// Instance name.
Name string `json:"name"`
// Instance description.
Description string `json:"description"`
// Flag describing whether the template is active.
// Default: false
Active bool `json:"active"`
// Config is JSON struct that is passed directly to the driver. There is no
// specific format, and the driver is responsible for validation.
Config interface{} `json:"config"`
// Labels define a list of labels that describe the driver instance. These
// are inherited from the pool when the driver instance is created.
Labels []string `json:"labels"`
// ControllerName specifies the controller that this instance is running on.
ControllerName string `json:"controllerName"`
// PoolID refers to the pool that this driver instance relates to.
PoolID string `json:"poolID"`
// DriverName specifies which capacity driver this is an instance of.
DriverName string `json:"driverName"`
// CapacityStats tracks that capacity usage of this driver instance on the
// current controller.
CapacityStats CapacityStats `json:"capacityStats"`
}
// DriverInstances is a collection of Driver instance objects.
type DriverInstances []*DriverInstance
func init() {
gob.Register(DriverInstance{})
gob.Register([]interface{}{})
}
// Find an instance matching the parameters.
func (i *DriverInstances) Find(pool string, driver string, controller string) *DriverInstance {
for _, inst := range *i {
if inst.PoolID == pool && inst.DriverName == driver && inst.ControllerName == controller {
return inst
}
}
return nil
}
// Add a new instance to the list of instances.
func (i *DriverInstances) Add(new *DriverInstance) {
for _, inst := range *i {
// Skip if it already exists
if inst.PoolID == new.PoolID && inst.DriverName == new.DriverName && inst.ControllerName == new.ControllerName {
return
}
}
*i = append(*i, new)
}
// Remove an instance to the list of instances.
func (i *DriverInstances) Remove(id string) {
// TODO: not working
// for ndx, inst := range *i {
// if inst.ID == id {
// // splice out the item to remove
// *i = append(*i[:ndx], *i[ndx+1:]...)
// return
// }
// }
}

View File

@@ -0,0 +1,13 @@
package types
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// ErrorResponse Represents an error.
// swagger:model ErrorResponse
type ErrorResponse struct {
// The error message.
// Required: true
Message string `json:"message"`
}

60
vendor/github.com/storageos/go-api/types/events.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
package types
import "time"
// EventType describes the type of event
type EventType string
// EventTypes are added to events to assist with type assertions
const (
RequestType EventType = "request"
ResponseType = "response"
HeartbeatType = "heartbeat"
BackupType = "backup"
)
// Event describes the fields that all events should implement. Event is
// intended to be inherherited in more specific Event types.
type Event struct {
ID string `json:"id"`
// Parent is used to specify parent event
Parent string `json:"parent"`
EventType EventType `json:"eventType"`
Action string `json:"action"`
Timestamp int64 `json:"timestamp"`
Status string `json:"status"`
Message string `json:"message"`
Log []string `json:"log"`
ProgressPercent int `json:"progressPercent"`
CreatedBy string `json:"createdBy"`
Target string `json:"target"`
ActionPayload interface{} `json:"actionPayload"`
// payload can be encoded into bytes as well
ActionPayloadBytes []byte `json:"actionPayloadBts"`
UpdatedAt time.Time `json:"updatedAt"`
CreatedAt time.Time `json:"createdAt"`
// retry related value
Retry bool `json:"retry"`
RetriedAt time.Time `json:"retriedAt"`
Attempts int `json:"attempts"`
// optional parameter
Deadline time.Time `json:"deadline"`
// optional events to dispatch
Rollback []*Request `json:"rollback"`
RollbackDone bool `json:"rollbackDone"`
Subject string `json:"subject"` // or "queue"
// controller ID which created this event
OriginController string `json:"originController"`
}
// Request is the message structure used for sending request events
type Request struct {
Event
}

View File

@@ -0,0 +1,19 @@
package types
import "context"
// ListOptions are optional parameters for finding and listing most objects.
type ListOptions struct {
// FieldSelector restricts the list of returned objects by their fields. Defaults to everything.
FieldSelector string
// LabelSelector restricts the list of returned objects by their labels. Defaults to everything.
LabelSelector string
// Namespace is the object scope, such as for teams and projects.
Namespace string
// Context can be set with a timeout or can be used to cancel a request.
Context context.Context
}

59
vendor/github.com/storageos/go-api/types/namespace.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
package types
import (
"context"
"time"
)
// Namespace is used to as a container to isolate namespace and rule obects.
type Namespace struct {
// Namespace unique ID.
// Read Only: true
ID string `json:"id"`
// Namespace name.
// Required: true
Name string `json:"name"`
// The optional DisplayName is how the project is displayed in the web console (defaults to name).
DisplayName string `json:"displayName"`
// Namespcae description.
Description string `json:"description"`
// User-defined key/value metadata.
Labels map[string]string `json:"labels"`
// When the namespace was created.
// Read Only: true
CreatedAt time.Time `json:"createdAt"`
// User that created the namespace.
// Read Only: true
CreatedBy string `json:"createdBy"`
// When the namespace was created.
// Read Only: true
UpdatedAt time.Time `json:"updatedAt"`
}
// NamespaceCreateOptions are available parameters for creating new namespaces.
type NamespaceCreateOptions struct {
// Name is the name of the namespace to create.
// Required: true
Name string `json:"name"`
// The optional DisplayName is how the project is displayed in the web console (defaults to name).
DisplayName string `json:"displayName"`
// Description describes the namespace.
Description string `json:"description"`
// Labels are user-defined key/value metadata.
Labels map[string]string `json:"labels"`
// Context can be set with a timeout or can be used to cancel a request.
Context context.Context `json:"-"`
}

19
vendor/github.com/storageos/go-api/types/operator.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
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"
)

55
vendor/github.com/storageos/go-api/types/pool.go generated vendored Normal file
View File

@@ -0,0 +1,55 @@
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"`
// DefaultDriver specifies the storage driver to use by default if there are
// multiple drivers in the pool and no driver was specified in the
// provisioning request or assigned by rules. If no driver was specified and
// no default set, driver weight is used to determine the default.
DefaultDriver string `json:"defaultDriver"`
// ControllerNames is a list of controller names that are participating in the
// storage pool.
ControllerNames []string `json:"controllerNames"`
// DriverNames is a list of backend storage drivers that are available in the
// storage pool.
DriverNames []string `json:"driverNames"`
// DriverInstances is used to track instances of each driver. Drivers have a
// default configuration, which can then be customised for each pool where
// they are used, which is representated as a DriverInstance.
// Read Only: true
DriverInstances []*DriverInstance `json:"driverInstances"`
// Flag describing whether the template is active.
// Default: false
Active bool `json:"active"`
// CapacityStats are used to track aggregate capacity usage information across
// all controllers and driver instances.
// Read Only: true
CapacityStats CapacityStats `json:"capacityStats"`
// 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

View File

@@ -0,0 +1,42 @@
package types
import "context"
// PoolCreateOptions are available parameters for creating new pools.
type PoolCreateOptions struct {
// 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"`
// DefaultDriver specifies the storage driver to use by default if there are
// multiple drivers in the pool and no driver was specified in the
// provisioning request or assigned by rules. If no driver was specified and
// no default set, driver weight is used to determine the default.
DefaultDriver string `json:"defaultDriver"`
// ControllerNames is a list of controller names that are participating in the
// storage pool.
ControllerNames []string `json:"controllerNames"`
// DriverNames is a list of backend storage drivers that are available in the
// storage pool.
DriverNames []string `json:"driverNames"`
// Flag describing whether the template is active.
// Default: false
Active bool `json:"active"`
// 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 Normal file
View File

@@ -0,0 +1,125 @@
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 Normal file
View File

@@ -0,0 +1,53 @@
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

View File

@@ -0,0 +1,51 @@
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:"-"`
}

26
vendor/github.com/storageos/go-api/types/version.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
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
}

133
vendor/github.com/storageos/go-api/types/volume.go generated vendored Normal file
View File

@@ -0,0 +1,133 @@
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"`
// 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"`
// 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"`
// 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:"-"`
}

View File

@@ -0,0 +1,33 @@
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"`
// 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:"-"`
}

View File

@@ -0,0 +1,31 @@
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"`
// 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:"-"`
}

16
vendor/github.com/storageos/go-api/util.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
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
}

74
vendor/github.com/storageos/go-api/validation.go generated vendored Normal file
View File

@@ -0,0 +1,74 @@
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 = errors.New("no ref provided or incorrect format")
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 Normal file
View File

@@ -0,0 +1,197 @@
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
}