Unverified Commit 23a08506 authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #69782 from darkowlzz/storageos-updates

StorageOS attach device before volume attach
parents 78573805 835183ad
...@@ -3158,23 +3158,23 @@ ...@@ -3158,23 +3158,23 @@
}, },
{ {
"ImportPath": "github.com/storageos/go-api", "ImportPath": "github.com/storageos/go-api",
"Comment": "0.3.4", "Comment": "1.0.0-rc1-2-g343b3eff91fcc8",
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e"
}, },
{ {
"ImportPath": "github.com/storageos/go-api/netutil", "ImportPath": "github.com/storageos/go-api/netutil",
"Comment": "0.3.4", "Comment": "1.0.0-rc1-2-g343b3eff91fcc8",
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e"
}, },
{ {
"ImportPath": "github.com/storageos/go-api/serror", "ImportPath": "github.com/storageos/go-api/serror",
"Comment": "0.3.4", "Comment": "1.0.0-rc1-2-g343b3eff91fcc8",
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e"
}, },
{ {
"ImportPath": "github.com/storageos/go-api/types", "ImportPath": "github.com/storageos/go-api/types",
"Comment": "0.3.4", "Comment": "1.0.0-rc1-2-g343b3eff91fcc8",
"Rev": "3a4032328d99c1b43fbda3d85bd3c80fa06e1707" "Rev": "343b3eff91fcc84b0165e252eb843f5fd720fa4e"
}, },
{ {
"ImportPath": "github.com/stretchr/objx", "ImportPath": "github.com/stretchr/objx",
......
...@@ -91176,7 +91176,7 @@ SOFTWARE. ...@@ -91176,7 +91176,7 @@ SOFTWARE.
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
...@@ -91220,7 +91220,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ...@@ -91220,7 +91220,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 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. 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
================================================================================ ================================================================================
...@@ -91229,7 +91229,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -91229,7 +91229,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
...@@ -91273,7 +91273,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ...@@ -91273,7 +91273,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 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. 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
================================================================================ ================================================================================
...@@ -91282,7 +91282,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -91282,7 +91282,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
...@@ -91326,7 +91326,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ...@@ -91326,7 +91326,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 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. 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
================================================================================ ================================================================================
...@@ -91335,7 +91335,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -91335,7 +91335,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
...@@ -91379,7 +91379,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ...@@ -91379,7 +91379,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 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. 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
================================================================================ ================================================================================
...@@ -281,6 +281,8 @@ type storageosManager interface { ...@@ -281,6 +281,8 @@ type storageosManager interface {
CreateVolume(provisioner *storageosProvisioner) (*storageosVolume, error) CreateVolume(provisioner *storageosProvisioner) (*storageosVolume, error)
// Attaches the disk to the kubelet's host machine. // Attaches the disk to the kubelet's host machine.
AttachVolume(mounter *storageosMounter) (string, error) 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. // Detaches the disk from the kubelet's host machine.
DetachVolume(unmounter *storageosUnmounter, dir string) error DetachVolume(unmounter *storageosUnmounter, dir string) error
// Mounts the disk on the Kubelet's host machine. // Mounts the disk on the Kubelet's host machine.
...@@ -351,6 +353,14 @@ func (b *storageosMounter) SetUp(fsGroup *int64) error { ...@@ -351,6 +353,14 @@ func (b *storageosMounter) SetUp(fsGroup *int64) error {
b.volNamespace = b.podNamespace b.volNamespace = b.podNamespace
} }
targetPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
// Attach the device to the host.
if err := b.manager.AttachDevice(b, targetPath); err != nil {
klog.Errorf("Failed to attach device at %s: %s", targetPath, err.Error())
return err
}
// Attach the StorageOS volume as a block device // Attach the StorageOS volume as a block device
devicePath, err := b.manager.AttachVolume(b) devicePath, err := b.manager.AttachVolume(b)
if err != nil { if err != nil {
...@@ -359,8 +369,7 @@ func (b *storageosMounter) SetUp(fsGroup *int64) error { ...@@ -359,8 +369,7 @@ func (b *storageosMounter) SetUp(fsGroup *int64) error {
} }
// Mount the loop device into the plugin's disk global mount dir. // 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, targetPath)
err = b.manager.MountVolume(b, devicePath, globalPDPath)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -75,13 +75,14 @@ func TestGetAccessModes(t *testing.T) { ...@@ -75,13 +75,14 @@ func TestGetAccessModes(t *testing.T) {
} }
type fakePDManager struct { type fakePDManager struct {
api apiImplementer api apiImplementer
attachCalled bool attachCalled bool
detachCalled bool attachDeviceCalled bool
mountCalled bool detachCalled bool
unmountCalled bool mountCalled bool
createCalled bool unmountCalled bool
deleteCalled bool createCalled bool
deleteCalled bool
} }
func (fake *fakePDManager) NewAPI(apiCfg *storageosAPIConfig) error { func (fake *fakePDManager) NewAPI(apiCfg *storageosAPIConfig) error {
...@@ -108,6 +109,11 @@ func (fake *fakePDManager) AttachVolume(b *storageosMounter) (string, error) { ...@@ -108,6 +109,11 @@ func (fake *fakePDManager) AttachVolume(b *storageosMounter) (string, error) {
return "", nil 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 { func (fake *fakePDManager) DetachVolume(b *storageosUnmounter, loopDevice string) error {
fake.detachCalled = true fake.detachCalled = true
return nil return nil
...@@ -213,6 +219,9 @@ func TestPlugin(t *testing.T) { ...@@ -213,6 +219,9 @@ func TestPlugin(t *testing.T) {
} }
} }
if !fakeManager.attachDeviceCalled {
t.Errorf("AttachDevice not called")
}
if !fakeManager.attachCalled { if !fakeManager.attachCalled {
t.Errorf("Attach not called") t.Errorf("Attach not called")
} }
......
...@@ -69,7 +69,7 @@ type apiImplementer interface { ...@@ -69,7 +69,7 @@ type apiImplementer interface {
VolumeMount(opts storageostypes.VolumeMountOptions) error VolumeMount(opts storageostypes.VolumeMountOptions) error
VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error
VolumeDelete(opt storageostypes.DeleteOptions) 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. // storageosUtil is the utility structure to interact with the StorageOS API.
...@@ -203,6 +203,25 @@ func (u *storageosUtil) DetachVolume(b *storageosUnmounter, devicePath string) e ...@@ -203,6 +203,25 @@ func (u *storageosUtil) DetachVolume(b *storageosUnmounter, devicePath string) e
return removeLoopDevice(devicePath, b.exec) 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. // Mount mounts the volume on the host.
func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountPath string) error { func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountPath string) error {
notMnt, err := b.mounter.IsLikelyNotMountPoint(deviceMountPath) notMnt, err := b.mounter.IsLikelyNotMountPoint(deviceMountPath)
...@@ -231,22 +250,7 @@ func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountP ...@@ -231,22 +250,7 @@ func (u *storageosUtil) MountVolume(b *storageosMounter, mntDevice, deviceMountP
return err return err
} }
} }
if err != nil { return err
return err
}
if err := u.NewAPI(b.apiCfg); err != nil {
return err
}
opts := storageostypes.VolumeMountOptions{
Name: b.volName,
Namespace: b.volNamespace,
FsType: b.fsType,
Mountpoint: deviceMountPath,
Client: b.plugin.host.GetHostName(),
}
return u.api.VolumeMount(opts)
} }
// Unmount removes the mount reference from the volume allowing it to be // Unmount removes the mount reference from the volume allowing it to be
...@@ -289,7 +293,7 @@ func (u *storageosUtil) DeleteVolume(d *storageosDeleter) error { ...@@ -289,7 +293,7 @@ func (u *storageosUtil) DeleteVolume(d *storageosDeleter) error {
// specified. // specified.
func (u *storageosUtil) DeviceDir(b *storageosMounter) string { 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 { if err != nil {
klog.Warningf("node device path lookup failed: %v", err) klog.Warningf("node device path lookup failed: %v", err)
return defaultDeviceDir return defaultDeviceDir
......
...@@ -108,8 +108,8 @@ func (f fakeAPI) VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error { ...@@ -108,8 +108,8 @@ func (f fakeAPI) VolumeUnmount(opts storageostypes.VolumeUnmountOptions) error {
func (f fakeAPI) VolumeDelete(opts storageostypes.DeleteOptions) error { func (f fakeAPI) VolumeDelete(opts storageostypes.DeleteOptions) error {
return nil return nil
} }
func (f fakeAPI) Controller(ref string) (*storageostypes.Controller, error) { func (f fakeAPI) Node(ref string) (*storageostypes.Node, error) {
return &storageostypes.Controller{}, nil return &storageostypes.Node{}, nil
} }
func TestCreateVolume(t *testing.T) { func TestCreateVolume(t *testing.T) {
......
...@@ -4,12 +4,12 @@ go_library( ...@@ -4,12 +4,12 @@ go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"client.go", "client.go",
"controller.go",
"event.go",
"health.go", "health.go",
"logger.go", "logger.go",
"login.go", "login.go",
"namespace.go", "namespace.go",
"network_diagnostics.go",
"node.go",
"policy.go", "policy.go",
"pool.go", "pool.go",
"rule.go", "rule.go",
...@@ -24,7 +24,6 @@ go_library( ...@@ -24,7 +24,6 @@ go_library(
importpath = "github.com/storageos/go-api", importpath = "github.com/storageos/go-api",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ 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/netutil:go_default_library",
"//vendor/github.com/storageos/go-api/serror:go_default_library", "//vendor/github.com/storageos/go-api/serror:go_default_library",
"//vendor/github.com/storageos/go-api/types:go_default_library", "//vendor/github.com/storageos/go-api/types:go_default_library",
......
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
......
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
}
...@@ -23,12 +23,14 @@ func (c *Client) CPHealth(ctx context.Context, hostname string) (*types.CPHealth ...@@ -23,12 +23,14 @@ func (c *Client) CPHealth(ctx context.Context, hostname string) (*types.CPHealth
return nil, err return nil, err
} }
req.Header.Set("User-Agent", userAgent) req.Header.Set("User-Agent", c.userAgent)
if c.username != "" && c.secret != "" { if c.username != "" && c.secret != "" {
req.SetBasicAuth(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 { if err != nil {
return nil, err return nil, err
} }
...@@ -51,12 +53,14 @@ func (c *Client) DPHealth(ctx context.Context, hostname string) (*types.DPHealth ...@@ -51,12 +53,14 @@ func (c *Client) DPHealth(ctx context.Context, hostname string) (*types.DPHealth
return nil, err return nil, err
} }
req.Header.Set("User-Agent", userAgent) req.Header.Set("User-Agent", c.userAgent)
if c.username != "" && c.secret != "" { if c.username != "" && c.secret != "" {
req.SetBasicAuth(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 { if err != nil {
return nil, err return nil, err
} }
......
...@@ -8,7 +8,8 @@ import ( ...@@ -8,7 +8,8 @@ import (
var ( var (
// LoginAPIPrefix is a partial path to the HTTP endpoint. // LoginAPIPrefix is a partial path to the HTTP endpoint.
LoginAPIPrefix = "auth/login" 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 // Login attemps to get a token from the API
......
...@@ -4,7 +4,7 @@ go_library( ...@@ -4,7 +4,7 @@ go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"errors.go", "errors.go",
"multidialer.go", "netutil.go",
"parsers.go", "parsers.go",
], ],
importmap = "k8s.io/kubernetes/vendor/github.com/storageos/go-api/netutil", importmap = "k8s.io/kubernetes/vendor/github.com/storageos/go-api/netutil",
......
...@@ -3,11 +3,14 @@ package netutil ...@@ -3,11 +3,14 @@ package netutil
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/storageos/go-api/serror"
"strings" "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, ",")) 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" 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 { ...@@ -21,6 +24,8 @@ func newInvalidNodeError(err error) error {
return serror.NewTypedStorageOSError(serror.InvalidHostConfig, err, msg, help) return serror.NewTypedStorageOSError(serror.InvalidHostConfig, err, msg, help)
} }
var errNoAddresses = errors.New("the MultiDialer instance has not been initialised with client addresses") var (
var errUnsupportedScheme = errors.New("unsupported URL scheme") errUnsupportedScheme = errors.New("unsupported URL scheme")
var errInvalidPortNumber = errors.New("invalid port number") errInvalidHostName = errors.New("invalid hostname")
errInvalidPortNumber = errors.New("invalid port number")
)
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)
}
// 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"
package netutil package netutil
import ( import (
"net"
"net/url" "net/url"
"strconv" "strconv"
"strings" "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. // format along with any error encountered.
// //
// The function accepts node hosts in URL, ip, ip:port, resolvable-name and resolvable-name:port // 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. // formats and will append the default port value if needed. For hosts where the scheme has been omitted,
func addrsFromNodes(nodes []string) ([]string, error) { // the scheme for the first host will be used. If the first host has no scheme, it will default to http.
var addrs []string func AddressesFromNodes(nodes []string) ([]string, error) {
var addresses []string
for _, n := range nodes {
switch { var scheme string
// Assume that the node is provided as a URL
case strings.Contains(n, "://"): for _, node := range nodes {
newAddrs, err := parseURL(n) address := node
if err != nil { // If no scheme present, set the first scheme
return nil, newInvalidNodeError(err) if !strings.Contains(address, "://") {
if scheme == "" {
scheme = httpScheme
} }
address = strings.Join([]string{scheme, address}, "://")
}
addrs = append(addrs, newAddrs...) url, err := url.Parse(address)
if err != nil {
return nil, newInvalidNodeError(err)
}
// Assume the node is in hostname:port or ip:port format switch url.Scheme {
case strings.Contains(n, ":"): case tcpScheme:
newAddrs, err := parseHostPort(n) url.Scheme = httpScheme
if err != nil { fallthrough
return nil, newInvalidNodeError(err) case httpScheme, httpsScheme:
if scheme == "" {
scheme = url.Scheme
} }
addrs = append(addrs, newAddrs...)
// Assume hostname or ip
default: default:
newAddrs, err := parseHost(n) return nil, newInvalidNodeError(errUnsupportedScheme)
if err != nil { }
return nil, newInvalidNodeError(err)
} host := url.Hostname()
if host == "" {
return nil, newInvalidNodeError(errInvalidHostName)
}
addrs = append(addrs, newAddrs...) 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 { func validPort(port string) bool {
...@@ -56,87 +76,3 @@ func validPort(port string) bool { ...@@ -56,87 +76,3 @@ func validPort(port string) bool {
(intPort > 0) && (intPort > 0) &&
(intPort <= 65535) (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)
}
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
}
...@@ -11,18 +11,18 @@ import ( ...@@ -11,18 +11,18 @@ import (
var ( var (
// ControllerAPIPrefix is a partial path to the HTTP endpoint. // NodeAPIPrefix is a partial path to the HTTP endpoint.
ControllerAPIPrefix = "controllers" NodeAPIPrefix = "nodes"
// ErrNoSuchController is the error returned when the controller does not exist. // ErrNoSuchNode is the error returned when the node does not exist.
ErrNoSuchController = errors.New("no such controller") ErrNoSuchNode = errors.New("no such node")
// ErrControllerInUse is the error returned when the controller requested to be removed is still in use. // ErrNodeInUse is the error returned when the node requested to be removed is still in use.
ErrControllerInUse = errors.New("controller in use and cannot be removed") ErrNodeInUse = errors.New("node in use and cannot be removed")
) )
// ControllerList returns the list of available controllers. // NodeList returns the list of available nodes.
func (c *Client) ControllerList(opts types.ListOptions) ([]*types.Controller, error) { func (c *Client) NodeList(opts types.ListOptions) ([]*types.Node, error) {
listOpts := doOptions{ listOpts := doOptions{
fieldSelector: opts.FieldSelector, fieldSelector: opts.FieldSelector,
labelSelector: opts.LabelSelector, labelSelector: opts.LabelSelector,
...@@ -35,43 +35,43 @@ func (c *Client) ControllerList(opts types.ListOptions) ([]*types.Controller, er ...@@ -35,43 +35,43 @@ func (c *Client) ControllerList(opts types.ListOptions) ([]*types.Controller, er
listOpts.values = query listOpts.values = query
} }
resp, err := c.do("GET", ControllerAPIPrefix, listOpts) resp, err := c.do("GET", NodeAPIPrefix, listOpts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var controllers []*types.Controller var nodes []*types.Node
if err := json.NewDecoder(resp.Body).Decode(&controllers); err != nil { if err := json.NewDecoder(resp.Body).Decode(&nodes); err != nil {
return nil, err return nil, err
} }
return controllers, nil return nodes, nil
} }
// Controller returns a controller by its reference. // Node returns a node by its reference.
func (c *Client) Controller(ref string) (*types.Controller, error) { func (c *Client) Node(ref string) (*types.Node, error) {
resp, err := c.do("GET", ControllerAPIPrefix+"/"+ref, doOptions{}) resp, err := c.do("GET", NodeAPIPrefix+"/"+ref, doOptions{})
if err != nil { if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchController return nil, ErrNoSuchNode
} }
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var controller types.Controller var node types.Node
if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil { if err := json.NewDecoder(resp.Body).Decode(&node); err != nil {
return nil, err return nil, err
} }
return &controller, nil return &node, nil
} }
// ControllerUpdate updates a controller on the server. // NodeUpdate updates a node on the server.
func (c *Client) ControllerUpdate(opts types.ControllerUpdateOptions) (*types.Controller, error) { func (c *Client) NodeUpdate(opts types.NodeUpdateOptions) (*types.Node, error) {
ref := opts.Name ref := opts.Name
if IsUUID(opts.ID) { if IsUUID(opts.ID) {
ref = opts.ID ref = opts.ID
} }
resp, err := c.do("PUT", ControllerAPIPrefix+"/"+ref, doOptions{ resp, err := c.do("PUT", NodeAPIPrefix+"/"+ref, doOptions{
data: opts, data: opts,
context: opts.Context, context: opts.Context,
}) })
...@@ -79,28 +79,28 @@ func (c *Client) ControllerUpdate(opts types.ControllerUpdateOptions) (*types.Co ...@@ -79,28 +79,28 @@ func (c *Client) ControllerUpdate(opts types.ControllerUpdateOptions) (*types.Co
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
var controller types.Controller var node types.Node
if err := json.NewDecoder(resp.Body).Decode(&controller); err != nil { if err := json.NewDecoder(resp.Body).Decode(&node); err != nil {
return nil, err return nil, err
} }
return &controller, nil return &node, nil
} }
// ControllerDelete removes a controller by its reference. // NodeDelete removes a node by its reference.
func (c *Client) ControllerDelete(opts types.DeleteOptions) error { func (c *Client) NodeDelete(opts types.DeleteOptions) error {
deleteOpts := doOptions{ deleteOpts := doOptions{
namespace: opts.Namespace, namespace: opts.Namespace,
force: opts.Force, force: opts.Force,
context: opts.Context, context: opts.Context,
} }
resp, err := c.do("DELETE", ControllerAPIPrefix+"/"+opts.Name, deleteOpts) resp, err := c.do("DELETE", NodeAPIPrefix+"/"+opts.Name, deleteOpts)
if err != nil { if err != nil {
if e, ok := err.(*Error); ok { if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound { if e.Status == http.StatusNotFound {
return ErrNoSuchController return ErrNoSuchNode
} }
if e.Status == http.StatusConflict { if e.Status == http.StatusConflict {
return ErrControllerInUse return ErrNodeInUse
} }
} }
return err return err
......
...@@ -5,16 +5,15 @@ import ( ...@@ -5,16 +5,15 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/storageos/go-api/types"
"net/http" "net/http"
"net/url" "net/url"
"github.com/storageos/go-api/types"
) )
var ( var (
// PolicyAPIPrefix is a partial path to the HTTP endpoint. // PolicyAPIPrefix is a partial path to the HTTP endpoint.
PolicyAPIPrefix = "policies" PolicyAPIPrefix = "policies"
// ErrNoSuchPolicy is the error returned when the policy does not exist. // ErrNoSuchPolicy is the error returned when the policy does not exist.
ErrNoSuchPolicy = errors.New("no such policy") ErrNoSuchPolicy = errors.New("no such policy")
) )
......
...@@ -41,7 +41,7 @@ func (c *Client) PoolList(opts types.ListOptions) ([]*types.Pool, error) { ...@@ -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. // 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{ resp, err := c.do("POST", PoolAPIPrefix, doOptions{
data: opts, data: opts,
context: opts.Context, context: opts.Context,
...@@ -56,6 +56,27 @@ func (c *Client) PoolCreate(opts types.PoolCreateOptions) (*types.Pool, error) { ...@@ -56,6 +56,27 @@ func (c *Client) PoolCreate(opts types.PoolCreateOptions) (*types.Pool, error) {
return &pool, nil 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. // Pool returns a pool by its reference.
func (c *Client) Pool(ref string) (*types.Pool, error) { func (c *Client) Pool(ref string) (*types.Pool, error) {
resp, err := c.do("GET", PoolAPIPrefix+"/"+ref, doOptions{}) resp, err := c.do("GET", PoolAPIPrefix+"/"+ref, doOptions{})
......
...@@ -3,7 +3,6 @@ package storageos ...@@ -3,7 +3,6 @@ package storageos
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"net/http" "net/http"
"net/url" "net/url"
...@@ -98,7 +97,7 @@ func (c *Client) RuleUpdate(opts types.RuleUpdateOptions) (*types.Rule, error) { ...@@ -98,7 +97,7 @@ func (c *Client) RuleUpdate(opts types.RuleUpdateOptions) (*types.Rule, error) {
if IsUUID(opts.ID) { if IsUUID(opts.ID) {
ref = opts.ID ref = opts.ID
} }
fmt.Printf("%#v\n", opts)
path, err := namespacedRefPath(opts.Namespace, RuleAPIPrefix, ref) path, err := namespacedRefPath(opts.Namespace, RuleAPIPrefix, ref)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -5,21 +5,22 @@ go_library( ...@@ -5,21 +5,22 @@ go_library(
srcs = [ srcs = [
"auth.go", "auth.go",
"capacity_stats.go", "capacity_stats.go",
"controller.go", "connectivity.go",
"controller_update_options.go",
"delete_options.go", "delete_options.go",
"deployment.go", "deployment.go",
"driver_instance.go", "driver_instance.go",
"error_response.go", "error_response.go",
"events.go", "events.go",
"health.go",
"list_options.go", "list_options.go",
"logger.go", "logger.go",
"namespace.go", "namespace.go",
"node.go", "node.go",
"node_update_options.go",
"operator.go", "operator.go",
"policy.go", "policy.go",
"pool.go", "pool.go",
"pool_create_options.go", "pool_options.go",
"rule.go", "rule.go",
"template.go", "template.go",
"template_create_options.go", "template_create_options.go",
...@@ -27,6 +28,7 @@ go_library( ...@@ -27,6 +28,7 @@ go_library(
"version.go", "version.go",
"volume.go", "volume.go",
"volume_create_options.go", "volume_create_options.go",
"volume_stats.go",
"volume_update_options.go", "volume_update_options.go",
], ],
importmap = "k8s.io/kubernetes/vendor/github.com/storageos/go-api/types", importmap = "k8s.io/kubernetes/vendor/github.com/storageos/go-api/types",
......
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
}
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"`
}
...@@ -14,13 +14,13 @@ type Deployment struct { ...@@ -14,13 +14,13 @@ type Deployment struct {
// Read Only: true // Read Only: true
Inode uint32 `json:"inode"` Inode uint32 `json:"inode"`
// Controller ID // Node ID
// Read Only: true // Read Only: true
Controller string `json:"controller"` Node string `json:"node"`
// Controller name // Node name
// Read Only: true // Read Only: true
ControllerName string `json:"controllerName"` NodeName string `json:"nodeName"`
// Health // Health
// Read Only: true // Read Only: true
......
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,
}
}
package types package types
import ( import (
"encoding/json" "time"
) )
type SubModuleStatus struct { // Node represents a StorageOS cluster node.
Status string `json:"status"` type Node struct {
UpdatedAt string `json:"updatedAt"` NodeConfig
ChangedAt string `json:"changedAt"`
Message string `json:"message"`
}
type NamedSubModuleStatus struct { HostID uint32 `json:"hostID"`
Name string Name string `json:"name"`
SubModuleStatus Description string `json:"description"`
} CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
type CPHealthStatus struct { Health string `json:"health"`
KV SubModuleStatus HealthUpdatedAt time.Time `json:"healthUpdatedAt"`
KVWrite SubModuleStatus
NATS SubModuleStatus
Scheduler SubModuleStatus
}
func (c *CPHealthStatus) ToNamedSubmodules() []NamedSubModuleStatus { VersionInfo map[string]VersionInfo `json:"versionInfo"`
return []NamedSubModuleStatus{ Version string `json:"version"`
{Name: "nats", SubModuleStatus: c.NATS}, Revision string // the GitCommit this maps to
{Name: "kv", SubModuleStatus: c.KV},
{Name: "kv_write", SubModuleStatus: c.KVWrite},
{Name: "scheduler", SubModuleStatus: c.Scheduler},
}
}
func (c *CPHealthStatus) UnmarshalJSON(data []byte) error { Scheduler bool `json:"scheduler"`
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 { Cordon bool `json:"cordon"`
DirectFSClient SubModuleStatus Drain bool `json:"drain"`
DirectFSServer SubModuleStatus
Director SubModuleStatus VolumeStats VolumeStats `json:"volumeStats"`
FSDriver SubModuleStatus
FS SubModuleStatus // PoolStats map[string]map[string]CapacityStats `json:"poolStats"`
CapacityStats CapacityStats `json:"capacityStats"`
} }
func (d *DPHealthStatus) ToNamedSubmodules() []NamedSubModuleStatus { // NodeConfig is a read-only representation of the node's configuration, set at
return []NamedSubModuleStatus{ // start time by environment variables passed to the container or using defaults.
{Name: "dfs_client", SubModuleStatus: d.DirectFSClient}, type NodeConfig struct {
{Name: "dfs_server", SubModuleStatus: d.DirectFSServer}, // UUID is the unique identifier of the node. It cannot be changed once set.
{Name: "director", SubModuleStatus: d.Director}, ID string `json:"id,omitempty"`
{Name: "fs_driver", SubModuleStatus: d.FSDriver},
{Name: "fs", SubModuleStatus: d.FS}, // 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"`
} }
func (d *DPHealthStatus) UnmarshalJSON(data []byte) error { // Device - device type
unmarsh := struct { type Device struct {
Submodules struct { ID string
DirectFSClient SubModuleStatus `json:"directfs-client"` Labels map[string]string `json:"labels"`
DirectFSServer SubModuleStatus `json:"directfs-server"` Status string `json:"status"`
Director SubModuleStatus `json:"director"` Identifier string `json:"identifier"`
FSDriver SubModuleStatus `json:"filesystem-driver"` Class string `json:"class"`
FS SubModuleStatus `json:"fs"` CapacityStats CapacityStats `json:"capacityStats"`
} `json:"submodules"` CreatedAt time.Time `json:"createdAt"`
}{} UpdatedAt time.Time `json:"updatedAt"`
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
} }
...@@ -2,25 +2,26 @@ package types ...@@ -2,25 +2,26 @@ package types
import "context" import "context"
// ControllerUpdateOptions are available parameters for updating existing controllers. // NodeUpdateOptions are available parameters for updating existing nodes.
type ControllerUpdateOptions struct { type NodeUpdateOptions struct {
// Controller unique ID. // Node unique ID.
// Read Only: true // Read Only: true
ID string `json:"id"` ID string `json:"id"`
// Controller name. // Node name.
// Read Only: true // Read Only: true
Name string `json:"name"` Name string `json:"name"`
// Description of the controller. // Description of the node.
Description string `json:"description"` Description string `json:"description"`
// Labels are user-defined key/value metadata. // Labels are user-defined key/value metadata.
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`
// Cordon sets the controler into an unschedulable state if true // Cordon marks the node as unschedulable if true
Cordon bool `json:"unschedulable"` Cordon bool `json:"cordon"`
Drain bool `json:"drain"`
// Context can be set with a timeout or can be used to cancel a request. // Context can be set with a timeout or can be used to cancel a request.
Context context.Context `json:"-"` Context context.Context `json:"-"`
......
...@@ -18,35 +18,19 @@ type Pool struct { ...@@ -18,35 +18,19 @@ type Pool struct {
// provisioned without a pool specified. There can only be one default pool. // provisioned without a pool specified. There can only be one default pool.
Default bool `json:"default"` Default bool `json:"default"`
// DefaultDriver specifies the storage driver to use by default if there are NodeSelector string `json:"nodeSelector"`
// multiple drivers in the pool and no driver was specified in the
// provisioning request or assigned by rules. If no driver was specified and
// no default set, driver weight is used to determine the default.
DefaultDriver string `json:"defaultDriver"`
// ControllerNames is a list of controller names that are participating in the
// storage pool.
ControllerNames []string `json:"controllerNames"`
// DriverNames is a list of backend storage drivers that are available in the
// storage pool.
DriverNames []string `json:"driverNames"`
// DriverInstances is used to track instances of each driver. Drivers have a
// default configuration, which can then be customised for each pool where
// they are used, which is representated as a DriverInstance.
// Read Only: true
DriverInstances []*DriverInstance `json:"driverInstances"`
// Flag describing whether the template is active. // DeviceSelector - specifies a selector to filter node devices based on their labels.
// Default: false // Only devices from nodes that are in the 'NodeNames' list can be selected
Active bool `json:"active"` DeviceSelector string `json:"deviceSelector"`
// CapacityStats are used to track aggregate capacity usage information across // Populated by the system. Read-only.
// all controllers and driver instances.
// Read Only: true
CapacityStats CapacityStats `json:"capacityStats"` 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 define a list of labels that describe the pool.
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`
} }
......
...@@ -2,11 +2,9 @@ package types ...@@ -2,11 +2,9 @@ package types
import "context" import "context"
// PoolCreateOptions are available parameters for creating new pools. // PoolOptions are available parameters for creating or updating pools.
type PoolCreateOptions struct { type PoolOptions struct {
ID string `json:"id"`
// Pool name.
// Required: true
Name string `json:"name"` Name string `json:"name"`
// Pool description. // Pool description.
...@@ -16,23 +14,11 @@ type PoolCreateOptions struct { ...@@ -16,23 +14,11 @@ type PoolCreateOptions struct {
// provisioned without a pool specified. There can only be one default pool. // provisioned without a pool specified. There can only be one default pool.
Default bool `json:"default"` Default bool `json:"default"`
// DefaultDriver specifies the storage driver to use by default if there are NodeSelector string `json:"nodeSelector"`
// 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. // DeviceSelector - specifies a selector to filter node devices based on their labels.
// Default: false // Only devices from nodes that are in the 'NodeNames' list can be selected
Active bool `json:"active"` DeviceSelector string `json:"deviceSelector"`
// Labels define a list of labels that describe the pool. // Labels define a list of labels that describe the pool.
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`
......
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"`
}
...@@ -22,7 +22,9 @@ var ( ...@@ -22,7 +22,9 @@ var (
// collection of restricted characters. // collection of restricted characters.
NamePattern = regexp.MustCompile(`^` + NameFormat + `$`) 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") ErrNoNamespace = errors.New("no namespace provided or incorrect format")
) )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment