Updae retag api spec

Signed-off-by: 陈德 <chende@caicloud.io>
This commit is contained in:
陈德 2018-09-18 14:05:35 +08:00
parent 75f1cdb449
commit 03d5157eaf
6 changed files with 122 additions and 112 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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