mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-23 00:57:44 +01:00
repo tag delete
This commit is contained in:
parent
fefcae2634
commit
85869f1b81
@ -57,7 +57,10 @@ func (b *BaseAPI) ValidateUser() int {
|
||||
username, password, ok := b.Ctx.Request.BasicAuth()
|
||||
if ok {
|
||||
log.Infof("Requst with Basic Authentication header, username: %s", username)
|
||||
user, err := auth.Login(models.AuthModel{username, password})
|
||||
user, err := auth.Login(models.AuthModel{
|
||||
Principal: username,
|
||||
Password: password,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Error while trying to login, username: %s, error: %v", username, err)
|
||||
user = nil
|
||||
|
@ -18,6 +18,7 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -26,6 +27,8 @@ import (
|
||||
"github.com/vmware/harbor/models"
|
||||
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/auth"
|
||||
)
|
||||
|
||||
// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
|
||||
@ -36,6 +39,7 @@ type RepositoryAPI struct {
|
||||
BaseAPI
|
||||
userID int
|
||||
username string
|
||||
registry *registry.Registry
|
||||
}
|
||||
|
||||
// Prepare will set a non existent user ID in case the request tries to view repositories under a project he doesn't has permission.
|
||||
@ -53,6 +57,46 @@ func (ra *RepositoryAPI) Prepare() {
|
||||
} else {
|
||||
ra.username = username
|
||||
}
|
||||
|
||||
var client *http.Client
|
||||
|
||||
//no session, initialize a standard auth handler
|
||||
if ra.userID == dao.NonExistUserID && len(ra.username) == 0 {
|
||||
credential := &auth.Credential{}
|
||||
username, password, ok := ra.Ctx.Request.BasicAuth()
|
||||
if ok {
|
||||
credential.Username = username
|
||||
credential.Password = password
|
||||
}
|
||||
client = registry.NewClientStandardAuthHandlerEmbeded(credential)
|
||||
log.Debug("initializing standard auth handler")
|
||||
|
||||
} else {
|
||||
// session works, initialize a username auth handler
|
||||
username := ra.username
|
||||
if len(username) == 0 {
|
||||
user, err := dao.GetUser(models.User{
|
||||
UserID: ra.userID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("error occurred whiling geting user for initializing a username auth handler: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
username = user.Username
|
||||
}
|
||||
|
||||
client = registry.NewClientUsernameAuthHandlerEmbeded(username)
|
||||
log.Debug("initializing username auth handler: %s", username)
|
||||
}
|
||||
|
||||
endpoint := os.Getenv("REGISTRY_URL")
|
||||
r, err := registry.New(endpoint, client)
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while initializing auth handler for repository API: %v", err)
|
||||
}
|
||||
|
||||
ra.registry = r
|
||||
}
|
||||
|
||||
// Get ...
|
||||
@ -77,11 +121,13 @@ func (ra *RepositoryAPI) Get() {
|
||||
ra.RenderError(http.StatusForbidden, "")
|
||||
return
|
||||
}
|
||||
|
||||
repoList, err := svc_utils.GetRepoFromCache()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get repo from cache, error: %v", err)
|
||||
ra.RenderError(http.StatusInternalServerError, "internal sever error")
|
||||
}
|
||||
|
||||
projectName := p.Name
|
||||
q := ra.GetString("q")
|
||||
var resp []string
|
||||
@ -105,6 +151,56 @@ func (ra *RepositoryAPI) Get() {
|
||||
ra.ServeJSON()
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (ra *RepositoryAPI) Delete() {
|
||||
repoName := ra.GetString("repo_name")
|
||||
if len(repoName) == 0 {
|
||||
ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
|
||||
}
|
||||
|
||||
tags := []string{}
|
||||
tag := ra.GetString("tag")
|
||||
if len(tag) == 0 {
|
||||
tagList, err := ra.registry.ListTag(repoName)
|
||||
if err != nil {
|
||||
e, ok := registry.ParseError(err)
|
||||
if ok {
|
||||
log.Info(e)
|
||||
ra.CustomAbort(e.StatusCode, e.Message)
|
||||
} else {
|
||||
log.Error(err)
|
||||
ra.CustomAbort(http.StatusInternalServerError, "internal error")
|
||||
}
|
||||
|
||||
}
|
||||
tags = append(tags, tagList...)
|
||||
|
||||
} else {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
for _, t := range tags {
|
||||
if err := ra.registry.DeleteTag(repoName, t); err != nil {
|
||||
e, ok := registry.ParseError(err)
|
||||
if ok {
|
||||
ra.CustomAbort(e.StatusCode, e.Message)
|
||||
} else {
|
||||
log.Error(err)
|
||||
ra.CustomAbort(http.StatusInternalServerError, "internal error")
|
||||
}
|
||||
}
|
||||
log.Infof("delete tag: %s %s", repoName, t)
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Debug("refreshing catalog cache")
|
||||
if err := svc_utils.RefreshCatalogCache(); err != nil {
|
||||
log.Errorf("error occurred while refresh catalog cache: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
type tag struct {
|
||||
Name string `json:"name"`
|
||||
Tags []string `json:"tags"`
|
||||
@ -128,7 +224,7 @@ func (ra *RepositoryAPI) GetTags() {
|
||||
var tags []string
|
||||
|
||||
repoName := ra.GetString("repo_name")
|
||||
result, err := svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repoName, "tags", "list"), ra.username)
|
||||
result, err := registry.APIGet(registry.BuildRegistryURL(repoName, "tags", "list"), ra.username)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get repo tags, repo name: %s, error: %v", repoName, err)
|
||||
ra.RenderError(http.StatusInternalServerError, "Failed to get repo tags")
|
||||
@ -148,7 +244,7 @@ func (ra *RepositoryAPI) GetManifests() {
|
||||
|
||||
item := models.RepoItem{}
|
||||
|
||||
result, err := svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repoName, "manifests", tag), ra.username)
|
||||
result, err := registry.APIGet(registry.BuildRegistryURL(repoName, "manifests", tag), ra.username)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get manifests for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err)
|
||||
ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
|
||||
|
@ -49,7 +49,10 @@ func (c *CommonController) Login() {
|
||||
principal := c.GetString("principal")
|
||||
password := c.GetString("password")
|
||||
|
||||
user, err := auth.Login(models.AuthModel{principal, password})
|
||||
user, err := auth.Login(models.AuthModel{
|
||||
Principal: principal,
|
||||
Password: password,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserLogin: %v", err)
|
||||
c.CustomAbort(http.StatusUnauthorized, "")
|
||||
|
@ -206,7 +206,10 @@ func TestLoginByUserName(t *testing.T) {
|
||||
Password: "Abc12345",
|
||||
}
|
||||
|
||||
loginUser, err := LoginByDb(models.AuthModel{userQuery.Username, userQuery.Password})
|
||||
loginUser, err := LoginByDb(models.AuthModel{
|
||||
Principal: userQuery.Username,
|
||||
Password: userQuery.Password,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
@ -226,7 +229,10 @@ func TestLoginByEmail(t *testing.T) {
|
||||
Password: "Abc12345",
|
||||
}
|
||||
|
||||
loginUser, err := LoginByDb(models.AuthModel{userQuery.Email, userQuery.Password})
|
||||
loginUser, err := LoginByDb(models.AuthModel{
|
||||
Principal: userQuery.Email,
|
||||
Password: userQuery.Password,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json
|
||||
// Post handles POST request, and records audit log or refreshes cache based on event.
|
||||
func (n *NotificationHandler) Post() {
|
||||
var notification models.Notification
|
||||
// log.Info("Notification Handler triggered!\n")
|
||||
//log.Info("Notification Handler triggered!\n")
|
||||
// log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody()))
|
||||
err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification)
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
package token
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
@ -80,7 +80,7 @@ func FilterAccess(username string, authenticated bool, a *token.ResourceActions)
|
||||
return
|
||||
}
|
||||
if exist {
|
||||
permission = "RW"
|
||||
permission = "RWM"
|
||||
} else {
|
||||
permission = ""
|
||||
log.Infof("project %s does not exist, set empty permission for admin\n", projectName)
|
||||
@ -96,6 +96,9 @@ func FilterAccess(username string, authenticated bool, a *token.ResourceActions)
|
||||
if strings.Contains(permission, "W") {
|
||||
a.Actions = append(a.Actions, "push")
|
||||
}
|
||||
if strings.Contains(permission, "M") {
|
||||
a.Actions = append(a.Actions, "*")
|
||||
}
|
||||
if strings.Contains(permission, "R") || dao.IsProjectPublic(projectName) {
|
||||
a.Actions = append(a.Actions, "pull")
|
||||
}
|
@ -13,53 +13,53 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package service
|
||||
package token
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/auth"
|
||||
"github.com/vmware/harbor/models"
|
||||
svc_utils "github.com/vmware/harbor/service/utils"
|
||||
//svc_utils "github.com/vmware/harbor/service/utils"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
)
|
||||
|
||||
// TokenHandler handles request on /service/token, which is the auth provider for registry.
|
||||
type TokenHandler struct {
|
||||
// Handler handles request on /service/token, which is the auth provider for registry.
|
||||
type Handler struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// Get handles GET request, it checks the http header for user credentials
|
||||
// and parse service and scope based on docker registry v2 standard,
|
||||
// checkes the permission agains local DB and generates jwt token.
|
||||
func (a *TokenHandler) Get() {
|
||||
func (h *Handler) Get() {
|
||||
|
||||
request := a.Ctx.Request
|
||||
request := h.Ctx.Request
|
||||
log.Infof("request url: %v", request.URL.String())
|
||||
username, password, _ := request.BasicAuth()
|
||||
authenticated := authenticate(username, password)
|
||||
service := a.GetString("service")
|
||||
scopes := a.GetStrings("scope")
|
||||
service := h.GetString("service")
|
||||
scopes := h.GetStrings("scope")
|
||||
log.Debugf("scopes: %+v", scopes)
|
||||
|
||||
if len(scopes) == 0 && !authenticated {
|
||||
log.Info("login request with invalid credentials")
|
||||
a.CustomAbort(http.StatusUnauthorized, "")
|
||||
h.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
access := svc_utils.GetResourceActions(scopes)
|
||||
access := GetResourceActions(scopes)
|
||||
for _, a := range access {
|
||||
svc_utils.FilterAccess(username, authenticated, a)
|
||||
FilterAccess(username, authenticated, a)
|
||||
}
|
||||
a.serveToken(username, service, access)
|
||||
h.serveToken(username, service, access)
|
||||
}
|
||||
|
||||
func (a *TokenHandler) serveToken(username, service string, access []*token.ResourceActions) {
|
||||
writer := a.Ctx.ResponseWriter
|
||||
func (h *Handler) serveToken(username, service string, access []*token.ResourceActions) {
|
||||
writer := h.Ctx.ResponseWriter
|
||||
//create token
|
||||
rawToken, err := svc_utils.MakeToken(username, service, access)
|
||||
rawToken, err := MakeToken(username, service, access)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to make token, error: %v", err)
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
@ -67,12 +67,15 @@ func (a *TokenHandler) serveToken(username, service string, access []*token.Reso
|
||||
}
|
||||
tk := make(map[string]string)
|
||||
tk["token"] = rawToken
|
||||
a.Data["json"] = tk
|
||||
a.ServeJSON()
|
||||
h.Data["json"] = tk
|
||||
h.ServeJSON()
|
||||
}
|
||||
|
||||
func authenticate(principal, password string) bool {
|
||||
user, err := auth.Login(models.AuthModel{principal, password})
|
||||
user, err := auth.Login(models.AuthModel{
|
||||
Principal: principal,
|
||||
Password: password,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserLogin: %v", err)
|
||||
return false
|
@ -17,10 +17,12 @@ package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
"github.com/vmware/harbor/utils/registry"
|
||||
|
||||
"github.com/astaxie/beego/cache"
|
||||
)
|
||||
@ -28,6 +30,8 @@ import (
|
||||
// Cache is the global cache in system.
|
||||
var Cache cache.Cache
|
||||
|
||||
var registryClient *registry.Registry
|
||||
|
||||
const catalogKey string = "catalog"
|
||||
|
||||
func init() {
|
||||
@ -36,11 +40,19 @@ func init() {
|
||||
if err != nil {
|
||||
log.Errorf("Failed to initialize cache, error:%v", err)
|
||||
}
|
||||
|
||||
endpoint := os.Getenv("REGISTRY_URL")
|
||||
client := registry.NewClientUsernameAuthHandlerEmbeded("admin")
|
||||
registryClient, err = registry.New(endpoint, client)
|
||||
if err != nil {
|
||||
log.Fatalf("error occurred while initializing authentication handler used by cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// RefreshCatalogCache calls registry's API to get repository list and write it to cache.
|
||||
func RefreshCatalogCache() error {
|
||||
result, err := RegistryAPIGet(BuildRegistryURL("_catalog"), "")
|
||||
log.Debug("refreshing catalog cache...")
|
||||
result, err := registry.APIGet(registry.BuildRegistryURL("_catalog"), "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -49,7 +61,23 @@ func RefreshCatalogCache() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Cache.Put(catalogKey, repoResp.Repositories, 600*time.Second)
|
||||
|
||||
repos := []string{}
|
||||
|
||||
for _, repo := range repoResp.Repositories {
|
||||
tags, err := registryClient.ListTag(repo)
|
||||
if err != nil {
|
||||
log.Errorf("error occurred while list tag for %s: %v", repo, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(tags) != 0 {
|
||||
repos = append(repos, repo)
|
||||
log.Debugf("add %s to catalog cache", repo)
|
||||
}
|
||||
}
|
||||
|
||||
Cache.Put(catalogKey, repos, 600*time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"github.com/vmware/harbor/api"
|
||||
"github.com/vmware/harbor/controllers"
|
||||
"github.com/vmware/harbor/service"
|
||||
"github.com/vmware/harbor/service/token"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
@ -63,5 +64,5 @@ func initRouters() {
|
||||
|
||||
//external service that hosted on harbor process:
|
||||
beego.Router("/service/notifications", &service.NotificationHandler{})
|
||||
beego.Router("/service/token", &service.TokenHandler{})
|
||||
beego.Router("/service/token", &token.Handler{})
|
||||
}
|
||||
|
@ -23,10 +23,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/service/utils"
|
||||
token_util "github.com/vmware/harbor/service/token"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
@ -57,7 +54,9 @@ type standardTokenHandler struct {
|
||||
credential *Credential
|
||||
}
|
||||
|
||||
// NewTokenHandler ...
|
||||
// NewStandardTokenHandler returns a standard token handler. The handler will request a token
|
||||
// from token server whose URL is specified in the "WWW-authentication" header and add it to
|
||||
// the origin request
|
||||
// TODO deal with https
|
||||
func NewStandardTokenHandler(credential *Credential) Handler {
|
||||
return &standardTokenHandler{
|
||||
@ -68,12 +67,12 @@ func NewStandardTokenHandler(credential *Credential) Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// Scheme : see interface AuthHandler
|
||||
// Schema implements the corresponding method in interface AuthHandler
|
||||
func (t *standardTokenHandler) Schema() string {
|
||||
return "bearer"
|
||||
}
|
||||
|
||||
// AuthorizeRequest : see interface AuthHandler
|
||||
// AuthorizeRequest implements the corresponding method in interface AuthHandler
|
||||
func (t *standardTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
||||
realm, ok := params["realm"]
|
||||
if !ok {
|
||||
@ -103,9 +102,7 @@ func (t *standardTokenHandler) AuthorizeRequest(req *http.Request, params map[st
|
||||
}
|
||||
|
||||
// TODO support secretKey
|
||||
if len(t.credential.Username) != 0 {
|
||||
r.SetBasicAuth(t.credential.Username, t.credential.Password)
|
||||
}
|
||||
r.SetBasicAuth(t.credential.Username, t.credential.Password)
|
||||
|
||||
resp, err := t.client.Do(r)
|
||||
if err != nil {
|
||||
@ -133,52 +130,25 @@ func (t *standardTokenHandler) AuthorizeRequest(req *http.Request, params map[st
|
||||
return nil
|
||||
}
|
||||
|
||||
type sessionTokenHandler struct {
|
||||
sessionID string
|
||||
type usernameTokenHandler struct {
|
||||
username string
|
||||
}
|
||||
|
||||
func NewSessionTokenHandler(sessionID string) Handler {
|
||||
return &sessionTokenHandler{
|
||||
sessionID: sessionID,
|
||||
// NewUsernameTokenHandler returns a handler which will generate
|
||||
// a token according the user's privileges
|
||||
func NewUsernameTokenHandler(username string) Handler {
|
||||
return &usernameTokenHandler{
|
||||
username: username,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sessionTokenHandler) Schema() string {
|
||||
// Schema implements the corresponding method in interface AuthHandler
|
||||
func (u *usernameTokenHandler) Schema() string {
|
||||
return "bearer"
|
||||
}
|
||||
|
||||
func (s *sessionTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
||||
session, err := beego.GlobalSessions.GetSessionStore(s.sessionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
username, ok := session.Get("username").(string)
|
||||
if !ok {
|
||||
return errors.New("username in session can not be converted to string")
|
||||
}
|
||||
|
||||
if len(username) == 0 {
|
||||
userID, ok := session.Get("userId").(int)
|
||||
if !ok {
|
||||
return errors.New("userId in session can not be converted to int")
|
||||
}
|
||||
|
||||
u := models.User{
|
||||
UserID: userID,
|
||||
}
|
||||
user, err := dao.GetUser(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return fmt.Errorf("user with id %d does not exist", userID)
|
||||
}
|
||||
|
||||
username = user.Username
|
||||
}
|
||||
|
||||
// AuthorizeRequest implements the corresponding method in interface AuthHandler
|
||||
func (u *usernameTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
||||
service := params["service"]
|
||||
|
||||
scopes := []string{}
|
||||
@ -187,14 +157,14 @@ func (s *sessionTokenHandler) AuthorizeRequest(req *http.Request, params map[str
|
||||
scopes = strings.Split(scope, " ")
|
||||
}
|
||||
|
||||
token, err := utils.GenTokenForUI(username, service, scopes)
|
||||
token, err := token_util.GenTokenForUI(u.username, service, scopes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add(http.CanonicalHeaderKey("Authorization"), fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
log.Debugf("sessionTokenHandler generated token successfully | %s %s", req.Method, req.URL)
|
||||
log.Debugf("usernameTokenHandler generated token successfully | %s %s", req.Method, req.URL)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
38
utils/registry/error.go
Normal file
38
utils/registry/error.go
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
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 registry
|
||||
|
||||
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
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error ...
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("%d %s", e.StatusCode, 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
|
||||
}
|
@ -22,9 +22,9 @@ import (
|
||||
"github.com/vmware/harbor/utils/registry/auth"
|
||||
)
|
||||
|
||||
// NewHTTPClientAuthHandlersEmbeded return a http.Client which will authorize the request
|
||||
// and send it again when encounters a 401 error
|
||||
func NewClientStandardAuthHandlersEmbeded(credential *auth.Credential) *http.Client {
|
||||
// NewClientStandardAuthHandlerEmbeded return a http.Client which will authorize the request
|
||||
// according to the credential provided and send it again when encounters a 401 error
|
||||
func NewClientStandardAuthHandlerEmbeded(credential *auth.Credential) *http.Client {
|
||||
handlers := []auth.Handler{}
|
||||
|
||||
tokenHandler := auth.NewStandardTokenHandler(credential)
|
||||
@ -38,10 +38,12 @@ func NewClientStandardAuthHandlersEmbeded(credential *auth.Credential) *http.Cli
|
||||
}
|
||||
}
|
||||
|
||||
func NewClientSessionAuthHandlersEmbeded(sessionID string) *http.Client {
|
||||
// NewClientUsernameAuthHandlerEmbeded return a http.Client which will authorize the request
|
||||
// according to the user's privileges and send it again when encounters a 401 error
|
||||
func NewClientUsernameAuthHandlerEmbeded(username string) *http.Client {
|
||||
handlers := []auth.Handler{}
|
||||
|
||||
tokenHandler := auth.NewSessionTokenHandler(sessionID)
|
||||
tokenHandler := auth.NewUsernameTokenHandler(username)
|
||||
|
||||
handlers = append(handlers, tokenHandler)
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -51,6 +52,47 @@ func New(endpoint string, client *http.Client) (*Registry, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListTag ...
|
||||
func (r *Registry) ListTag(name string) ([]string, error) {
|
||||
tags := []string{}
|
||||
req, err := http.NewRequest("GET", r.ub.buildTagListURL(name), nil)
|
||||
if err != nil {
|
||||
return tags, err
|
||||
}
|
||||
|
||||
resp, err := r.client.Do(req)
|
||||
if err != nil {
|
||||
return tags, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return tags, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
tagsResp := struct {
|
||||
Tags []string `json:"tags"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(b, &tagsResp); err != nil {
|
||||
return tags, err
|
||||
}
|
||||
|
||||
tags = tagsResp.Tags
|
||||
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
return tags, Error{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: string(b),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ManifestExist ...
|
||||
func (r *Registry) ManifestExist(name, reference string) (digest string, exist bool, err error) {
|
||||
req, err := http.NewRequest("HEAD", r.ub.buildManifestURL(name, reference), nil)
|
||||
@ -58,7 +100,8 @@ func (r *Registry) ManifestExist(name, reference string) (digest string, exist b
|
||||
return
|
||||
}
|
||||
|
||||
// Schema 2 manifest
|
||||
// request Schema 2 manifest, if the registry does not support it,
|
||||
// Schema 1 manifest will be returned
|
||||
req.Header.Set(http.CanonicalHeaderKey("Accept"), schema2.MediaTypeManifest)
|
||||
|
||||
resp, err := r.client.Do(req)
|
||||
@ -78,12 +121,15 @@ func (r *Registry) ManifestExist(name, reference string) (digest string, exist b
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
message, err := ioutil.ReadAll(resp.Body)
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = fmt.Errorf("%s %s : %s %s", req.Method, req.URL, resp.Status, string(message))
|
||||
err = Error{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: string(b),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -104,20 +150,23 @@ func (r *Registry) PullManifest(name, reference string) (digest, mediaType strin
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
digest = resp.Header.Get(http.CanonicalHeaderKey("Docker-Content-Digest"))
|
||||
mediaType = resp.Header.Get(http.CanonicalHeaderKey("Content-Type"))
|
||||
payload, err = ioutil.ReadAll(resp.Body)
|
||||
return
|
||||
}
|
||||
|
||||
message, err := ioutil.ReadAll(resp.Body)
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = fmt.Errorf("%s %s : %s %s", req.Method, req.URL, resp.Status, string(message))
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
digest = resp.Header.Get(http.CanonicalHeaderKey("Docker-Content-Digest"))
|
||||
mediaType = resp.Header.Get(http.CanonicalHeaderKey("Content-Type"))
|
||||
payload = b
|
||||
return
|
||||
}
|
||||
|
||||
err = Error{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: string(b),
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -139,21 +188,30 @@ func (r *Registry) DeleteManifest(name, digest string) error {
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
message, err := ioutil.ReadAll(resp.Body)
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s %s : %s %s", req.Method, req.URL, resp.Status, string(message))
|
||||
return Error{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: string(b),
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteTag ...
|
||||
func (r *Registry) DeleteTag(name, tag string) error {
|
||||
digest, _, err := r.ManifestExist(name, tag)
|
||||
digest, exist, err := r.ManifestExist(name, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exist {
|
||||
return Error{
|
||||
StatusCode: http.StatusNotFound,
|
||||
}
|
||||
}
|
||||
|
||||
return r.DeleteManifest(name, digest)
|
||||
}
|
||||
|
||||
@ -175,12 +233,19 @@ func (r *Registry) DeleteBlob(name, digest string) error {
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
message, err := ioutil.ReadAll(resp.Body)
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s %s : %s %s", req.Method, req.URL, resp.Status, string(message))
|
||||
return Error{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: string(b),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *uRLBuilder) buildTagListURL(name string) string {
|
||||
return fmt.Sprintf("%s/v2/%s/tags/list", u.root.String(), name)
|
||||
}
|
||||
|
||||
func (u *uRLBuilder) buildManifestURL(name, reference string) string {
|
||||
|
@ -13,7 +13,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -23,6 +23,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
token_util "github.com/vmware/harbor/service/token"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
@ -43,9 +44,9 @@ func BuildRegistryURL(segments ...string) string {
|
||||
return url
|
||||
}
|
||||
|
||||
// RegistryAPIGet triggers GET request to the URL which is the endpoint of registry and returns the response body.
|
||||
// APIGet triggers GET request to the URL which is the endpoint of registry and returns the response body.
|
||||
// It will attach a valid jwt token to the request if registry requires.
|
||||
func RegistryAPIGet(url, username string) ([]byte, error) {
|
||||
func APIGet(url, username string) ([]byte, error) {
|
||||
|
||||
log.Debugf("Registry API url: %s", url)
|
||||
response, err := http.Get(url)
|
||||
@ -71,7 +72,7 @@ func RegistryAPIGet(url, username string) ([]byte, error) {
|
||||
service = res[1]
|
||||
scopes = append(scopes, res[2])
|
||||
}
|
||||
token, err := GenTokenForUI(username, service, scopes)
|
||||
token, err := token_util.GenTokenForUI(username, service, scopes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
Loading…
Reference in New Issue
Block a user