github.com/artpar/rclone@v1.67.3/cmd/serve/docker/api.go (about) 1 package docker 2 3 import ( 4 "encoding/json" 5 "net/http" 6 7 "github.com/artpar/rclone/fs" 8 "github.com/go-chi/chi/v5" 9 ) 10 11 const ( 12 contentType = "application/vnd.docker.plugins.v1.1+json" 13 activatePath = "/Plugin.Activate" 14 createPath = "/VolumeDriver.Create" 15 getPath = "/VolumeDriver.Get" 16 listPath = "/VolumeDriver.List" 17 removePath = "/VolumeDriver.Remove" 18 pathPath = "/VolumeDriver.Path" 19 mountPath = "/VolumeDriver.Mount" 20 unmountPath = "/VolumeDriver.Unmount" 21 capsPath = "/VolumeDriver.Capabilities" 22 ) 23 24 // CreateRequest is the structure that docker's requests are deserialized to. 25 type CreateRequest struct { 26 Name string 27 Options map[string]string `json:"Opts,omitempty"` 28 } 29 30 // RemoveRequest structure for a volume remove request 31 type RemoveRequest struct { 32 Name string 33 } 34 35 // MountRequest structure for a volume mount request 36 type MountRequest struct { 37 Name string 38 ID string 39 } 40 41 // MountResponse structure for a volume mount response 42 type MountResponse struct { 43 Mountpoint string 44 } 45 46 // UnmountRequest structure for a volume unmount request 47 type UnmountRequest struct { 48 Name string 49 ID string 50 } 51 52 // PathRequest structure for a volume path request 53 type PathRequest struct { 54 Name string 55 } 56 57 // PathResponse structure for a volume path response 58 type PathResponse struct { 59 Mountpoint string 60 } 61 62 // GetRequest structure for a volume get request 63 type GetRequest struct { 64 Name string 65 } 66 67 // GetResponse structure for a volume get response 68 type GetResponse struct { 69 Volume *VolInfo 70 } 71 72 // ListResponse structure for a volume list response 73 type ListResponse struct { 74 Volumes []*VolInfo 75 } 76 77 // CapabilitiesResponse structure for a volume capability response 78 type CapabilitiesResponse struct { 79 Capabilities Capability 80 } 81 82 // Capability represents the list of capabilities a volume driver can return 83 type Capability struct { 84 Scope string 85 } 86 87 // ErrorResponse is a formatted error message that docker can understand 88 type ErrorResponse struct { 89 Err string 90 } 91 92 func newRouter(drv *Driver) http.Handler { 93 r := chi.NewRouter() 94 r.Post(activatePath, func(w http.ResponseWriter, r *http.Request) { 95 res := map[string]interface{}{ 96 "Implements": []string{"VolumeDriver"}, 97 } 98 encodeResponse(w, res, nil, activatePath) 99 }) 100 r.Post(createPath, func(w http.ResponseWriter, r *http.Request) { 101 var req CreateRequest 102 if decodeRequest(w, r, &req) { 103 err := drv.Create(&req) 104 encodeResponse(w, nil, err, createPath) 105 } 106 }) 107 r.Post(removePath, func(w http.ResponseWriter, r *http.Request) { 108 var req RemoveRequest 109 if decodeRequest(w, r, &req) { 110 err := drv.Remove(&req) 111 encodeResponse(w, nil, err, removePath) 112 } 113 }) 114 r.Post(mountPath, func(w http.ResponseWriter, r *http.Request) { 115 var req MountRequest 116 if decodeRequest(w, r, &req) { 117 res, err := drv.Mount(&req) 118 encodeResponse(w, res, err, mountPath) 119 } 120 }) 121 r.Post(pathPath, func(w http.ResponseWriter, r *http.Request) { 122 var req PathRequest 123 if decodeRequest(w, r, &req) { 124 res, err := drv.Path(&req) 125 encodeResponse(w, res, err, pathPath) 126 } 127 }) 128 r.Post(getPath, func(w http.ResponseWriter, r *http.Request) { 129 var req GetRequest 130 if decodeRequest(w, r, &req) { 131 res, err := drv.Get(&req) 132 encodeResponse(w, res, err, getPath) 133 } 134 }) 135 r.Post(unmountPath, func(w http.ResponseWriter, r *http.Request) { 136 var req UnmountRequest 137 if decodeRequest(w, r, &req) { 138 err := drv.Unmount(&req) 139 encodeResponse(w, nil, err, unmountPath) 140 } 141 }) 142 r.Post(listPath, func(w http.ResponseWriter, r *http.Request) { 143 res, err := drv.List() 144 encodeResponse(w, res, err, listPath) 145 }) 146 r.Post(capsPath, func(w http.ResponseWriter, r *http.Request) { 147 res := &CapabilitiesResponse{ 148 Capabilities: Capability{Scope: pluginScope}, 149 } 150 encodeResponse(w, res, nil, capsPath) 151 }) 152 return r 153 } 154 155 func decodeRequest(w http.ResponseWriter, r *http.Request, req interface{}) bool { 156 if err := json.NewDecoder(r.Body).Decode(req); err != nil { 157 http.Error(w, err.Error(), http.StatusBadRequest) 158 return false 159 } 160 return true 161 } 162 163 func encodeResponse(w http.ResponseWriter, res interface{}, err error, path string) { 164 w.Header().Set("Content-Type", contentType) 165 if err != nil { 166 fs.Debugf(path, "Request returned error: %v", err) 167 w.WriteHeader(http.StatusInternalServerError) 168 res = &ErrorResponse{Err: err.Error()} 169 } else if res == nil { 170 res = struct{}{} 171 } 172 if err = json.NewEncoder(w).Encode(res); err != nil { 173 fs.Debugf(path, "Response encoding failed: %v", err) 174 } 175 }