mirror of https://github.com/docker/docs.git
Merge pull request #13835 from cpuguy83/gen-prc
generate plugin clients via template
This commit is contained in:
commit
806b3fa145
|
@ -0,0 +1,35 @@
|
||||||
|
package foo
|
||||||
|
|
||||||
|
type wobble struct {
|
||||||
|
Some string
|
||||||
|
Val string
|
||||||
|
Inception *wobble
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooer interface{}
|
||||||
|
|
||||||
|
type Fooer2 interface {
|
||||||
|
Foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooer4 interface {
|
||||||
|
Foo() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar interface {
|
||||||
|
Boo(a string, b string) (s string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooer5 interface {
|
||||||
|
Foo()
|
||||||
|
Bar
|
||||||
|
}
|
|
@ -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:]
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 strings.ToLower(a.Name) + " " + strings.ToLower(a.ArgType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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 }}
|
||||||
|
`))
|
|
@ -1,20 +1,23 @@
|
||||||
|
//go:generate pluginrpc-gen -i $GOFILE -o proxy.go -type VolumeDriver -name VolumeDriver
|
||||||
|
|
||||||
package volumedrivers
|
package volumedrivers
|
||||||
|
|
||||||
import "github.com/docker/docker/volume"
|
import "github.com/docker/docker/volume"
|
||||||
|
|
||||||
type client interface {
|
|
||||||
Call(string, interface{}, interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVolumeDriver(name string, c client) volume.Driver {
|
func NewVolumeDriver(name string, c client) volume.Driver {
|
||||||
proxy := &volumeDriverProxy{c}
|
proxy := &volumeDriverProxy{c}
|
||||||
return &volumeDriverAdapter{name, proxy}
|
return &volumeDriverAdapter{name, proxy}
|
||||||
}
|
}
|
||||||
|
|
||||||
type VolumeDriver interface {
|
type VolumeDriver interface {
|
||||||
|
// Create a volume with the given name
|
||||||
Create(name string) (err error)
|
Create(name string) (err error)
|
||||||
|
// Remove the volume with the given name
|
||||||
Remove(name string) (err error)
|
Remove(name string) (err error)
|
||||||
|
// Get the mountpoint of the given volume
|
||||||
Path(name string) (mountpoint string, err error)
|
Path(name string) (mountpoint string, err error)
|
||||||
|
// Mount the given volume and return the mountpoint
|
||||||
Mount(name string) (mountpoint string, err error)
|
Mount(name string) (mountpoint string, err error)
|
||||||
|
// Unmount the given volume
|
||||||
Unmount(name string) (err error)
|
Unmount(name string) (err error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +1,149 @@
|
||||||
|
// generated code - DO NOT EDIT
|
||||||
|
|
||||||
package volumedrivers
|
package volumedrivers
|
||||||
|
|
||||||
import "fmt"
|
import "errors"
|
||||||
|
|
||||||
// currently created by hand. generation tool would generate this like:
|
type client interface {
|
||||||
// $ rpc-gen volume/drivers/api.go VolumeDriver > volume/drivers/proxy.go
|
Call(string, interface{}, interface{}) error
|
||||||
|
|
||||||
type volumeDriverRequest struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type volumeDriverResponse struct {
|
|
||||||
Mountpoint string `json:",omitempty"`
|
|
||||||
Err string `json:",omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type volumeDriverProxy struct {
|
type volumeDriverProxy struct {
|
||||||
c client
|
client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *volumeDriverProxy) Create(name string) error {
|
type volumeDriverProxyCreateRequest struct {
|
||||||
args := volumeDriverRequest{name}
|
Name string
|
||||||
var ret volumeDriverResponse
|
|
||||||
err := pp.c.Call("VolumeDriver.Create", args, &ret)
|
|
||||||
if err != nil {
|
|
||||||
return pp.fmtError(name, err.Error())
|
|
||||||
}
|
|
||||||
return pp.fmtError(name, ret.Err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *volumeDriverProxy) Remove(name string) error {
|
type volumeDriverProxyCreateResponse struct {
|
||||||
args := volumeDriverRequest{name}
|
Err string
|
||||||
var ret volumeDriverResponse
|
|
||||||
err := pp.c.Call("VolumeDriver.Remove", args, &ret)
|
|
||||||
if err != nil {
|
|
||||||
return pp.fmtError(name, err.Error())
|
|
||||||
}
|
|
||||||
return pp.fmtError(name, ret.Err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *volumeDriverProxy) Path(name string) (string, error) {
|
func (pp *volumeDriverProxy) Create(name string) (err error) {
|
||||||
args := volumeDriverRequest{name}
|
var (
|
||||||
var ret volumeDriverResponse
|
req volumeDriverProxyCreateRequest
|
||||||
if err := pp.c.Call("VolumeDriver.Path", args, &ret); err != nil {
|
ret volumeDriverProxyCreateResponse
|
||||||
return "", pp.fmtError(name, err.Error())
|
)
|
||||||
}
|
|
||||||
return ret.Mountpoint, pp.fmtError(name, ret.Err)
|
req.Name = name
|
||||||
|
if err = pp.Call("VolumeDriver.Create", req, &ret); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *volumeDriverProxy) Mount(name string) (string, error) {
|
if ret.Err != "" {
|
||||||
args := volumeDriverRequest{name}
|
err = errors.New(ret.Err)
|
||||||
var ret volumeDriverResponse
|
|
||||||
if err := pp.c.Call("VolumeDriver.Mount", args, &ret); err != nil {
|
|
||||||
return "", pp.fmtError(name, err.Error())
|
|
||||||
}
|
|
||||||
return ret.Mountpoint, pp.fmtError(name, ret.Err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *volumeDriverProxy) Unmount(name string) error {
|
return
|
||||||
args := volumeDriverRequest{name}
|
|
||||||
var ret volumeDriverResponse
|
|
||||||
err := pp.c.Call("VolumeDriver.Unmount", args, &ret)
|
|
||||||
if err != nil {
|
|
||||||
return pp.fmtError(name, err.Error())
|
|
||||||
}
|
|
||||||
return pp.fmtError(name, ret.Err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *volumeDriverProxy) fmtError(name string, err string) error {
|
type volumeDriverProxyRemoveRequest struct {
|
||||||
if len(err) == 0 {
|
Name string
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("External volume driver request failed for %s: %v", name, err)
|
|
||||||
|
type volumeDriverProxyRemoveResponse struct {
|
||||||
|
Err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *volumeDriverProxy) Remove(name string) (err error) {
|
||||||
|
var (
|
||||||
|
req volumeDriverProxyRemoveRequest
|
||||||
|
ret volumeDriverProxyRemoveResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
req.Name = name
|
||||||
|
if err = pp.Call("VolumeDriver.Remove", req, &ret); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.Err != "" {
|
||||||
|
err = errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeDriverProxyPathRequest struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeDriverProxyPathResponse struct {
|
||||||
|
Mountpoint string
|
||||||
|
Err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *volumeDriverProxy) Path(name string) (mountpoint string, err error) {
|
||||||
|
var (
|
||||||
|
req volumeDriverProxyPathRequest
|
||||||
|
ret volumeDriverProxyPathResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
req.Name = name
|
||||||
|
if err = pp.Call("VolumeDriver.Path", req, &ret); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mountpoint = ret.Mountpoint
|
||||||
|
|
||||||
|
if ret.Err != "" {
|
||||||
|
err = errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeDriverProxyMountRequest struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeDriverProxyMountResponse struct {
|
||||||
|
Mountpoint string
|
||||||
|
Err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *volumeDriverProxy) Mount(name string) (mountpoint string, err error) {
|
||||||
|
var (
|
||||||
|
req volumeDriverProxyMountRequest
|
||||||
|
ret volumeDriverProxyMountResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
req.Name = name
|
||||||
|
if err = pp.Call("VolumeDriver.Mount", req, &ret); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mountpoint = ret.Mountpoint
|
||||||
|
|
||||||
|
if ret.Err != "" {
|
||||||
|
err = errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeDriverProxyUnmountRequest struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeDriverProxyUnmountResponse struct {
|
||||||
|
Err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *volumeDriverProxy) Unmount(name string) (err error) {
|
||||||
|
var (
|
||||||
|
req volumeDriverProxyUnmountRequest
|
||||||
|
ret volumeDriverProxyUnmountResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
req.Name = name
|
||||||
|
if err = pp.Call("VolumeDriver.Unmount", req, &ret); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.Err != "" {
|
||||||
|
err = errors.New(ret.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue