Initial commit
This commit is contained in:
162
vendor/github.com/hyperhq/hypercli/pkg/plugins/client.go
generated
vendored
Normal file
162
vendor/github.com/hyperhq/hypercli/pkg/plugins/client.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
)
|
||||
|
||||
const (
|
||||
versionMimetype = "application/vnd.docker.plugins.v1.2+json"
|
||||
defaultTimeOut = 30
|
||||
)
|
||||
|
||||
// NewClient creates a new plugin client (http).
|
||||
func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
|
||||
tr := &http.Transport{}
|
||||
|
||||
c, err := tlsconfig.Client(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr.TLSClientConfig = c
|
||||
|
||||
protoAndAddr := strings.Split(addr, "://")
|
||||
sockets.ConfigureTCPTransport(tr, protoAndAddr[0], protoAndAddr[1])
|
||||
|
||||
scheme := protoAndAddr[0]
|
||||
if scheme != "https" {
|
||||
scheme = "http"
|
||||
}
|
||||
return &Client{&http.Client{Transport: tr}, scheme, protoAndAddr[1]}, nil
|
||||
}
|
||||
|
||||
// Client represents a plugin client.
|
||||
type Client struct {
|
||||
http *http.Client // http client to use
|
||||
scheme string // scheme protocol of the plugin
|
||||
addr string // http address of the plugin
|
||||
}
|
||||
|
||||
// Call calls the specified method with the specified arguments for the plugin.
|
||||
// It will retry for 30 seconds if a failure occurs when calling.
|
||||
func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
|
||||
var buf bytes.Buffer
|
||||
if args != nil {
|
||||
if err := json.NewEncoder(&buf).Encode(args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
body, err := c.callWithRetry(serviceMethod, &buf, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer body.Close()
|
||||
if ret != nil {
|
||||
if err := json.NewDecoder(body).Decode(&ret); err != nil {
|
||||
logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream calls the specified method with the specified arguments for the plugin and returns the response body
|
||||
func (c *Client) Stream(serviceMethod string, args interface{}) (io.ReadCloser, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.callWithRetry(serviceMethod, &buf, true)
|
||||
}
|
||||
|
||||
// SendFile calls the specified method, and passes through the IO stream
|
||||
func (c *Client) SendFile(serviceMethod string, data io.Reader, ret interface{}) error {
|
||||
body, err := c.callWithRetry(serviceMethod, data, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.NewDecoder(body).Decode(&ret); err != nil {
|
||||
logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool) (io.ReadCloser, error) {
|
||||
req, err := http.NewRequest("POST", "/"+serviceMethod, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Accept", versionMimetype)
|
||||
req.URL.Scheme = c.scheme
|
||||
req.URL.Host = c.addr
|
||||
|
||||
var retries int
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
if !retry {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeOff := backoff(retries)
|
||||
if abort(start, timeOff) {
|
||||
return nil, err
|
||||
}
|
||||
retries++
|
||||
logrus.Warnf("Unable to connect to plugin: %s, retrying in %v", c.addr, timeOff)
|
||||
time.Sleep(timeOff)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, &statusError{resp.StatusCode, serviceMethod, err.Error()}
|
||||
}
|
||||
|
||||
// Plugins' Response(s) should have an Err field indicating what went
|
||||
// wrong. Try to unmarshal into ResponseErr. Otherwise fallback to just
|
||||
// return the string(body)
|
||||
type responseErr struct {
|
||||
Err string
|
||||
}
|
||||
remoteErr := responseErr{}
|
||||
if err := json.Unmarshal(b, &remoteErr); err == nil {
|
||||
if remoteErr.Err != "" {
|
||||
return nil, &statusError{resp.StatusCode, serviceMethod, remoteErr.Err}
|
||||
}
|
||||
}
|
||||
// old way...
|
||||
return nil, &statusError{resp.StatusCode, serviceMethod, string(b)}
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
}
|
||||
|
||||
func backoff(retries int) time.Duration {
|
||||
b, max := 1, defaultTimeOut
|
||||
for b < max && retries > 0 {
|
||||
b *= 2
|
||||
retries--
|
||||
}
|
||||
if b > max {
|
||||
b = max
|
||||
}
|
||||
return time.Duration(b) * time.Second
|
||||
}
|
||||
|
||||
func abort(start time.Time, timeOff time.Duration) bool {
|
||||
return timeOff+time.Since(start) >= time.Duration(defaultTimeOut)*time.Second
|
||||
}
|
||||
127
vendor/github.com/hyperhq/hypercli/pkg/plugins/client_test.go
generated
vendored
Normal file
127
vendor/github.com/hyperhq/hypercli/pkg/plugins/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
)
|
||||
|
||||
var (
|
||||
mux *http.ServeMux
|
||||
server *httptest.Server
|
||||
)
|
||||
|
||||
func setupRemotePluginServer() string {
|
||||
mux = http.NewServeMux()
|
||||
server = httptest.NewServer(mux)
|
||||
return server.URL
|
||||
}
|
||||
|
||||
func teardownRemotePluginServer() {
|
||||
if server != nil {
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailedConnection(t *testing.T) {
|
||||
c, _ := NewClient("tcp://127.0.0.1:1", tlsconfig.Options{InsecureSkipVerify: true})
|
||||
_, err := c.callWithRetry("Service.Method", nil, false)
|
||||
if err == nil {
|
||||
t.Fatal("Unexpected successful connection")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoInputOutput(t *testing.T) {
|
||||
addr := setupRemotePluginServer()
|
||||
defer teardownRemotePluginServer()
|
||||
|
||||
m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}}
|
||||
|
||||
mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
t.Fatalf("Expected POST, got %s\n", r.Method)
|
||||
}
|
||||
|
||||
header := w.Header()
|
||||
header.Set("Content-Type", versionMimetype)
|
||||
|
||||
io.Copy(w, r.Body)
|
||||
})
|
||||
|
||||
c, _ := NewClient(addr, tlsconfig.Options{InsecureSkipVerify: true})
|
||||
var output Manifest
|
||||
err := c.Call("Test.Echo", m, &output)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(output, m) {
|
||||
t.Fatalf("Expected %v, was %v\n", m, output)
|
||||
}
|
||||
err = c.Call("Test.Echo", nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackoff(t *testing.T) {
|
||||
cases := []struct {
|
||||
retries int
|
||||
expTimeOff time.Duration
|
||||
}{
|
||||
{0, time.Duration(1)},
|
||||
{1, time.Duration(2)},
|
||||
{2, time.Duration(4)},
|
||||
{4, time.Duration(16)},
|
||||
{6, time.Duration(30)},
|
||||
{10, time.Duration(30)},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
s := c.expTimeOff * time.Second
|
||||
if d := backoff(c.retries); d != s {
|
||||
t.Fatalf("Retry %v, expected %v, was %v\n", c.retries, s, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbortRetry(t *testing.T) {
|
||||
cases := []struct {
|
||||
timeOff time.Duration
|
||||
expAbort bool
|
||||
}{
|
||||
{time.Duration(1), false},
|
||||
{time.Duration(2), false},
|
||||
{time.Duration(10), false},
|
||||
{time.Duration(30), true},
|
||||
{time.Duration(40), true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
s := c.timeOff * time.Second
|
||||
if a := abort(time.Now(), s); a != c.expAbort {
|
||||
t.Fatalf("Duration %v, expected %v, was %v\n", c.timeOff, s, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientScheme(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"tcp://127.0.0.1:8080": "http",
|
||||
"unix:///usr/local/plugins/foo": "http",
|
||||
"http://127.0.0.1:8080": "http",
|
||||
"https://127.0.0.1:8080": "https",
|
||||
}
|
||||
|
||||
for addr, scheme := range cases {
|
||||
c, _ := NewClient(addr, tlsconfig.Options{InsecureSkipVerify: true})
|
||||
if c.scheme != scheme {
|
||||
t.Fatalf("URL scheme mismatch, expected %s, got %s", scheme, c.scheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
130
vendor/github.com/hyperhq/hypercli/pkg/plugins/discovery.go
generated
vendored
Normal file
130
vendor/github.com/hyperhq/hypercli/pkg/plugins/discovery.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotFound plugin not found
|
||||
ErrNotFound = errors.New("plugin not found")
|
||||
socketsPath = "/run/docker/plugins"
|
||||
specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"}
|
||||
)
|
||||
|
||||
// localRegistry defines a registry that is local (using unix socket).
|
||||
type localRegistry struct{}
|
||||
|
||||
func newLocalRegistry() localRegistry {
|
||||
return localRegistry{}
|
||||
}
|
||||
|
||||
// Scan scans all the plugin paths and returns all the names it found
|
||||
func Scan() ([]string, error) {
|
||||
var names []string
|
||||
if err := filepath.Walk(socketsPath, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeSocket != 0 {
|
||||
name := strings.TrimSuffix(fi.Name(), filepath.Ext(fi.Name()))
|
||||
names = append(names, name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, path := range specsPaths {
|
||||
if err := filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
|
||||
if err != nil || fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
name := strings.TrimSuffix(fi.Name(), filepath.Ext(fi.Name()))
|
||||
names = append(names, name)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Plugin returns the plugin registered with the given name (or returns an error).
|
||||
func (l *localRegistry) Plugin(name string) (*Plugin, error) {
|
||||
socketpaths := pluginPaths(socketsPath, name, ".sock")
|
||||
|
||||
for _, p := range socketpaths {
|
||||
if fi, err := os.Stat(p); err == nil && fi.Mode()&os.ModeSocket != 0 {
|
||||
return newLocalPlugin(name, "unix://"+p), nil
|
||||
}
|
||||
}
|
||||
|
||||
var txtspecpaths []string
|
||||
for _, p := range specsPaths {
|
||||
txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".spec")...)
|
||||
txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".json")...)
|
||||
}
|
||||
|
||||
for _, p := range txtspecpaths {
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
if strings.HasSuffix(p, ".json") {
|
||||
return readPluginJSONInfo(name, p)
|
||||
}
|
||||
return readPluginInfo(name, p)
|
||||
}
|
||||
}
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func readPluginInfo(name, path string) (*Plugin, error) {
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr := strings.TrimSpace(string(content))
|
||||
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(u.Scheme) == 0 {
|
||||
return nil, fmt.Errorf("Unknown protocol")
|
||||
}
|
||||
|
||||
return newLocalPlugin(name, addr), nil
|
||||
}
|
||||
|
||||
func readPluginJSONInfo(name, path string) (*Plugin, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var p Plugin
|
||||
if err := json.NewDecoder(f).Decode(&p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Name = name
|
||||
if len(p.TLSConfig.CAFile) == 0 {
|
||||
p.TLSConfig.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func pluginPaths(base, name, ext string) []string {
|
||||
return []string{
|
||||
filepath.Join(base, name+ext),
|
||||
filepath.Join(base, name, name+ext),
|
||||
}
|
||||
}
|
||||
169
vendor/github.com/hyperhq/hypercli/pkg/plugins/discovery_test.go
generated
vendored
Normal file
169
vendor/github.com/hyperhq/hypercli/pkg/plugins/discovery_test.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setup(t *testing.T) (string, func()) {
|
||||
tmpdir, err := ioutil.TempDir("", "docker-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backup := socketsPath
|
||||
socketsPath = tmpdir
|
||||
specsPaths = []string{tmpdir}
|
||||
|
||||
return tmpdir, func() {
|
||||
socketsPath = backup
|
||||
os.RemoveAll(tmpdir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalSocket(t *testing.T) {
|
||||
tmpdir, unregister := setup(t)
|
||||
defer unregister()
|
||||
|
||||
cases := []string{
|
||||
filepath.Join(tmpdir, "echo.sock"),
|
||||
filepath.Join(tmpdir, "echo", "echo.sock"),
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if err := os.MkdirAll(filepath.Dir(c), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
l, err := net.Listen("unix", c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := newLocalRegistry()
|
||||
p, err := r.Plugin("echo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pp, err := r.Plugin("echo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(p, pp) {
|
||||
t.Fatalf("Expected %v, was %v\n", p, pp)
|
||||
}
|
||||
|
||||
if p.Name != "echo" {
|
||||
t.Fatalf("Expected plugin `echo`, got %s\n", p.Name)
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("unix://%s", c)
|
||||
if p.Addr != addr {
|
||||
t.Fatalf("Expected plugin addr `%s`, got %s\n", addr, p.Addr)
|
||||
}
|
||||
if p.TLSConfig.InsecureSkipVerify != true {
|
||||
t.Fatalf("Expected TLS verification to be skipped")
|
||||
}
|
||||
l.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSpecPlugin(t *testing.T) {
|
||||
tmpdir, unregister := setup(t)
|
||||
defer unregister()
|
||||
|
||||
cases := []struct {
|
||||
path string
|
||||
name string
|
||||
addr string
|
||||
fail bool
|
||||
}{
|
||||
{filepath.Join(tmpdir, "echo.spec"), "echo", "unix://var/lib/docker/plugins/echo.sock", false},
|
||||
{filepath.Join(tmpdir, "echo", "echo.spec"), "echo", "unix://var/lib/docker/plugins/echo.sock", false},
|
||||
{filepath.Join(tmpdir, "foo.spec"), "foo", "tcp://localhost:8080", false},
|
||||
{filepath.Join(tmpdir, "foo", "foo.spec"), "foo", "tcp://localhost:8080", false},
|
||||
{filepath.Join(tmpdir, "bar.spec"), "bar", "localhost:8080", true}, // unknown transport
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if err := os.MkdirAll(filepath.Dir(c.path), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(c.path, []byte(c.addr), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := newLocalRegistry()
|
||||
p, err := r.Plugin(c.name)
|
||||
if c.fail && err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if p.Name != c.name {
|
||||
t.Fatalf("Expected plugin `%s`, got %s\n", c.name, p.Name)
|
||||
}
|
||||
|
||||
if p.Addr != c.addr {
|
||||
t.Fatalf("Expected plugin addr `%s`, got %s\n", c.addr, p.Addr)
|
||||
}
|
||||
|
||||
if p.TLSConfig.InsecureSkipVerify != true {
|
||||
t.Fatalf("Expected TLS verification to be skipped")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileJSONSpecPlugin(t *testing.T) {
|
||||
tmpdir, unregister := setup(t)
|
||||
defer unregister()
|
||||
|
||||
p := filepath.Join(tmpdir, "example.json")
|
||||
spec := `{
|
||||
"Name": "plugin-example",
|
||||
"Addr": "https://example.com/docker/plugin",
|
||||
"TLSConfig": {
|
||||
"CAFile": "/usr/shared/docker/certs/example-ca.pem",
|
||||
"CertFile": "/usr/shared/docker/certs/example-cert.pem",
|
||||
"KeyFile": "/usr/shared/docker/certs/example-key.pem"
|
||||
}
|
||||
}`
|
||||
|
||||
if err := ioutil.WriteFile(p, []byte(spec), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := newLocalRegistry()
|
||||
plugin, err := r.Plugin("example")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if plugin.Name != "example" {
|
||||
t.Fatalf("Expected plugin `plugin-example`, got %s\n", plugin.Name)
|
||||
}
|
||||
|
||||
if plugin.Addr != "https://example.com/docker/plugin" {
|
||||
t.Fatalf("Expected plugin addr `https://example.com/docker/plugin`, got %s\n", plugin.Addr)
|
||||
}
|
||||
|
||||
if plugin.TLSConfig.CAFile != "/usr/shared/docker/certs/example-ca.pem" {
|
||||
t.Fatalf("Expected plugin CA `/usr/shared/docker/certs/example-ca.pem`, got %s\n", plugin.TLSConfig.CAFile)
|
||||
}
|
||||
|
||||
if plugin.TLSConfig.CertFile != "/usr/shared/docker/certs/example-cert.pem" {
|
||||
t.Fatalf("Expected plugin Certificate `/usr/shared/docker/certs/example-cert.pem`, got %s\n", plugin.TLSConfig.CertFile)
|
||||
}
|
||||
|
||||
if plugin.TLSConfig.KeyFile != "/usr/shared/docker/certs/example-key.pem" {
|
||||
t.Fatalf("Expected plugin Key `/usr/shared/docker/certs/example-key.pem`, got %s\n", plugin.TLSConfig.KeyFile)
|
||||
}
|
||||
}
|
||||
33
vendor/github.com/hyperhq/hypercli/pkg/plugins/errors.go
generated
vendored
Normal file
33
vendor/github.com/hyperhq/hypercli/pkg/plugins/errors.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type statusError struct {
|
||||
status int
|
||||
method string
|
||||
err string
|
||||
}
|
||||
|
||||
// Error returns a formated string for this error type
|
||||
func (e *statusError) Error() string {
|
||||
return fmt.Sprintf("%s: %v", e.method, e.err)
|
||||
}
|
||||
|
||||
// IsNotFound indicates if the passed in error is from an http.StatusNotFound from the plugin
|
||||
func IsNotFound(err error) bool {
|
||||
return isStatusError(err, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func isStatusError(err error, status int) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
e, ok := err.(*statusError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return e.status == status
|
||||
}
|
||||
68
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/README.md
generated
vendored
Normal file
68
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/README.md
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
Plugin RPC Generator
|
||||
====================
|
||||
|
||||
Generates go code from a Go interface definition for proxying between the plugin
|
||||
API and the subsystem being extended.
|
||||
|
||||
## Usage
|
||||
|
||||
Given an interface definition:
|
||||
|
||||
```go
|
||||
type volumeDriver interface {
|
||||
Create(name string, opts opts) (err error)
|
||||
Remove(name string) (err error)
|
||||
Path(name string) (mountpoint string, err error)
|
||||
Mount(name string) (mountpoint string, err error)
|
||||
Unmount(name string) (err error)
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: All function options and return values must be named in the definition.
|
||||
|
||||
Run the generator:
|
||||
|
||||
```bash
|
||||
$ pluginrpc-gen --type volumeDriver --name VolumeDriver -i volumes/drivers/extpoint.go -o volumes/drivers/proxy.go
|
||||
```
|
||||
|
||||
Where:
|
||||
- `--type` is the name of the interface to use
|
||||
- `--name` is the subsystem that the plugin "Implements"
|
||||
- `-i` is the input file containing the interface definition
|
||||
- `-o` is the output file where the the generated code should go
|
||||
|
||||
**Note**: The generated code will use the same package name as the one defined in the input file
|
||||
|
||||
Optionally, you can skip functions on the interface that should not be
|
||||
implemented in the generated proxy code by passing in the function name to `--skip`.
|
||||
This flag can be specified multiple times.
|
||||
|
||||
You can also add build tags that should be prepended to the generated code by
|
||||
supplying `--tag`. This flag can be specified multiple times.
|
||||
|
||||
## Known issues
|
||||
|
||||
The parser can currently only handle types which are not specifically a map or
|
||||
a slice.
|
||||
You can, however, create a type that uses a map or a slice internally, for instance:
|
||||
|
||||
```go
|
||||
type opts map[string]string
|
||||
```
|
||||
|
||||
This `opts` type will work, whreas using a `map[string]string` directly will not.
|
||||
|
||||
## go-generate
|
||||
|
||||
You can also use this with go-generate, which is pretty awesome.
|
||||
To do so, place the code at the top of the file which contains the interface
|
||||
definition (i.e., the input file):
|
||||
|
||||
```go
|
||||
//go:generate pluginrpc-gen -i $GOFILE -o proxy.go -type volumeDriver -name VolumeDriver
|
||||
```
|
||||
|
||||
Then cd to the package dir and run `go generate`
|
||||
|
||||
**Note**: the `pluginrpc-gen` binary must be within your `$PATH`
|
||||
41
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/fixtures/foo.go
generated
vendored
Normal file
41
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/fixtures/foo.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package foo
|
||||
|
||||
type wobble struct {
|
||||
Some string
|
||||
Val string
|
||||
Inception *wobble
|
||||
}
|
||||
|
||||
// Fooer is an empty interface used for tests.
|
||||
type Fooer interface{}
|
||||
|
||||
// Fooer2 is an interface used for tests.
|
||||
type Fooer2 interface {
|
||||
Foo()
|
||||
}
|
||||
|
||||
// Fooer3 is an interface used for tests.
|
||||
type Fooer3 interface {
|
||||
Foo()
|
||||
Bar(a string)
|
||||
Baz(a string) (err error)
|
||||
Qux(a, b string) (val string, err error)
|
||||
Wobble() (w *wobble)
|
||||
Wiggle() (w wobble)
|
||||
}
|
||||
|
||||
// Fooer4 is an interface used for tests.
|
||||
type Fooer4 interface {
|
||||
Foo() error
|
||||
}
|
||||
|
||||
// Bar is an interface used for tests.
|
||||
type Bar interface {
|
||||
Boo(a string, b string) (s string, err error)
|
||||
}
|
||||
|
||||
// Fooer5 is an interface used for tests.
|
||||
type Fooer5 interface {
|
||||
Foo()
|
||||
Bar
|
||||
}
|
||||
91
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/main.go
generated
vendored
Normal file
91
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/main.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type stringSet struct {
|
||||
values map[string]struct{}
|
||||
}
|
||||
|
||||
func (s stringSet) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s stringSet) Set(value string) error {
|
||||
s.values[value] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
func (s stringSet) GetValues() map[string]struct{} {
|
||||
return s.values
|
||||
}
|
||||
|
||||
var (
|
||||
typeName = flag.String("type", "", "interface type to generate plugin rpc proxy for")
|
||||
rpcName = flag.String("name", *typeName, "RPC name, set if different from type")
|
||||
inputFile = flag.String("i", "", "input file path")
|
||||
outputFile = flag.String("o", *inputFile+"_proxy.go", "output file path")
|
||||
|
||||
skipFuncs map[string]struct{}
|
||||
flSkipFuncs = stringSet{make(map[string]struct{})}
|
||||
|
||||
flBuildTags = stringSet{make(map[string]struct{})}
|
||||
)
|
||||
|
||||
func errorOut(msg string, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", msg, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func checkFlags() error {
|
||||
if *outputFile == "" {
|
||||
return fmt.Errorf("missing required flag `-o`")
|
||||
}
|
||||
if *inputFile == "" {
|
||||
return fmt.Errorf("missing required flag `-i`")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Var(flSkipFuncs, "skip", "skip parsing for function")
|
||||
flag.Var(flBuildTags, "tag", "build tags to add to generated files")
|
||||
flag.Parse()
|
||||
skipFuncs = flSkipFuncs.GetValues()
|
||||
|
||||
errorOut("error", checkFlags())
|
||||
|
||||
pkg, err := Parse(*inputFile, *typeName)
|
||||
errorOut(fmt.Sprintf("error parsing requested type %s", *typeName), err)
|
||||
|
||||
var analysis = struct {
|
||||
InterfaceType string
|
||||
RPCName string
|
||||
BuildTags map[string]struct{}
|
||||
*ParsedPkg
|
||||
}{toLower(*typeName), *rpcName, flBuildTags.GetValues(), pkg}
|
||||
var buf bytes.Buffer
|
||||
|
||||
errorOut("parser error", generatedTempl.Execute(&buf, analysis))
|
||||
src, err := format.Source(buf.Bytes())
|
||||
errorOut("error formating generated source", err)
|
||||
errorOut("error writing file", ioutil.WriteFile(*outputFile, src, 0644))
|
||||
}
|
||||
|
||||
func toLower(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
return string(unicode.ToLower(r)) + s[n:]
|
||||
}
|
||||
163
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/parser.go
generated
vendored
Normal file
163
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/parser.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var errBadReturn = errors.New("found return arg with no name: all args must be named")
|
||||
|
||||
type errUnexpectedType struct {
|
||||
expected string
|
||||
actual interface{}
|
||||
}
|
||||
|
||||
func (e errUnexpectedType) Error() string {
|
||||
return fmt.Sprintf("got wrong type expecting %s, got: %v", e.expected, reflect.TypeOf(e.actual))
|
||||
}
|
||||
|
||||
// ParsedPkg holds information about a package that has been parsed,
|
||||
// its name and the list of functions.
|
||||
type ParsedPkg struct {
|
||||
Name string
|
||||
Functions []function
|
||||
}
|
||||
|
||||
type function struct {
|
||||
Name string
|
||||
Args []arg
|
||||
Returns []arg
|
||||
Doc string
|
||||
}
|
||||
|
||||
type arg struct {
|
||||
Name string
|
||||
ArgType string
|
||||
}
|
||||
|
||||
func (a *arg) String() string {
|
||||
return a.Name + " " + a.ArgType
|
||||
}
|
||||
|
||||
// Parse parses the given file for an interface definition with the given name.
|
||||
func Parse(filePath string, objName string) (*ParsedPkg, error) {
|
||||
fs := token.NewFileSet()
|
||||
pkg, err := parser.ParseFile(fs, filePath, nil, parser.AllErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &ParsedPkg{}
|
||||
p.Name = pkg.Name.Name
|
||||
obj, exists := pkg.Scope.Objects[objName]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("could not find object %s in %s", objName, filePath)
|
||||
}
|
||||
if obj.Kind != ast.Typ {
|
||||
return nil, fmt.Errorf("exected type, got %s", obj.Kind)
|
||||
}
|
||||
spec, ok := obj.Decl.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return nil, errUnexpectedType{"*ast.TypeSpec", obj.Decl}
|
||||
}
|
||||
iface, ok := spec.Type.(*ast.InterfaceType)
|
||||
if !ok {
|
||||
return nil, errUnexpectedType{"*ast.InterfaceType", spec.Type}
|
||||
}
|
||||
|
||||
p.Functions, err = parseInterface(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func parseInterface(iface *ast.InterfaceType) ([]function, error) {
|
||||
var functions []function
|
||||
for _, field := range iface.Methods.List {
|
||||
switch f := field.Type.(type) {
|
||||
case *ast.FuncType:
|
||||
method, err := parseFunc(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if method == nil {
|
||||
continue
|
||||
}
|
||||
functions = append(functions, *method)
|
||||
case *ast.Ident:
|
||||
spec, ok := f.Obj.Decl.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return nil, errUnexpectedType{"*ast.TypeSpec", f.Obj.Decl}
|
||||
}
|
||||
iface, ok := spec.Type.(*ast.InterfaceType)
|
||||
if !ok {
|
||||
return nil, errUnexpectedType{"*ast.TypeSpec", spec.Type}
|
||||
}
|
||||
funcs, err := parseInterface(iface)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
functions = append(functions, funcs...)
|
||||
default:
|
||||
return nil, errUnexpectedType{"*astFuncType or *ast.Ident", f}
|
||||
}
|
||||
}
|
||||
return functions, nil
|
||||
}
|
||||
|
||||
func parseFunc(field *ast.Field) (*function, error) {
|
||||
f := field.Type.(*ast.FuncType)
|
||||
method := &function{Name: field.Names[0].Name}
|
||||
if _, exists := skipFuncs[method.Name]; exists {
|
||||
fmt.Println("skipping:", method.Name)
|
||||
return nil, nil
|
||||
}
|
||||
if f.Params != nil {
|
||||
args, err := parseArgs(f.Params.List)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
method.Args = args
|
||||
}
|
||||
if f.Results != nil {
|
||||
returns, err := parseArgs(f.Results.List)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing function returns for %q: %v", method.Name, err)
|
||||
}
|
||||
method.Returns = returns
|
||||
}
|
||||
return method, nil
|
||||
}
|
||||
|
||||
func parseArgs(fields []*ast.Field) ([]arg, error) {
|
||||
var args []arg
|
||||
for _, f := range fields {
|
||||
if len(f.Names) == 0 {
|
||||
return nil, errBadReturn
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
var typeName string
|
||||
switch argType := f.Type.(type) {
|
||||
case *ast.Ident:
|
||||
typeName = argType.Name
|
||||
case *ast.StarExpr:
|
||||
i, ok := argType.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return nil, errUnexpectedType{"*ast.Ident", f.Type}
|
||||
}
|
||||
typeName = "*" + i.Name
|
||||
default:
|
||||
return nil, errUnexpectedType{"*ast.Ident or *ast.StarExpr", f.Type}
|
||||
}
|
||||
|
||||
args = append(args, arg{name.Name, typeName})
|
||||
}
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
168
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/parser_test.go
generated
vendored
Normal file
168
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/parser_test.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testFixture = "fixtures/foo.go"
|
||||
|
||||
func TestParseEmptyInterface(t *testing.T) {
|
||||
pkg, err := Parse(testFixture, "Fooer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertName(t, "foo", pkg.Name)
|
||||
assertNum(t, 0, len(pkg.Functions))
|
||||
}
|
||||
|
||||
func TestParseNonInterfaceType(t *testing.T) {
|
||||
_, err := Parse(testFixture, "wobble")
|
||||
if _, ok := err.(errUnexpectedType); !ok {
|
||||
t.Fatal("expected type error when parsing non-interface type")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWithOneFunction(t *testing.T) {
|
||||
pkg, err := Parse(testFixture, "Fooer2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertName(t, "foo", pkg.Name)
|
||||
assertNum(t, 1, len(pkg.Functions))
|
||||
assertName(t, "Foo", pkg.Functions[0].Name)
|
||||
assertNum(t, 0, len(pkg.Functions[0].Args))
|
||||
assertNum(t, 0, len(pkg.Functions[0].Returns))
|
||||
}
|
||||
|
||||
func TestParseWithMultipleFuncs(t *testing.T) {
|
||||
pkg, err := Parse(testFixture, "Fooer3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertName(t, "foo", pkg.Name)
|
||||
assertNum(t, 6, len(pkg.Functions))
|
||||
|
||||
f := pkg.Functions[0]
|
||||
assertName(t, "Foo", f.Name)
|
||||
assertNum(t, 0, len(f.Args))
|
||||
assertNum(t, 0, len(f.Returns))
|
||||
|
||||
f = pkg.Functions[1]
|
||||
assertName(t, "Bar", f.Name)
|
||||
assertNum(t, 1, len(f.Args))
|
||||
assertNum(t, 0, len(f.Returns))
|
||||
arg := f.Args[0]
|
||||
assertName(t, "a", arg.Name)
|
||||
assertName(t, "string", arg.ArgType)
|
||||
|
||||
f = pkg.Functions[2]
|
||||
assertName(t, "Baz", f.Name)
|
||||
assertNum(t, 1, len(f.Args))
|
||||
assertNum(t, 1, len(f.Returns))
|
||||
arg = f.Args[0]
|
||||
assertName(t, "a", arg.Name)
|
||||
assertName(t, "string", arg.ArgType)
|
||||
arg = f.Returns[0]
|
||||
assertName(t, "err", arg.Name)
|
||||
assertName(t, "error", arg.ArgType)
|
||||
|
||||
f = pkg.Functions[3]
|
||||
assertName(t, "Qux", f.Name)
|
||||
assertNum(t, 2, len(f.Args))
|
||||
assertNum(t, 2, len(f.Returns))
|
||||
arg = f.Args[0]
|
||||
assertName(t, "a", f.Args[0].Name)
|
||||
assertName(t, "string", f.Args[0].ArgType)
|
||||
arg = f.Args[1]
|
||||
assertName(t, "b", arg.Name)
|
||||
assertName(t, "string", arg.ArgType)
|
||||
arg = f.Returns[0]
|
||||
assertName(t, "val", arg.Name)
|
||||
assertName(t, "string", arg.ArgType)
|
||||
arg = f.Returns[1]
|
||||
assertName(t, "err", arg.Name)
|
||||
assertName(t, "error", arg.ArgType)
|
||||
|
||||
f = pkg.Functions[4]
|
||||
assertName(t, "Wobble", f.Name)
|
||||
assertNum(t, 0, len(f.Args))
|
||||
assertNum(t, 1, len(f.Returns))
|
||||
arg = f.Returns[0]
|
||||
assertName(t, "w", arg.Name)
|
||||
assertName(t, "*wobble", arg.ArgType)
|
||||
|
||||
f = pkg.Functions[5]
|
||||
assertName(t, "Wiggle", f.Name)
|
||||
assertNum(t, 0, len(f.Args))
|
||||
assertNum(t, 1, len(f.Returns))
|
||||
arg = f.Returns[0]
|
||||
assertName(t, "w", arg.Name)
|
||||
assertName(t, "wobble", arg.ArgType)
|
||||
}
|
||||
|
||||
func TestParseWithUnamedReturn(t *testing.T) {
|
||||
_, err := Parse(testFixture, "Fooer4")
|
||||
if !strings.HasSuffix(err.Error(), errBadReturn.Error()) {
|
||||
t.Fatalf("expected ErrBadReturn, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmbeddedInterface(t *testing.T) {
|
||||
pkg, err := Parse(testFixture, "Fooer5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertName(t, "foo", pkg.Name)
|
||||
assertNum(t, 2, len(pkg.Functions))
|
||||
|
||||
f := pkg.Functions[0]
|
||||
assertName(t, "Foo", f.Name)
|
||||
assertNum(t, 0, len(f.Args))
|
||||
assertNum(t, 0, len(f.Returns))
|
||||
|
||||
f = pkg.Functions[1]
|
||||
assertName(t, "Boo", f.Name)
|
||||
assertNum(t, 2, len(f.Args))
|
||||
assertNum(t, 2, len(f.Returns))
|
||||
|
||||
arg := f.Args[0]
|
||||
assertName(t, "a", arg.Name)
|
||||
assertName(t, "string", arg.ArgType)
|
||||
|
||||
arg = f.Args[1]
|
||||
assertName(t, "b", arg.Name)
|
||||
assertName(t, "string", arg.ArgType)
|
||||
|
||||
arg = f.Returns[0]
|
||||
assertName(t, "s", arg.Name)
|
||||
assertName(t, "string", arg.ArgType)
|
||||
|
||||
arg = f.Returns[1]
|
||||
assertName(t, "err", arg.Name)
|
||||
assertName(t, "error", arg.ArgType)
|
||||
}
|
||||
|
||||
func assertName(t *testing.T, expected, actual string) {
|
||||
if expected != actual {
|
||||
fatalOut(t, fmt.Sprintf("expected name to be `%s`, got: %s", expected, actual))
|
||||
}
|
||||
}
|
||||
|
||||
func assertNum(t *testing.T, expected, actual int) {
|
||||
if expected != actual {
|
||||
fatalOut(t, fmt.Sprintf("expected number to be %d, got: %d", expected, actual))
|
||||
}
|
||||
}
|
||||
|
||||
func fatalOut(t *testing.T, msg string) {
|
||||
_, file, ln, _ := runtime.Caller(2)
|
||||
t.Fatalf("%s:%d: %s", filepath.Base(file), ln, msg)
|
||||
}
|
||||
97
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/template.go
generated
vendored
Normal file
97
vendor/github.com/hyperhq/hypercli/pkg/plugins/pluginrpc-gen/template.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func printArgs(args []arg) string {
|
||||
var argStr []string
|
||||
for _, arg := range args {
|
||||
argStr = append(argStr, arg.String())
|
||||
}
|
||||
return strings.Join(argStr, ", ")
|
||||
}
|
||||
|
||||
func marshalType(t string) string {
|
||||
switch t {
|
||||
case "error":
|
||||
// convert error types to plain strings to ensure the values are encoded/decoded properly
|
||||
return "string"
|
||||
default:
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
func isErr(t string) bool {
|
||||
switch t {
|
||||
case "error":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Need to use this helper due to issues with go-vet
|
||||
func buildTag(s string) string {
|
||||
return "+build " + s
|
||||
}
|
||||
|
||||
var templFuncs = template.FuncMap{
|
||||
"printArgs": printArgs,
|
||||
"marshalType": marshalType,
|
||||
"isErr": isErr,
|
||||
"lower": strings.ToLower,
|
||||
"title": strings.Title,
|
||||
"tag": buildTag,
|
||||
}
|
||||
|
||||
var generatedTempl = template.Must(template.New("rpc_cient").Funcs(templFuncs).Parse(`
|
||||
// generated code - DO NOT EDIT
|
||||
{{ range $k, $v := .BuildTags }}
|
||||
// {{ tag $k }} {{ end }}
|
||||
|
||||
package {{ .Name }}
|
||||
|
||||
import "errors"
|
||||
|
||||
type client interface{
|
||||
Call(string, interface{}, interface{}) error
|
||||
}
|
||||
|
||||
type {{ .InterfaceType }}Proxy struct {
|
||||
client
|
||||
}
|
||||
|
||||
{{ range .Functions }}
|
||||
type {{ $.InterfaceType }}Proxy{{ .Name }}Request struct{
|
||||
{{ range .Args }}
|
||||
{{ title .Name }} {{ .ArgType }} {{ end }}
|
||||
}
|
||||
|
||||
type {{ $.InterfaceType }}Proxy{{ .Name }}Response struct{
|
||||
{{ range .Returns }}
|
||||
{{ title .Name }} {{ marshalType .ArgType }} {{ end }}
|
||||
}
|
||||
|
||||
func (pp *{{ $.InterfaceType }}Proxy) {{ .Name }}({{ printArgs .Args }}) ({{ printArgs .Returns }}) {
|
||||
var(
|
||||
req {{ $.InterfaceType }}Proxy{{ .Name }}Request
|
||||
ret {{ $.InterfaceType }}Proxy{{ .Name }}Response
|
||||
)
|
||||
{{ range .Args }}
|
||||
req.{{ title .Name }} = {{ lower .Name }} {{ end }}
|
||||
if err = pp.Call("{{ $.RPCName }}.{{ .Name }}", req, &ret); err != nil {
|
||||
return
|
||||
}
|
||||
{{ range $r := .Returns }}
|
||||
{{ if isErr .ArgType }}
|
||||
if ret.{{ title .Name }} != "" {
|
||||
{{ lower .Name }} = errors.New(ret.{{ title .Name }})
|
||||
} {{ end }}
|
||||
{{ if isErr .ArgType | not }} {{ lower .Name }} = ret.{{ title .Name }} {{ end }} {{ end }}
|
||||
|
||||
return
|
||||
}
|
||||
{{ end }}
|
||||
`))
|
||||
222
vendor/github.com/hyperhq/hypercli/pkg/plugins/plugins.go
generated
vendored
Normal file
222
vendor/github.com/hyperhq/hypercli/pkg/plugins/plugins.go
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
// Package plugins provides structures and helper functions to manage Docker
|
||||
// plugins.
|
||||
//
|
||||
// Docker discovers plugins by looking for them in the plugin directory whenever
|
||||
// a user or container tries to use one by name. UNIX domain socket files must
|
||||
// be located under /run/docker/plugins, whereas spec files can be located
|
||||
// either under /etc/docker/plugins or /usr/lib/docker/plugins. This is handled
|
||||
// by the Registry interface, which lets you list all plugins or get a plugin by
|
||||
// its name if it exists.
|
||||
//
|
||||
// The plugins need to implement an HTTP server and bind this to the UNIX socket
|
||||
// or the address specified in the spec files.
|
||||
// A handshake is send at /Plugin.Activate, and plugins are expected to return
|
||||
// a Manifest with a list of of Docker subsystems which this plugin implements.
|
||||
//
|
||||
// In order to use a plugins, you can use the ``Get`` with the name of the
|
||||
// plugin and the subsystem it implements.
|
||||
//
|
||||
// plugin, err := plugins.Get("example", "VolumeDriver")
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("Error looking up volume plugin example: %v", err)
|
||||
// }
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotImplements is returned if the plugin does not implement the requested driver.
|
||||
ErrNotImplements = errors.New("Plugin does not implement the requested driver")
|
||||
)
|
||||
|
||||
type plugins struct {
|
||||
sync.Mutex
|
||||
plugins map[string]*Plugin
|
||||
}
|
||||
|
||||
var (
|
||||
storage = plugins{plugins: make(map[string]*Plugin)}
|
||||
extpointHandlers = make(map[string]func(string, *Client))
|
||||
)
|
||||
|
||||
// Manifest lists what a plugin implements.
|
||||
type Manifest struct {
|
||||
// List of subsystem the plugin implements.
|
||||
Implements []string
|
||||
}
|
||||
|
||||
// Plugin is the definition of a docker plugin.
|
||||
type Plugin struct {
|
||||
// Name of the plugin
|
||||
Name string `json:"-"`
|
||||
// Address of the plugin
|
||||
Addr string
|
||||
// TLS configuration of the plugin
|
||||
TLSConfig tlsconfig.Options
|
||||
// Client attached to the plugin
|
||||
Client *Client `json:"-"`
|
||||
// Manifest of the plugin (see above)
|
||||
Manifest *Manifest `json:"-"`
|
||||
|
||||
activatErr error
|
||||
activateOnce sync.Once
|
||||
}
|
||||
|
||||
func newLocalPlugin(name, addr string) *Plugin {
|
||||
return &Plugin{
|
||||
Name: name,
|
||||
Addr: addr,
|
||||
TLSConfig: tlsconfig.Options{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Plugin) activate() error {
|
||||
p.activateOnce.Do(func() {
|
||||
p.activatErr = p.activateWithLock()
|
||||
})
|
||||
return p.activatErr
|
||||
}
|
||||
|
||||
func (p *Plugin) activateWithLock() error {
|
||||
c, err := NewClient(p.Addr, p.TLSConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Client = c
|
||||
|
||||
m := new(Manifest)
|
||||
if err = p.Client.Call("Plugin.Activate", nil, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Manifest = m
|
||||
|
||||
for _, iface := range m.Implements {
|
||||
handler, handled := extpointHandlers[iface]
|
||||
if !handled {
|
||||
continue
|
||||
}
|
||||
handler(p.Name, p.Client)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) implements(kind string) bool {
|
||||
for _, driver := range p.Manifest.Implements {
|
||||
if driver == kind {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func load(name string) (*Plugin, error) {
|
||||
return loadWithRetry(name, true)
|
||||
}
|
||||
|
||||
func loadWithRetry(name string, retry bool) (*Plugin, error) {
|
||||
registry := newLocalRegistry()
|
||||
start := time.Now()
|
||||
|
||||
var retries int
|
||||
for {
|
||||
pl, err := registry.Plugin(name)
|
||||
if err != nil {
|
||||
if !retry {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeOff := backoff(retries)
|
||||
if abort(start, timeOff) {
|
||||
return nil, err
|
||||
}
|
||||
retries++
|
||||
logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
|
||||
time.Sleep(timeOff)
|
||||
continue
|
||||
}
|
||||
|
||||
storage.Lock()
|
||||
storage.plugins[name] = pl
|
||||
storage.Unlock()
|
||||
|
||||
err = pl.activate()
|
||||
|
||||
if err != nil {
|
||||
storage.Lock()
|
||||
delete(storage.plugins, name)
|
||||
storage.Unlock()
|
||||
}
|
||||
|
||||
return pl, err
|
||||
}
|
||||
}
|
||||
|
||||
func get(name string) (*Plugin, error) {
|
||||
storage.Lock()
|
||||
pl, ok := storage.plugins[name]
|
||||
storage.Unlock()
|
||||
if ok {
|
||||
return pl, pl.activate()
|
||||
}
|
||||
return load(name)
|
||||
}
|
||||
|
||||
// Get returns the plugin given the specified name and requested implementation.
|
||||
func Get(name, imp string) (*Plugin, error) {
|
||||
pl, err := get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pl.implements(imp) {
|
||||
logrus.Debugf("%s implements: %s", name, imp)
|
||||
return pl, nil
|
||||
}
|
||||
return nil, ErrNotImplements
|
||||
}
|
||||
|
||||
// Handle adds the specified function to the extpointHandlers.
|
||||
func Handle(iface string, fn func(string, *Client)) {
|
||||
extpointHandlers[iface] = fn
|
||||
}
|
||||
|
||||
// GetAll returns all the plugins for the specified implementation
|
||||
func GetAll(imp string) ([]*Plugin, error) {
|
||||
pluginNames, err := Scan()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type plLoad struct {
|
||||
pl *Plugin
|
||||
err error
|
||||
}
|
||||
|
||||
chPl := make(chan plLoad, len(pluginNames))
|
||||
for _, name := range pluginNames {
|
||||
go func(name string) {
|
||||
pl, err := loadWithRetry(name, false)
|
||||
chPl <- plLoad{pl, err}
|
||||
}(name)
|
||||
}
|
||||
|
||||
var out []*Plugin
|
||||
for i := 0; i < len(pluginNames); i++ {
|
||||
pl := <-chPl
|
||||
if pl.err != nil {
|
||||
logrus.Error(err)
|
||||
continue
|
||||
}
|
||||
if pl.pl.implements(imp) {
|
||||
out = append(out, pl.pl)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user