... | ... |
@@ -13,7 +13,7 @@ import ( |
13 | 13 |
"strings" |
14 | 14 |
) |
15 | 15 |
|
16 |
-const API_VERSION = 1.1 |
|
16 |
+const API_VERSION = 1.2 |
|
17 | 17 |
|
18 | 18 |
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { |
19 | 19 |
conn, _, err := w.(http.Hijacker).Hijack() |
... | ... |
@@ -68,15 +68,55 @@ func getBoolParam(value string) (bool, error) { |
68 | 68 |
return false, fmt.Errorf("Bad parameter") |
69 | 69 |
} |
70 | 70 |
|
71 |
-func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
|
72 |
- authConfig := &auth.AuthConfig{} |
|
73 |
- if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { |
|
71 |
+func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
|
72 |
+ if version > 1.1 { |
|
73 |
+ w.WriteHeader(http.StatusNotFound) |
|
74 |
+ return nil |
|
75 |
+ } |
|
76 |
+ authConfig, err := auth.LoadConfig(srv.runtime.root) |
|
77 |
+ if err != nil { |
|
78 |
+ if err != auth.ErrConfigFileMissing { |
|
79 |
+ return err |
|
80 |
+ } |
|
81 |
+ authConfig = &auth.AuthConfig{} |
|
82 |
+ } |
|
83 |
+ b, err := json.Marshal(&auth.AuthConfig{Username: authConfig.Username, Email: authConfig.Email}) |
|
84 |
+ if err != nil { |
|
74 | 85 |
return err |
75 | 86 |
} |
76 |
- status, err := auth.Login(authConfig) |
|
87 |
+ writeJson(w, b) |
|
88 |
+ return nil |
|
89 |
+} |
|
90 |
+ |
|
91 |
+func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
|
92 |
+ authConfig := &auth.AuthConfig{} |
|
93 |
+ err := json.NewDecoder(r.Body).Decode(authConfig) |
|
77 | 94 |
if err != nil { |
78 | 95 |
return err |
79 | 96 |
} |
97 |
+ status := "" |
|
98 |
+ if version > 1.1 { |
|
99 |
+ status, err = auth.Login(authConfig, false) |
|
100 |
+ if err != nil { |
|
101 |
+ return err |
|
102 |
+ } |
|
103 |
+ } else { |
|
104 |
+ localAuthConfig, err := auth.LoadConfig(srv.runtime.root) |
|
105 |
+ if err != nil { |
|
106 |
+ if err != auth.ErrConfigFileMissing { |
|
107 |
+ return err |
|
108 |
+ } |
|
109 |
+ } |
|
110 |
+ if authConfig.Username == localAuthConfig.Username { |
|
111 |
+ authConfig.Password = localAuthConfig.Password |
|
112 |
+ } |
|
113 |
+ |
|
114 |
+ newAuthConfig := auth.NewAuthConfig(authConfig.Username, authConfig.Password, authConfig.Email, srv.runtime.root) |
|
115 |
+ status, err = auth.Login(newAuthConfig, true) |
|
116 |
+ if err != nil { |
|
117 |
+ return err |
|
118 |
+ } |
|
119 |
+ } |
|
80 | 120 |
if status != "" { |
81 | 121 |
b, err := json.Marshal(&ApiAuth{Status: status}) |
82 | 122 |
if err != nil { |
... | ... |
@@ -288,7 +328,15 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht |
288 | 288 |
if image != "" { //pull |
289 | 289 |
registry := r.Form.Get("registry") |
290 | 290 |
authConfig := &auth.AuthConfig{} |
291 |
- json.NewDecoder(r.Body).Decode(authConfig) |
|
291 |
+ if version > 1.1 { |
|
292 |
+ json.NewDecoder(r.Body).Decode(authConfig) |
|
293 |
+ } else { |
|
294 |
+ localAuthConfig, err := auth.LoadConfig(srv.runtime.root) |
|
295 |
+ if err != nil && err != auth.ErrConfigFileMissing { |
|
296 |
+ return err |
|
297 |
+ } |
|
298 |
+ authConfig = localAuthConfig |
|
299 |
+ } |
|
292 | 300 |
if err := srv.ImagePull(image, tag, registry, w, sf, authConfig); err != nil { |
293 | 301 |
if sf.Used() { |
294 | 302 |
w.Write(sf.FormatError(err)) |
... | ... |
@@ -358,8 +406,16 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht |
358 | 358 |
|
359 | 359 |
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
360 | 360 |
authConfig := &auth.AuthConfig{} |
361 |
- if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { |
|
362 |
- return err |
|
361 |
+ if version > 1.1 { |
|
362 |
+ if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { |
|
363 |
+ return err |
|
364 |
+ } |
|
365 |
+ } else { |
|
366 |
+ localAuthConfig, err := auth.LoadConfig(srv.runtime.root) |
|
367 |
+ if err != nil && err != auth.ErrConfigFileMissing { |
|
368 |
+ return err |
|
369 |
+ } |
|
370 |
+ authConfig = localAuthConfig |
|
363 | 371 |
} |
364 | 372 |
if err := parseForm(r); err != nil { |
365 | 373 |
return err |
... | ... |
@@ -682,6 +738,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error { |
682 | 682 |
|
683 | 683 |
m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{ |
684 | 684 |
"GET": { |
685 |
+ "/auth": getAuth, |
|
685 | 686 |
"/version": getVersion, |
686 | 687 |
"/info": getInfo, |
687 | 688 |
"/images/json": getImagesJson, |
... | ... |
@@ -120,7 +120,8 @@ func SaveConfig(authConfig *AuthConfig) error { |
120 | 120 |
} |
121 | 121 |
|
122 | 122 |
// try to register/login to the registry server |
123 |
-func Login(authConfig *AuthConfig) (string, error) { |
|
123 |
+func Login(authConfig *AuthConfig, store bool) (string, error) { |
|
124 |
+ storeConfig := false |
|
124 | 125 |
client := &http.Client{} |
125 | 126 |
reqStatusCode := 0 |
126 | 127 |
var status string |
... | ... |
@@ -146,6 +147,7 @@ func Login(authConfig *AuthConfig) (string, error) { |
146 | 146 |
if reqStatusCode == 201 { |
147 | 147 |
status = "Account created. Please use the confirmation link we sent" + |
148 | 148 |
" to your e-mail to activate it.\n" |
149 |
+ storeConfig = true |
|
149 | 150 |
} else if reqStatusCode == 403 { |
150 | 151 |
return "", fmt.Errorf("Login: Your account hasn't been activated. " + |
151 | 152 |
"Please check your e-mail for a confirmation link.") |
... | ... |
@@ -164,7 +166,13 @@ func Login(authConfig *AuthConfig) (string, error) { |
164 | 164 |
} |
165 | 165 |
if resp.StatusCode == 200 { |
166 | 166 |
status = "Login Succeeded\n" |
167 |
+ storeConfig = true |
|
167 | 168 |
} else if resp.StatusCode == 401 { |
169 |
+ if store { |
|
170 |
+ if err := SaveConfig(authConfig); err != nil { |
|
171 |
+ return "", err |
|
172 |
+ } |
|
173 |
+ } |
|
168 | 174 |
return "", fmt.Errorf("Wrong login/password, please try again") |
169 | 175 |
} else { |
170 | 176 |
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, |
... | ... |
@@ -176,5 +184,10 @@ func Login(authConfig *AuthConfig) (string, error) { |
176 | 176 |
} else { |
177 | 177 |
return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody) |
178 | 178 |
} |
179 |
+ if storeConfig && store { |
|
180 |
+ if err := SaveConfig(authConfig); err != nil { |
|
181 |
+ return "", err |
|
182 |
+ } |
|
183 |
+ } |
|
179 | 184 |
return status, nil |
180 | 185 |
} |
... | ... |
@@ -14,12 +14,13 @@ Docker Remote API |
14 | 14 |
- The Remote API is replacing rcli |
15 | 15 |
- Default port in the docker deamon is 4243 |
16 | 16 |
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr |
17 |
+- Since API version 1.2, the auth configuration is now handled client side, so the client has to send the authConfig as POST in /images/create and /images/<name>/pull |
|
17 | 18 |
|
18 | 19 |
2. Version |
19 | 20 |
========== |
20 | 21 |
|
21 |
-The current verson of the API is 1.1 |
|
22 |
-Calling /images/<name>/insert is the same as calling /v1.1/images/<name>/insert |
|
22 |
+The current verson of the API is 1.2 |
|
23 |
+Calling /images/<name>/insert is the same as calling /v1.2/images/<name>/insert |
|
23 | 24 |
You can still call an old version of the api using /v1.0/images/<name>/insert |
24 | 25 |
|
25 | 26 |
3. Endpoints |
... | ... |
@@ -550,11 +551,18 @@ Create an image |
550 | 550 |
|
551 | 551 |
Create an image, either by pull it from the registry or by importing it |
552 | 552 |
|
553 |
- **Example request**: |
|
553 |
+ **Example request v1.0**: |
|
554 |
+ |
|
555 |
+ .. sourcecode:: http |
|
556 |
+ |
|
557 |
+ POST /images/create?fromImage=base HTTP/1.1 |
|
558 |
+ |
|
559 |
+ **Example request v1.2**: |
|
554 | 560 |
|
555 | 561 |
.. sourcecode:: http |
556 | 562 |
|
557 | 563 |
POST /images/create?fromImage=base HTTP/1.1 |
564 |
+ {{ authConfig }} |
|
558 | 565 |
|
559 | 566 |
**Example response v1.1**: |
560 | 567 |
|
... | ... |
@@ -720,11 +728,18 @@ Push an image on the registry |
720 | 720 |
|
721 | 721 |
Push the image ``name`` on the registry |
722 | 722 |
|
723 |
- **Example request**: |
|
723 |
+ **Example request v1.0**: |
|
724 |
+ |
|
725 |
+ .. sourcecode:: http |
|
726 |
+ |
|
727 |
+ POST /images/test/push HTTP/1.1 |
|
728 |
+ |
|
729 |
+ **Example request v1.2**: |
|
724 | 730 |
|
725 | 731 |
.. sourcecode:: http |
726 | 732 |
|
727 | 733 |
POST /images/test/push HTTP/1.1 |
734 |
+ {{ authConfig }} |
|
728 | 735 |
|
729 | 736 |
**Example response v1.1**: |
730 | 737 |
|
... | ... |
@@ -875,7 +890,7 @@ Build an image from Dockerfile via stdin |
875 | 875 |
:statuscode 500: server error |
876 | 876 |
|
877 | 877 |
|
878 |
-Get default username and email |
|
878 |
+Get default username and email <deprecated with 1.2> |
|
879 | 879 |
****************************** |
880 | 880 |
|
881 | 881 |
.. http:get:: /auth |
... | ... |
@@ -904,8 +919,8 @@ Get default username and email |
904 | 904 |
:statuscode 500: server error |
905 | 905 |
|
906 | 906 |
|
907 |
-Set auth configuration |
|
908 |
-********************** |
|
907 |
+Check auth configuration (and store if if api < 1.2) |
|
908 |
+**************************************************** |
|
909 | 909 |
|
910 | 910 |
.. http:post:: /auth |
911 | 911 |
|