Skip to content

Commit 3043c26

Browse files
committed
Return registy status code in error
Added Details map to the JSONMessage
1 parent d5a57a4 commit 3043c26

5 files changed

Lines changed: 75 additions & 34 deletions

File tree

api.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,15 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
388388
if image != "" { //pull
389389
if err := srv.ImagePull(image, tag, w, sf, &auth.AuthConfig{}); err != nil {
390390
if sf.Used() {
391-
w.Write(sf.FormatError(err))
391+
w.Write(sf.FormatError(err, 0))
392392
return nil
393393
}
394394
return err
395395
}
396396
} else { //import
397397
if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil {
398398
if sf.Used() {
399-
w.Write(sf.FormatError(err))
399+
w.Write(sf.FormatError(err, 0))
400400
return nil
401401
}
402402
return err
@@ -441,7 +441,7 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
441441
imgID, err := srv.ImageInsert(name, url, path, w, sf)
442442
if err != nil {
443443
if sf.Used() {
444-
w.Write(sf.FormatError(err))
444+
w.Write(sf.FormatError(err, 0))
445445
return nil
446446
}
447447
}
@@ -472,7 +472,11 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
472472
sf := utils.NewStreamFormatter(version > 1.0)
473473
if err := srv.ImagePush(name, w, sf, authConfig); err != nil {
474474
if sf.Used() {
475-
w.Write(sf.FormatError(err))
475+
var code int
476+
if httpErr, ok := err.(*utils.HTTPRequestError); ok {
477+
code = httpErr.StatusCode
478+
}
479+
w.Write(sf.FormatError(err, code))
476480
return nil
477481
}
478482
return err

commands.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import (
3030
const VERSION = "0.5.0-dev"
3131

3232
var (
33-
GITCOMMIT string
33+
GITCOMMIT string
34+
AuthRequiredError error = fmt.Errorf("Authentication is required.")
3435
)
3536

3637
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
@@ -814,10 +815,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
814815
return nil
815816
}
816817

817-
if err := cli.checkIfLogged("push"); err != nil {
818-
return err
819-
}
820-
821818
// If we're not using a custom registry, we know the restrictions
822819
// applied to repository names and can warn the user in advance.
823820
// Custom repositories can have different rules, and we must also
@@ -826,13 +823,22 @@ func (cli *DockerCli) CmdPush(args ...string) error {
826823
return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", cli.configFile.Configs[auth.IndexServerAddress()].Username, name)
827824
}
828825

829-
buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()])
830-
if err != nil {
831-
return err
826+
v := url.Values{}
827+
push := func() error {
828+
buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()])
829+
if err != nil {
830+
return err
831+
}
832+
833+
return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out)
832834
}
833835

834-
v := url.Values{}
835-
if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out); err != nil {
836+
if err := push(); err != nil {
837+
if err == AuthRequiredError {
838+
if err = cli.checkIfLogged("push"); err == nil {
839+
return push()
840+
}
841+
}
836842
return err
837843
}
838844
return nil
@@ -1559,6 +1565,9 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
15591565
} else if err != nil {
15601566
return err
15611567
}
1568+
if jm.Error != nil && jm.Error.Code == 401 {
1569+
return AuthRequiredError
1570+
}
15621571
jm.Display(out)
15631572
}
15641573
} else {

registry/registry.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func (r *Registry) GetRemoteHistory(imgID, registry string, token []string) ([]s
147147
res, err := doWithCookies(r.client, req)
148148
if err != nil || res.StatusCode != 200 {
149149
if res != nil {
150-
return nil, fmt.Errorf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgID)
150+
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res)
151151
}
152152
return nil, err
153153
}
@@ -197,7 +197,7 @@ func (r *Registry) GetRemoteImageJSON(imgID, registry string, token []string) ([
197197
}
198198
defer res.Body.Close()
199199
if res.StatusCode != 200 {
200-
return nil, -1, fmt.Errorf("HTTP code %d", res.StatusCode)
200+
return nil, -1, utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d", res.StatusCode), res)
201201
}
202202

203203
imageSize, err := strconv.Atoi(res.Header.Get("X-Docker-Size"))
@@ -289,12 +289,12 @@ func (r *Registry) GetRepositoryData(indexEp, remote string) (*RepositoryData, e
289289
}
290290
defer res.Body.Close()
291291
if res.StatusCode == 401 {
292-
return nil, fmt.Errorf("Please login first (HTTP code %d)", res.StatusCode)
292+
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Please login first (HTTP code %d)", res.StatusCode), res)
293293
}
294294
// TODO: Right now we're ignoring checksums in the response body.
295295
// In the future, we need to use them to check image validity.
296296
if res.StatusCode != 200 {
297-
return nil, fmt.Errorf("HTTP code: %d", res.StatusCode)
297+
return nil, utils.NewHTTPRequestError(fmt.Sprintf("HTTP code: %d", res.StatusCode), res)
298298
}
299299

300300
var tokens []string
@@ -391,15 +391,15 @@ func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regis
391391
if res.StatusCode != 200 {
392392
errBody, err := ioutil.ReadAll(res.Body)
393393
if err != nil {
394-
return fmt.Errorf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err)
394+
return utils.NewHTTPRequestError(fmt.Sprint("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
395395
}
396396
var jsonBody map[string]string
397397
if err := json.Unmarshal(errBody, &jsonBody); err != nil {
398398
errBody = []byte(err.Error())
399399
} else if jsonBody["error"] == "Image already exists" {
400400
return ErrAlreadyExists
401401
}
402-
return fmt.Errorf("HTTP code %d while uploading metadata: %s", res.StatusCode, errBody)
402+
return utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata: %s", res.StatusCode, errBody), res)
403403
}
404404
return nil
405405
}
@@ -427,9 +427,9 @@ func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registr
427427
if res.StatusCode != 200 {
428428
errBody, err := ioutil.ReadAll(res.Body)
429429
if err != nil {
430-
return "", fmt.Errorf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err)
430+
return utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
431431
}
432-
return "", fmt.Errorf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody)
432+
return utils.NewHTTPRequestError(fmt.Sprintf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody), res)
433433
}
434434
return tarsumLayer.Sum(jsonRaw), nil
435435
}
@@ -463,7 +463,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token
463463
}
464464
res.Body.Close()
465465
if res.StatusCode != 200 && res.StatusCode != 201 {
466-
return fmt.Errorf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote)
466+
return utils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote), res)
467467
}
468468
return nil
469469
}
@@ -540,7 +540,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData
540540
if err != nil {
541541
return nil, err
542542
}
543-
return nil, fmt.Errorf("Error: Status %d trying to push repository %s: %s", res.StatusCode, remote, errBody)
543+
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %s", res.StatusCode, remote, errBody), res)
544544
}
545545
if res.Header.Get("X-Docker-Token") != "" {
546546
tokens = res.Header["X-Docker-Token"]
@@ -564,7 +564,7 @@ func (r *Registry) PushImageJSONIndex(indexEp, remote string, imgList []*ImgData
564564
if err != nil {
565565
return nil, err
566566
}
567-
return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
567+
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody), res)
568568
}
569569
}
570570

@@ -586,7 +586,7 @@ func (r *Registry) SearchRepositories(term string) (*SearchResults, error) {
586586
}
587587
defer res.Body.Close()
588588
if res.StatusCode != 200 {
589-
return nil, fmt.Errorf("Unexepected status code %d", res.StatusCode)
589+
return nil, utils.NewHTTPRequestError(fmt.Sprintf("Unexepected status code %d", res.StatusCode), res)
590590
}
591591
rawData, err := ioutil.ReadAll(res.Body)
592592
if err != nil {

utils/error.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package utils
2+
3+
import (
4+
"net/http"
5+
)
6+
7+
type HTTPRequestError struct {
8+
Message string
9+
StatusCode int
10+
}
11+
12+
func (e *HTTPRequestError) Error() string {
13+
return e.Message
14+
}
15+
16+
func NewHTTPRequestError(msg string, resp *http.Response) error {
17+
return &HTTPRequestError{Message: msg, StatusCode: resp.StatusCode}
18+
}

utils/utils.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -607,12 +607,22 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
607607
return &WriteFlusher{w: w, flusher: flusher}
608608
}
609609

610+
type JSONError struct {
611+
Code int `json:"code,omitempty"`
612+
Message string `json:"message,omitempty"`
613+
}
614+
610615
type JSONMessage struct {
611-
Status string `json:"status,omitempty"`
612-
Progress string `json:"progress,omitempty"`
613-
Error string `json:"error,omitempty"`
614-
ID string `json:"id,omitempty"`
615-
Time int64 `json:"time,omitempty"`
616+
Status string `json:"status,omitempty"`
617+
Progress string `json:"progress,omitempty"`
618+
ErrorMessage string `json:"error,omitempty"` //deprecated
619+
ID string `json:"id,omitempty"`
620+
Time int64 `json:"time,omitempty"`
621+
Error *JSONError `json:"errorDetail,omitempty"`
622+
}
623+
624+
func (e *JSONError) Error() string {
625+
return e.Message
616626
}
617627

618628
func (jm *JSONMessage) Display(out io.Writer) error {
@@ -621,8 +631,8 @@ func (jm *JSONMessage) Display(out io.Writer) error {
621631
}
622632
if jm.Progress != "" {
623633
fmt.Fprintf(out, "%s %s\r", jm.Status, jm.Progress)
624-
} else if jm.Error != "" {
625-
return fmt.Errorf(jm.Error)
634+
} else if jm.Error != nil {
635+
return jm.Error
626636
} else if jm.ID != "" {
627637
fmt.Fprintf(out, "%s: %s\n", jm.ID, jm.Status)
628638
} else {
@@ -656,7 +666,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte
656666
func (sf *StreamFormatter) FormatError(err error) []byte {
657667
sf.used = true
658668
if sf.json {
659-
if b, err := json.Marshal(&JSONMessage{Error: err.Error()}); err == nil {
669+
if b, err := json.Marshal(&JSONMessage{Error: &JSONError{Code: code, Message: err.Error()}, ErrorMessage: err.Error()}); err == nil {
660670
return b
661671
}
662672
return []byte("{\"error\":\"format error\"}")

0 commit comments

Comments
 (0)