cmd/serve: add serve docker command (#5415)
Fixes #4750 Co-authored-by: Ivan Andreev <ivandeex@gmail.com>
This commit is contained in:
parent
221dfc3882
commit
daf449b5f2
15 changed files with 1864 additions and 0 deletions
175
cmd/serve/docker/api.go
Normal file
175
cmd/serve/docker/api.go
Normal file
|
@ -0,0 +1,175 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/rclone/rclone/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
contentType = "application/vnd.docker.plugins.v1.1+json"
|
||||
activatePath = "/Plugin.Activate"
|
||||
createPath = "/VolumeDriver.Create"
|
||||
getPath = "/VolumeDriver.Get"
|
||||
listPath = "/VolumeDriver.List"
|
||||
removePath = "/VolumeDriver.Remove"
|
||||
pathPath = "/VolumeDriver.Path"
|
||||
mountPath = "/VolumeDriver.Mount"
|
||||
unmountPath = "/VolumeDriver.Unmount"
|
||||
capsPath = "/VolumeDriver.Capabilities"
|
||||
)
|
||||
|
||||
// CreateRequest is the structure that docker's requests are deserialized to.
|
||||
type CreateRequest struct {
|
||||
Name string
|
||||
Options map[string]string `json:"Opts,omitempty"`
|
||||
}
|
||||
|
||||
// RemoveRequest structure for a volume remove request
|
||||
type RemoveRequest struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// MountRequest structure for a volume mount request
|
||||
type MountRequest struct {
|
||||
Name string
|
||||
ID string
|
||||
}
|
||||
|
||||
// MountResponse structure for a volume mount response
|
||||
type MountResponse struct {
|
||||
Mountpoint string
|
||||
}
|
||||
|
||||
// UnmountRequest structure for a volume unmount request
|
||||
type UnmountRequest struct {
|
||||
Name string
|
||||
ID string
|
||||
}
|
||||
|
||||
// PathRequest structure for a volume path request
|
||||
type PathRequest struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// PathResponse structure for a volume path response
|
||||
type PathResponse struct {
|
||||
Mountpoint string
|
||||
}
|
||||
|
||||
// GetRequest structure for a volume get request
|
||||
type GetRequest struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// GetResponse structure for a volume get response
|
||||
type GetResponse struct {
|
||||
Volume *VolInfo
|
||||
}
|
||||
|
||||
// ListResponse structure for a volume list response
|
||||
type ListResponse struct {
|
||||
Volumes []*VolInfo
|
||||
}
|
||||
|
||||
// CapabilitiesResponse structure for a volume capability response
|
||||
type CapabilitiesResponse struct {
|
||||
Capabilities Capability
|
||||
}
|
||||
|
||||
// Capability represents the list of capabilities a volume driver can return
|
||||
type Capability struct {
|
||||
Scope string
|
||||
}
|
||||
|
||||
// ErrorResponse is a formatted error message that docker can understand
|
||||
type ErrorResponse struct {
|
||||
Err string
|
||||
}
|
||||
|
||||
func newRouter(drv *Driver) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Post(activatePath, func(w http.ResponseWriter, r *http.Request) {
|
||||
res := map[string]interface{}{
|
||||
"Implements": []string{"VolumeDriver"},
|
||||
}
|
||||
encodeResponse(w, res, nil, activatePath)
|
||||
})
|
||||
r.Post(createPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
var req CreateRequest
|
||||
if decodeRequest(w, r, &req) {
|
||||
err := drv.Create(&req)
|
||||
encodeResponse(w, nil, err, createPath)
|
||||
}
|
||||
})
|
||||
r.Post(removePath, func(w http.ResponseWriter, r *http.Request) {
|
||||
var req RemoveRequest
|
||||
if decodeRequest(w, r, &req) {
|
||||
err := drv.Remove(&req)
|
||||
encodeResponse(w, nil, err, removePath)
|
||||
}
|
||||
})
|
||||
r.Post(mountPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
var req MountRequest
|
||||
if decodeRequest(w, r, &req) {
|
||||
res, err := drv.Mount(&req)
|
||||
encodeResponse(w, res, err, mountPath)
|
||||
}
|
||||
})
|
||||
r.Post(pathPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
var req PathRequest
|
||||
if decodeRequest(w, r, &req) {
|
||||
res, err := drv.Path(&req)
|
||||
encodeResponse(w, res, err, pathPath)
|
||||
}
|
||||
})
|
||||
r.Post(getPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
var req GetRequest
|
||||
if decodeRequest(w, r, &req) {
|
||||
res, err := drv.Get(&req)
|
||||
encodeResponse(w, res, err, getPath)
|
||||
}
|
||||
})
|
||||
r.Post(unmountPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
var req UnmountRequest
|
||||
if decodeRequest(w, r, &req) {
|
||||
err := drv.Unmount(&req)
|
||||
encodeResponse(w, nil, err, unmountPath)
|
||||
}
|
||||
})
|
||||
r.Post(listPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
res, err := drv.List()
|
||||
encodeResponse(w, res, err, listPath)
|
||||
})
|
||||
r.Post(capsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
res := &CapabilitiesResponse{
|
||||
Capabilities: Capability{Scope: pluginScope},
|
||||
}
|
||||
encodeResponse(w, res, nil, capsPath)
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func decodeRequest(w http.ResponseWriter, r *http.Request, req interface{}) bool {
|
||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func encodeResponse(w http.ResponseWriter, res interface{}, err error, path string) {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
if err != nil {
|
||||
fs.Debugf(path, "Request returned error: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
res = &ErrorResponse{Err: err.Error()}
|
||||
} else if res == nil {
|
||||
res = struct{}{}
|
||||
}
|
||||
if err = json.NewEncoder(w).Encode(res); err != nil {
|
||||
fs.Debugf(path, "Response encoding failed: %v", err)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue