CRUD of target API

This commit is contained in:
Wenkai Yin 2016-05-24 14:59:36 +08:00
parent 7449b3604c
commit de72efaba8
8 changed files with 155 additions and 195 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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{})

View File

@ -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 = &registry_error.Error{
StatusCode: resp.StatusCode,
StatusText: resp.Status,
Message: string(b),
Detail: string(b),
}
return
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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, &registry_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 &registry_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 &registry_error.Error{
StatusCode: resp.StatusCode,
StatusText: resp.Status,
Message: string(b),
Detail: string(b),
}
}

View File

@ -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, &registry_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 = &registry_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 = &registry_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 = &registry_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 &registry_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 &registry_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, &registry_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 = &registry_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 = &registry_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 &registry_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 &registry_error.Error{
StatusCode: resp.StatusCode,
StatusText: resp.Status,
Message: string(b),
Detail: string(b),
}
}