From a4cc754fb4affb1bb3c0fc6c80d658bcf73ce68e Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 14 Sep 2018 19:36:57 +0530 Subject: [PATCH 1/2] StorageOS update api library --- Godeps/Godeps.json | 16 +- Godeps/LICENSES | 16 +- pkg/volume/storageos/storageos_util.go | 4 +- vendor/github.com/storageos/go-api/BUILD | 5 +- vendor/github.com/storageos/go-api/LICENCE | 2 +- vendor/github.com/storageos/go-api/client.go | 306 ++++--- .../github.com/storageos/go-api/controller.go | 110 --- vendor/github.com/storageos/go-api/event.go | 189 ----- vendor/github.com/storageos/go-api/health.go | 12 +- vendor/github.com/storageos/go-api/login.go | 3 +- .../github.com/storageos/go-api/netutil/BUILD | 2 +- .../storageos/go-api/netutil/errors.go | 15 +- .../storageos/go-api/netutil/multidialer.go | 109 --- .../storageos/go-api/netutil/netutil.go | 5 + .../storageos/go-api/netutil/parsers.go | 170 ++-- .../storageos/go-api/network_diagnostics.go | 35 + vendor/github.com/storageos/go-api/node.go | 110 +++ vendor/github.com/storageos/go-api/policy.go | 5 +- vendor/github.com/storageos/go-api/pool.go | 23 +- vendor/github.com/storageos/go-api/rule.go | 3 +- .../storageos/go-api/testing_notes.txt | 769 ------------------ .../github.com/storageos/go-api/types/BUILD | 8 +- .../storageos/go-api/types/connectivity.go | 41 + .../storageos/go-api/types/controller.go | 78 -- .../storageos/go-api/types/deployment.go | 8 +- .../storageos/go-api/types/health.go | 134 +++ .../github.com/storageos/go-api/types/node.go | 181 +++-- ...date_options.go => node_update_options.go} | 15 +- .../github.com/storageos/go-api/types/pool.go | 34 +- .../go-api/types/pool_create_options.go | 42 - .../storageos/go-api/types/pool_options.go | 28 + .../storageos/go-api/types/volume_stats.go | 8 + .../github.com/storageos/go-api/validation.go | 4 +- 33 files changed, 795 insertions(+), 1695 deletions(-) delete mode 100644 vendor/github.com/storageos/go-api/controller.go delete mode 100644 vendor/github.com/storageos/go-api/event.go delete mode 100644 vendor/github.com/storageos/go-api/netutil/multidialer.go create mode 100644 vendor/github.com/storageos/go-api/netutil/netutil.go create mode 100644 vendor/github.com/storageos/go-api/network_diagnostics.go create mode 100644 vendor/github.com/storageos/go-api/node.go delete mode 100644 vendor/github.com/storageos/go-api/testing_notes.txt create mode 100644 vendor/github.com/storageos/go-api/types/connectivity.go delete mode 100644 vendor/github.com/storageos/go-api/types/controller.go create mode 100644 vendor/github.com/storageos/go-api/types/health.go rename vendor/github.com/storageos/go-api/types/{controller_update_options.go => node_update_options.go} (54%) delete mode 100644 vendor/github.com/storageos/go-api/types/pool_create_options.go create mode 100644 vendor/github.com/storageos/go-api/types/pool_options.go create mode 100644 vendor/github.com/storageos/go-api/types/volume_stats.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 0febbc8e82..765216c47c 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -3084,23 +3084,23 @@ }, { "ImportPath": "github.com/storageos/go-api", - "Comment": "0.3.4", - "Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" + "Comment": "1.0.0-rc1-2-g343b3eff91fcc8", + "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e" }, { "ImportPath": "github.com/storageos/go-api/netutil", - "Comment": "0.3.4", - "Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" + "Comment": "1.0.0-rc1-2-g343b3eff91fcc8", + "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e" }, { "ImportPath": "github.com/storageos/go-api/serror", - "Comment": "0.3.4", - "Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" + "Comment": "1.0.0-rc1-2-g343b3eff91fcc8", + "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e" }, { "ImportPath": "github.com/storageos/go-api/types", - "Comment": "0.3.4", - "Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" + "Comment": "1.0.0-rc1-2-g343b3eff91fcc8", + "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e" }, { "ImportPath": "github.com/stretchr/objx", diff --git a/Godeps/LICENSES b/Godeps/LICENSES index 41628249ad..9d433286d9 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -90086,7 +90086,7 @@ SOFTWARE. MIT License -Copyright (c) 2015-2017 StorageOS +Copyright (c) 2015-2018 StorageOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -90130,7 +90130,7 @@ 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/LICENCE 10d8703157b5fd9422b1813bae555905 ================================================================================ @@ -90139,7 +90139,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MIT License -Copyright (c) 2015-2017 StorageOS +Copyright (c) 2015-2018 StorageOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -90183,7 +90183,7 @@ 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/LICENCE 10d8703157b5fd9422b1813bae555905 ================================================================================ @@ -90192,7 +90192,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MIT License -Copyright (c) 2015-2017 StorageOS +Copyright (c) 2015-2018 StorageOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -90236,7 +90236,7 @@ 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/LICENCE 10d8703157b5fd9422b1813bae555905 ================================================================================ @@ -90245,7 +90245,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MIT License -Copyright (c) 2015-2017 StorageOS +Copyright (c) 2015-2018 StorageOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -90289,7 +90289,7 @@ 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/LICENCE 10d8703157b5fd9422b1813bae555905 ================================================================================ diff --git a/pkg/volume/storageos/storageos_util.go b/pkg/volume/storageos/storageos_util.go index c102e4226d..0f5ebb80f8 100644 --- a/pkg/volume/storageos/storageos_util.go +++ b/pkg/volume/storageos/storageos_util.go @@ -69,7 +69,7 @@ type apiImplementer interface { VolumeMount(opts storageostypes.VolumeMountOptions) error VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error VolumeDelete(opt storageostypes.DeleteOptions) error - Controller(ref string) (*storageostypes.Controller, error) + Node(ref string) (*storageostypes.Node, error) } // storageosUtil is the utility structure to interact with the StorageOS API. @@ -289,7 +289,7 @@ func (u *storageosUtil) DeleteVolume(d *storageosDeleter) error { // specified. func (u *storageosUtil) DeviceDir(b *storageosMounter) string { - ctrl, err := u.api.Controller(b.plugin.host.GetHostName()) + ctrl, err := u.api.Node(b.plugin.host.GetHostName()) if err != nil { klog.Warningf("node device path lookup failed: %v", err) return defaultDeviceDir diff --git a/vendor/github.com/storageos/go-api/BUILD b/vendor/github.com/storageos/go-api/BUILD index 906584ce29..c052c8e326 100644 --- a/vendor/github.com/storageos/go-api/BUILD +++ b/vendor/github.com/storageos/go-api/BUILD @@ -4,12 +4,12 @@ go_library( name = "go_default_library", srcs = [ "client.go", - "controller.go", - "event.go", "health.go", "logger.go", "login.go", "namespace.go", + "network_diagnostics.go", + "node.go", "policy.go", "pool.go", "rule.go", @@ -24,7 +24,6 @@ go_library( 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", diff --git a/vendor/github.com/storageos/go-api/LICENCE b/vendor/github.com/storageos/go-api/LICENCE index ecaf938c25..c577408a6d 100644 --- a/vendor/github.com/storageos/go-api/LICENCE +++ b/vendor/github.com/storageos/go-api/LICENCE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2015-2017 StorageOS +Copyright (c) 2015-2018 StorageOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/storageos/go-api/client.go b/vendor/github.com/storageos/go-api/client.go index 5be9924f3d..4a95516b14 100644 --- a/vendor/github.com/storageos/go-api/client.go +++ b/vendor/github.com/storageos/go-api/client.go @@ -3,27 +3,32 @@ package storageos import ( "bytes" "context" - "crypto/tls" "encoding/json" "errors" "fmt" - "github.com/storageos/go-api/netutil" - "github.com/storageos/go-api/serror" "io" "io/ioutil" + "math/rand" "net" "net/http" "net/url" "reflect" "strconv" "strings" + "sync" "time" + + "github.com/storageos/go-api/netutil" + "github.com/storageos/go-api/serror" ) const ( - userAgent = "go-storageosclient" + // DefaultUserAgent is the default User-Agent header to include in HTTP requests. + DefaultUserAgent = "go-storageosclient" + // DefaultVersionStr is the string value of the default API version. DefaultVersionStr = "1" - DefaultVersion = 1 + // DefaultVersion is the default API version. + DefaultVersion = 1 ) var ( @@ -36,14 +41,17 @@ 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 + // ErrProxyNotSupported is returned when a client is unable to set a proxy for http requests. + ErrProxyNotSupported = errors.New("client does not support http proxy") + + // DefaultPort is the default API port. DefaultPort = "5705" - // DataplaneHealthPort is the the port used by the dataplane health-check service + // DataplaneHealthPort is the the port used by the dataplane health-check service. DataplaneHealthPort = "5704" - // DefaultHost is the default API host - DefaultHost = "tcp://localhost:" + DefaultPort + // DefaultHost is the default API host. + DefaultHost = "http://localhost:" + DefaultPort ) // APIVersion is an internal representation of a version of the Remote API. @@ -70,16 +78,21 @@ func (version APIVersion) String() string { // Client is the basic type of this package. It provides methods for // interaction with the API. type Client struct { + httpClient *http.Client + + addresses []string + username string + secret string + userAgent string + + configLock *sync.RWMutex // Lock for config changes + addressLock *sync.Mutex // Lock used to copy/update the address slice + + requestedAPIVersion APIVersion + serverAPIVersion APIVersion + expectedAPIVersion APIVersion + SkipServerVersionCheck bool - HTTPClient *http.Client - TLSConfig *tls.Config - username string - secret string - requestedAPIVersion APIVersion - serverAPIVersion APIVersion - expectedAPIVersion APIVersion - nativeHTTPClient *http.Client - useTLS bool } // ClientVersion returns the API version of the client @@ -103,6 +116,7 @@ func NewClient(nodes string) (*Client, error) { return nil, err } client.SkipServerVersionCheck = true + client.userAgent = DefaultUserAgent return client, nil } @@ -110,22 +124,24 @@ func NewClient(nodes string) (*Client, error) { // the given server endpoint, using a specific remote API version. func NewVersionedClient(nodestring string, apiVersionString string) (*Client, error) { nodes := strings.Split(nodestring, ",") - - d, err := netutil.NewMultiDialer(nodes, nil) + addresses, err := netutil.AddressesFromNodes(nodes) 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 - } + if len(addresses) > 1 { + // Shuffle returned addresses in attempt to spread the load + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) + rnd.Shuffle(len(addresses), func(i, j int) { + addresses[i], addresses[j] = addresses[j], addresses[i] + }) } - c := &Client{ - HTTPClient: defaultClient(d), - useTLS: useTLS, + client := &Client{ + httpClient: defaultClient(), + addresses: addresses, + configLock: &sync.RWMutex{}, + addressLock: &sync.Mutex{}, } if apiVersionString != "" { @@ -133,15 +149,24 @@ func NewVersionedClient(nodestring string, apiVersionString string) (*Client, er if err != nil { return nil, err } - c.requestedAPIVersion = APIVersion(version) + client.requestedAPIVersion = APIVersion(version) } - return c, nil + return client, nil +} + +// SetUserAgent sets the client useragent. +func (c *Client) SetUserAgent(useragent string) { + c.configLock.Lock() + defer c.configLock.Unlock() + c.userAgent = useragent } // SetAuth sets the API username and secret to be used for all API requests. // It should not be called concurrently with any other Client methods. func (c *Client) SetAuth(username string, secret string) { + c.configLock.Lock() + defer c.configLock.Unlock() if username != "" { c.username = username } @@ -150,15 +175,31 @@ func (c *Client) SetAuth(username string, secret string) { } } +// SetProxy will set the proxy URL for both the HTTPClient. +// If the transport method does not support usage +// of proxies, an error will be returned. +func (c *Client) SetProxy(proxy *url.URL) error { + c.configLock.Lock() + defer c.configLock.Unlock() + + if client := c.httpClient; client != nil { + transport, supported := client.Transport.(*http.Transport) + if !supported { + return ErrProxyNotSupported + } + transport.Proxy = http.ProxyURL(proxy) + } + return nil +} + // SetTimeout takes a timeout and applies it to both the HTTPClient and // nativeHTTPClient. It should not be called concurrently with any other Client // methods. func (c *Client) SetTimeout(t time.Duration) { - if c.HTTPClient != nil { - c.HTTPClient.Timeout = t - } - if c.nativeHTTPClient != nil { - c.nativeHTTPClient.Timeout = t + c.configLock.Lock() + defer c.configLock.Unlock() + if c.httpClient != nil { + c.httpClient.Timeout = t } } @@ -171,6 +212,8 @@ func (c *Client) checkAPIVersion() error { if err != nil { return err } + c.configLock.Lock() + defer c.configLock.Unlock() if c.requestedAPIVersion == 0 { c.expectedAPIVersion = c.serverAPIVersion } else { @@ -191,8 +234,7 @@ func (c *Client) Ping() error { if resp.StatusCode != http.StatusOK { return newError(resp) } - resp.Body.Close() - return nil + return resp.Body.Close() } func (c *Client) getServerAPIVersionString() (version string, err error) { @@ -204,16 +246,19 @@ func (c *Client) getServerAPIVersionString() (version string, err error) { } type doOptions struct { - data interface{} + context context.Context + data interface{} + + values url.Values + headers map[string]string + fieldSelector string labelSelector string namespace string - forceJSON bool - force bool - values url.Values - headers map[string]string - unversioned bool - context context.Context + + forceJSON bool + force bool + unversioned bool } func (c *Client) do(method, urlpath string, doOptions doOptions) (*http.Response, error) { @@ -247,76 +292,118 @@ func (c *Client) do(method, urlpath string, doOptions doOptions) (*http.Response query.Add("force", "1") } - httpClient := c.HTTPClient - u := c.getAPIPath(urlpath, query, doOptions.unversioned) + // Obtain a reader lock to prevent the http client from being + // modified underneath us during a do(). + c.configLock.RLock() + defer c.configLock.RUnlock() // This defer matches both the initial and the above lock - req, err := http.NewRequest(method, u, params) - if err != nil { - return nil, err - } - req.Header.Set("User-Agent", userAgent) - if doOptions.data != nil { - req.Header.Set("Content-Type", "application/json") - } else if method == "POST" { - req.Header.Set("Content-Type", "plain/text") - } - if c.username != "" && c.secret != "" { - req.SetBasicAuth(c.username, c.secret) - } - - for k, v := range doOptions.headers { - req.Header.Set(k, v) - } + httpClient := c.httpClient + endpoint := c.getAPIPath(urlpath, query, doOptions.unversioned) + // The doOptions Context is shared for every attempted request in the do. ctx := doOptions.context if ctx == nil { ctx = context.Background() } - 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) { + var failedAddresses = map[string]struct{}{} + + c.addressLock.Lock() + var addresses = make([]string, len(c.addresses)) + copy(addresses, c.addresses) + c.addressLock.Unlock() + + for _, address := range addresses { + target := address + endpoint + + req, err := http.NewRequest(method, target, params) + if err != nil { + // Probably should not try and continue if we're unable + // to create the request. return nil, err } - - if strings.Contains(err.Error(), "connection refused") { - return nil, ErrConnectionRefused + req.Header.Set("User-Agent", c.userAgent) + if doOptions.data != nil { + req.Header.Set("Content-Type", "application/json") + } else if method == "POST" { + req.Header.Set("Content-Type", "plain/text") + } + if c.username != "" && c.secret != "" { + req.SetBasicAuth(c.username, c.secret) } - return nil, chooseError(ctx, err) - } - if resp.StatusCode < 200 || resp.StatusCode >= 400 { - return nil, newError(resp) - } - return resp, nil -} -// if error in context, return that instead of generic http error -func chooseError(ctx context.Context, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - return err + for k, v := range doOptions.headers { + req.Header.Set(k, v) + } + + resp, err := httpClient.Do(req.WithContext(ctx)) + if err != nil { + // If it is a custom error, return it. It probably knows more than us + if serror.IsStorageOSError(err) { + switch serror.ErrorKind(err) { + case serror.APIUncontactable: + // If API isn't contactable we should try the next address + failedAddresses[address] = struct{}{} + continue + case serror.InvalidHostConfig: + // If invalid host or unknown error, we should report back + fallthrough + case serror.UnknownError: + return nil, err + } + } + + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + if _, ok := err.(net.Error); ok { + // Be optimistic and try the next endpoint + failedAddresses[address] = struct{}{} + continue + } + return nil, err + } + } + + // If we get to the point of response, we should move any failed + // addresses to the back. + failed := len(failedAddresses) + if failed > 0 { + // Copy addresses we think are okay into the head of the list + newOrder := make([]string, 0, len(addresses)-failed) + + for _, addr := range addresses { + if _, exists := failedAddresses[addr]; !exists { + newOrder = append(newOrder, addr) + } + } + for addr := range failedAddresses { + newOrder = append(newOrder, addr) + } + + c.addressLock.Lock() + // Bring in the new order + c.addresses = newOrder + c.addressLock.Unlock() + } + + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + return nil, newError(resp) // These status codes are likely to be fatal + } + return resp, nil } + + return nil, netutil.ErrAllFailed(addresses) } func (c *Client) getAPIPath(path string, query url.Values, unversioned bool) string { - // 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 = strings.TrimLeft(path, "/") - var apiPath string - - path = strings.TrimLeft(path, "/") - if unversioned { - apiPath = fmt.Sprintf("%s/%s", urlStr, path) + if !unversioned { + apiPath = fmt.Sprintf("/%s/%s", c.requestedAPIVersion, apiPath) } else { - apiPath = fmt.Sprintf("%s/%s/%s", urlStr, c.requestedAPIVersion, path) + apiPath = fmt.Sprintf("/%s", apiPath) } if len(query) > 0 { @@ -441,23 +528,14 @@ func (e *Error) Error() string { 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(d Dialer) *http.Transport { - transport := defaultPooledTransport(d) - transport.DisableKeepAlives = true - transport.MaxIdleConnsPerHost = -1 - return transport -} - // defaultPooledTransport returns a new http.Transport with similar default // values to http.DefaultTransport. Do not use this for transient transports as // it can leak file descriptors over time. Only use this for transports that // will be re-used for the same host(s). -func defaultPooledTransport(d Dialer) *http.Transport { +func defaultPooledTransport(dialer Dialer) *http.Transport { transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, - Dial: d.Dial, + Dial: dialer.Dial, TLSHandshakeTimeout: 5 * time.Second, DisableKeepAlives: false, MaxIdleConnsPerHost: 1, @@ -469,15 +547,13 @@ func defaultPooledTransport(d Dialer) *http.Transport { // http.Client, but with a non-shared Transport, idle connections disabled, and // keepalives disabled. // If a custom dialer is not provided, one with sane defaults will be created. -func defaultClient(d Dialer) *http.Client { - if d == nil { - d = &net.Dialer{ - Timeout: 5 * time.Second, - KeepAlive: 5 * time.Second, - } +func defaultClient() *http.Client { + dialer := &net.Dialer{ + Timeout: 5 * time.Second, + KeepAlive: 5 * time.Second, } return &http.Client{ - Transport: defaultTransport(d), + Transport: defaultPooledTransport(dialer), } } diff --git a/vendor/github.com/storageos/go-api/controller.go b/vendor/github.com/storageos/go-api/controller.go deleted file mode 100644 index 1b4f9ea128..0000000000 --- a/vendor/github.com/storageos/go-api/controller.go +++ /dev/null @@ -1,110 +0,0 @@ -package storageos - -import ( - "encoding/json" - "errors" - "net/http" - "net/url" - - "github.com/storageos/go-api/types" -) - -var ( - - // ControllerAPIPrefix is a partial path to the HTTP endpoint. - ControllerAPIPrefix = "controllers" - - // ErrNoSuchController is the error returned when the controller does not exist. - ErrNoSuchController = errors.New("no such controller") - - // ErrControllerInUse is the error returned when the controller requested to be removed is still in use. - ErrControllerInUse = errors.New("controller in use and cannot be removed") -) - -// ControllerList returns the list of available controllers. -func (c *Client) ControllerList(opts types.ListOptions) ([]*types.Controller, error) { - listOpts := doOptions{ - fieldSelector: opts.FieldSelector, - labelSelector: opts.LabelSelector, - context: opts.Context, - } - - if opts.LabelSelector != "" { - query := url.Values{} - query.Add("labelSelector", opts.LabelSelector) - listOpts.values = query - } - - resp, err := c.do("GET", ControllerAPIPrefix, listOpts) - if err != nil { - return nil, err - } - defer resp.Body.Close() - var controllers []*types.Controller - if err := json.NewDecoder(resp.Body).Decode(&controllers); err != nil { - return nil, err - } - return controllers, nil -} - -// Controller returns a controller by its reference. -func (c *Client) Controller(ref string) (*types.Controller, error) { - - resp, err := c.do("GET", ControllerAPIPrefix+"/"+ref, doOptions{}) - if err != nil { - if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { - return nil, ErrNoSuchController - } - return nil, err - } - defer resp.Body.Close() - var controller types.Controller - if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil { - return nil, err - } - return &controller, nil -} - -// ControllerUpdate updates a controller on the server. -func (c *Client) ControllerUpdate(opts types.ControllerUpdateOptions) (*types.Controller, error) { - ref := opts.Name - if IsUUID(opts.ID) { - ref = opts.ID - } - resp, err := c.do("PUT", ControllerAPIPrefix+"/"+ref, doOptions{ - data: opts, - context: opts.Context, - }) - if err != nil { - return nil, err - } - defer resp.Body.Close() - var controller types.Controller - if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil { - return nil, err - } - return &controller, nil -} - -// ControllerDelete removes a controller by its reference. -func (c *Client) ControllerDelete(opts types.DeleteOptions) error { - deleteOpts := doOptions{ - namespace: opts.Namespace, - force: opts.Force, - context: opts.Context, - } - resp, err := c.do("DELETE", ControllerAPIPrefix+"/"+opts.Name, deleteOpts) - if err != nil { - if e, ok := err.(*Error); ok { - if e.Status == http.StatusNotFound { - return ErrNoSuchController - } - if e.Status == http.StatusConflict { - return ErrControllerInUse - } - } - return err - } - defer resp.Body.Close() - return nil -} diff --git a/vendor/github.com/storageos/go-api/event.go b/vendor/github.com/storageos/go-api/event.go deleted file mode 100644 index c9d9f00224..0000000000 --- a/vendor/github.com/storageos/go-api/event.go +++ /dev/null @@ -1,189 +0,0 @@ -package storageos - -import ( - "context" - "encoding/json" - "errors" - "log" - "net/http" - "time" - - "github.com/gorilla/websocket" - - "github.com/storageos/go-api/types" -) - -var ( - - // EventAPIPrefix is a partial path to the HTTP endpoint. - EventAPIPrefix = "event" - - // ErrNoSuchEvent is the error returned when the event does not exist. - ErrNoSuchEvent = errors.New("no such event") -) - -// EventList returns the list of available events. -func (c *Client) EventList(opts types.ListOptions) ([]*types.Event, error) { - listOpts := doOptions{ - fieldSelector: opts.FieldSelector, - labelSelector: opts.LabelSelector, - context: opts.Context, - } - resp, err := c.do("GET", EventAPIPrefix, listOpts) - if err != nil { - return nil, err - } - defer resp.Body.Close() - var events []*types.Event - if err := json.NewDecoder(resp.Body).Decode(&events); err != nil { - return nil, err - } - return events, nil -} - -// Events returns a stream of events in the daemon. It's up to the caller to close the stream -// by cancelling the context. Once the stream has been completely read an io.EOF error will -// be sent over the error channel. If an error is sent all processing will be stopped. It's up -// to the caller to reopen the stream in the event of an error by reinvoking this method. -func (c *Client) Events(ctx context.Context, opts types.ListOptions) (<-chan types.Request, <-chan error) { - - // listOpts := doOptions{ - // fieldSelector: opts.FieldSelector, - // labelSelector: opts.LabelSelector, - // context: ctx, - // } - - messages := make(chan types.Request) - errs := make(chan error, 1) - - // started := make(chan struct{}) - ws, _, err := websocket.DefaultDialer.Dial("ws://10.245.103.2:8000/v1/ws/event", nil) - if err != nil { - // close(started) - // errs <- err - log.Fatal(err) - } - // defer ws.Close() - - done := make(chan struct{}) - go func() { - defer ws.Close() - defer close(done) - for { - _, message, err := ws.ReadMessage() - if err != nil { - log.Println("read:", err) - errs <- err - return - } - // log.Printf("recv: %s", message) - var request types.Request - if err := json.Unmarshal(message, &request); err != nil { - log.Printf("decode error: %s", message) - errs <- err - return - } - messages <- request - } - }() - - ticker := time.NewTicker(time.Second) - defer ticker.Stop() - - go func() { - for { - select { - case t := <-ticker.C: - log.Printf("tick: %s\n", t.String()) - err := ws.WriteMessage(websocket.TextMessage, []byte(t.String())) - if err != nil { - log.Println("write:", err) - return - } - case <-ctx.Done(): - log.Println("done") - err := ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - if err != nil { - log.Println("write close:", err) - return - } - errs <- ctx.Err() - select { - case <-done: - case <-time.After(time.Second): - } - ws.Close() - return - } - } - }() - - // go func() { - // defer ws.Close() - // defer close(errs) - // - // // query, err := buildEventsQueryParams(cli.version, options) - // // if err != nil { - // // close(started) - // // errs <- err - // // return - // // } - // - // // resp, err := cli.get(ctx, "/events", query, nil) - // - // // decoder := json.NewDecoder(resp.Body) - // - // close(started) - // for { - // select { - // case <-ctx.Done(): - // log.Println("done") - // errs <- ctx.Err() - // return - // default: - // log.Println("default") - // _, message, err := ws.ReadMessage() - // if err != nil { - // log.Println("read:", err) - // return - // } - // log.Printf("recv: %s", message) - // var event types.Event - // if err := json.Unmarshal(message, &event); err != nil { - // log.Printf("decode error: %s", message) - // errs <- err - // return - // } - // log.Printf("sent: %v", event) - // messages <- event - // - // // select { - // // case messages <- event: - // // case <-ctx.Done(): - // // errs <- ctx.Err() - // // return - // // } - // } - // } - // }() - // <-started - log.Println("returning") - return messages, errs -} - -// Event returns a event by its reference. -func (c *Client) Event(ref string) (*types.Event, error) { - resp, err := c.do("GET", EventAPIPrefix+"/"+ref, doOptions{}) - if err != nil { - if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { - return nil, ErrNoSuchEvent - } - return nil, err - } - defer resp.Body.Close() - var event types.Event - if err := json.NewDecoder(resp.Body).Decode(&event); err != nil { - return nil, err - } - return &event, nil -} diff --git a/vendor/github.com/storageos/go-api/health.go b/vendor/github.com/storageos/go-api/health.go index f4a37807d5..7b44100074 100644 --- a/vendor/github.com/storageos/go-api/health.go +++ b/vendor/github.com/storageos/go-api/health.go @@ -23,12 +23,14 @@ func (c *Client) CPHealth(ctx context.Context, hostname string) (*types.CPHealth return nil, err } - req.Header.Set("User-Agent", userAgent) + req.Header.Set("User-Agent", c.userAgent) if c.username != "" && c.secret != "" { req.SetBasicAuth(c.username, c.secret) } - resp, err := c.HTTPClient.Do(req.WithContext(ctx)) + c.configLock.RLock() + resp, err := c.httpClient.Do(req.WithContext(ctx)) + c.configLock.RUnlock() if err != nil { return nil, err } @@ -51,12 +53,14 @@ func (c *Client) DPHealth(ctx context.Context, hostname string) (*types.DPHealth return nil, err } - req.Header.Set("User-Agent", userAgent) + req.Header.Set("User-Agent", c.userAgent) if c.username != "" && c.secret != "" { req.SetBasicAuth(c.username, c.secret) } - resp, err := c.HTTPClient.Do(req.WithContext(ctx)) + c.configLock.RLock() + resp, err := c.httpClient.Do(req.WithContext(ctx)) + c.configLock.RUnlock() if err != nil { return nil, err } diff --git a/vendor/github.com/storageos/go-api/login.go b/vendor/github.com/storageos/go-api/login.go index 31b0a471f9..371a31ec75 100644 --- a/vendor/github.com/storageos/go-api/login.go +++ b/vendor/github.com/storageos/go-api/login.go @@ -8,7 +8,8 @@ import ( var ( // LoginAPIPrefix is a partial path to the HTTP endpoint. LoginAPIPrefix = "auth/login" - ErrLoginFailed = errors.New("Failed to get token from API endpoint") + // ErrLoginFailed is the error returned on an unsuccessful login. + ErrLoginFailed = errors.New("failed to get token from API endpoint") ) // Login attemps to get a token from the API diff --git a/vendor/github.com/storageos/go-api/netutil/BUILD b/vendor/github.com/storageos/go-api/netutil/BUILD index 4389448cd3..3fec107c64 100644 --- a/vendor/github.com/storageos/go-api/netutil/BUILD +++ b/vendor/github.com/storageos/go-api/netutil/BUILD @@ -4,7 +4,7 @@ go_library( name = "go_default_library", srcs = [ "errors.go", - "multidialer.go", + "netutil.go", "parsers.go", ], importmap = "k8s.io/kubernetes/vendor/github.com/storageos/go-api/netutil", diff --git a/vendor/github.com/storageos/go-api/netutil/errors.go b/vendor/github.com/storageos/go-api/netutil/errors.go index 245001e72c..f61980d08c 100644 --- a/vendor/github.com/storageos/go-api/netutil/errors.go +++ b/vendor/github.com/storageos/go-api/netutil/errors.go @@ -3,11 +3,14 @@ package netutil import ( "errors" "fmt" - "github.com/storageos/go-api/serror" "strings" + + "github.com/storageos/go-api/serror" ) -func errAllFailed(addrs []string) error { +// ErrAllFailed produces a typed StorageOS error which should be used to indicate that +// the API is not contactable for all of the supplied node addresses. +func ErrAllFailed(addrs []string) error { msg := fmt.Sprintf("failed to dial all known cluster members, (%s)", strings.Join(addrs, ",")) help := "ensure that the value of $STORAGEOS_HOST (or the -H flag) is correct, and that there are healthy StorageOS nodes in this cluster" @@ -21,6 +24,8 @@ func newInvalidNodeError(err error) error { 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") +var ( + errUnsupportedScheme = errors.New("unsupported URL scheme") + errInvalidHostName = errors.New("invalid hostname") + errInvalidPortNumber = errors.New("invalid port number") +) diff --git a/vendor/github.com/storageos/go-api/netutil/multidialer.go b/vendor/github.com/storageos/go-api/netutil/multidialer.go deleted file mode 100644 index 30a8226b13..0000000000 --- a/vendor/github.com/storageos/go-api/netutil/multidialer.go +++ /dev/null @@ -1,109 +0,0 @@ -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) -} diff --git a/vendor/github.com/storageos/go-api/netutil/netutil.go b/vendor/github.com/storageos/go-api/netutil/netutil.go new file mode 100644 index 0000000000..7f9c550c46 --- /dev/null +++ b/vendor/github.com/storageos/go-api/netutil/netutil.go @@ -0,0 +1,5 @@ +// Package netutil provides network related errors and helper functions. +package netutil + +// DefaultDialPort is the default port which the API is contacted on. +const DefaultDialPort = "5705" diff --git a/vendor/github.com/storageos/go-api/netutil/parsers.go b/vendor/github.com/storageos/go-api/netutil/parsers.go index a8a8701ffa..cb297e8348 100644 --- a/vendor/github.com/storageos/go-api/netutil/parsers.go +++ b/vendor/github.com/storageos/go-api/netutil/parsers.go @@ -1,52 +1,72 @@ 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 +const ( + httpScheme = "http" + httpsScheme = "https" + tcpScheme = "tcp" +) + +// AddressesFromNodes takes a list of node hosts and attempts to return a list of hosts in host:port // format along with any error encountered. // // The function accepts node hosts in URL, ip, ip:port, resolvable-name and resolvable-name:port -// formats and will append the default port value if needed. -func addrsFromNodes(nodes []string) ([]string, error) { - var addrs []string +// formats and will append the default port value if needed. For hosts where the scheme has been omitted, +// the scheme for the first host will be used. If the first host has no scheme, it will default to http. +func AddressesFromNodes(nodes []string) ([]string, error) { + var addresses []string - 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) + var scheme string + + for _, node := range nodes { + address := node + // If no scheme present, set the first scheme + if !strings.Contains(address, "://") { + if scheme == "" { + scheme = httpScheme } - - 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...) + address = strings.Join([]string{scheme, address}, "://") } + + url, err := url.Parse(address) + if err != nil { + return nil, newInvalidNodeError(err) + } + + switch url.Scheme { + case tcpScheme: + url.Scheme = httpScheme + fallthrough + case httpScheme, httpsScheme: + if scheme == "" { + scheme = url.Scheme + } + default: + return nil, newInvalidNodeError(errUnsupportedScheme) + } + + host := url.Hostname() + if host == "" { + return nil, newInvalidNodeError(errInvalidHostName) + } + + port := url.Port() + if port == "" { + port = DefaultDialPort + } + if !validPort(port) { + return nil, newInvalidNodeError(errInvalidPortNumber) + } + + addresses = append(addresses, strings.TrimRight(url.String(), "/")) } - return addrs, nil + return addresses, nil } func validPort(port string) bool { @@ -56,87 +76,3 @@ func validPort(port string) bool { (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) -} diff --git a/vendor/github.com/storageos/go-api/network_diagnostics.go b/vendor/github.com/storageos/go-api/network_diagnostics.go new file mode 100644 index 0000000000..5d8a0bcc8f --- /dev/null +++ b/vendor/github.com/storageos/go-api/network_diagnostics.go @@ -0,0 +1,35 @@ +package storageos + +import ( + "encoding/json" + "net/http" + "path" + + "github.com/storageos/go-api/types" +) + +var ( + // NetworkDiagnosticsAPIPrefix is a partial path to the HTTP endpoint for + // the node connectivity diagnostics report. + NetworkDiagnosticsAPIPrefix = "diagnostics/network" +) + +// NetworkDiagnostics returns a collection of network connectivity reports. If +// a reference to a node is given, it will only check connectivity from that +// node. Otherwise, connectivity between all cluster nodes will be returned. +func (c *Client) NetworkDiagnostics(ref string) (types.ConnectivityResults, error) { + resp, err := c.do("GET", path.Join(NetworkDiagnosticsAPIPrefix, ref), doOptions{}) + if err != nil { + if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { + return nil, ErrNoSuchNode + } + return nil, err + } + defer resp.Body.Close() + + var results types.ConnectivityResults + if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { + return nil, err + } + return results, nil +} diff --git a/vendor/github.com/storageos/go-api/node.go b/vendor/github.com/storageos/go-api/node.go new file mode 100644 index 0000000000..4188f582a4 --- /dev/null +++ b/vendor/github.com/storageos/go-api/node.go @@ -0,0 +1,110 @@ +package storageos + +import ( + "encoding/json" + "errors" + "net/http" + "net/url" + + "github.com/storageos/go-api/types" +) + +var ( + + // NodeAPIPrefix is a partial path to the HTTP endpoint. + NodeAPIPrefix = "nodes" + + // ErrNoSuchNode is the error returned when the node does not exist. + ErrNoSuchNode = errors.New("no such node") + + // ErrNodeInUse is the error returned when the node requested to be removed is still in use. + ErrNodeInUse = errors.New("node in use and cannot be removed") +) + +// NodeList returns the list of available nodes. +func (c *Client) NodeList(opts types.ListOptions) ([]*types.Node, error) { + listOpts := doOptions{ + fieldSelector: opts.FieldSelector, + labelSelector: opts.LabelSelector, + context: opts.Context, + } + + if opts.LabelSelector != "" { + query := url.Values{} + query.Add("labelSelector", opts.LabelSelector) + listOpts.values = query + } + + resp, err := c.do("GET", NodeAPIPrefix, listOpts) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var nodes []*types.Node + if err := json.NewDecoder(resp.Body).Decode(&nodes); err != nil { + return nil, err + } + return nodes, nil +} + +// Node returns a node by its reference. +func (c *Client) Node(ref string) (*types.Node, error) { + + resp, err := c.do("GET", NodeAPIPrefix+"/"+ref, doOptions{}) + if err != nil { + if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { + return nil, ErrNoSuchNode + } + return nil, err + } + defer resp.Body.Close() + var node types.Node + if err := json.NewDecoder(resp.Body).Decode(&node); err != nil { + return nil, err + } + return &node, nil +} + +// NodeUpdate updates a node on the server. +func (c *Client) NodeUpdate(opts types.NodeUpdateOptions) (*types.Node, error) { + ref := opts.Name + if IsUUID(opts.ID) { + ref = opts.ID + } + resp, err := c.do("PUT", NodeAPIPrefix+"/"+ref, doOptions{ + data: opts, + context: opts.Context, + }) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var node types.Node + if err := json.NewDecoder(resp.Body).Decode(&node); err != nil { + return nil, err + } + return &node, nil +} + +// NodeDelete removes a node by its reference. +func (c *Client) NodeDelete(opts types.DeleteOptions) error { + deleteOpts := doOptions{ + namespace: opts.Namespace, + force: opts.Force, + context: opts.Context, + } + resp, err := c.do("DELETE", NodeAPIPrefix+"/"+opts.Name, deleteOpts) + if err != nil { + if e, ok := err.(*Error); ok { + if e.Status == http.StatusNotFound { + return ErrNoSuchNode + } + if e.Status == http.StatusConflict { + return ErrNodeInUse + } + } + return err + } + defer resp.Body.Close() + return nil +} diff --git a/vendor/github.com/storageos/go-api/policy.go b/vendor/github.com/storageos/go-api/policy.go index e2539c5b11..9d2f6604fd 100644 --- a/vendor/github.com/storageos/go-api/policy.go +++ b/vendor/github.com/storageos/go-api/policy.go @@ -5,16 +5,15 @@ import ( "encoding/json" "errors" "fmt" - "github.com/storageos/go-api/types" "net/http" "net/url" + + "github.com/storageos/go-api/types" ) var ( - // PolicyAPIPrefix is a partial path to the HTTP endpoint. PolicyAPIPrefix = "policies" - // ErrNoSuchPolicy is the error returned when the policy does not exist. ErrNoSuchPolicy = errors.New("no such policy") ) diff --git a/vendor/github.com/storageos/go-api/pool.go b/vendor/github.com/storageos/go-api/pool.go index c30adc0c99..c4629d74f6 100644 --- a/vendor/github.com/storageos/go-api/pool.go +++ b/vendor/github.com/storageos/go-api/pool.go @@ -41,7 +41,7 @@ func (c *Client) PoolList(opts types.ListOptions) ([]*types.Pool, error) { } // PoolCreate creates a pool on the server and returns the new object. -func (c *Client) PoolCreate(opts types.PoolCreateOptions) (*types.Pool, error) { +func (c *Client) PoolCreate(opts types.PoolOptions) (*types.Pool, error) { resp, err := c.do("POST", PoolAPIPrefix, doOptions{ data: opts, context: opts.Context, @@ -56,6 +56,27 @@ func (c *Client) PoolCreate(opts types.PoolCreateOptions) (*types.Pool, error) { return &pool, nil } +// PoolUpdate - update pool +func (c *Client) PoolUpdate(opts types.PoolOptions) (*types.Pool, error) { + ref := opts.Name + if IsUUID(opts.ID) { + ref = opts.ID + } + + resp, err := c.do("PUT", PoolAPIPrefix+"/"+ref, doOptions{ + data: opts, + context: opts.Context, + }) + if err != nil { + return nil, err + } + var pool types.Pool + if err := json.NewDecoder(resp.Body).Decode(&pool); err != nil { + return nil, err + } + return &pool, nil +} + // Pool returns a pool by its reference. func (c *Client) Pool(ref string) (*types.Pool, error) { resp, err := c.do("GET", PoolAPIPrefix+"/"+ref, doOptions{}) diff --git a/vendor/github.com/storageos/go-api/rule.go b/vendor/github.com/storageos/go-api/rule.go index a187e2569c..fc92d15022 100644 --- a/vendor/github.com/storageos/go-api/rule.go +++ b/vendor/github.com/storageos/go-api/rule.go @@ -3,7 +3,6 @@ package storageos import ( "encoding/json" "errors" - "fmt" "net/http" "net/url" @@ -98,7 +97,7 @@ func (c *Client) RuleUpdate(opts types.RuleUpdateOptions) (*types.Rule, error) { if IsUUID(opts.ID) { ref = opts.ID } - fmt.Printf("%#v\n", opts) + path, err := namespacedRefPath(opts.Namespace, RuleAPIPrefix, ref) if err != nil { return nil, err diff --git a/vendor/github.com/storageos/go-api/testing_notes.txt b/vendor/github.com/storageos/go-api/testing_notes.txt deleted file mode 100644 index 8014a11426..0000000000 --- a/vendor/github.com/storageos/go-api/testing_notes.txt +++ /dev/null @@ -1,769 +0,0 @@ -docs.storageos.com - API webpage -================================ - -TODO: Openind paragraph of text has multiple typos and grammar errors. Need to fix this. [DONE] -TODO: hard to see the text for the example JSON post messages on the RHS. Possibly a Safari thing?! -TODO: some of the JSON examples on the RHS of the webpage look wrong?! Safari issue? -TODO: docs should use all lowercase for consistency? - -VOLUMES -======= - -List volumes -============ - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - no provisioned volumes. -- No auth username and password supplied. ---> -GET: 172.28.128.3:5705/v1/namespaces/default/volumes -<-- -401 UNAUTHORIZED -Unauthorized --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - no provisioned volumes. -- bad namespace used. ---> -GET: 172.28.128.3:5705/v1/namespaces/defaultxxx/volumes -<-- -404 NOT FOUND -JSON: "message": "Not found" -Unauthorized --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - no provisioned volumes. -- Dropped the {namespace} path parameter. ---> -GET: 172.28.128.3:5705/v1/namespaces/volumes -<-- -404 NOT FOUND -Not found --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - no provisioned volumes. -- One volume has already been created successfully. ---> -GET: 172.28.128.3:5705/v1/namespaces/volumes -<-- -JSON: valid volume/inode state data (note: this time the master data is correctly set i.e. has non-zero data) --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster - single provisioned volumes. ---> -GET: 172.28.128.3:5705/v1/namespaces/volumes -<-- -JSON: valid volume/inode state data. --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster - multiple provisioned volumes. ---> -GET: 172.28.128.3:5705/v1/namespaces/volumes -<-- -JSON: an array of valid volume/inode state data for multiple volumes. --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster - multiple provisioned volumes. -- labelSelector ---> -GET: 172.28.128.3:5705/v1/namespaces/volumes?labelSelector=com.example.some-label -<-- -JSON: an array of valid volume/inode state data for multiple volumes. --FAILED- doesn't appear to filter. -XXX: - -=============================================================================== - -SCENARIO: -- 3 node cluster - multiple provisioned volumes. -- fieldSelector ---> -GET: 172.28.128.3:5705/v1/namespaces/volumes?fieldSelector= -<-- -JSON: an array of valid volume/inode state data for multiple volumes. --UNTESTED- don't know what to put for fieldSelector's value?? -XXX: - -=============================================================================== - - -Create volumes -============== - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - no provisioned volumes. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "name": "redis-vol01", - "size": 1 -} -<-- -JSON: valid volume/inode state data (note: master data is all zerod at this point) --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - with a single volume already provisioned. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "name": "redis-vol02", - "size": 2 -} -<-- -JSON: valid volume/inode state data (note: master data is all zerod at this point) --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - with two volumes already provisioned. -- Now trying to provision a third with bad JSON body -- using CAPITAL first letters for Name and Size. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "Name": "redis-vol03", - "Size": 3 -} -<-- -JSON: valid volume/inode state data (note: master data is all zerod at this point) --EXPECTED- WORKS?! This implies that the JSON keys are non-case sensitive. - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - with two volumes already provisioned. -- Now trying to provision a third with bad JSON body -- using all CAPITALS for NAME and SIZE. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "NAME": "redis-vol03", - "SIZE": 3 -} -<-- -JSON: valid volume/inode state data (note: master data is all zerod at this point) --EXPECTED- WORKS?! This implies that the JSON keys are non-case sensitive. - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - with two volumes already provisioned. -- Now trying to provision a volume with bad JSON body -- missing size parameter. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "name": "redis-vol05", -} -<-- -JSON: valid volume/inode state data (note: master data is all zerod at this point) --EXPECTED- Size defaults to 10 -TODO: update documentation to reflect this. [DONE] - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - with two volumes already provisioned. -- Now trying to provision a volume with bad JSON body -- with size (0) parameter. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "name": "redis-vol05", - "size" 0 -} -<-- -JSON: valid volume/inode state data (note: master data is all zerod at this point) --EXPECTED- Size defaults to 10 -TODO: update documentation to reflect this. [DONE] - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - with two volumes already provisioned. -- Now trying to provision a volume with bad JSON body -- empty JSON object. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ -} -<-- -volume name not valid --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - with two volumes already provisioned. -- Now trying to provision a volume with no JSON body -- empty ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -<-- -JSON: "message": "EOF" --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster. -- Now trying to provision a volume with no JSON name field ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "size": 2 -} -<-- -volume name not valid --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster. -- Now trying to provision a volume with no JSON name field ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "size": 2 -} -<-- -volume name not valid --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster. -- Now trying to provision a volume with same name as one that has already been provisioned. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "name": "myvol1", - "size": 5 -} -<-- -volume with name 'myvol1' already exists --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster. -- Now trying to provision a volume with pool name. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "name": "myvol1", - "size": 5, - "pool": "mypool1" -} -<-- -JSON: valid volume/inode state data with correct pool field. --EXPECTED- - -=============================================================================== - -SCENARIO: -- 3 node cluster. -- Now trying to provision a volume with optional labels. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes -BODY (JSON): -{ - "Name": "vol01", - "Size": 5, - "Labels": { - "com.example.some-label": "some-value", - "com.example.some-other-label": "some-other-value" - } -} -<-- -JSON: valid volume/inode state data with correct labels. --EXPECTED- - -=============================================================================== - - -Get volumes -=========== - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Get by name. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03 -<-- -JSON: correct volume/inode state data. --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Get by id. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes/270c1fc2-c578-77f8-2d7c-1515e626b6c3 -<-- -JSON: correct volume/inode state data. --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Passing bad name/id. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes/this-volume-does-not-exist -<-- -Not Found --EXPECTED- - -=============================================================================== - - -Update volumes -============== - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Update a volume ---> -PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03 -JSON: -{ - "Description": "string", - "Size": 5, - "Labels": { - "property1": "string", - "property2": "string" - } -} -<-- -200 OK --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Update a volume, trying with bad JSON, missing opening curly brace! ---> -PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03 -JSON: - "Description": "string", - "Size": 5, - "Labels": { - "property1": "string", - "property2": "string" - } -} -<-- -400 BAD REQUEST -Request decode failed: json: cannot unmarshal string into Go value of type types.Volume --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Update a volume, trying with size (0) and same property1 and new property3. ---> -PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol03 -JSON: -{ - "Description": "string", - "Size": 0, - "Labels": { - "property1": "string", - "property3": "string3" - } -} -<-- -200 OK --NOT EXPECTED- -The old labels are completely overwritten anew (hence the previous property2 label is not present). I assume this is the desired behaviour?! -TODO: However size is now zero?! Check this is ok! Probably not; since the Create volume API defaults to 10 when 0 is passed or omitted. -XXX: - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Update a volume, trying with omitting size parameter ---> -PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz -JSON: -{ - "Description": "string", - "Labels": { - "property1": "string", - "property3": "string3" - } -} -<-- -200 OK --NOT EXPECTED- -XXX: size is now zero when not passing size in JSON body of PUT request. - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Update a volume, trying with omitting description parameter ---> -PUT: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz -JSON: -{ - "Labels": { - "property1": "string", - "property3": "string3" - } -} -<-- -200 OK --NOT EXPECTED- -XXX: description string is empty i.e. "" when not passing description in JSON body of PUT request. -The above implies that the update volume PUT request receiving side interprets missing update parameters as their null-value counterparts. So it's is not possible to update just specific parameters. - -=============================================================================== - - -Delete volumes -============== - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Delete a volume. But not specifying a name in the path. ---> -DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes -<-- -404 NOT FOUND -404 page not found --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Delete a volume by specifying the volume's name. ---> -DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol05 -<-- -200 OK --EXPECTED- -TODO: But when doing a GET ~volumes/redis-vol05 it is still present So DELETE volumes doesn't appear to be working. - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Delete a volume by specifying the volume's id. ---> -DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/5233930b-b77f-2863-0895-b1eb5d73ec45 -<-- -200 OK --EXPECTED- -TODO: But when doing a GET ~volumes/5233930b-b77f-2863-0895-b1eb5d73ec45 it is still present So DELETE volumes doesn't appear to be working. - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Trying to delete a mounted volume ---> -DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz -<-- -412 PRECONDITION FAILED -cannot delete mounted volume --EXPECTED- -TODO: seems correct, this hints that the mount is working which is in conflict with my observation below for MOUNT and UNMOUNT. Q. Is it checking the mount status via the OS, or by some other means i.e. locally cached value? - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Trying to delete a unmounted volume ---> -DELETE: 172.28.128.3:5705/v1/namespaces/default/volumes/volxyz -<-- -200 OK --EXPECTED- - -=============================================================================== - - -Mount volumes -============= - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Mount a volume. But not specifying a name in the path. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol02/mount -JSON: -{ - "client": "storageos-1-68228" -} -<-- -200 OK --EXPECTED- -NOTE: in the JSON response, no_of_mounts does increase correctly by one each time. And mounted is set to true correctly. -TODO: although this worked, the documentation doesn't give the proper JSON request body. -TODO: no_of_mounts is still 0 in /var/lib/storageos/state/inode/178101 (should increase by 1 for every mount.) -BUG^ -TODO: Also not sure if really mounted this volume, since running the storageos cli e.g.: -$ ./storageos volume ls -The MOUNTED BY column is always empty ?? Either cli doesn't show this info, yet. Or the volume isn't mounted. -=============================================================================== - - -Unmount volumes -=============== - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Mount a volume. But not specifying a name in the path. ---> -POST: 172.28.128.3:5705/v1/namespaces/default/volumes/redis-vol02/unmount -JSON: -{ - "can-be-anything": "storageos-1-68228" -} -<-- -200 OK --EXPECTED- -NOTE: in the JSON response, mounted is set back to false correctly. -TODO: although this worked, the documentation doesn't give the proper JSON request body. The - -=============================================================================== - - -POOLS -===== - -List pools -========== - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster. -- No auth username and password supplied. ---> -GET: 172.28.128.3:5705/v1/pools -<-- -401 UNAUTHORIZED -Unauthorized --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster. No pools. ---> -GET: 172.28.128.3:5705/v1/pools -<-- -200 OK -JSON: an array of pools. --EXPECTED- - -=============================================================================== - - -Create pools -============ - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster. ---> -POST: 172.28.128.3:5705/v1/volumes -BODY (JSON): -{ - "name": "mypool1" -} -<-- -201 CREATED -JSON: valid pool data. --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster. -- Trying to create a pool with the same name as another already created. ---> -POST: 172.28.128.3:5705/v1/volumes -BODY (JSON): -{ - "name": "mypool1" -} -<-- -409 CONFLICT -Pool with name 'mypool1' already exists --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster. -- Trying to create a pool with the same name as another already created. ---> -POST: 172.28.128.3:5705/v1/volumes -BODY (JSON): -{ - "name": "mypool2", - "description": "hello world!" -} -<-- -201 CREATED -JSON: valid pool data. --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster. -- Trying to create a pool with the defaultDriver string set. ---> -POST: 172.28.128.3:5705/v1/volumes -BODY (JSON): -{ - "name": "mypool6", - "description": "hello world again!", - "default": true, - "defautDriver": "I'm the default driver :)" -} -<-- -201 CREATED -JSON: Is correct for the most part, but defaultDriver is an empty string?? --NOT EXPECTED- -XXX - -=============================================================================== - - -Get Pools -========= - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Get by name. ---> -POST: 172.28.128.3:5705/v1/pools/mypool1 -<-- -200 OK -JSON: correct pool data. --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Get by id. ---> -POST: 172.28.128.3:5705/v1/pools/ea477d68-8193-1179-d889-aa6ea8797082 -<-- -200 OK -JSON: correct pool data. --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned volumes. -- Get by name. Try passing invalid name. ---> -POST: 172.28.128.3:5705/v1/pools/mypool1xxx -<-- -404 NOT FOUND -Not Found --EXPECTED- - -=============================================================================== - - -Delete Pools -============ - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned pools. -- Trying to delete without specifying name etc. in the path. ---> -POST: 172.28.128.3:5705/v1/pools -<-- -404 NOT FOUND -404 page not found --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned pools. -- Delete by name. ---> -POST: 172.28.128.3:5705/v1/pools/mypool2 -<-- -200 OK --EXPECTED- - -=============================================================================== - -SCENARIO: -- Fresh start - 3 node cluster - N provisioned pools. -- Delete by id. ---> -POST: 172.28.128.3:5705/v1/pools/9a10bbfe-eaaa-af3c-2a9b-d78e0790efb4 -<-- -200 OK --EXPECTED- - -=============================================================================== diff --git a/vendor/github.com/storageos/go-api/types/BUILD b/vendor/github.com/storageos/go-api/types/BUILD index d1a976b89c..02b1afff3f 100644 --- a/vendor/github.com/storageos/go-api/types/BUILD +++ b/vendor/github.com/storageos/go-api/types/BUILD @@ -5,21 +5,22 @@ go_library( srcs = [ "auth.go", "capacity_stats.go", - "controller.go", - "controller_update_options.go", + "connectivity.go", "delete_options.go", "deployment.go", "driver_instance.go", "error_response.go", "events.go", + "health.go", "list_options.go", "logger.go", "namespace.go", "node.go", + "node_update_options.go", "operator.go", "policy.go", "pool.go", - "pool_create_options.go", + "pool_options.go", "rule.go", "template.go", "template_create_options.go", @@ -27,6 +28,7 @@ go_library( "version.go", "volume.go", "volume_create_options.go", + "volume_stats.go", "volume_update_options.go", ], importmap = "k8s.io/kubernetes/vendor/github.com/storageos/go-api/types", diff --git a/vendor/github.com/storageos/go-api/types/connectivity.go b/vendor/github.com/storageos/go-api/types/connectivity.go new file mode 100644 index 0000000000..8d3177d1ac --- /dev/null +++ b/vendor/github.com/storageos/go-api/types/connectivity.go @@ -0,0 +1,41 @@ +package types + +import "time" + +// ConnectivityResult capture's a node connectivity report to a given target. +type ConnectivityResult struct { + // Label is a human-readable reference for the service being tested. + Label string `json:"label"` + + // Address is the host:port of the service being tested. + Address string `json:"address"` + + // Source is a human-readable reference for the source host where the tests + // were run from. + Source string `json:"source"` + + // LatencyNS is the duration in nanoseconds that the check took to complete. + // Will also be set on unsuccessful attempts. + LatencyNS time.Duration `json:"latency_ns"` + + // Error is set if the test returned an error. + Error string `json:"error"` +} + +// IsOK returns true iff no error +func (r ConnectivityResult) IsOK() bool { + return len(r.Error) == 0 +} + +// ConnectivityResults is a collection of connectivty reports. +type ConnectivityResults []ConnectivityResult + +// IsOK returns true iff no error in any result. +func (r ConnectivityResults) IsOK() bool { + for _, result := range r { + if !result.IsOK() { + return false + } + } + return true +} diff --git a/vendor/github.com/storageos/go-api/types/controller.go b/vendor/github.com/storageos/go-api/types/controller.go deleted file mode 100644 index e32df76907..0000000000 --- a/vendor/github.com/storageos/go-api/types/controller.go +++ /dev/null @@ -1,78 +0,0 @@ -package types - -// Versions and Prefixes used in API and KV URLs -import "time" - -const ( - ControllerAPIPrefix = "controller" - ControllerDefaultPort = "3260" - ControllerScanAPIPrefix = "config/scan" -) - -// ControllerCurrent - current controller -var ControllerCurrent = "" - -// Controller status phases -const ( - ControllerStatusPending = "pending" - ControllerStatusEvaluating = "evaluating" - ControllerStatusDeploying = "deploying" - ControllerStatusActive = "active" - ControllerStatusFailed = "failed" - ControllerStatusDeleting = "deleting" - - ControllerHealthStarting = "starting" - ControllerHealthOK = "healthy" - ControllerHealthDegraded = "degraded" - ControllerHealthOffline = "offline" -) - -// Errors for controller related things -const ( - ErrControllerHostIDAllocation string = "error, could not allocate hostid" - ErrControllerIDNotSet = "error, controller ID not set" - ErrControllerNotFound = "controller not found" -) - -// Controller is used to represent a storage node in a cluster -type Controller struct { - ID string `json:"id,omitempty"` - HostID uint16 `json:"hostID"` - Scheduler bool `json:"scheduler"` - Name string `json:"name"` - Address string `json:"address"` - DeviceDir string `json:"deviceDir"` - APIPort int `json:"apiPort"` - NatsPort int `json:"natsPort"` - NatsClusterPort int `json:"natsClusterPort"` - SerfPort int `json:"serfPort"` - DFSPort int `json:"dfsPort"` - Description string `json:"description"` - ControllerGroups []string `json:"controllerGroups"` - Tags []string `json:"tags"` - Labels map[string]string `json:"labels"` - VolumeStats VolumeStats `json:"volumeStats"` - PoolStats map[string]DriverStats `json:"poolStats"` - - // health is updated by the - Health string `json:"health"` - HealthUpdatedAt time.Time `json:"healthUpdatedAt"` - VersionInfo map[string]VersionInfo `json:"versionInfo"` - Version string `json:"version"` - - // 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"` -} - -// DriverStats is used to report stats for all drivers in a pool. -type DriverStats map[string]CapacityStats - -// VolumeStats - volume stats (volume counts, looking forward to capacity) -type VolumeStats struct { - MasterVolumeCount int `json:"masterVolumeCount"` - ReplicaVolumeCount int `json:"replicaVolumeCount"` - VirtualVolumeCount int `json:"virtualVolumeCount"` -} diff --git a/vendor/github.com/storageos/go-api/types/deployment.go b/vendor/github.com/storageos/go-api/types/deployment.go index 63f63f2a4e..cdc33e4076 100644 --- a/vendor/github.com/storageos/go-api/types/deployment.go +++ b/vendor/github.com/storageos/go-api/types/deployment.go @@ -14,13 +14,13 @@ type Deployment struct { // Read Only: true Inode uint32 `json:"inode"` - // Controller ID + // Node ID // Read Only: true - Controller string `json:"controller"` + Node string `json:"node"` - // Controller name + // Node name // Read Only: true - ControllerName string `json:"controllerName"` + NodeName string `json:"nodeName"` // Health // Read Only: true diff --git a/vendor/github.com/storageos/go-api/types/health.go b/vendor/github.com/storageos/go-api/types/health.go new file mode 100644 index 0000000000..d6c6d8eb6c --- /dev/null +++ b/vendor/github.com/storageos/go-api/types/health.go @@ -0,0 +1,134 @@ +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 +} + +// HealthStatus is the health status json object. +type HealthStatus struct { + Submodules HealthSubmodules `json:"submodules"` +} + +// HealthSubmodules is the "submodules" attribuet of HealthStatus. +type HealthSubmodules struct { + KV SubModuleStatus `json:"kv,omitempty"` + KVWrite SubModuleStatus `json:"kv_write,omitempty"` + NATS SubModuleStatus `json:"nats,omitempty"` + Scheduler SubModuleStatus `json:"scheduler,omitempty"` + DirectFSClient SubModuleStatus `json:"directfs_initiator,omitempty"` + DirectFSServer SubModuleStatus `json:"directfs_responder,omitempty"` + Director SubModuleStatus `json:"director,omitempty"` + FSDriver SubModuleStatus `json:"rdb,omitempty"` + FS SubModuleStatus `json:"presentation,omitempty"` +} + +// ToCPHealthStatus returns only CPHealthStatus from the HealthStatus. +func (h *HealthStatus) ToCPHealthStatus() *CPHealthStatus { + return &CPHealthStatus{ + KV: h.Submodules.KV, + KVWrite: h.Submodules.KVWrite, + NATS: h.Submodules.KVWrite, + Scheduler: h.Submodules.Scheduler, + } +} + +// ToDPHealthStatus returns only DPHealthStatus from the HealthStatus. +func (h *HealthStatus) ToDPHealthStatus() *DPHealthStatus { + return &DPHealthStatus{ + DirectFSClient: h.Submodules.DirectFSClient, + DirectFSServer: h.Submodules.DirectFSServer, + Director: h.Submodules.Director, + FSDriver: h.Submodules.FSDriver, + FS: h.Submodules.FS, + } +} diff --git a/vendor/github.com/storageos/go-api/types/node.go b/vendor/github.com/storageos/go-api/types/node.go index 52f5da63d3..6d60d86110 100644 --- a/vendor/github.com/storageos/go-api/types/node.go +++ b/vendor/github.com/storageos/go-api/types/node.go @@ -1,97 +1,104 @@ package types import ( - "encoding/json" + "time" ) -type SubModuleStatus struct { - Status string `json:"status"` - UpdatedAt string `json:"updatedAt"` - ChangedAt string `json:"changedAt"` - Message string `json:"message"` +// Node represents a StorageOS cluster node. +type Node struct { + NodeConfig + + HostID uint32 `json:"hostID"` + Name string `json:"name"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + Health string `json:"health"` + HealthUpdatedAt time.Time `json:"healthUpdatedAt"` + + VersionInfo map[string]VersionInfo `json:"versionInfo"` + Version string `json:"version"` + Revision string // the GitCommit this maps to + + Scheduler bool `json:"scheduler"` + + Cordon bool `json:"cordon"` + Drain bool `json:"drain"` + + VolumeStats VolumeStats `json:"volumeStats"` + + // PoolStats map[string]map[string]CapacityStats `json:"poolStats"` + + CapacityStats CapacityStats `json:"capacityStats"` } -type NamedSubModuleStatus struct { - Name string - SubModuleStatus +// NodeConfig is a read-only representation of the node's configuration, set at +// start time by environment variables passed to the container or using defaults. +type NodeConfig struct { + // UUID is the unique identifier of the node. It cannot be changed once set. + ID string `json:"id,omitempty"` + + // Hostname of the node. + Hostname string `json:"hostname"` + + // Address is is used for communication between nodes. + // Nodes will fail to start if the address they first registered with + // changes. This protects against the container being re-scheduled on a + // different host. Nodes will typically use the host server's ip address, + // running the docker container in -net host mode. + Address string `json:"address"` + + // KvAddr is the address of the KV store to use for storing configuration. + // It can include the address or FQDN with optional port. Defaults to + // Address/ADVERTISE_IP. + KvAddr string `json:"kvAddr"` + + // Port allocations + APIPort int `json:"apiPort"` + NatsPort int `json:"natsPort"` + NatsClusterPort int `json:"natsClusterPort"` + SerfPort int `json:"serfPort"` + DFSPort int `json:"dfsPort"` + KVPeerPort int `json:"kvPeerPort"` + KVClientPort int `json:"kvClientPort"` + + Labels map[string]string `json:"labels"` + + LogLevel string `json:"logLevel"` // the level of the logs to outout + LogFormat string `json:"logFormat"` // either text or json + LogFilter string `json:"logFilter"` // used to discard messages based on the message's category + + // BindAddr is used to control the default address StorageOS binds to. This + // should always be set to 0.0.0.0 (all interfaces). + BindAddr string `json:"bindAddr"` + + // DeviceDir is where the volumes are exported. This directory must be + // shared into the container using the rshared volume mount option. + DeviceDir string `json:"deviceDir"` + + // Join existing cluster + Join string `json:"join"` + + // Backend selects the KV backend, either embedded (testing only) or etcd. + Backend string `json:"kvBackend"` + + // EnableDebug is used to enable various debugging features. Used by http + // to enable debug endpoints and as a shortcut to enable debug logging. + EnableDebug bool `json:"debug"` + + // Devices specify all devices that are available on the node. + Devices []Device `json:"devices"` } -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 +// Device - device type +type Device struct { + ID string + Labels map[string]string `json:"labels"` + Status string `json:"status"` + Identifier string `json:"identifier"` + Class string `json:"class"` + CapacityStats CapacityStats `json:"capacityStats"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } diff --git a/vendor/github.com/storageos/go-api/types/controller_update_options.go b/vendor/github.com/storageos/go-api/types/node_update_options.go similarity index 54% rename from vendor/github.com/storageos/go-api/types/controller_update_options.go rename to vendor/github.com/storageos/go-api/types/node_update_options.go index a55b8160d3..f902567541 100644 --- a/vendor/github.com/storageos/go-api/types/controller_update_options.go +++ b/vendor/github.com/storageos/go-api/types/node_update_options.go @@ -2,25 +2,26 @@ package types import "context" -// ControllerUpdateOptions are available parameters for updating existing controllers. -type ControllerUpdateOptions struct { +// NodeUpdateOptions are available parameters for updating existing nodes. +type NodeUpdateOptions struct { - // Controller unique ID. + // Node unique ID. // Read Only: true ID string `json:"id"` - // Controller name. + // Node name. // Read Only: true Name string `json:"name"` - // Description of the controller. + // Description of the node. Description string `json:"description"` // 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"` + // Cordon marks the node as unschedulable if true + Cordon bool `json:"cordon"` + Drain bool `json:"drain"` // Context can be set with a timeout or can be used to cancel a request. Context context.Context `json:"-"` diff --git a/vendor/github.com/storageos/go-api/types/pool.go b/vendor/github.com/storageos/go-api/types/pool.go index eaca5c1bcb..9487d80551 100644 --- a/vendor/github.com/storageos/go-api/types/pool.go +++ b/vendor/github.com/storageos/go-api/types/pool.go @@ -18,35 +18,19 @@ type Pool struct { // provisioned without a pool specified. There can only be one default pool. Default bool `json:"default"` - // DefaultDriver specifies the storage driver to use by default if there are - // multiple drivers in the pool and no driver was specified in the - // provisioning request or assigned by rules. If no driver was specified and - // no default set, driver weight is used to determine the default. - DefaultDriver string `json:"defaultDriver"` + NodeSelector string `json:"nodeSelector"` - // ControllerNames is a list of controller names that are participating in the - // storage pool. - ControllerNames []string `json:"controllerNames"` + // DeviceSelector - specifies a selector to filter node devices based on their labels. + // Only devices from nodes that are in the 'NodeNames' list can be selected + DeviceSelector string `json:"deviceSelector"` - // DriverNames is a list of backend storage drivers that are available in the - // storage pool. - DriverNames []string `json:"driverNames"` - - // DriverInstances is used to track instances of each driver. Drivers have a - // default configuration, which can then be customised for each pool where - // they are used, which is representated as a DriverInstance. - // Read Only: true - DriverInstances []*DriverInstance `json:"driverInstances"` - - // Flag describing whether the template is active. - // Default: false - Active bool `json:"active"` - - // CapacityStats are used to track aggregate capacity usage information across - // all controllers and driver instances. - // Read Only: true + // Populated by the system. Read-only. CapacityStats CapacityStats `json:"capacityStats"` + // This field is computed based on NodeSelector value + // Populated by the system. Read-only. + Nodes []*Node `json:"nodes"` + // Labels define a list of labels that describe the pool. Labels map[string]string `json:"labels"` } diff --git a/vendor/github.com/storageos/go-api/types/pool_create_options.go b/vendor/github.com/storageos/go-api/types/pool_create_options.go deleted file mode 100644 index 006eab61b2..0000000000 --- a/vendor/github.com/storageos/go-api/types/pool_create_options.go +++ /dev/null @@ -1,42 +0,0 @@ -package types - -import "context" - -// PoolCreateOptions are available parameters for creating new pools. -type PoolCreateOptions struct { - - // Pool name. - // Required: true - Name string `json:"name"` - - // Pool description. - Description string `json:"description"` - - // Default determines whether this pool is the default if a volume is - // provisioned without a pool specified. There can only be one default pool. - Default bool `json:"default"` - - // DefaultDriver specifies the storage driver to use by default if there are - // multiple drivers in the pool and no driver was specified in the - // provisioning request or assigned by rules. If no driver was specified and - // no default set, driver weight is used to determine the default. - DefaultDriver string `json:"defaultDriver"` - - // ControllerNames is a list of controller names that are participating in the - // storage pool. - ControllerNames []string `json:"controllerNames"` - - // DriverNames is a list of backend storage drivers that are available in the - // storage pool. - DriverNames []string `json:"driverNames"` - - // Flag describing whether the template is active. - // Default: false - Active bool `json:"active"` - - // Labels define a list of labels that describe the pool. - Labels map[string]string `json:"labels"` - - // Context can be set with a timeout or can be used to cancel a request. - Context context.Context `json:"-"` -} diff --git a/vendor/github.com/storageos/go-api/types/pool_options.go b/vendor/github.com/storageos/go-api/types/pool_options.go new file mode 100644 index 0000000000..dd08a98c0b --- /dev/null +++ b/vendor/github.com/storageos/go-api/types/pool_options.go @@ -0,0 +1,28 @@ +package types + +import "context" + +// PoolOptions are available parameters for creating or updating pools. +type PoolOptions struct { + ID string `json:"id"` + Name string `json:"name"` + + // Pool description. + Description string `json:"description"` + + // Default determines whether this pool is the default if a volume is + // provisioned without a pool specified. There can only be one default pool. + Default bool `json:"default"` + + NodeSelector string `json:"nodeSelector"` + + // DeviceSelector - specifies a selector to filter node devices based on their labels. + // Only devices from nodes that are in the 'NodeNames' list can be selected + DeviceSelector string `json:"deviceSelector"` + + // Labels define a list of labels that describe the pool. + Labels map[string]string `json:"labels"` + + // Context can be set with a timeout or can be used to cancel a request. + Context context.Context `json:"-"` +} diff --git a/vendor/github.com/storageos/go-api/types/volume_stats.go b/vendor/github.com/storageos/go-api/types/volume_stats.go new file mode 100644 index 0000000000..cda4de047f --- /dev/null +++ b/vendor/github.com/storageos/go-api/types/volume_stats.go @@ -0,0 +1,8 @@ +package types + +// VolumeStats - volume stats (volume counts, looking forward to capacity) +type VolumeStats struct { + MasterVolumeCount int `json:"masterVolumeCount"` + ReplicaVolumeCount int `json:"replicaVolumeCount"` + VirtualVolumeCount int `json:"virtualVolumeCount"` +} diff --git a/vendor/github.com/storageos/go-api/validation.go b/vendor/github.com/storageos/go-api/validation.go index 85b3179224..f58063044a 100644 --- a/vendor/github.com/storageos/go-api/validation.go +++ b/vendor/github.com/storageos/go-api/validation.go @@ -22,7 +22,9 @@ var ( // collection of restricted characters. NamePattern = regexp.MustCompile(`^` + NameFormat + `$`) - ErrNoRef = errors.New("no ref provided or incorrect format") + // ErrNoRef is given when the reference given is invalid. + ErrNoRef = errors.New("no ref provided or incorrect format") + // ErrNoNamespace is given when the namespace given is invalid. ErrNoNamespace = errors.New("no namespace provided or incorrect format") ) From 835183ad5075249c4017f49cf60ae08d7c05687e Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 14 Sep 2018 20:15:27 +0530 Subject: [PATCH 2/2] Add AttachDevice() to attach the device to a host AttachDevice() ensures that the volume device is attached to the host before they are used. --- pkg/volume/storageos/storageos.go | 13 ++++++-- pkg/volume/storageos/storageos_test.go | 23 +++++++++---- pkg/volume/storageos/storageos_util.go | 36 ++++++++++++--------- pkg/volume/storageos/storageos_util_test.go | 4 +-- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/pkg/volume/storageos/storageos.go b/pkg/volume/storageos/storageos.go index 745aefb41e..fc859ce154 100644 --- a/pkg/volume/storageos/storageos.go +++ b/pkg/volume/storageos/storageos.go @@ -281,6 +281,8 @@ type storageosManager interface { CreateVolume(provisioner *storageosProvisioner) (*storageosVolume, error) // Attaches the disk to the kubelet's host machine. AttachVolume(mounter *storageosMounter) (string, error) + // Attaches the device to the host at a mount path. + AttachDevice(mounter *storageosMounter, deviceMountPath string) error // Detaches the disk from the kubelet's host machine. DetachVolume(unmounter *storageosUnmounter, dir string) error // Mounts the disk on the Kubelet's host machine. @@ -351,6 +353,14 @@ func (b *storageosMounter) SetUp(fsGroup *int64) error { b.volNamespace = b.podNamespace } + targetPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName) + + // Attach the device to the host. + if err := b.manager.AttachDevice(b, targetPath); err != nil { + klog.Errorf("Failed to attach device at %s: %s", targetPath, err.Error()) + return err + } + // Attach the StorageOS volume as a block device devicePath, err := b.manager.AttachVolume(b) if err != nil { @@ -359,8 +369,7 @@ func (b *storageosMounter) SetUp(fsGroup *int64) error { } // Mount the loop device into the plugin's disk global mount dir. - globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.podNamespace, b.volName) - err = b.manager.MountVolume(b, devicePath, globalPDPath) + err = b.manager.MountVolume(b, devicePath, targetPath) if err != nil { return err } diff --git a/pkg/volume/storageos/storageos_test.go b/pkg/volume/storageos/storageos_test.go index a5732fff7a..2b448bc916 100644 --- a/pkg/volume/storageos/storageos_test.go +++ b/pkg/volume/storageos/storageos_test.go @@ -75,13 +75,14 @@ func TestGetAccessModes(t *testing.T) { } type fakePDManager struct { - api apiImplementer - attachCalled bool - detachCalled bool - mountCalled bool - unmountCalled bool - createCalled bool - deleteCalled bool + api apiImplementer + attachCalled bool + attachDeviceCalled bool + detachCalled bool + mountCalled bool + unmountCalled bool + createCalled bool + deleteCalled bool } func (fake *fakePDManager) NewAPI(apiCfg *storageosAPIConfig) error { @@ -108,6 +109,11 @@ func (fake *fakePDManager) AttachVolume(b *storageosMounter) (string, error) { return "", nil } +func (fake *fakePDManager) AttachDevice(b *storageosMounter, dir string) error { + fake.attachDeviceCalled = true + return nil +} + func (fake *fakePDManager) DetachVolume(b *storageosUnmounter, loopDevice string) error { fake.detachCalled = true return nil @@ -213,6 +219,9 @@ func TestPlugin(t *testing.T) { } } + if !fakeManager.attachDeviceCalled { + t.Errorf("AttachDevice not called") + } if !fakeManager.attachCalled { t.Errorf("Attach not called") } diff --git a/pkg/volume/storageos/storageos_util.go b/pkg/volume/storageos/storageos_util.go index 0f5ebb80f8..eb669186f6 100644 --- a/pkg/volume/storageos/storageos_util.go +++ b/pkg/volume/storageos/storageos_util.go @@ -203,6 +203,25 @@ func (u *storageosUtil) DetachVolume(b *storageosUnmounter, devicePath string) e return removeLoopDevice(devicePath, b.exec) } +// AttachDevice attaches the volume device to the host at a given mount path. +func (u *storageosUtil) AttachDevice(b *storageosMounter, deviceMountPath string) error { + if err := u.NewAPI(b.apiCfg); err != nil { + return err + } + + opts := storageostypes.VolumeMountOptions{ + Name: b.volName, + Namespace: b.volNamespace, + FsType: b.fsType, + Mountpoint: deviceMountPath, + Client: b.plugin.host.GetHostName(), + } + if err := u.api.VolumeMount(opts); err != nil { + return err + } + return nil +} + // Mount mounts the volume on the host. func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountPath string) error { notMnt, err := b.mounter.IsLikelyNotMountPoint(deviceMountPath) @@ -231,22 +250,7 @@ func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountP return err } } - if err != nil { - return err - } - - if err := u.NewAPI(b.apiCfg); err != nil { - return err - } - - opts := storageostypes.VolumeMountOptions{ - Name: b.volName, - Namespace: b.volNamespace, - FsType: b.fsType, - Mountpoint: deviceMountPath, - Client: b.plugin.host.GetHostName(), - } - return u.api.VolumeMount(opts) + return err } // Unmount removes the mount reference from the volume allowing it to be diff --git a/pkg/volume/storageos/storageos_util_test.go b/pkg/volume/storageos/storageos_util_test.go index e4800cbe51..b3dabba28a 100644 --- a/pkg/volume/storageos/storageos_util_test.go +++ b/pkg/volume/storageos/storageos_util_test.go @@ -108,8 +108,8 @@ func (f fakeAPI) VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error { func (f fakeAPI) VolumeDelete(opts storageostypes.DeleteOptions) error { return nil } -func (f fakeAPI) Controller(ref string) (*storageostypes.Controller, error) { - return &storageostypes.Controller{}, nil +func (f fakeAPI) Node(ref string) (*storageostypes.Node, error) { + return &storageostypes.Node{}, nil } func TestCreateVolume(t *testing.T) {