Tizen_4.0 base
[platform/upstream/docker-engine.git] / api / server / router / plugin / plugin_routes.go
1 package plugin
2
3 import (
4         "encoding/base64"
5         "encoding/json"
6         "net/http"
7         "strconv"
8         "strings"
9
10         "github.com/docker/distribution/reference"
11         "github.com/docker/docker/api/server/httputils"
12         "github.com/docker/docker/api/types"
13         "github.com/docker/docker/api/types/filters"
14         "github.com/docker/docker/pkg/ioutils"
15         "github.com/docker/docker/pkg/streamformatter"
16         "github.com/pkg/errors"
17         "golang.org/x/net/context"
18 )
19
20 func parseHeaders(headers http.Header) (map[string][]string, *types.AuthConfig) {
21
22         metaHeaders := map[string][]string{}
23         for k, v := range headers {
24                 if strings.HasPrefix(k, "X-Meta-") {
25                         metaHeaders[k] = v
26                 }
27         }
28
29         // Get X-Registry-Auth
30         authEncoded := headers.Get("X-Registry-Auth")
31         authConfig := &types.AuthConfig{}
32         if authEncoded != "" {
33                 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
34                 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
35                         authConfig = &types.AuthConfig{}
36                 }
37         }
38
39         return metaHeaders, authConfig
40 }
41
42 // parseRemoteRef parses the remote reference into a reference.Named
43 // returning the tag associated with the reference. In the case the
44 // given reference string includes both digest and tag, the returned
45 // reference will have the digest without the tag, but the tag will
46 // be returned.
47 func parseRemoteRef(remote string) (reference.Named, string, error) {
48         // Parse remote reference, supporting remotes with name and tag
49         remoteRef, err := reference.ParseNormalizedNamed(remote)
50         if err != nil {
51                 return nil, "", err
52         }
53
54         type canonicalWithTag interface {
55                 reference.Canonical
56                 Tag() string
57         }
58
59         if canonical, ok := remoteRef.(canonicalWithTag); ok {
60                 remoteRef, err = reference.WithDigest(reference.TrimNamed(remoteRef), canonical.Digest())
61                 if err != nil {
62                         return nil, "", err
63                 }
64                 return remoteRef, canonical.Tag(), nil
65         }
66
67         remoteRef = reference.TagNameOnly(remoteRef)
68
69         return remoteRef, "", nil
70 }
71
72 func (pr *pluginRouter) getPrivileges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
73         if err := httputils.ParseForm(r); err != nil {
74                 return err
75         }
76
77         metaHeaders, authConfig := parseHeaders(r.Header)
78
79         ref, _, err := parseRemoteRef(r.FormValue("remote"))
80         if err != nil {
81                 return err
82         }
83
84         privileges, err := pr.backend.Privileges(ctx, ref, metaHeaders, authConfig)
85         if err != nil {
86                 return err
87         }
88         return httputils.WriteJSON(w, http.StatusOK, privileges)
89 }
90
91 func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
92         if err := httputils.ParseForm(r); err != nil {
93                 return errors.Wrap(err, "failed to parse form")
94         }
95
96         var privileges types.PluginPrivileges
97         dec := json.NewDecoder(r.Body)
98         if err := dec.Decode(&privileges); err != nil {
99                 return errors.Wrap(err, "failed to parse privileges")
100         }
101         if dec.More() {
102                 return errors.New("invalid privileges")
103         }
104
105         metaHeaders, authConfig := parseHeaders(r.Header)
106         ref, tag, err := parseRemoteRef(r.FormValue("remote"))
107         if err != nil {
108                 return err
109         }
110
111         name, err := getName(ref, tag, vars["name"])
112         if err != nil {
113                 return err
114         }
115         w.Header().Set("Docker-Plugin-Name", name)
116
117         w.Header().Set("Content-Type", "application/json")
118         output := ioutils.NewWriteFlusher(w)
119
120         if err := pr.backend.Upgrade(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
121                 if !output.Flushed() {
122                         return err
123                 }
124                 output.Write(streamformatter.FormatError(err))
125         }
126
127         return nil
128 }
129
130 func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
131         if err := httputils.ParseForm(r); err != nil {
132                 return errors.Wrap(err, "failed to parse form")
133         }
134
135         var privileges types.PluginPrivileges
136         dec := json.NewDecoder(r.Body)
137         if err := dec.Decode(&privileges); err != nil {
138                 return errors.Wrap(err, "failed to parse privileges")
139         }
140         if dec.More() {
141                 return errors.New("invalid privileges")
142         }
143
144         metaHeaders, authConfig := parseHeaders(r.Header)
145         ref, tag, err := parseRemoteRef(r.FormValue("remote"))
146         if err != nil {
147                 return err
148         }
149
150         name, err := getName(ref, tag, r.FormValue("name"))
151         if err != nil {
152                 return err
153         }
154         w.Header().Set("Docker-Plugin-Name", name)
155
156         w.Header().Set("Content-Type", "application/json")
157         output := ioutils.NewWriteFlusher(w)
158
159         if err := pr.backend.Pull(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
160                 if !output.Flushed() {
161                         return err
162                 }
163                 output.Write(streamformatter.FormatError(err))
164         }
165
166         return nil
167 }
168
169 func getName(ref reference.Named, tag, name string) (string, error) {
170         if name == "" {
171                 if _, ok := ref.(reference.Canonical); ok {
172                         trimmed := reference.TrimNamed(ref)
173                         if tag != "" {
174                                 nt, err := reference.WithTag(trimmed, tag)
175                                 if err != nil {
176                                         return "", err
177                                 }
178                                 name = reference.FamiliarString(nt)
179                         } else {
180                                 name = reference.FamiliarString(reference.TagNameOnly(trimmed))
181                         }
182                 } else {
183                         name = reference.FamiliarString(ref)
184                 }
185         } else {
186                 localRef, err := reference.ParseNormalizedNamed(name)
187                 if err != nil {
188                         return "", err
189                 }
190                 if _, ok := localRef.(reference.Canonical); ok {
191                         return "", errors.New("cannot use digest in plugin tag")
192                 }
193                 if reference.IsNameOnly(localRef) {
194                         // TODO: log change in name to out stream
195                         name = reference.FamiliarString(reference.TagNameOnly(localRef))
196                 }
197         }
198         return name, nil
199 }
200
201 func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
202         if err := httputils.ParseForm(r); err != nil {
203                 return err
204         }
205
206         options := &types.PluginCreateOptions{
207                 RepoName: r.FormValue("name")}
208
209         if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil {
210                 return err
211         }
212         //TODO: send progress bar
213         w.WriteHeader(http.StatusNoContent)
214         return nil
215 }
216
217 func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
218         if err := httputils.ParseForm(r); err != nil {
219                 return err
220         }
221
222         name := vars["name"]
223         timeout, err := strconv.Atoi(r.Form.Get("timeout"))
224         if err != nil {
225                 return err
226         }
227         config := &types.PluginEnableConfig{Timeout: timeout}
228
229         return pr.backend.Enable(name, config)
230 }
231
232 func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
233         if err := httputils.ParseForm(r); err != nil {
234                 return err
235         }
236
237         name := vars["name"]
238         config := &types.PluginDisableConfig{
239                 ForceDisable: httputils.BoolValue(r, "force"),
240         }
241
242         return pr.backend.Disable(name, config)
243 }
244
245 func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
246         if err := httputils.ParseForm(r); err != nil {
247                 return err
248         }
249
250         name := vars["name"]
251         config := &types.PluginRmConfig{
252                 ForceRemove: httputils.BoolValue(r, "force"),
253         }
254         return pr.backend.Remove(name, config)
255 }
256
257 func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
258         if err := httputils.ParseForm(r); err != nil {
259                 return errors.Wrap(err, "failed to parse form")
260         }
261
262         metaHeaders, authConfig := parseHeaders(r.Header)
263
264         w.Header().Set("Content-Type", "application/json")
265         output := ioutils.NewWriteFlusher(w)
266
267         if err := pr.backend.Push(ctx, vars["name"], metaHeaders, authConfig, output); err != nil {
268                 if !output.Flushed() {
269                         return err
270                 }
271                 output.Write(streamformatter.FormatError(err))
272         }
273         return nil
274 }
275
276 func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
277         var args []string
278         if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
279                 return err
280         }
281         if err := pr.backend.Set(vars["name"], args); err != nil {
282                 return err
283         }
284         w.WriteHeader(http.StatusNoContent)
285         return nil
286 }
287
288 func (pr *pluginRouter) listPlugins(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
289         if err := httputils.ParseForm(r); err != nil {
290                 return err
291         }
292
293         pluginFilters, err := filters.FromParam(r.Form.Get("filters"))
294         if err != nil {
295                 return err
296         }
297         l, err := pr.backend.List(pluginFilters)
298         if err != nil {
299                 return err
300         }
301         return httputils.WriteJSON(w, http.StatusOK, l)
302 }
303
304 func (pr *pluginRouter) inspectPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
305         result, err := pr.backend.Inspect(vars["name"])
306         if err != nil {
307                 return err
308         }
309         return httputils.WriteJSON(w, http.StatusOK, result)
310 }