From 6ce9d998d0b8e15d7a673626a54477a0bfc9f768 Mon Sep 17 00:00:00 2001 From: meilier 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