mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-29 21:54:13 +01:00
Update swagger file to add retag API
Signed-off-by: 陈德 <chende@caicloud.io>
This commit is contained in:
parent
48d2435146
commit
75f1cdb449
@ -41,6 +41,32 @@ paths:
|
|||||||
$ref: '#/definitions/Search'
|
$ref: '#/definitions/Search'
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
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:
|
/projects:
|
||||||
get:
|
get:
|
||||||
summary: List projects
|
summary: List projects
|
||||||
@ -2966,6 +2992,15 @@ definitions:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/SearchResult'
|
$ref: '#/definitions/SearchResult'
|
||||||
|
RetagReq:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
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
|
||||||
SearchRepository:
|
SearchRepository:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -19,17 +19,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RetagRequest gives the source image and target image of retag
|
||||||
type RetagRequest struct {
|
type RetagRequest struct {
|
||||||
SrcImage string `json:"src_image"`
|
SrcImage string `json:"src_image"`
|
||||||
DestImage string `json:"dest_image"`
|
DestImage string `json:"dest_image"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Image holds each part (project, repo, tag) of an image name
|
||||||
type Image struct {
|
type Image struct {
|
||||||
Project string
|
Project string
|
||||||
Repo string
|
Repo string
|
||||||
Tag string
|
Tag string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseImage parses an image name such as 'library/app:v1.0' to a structure with
|
||||||
|
// project, repo, and tag fields
|
||||||
func ParseImage(image string) (*Image, error) {
|
func ParseImage(image string) (*Image, error) {
|
||||||
repo := strings.SplitN(image, "/", 2)
|
repo := strings.SplitN(image, "/", 2)
|
||||||
if len(repo) < 2 {
|
if len(repo) < 2 {
|
||||||
@ -38,10 +42,10 @@ func ParseImage(image string) (*Image, error) {
|
|||||||
i := strings.SplitN(repo[1], ":", 2)
|
i := strings.SplitN(repo[1], ":", 2)
|
||||||
res := &Image{
|
res := &Image{
|
||||||
Project: repo[0],
|
Project: repo[0],
|
||||||
Repo: i[0],
|
Repo: i[0],
|
||||||
}
|
}
|
||||||
if len(i) == 2 {
|
if len(i) == 2 {
|
||||||
res.Tag = i[1]
|
res.Tag = i[1]
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -15,23 +15,24 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseImage(t *testing.T) {
|
func TestParseImage(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Input string
|
Input string
|
||||||
Expected *Image
|
Expected *Image
|
||||||
Valid bool
|
Valid bool
|
||||||
} {
|
}{
|
||||||
{
|
{
|
||||||
Input: "library/busybox",
|
Input: "library/busybox",
|
||||||
Expected: &Image{
|
Expected: &Image{
|
||||||
Project: "library",
|
Project: "library",
|
||||||
Repo: "busybox",
|
Repo: "busybox",
|
||||||
Tag: "",
|
Tag: "",
|
||||||
},
|
},
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
@ -39,8 +40,8 @@ func TestParseImage(t *testing.T) {
|
|||||||
Input: "library/busybox:v1.0",
|
Input: "library/busybox:v1.0",
|
||||||
Expected: &Image{
|
Expected: &Image{
|
||||||
Project: "library",
|
Project: "library",
|
||||||
Repo: "busybox",
|
Repo: "busybox",
|
||||||
Tag: "v1.0",
|
Tag: "v1.0",
|
||||||
},
|
},
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
@ -62,4 +63,4 @@ func TestParseImage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,6 +258,7 @@ func (r *Repository) DeleteManifest(digest string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MountBlob ...
|
||||||
func (r *Repository) MountBlob(digest, from string) error {
|
func (r *Repository) MountBlob(digest, from string) error {
|
||||||
req, err := http.NewRequest("POST", buildMountBlobURL(r.Endpoint.String(), r.Name, digest, from), nil)
|
req, err := http.NewRequest("POST", buildMountBlobURL(r.Endpoint.String(), r.Name, digest, from), nil)
|
||||||
req.Header.Set(http.CanonicalHeaderKey("Content-Length"), "0")
|
req.Header.Set(http.CanonicalHeaderKey("Content-Length"), "0")
|
||||||
|
@ -28,6 +28,7 @@ type RetagAPI struct {
|
|||||||
BaseController
|
BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retag tags an image to another
|
||||||
func (r *RetagAPI) Retag() {
|
func (r *RetagAPI) Retag() {
|
||||||
if !r.SecurityCtx.IsAuthenticated() {
|
if !r.SecurityCtx.IsAuthenticated() {
|
||||||
r.HandleUnauthorized()
|
r.HandleUnauthorized()
|
||||||
|
@ -25,13 +25,15 @@ import (
|
|||||||
"github.com/docker/distribution/manifest/schema2"
|
"github.com/docker/distribution/manifest/schema2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Retag tags an image to another
|
||||||
func Retag(srcImage, destImage *models.Image) error {
|
func Retag(srcImage, destImage *models.Image) error {
|
||||||
|
isSameRepo := getRepoName(srcImage) == getRepoName(destImage)
|
||||||
srcClient, err := NewRepositoryClientForUI("harbor-ui", getRepoName(srcImage))
|
srcClient, err := NewRepositoryClientForUI("harbor-ui", getRepoName(srcImage))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
destClient := srcClient
|
destClient := srcClient
|
||||||
if getRepoName(srcImage) != getRepoName(destImage) {
|
if !isSameRepo {
|
||||||
destClient, err = NewRepositoryClientForUI("harbor-ui", getRepoName(destImage))
|
destClient, err = NewRepositoryClientForUI("harbor-ui", getRepoName(destImage))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -70,7 +72,7 @@ func Retag(srcImage, destImage *models.Image) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if getRepoName(srcImage) != getRepoName(destImage) {
|
if !isSameRepo {
|
||||||
for _, descriptor := range manifest.References() {
|
for _, descriptor := range manifest.References() {
|
||||||
err := destClient.MountBlob(descriptor.Digest.String(), srcClient.Name)
|
err := destClient.MountBlob(descriptor.Digest.String(), srcClient.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user