diff options
author | Matthew Heon <mheon@redhat.com> | 2020-11-16 14:32:12 -0500 |
---|---|---|
committer | Matthew Heon <mheon@redhat.com> | 2020-12-01 12:56:55 -0500 |
commit | 594ac4a14658a90ad7bc5541dab6349a0c629a5c (patch) | |
tree | d43d561d74c6331166bca6f5101b7d0fd8d8bd1d /vendor/github.com/docker/go-plugins-helpers/sdk | |
parent | 429d9492f85faf4c3a595b9d7b2a38743a4b8e42 (diff) | |
download | podman-594ac4a14658a90ad7bc5541dab6349a0c629a5c.tar.gz podman-594ac4a14658a90ad7bc5541dab6349a0c629a5c.tar.bz2 podman-594ac4a14658a90ad7bc5541dab6349a0c629a5c.zip |
Add API for communicating with Docker volume plugins
Docker provides extensibility through a plugin system, of which
several types are available. This provides an initial library API
for communicating with one type of plugins, volume plugins.
Volume plugins allow for an external service to create and manage
a volume on Podman's behalf.
This does not integrate the plugin system into Libpod or Podman
yet; that will come in subsequent pull requests.
Signed-off-by: Matthew Heon <mheon@redhat.com>
Diffstat (limited to 'vendor/github.com/docker/go-plugins-helpers/sdk')
12 files changed, 444 insertions, 0 deletions
diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/encoder.go b/vendor/github.com/docker/go-plugins-helpers/sdk/encoder.go new file mode 100644 index 000000000..195812a44 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/encoder.go @@ -0,0 +1,37 @@ +package sdk + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +// DefaultContentTypeV1_1 is the default content type accepted and sent by the plugins. +const DefaultContentTypeV1_1 = "application/vnd.docker.plugins.v1.1+json" + +// DecodeRequest decodes an http request into a given structure. +func DecodeRequest(w http.ResponseWriter, r *http.Request, req interface{}) (err error) { + if err = json.NewDecoder(r.Body).Decode(req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + } + return +} + +// EncodeResponse encodes the given structure into an http response. +func EncodeResponse(w http.ResponseWriter, res interface{}, err bool) { + w.Header().Set("Content-Type", DefaultContentTypeV1_1) + if err { + w.WriteHeader(http.StatusInternalServerError) + } + json.NewEncoder(w).Encode(res) +} + +// StreamResponse streams a response object to the client +func StreamResponse(w http.ResponseWriter, data io.ReadCloser) { + w.Header().Set("Content-Type", DefaultContentTypeV1_1) + if _, err := copyBuf(w, data); err != nil { + fmt.Printf("ERROR in stream: %v\n", err) + } + data.Close() +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/handler.go b/vendor/github.com/docker/go-plugins-helpers/sdk/handler.go new file mode 100644 index 000000000..c0d042ed0 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/handler.go @@ -0,0 +1,88 @@ +package sdk + +import ( + "crypto/tls" + "fmt" + "net" + "net/http" + "os" +) + +const activatePath = "/Plugin.Activate" + +// Handler is the base to create plugin handlers. +// It initializes connections and sockets to listen to. +type Handler struct { + mux *http.ServeMux +} + +// NewHandler creates a new Handler with an http mux. +func NewHandler(manifest string) Handler { + mux := http.NewServeMux() + + mux.HandleFunc(activatePath, func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", DefaultContentTypeV1_1) + fmt.Fprintln(w, manifest) + }) + + return Handler{mux: mux} +} + +// Serve sets up the handler to serve requests on the passed in listener +func (h Handler) Serve(l net.Listener) error { + server := http.Server{ + Addr: l.Addr().String(), + Handler: h.mux, + } + return server.Serve(l) +} + +// ServeTCP makes the handler to listen for request in a given TCP address. +// It also writes the spec file in the right directory for docker to read. +// Due to constrains for running Docker in Docker on Windows, data-root directory +// of docker daemon must be provided. To get default directory, use +// WindowsDefaultDaemonRootDir() function. On Unix, this parameter is ignored. +func (h Handler) ServeTCP(pluginName, addr, daemonDir string, tlsConfig *tls.Config) error { + l, spec, err := newTCPListener(addr, pluginName, daemonDir, tlsConfig) + if err != nil { + return err + } + if spec != "" { + defer os.Remove(spec) + } + return h.Serve(l) +} + +// ServeUnix makes the handler to listen for requests in a unix socket. +// It also creates the socket file in the right directory for docker to read. +func (h Handler) ServeUnix(addr string, gid int) error { + l, spec, err := newUnixListener(addr, gid) + if err != nil { + return err + } + if spec != "" { + defer os.Remove(spec) + } + return h.Serve(l) +} + +// ServeWindows makes the handler to listen for request in a Windows named pipe. +// It also creates the spec file in the right directory for docker to read. +// Due to constrains for running Docker in Docker on Windows, data-root directory +// of docker daemon must be provided. To get default directory, use +// WindowsDefaultDaemonRootDir() function. On Unix, this parameter is ignored. +func (h Handler) ServeWindows(addr, pluginName, daemonDir string, pipeConfig *WindowsPipeConfig) error { + l, spec, err := newWindowsListener(addr, pluginName, daemonDir, pipeConfig) + if err != nil { + return err + } + if spec != "" { + defer os.Remove(spec) + } + return h.Serve(l) +} + +// HandleFunc registers a function to handle a request path with. +func (h Handler) HandleFunc(path string, fn func(w http.ResponseWriter, r *http.Request)) { + h.mux.HandleFunc(path, fn) +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/pool.go b/vendor/github.com/docker/go-plugins-helpers/sdk/pool.go new file mode 100644 index 000000000..316775973 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/pool.go @@ -0,0 +1,18 @@ +package sdk + +import ( + "io" + "sync" +) + +const buffer32K = 32 * 1024 + +var buffer32KPool = &sync.Pool{New: func() interface{} { return make([]byte, buffer32K) }} + +// copyBuf uses a shared buffer pool with io.CopyBuffer +func copyBuf(w io.Writer, r io.Reader) (int64, error) { + buf := buffer32KPool.Get().([]byte) + written, err := io.CopyBuffer(w, r, buf) + buffer32KPool.Put(buf) + return written, err +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/spec_file_generator.go b/vendor/github.com/docker/go-plugins-helpers/sdk/spec_file_generator.go new file mode 100644 index 000000000..bc8cfc644 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/spec_file_generator.go @@ -0,0 +1,58 @@ +package sdk + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +type protocol string + +const ( + protoTCP protocol = "tcp" + protoNamedPipe protocol = "npipe" +) + +// PluginSpecDir returns plugin spec dir in relation to daemon root directory. +func PluginSpecDir(daemonRoot string) string { + return ([]string{filepath.Join(daemonRoot, "plugins")})[0] +} + +// WindowsDefaultDaemonRootDir returns default data directory of docker daemon on Windows. +func WindowsDefaultDaemonRootDir() string { + return filepath.Join(os.Getenv("programdata"), "docker") +} + +func createPluginSpecDirWindows(name, address, daemonRoot string) (string, error) { + _, err := os.Stat(daemonRoot) + if os.IsNotExist(err) { + return "", fmt.Errorf("Deamon root directory must already exist: %s", err) + } + + pluginSpecDir := PluginSpecDir(daemonRoot) + + if err := windowsCreateDirectoryWithACL(pluginSpecDir); err != nil { + return "", err + } + return pluginSpecDir, nil +} + +func createPluginSpecDirUnix(name, address string) (string, error) { + pluginSpecDir := PluginSpecDir("/etc/docker") + if err := os.MkdirAll(pluginSpecDir, 0755); err != nil { + return "", err + } + return pluginSpecDir, nil +} + +func writeSpecFile(name, address, pluginSpecDir string, proto protocol) (string, error) { + specFileDir := filepath.Join(pluginSpecDir, name+".spec") + + url := string(proto) + "://" + address + if err := ioutil.WriteFile(specFileDir, []byte(url), 0644); err != nil { + return "", err + } + + return specFileDir, nil +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/tcp_listener.go b/vendor/github.com/docker/go-plugins-helpers/sdk/tcp_listener.go new file mode 100644 index 000000000..bad85f7fd --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/tcp_listener.go @@ -0,0 +1,34 @@ +package sdk + +import ( + "crypto/tls" + "net" + "runtime" + + "github.com/docker/go-connections/sockets" +) + +func newTCPListener(address, pluginName, daemonDir string, tlsConfig *tls.Config) (net.Listener, string, error) { + listener, err := sockets.NewTCPSocket(address, tlsConfig) + if err != nil { + return nil, "", err + } + + addr := listener.Addr().String() + + var specDir string + if runtime.GOOS == "windows" { + specDir, err = createPluginSpecDirWindows(pluginName, addr, daemonDir) + } else { + specDir, err = createPluginSpecDirUnix(pluginName, addr) + } + if err != nil { + return nil, "", err + } + + specFile, err := writeSpecFile(pluginName, addr, specDir, protoTCP) + if err != nil { + return nil, "", err + } + return listener, specFile, nil +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener.go b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener.go new file mode 100644 index 000000000..54b9a6d31 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener.go @@ -0,0 +1,35 @@ +// +build linux freebsd + +package sdk + +import ( + "net" + "os" + "path/filepath" + + "github.com/docker/go-connections/sockets" +) + +const pluginSockDir = "/run/docker/plugins" + +func newUnixListener(pluginName string, gid int) (net.Listener, string, error) { + path, err := fullSocketAddress(pluginName) + if err != nil { + return nil, "", err + } + listener, err := sockets.NewUnixSocket(path, gid) + if err != nil { + return nil, "", err + } + return listener, path, nil +} + +func fullSocketAddress(address string) (string, error) { + if err := os.MkdirAll(pluginSockDir, 0755); err != nil { + return "", err + } + if filepath.IsAbs(address) { + return address, nil + } + return filepath.Join(pluginSockDir, address+".sock"), nil +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_nosystemd.go b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_nosystemd.go new file mode 100644 index 000000000..a798b8722 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_nosystemd.go @@ -0,0 +1,10 @@ +// +build linux freebsd +// +build nosystemd + +package sdk + +import "net" + +func setupSocketActivation() (net.Listener, error) { + return nil, nil +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_systemd.go b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_systemd.go new file mode 100644 index 000000000..5d5d8f427 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_systemd.go @@ -0,0 +1,45 @@ +// +build linux freebsd +// +build !nosystemd + +package sdk + +import ( + "fmt" + "net" + "os" + + "github.com/coreos/go-systemd/activation" +) + +// isRunningSystemd checks whether the host was booted with systemd as its init +// system. This functions similarly to systemd's `sd_booted(3)`: internally, it +// checks whether /run/systemd/system/ exists and is a directory. +// http://www.freedesktop.org/software/systemd/man/sd_booted.html +// +// Copied from github.com/coreos/go-systemd/util.IsRunningSystemd +func isRunningSystemd() bool { + fi, err := os.Lstat("/run/systemd/system") + if err != nil { + return false + } + return fi.IsDir() +} + +func setupSocketActivation() (net.Listener, error) { + if !isRunningSystemd() { + return nil, nil + } + listenFds := activation.Files(false) + if len(listenFds) > 1 { + return nil, fmt.Errorf("expected only one socket from systemd, got %d", len(listenFds)) + } + var listener net.Listener + if len(listenFds) == 1 { + l, err := net.FileListener(listenFds[0]) + if err != nil { + return nil, err + } + listener = l + } + return listener, nil +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_unsupported.go b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_unsupported.go new file mode 100644 index 000000000..344cf751b --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/unix_listener_unsupported.go @@ -0,0 +1,16 @@ +// +build !linux,!freebsd + +package sdk + +import ( + "errors" + "net" +) + +var ( + errOnlySupportedOnLinuxAndFreeBSD = errors.New("unix socket creation is only supported on Linux and FreeBSD") +) + +func newUnixListener(pluginName string, gid int) (net.Listener, string, error) { + return nil, "", errOnlySupportedOnLinuxAndFreeBSD +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/windows_listener.go b/vendor/github.com/docker/go-plugins-helpers/sdk/windows_listener.go new file mode 100644 index 000000000..b5deaba6d --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/windows_listener.go @@ -0,0 +1,70 @@ +// +build windows + +package sdk + +import ( + "net" + "os" + "syscall" + "unsafe" + + "github.com/Microsoft/go-winio" +) + +// Named pipes use Windows Security Descriptor Definition Language to define ACL. Following are +// some useful definitions. +const ( + // This will set permissions for everyone to have full access + AllowEveryone = "S:(ML;;NW;;;LW)D:(A;;0x12019f;;;WD)" + + // This will set permissions for Service, System, Adminstrator group and account to have full access + AllowServiceSystemAdmin = "D:(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;FA;;;LA)(A;ID;FA;;;LS)" +) + +func newWindowsListener(address, pluginName, daemonRoot string, pipeConfig *WindowsPipeConfig) (net.Listener, string, error) { + winioPipeConfig := winio.PipeConfig{ + SecurityDescriptor: pipeConfig.SecurityDescriptor, + InputBufferSize: pipeConfig.InBufferSize, + OutputBufferSize: pipeConfig.OutBufferSize, + } + listener, err := winio.ListenPipe(address, &winioPipeConfig) + if err != nil { + return nil, "", err + } + + addr := listener.Addr().String() + + specDir, err := createPluginSpecDirWindows(pluginName, addr, daemonRoot) + if err != nil { + return nil, "", err + } + + spec, err := writeSpecFile(pluginName, addr, specDir, protoNamedPipe) + if err != nil { + return nil, "", err + } + return listener, spec, nil +} + +func windowsCreateDirectoryWithACL(name string) error { + sa := syscall.SecurityAttributes{Length: 0} + sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" + sd, err := winio.SddlToSecurityDescriptor(sddl) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + + namep, err := syscall.UTF16PtrFromString(name) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + + e := syscall.CreateDirectory(namep, &sa) + if e != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: e} + } + return nil +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/windows_listener_unsupported.go b/vendor/github.com/docker/go-plugins-helpers/sdk/windows_listener_unsupported.go new file mode 100644 index 000000000..0f5e113c1 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/windows_listener_unsupported.go @@ -0,0 +1,20 @@ +// +build !windows + +package sdk + +import ( + "errors" + "net" +) + +var ( + errOnlySupportedOnWindows = errors.New("named pipe creation is only supported on Windows") +) + +func newWindowsListener(address, pluginName, daemonRoot string, pipeConfig *WindowsPipeConfig) (net.Listener, string, error) { + return nil, "", errOnlySupportedOnWindows +} + +func windowsCreateDirectoryWithACL(name string) error { + return nil +} diff --git a/vendor/github.com/docker/go-plugins-helpers/sdk/windows_pipe_config.go b/vendor/github.com/docker/go-plugins-helpers/sdk/windows_pipe_config.go new file mode 100644 index 000000000..256fa3d67 --- /dev/null +++ b/vendor/github.com/docker/go-plugins-helpers/sdk/windows_pipe_config.go @@ -0,0 +1,13 @@ +package sdk + +// WindowsPipeConfig is a helper structure for configuring named pipe parameters on Windows. +type WindowsPipeConfig struct { + // SecurityDescriptor contains a Windows security descriptor in SDDL format. + SecurityDescriptor string + + // InBufferSize in bytes. + InBufferSize int32 + + // OutBufferSize in bytes. + OutBufferSize int32 +} |