mirror of
https://github.com/rclone/rclone.git
synced 2024-12-23 04:10:31 +08:00
176 lines
4.4 KiB
Go
176 lines
4.4 KiB
Go
|
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)
|
||
|
}
|
||
|
}
|