From de72efaba8684682d1eb930e31208d4f1d7fe58d Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 24 May 2016 14:59:36 +0800 Subject: [PATCH] CRUD of target API --- api/repository.go | 47 +++++------ api/target.go | 89 +++++++++++++-------- ui/router.go | 2 +- utils/registry/auth/tokenhandler.go | 22 +++--- utils/registry/{ => error}/error.go | 4 +- utils/registry/errors/error.go | 39 ---------- utils/registry/registry.go | 30 +++---- utils/registry/repository.go | 117 ++++++++++------------------ 8 files changed, 155 insertions(+), 195 deletions(-) rename utils/registry/{ => error}/error.go (83%) delete mode 100644 utils/registry/errors/error.go diff --git a/api/repository.go b/api/repository.go index a827d58bc..dc702f4e8 100644 --- a/api/repository.go +++ b/api/repository.go @@ -29,7 +29,7 @@ import ( svc_utils "github.com/vmware/harbor/service/utils" "github.com/vmware/harbor/utils/log" "github.com/vmware/harbor/utils/registry" - "github.com/vmware/harbor/utils/registry/errors" + registry_error "github.com/vmware/harbor/utils/registry/error" ) // RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put @@ -116,14 +116,12 @@ func (ra *RepositoryAPI) Delete() { if len(tag) == 0 { tagList, err := rc.ListTag() if err != nil { - e, ok := errors.ParseError(err) - if ok { - log.Info(e) - ra.CustomAbort(e.StatusCode, e.Message) - } else { - log.Error(err) - ra.CustomAbort(http.StatusInternalServerError, "internal error") + if regErr, ok := err.(*registry_error.Error); ok { + ra.CustomAbort(regErr.StatusCode, regErr.Detail) } + + log.Errorf("error occurred while listing tags of %s: %v", repoName, err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") } tags = append(tags, tagList...) } else { @@ -132,13 +130,12 @@ func (ra *RepositoryAPI) Delete() { for _, t := range tags { if err := rc.DeleteTag(t); err != nil { - e, ok := errors.ParseError(err) - if ok { - ra.CustomAbort(e.StatusCode, e.Message) - } else { - log.Error(err) - ra.CustomAbort(http.StatusInternalServerError, "internal error") + if regErr, ok := err.(*registry_error.Error); ok { + ra.CustomAbort(regErr.StatusCode, regErr.Detail) } + + log.Errorf("error occurred while deleting tags of %s: %v", repoName, err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") } log.Infof("delete tag: %s %s", repoName, t) } @@ -174,13 +171,12 @@ func (ra *RepositoryAPI) GetTags() { ts, err := rc.ListTag() if err != nil { - e, ok := errors.ParseError(err) - if ok { - ra.CustomAbort(e.StatusCode, e.Message) - } else { - log.Error(err) - ra.CustomAbort(http.StatusInternalServerError, "internal error") + if regErr, ok := err.(*registry_error.Error); ok { + ra.CustomAbort(regErr.StatusCode, regErr.Detail) } + + log.Errorf("error occurred while listing tags of %s: %v", repoName, err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") } tags = append(tags, ts...) @@ -209,13 +205,12 @@ func (ra *RepositoryAPI) GetManifests() { mediaTypes := []string{schema1.MediaTypeManifest} _, _, payload, err := rc.PullManifest(tag, mediaTypes) if err != nil { - e, ok := errors.ParseError(err) - if ok { - ra.CustomAbort(e.StatusCode, e.Message) - } else { - log.Error(err) - ra.CustomAbort(http.StatusInternalServerError, "internal error") + if regErr, ok := err.(*registry_error.Error); ok { + ra.CustomAbort(regErr.StatusCode, regErr.Detail) } + + log.Errorf("error occurred while getting manifest of %s:%s: %v", repoName, tag, err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") } mani := models.Manifest{} err = json.Unmarshal(payload, &mani) diff --git a/api/target.go b/api/target.go index 8f6f19cf2..7d925b3ca 100644 --- a/api/target.go +++ b/api/target.go @@ -18,6 +18,7 @@ package api import ( "encoding/base64" "fmt" + "net" "net/http" "net/url" "strconv" @@ -27,6 +28,7 @@ import ( "github.com/vmware/harbor/utils/log" registry_util "github.com/vmware/harbor/utils/registry" "github.com/vmware/harbor/utils/registry/auth" + registry_error "github.com/vmware/harbor/utils/registry/error" ) // TargetAPI handles request to /api/targets/ping /api/targets/{} @@ -64,9 +66,24 @@ func (t *TargetAPI) Ping() { log.Errorf("failed to get target %d: %v", id, err) t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } + + if target == nil { + t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + endpoint = target.URL username = target.Username password = target.Password + + if len(password) != 0 { + b, err := base64.StdEncoding.DecodeString(password) + if err != nil { + log.Errorf("failed to decode password: %v", err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + password = string(b) + } } else { endpoint = t.GetString("endpoint") if len(endpoint) == 0 { @@ -74,58 +91,44 @@ func (t *TargetAPI) Ping() { } username = t.GetString("username") - pwd := t.GetString("password") - b, err := base64.StdEncoding.DecodeString(pwd) - if err != nil { - log.Errorf("failed to decode password: %v", err) - t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) - } - - password = string(b) + password = t.GetString("password") } credential := auth.NewBasicAuthCredential(username, password) registry, err := registry_util.NewRegistryWithCredential(endpoint, credential) if err != nil { - log.Errorf("failed to create registry client: %v", err) + // timeout, dns resolve error, connection refused, etc. + if urlErr, ok := err.(*url.Error); ok { + if netErr, ok := urlErr.Err.(net.Error); ok { + t.CustomAbort(http.StatusBadRequest, netErr.Error()) + } else { + t.CustomAbort(http.StatusBadRequest, urlErr.Error()) + } + } + + log.Errorf("failed to create registry client: %#v", err) t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if err = registry.Ping(); err != nil { - log.Errorf("failed to ping registry %s: %v", registry.Endpoint.String(), err) - // timeout, dns resolve error, connection refused, etc. if urlErr, ok := err.(*url.Error); ok { - t.CustomAbort(http.StatusBadRequest, urlErr.Error()) + if netErr, ok := urlErr.Err.(net.Error); ok { + t.CustomAbort(http.StatusBadRequest, netErr.Error()) + } else { + t.CustomAbort(http.StatusBadRequest, urlErr.Error()) + } } - if regErr, ok := err.(*registry_util.Error); ok { + if regErr, ok := err.(*registry_error.Error); ok { t.CustomAbort(regErr.StatusCode, regErr.Detail) } + log.Errorf("failed to ping registry %s: %v", registry.Endpoint.String(), err) t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } } -// Head ... -func (t *TargetAPI) Head() { - id := t.getIDFromURL() - if id == 0 { - t.CustomAbort(http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) - } - - target, err := dao.GetRepTarget(id) - if err != nil { - log.Errorf("failed to get target %d: %v", id, err) - t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) - } - - // not exist - if target == nil { - t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) - } -} - // Get ... func (t *TargetAPI) Get() { id := t.getIDFromURL() @@ -222,6 +225,28 @@ func (t *TargetAPI) Put() { } } +func (t *TargetAPI) Delete() { + id := t.getIDFromURL() + if id == 0 { + t.CustomAbort(http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + target, err := dao.GetRepTarget(id) + if err != nil { + log.Errorf("failed to get target %d: %v", id, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if target == nil { + t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + + if err = dao.DeleteRepTarget(id); err != nil { + log.Errorf("failed to delete target %d: %v", id, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } +} + func (t *TargetAPI) getIDFromURL() int64 { idStr := t.Ctx.Input.Param(":id") if len(idStr) == 0 { diff --git a/ui/router.go b/ui/router.go index d5a07c434..e1f35da25 100644 --- a/ui/router.go +++ b/ui/router.go @@ -62,7 +62,7 @@ func initRouters() { beego.Router("/api/repositories/tags", &api.RepositoryAPI{}, "get:GetTags") beego.Router("/api/repositories/manifests", &api.RepositoryAPI{}, "get:GetManifests") beego.Router("/api/targets/?:id", &api.TargetAPI{}) - beego.Router("/api/targets/ping", &api.TargetAPI{}, "get:Ping") + beego.Router("/api/target_ping", &api.TargetAPI{}, "get:Ping") //external service that hosted on harbor process: beego.Router("/service/notifications", &service.NotificationHandler{}) diff --git a/utils/registry/auth/tokenhandler.go b/utils/registry/auth/tokenhandler.go index d6c14be87..8aa5d3cbb 100644 --- a/utils/registry/auth/tokenhandler.go +++ b/utils/registry/auth/tokenhandler.go @@ -27,7 +27,7 @@ import ( token_util "github.com/vmware/harbor/service/token" "github.com/vmware/harbor/utils/log" - registry_errors "github.com/vmware/harbor/utils/registry/errors" + registry_error "github.com/vmware/harbor/utils/registry/error" ) type scope struct { @@ -75,7 +75,9 @@ func (t *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]str hasFrom = true } - scopes = append(scopes, t.scope) + if t.scope != nil { + scopes = append(scopes, t.scope) + } expired := true @@ -143,11 +145,14 @@ func NewStandardTokenHandler(credential Credential, scopeType, scopeName string, credential: credential, } - handler.scope = &scope{ - Type: scopeType, - Name: scopeName, - Actions: scopeActions, + if len(scopeType) != 0 || len(scopeName) != 0 { + handler.scope = &scope{ + Type: scopeType, + Name: scopeName, + Actions: scopeActions, + } } + handler.tg = handler.generateToken return handler @@ -182,10 +187,9 @@ func (s *standardTokenHandler) generateToken(realm, service string, scopes []str return } if resp.StatusCode != http.StatusOK { - err = registry_errors.Error{ + err = ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } return } diff --git a/utils/registry/error.go b/utils/registry/error/error.go similarity index 83% rename from utils/registry/error.go rename to utils/registry/error/error.go index f713c1c26..8ef10aa8d 100644 --- a/utils/registry/error.go +++ b/utils/registry/error/error.go @@ -13,17 +13,19 @@ limitations under the License. */ -package registry +package error import ( "fmt" ) +// Error : if response is returned but the status code is not 200, an Error instance will be returned type Error struct { StatusCode int Detail string } +// Error returns the details as string func (e *Error) Error() string { return fmt.Sprintf("%d %s", e.StatusCode, e.Detail) } diff --git a/utils/registry/errors/error.go b/utils/registry/errors/error.go deleted file mode 100644 index 7a1311b00..000000000 --- a/utils/registry/errors/error.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package errors - -import ( - "fmt" -) - -// Error : if response's status code is not 200 or does not meet requirement, -// an Error instance will be returned -type Error struct { - StatusCode int - StatusText string - Message string -} - -// Error ... -func (e Error) Error() string { - return fmt.Sprintf("%d %s %s", e.StatusCode, e.StatusText, e.Message) -} - -// ParseError parses err, if err is type Error, convert it to Error -func ParseError(err error) (Error, bool) { - e, ok := err.(Error) - return e, ok -} diff --git a/utils/registry/registry.go b/utils/registry/registry.go index 52c6f33f4..1b944664f 100644 --- a/utils/registry/registry.go +++ b/utils/registry/registry.go @@ -25,7 +25,7 @@ import ( "github.com/vmware/harbor/utils/log" "github.com/vmware/harbor/utils/registry/auth" - "github.com/vmware/harbor/utils/registry/errors" + registry_error "github.com/vmware/harbor/utils/registry/error" ) const ( @@ -121,10 +121,10 @@ func (r *Registry) Catalog() ([]string, error) { resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - return repos, e + if regErr, ok := err.(*registry_error.Error); ok { + return repos, regErr } + return repos, err } @@ -149,10 +149,9 @@ func (r *Registry) Catalog() ([]string, error) { return repos, nil } - return repos, errors.Error{ + return repos, ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } } @@ -165,10 +164,16 @@ func (r *Registry) Ping() error { resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - return e + if urlErr, ok := err.(*url.Error); ok { + if regErr, ok := urlErr.Err.(*registry_error.Error); ok { + return ®istry_error.Error{ + StatusCode: regErr.StatusCode, + Detail: regErr.Detail, + } + } + return urlErr.Err } + return err } @@ -183,10 +188,9 @@ func (r *Registry) Ping() error { return err } - return errors.Error{ + return ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } } diff --git a/utils/registry/repository.go b/utils/registry/repository.go index 642b076aa..8a52a58d6 100644 --- a/utils/registry/repository.go +++ b/utils/registry/repository.go @@ -30,7 +30,7 @@ import ( "github.com/docker/distribution/manifest/schema2" "github.com/vmware/harbor/utils/log" "github.com/vmware/harbor/utils/registry/auth" - "github.com/vmware/harbor/utils/registry/errors" + registry_error "github.com/vmware/harbor/utils/registry/error" ) // Repository holds information of a repository entity @@ -112,17 +112,6 @@ func NewRepositoryWithUsername(name, endpoint, username string) (*Repository, er return repository, nil } -// try to convert err to errors.Error if it is -func isUnauthorizedError(err error) (bool, error) { - if strings.Contains(err.Error(), http.StatusText(http.StatusUnauthorized)) { - return true, errors.Error{ - StatusCode: http.StatusUnauthorized, - StatusText: http.StatusText(http.StatusUnauthorized), - } - } - return false, err -} - // ListTag ... func (r *Repository) ListTag() ([]string, error) { tags := []string{} @@ -133,10 +122,10 @@ func (r *Repository) ListTag() ([]string, error) { resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - return tags, e + if regErr, ok := err.(*registry_error.Error); ok { + return tags, regErr } + return tags, err } @@ -160,10 +149,9 @@ func (r *Repository) ListTag() ([]string, error) { return tags, nil } - return tags, errors.Error{ + return tags, ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } } @@ -180,9 +168,8 @@ func (r *Repository) ManifestExist(reference string) (digest string, exist bool, resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - err = e + if regErr, ok := err.(*registry_error.Error); ok { + err = regErr return } return @@ -205,10 +192,9 @@ func (r *Repository) ManifestExist(reference string) (digest string, exist bool, return } - err = errors.Error{ + err = ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } return } @@ -226,9 +212,8 @@ func (r *Repository) PullManifest(reference string, acceptMediaTypes []string) ( resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - err = e + if regErr, ok := err.(*registry_error.Error); ok { + err = regErr return } return @@ -247,10 +232,9 @@ func (r *Repository) PullManifest(reference string, acceptMediaTypes []string) ( return } - err = errors.Error{ + err = ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } return @@ -267,9 +251,8 @@ func (r *Repository) PushManifest(reference, mediaType string, payload []byte) ( resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - err = e + if regErr, ok := err.(*registry_error.Error); ok { + err = regErr return } return @@ -287,10 +270,9 @@ func (r *Repository) PushManifest(reference, mediaType string, payload []byte) ( return } - err = errors.Error{ + err = ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } return @@ -305,9 +287,8 @@ func (r *Repository) DeleteManifest(digest string) error { resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - return e + if regErr, ok := err.(*registry_error.Error); ok { + return regErr } return err } @@ -323,10 +304,9 @@ func (r *Repository) DeleteManifest(digest string) error { return err } - return errors.Error{ + return ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } } @@ -338,9 +318,8 @@ func (r *Repository) DeleteTag(tag string) error { } if !exist { - return errors.Error{ + return ®istry_error.Error{ StatusCode: http.StatusNotFound, - StatusText: http.StatusText(http.StatusNotFound), } } @@ -356,9 +335,8 @@ func (r *Repository) BlobExist(digest string) (bool, error) { resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - return false, e + if regErr, ok := err.(*registry_error.Error); ok { + return false, regErr } return false, err } @@ -378,10 +356,9 @@ func (r *Repository) BlobExist(digest string) (bool, error) { return false, err } - return false, errors.Error{ + return false, ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } } @@ -394,9 +371,8 @@ func (r *Repository) PullBlob(digest string) (size int64, data io.ReadCloser, er resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - err = e + if regErr, ok := err.(*registry_error.Error); ok { + err = regErr return } return @@ -418,10 +394,9 @@ func (r *Repository) PullBlob(digest string) (size int64, data io.ReadCloser, er return } - err = errors.Error{ + err = ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } return @@ -433,9 +408,8 @@ func (r *Repository) initiateBlobUpload(name string) (location, uploadUUID strin resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - err = e + if regErr, ok := err.(*registry_error.Error); ok { + err = regErr return } return @@ -454,10 +428,9 @@ func (r *Repository) initiateBlobUpload(name string) (location, uploadUUID strin return } - err = errors.Error{ + err = ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } return @@ -471,9 +444,8 @@ func (r *Repository) monolithicBlobUpload(location, digest string, size int64, d resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - return e + if regErr, ok := err.(*registry_error.Error); ok { + return regErr } return err } @@ -489,10 +461,9 @@ func (r *Repository) monolithicBlobUpload(location, digest string, size int64, d return err } - return errors.Error{ + return ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } } @@ -515,9 +486,8 @@ func (r *Repository) DeleteBlob(digest string) error { resp, err := r.client.Do(req) if err != nil { - ok, e := isUnauthorizedError(err) - if ok { - return e + if regErr, ok := err.(*registry_error.Error); ok { + return regErr } return err } @@ -533,10 +503,9 @@ func (r *Repository) DeleteBlob(digest string) error { return err } - return errors.Error{ + return ®istry_error.Error{ StatusCode: resp.StatusCode, - StatusText: resp.Status, - Message: string(b), + Detail: string(b), } }