mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-16 20:01:35 +01:00
Updae retag api spec
Signed-off-by: 陈德 <chende@caicloud.io>
This commit is contained in:
parent
75f1cdb449
commit
03d5157eaf
@ -41,32 +41,6 @@ paths:
|
||||
$ref: '#/definitions/Search'
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/retag:
|
||||
post:
|
||||
summary: Retag an image
|
||||
description: >
|
||||
This endpoint tags an image with another tag in the same repo, or
|
||||
to another repo or project.
|
||||
parameters:
|
||||
- name: request
|
||||
in: body
|
||||
description: reqeust to given source image and target image
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/RetagReq'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Image retag successfully.
|
||||
'400':
|
||||
description: Invalid image values provided
|
||||
'401':
|
||||
description: User has no permission to the source project or destination project.
|
||||
'404':
|
||||
description: Project or repository not found.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/projects:
|
||||
get:
|
||||
summary: List projects
|
||||
@ -1157,6 +1131,33 @@ paths:
|
||||
$ref: '#/definitions/DetailedTag'
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
post:
|
||||
summary: Retag an image
|
||||
description: >
|
||||
This endpoint tags an existing image with another tag in this repo, source images
|
||||
can be in different repos or projects.
|
||||
parameters:
|
||||
- name: request
|
||||
in: body
|
||||
description: reqeust to given source image and target tag
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/RetagReq'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Image retag successfully.
|
||||
'400':
|
||||
description: Invalid image values provided
|
||||
'401':
|
||||
description: User has no permission to the source project or destination project.
|
||||
'404':
|
||||
description: Project or repository not found.
|
||||
'409':
|
||||
description: Target tag already exists.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
'/repositories/{repo_name}/tags/{tag}/labels':
|
||||
get:
|
||||
summary: Get labels of an image.
|
||||
@ -2995,12 +2996,15 @@ definitions:
|
||||
RetagReq:
|
||||
type: object
|
||||
properties:
|
||||
tag:
|
||||
description: new tag to be created
|
||||
type: string
|
||||
src_image:
|
||||
description: Source image to be retagged, e.g. 'stage/app:v1.0'
|
||||
type: string
|
||||
dest_image:
|
||||
description: Destination image tag to, e.g. 'product/app:v1.0'
|
||||
type: string
|
||||
override:
|
||||
description: If target tag already exists, whether to override it
|
||||
type: boolean
|
||||
SearchRepository:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -21,8 +21,9 @@ import (
|
||||
|
||||
// RetagRequest gives the source image and target image of retag
|
||||
type RetagRequest struct {
|
||||
SrcImage string `json:"src_image"`
|
||||
DestImage string `json:"dest_image"`
|
||||
Tag string `json:"tag"` // The new tag
|
||||
SrcImage string `json:"src_image"` // Source images in format <project>/<repo>:<reference>
|
||||
Override bool `json:"override"` // If target tag exists, whether override it
|
||||
}
|
||||
|
||||
// Image holds each part (project, repo, tag) of an image name
|
||||
|
@ -45,6 +45,15 @@ func TestParseImage(t *testing.T) {
|
||||
},
|
||||
Valid: true,
|
||||
},
|
||||
{
|
||||
Input: "library/busybox:sha256:9e2c9d5f44efbb6ee83aecd17a120c513047d289d142ec5738c9f02f9b24ad07",
|
||||
Expected: &Image{
|
||||
Project: "library",
|
||||
Repo: "busybox",
|
||||
Tag: "sha256:9e2c9d5f44efbb6ee83aecd17a120c513047d289d142ec5738c9f02f9b24ad07",
|
||||
},
|
||||
Valid: true,
|
||||
},
|
||||
{
|
||||
Input: "busybox/v1.0",
|
||||
Valid: false,
|
||||
|
@ -424,6 +424,82 @@ func (ra *RepositoryAPI) GetTag() {
|
||||
ra.ServeJSON()
|
||||
}
|
||||
|
||||
// Retag tags an existing image to another tag in this repo, the source image is specified by request body.
|
||||
func (ra *RepositoryAPI) Retag() {
|
||||
if !ra.SecurityCtx.IsAuthenticated() {
|
||||
ra.HandleUnauthorized()
|
||||
return
|
||||
}
|
||||
|
||||
repoName := ra.GetString(":splat")
|
||||
request := models.RetagRequest{}
|
||||
ra.DecodeJSONReq(&request)
|
||||
srcImage, err := models.ParseImage(request.SrcImage)
|
||||
if err != nil {
|
||||
ra.HandleBadRequest(fmt.Sprintf("invalid src image string '%s', should in format '<project>/<repo>:<tag>'", request.SrcImage))
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether source image exists
|
||||
exist, _, err := ra.checkExistence(fmt.Sprintf("%s/%s", srcImage.Project, srcImage.Repo), srcImage.Tag)
|
||||
if err != nil {
|
||||
ra.HandleInternalServerError(fmt.Sprintf("check existence of %s error: %v", request.SrcImage, err))
|
||||
return
|
||||
}
|
||||
if !exist {
|
||||
ra.HandleNotFound(fmt.Sprintf("image %s not exist", request.SrcImage))
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether target project exists
|
||||
project, repo := utils.ParseRepository(repoName)
|
||||
exist, err = ra.ProjectMgr.Exists(project)
|
||||
if err != nil {
|
||||
ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", project), err)
|
||||
return
|
||||
}
|
||||
if !exist {
|
||||
ra.HandleNotFound(fmt.Sprintf("project %s not found", project))
|
||||
return
|
||||
}
|
||||
|
||||
// If override not allowed, check whether target tag already exists
|
||||
if !request.Override {
|
||||
exist, _, err := ra.checkExistence(repoName, request.Tag)
|
||||
if err != nil {
|
||||
ra.HandleInternalServerError(fmt.Sprintf("check existence of %s:%s error: %v", repoName, request.Tag, err))
|
||||
return
|
||||
}
|
||||
if exist {
|
||||
ra.HandleConflict(fmt.Sprintf("tag '%s' already existed for '%s'", request.Tag, repoName))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether use has read permission to source project
|
||||
if !ra.SecurityCtx.HasReadPerm(srcImage.Project) {
|
||||
log.Errorf("user has no read permission to project '%s'", srcImage.Project)
|
||||
ra.HandleUnauthorized()
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether user has write permission to target project
|
||||
if !ra.SecurityCtx.HasWritePerm(project) {
|
||||
log.Errorf("user has no write permission to project '%s'", project)
|
||||
ra.HandleUnauthorized()
|
||||
return
|
||||
}
|
||||
|
||||
// Retag the image
|
||||
if err = uiutils.Retag(srcImage, &models.Image{
|
||||
Project: project,
|
||||
Repo: repo,
|
||||
Tag: request.Tag,
|
||||
}); err != nil {
|
||||
ra.HandleInternalServerError(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// GetTags returns tags of a repository
|
||||
func (ra *RepositoryAPI) GetTags() {
|
||||
repoName := ra.GetString(":splat")
|
||||
|
@ -58,7 +58,6 @@ func initRouters() {
|
||||
// API
|
||||
beego.Router("/api/ping", &api.SystemInfoAPI{}, "get:Ping")
|
||||
beego.Router("/api/search", &api.SearchAPI{})
|
||||
beego.Router("api/retag", &api.RetagAPI{}, "post:Retag")
|
||||
beego.Router("/api/projects/", &api.ProjectAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/projects/:id([0-9]+)/logs", &api.ProjectAPI{}, "get:Logs")
|
||||
beego.Router("/api/projects/:id([0-9]+)/_deletable", &api.ProjectAPI{}, "get:Deletable")
|
||||
@ -73,7 +72,7 @@ func initRouters() {
|
||||
beego.Router("/api/repositories/*/tags/:tag", &api.RepositoryAPI{}, "delete:Delete;get:GetTag")
|
||||
beego.Router("/api/repositories/*/tags/:tag/labels", &api.RepositoryLabelAPI{}, "get:GetOfImage;post:AddToImage")
|
||||
beego.Router("/api/repositories/*/tags/:tag/labels/:id([0-9]+)", &api.RepositoryLabelAPI{}, "delete:RemoveFromImage")
|
||||
beego.Router("/api/repositories/*/tags", &api.RepositoryAPI{}, "get:GetTags")
|
||||
beego.Router("/api/repositories/*/tags", &api.RepositoryAPI{}, "get:GetTags;post:Retag")
|
||||
beego.Router("/api/repositories/*/tags/:tag/scan", &api.RepositoryAPI{}, "post:ScanImage")
|
||||
beego.Router("/api/repositories/*/tags/:tag/vulnerability/details", &api.RepositoryAPI{}, "Get:VulnerabilityDetails")
|
||||
beego.Router("/api/repositories/*/tags/:tag/manifest", &api.RepositoryAPI{}, "get:GetManifests")
|
||||
|
@ -1,79 +0,0 @@
|
||||
// Copyright (c) 2017 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 api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/ui/utils"
|
||||
)
|
||||
|
||||
// RetagAPI retag an image
|
||||
type RetagAPI struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Retag tags an image to another
|
||||
func (r *RetagAPI) Retag() {
|
||||
if !r.SecurityCtx.IsAuthenticated() {
|
||||
r.HandleUnauthorized()
|
||||
return
|
||||
}
|
||||
|
||||
request := models.RetagRequest{}
|
||||
r.DecodeJSONReq(&request)
|
||||
|
||||
srcImage, err := models.ParseImage(request.SrcImage)
|
||||
if err != nil {
|
||||
r.HandleBadRequest(fmt.Sprintf("invalid src image string '%s', should in format '<project>/<repo>:<tag>'", request.SrcImage))
|
||||
return
|
||||
}
|
||||
destImage, err := models.ParseImage(request.DestImage)
|
||||
if err != nil {
|
||||
r.HandleBadRequest(fmt.Sprintf("invalid dest image string '%s', should in format '<project>/<repo>:<tag>'", request.DestImage))
|
||||
return
|
||||
}
|
||||
|
||||
if !dao.RepositoryExists(fmt.Sprintf("%s/%s", srcImage.Project, srcImage.Repo)) {
|
||||
log.Errorf("source repository '%s/%s' not exist", srcImage.Project, srcImage.Repo)
|
||||
r.HandleNotFound(fmt.Sprintf("repository '%s/%s' not found", srcImage.Project, srcImage.Repo))
|
||||
return
|
||||
}
|
||||
|
||||
if !dao.ProjectExistsByName(destImage.Project) {
|
||||
log.Errorf("destination project '%s' not exist", destImage.Project)
|
||||
r.HandleNotFound(fmt.Sprintf("project '%s' not found", destImage.Project))
|
||||
return
|
||||
}
|
||||
|
||||
if !r.SecurityCtx.HasReadPerm(srcImage.Project) {
|
||||
log.Errorf("user has no read permission to project '%s'", srcImage.Project)
|
||||
r.HandleUnauthorized()
|
||||
return
|
||||
}
|
||||
|
||||
if !r.SecurityCtx.HasWritePerm(destImage.Project) {
|
||||
log.Errorf("user has no write permission to project '%s'", destImage.Project)
|
||||
r.HandleUnauthorized()
|
||||
return
|
||||
}
|
||||
|
||||
if err = utils.Retag(srcImage, destImage); err != nil {
|
||||
r.HandleInternalServerError(fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user