mirror of
https://github.com/clearlinux/docker.git
synced 2026-09-05 13:11:30 +00:00
Factored out docker/rcli (remote cli protocol), docker/fake (mocking utilities) and docker/future (real utilities which don't yet fit in the core)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package rcli
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
// Use this key to encode an RPC call into an URL,
|
||||
// eg. domain.tld/path/to/method?q=get_user&q=gordon
|
||||
const ARG_URL_KEY = "q"
|
||||
|
||||
func URLToCall(u *url.URL) (method string, args []string) {
|
||||
return path.Base(u.Path), u.Query()[ARG_URL_KEY]
|
||||
}
|
||||
|
||||
|
||||
func ListenAndServeHTTP(addr string, service Service) error {
|
||||
return http.ListenAndServe(addr, http.HandlerFunc(
|
||||
func (w http.ResponseWriter, r *http.Request) {
|
||||
cmd, args := URLToCall(r.URL)
|
||||
if err := call(service, r.Body, &AutoFlush{w}, append([]string{cmd}, args...)...); err != nil {
|
||||
fmt.Fprintf(w, "Error: " + err.Error() + "\n")
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
type AutoFlush struct {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
func (w *AutoFlush) Write(data []byte) (int, error) {
|
||||
ret, err := w.ResponseWriter.Write(data)
|
||||
if flusher, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package rcli
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"log"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"bufio"
|
||||
)
|
||||
|
||||
func CallTCP(addr string, args ...string) (io.ReadWriteCloser, error) {
|
||||
cmd, err := json.Marshal(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := fmt.Fprintln(conn, string(cmd)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func ListenAndServeTCP(addr string, service Service) error {
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer listener.Close()
|
||||
for {
|
||||
if conn, err := listener.Accept(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
go func() {
|
||||
if err := Serve(conn, service); err != nil {
|
||||
log.Printf("Error: " + err.Error() + "\n")
|
||||
fmt.Fprintf(conn, "Error: " + err.Error() + "\n")
|
||||
}
|
||||
conn.Close()
|
||||
}()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Serve(conn io.ReadWriter, service Service) error {
|
||||
r := bufio.NewReader(conn)
|
||||
var args []string
|
||||
if line, err := r.ReadString('\n'); err != nil {
|
||||
return err
|
||||
} else if err := json.Unmarshal([]byte(line), &args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return call(service, ioutil.NopCloser(r), conn, args...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package rcli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"flag"
|
||||
"log"
|
||||
"strings"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
Name() string
|
||||
Help() string
|
||||
}
|
||||
|
||||
type Cmd func(io.ReadCloser, io.Writer, ...string) error
|
||||
type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error
|
||||
|
||||
|
||||
func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||
flags := flag.NewFlagSet("main", flag.ContinueOnError)
|
||||
flags.SetOutput(stdout)
|
||||
flags.Usage = func() { stdout.Write([]byte(service.Help())) }
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := flags.Arg(0)
|
||||
log.Printf("%s\n", strings.Join(append(append([]string{service.Name()}, cmd), flags.Args()[1:]...), " "))
|
||||
if cmd == "" {
|
||||
cmd = "help"
|
||||
}
|
||||
method := getMethod(service, cmd)
|
||||
if method != nil {
|
||||
return method(stdin, stdout, args[1:]...)
|
||||
}
|
||||
return errors.New("No such command: " + cmd)
|
||||
}
|
||||
|
||||
func getMethod(service Service, name string) Cmd {
|
||||
if name == "help" {
|
||||
return func(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||
if len(args) == 0 {
|
||||
stdout.Write([]byte(service.Help()))
|
||||
} else {
|
||||
if method := getMethod(service, args[0]); method == nil {
|
||||
return errors.New("No such command: " + args[0])
|
||||
} else {
|
||||
method(stdin, stdout, "--help")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
methodName := "Cmd"+strings.ToUpper(name[:1])+strings.ToLower(name[1:])
|
||||
method, exists := reflect.TypeOf(service).MethodByName(methodName)
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
return func(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||
ret := method.Func.CallSlice([]reflect.Value{
|
||||
reflect.ValueOf(service),
|
||||
reflect.ValueOf(stdin),
|
||||
reflect.ValueOf(stdout),
|
||||
reflect.ValueOf(args),
|
||||
})[0].Interface()
|
||||
if ret == nil {
|
||||
return nil
|
||||
}
|
||||
return ret.(error)
|
||||
}
|
||||
}
|
||||
|
||||
func Subcmd(output io.Writer, name, signature, description string) *flag.FlagSet {
|
||||
flags := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||
flags.SetOutput(output)
|
||||
flags.Usage = func() {
|
||||
fmt.Fprintf(output, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
|
||||
flags.PrintDefaults()
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user