337 lines
9.5 KiB
Diff
337 lines
9.5 KiB
Diff
From 6ce9d998d0b8e15d7a673626a54477a0bfc9f768 Mon Sep 17 00:00:00 2001
|
|
From: meilier <xingweizheng@huawei.com>
|
|
Date: Wed, 3 Feb 2021 01:04:17 +0800
|
|
Subject: [PATCH 08/10] check if add default tag to image name when using push
|
|
and save command
|
|
|
|
---
|
|
daemon/pull_test.go | 6 +-
|
|
daemon/push.go | 6 ++
|
|
daemon/push_test.go | 13 ++-
|
|
daemon/save.go | 8 ++
|
|
daemon/save_test.go | 193 ++++++++++++++++++++++++++++++++++++++++++++
|
|
image/image.go | 23 ++++++
|
|
6 files changed, 246 insertions(+), 3 deletions(-)
|
|
create mode 100644 daemon/save_test.go
|
|
|
|
diff --git a/daemon/pull_test.go b/daemon/pull_test.go
|
|
index 67459d19..27a4d6e8 100644
|
|
--- a/daemon/pull_test.go
|
|
+++ b/daemon/pull_test.go
|
|
@@ -58,7 +58,6 @@ func (c *controlPullServer) Send(response *pb.PullResponse) error {
|
|
|
|
func init() {
|
|
reexec.Init()
|
|
-
|
|
}
|
|
|
|
func prepare(t *testing.T) daemonTestOptions {
|
|
@@ -100,7 +99,10 @@ func TestPull(t *testing.T) {
|
|
defer tmpClean(d)
|
|
|
|
options := &storage.ImageOptions{}
|
|
- d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"image:test"}, "", "", options)
|
|
+ _, err := d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"image:test"}, "", "", options)
|
|
+ if err != nil {
|
|
+ t.Fatalf("create image with error: %v", err)
|
|
+ }
|
|
|
|
testcases := []struct {
|
|
name string
|
|
diff --git a/daemon/push.go b/daemon/push.go
|
|
index e6053dd8..4e3a6ed9 100644
|
|
--- a/daemon/push.go
|
|
+++ b/daemon/push.go
|
|
@@ -63,6 +63,12 @@ func (b *Backend) Push(req *pb.PushRequest, stream pb.Control_PushServer) error
|
|
return err
|
|
}
|
|
|
|
+ imageName, err := image.CheckAndAddDefaultTag(opt.imageName, opt.localStore)
|
|
+ if err != nil {
|
|
+ return err
|
|
+ }
|
|
+ opt.imageName = imageName
|
|
+
|
|
manifestType, gErr := exporter.GetManifestType(opt.format)
|
|
if gErr != nil {
|
|
return gErr
|
|
diff --git a/daemon/push_test.go b/daemon/push_test.go
|
|
index 573e97fe..f4a9e2b1 100644
|
|
--- a/daemon/push_test.go
|
|
+++ b/daemon/push_test.go
|
|
@@ -79,10 +79,21 @@ func TestPush(t *testing.T) {
|
|
Format: "oci",
|
|
},
|
|
},
|
|
+ {
|
|
+ name: "manifestNotExist fine without tag latest",
|
|
+ pushRequest: &pb.PushRequest{
|
|
+ PushID: stringid.GenerateNonCryptoID()[:constant.DefaultIDLen],
|
|
+ ImageName: "127.0.0.1/no-repository/no-name",
|
|
+ Format: "oci",
|
|
+ },
|
|
+ },
|
|
}
|
|
|
|
options := &storage.ImageOptions{}
|
|
- d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"127.0.0.1/no-repository/no-name:latest"}, "", "", options)
|
|
+ _, err := d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"127.0.0.1/no-repository/no-name:latest"}, "", "", options)
|
|
+ if err != nil {
|
|
+ t.Fatalf("create image with error: %v", err)
|
|
+ }
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
diff --git a/daemon/save.go b/daemon/save.go
|
|
index c6411e04..fd6174b4 100644
|
|
--- a/daemon/save.go
|
|
+++ b/daemon/save.go
|
|
@@ -79,6 +79,14 @@ func (b *Backend) Save(req *pb.SaveRequest, stream pb.Control_SaveServer) error
|
|
return errors.New("wrong image format provided")
|
|
}
|
|
|
|
+ for i, imageName := range opts.oriImgList {
|
|
+ nameWithTag, cErr := image.CheckAndAddDefaultTag(imageName, opts.localStore)
|
|
+ if cErr != nil {
|
|
+ return cErr
|
|
+ }
|
|
+ opts.oriImgList[i] = nameWithTag
|
|
+ }
|
|
+
|
|
defer func() {
|
|
if err != nil {
|
|
if rErr := os.Remove(opts.outputPath); rErr != nil && !os.IsNotExist(rErr) {
|
|
diff --git a/daemon/save_test.go b/daemon/save_test.go
|
|
new file mode 100644
|
|
index 00000000..a59086a8
|
|
--- /dev/null
|
|
+++ b/daemon/save_test.go
|
|
@@ -0,0 +1,193 @@
|
|
+// Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved.
|
|
+// isula-build licensed under the Mulan PSL v2.
|
|
+// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
+// You may obtain a copy of Mulan PSL v2 at:
|
|
+// http://license.coscl.org.cn/MulanPSL2
|
|
+// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
+// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
+// PURPOSE.
|
|
+// See the Mulan PSL v2 for more details.
|
|
+// Author: Weizheng Xing
|
|
+// Create: 2020-02-03
|
|
+// Description: This file tests Save interface
|
|
+
|
|
+package daemon
|
|
+
|
|
+import (
|
|
+ "context"
|
|
+ "testing"
|
|
+
|
|
+ "github.com/containers/storage"
|
|
+ "github.com/containers/storage/pkg/reexec"
|
|
+ "github.com/containers/storage/pkg/stringid"
|
|
+ "github.com/pkg/errors"
|
|
+ "golang.org/x/sync/errgroup"
|
|
+ "google.golang.org/grpc"
|
|
+ "gotest.tools/v3/assert"
|
|
+ "gotest.tools/v3/fs"
|
|
+
|
|
+ constant "isula.org/isula-build"
|
|
+ pb "isula.org/isula-build/api/services"
|
|
+ _ "isula.org/isula-build/exporter/register"
|
|
+ "isula.org/isula-build/pkg/logger"
|
|
+)
|
|
+
|
|
+type controlSaveServer struct {
|
|
+ grpc.ServerStream
|
|
+}
|
|
+
|
|
+func (c *controlSaveServer) Context() context.Context {
|
|
+ return context.Background()
|
|
+}
|
|
+
|
|
+func (c *controlSaveServer) Send(response *pb.SaveResponse) error {
|
|
+ if response.Log == "error" {
|
|
+ return errors.New("error happened")
|
|
+ }
|
|
+ return nil
|
|
+}
|
|
+
|
|
+func init() {
|
|
+ reexec.Init()
|
|
+}
|
|
+
|
|
+func TestSave(t *testing.T) {
|
|
+ d := prepare(t)
|
|
+ defer tmpClean(d)
|
|
+
|
|
+ //TODO: create image manually and save
|
|
+ options := &storage.ImageOptions{}
|
|
+ img, err := d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"image:latest"}, "", "", options)
|
|
+ if err != nil {
|
|
+ t.Fatalf("create image with error: %v", err)
|
|
+ }
|
|
+
|
|
+ _, err = d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"image2:test"}, "", "", options)
|
|
+ if err != nil {
|
|
+ t.Fatalf("create image with error: %v", err)
|
|
+ }
|
|
+
|
|
+ tempTarfileDir := fs.NewDir(t, t.Name())
|
|
+ defer tempTarfileDir.Remove()
|
|
+
|
|
+ testcases := []struct {
|
|
+ name string
|
|
+ req *pb.SaveRequest
|
|
+ wantErr bool
|
|
+ errString string
|
|
+ }{
|
|
+ {
|
|
+ name: "normal case save with repository[:tag]",
|
|
+ req: &pb.SaveRequest{
|
|
+ SaveID: stringid.GenerateNonCryptoID()[:constant.DefaultIDLen],
|
|
+ Images: []string{"image:latest"},
|
|
+ Path: tempTarfileDir.Join("repotag.tar"),
|
|
+ Format: "docker",
|
|
+ },
|
|
+ wantErr: true,
|
|
+ errString: "file does not exist",
|
|
+ },
|
|
+ {
|
|
+ name: "normal case save with repository add default latest",
|
|
+ req: &pb.SaveRequest{
|
|
+ SaveID: stringid.GenerateNonCryptoID()[:constant.DefaultIDLen],
|
|
+ Images: []string{"image"},
|
|
+ Path: tempTarfileDir.Join("repolatest.tar"),
|
|
+ Format: "oci",
|
|
+ },
|
|
+ wantErr: true,
|
|
+ errString: "file does not exist",
|
|
+ },
|
|
+ {
|
|
+ name: "normal case with imageid",
|
|
+ req: &pb.SaveRequest{
|
|
+ SaveID: stringid.GenerateNonCryptoID()[:constant.DefaultIDLen],
|
|
+ Images: []string{img.ID},
|
|
+ Path: tempTarfileDir.Join("imageid.tar"),
|
|
+ Format: "docker",
|
|
+ },
|
|
+ wantErr: true,
|
|
+ errString: "file does not exist",
|
|
+ },
|
|
+ {
|
|
+ name: "normal case save multiple images with repository and ID",
|
|
+ req: &pb.SaveRequest{
|
|
+ SaveID: stringid.GenerateNonCryptoID()[:constant.DefaultIDLen],
|
|
+ Images: []string{"image2:test", img.ID},
|
|
+ Path: tempTarfileDir.Join("double.tar"),
|
|
+ Format: "docker",
|
|
+ },
|
|
+ wantErr: true,
|
|
+ errString: "file does not exist",
|
|
+ },
|
|
+ {
|
|
+ name: "abnormal case save image that not exist in local store",
|
|
+ req: &pb.SaveRequest{
|
|
+ SaveID: stringid.GenerateNonCryptoID()[:constant.DefaultIDLen],
|
|
+ Images: []string{"noexist", img.ID},
|
|
+ Path: tempTarfileDir.Join("notexist.tar"),
|
|
+ Format: "docker",
|
|
+ },
|
|
+ wantErr: true,
|
|
+ errString: "failed to parse image",
|
|
+ },
|
|
+ {
|
|
+ name: "abnormal case wrong image format",
|
|
+ req: &pb.SaveRequest{
|
|
+ SaveID: stringid.GenerateNonCryptoID()[:constant.DefaultIDLen],
|
|
+ Images: []string{"image", img.ID},
|
|
+ Path: tempTarfileDir.Join("image.tar"),
|
|
+ Format: "dock",
|
|
+ },
|
|
+ wantErr: true,
|
|
+ errString: "wrong image format provided",
|
|
+ },
|
|
+ }
|
|
+
|
|
+ for _, tc := range testcases {
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
+ stream := &controlSaveServer{}
|
|
+
|
|
+ err := d.Daemon.backend.Save(tc.req, stream)
|
|
+ if tc.wantErr == true {
|
|
+ assert.ErrorContains(t, err, tc.errString)
|
|
+ }
|
|
+ if tc.wantErr == false {
|
|
+ assert.NilError(t, err)
|
|
+ }
|
|
+ })
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+func TestSaveHandler(t *testing.T) {
|
|
+ ctx := context.TODO()
|
|
+ eg, _ := errgroup.WithContext(ctx)
|
|
+
|
|
+ eg.Go(saveHandlerPrint("Push Response"))
|
|
+ eg.Go(saveHandlerPrint(""))
|
|
+ eg.Go(saveHandlerPrint("error"))
|
|
+
|
|
+ eg.Wait()
|
|
+}
|
|
+
|
|
+func saveHandlerPrint(message string) func() error {
|
|
+ return func() error {
|
|
+ stream := &controlSaveServer{}
|
|
+ cliLogger := logger.NewCliLogger(constant.CliLogBufferLen)
|
|
+
|
|
+ ctx := context.TODO()
|
|
+ eg, _ := errgroup.WithContext(ctx)
|
|
+
|
|
+ eg.Go(messageHandler(stream, cliLogger))
|
|
+ eg.Go(func() error {
|
|
+ cliLogger.Print(message)
|
|
+ cliLogger.CloseContent()
|
|
+ return nil
|
|
+ })
|
|
+
|
|
+ eg.Wait()
|
|
+
|
|
+ return nil
|
|
+ }
|
|
+}
|
|
diff --git a/image/image.go b/image/image.go
|
|
index 36785bdf..1e480391 100644
|
|
--- a/image/image.go
|
|
+++ b/image/image.go
|
|
@@ -689,3 +689,26 @@ func tryResolveNameInRegistries(name string, sc *types.SystemContext) ([]string,
|
|
}
|
|
return candidates, exporter.DockerTransport
|
|
}
|
|
+
|
|
+// CheckAndAddDefaultTag checks if src is format of repository[:tag], add default tag if src without tag
|
|
+func CheckAndAddDefaultTag(imageName string, store *store.Store) (string, error) {
|
|
+ _, img, err := FindImage(store, imageName)
|
|
+ if err != nil {
|
|
+ return "", errors.Wrapf(err, "find src image: %q failed", imageName)
|
|
+ }
|
|
+
|
|
+ defaultTag := "latest"
|
|
+ for _, name := range img.Names {
|
|
+ // for imageName is the format of repository[:tag]
|
|
+ if imageName == name {
|
|
+ return imageName, nil
|
|
+ }
|
|
+ // for name is the format of repository
|
|
+ if fmt.Sprintf("%s:%s", imageName, defaultTag) == name {
|
|
+ return name, nil
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // for imageName is the format of imageID
|
|
+ return imageName, nil
|
|
+}
|
|
--
|
|
2.27.0
|
|
|