mirror of https://github.com/k3s-io/k3s
Update storageos api dependency to 0.3.4
parent
68da45623f
commit
8d0ba4a978
|
@ -2592,11 +2592,23 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/storageos/go-api",
|
||||
"Rev": "74f9beb613cacf0cc282facc2e1550a3231e126f"
|
||||
"Comment": "0.3.4",
|
||||
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/storageos/go-api/netutil",
|
||||
"Comment": "0.3.4",
|
||||
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/storageos/go-api/serror",
|
||||
"Comment": "0.3.4",
|
||||
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/storageos/go-api/types",
|
||||
"Rev": "74f9beb613cacf0cc282facc2e1550a3231e126f"
|
||||
"Comment": "0.3.4",
|
||||
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/objx",
|
||||
|
|
|
@ -82052,6 +82052,112 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/storageos/go-api/netutil licensed under: =
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2017 StorageOS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
Copyright (c) 2013-2017, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
= vendor/github.com/storageos/go-api/LICENCE d8f852a0f38554263e64363f57b07fc4
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/storageos/go-api/serror licensed under: =
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2017 StorageOS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
Copyright (c) 2013-2017, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
= vendor/github.com/storageos/go-api/LICENCE d8f852a0f38554263e64363f57b07fc4
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/storageos/go-api/types licensed under: =
|
||||
|
||||
|
|
|
@ -6,61 +6,28 @@ go_library(
|
|||
"client.go",
|
||||
"controller.go",
|
||||
"event.go",
|
||||
"health.go",
|
||||
"logger.go",
|
||||
"login.go",
|
||||
"namespace.go",
|
||||
"policy.go",
|
||||
"pool.go",
|
||||
"rule.go",
|
||||
"server_version.go",
|
||||
"template.go",
|
||||
"user.go",
|
||||
"util.go",
|
||||
"validation.go",
|
||||
"volume.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"client_unix.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"client_windows.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
],
|
||||
importpath = "github.com/storageos/go-api",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/gorilla/websocket:go_default_library",
|
||||
"//vendor/github.com/storageos/go-api/netutil:go_default_library",
|
||||
"//vendor/github.com/storageos/go-api/serror:go_default_library",
|
||||
"//vendor/github.com/storageos/go-api/types:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//vendor/github.com/Microsoft/go-winio:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
|
@ -74,6 +41,8 @@ filegroup(
|
|||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//vendor/github.com/storageos/go-api/netutil:all-srcs",
|
||||
"//vendor/github.com/storageos/go-api/serror:all-srcs",
|
||||
"//vendor/github.com/storageos/go-api/types:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
# StorageOS API client library
|
||||
|
||||
## Swagger Spec
|
||||
Swagger specification for this repo is available in the [StorageOS public documentation](https://github.com/storageos/storageos.github.io/blob/master/swagger.yaml).
|
||||
|
||||
|
|
|
@ -4,16 +4,16 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/storageos/go-api/netutil"
|
||||
"github.com/storageos/go-api/serror"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -22,17 +22,11 @@ import (
|
|||
|
||||
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")
|
||||
|
||||
|
@ -42,8 +36,14 @@ var (
|
|||
// ErrInvalidVersion is returned when a versioned client was requested but no version specified.
|
||||
ErrInvalidVersion = errors.New("invalid version")
|
||||
|
||||
// DefaultPort is the default API port
|
||||
DefaultPort = "5705"
|
||||
|
||||
// DataplaneHealthPort is the the port used by the dataplane health-check service
|
||||
DataplaneHealthPort = "5704"
|
||||
|
||||
// DefaultHost is the default API host
|
||||
DefaultHost = "tcp://localhost:5705"
|
||||
DefaultHost = "tcp://localhost:" + DefaultPort
|
||||
)
|
||||
|
||||
// APIVersion is an internal representation of a version of the Remote API.
|
||||
|
@ -73,15 +73,13 @@ 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
|
||||
useTLS bool
|
||||
}
|
||||
|
||||
// ClientVersion returns the API version of the client
|
||||
|
@ -99,20 +97,8 @@ type Dialer interface {
|
|||
// 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, "")
|
||||
func NewClient(nodes string) (*Client, error) {
|
||||
client, err := NewVersionedClient(nodes, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -122,17 +108,24 @@ func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
|
|||
|
||||
// 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)
|
||||
func NewVersionedClient(nodestring string, apiVersionString string) (*Client, error) {
|
||||
nodes := strings.Split(nodestring, ",")
|
||||
|
||||
d, err := netutil.NewMultiDialer(nodes, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var useTLS bool
|
||||
if len(nodes) > 0 {
|
||||
if u, err := url.Parse(nodes[0]); err != nil && u.Scheme == "https" {
|
||||
useTLS = true
|
||||
}
|
||||
}
|
||||
|
||||
c := &Client{
|
||||
HTTPClient: defaultClient(),
|
||||
Dialer: &net.Dialer{},
|
||||
endpoint: endpoint,
|
||||
endpointURL: u,
|
||||
HTTPClient: defaultClient(d),
|
||||
useTLS: useTLS,
|
||||
}
|
||||
|
||||
if apiVersionString != "" {
|
||||
|
@ -143,85 +136,6 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -265,13 +179,6 @@ func (c *Client) checkAPIVersion() error {
|
|||
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.
|
||||
|
@ -341,15 +248,7 @@ func (c *Client) do(method, urlpath string, doOptions doOptions) (*http.Response
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
u := c.getAPIPath(urlpath, query, doOptions.unversioned)
|
||||
|
||||
req, err := http.NewRequest(method, u, params)
|
||||
if err != nil {
|
||||
|
@ -376,6 +275,11 @@ func (c *Client) do(method, urlpath string, doOptions doOptions) (*http.Response
|
|||
|
||||
resp, err := httpClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
// If it is a custom error, return it. It probably knows more than us
|
||||
if serror.IsStorageOSError(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, ErrConnectionRefused
|
||||
}
|
||||
|
@ -397,27 +301,18 @@ func chooseError(ctx context.Context, err error) error {
|
|||
}
|
||||
}
|
||||
|
||||
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 = ""
|
||||
// The custom dialer contacts the hosts for us, making this hosname irrelevant
|
||||
var urlStr string
|
||||
if c.useTLS {
|
||||
urlStr = "https://storageos-cluster"
|
||||
} else {
|
||||
urlStr = "http://storageos-cluster"
|
||||
}
|
||||
|
||||
var apiPath string
|
||||
|
||||
path = strings.TrimLeft(path, "/")
|
||||
if unversioned {
|
||||
apiPath = fmt.Sprintf("%s/%s", urlStr, path)
|
||||
} else {
|
||||
|
@ -431,30 +326,6 @@ func (c *Client) getAPIPath(path string, query url.Values, unversioned bool) str
|
|||
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 ""
|
||||
|
@ -530,63 +401,50 @@ type Error struct {
|
|||
}
|
||||
|
||||
func newError(resp *http.Response) *Error {
|
||||
type jsonError struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return &Error{Status: resp.StatusCode, Message: fmt.Sprintf("cannot read body, err: %v", err)}
|
||||
}
|
||||
return &Error{Status: resp.StatusCode, Message: string(data)}
|
||||
|
||||
// attempt to unmarshal the error if in json format
|
||||
jerr := &jsonError{}
|
||||
err = json.Unmarshal(data, jerr)
|
||||
if err != nil {
|
||||
return &Error{Status: resp.StatusCode, Message: string(data)} // Failed, just return string
|
||||
}
|
||||
|
||||
return &Error{Status: resp.StatusCode, Message: jerr.Message}
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
|
||||
}
|
||||
var niceStatus string
|
||||
|
||||
func parseEndpoint(endpoint string, tls bool) (*url.URL, error) {
|
||||
if endpoint != "" && !strings.Contains(endpoint, "://") {
|
||||
endpoint = "tcp://" + endpoint
|
||||
switch e.Status {
|
||||
case 400, 500:
|
||||
niceStatus = "Server failed to process your request. Was the data correct?"
|
||||
case 401:
|
||||
niceStatus = "Unauthenticated access of secure endpoint, please retry after authentication"
|
||||
case 403:
|
||||
niceStatus = "Forbidden request. Your user cannot perform this action"
|
||||
case 404:
|
||||
niceStatus = "Requested object not found. Does this item exist?"
|
||||
}
|
||||
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
|
||||
|
||||
if niceStatus != "" {
|
||||
return fmt.Sprintf("API error (%s): %s", niceStatus, e.Message)
|
||||
}
|
||||
return fmt.Sprintf("API error (%s): %s", http.StatusText(e.Status), e.Message)
|
||||
}
|
||||
|
||||
// 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()
|
||||
func defaultTransport(d Dialer) *http.Transport {
|
||||
transport := defaultPooledTransport(d)
|
||||
transport.DisableKeepAlives = true
|
||||
transport.MaxIdleConnsPerHost = -1
|
||||
return transport
|
||||
|
@ -596,14 +454,11 @@ func defaultTransport() *http.Transport {
|
|||
// 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 {
|
||||
func defaultPooledTransport(d Dialer) *http.Transport {
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: d.Dial,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
DisableKeepAlives: false,
|
||||
MaxIdleConnsPerHost: 1,
|
||||
}
|
||||
|
@ -613,8 +468,16 @@ func defaultPooledTransport() *http.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 {
|
||||
// If a custom dialer is not provided, one with sane defaults will be created.
|
||||
func defaultClient(d Dialer) *http.Client {
|
||||
if d == nil {
|
||||
d = &net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 5 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Transport: defaultTransport(),
|
||||
Transport: defaultTransport(d),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
// +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}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// +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}
|
||||
}
|
|
@ -26,7 +26,6 @@ func (c *Client) ControllerList(opts types.ListOptions) ([]*types.Controller, er
|
|||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// HealthAPIPrefix is a partial path to the HTTP endpoint.
|
||||
HealthAPIPrefix = "health"
|
||||
)
|
||||
|
||||
// CPHealth returns the health of the control plane server at a given url.
|
||||
func (c *Client) CPHealth(ctx context.Context, hostname string) (*types.CPHealthStatus, error) {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/v1/%s", hostname, DefaultPort, HealthAPIPrefix)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
if c.username != "" && c.secret != "" {
|
||||
req.SetBasicAuth(c.username, c.secret)
|
||||
}
|
||||
|
||||
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status *types.CPHealthStatus
|
||||
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// DPHealth returns the health of the data plane server at a given url.
|
||||
func (c *Client) DPHealth(ctx context.Context, hostname string) (*types.DPHealthStatus, error) {
|
||||
|
||||
url := fmt.Sprintf("http://%s:%s/v1/%s", hostname, DataplaneHealthPort, HealthAPIPrefix)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
if c.username != "" && c.secret != "" {
|
||||
req.SetBasicAuth(c.username, c.secret)
|
||||
}
|
||||
|
||||
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var status *types.DPHealthStatus
|
||||
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// LoggerAPIPrefix is a partial path to the HTTP endpoint.
|
||||
LoggerAPIPrefix = "logs"
|
||||
)
|
||||
|
||||
// LoggerConfig returns every cluster node's logging configuration.
|
||||
func (c *Client) LoggerConfig(opts types.ListOptions) ([]*types.Logger, error) {
|
||||
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", LoggerAPIPrefix+"/cluster/config", listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var loggers []*types.Logger
|
||||
if err := json.NewDecoder(resp.Body).Decode(&loggers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loggers, nil
|
||||
|
||||
}
|
||||
|
||||
// LoggerUpdate patches updates to logging configuration. Fields to update must
|
||||
// be listed in the Fields value, and if a list of Nodes is given it will only
|
||||
// apply to the nodes listed. Returns the updated configuration.
|
||||
func (c *Client) LoggerUpdate(opts types.LoggerUpdateOptions) ([]*types.Logger, error) {
|
||||
|
||||
resp, err := c.do("PATCH", LoggerAPIPrefix+"/cluster/config", doOptions{
|
||||
data: opts,
|
||||
context: context.Background(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var loggers []*types.Logger
|
||||
if err := json.NewDecoder(resp.Body).Decode(&loggers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loggers, nil
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// LoginAPIPrefix is a partial path to the HTTP endpoint.
|
||||
LoginAPIPrefix = "auth/login"
|
||||
ErrLoginFailed = errors.New("Failed to get token from API endpoint")
|
||||
)
|
||||
|
||||
// Login attemps to get a token from the API
|
||||
func (c *Client) Login() (token string, err error) {
|
||||
resp, err := c.do("POST", LoginAPIPrefix, doOptions{data: struct {
|
||||
User string `json:"username"`
|
||||
Pass string `json:"password"`
|
||||
}{c.username, c.secret}})
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*Error); ok {
|
||||
return "", ErrLoginFailed
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", ErrLoginFailed
|
||||
}
|
||||
|
||||
unmarsh := struct {
|
||||
Token string `json:"token"`
|
||||
}{}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&unmarsh); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if unmarsh.Token == "" {
|
||||
return "", ErrLoginFailed
|
||||
}
|
||||
|
||||
return unmarsh.Token, nil
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"errors.go",
|
||||
"multidialer.go",
|
||||
"parsers.go",
|
||||
],
|
||||
importpath = "github.com/storageos/go-api/netutil",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/github.com/storageos/go-api/serror:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,26 @@
|
|||
package netutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/storageos/go-api/serror"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func errAllFailed(addrs []string) error {
|
||||
msg := fmt.Sprintf("failed to dial all known cluster members, (%s)", strings.Join(addrs, ","))
|
||||
help := "ensure that the value of $STORAGEOS_HOST (or the -H flag) is correct, and that there are healthy StorageOS nodes in this cluster"
|
||||
|
||||
return serror.NewTypedStorageOSError(serror.APIUncontactable, nil, msg, help)
|
||||
}
|
||||
|
||||
func newInvalidNodeError(err error) error {
|
||||
msg := fmt.Sprintf("invalid node format: %s", err)
|
||||
help := "please check the format of $STORAGEOS_HOST (or the -H flag) complies with the StorageOS JOIN format"
|
||||
|
||||
return serror.NewTypedStorageOSError(serror.InvalidHostConfig, err, msg, help)
|
||||
}
|
||||
|
||||
var errNoAddresses = errors.New("the MultiDialer instance has not been initialised with client addresses")
|
||||
var errUnsupportedScheme = errors.New("unsupported URL scheme")
|
||||
var errInvalidPortNumber = errors.New("invalid port number")
|
|
@ -0,0 +1,109 @@
|
|||
package netutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
var DefaultDialPort = "5705"
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// Dialer is an interface that matches *net.Dialer. The intention is to allow either the stdlib
|
||||
// dialer or a custom implementation to be passed to the MultiDialer constructor. This also makes
|
||||
// the component easier to test.
|
||||
type Dialer interface {
|
||||
DialContext(context.Context, string, string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// MultiDialer is a custom net Dialer (to be used in a net.Transport field) that attemps to dial
|
||||
// out to any (potentialy many) of a set of pre-defined addresses. The intended use of this
|
||||
// function is to extend the functionality of the stdlib http.Client to transparently support
|
||||
// requests to any member of a given storageos cluster.
|
||||
type MultiDialer struct {
|
||||
Addresses []string
|
||||
Dialer *net.Dialer
|
||||
}
|
||||
|
||||
// NewMultiDialer returns a new MultiDialer instance, configured to dial out to the given set of
|
||||
// nodes. Nodes can be provided using a URL format (e.g. http://google.com:80), or a host-port pair
|
||||
// (e.g. localhost:4567).
|
||||
//
|
||||
// If a port number is omitted, the value of DefaultDialPort is used.
|
||||
// Given hostnames are resolved to IP addresses, and IP addresses are used verbatim.
|
||||
//
|
||||
// If called with a non-nil dialer, the MultiDialer instance will use this for internall dial
|
||||
// requests. If this value is nil, the function will initialise one with sane defaults.
|
||||
func NewMultiDialer(nodes []string, dialer *net.Dialer) (*MultiDialer, error) {
|
||||
// If a dialer is not provided, initialise one with sane defaults
|
||||
if dialer == nil {
|
||||
dialer = &net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 5 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
addrs, err := addrsFromNodes(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MultiDialer{
|
||||
Addresses: addrs,
|
||||
Dialer: dialer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DialContext will dial each of the MultiDialer's internal addresses in a random order until one
|
||||
// successfully returns a connection, it has run out of addresses (returning ErrAllFailed), or the
|
||||
// given context has been closed.
|
||||
//
|
||||
// Due to the intrinsic behaviour of this function, any address passed to this function will be
|
||||
// ignored.
|
||||
func (m *MultiDialer) DialContext(ctx context.Context, network, ignoredAddress string) (net.Conn, error) {
|
||||
if len(m.Addresses) == 0 {
|
||||
return nil, newInvalidNodeError(errNoAddresses)
|
||||
}
|
||||
|
||||
// Shuffle a copy of the addresses (for even load balancing)
|
||||
addrs := make([]string, len(m.Addresses))
|
||||
copy(addrs, m.Addresses)
|
||||
|
||||
// Fisher–Yates shuffle algorithm
|
||||
for i := len(addrs) - 1; i > 0; i-- {
|
||||
j := rand.Intn(i + 1)
|
||||
addrs[i], addrs[j] = addrs[j], addrs[i]
|
||||
}
|
||||
|
||||
// Try to dial each of these addresses in turn, or return on closed context
|
||||
for _, addr := range addrs {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
|
||||
default:
|
||||
// Create new child context for a single dial
|
||||
dctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
|
||||
conn, err := m.Dialer.DialContext(dctx, network, addr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
|
||||
// We failed to dail all of the addresses we have
|
||||
return nil, errAllFailed(m.Addresses)
|
||||
}
|
||||
|
||||
// Dial returns the result of a call to m.DialContext passing in the background context
|
||||
func (m *MultiDialer) Dial(network, addr string) (net.Conn, error) {
|
||||
return m.DialContext(context.Background(), network, addr)
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package netutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// addrsFromNodes takes a list of node hosts and attempts to return a list of hosts in ip:port
|
||||
// format along with any error encountered.
|
||||
//
|
||||
// The function accepts node hosts in URL, ip, ip:port, resolvable-name and resolvable-name:port
|
||||
// formats and will append the default port value if needed.
|
||||
func addrsFromNodes(nodes []string) ([]string, error) {
|
||||
var addrs []string
|
||||
|
||||
for _, n := range nodes {
|
||||
switch {
|
||||
// Assume that the node is provided as a URL
|
||||
case strings.Contains(n, "://"):
|
||||
newAddrs, err := parseURL(n)
|
||||
if err != nil {
|
||||
return nil, newInvalidNodeError(err)
|
||||
}
|
||||
|
||||
addrs = append(addrs, newAddrs...)
|
||||
|
||||
// Assume the node is in hostname:port or ip:port format
|
||||
case strings.Contains(n, ":"):
|
||||
newAddrs, err := parseHostPort(n)
|
||||
if err != nil {
|
||||
return nil, newInvalidNodeError(err)
|
||||
}
|
||||
|
||||
addrs = append(addrs, newAddrs...)
|
||||
|
||||
// Assume hostname or ip
|
||||
default:
|
||||
newAddrs, err := parseHost(n)
|
||||
if err != nil {
|
||||
return nil, newInvalidNodeError(err)
|
||||
}
|
||||
|
||||
addrs = append(addrs, newAddrs...)
|
||||
}
|
||||
}
|
||||
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
func validPort(port string) bool {
|
||||
intPort, err := strconv.Atoi(port)
|
||||
|
||||
return (err == nil) &&
|
||||
(intPort > 0) &&
|
||||
(intPort <= 65535)
|
||||
}
|
||||
|
||||
// parseURL takes a valid URL and verifies that it is using a correct scheme, has a resolvable
|
||||
// address (or is an IP) and has a valid port (or adds the default if the port is omitted). The
|
||||
// function then returns a list of addresses in ip:port format along with any error encountered.
|
||||
//
|
||||
// The function may return multiple addresses depending on the dns answer received when resolving
|
||||
// the host.
|
||||
func parseURL(node string) ([]string, error) {
|
||||
url, err := url.Parse(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Verify a valid scheme
|
||||
switch url.Scheme {
|
||||
case "tcp", "http", "https":
|
||||
host, port, err := net.SplitHostPort(url.Host)
|
||||
if err != nil {
|
||||
// We could be here as there is no port, lets try one last time with default port added
|
||||
host, port, err = net.SplitHostPort(url.Host + ":" + DefaultDialPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !validPort(port) {
|
||||
return nil, errInvalidPortNumber
|
||||
}
|
||||
|
||||
// LookupHost works for IP addr too
|
||||
addrs, err := net.LookupHost(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, a := range addrs {
|
||||
addrs[i] = a + ":" + port
|
||||
}
|
||||
|
||||
return addrs, nil
|
||||
|
||||
default:
|
||||
return nil, errUnsupportedScheme
|
||||
}
|
||||
}
|
||||
|
||||
// parseHostPort takes a string in host:port format and checks it has a resolvable address (or is
|
||||
// an IP) and a valid port (or adds the default if the port is omitted). The function then returns
|
||||
// a list of addresses in ip:port format along with any error encountered.
|
||||
//
|
||||
// The function may return multiple addresses depending on the dns answer received when resolving
|
||||
// the host.
|
||||
func parseHostPort(node string) ([]string, error) {
|
||||
host, port, err := net.SplitHostPort(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !validPort(port) {
|
||||
return nil, errInvalidPortNumber
|
||||
}
|
||||
|
||||
// LookupHost works for IP addr too
|
||||
addrs, err := net.LookupHost(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, a := range addrs {
|
||||
addrs[i] = a + ":" + port
|
||||
}
|
||||
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// parseHostPort takes a hostname string and checks it is resolvable to an address (or is already
|
||||
// an IP) The function then returns a list of addresses in ip:port format (where port is the
|
||||
// default port) along with any error encountered.
|
||||
//
|
||||
// The function may return multiple addresses depending on the dns answer received when resolving
|
||||
// the host.
|
||||
func parseHost(node string) ([]string, error) {
|
||||
return parseHostPort(node + ":" + DefaultDialPort)
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/storageos/go-api/types"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// PolicyAPIPrefix is a partial path to the HTTP endpoint.
|
||||
PolicyAPIPrefix = "policies"
|
||||
|
||||
// ErrNoSuchPolicy is the error returned when the policy does not exist.
|
||||
ErrNoSuchPolicy = errors.New("no such policy")
|
||||
)
|
||||
|
||||
// nopMarshaler is an alias to a []byte that implements json.Marshaler
|
||||
// it bypasses the base64 encoded string representation that json will give byte slices.
|
||||
// It should only be used to wrap []byte types containing pre-rendered valid json that will later
|
||||
// (out of the caller's control) be run through json.Marshal
|
||||
type nopMarshaler []byte
|
||||
|
||||
func (n *nopMarshaler) MarshalJSON() ([]byte, error) {
|
||||
return *n, nil
|
||||
}
|
||||
|
||||
// PolicyCreate creates a policy on the server.
|
||||
func (c *Client) PolicyCreate(jsonl []byte, ctx context.Context) error {
|
||||
nopm := nopMarshaler(jsonl)
|
||||
_, err := c.do("POST", PolicyAPIPrefix, doOptions{
|
||||
data: &nopm,
|
||||
context: ctx,
|
||||
headers: map[string]string{"Content-Type": "application/x-jsonlines"},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Policy returns a policy on the server by ID.
|
||||
func (c *Client) Policy(id string) (*types.Policy, error) {
|
||||
path := fmt.Sprintf("%s/%s", PolicyAPIPrefix, id)
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchPolicy
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var policy *types.Policy
|
||||
if err := json.NewDecoder(resp.Body).Decode(&policy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// PolicyList returns the list of policies on the server.
|
||||
func (c *Client) PolicyList(opts types.ListOptions) (types.PolicySet, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", PolicyAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var policies types.PolicySet
|
||||
if err := json.NewDecoder(resp.Body).Decode(&policies); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// PolicyDelete deletes a policy on the server by ID.
|
||||
func (c *Client) PolicyDelete(opts types.DeleteOptions) error {
|
||||
resp, err := c.do("DELETE", PolicyAPIPrefix+"/"+opts.Name, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchPolicy
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
|
@ -72,10 +72,15 @@ func (c *Client) Rule(namespace string, ref string) (*types.Rule, error) {
|
|||
|
||||
// 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,
|
||||
path, err := namespacedPath(opts.Namespace, RuleAPIPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: opts,
|
||||
// namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"error_kind.go",
|
||||
"kind_lookup_map.go",
|
||||
"storageos_error.go",
|
||||
"storageoserrorkind_string.go",
|
||||
"typed_error.go",
|
||||
],
|
||||
importpath = "github.com/storageos/go-api/serror",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,11 @@
|
|||
package serror
|
||||
|
||||
//go:generate stringer -type=StorageOSErrorKind error_kind.go
|
||||
type StorageOSErrorKind int
|
||||
|
||||
// Known error kinds
|
||||
const (
|
||||
UnknownError StorageOSErrorKind = iota
|
||||
APIUncontactable
|
||||
InvalidHostConfig
|
||||
)
|
|
@ -0,0 +1,37 @@
|
|||
package serror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var kindLookupMap map[string]StorageOSErrorKind
|
||||
|
||||
func init() {
|
||||
kindLookupMap = make(map[string]StorageOSErrorKind)
|
||||
|
||||
// Populate the lookup map with all the known constants
|
||||
for i := StorageOSErrorKind(0); !strings.HasPrefix(i.String(), "StorageOSErrorKind("); i++ {
|
||||
kindLookupMap[i.String()] = i
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StorageOSErrorKind) UnmarshalJSON(b []byte) error {
|
||||
str := ""
|
||||
if err := json.Unmarshal(b, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, ok := kindLookupMap[str]
|
||||
if !ok {
|
||||
return fmt.Errorf("Failed to unmarshal ErrorKind %s", s)
|
||||
}
|
||||
|
||||
*s = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StorageOSErrorKind) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(s.String())
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package serror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type StorageOSError interface {
|
||||
// embedding error provides compatibility with standard error handling code
|
||||
error
|
||||
|
||||
// Encoding/decoding methods to help errors traverse API boundaries
|
||||
json.Marshaler
|
||||
json.Unmarshaler
|
||||
|
||||
Err() error // Returns the underlying error that caused this event
|
||||
String() string // A short string representing the error (for logging etc)
|
||||
Help() string // A larger string that should provide informative debug instruction to users
|
||||
Kind() StorageOSErrorKind // A type representing a set of known error conditions, helpful to switch on
|
||||
Extra() map[string]string // A container for error specific information
|
||||
|
||||
// TODO: should we include callstack traces here? We could have a debug mode for it.
|
||||
}
|
||||
|
||||
func ErrorKind(err error) StorageOSErrorKind {
|
||||
if serr, ok := err.(StorageOSError); ok {
|
||||
return serr.Kind()
|
||||
}
|
||||
return UnknownError
|
||||
}
|
||||
|
||||
func IsStorageOSError(err error) bool {
|
||||
_, ok := err.(StorageOSError)
|
||||
return ok
|
||||
}
|
16
vendor/github.com/storageos/go-api/serror/storageoserrorkind_string.go
generated
vendored
Normal file
16
vendor/github.com/storageos/go-api/serror/storageoserrorkind_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Code generated by "stringer -type=StorageOSErrorKind error_kind.go"; DO NOT EDIT.
|
||||
|
||||
package serror
|
||||
|
||||
import "strconv"
|
||||
|
||||
const _StorageOSErrorKind_name = "UnknownErrorAPIUncontactableInvalidHostConfig"
|
||||
|
||||
var _StorageOSErrorKind_index = [...]uint8{0, 12, 28, 45}
|
||||
|
||||
func (i StorageOSErrorKind) String() string {
|
||||
if i < 0 || i >= StorageOSErrorKind(len(_StorageOSErrorKind_index)-1) {
|
||||
return "StorageOSErrorKind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _StorageOSErrorKind_name[_StorageOSErrorKind_index[i]:_StorageOSErrorKind_index[i+1]]
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package serror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func NewTypedStorageOSError(kind StorageOSErrorKind, err error, msg string, help string) StorageOSError {
|
||||
return &typedStorageOSError{
|
||||
internal: &internal_TypedStorageOSError{
|
||||
ErrorKind: &kind,
|
||||
Cause: err,
|
||||
ErrMessage: msg,
|
||||
HelpMessage: help,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewUntypedStorageOSError(err error, msg string, help string) StorageOSError {
|
||||
var kind StorageOSErrorKind = UnknownError
|
||||
|
||||
return &typedStorageOSError{
|
||||
internal: &internal_TypedStorageOSError{
|
||||
ErrorKind: &kind,
|
||||
Cause: err,
|
||||
ErrMessage: msg,
|
||||
HelpMessage: help,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type internal_TypedStorageOSError struct {
|
||||
ErrorKind *StorageOSErrorKind `json:"error_kind"`
|
||||
Cause error `json:"caused_by"`
|
||||
ErrMessage string `json:"error_message"`
|
||||
HelpMessage string `json:"help_message"`
|
||||
ExtraMap map[string]string `json:"extra"`
|
||||
}
|
||||
|
||||
type typedStorageOSError struct {
|
||||
internal *internal_TypedStorageOSError
|
||||
}
|
||||
|
||||
func (t *typedStorageOSError) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.internal)
|
||||
}
|
||||
|
||||
func (t *typedStorageOSError) UnmarshalJSON(d []byte) error {
|
||||
internal := &internal_TypedStorageOSError{}
|
||||
|
||||
err := json.Unmarshal(d, internal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.internal = internal
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *typedStorageOSError) Error() string { return t.String() }
|
||||
func (t *typedStorageOSError) Err() error { return t.internal.Cause }
|
||||
func (t *typedStorageOSError) String() string { return t.internal.ErrMessage }
|
||||
func (t *typedStorageOSError) Help() string { return t.internal.HelpMessage }
|
||||
func (t *typedStorageOSError) Kind() StorageOSErrorKind { return *t.internal.ErrorKind }
|
||||
func (t *typedStorageOSError) Extra() map[string]string { return t.internal.ExtraMap }
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
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"
|
|
@ -1,854 +0,0 @@
|
|||
|
||||
# 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"
|
|
@ -13,13 +13,17 @@ go_library(
|
|||
"error_response.go",
|
||||
"events.go",
|
||||
"list_options.go",
|
||||
"logger.go",
|
||||
"namespace.go",
|
||||
"node.go",
|
||||
"operator.go",
|
||||
"policy.go",
|
||||
"pool.go",
|
||||
"pool_create_options.go",
|
||||
"rule.go",
|
||||
"template.go",
|
||||
"template_create_options.go",
|
||||
"user.go",
|
||||
"version.go",
|
||||
"volume.go",
|
||||
"volume_create_options.go",
|
||||
|
|
|
@ -41,6 +41,7 @@ type Controller struct {
|
|||
Scheduler bool `json:"scheduler"`
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
DeviceDir string `json:"deviceDir"`
|
||||
APIPort int `json:"apiPort"`
|
||||
NatsPort int `json:"natsPort"`
|
||||
NatsClusterPort int `json:"natsClusterPort"`
|
||||
|
@ -59,6 +60,9 @@ type Controller struct {
|
|||
VersionInfo map[string]VersionInfo `json:"versionInfo"`
|
||||
Version string `json:"version"`
|
||||
|
||||
// Cordon true if in an unschedulable state
|
||||
Cordon bool `json:"unschedulable"`
|
||||
|
||||
// high level stats that combine info from all driver instances
|
||||
CapacityStats CapacityStats `json:"capacityStats"`
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ type ControllerUpdateOptions struct {
|
|||
// Labels are user-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
// Cordon sets the controler into an unschedulable state if true
|
||||
Cordon bool `json:"unschedulable"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ type Deployment struct {
|
|||
// Read Only: true
|
||||
Controller string `json:"controller"`
|
||||
|
||||
// Controller name
|
||||
// Read Only: true
|
||||
ControllerName string `json:"controllerName"`
|
||||
|
||||
// Health
|
||||
// Read Only: true
|
||||
Health string `json:"health"`
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package types
|
||||
|
||||
import "context"
|
||||
|
||||
// Logger is the runtime configuration of the node's logging services.
|
||||
// swagger:model Logger
|
||||
type Logger struct {
|
||||
|
||||
// Node name
|
||||
Node string `json:"node"`
|
||||
|
||||
// Log level
|
||||
Level string `json:"level"`
|
||||
|
||||
// Log filter
|
||||
Filter string `json:"filter"`
|
||||
|
||||
// Log filters by category
|
||||
// Read Only: true
|
||||
Categories map[string]string `json:"categories"`
|
||||
}
|
||||
|
||||
// LoggerUpdateOptions are the available parameters for updating loggers.
|
||||
type LoggerUpdateOptions struct {
|
||||
|
||||
// Log level
|
||||
Level string `json:"level"`
|
||||
|
||||
// Log filter
|
||||
Filter string `json:"filter"`
|
||||
|
||||
// List of nodes to update. All if not set.
|
||||
Nodes []string `json:"nodes"`
|
||||
|
||||
// List of fields to update. Must be set.
|
||||
Fields []string `json:"fields"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type SubModuleStatus struct {
|
||||
Status string `json:"status"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
ChangedAt string `json:"changedAt"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type NamedSubModuleStatus struct {
|
||||
Name string
|
||||
SubModuleStatus
|
||||
}
|
||||
|
||||
type CPHealthStatus struct {
|
||||
KV SubModuleStatus
|
||||
KVWrite SubModuleStatus
|
||||
NATS SubModuleStatus
|
||||
Scheduler SubModuleStatus
|
||||
}
|
||||
|
||||
func (c *CPHealthStatus) ToNamedSubmodules() []NamedSubModuleStatus {
|
||||
return []NamedSubModuleStatus{
|
||||
{Name: "nats", SubModuleStatus: c.NATS},
|
||||
{Name: "kv", SubModuleStatus: c.KV},
|
||||
{Name: "kv_write", SubModuleStatus: c.KVWrite},
|
||||
{Name: "scheduler", SubModuleStatus: c.Scheduler},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CPHealthStatus) UnmarshalJSON(data []byte) error {
|
||||
unmarsh := struct {
|
||||
Submodules struct {
|
||||
KV SubModuleStatus `json:"kv"`
|
||||
KVWrite SubModuleStatus `json:"kv_write"`
|
||||
NATS SubModuleStatus `json:"nats"`
|
||||
Scheduler SubModuleStatus `json:"scheduler"`
|
||||
} `json:"submodules"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(data, &unmarsh); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.KV = unmarsh.Submodules.KV
|
||||
c.KVWrite = unmarsh.Submodules.KVWrite
|
||||
c.NATS = unmarsh.Submodules.NATS
|
||||
c.Scheduler = unmarsh.Submodules.Scheduler
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DPHealthStatus struct {
|
||||
DirectFSClient SubModuleStatus
|
||||
DirectFSServer SubModuleStatus
|
||||
Director SubModuleStatus
|
||||
FSDriver SubModuleStatus
|
||||
FS SubModuleStatus
|
||||
}
|
||||
|
||||
func (d *DPHealthStatus) ToNamedSubmodules() []NamedSubModuleStatus {
|
||||
return []NamedSubModuleStatus{
|
||||
{Name: "dfs_client", SubModuleStatus: d.DirectFSClient},
|
||||
{Name: "dfs_server", SubModuleStatus: d.DirectFSServer},
|
||||
{Name: "director", SubModuleStatus: d.Director},
|
||||
{Name: "fs_driver", SubModuleStatus: d.FSDriver},
|
||||
{Name: "fs", SubModuleStatus: d.FS},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DPHealthStatus) UnmarshalJSON(data []byte) error {
|
||||
unmarsh := struct {
|
||||
Submodules struct {
|
||||
DirectFSClient SubModuleStatus `json:"directfs-client"`
|
||||
DirectFSServer SubModuleStatus `json:"directfs-server"`
|
||||
Director SubModuleStatus `json:"director"`
|
||||
FSDriver SubModuleStatus `json:"filesystem-driver"`
|
||||
FS SubModuleStatus `json:"fs"`
|
||||
} `json:"submodules"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(data, &unmarsh); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.DirectFSClient = unmarsh.Submodules.DirectFSClient
|
||||
d.DirectFSServer = unmarsh.Submodules.DirectFSServer
|
||||
d.Director = unmarsh.Submodules.Director
|
||||
d.FSDriver = unmarsh.Submodules.FSDriver
|
||||
d.FS = unmarsh.Submodules.FS
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Policy struct {
|
||||
Spec struct {
|
||||
User string `json:"user,omitempty"`
|
||||
Group string `json:"group,omitempty"`
|
||||
Readonly bool `json:"readonly,omitempty"`
|
||||
APIGroup string `json:"apiGroup,omitempty"`
|
||||
Resource string `json:"resource,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
NonResourcePath string `json:"nonResourcePath,omitempty"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
|
||||
// PolicyWithId is used as an internal type to render table formated versions of the json response
|
||||
type PolicyWithID struct {
|
||||
Policy
|
||||
ID string
|
||||
}
|
||||
|
||||
// MarshalJSON returns a marshaled copy of the internal policy object, so it is still valid to use
|
||||
// with the REST API
|
||||
func (p *PolicyWithID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(p.Policy)
|
||||
}
|
||||
|
||||
// PolicySet is a representation of the data structure returned from the REST API
|
||||
type PolicySet map[string]Policy
|
||||
|
||||
func (p PolicySet) GetPoliciesWithID() []*PolicyWithID {
|
||||
rtn := make([]*PolicyWithID, 0, len(p))
|
||||
|
||||
for k, v := range p {
|
||||
rtn = append(rtn, &PolicyWithID{
|
||||
Policy: v,
|
||||
ID: k,
|
||||
})
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UUID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Groups []string `json:"groups"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
func (u *User) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
UUID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Groups string `json:"groups"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Role string `json:"role"`
|
||||
}{
|
||||
UUID: u.UUID,
|
||||
Username: u.Username,
|
||||
Groups: strings.Join(u.Groups, ","),
|
||||
Password: u.Password,
|
||||
Role: u.Role,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (u *User) UnmarshalJSON(data []byte) error {
|
||||
temp := &struct {
|
||||
UUID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Groups string `json:"groups"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(data, temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.UUID = temp.UUID
|
||||
u.Username = temp.Username
|
||||
u.Password = temp.Password
|
||||
u.Role = temp.Role
|
||||
u.Groups = strings.Split(temp.Groups, ",")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UserCreateOptions struct {
|
||||
Username string `json:"username"`
|
||||
Groups []string `json:"groups"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
|
||||
// Context can be set with a timeout or can be used to cancel a request.
|
||||
Context context.Context `json:"-"`
|
||||
}
|
||||
|
||||
func (u UserCreateOptions) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
Username string `json:"username"`
|
||||
Groups string `json:"groups"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
}{
|
||||
Username: u.Username,
|
||||
Groups: strings.Join(u.Groups, ","),
|
||||
Password: u.Password,
|
||||
Role: u.Role,
|
||||
})
|
||||
|
||||
}
|
|
@ -43,6 +43,9 @@ type Volume struct {
|
|||
// Namespace is the object name and authentication scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// node selector (where volumes should land)
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// Volume deployment information for the master volume.
|
||||
// Read Only: true
|
||||
Master *Deployment `json:"master,omitempty"`
|
||||
|
@ -51,6 +54,9 @@ type Volume struct {
|
|||
// Read Only: true
|
||||
Mounted bool `json:"mounted"`
|
||||
|
||||
// MountDevice, where the device is located
|
||||
MountDevice string `json:"mountDevice"`
|
||||
|
||||
// Mountpoint, where the volume is mounted
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
|
||||
|
@ -78,6 +84,10 @@ type Volume struct {
|
|||
// Read Only: true
|
||||
StatusMessage string `json:"statusMessage"`
|
||||
|
||||
// mkfs performed on new volumes
|
||||
MkfsDone bool `json:"mkfsDone"`
|
||||
MkfsDoneAt time.Time `json:"mkfsDoneAt"`
|
||||
|
||||
// When the volume was created.
|
||||
// Read Only: true
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
|
|
@ -25,6 +25,9 @@ type VolumeCreateOptions struct {
|
|||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// node selector (where volumes should land)
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// Labels are user-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ type VolumeUpdateOptions struct {
|
|||
// Namespace is the object scope, such as for teams and projects.
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// node selector (where volumes should land)
|
||||
NodeSelector string `json:"nodeSelector"`
|
||||
|
||||
// Labels are user-defined key/value metadata.
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
package storageos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/storageos/go-api/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// UserAPIPrefix is a partial path to the HTTP endpoint.
|
||||
UserAPIPrefix = "users"
|
||||
|
||||
// ErrNoSuchUser is the error returned when the user does not exist.
|
||||
ErrNoSuchUser = errors.New("no such user")
|
||||
)
|
||||
|
||||
// UserList returns the list of available users.
|
||||
func (c *Client) UserList(opts types.ListOptions) ([]*types.User, error) {
|
||||
listOpts := doOptions{
|
||||
fieldSelector: opts.FieldSelector,
|
||||
labelSelector: opts.LabelSelector,
|
||||
namespace: opts.Namespace,
|
||||
context: opts.Context,
|
||||
}
|
||||
|
||||
if opts.LabelSelector != "" {
|
||||
query := url.Values{}
|
||||
query.Add("labelSelector", opts.LabelSelector)
|
||||
listOpts.values = query
|
||||
}
|
||||
|
||||
resp, err := c.do("GET", UserAPIPrefix, listOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
users := make([]*types.User, 0)
|
||||
if err := json.NewDecoder(resp.Body).Decode(&users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// User returns a user by its username/id.
|
||||
func (c *Client) User(username string) (*types.User, error) {
|
||||
path := fmt.Sprintf("%s/%s", UserAPIPrefix, username)
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchUser
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var user *types.User
|
||||
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UserCreate creates a user on the server.
|
||||
func (c *Client) UserCreate(opts types.UserCreateOptions) error {
|
||||
_, err := c.do("POST", UserAPIPrefix, doOptions{
|
||||
data: opts,
|
||||
context: opts.Context,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// UserUpdate updates a user on the server.
|
||||
func (c *Client) UserUpdate(user *types.User, ctx context.Context) error {
|
||||
var ref string
|
||||
switch {
|
||||
case user.UUID != "":
|
||||
ref = user.UUID
|
||||
case user.Username != "":
|
||||
ref = user.Username
|
||||
default:
|
||||
return ErrNoSuchUser
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s", UserAPIPrefix, ref)
|
||||
resp, err := c.do("POST", path, doOptions{
|
||||
data: user,
|
||||
context: ctx,
|
||||
})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchUser
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UserDelete removes a user by its reference.
|
||||
func (c *Client) UserDelete(opts types.DeleteOptions) error {
|
||||
resp, err := c.do("DELETE", UserAPIPrefix+"/"+opts.Name, doOptions{})
|
||||
if err != nil {
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.Status == http.StatusNotFound {
|
||||
return ErrNoSuchUser
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue