From 950f2f8ba22dfe373c523fbdf94e53ca61363217 Mon Sep 17 00:00:00 2001 From: DCCooper <1866858@gmail.com> Date: Wed, 3 Mar 2021 19:29:30 +0800 Subject: [PATCH] isula-build:Sync upstream patches Signed-off-by: DCCooper <1866858@gmail.com> --- VERSION-openeuler | 2 +- git-commit | 2 +- isula-build.spec | 8 +- ...me-make-checkall-golangci-lint-flaws.patch | 136 ++++++ ...nt-add-go-test-for-RUN-panic-problem.patch | 66 +++ patch/0044-fix-load-oci-image-panic.patch | 340 +++++++++++++++ ...es-command-when-only-give-repository.patch | 409 ++++++++++++++++++ ...ault-tag-to-image-name-when-using-pu.patch | 336 ++++++++++++++ ...ndTag-return-empty-when-tag-is-empty.patch | 55 +++ ...counting-length-of-fields-to-avoid-p.patch | 111 +++++ series.conf | 7 + 11 files changed, 1469 insertions(+), 3 deletions(-) create mode 100644 patch/0042-fix-some-make-checkall-golangci-lint-flaws.patch create mode 100644 patch/0043-enhancement-add-go-test-for-RUN-panic-problem.patch create mode 100644 patch/0044-fix-load-oci-image-panic.patch create mode 100644 patch/0045-fix-images-command-when-only-give-repository.patch create mode 100644 patch/0046-check-if-add-default-tag-to-image-name-when-using-pu.patch create mode 100644 patch/0047-checkAndExpandTag-return-empty-when-tag-is-empty.patch create mode 100644 patch/0048-trim-space-when-counting-length-of-fields-to-avoid-p.patch diff --git a/VERSION-openeuler b/VERSION-openeuler index bd14de0..ee93a79 100644 --- a/VERSION-openeuler +++ b/VERSION-openeuler @@ -1 +1 @@ -0.9.5-5 +0.9.5-6 diff --git a/git-commit b/git-commit index 316482f..0ab27e6 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -4678e505019fae52f1801e172fd19c84ddfc0a70 +b82408f23540642f79ab000483086997321305bf diff --git a/isula-build.spec b/isula-build.spec index 9e2c697..b41d432 100644 --- a/isula-build.spec +++ b/isula-build.spec @@ -2,7 +2,7 @@ Name: isula-build Version: 0.9.5 -Release: 5 +Release: 6 Summary: A tool to build container images License: Mulan PSL V2 URL: https://gitee.com/openeuler/isula-build @@ -85,6 +85,12 @@ fi /usr/share/bash-completion/completions/isula-build %changelog +* Wed Mar 03 2021 lixiang - 0.9.5-6 +- Type:enhancement +- CVE:NA +- SUG:restart +- DESC:sync patches from upstream + * Wed Feb 10 2021 lixiang - 0.9.5-5 - Type:enhancement - CVE:NA diff --git a/patch/0042-fix-some-make-checkall-golangci-lint-flaws.patch b/patch/0042-fix-some-make-checkall-golangci-lint-flaws.patch new file mode 100644 index 0000000..c6f5b1e --- /dev/null +++ b/patch/0042-fix-some-make-checkall-golangci-lint-flaws.patch @@ -0,0 +1,136 @@ +From 34fdae49f82410a8bcc9c6f5940af01a24538de6 Mon Sep 17 00:00:00 2001 +From: meilier +Date: Thu, 4 Feb 2021 18:40:31 +0800 +Subject: [PATCH 01/10] fix some make checkall golangci-lint flaws + +--- + builder/dockerfile/container/container_src.go | 2 +- + builder/dockerfile/container/help.go | 2 +- + daemon/load.go | 4 ++-- + daemon/login.go | 4 ++-- + daemon/logout.go | 4 ++-- + daemon/save.go | 2 -- + pkg/manifest/list.go | 2 +- + util/util.go | 2 -- + 8 files changed, 9 insertions(+), 13 deletions(-) + +diff --git a/builder/dockerfile/container/container_src.go b/builder/dockerfile/container/container_src.go +index ff52ee2c..9426ec76 100644 +--- a/builder/dockerfile/container/container_src.go ++++ b/builder/dockerfile/container/container_src.go +@@ -98,7 +98,7 @@ func (i *containerImageSource) GetBlob(ctx context.Context, blob types.BlobInfo, + return nil, -1, errors.Wrapf(err, "blob file %q is not exit", blobFile) + } + +- layerFile, err := os.OpenFile(blobFile, os.O_RDONLY, constant.DefaultRootFileMode) ++ layerFile, err := os.OpenFile(filepath.Clean(blobFile), os.O_RDONLY, constant.DefaultRootFileMode) + if err != nil { + return nil, -1, errors.Wrapf(err, "open the blob file %q failed", blobFile) + } +diff --git a/builder/dockerfile/container/help.go b/builder/dockerfile/container/help.go +index c5aa381d..475b479d 100644 +--- a/builder/dockerfile/container/help.go ++++ b/builder/dockerfile/container/help.go +@@ -170,7 +170,7 @@ func (ref *Reference) saveLayerToStorage(path string, layer *storage.Layer) (dif + }() + + filename := filepath.Join(path, "layer") +- layerFile, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, constant.DefaultRootFileMode) ++ layerFile, err := os.OpenFile(filepath.Clean(filename), os.O_CREATE|os.O_WRONLY, constant.DefaultRootFileMode) + if err != nil { + return "", des, errors.Wrapf(err, "error opening file: %s", filename) + } +diff --git a/daemon/load.go b/daemon/load.go +index 08fb5b1f..d756f9ed 100644 +--- a/daemon/load.go ++++ b/daemon/load.go +@@ -55,8 +55,8 @@ func (b *Backend) Load(req *pb.LoadRequest, stream pb.Control_LoadServer) error + ) + opts := b.getLoadOptions(req) + +- if err := util.CheckLoadFile(req.Path); err != nil { +- return err ++ if cErr := util.CheckLoadFile(req.Path); cErr != nil { ++ return cErr + } + + repoTags, err = tryToParseImageFormatFromTarball(b.daemon.opts.DataRoot, &opts) +diff --git a/daemon/login.go b/daemon/login.go +index e3399983..6eeda28e 100644 +--- a/daemon/login.go ++++ b/daemon/login.go +@@ -60,8 +60,8 @@ func (b *Backend) Login(ctx context.Context, req *pb.LoginRequest) (*pb.LoginRes + } + + if loginWithAuthFile(req) { +- auth, err := config.GetCredentials(sysCtx, req.Server) +- if err != nil || auth.Password == "" { ++ auth, gErr := config.GetCredentials(sysCtx, req.Server) ++ if gErr != nil || auth.Password == "" { + auth = types.DockerAuthConfig{} + return &pb.LoginResponse{Content: errTryToUseAuth}, errors.Errorf("failed to read auth file: %v", errTryToUseAuth) + } +diff --git a/daemon/logout.go b/daemon/logout.go +index 355b1f7a..d1fbebcb 100644 +--- a/daemon/logout.go ++++ b/daemon/logout.go +@@ -47,8 +47,8 @@ func (b *Backend) Logout(ctx context.Context, req *pb.LogoutRequest) (*pb.Logout + } + + if req.All { +- if err := config.RemoveAllAuthentication(sysCtx); err != nil { +- return &pb.LogoutResponse{Result: "Remove authentications failed"}, err ++ if rErr := config.RemoveAllAuthentication(sysCtx); rErr != nil { ++ return &pb.LogoutResponse{Result: "Remove authentications failed"}, rErr + } + logrus.Info("Success logout from all servers") + +diff --git a/daemon/save.go b/daemon/save.go +index 3dce7bdf..c6411e04 100644 +--- a/daemon/save.go ++++ b/daemon/save.go +@@ -17,7 +17,6 @@ import ( + "context" + "os" + +- "github.com/containers/image/v5/docker/archive" + "github.com/containers/image/v5/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +@@ -34,7 +33,6 @@ import ( + ) + + type saveOptions struct { +- writer *archive.Writer + sysCtx *types.SystemContext + logger *logger.Logger + localStore *store.Store +diff --git a/pkg/manifest/list.go b/pkg/manifest/list.go +index 3f8b2fed..381746f7 100644 +--- a/pkg/manifest/list.go ++++ b/pkg/manifest/list.go +@@ -129,7 +129,7 @@ func (l *List) SaveListToImage(store *store.Store, imageID, name string) (string + return "", errors.Wrapf(err, "save manifest list to image %v error", imageID) + } + +- //marshal list instance information ++ // marshal list instance information + instancesBytes, err := json.Marshal(&l.instances) + if err != nil { + return "", errors.Wrap(err, "marshall list instances error") +diff --git a/util/util.go b/util/util.go +index 61458c73..3f46d796 100644 +--- a/util/util.go ++++ b/util/util.go +@@ -57,8 +57,6 @@ const ( + var ( + // DefaultRegistryPathPrefix is the map for registry and path + DefaultRegistryPathPrefix map[string]string +- // clientExporters to map exporter whether will export the image to client +- clientExporters map[string]bool + ) + + func init() { +-- +2.27.0 + diff --git a/patch/0043-enhancement-add-go-test-for-RUN-panic-problem.patch b/patch/0043-enhancement-add-go-test-for-RUN-panic-problem.patch new file mode 100644 index 0000000..ec828c8 --- /dev/null +++ b/patch/0043-enhancement-add-go-test-for-RUN-panic-problem.patch @@ -0,0 +1,66 @@ +From c0d4159d7719fb94b4b421415b8f367c6f61c68e Mon Sep 17 00:00:00 2001 +From: DCCooper <1866858@gmail.com> +Date: Mon, 8 Feb 2021 10:22:33 +0800 +Subject: [PATCH 02/10] enhancement: add go test for RUN panic problem + +Signed-off-by: DCCooper <1866858@gmail.com> +--- + builder/dockerfile/parser/parser_test.go | 18 ++++++++++++++---- + .../testfiles/preprocess/run_with_directive | 2 ++ + 2 files changed, 16 insertions(+), 4 deletions(-) + create mode 100644 builder/dockerfile/parser/testfiles/preprocess/run_with_directive + +diff --git a/builder/dockerfile/parser/parser_test.go b/builder/dockerfile/parser/parser_test.go +index f0cce1e9..3da5bea6 100644 +--- a/builder/dockerfile/parser/parser_test.go ++++ b/builder/dockerfile/parser/parser_test.go +@@ -69,8 +69,9 @@ func TestPreProcess(t *testing.T) { + + func TestFormat(t *testing.T) { + type testcase struct { +- name string +- expect int ++ name string ++ expect int ++ wantErr bool + } + var testcases = []testcase{ + { +@@ -89,6 +90,10 @@ func TestFormat(t *testing.T) { + name: "yum_config", + expect: 8, + }, ++ { ++ name: "run_with_directive", ++ wantErr: true, ++ }, + } + + for _, tc := range testcases { +@@ -103,8 +108,13 @@ func TestFormat(t *testing.T) { + d, err := newDirective(bytes.NewReader(buf.Bytes())) + assert.NilError(t, err) + lines, err := format(rows, d) +- assert.NilError(t, err) +- assert.Equal(t, len(lines), tc.expect) ++ if (err != nil) != tc.wantErr { ++ t.Errorf("Testing failed. Expected: %v, got: %v", tc.wantErr, err) ++ } ++ if !tc.wantErr { ++ assert.NilError(t, err, file) ++ assert.Equal(t, len(lines), tc.expect) ++ } + }) + } + } +diff --git a/builder/dockerfile/parser/testfiles/preprocess/run_with_directive b/builder/dockerfile/parser/testfiles/preprocess/run_with_directive +new file mode 100644 +index 00000000..3f3465d3 +--- /dev/null ++++ b/builder/dockerfile/parser/testfiles/preprocess/run_with_directive +@@ -0,0 +1,2 @@ ++FROM scratch ++RUN \ +-- +2.27.0 + diff --git a/patch/0044-fix-load-oci-image-panic.patch b/patch/0044-fix-load-oci-image-panic.patch new file mode 100644 index 0000000..cc4bd3f --- /dev/null +++ b/patch/0044-fix-load-oci-image-panic.patch @@ -0,0 +1,340 @@ +From 947fc1ef0c103f687e195c467ddabd3cf0aa746f Mon Sep 17 00:00:00 2001 +From: meilier +Date: Sat, 20 Feb 2021 00:42:55 +0800 +Subject: [PATCH 06/10] fix load oci image panic + +--- + cmd/cli/save.go | 3 + + cmd/cli/save_test.go | 18 +++++ + daemon/load.go | 11 +-- + daemon/load_test.go | 188 +++++++++++++++++++++++++++++++++++-------- + 4 files changed, 181 insertions(+), 39 deletions(-) + +diff --git a/cmd/cli/save.go b/cmd/cli/save.go +index 64dc8acc..fe676731 100644 +--- a/cmd/cli/save.go ++++ b/cmd/cli/save.go +@@ -72,6 +72,9 @@ func saveCommand(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("save accepts at least one image") + } ++ if saveOpts.format == exporter.OCITransport && len(args) >= 2 { ++ return errors.New("oci image format now only supports saving single image") ++ } + if err := exporter.CheckImageFormat(saveOpts.format); err != nil { + return err + } +diff --git a/cmd/cli/save_test.go b/cmd/cli/save_test.go +index 4183aa8b..3fe6bf81 100644 +--- a/cmd/cli/save_test.go ++++ b/cmd/cli/save_test.go +@@ -38,6 +38,8 @@ func TestSaveCommand(t *testing.T) { + wantErr bool + } + ++ // For normal cases, default err is "invalid socket path: unix:///var/run/isula_build.sock". ++ // As daemon is not running as we run unit test. + var testcases = []testcase{ + { + name: "TC1 - normal case with format docker", +@@ -103,6 +105,22 @@ func TestSaveCommand(t *testing.T) { + errString: "colon in path", + format: "docker", + }, ++ { ++ name: "TC9 - normal case save multiple images with format docker", ++ path: tmpDir.Join("test9"), ++ args: []string{"testImage1", "testImage2"}, ++ wantErr: true, ++ errString: "isula_build.sock", ++ format: "docker", ++ }, ++ { ++ name: "TC10 - abnormal case save multiple images with format oci", ++ path: tmpDir.Join("test10"), ++ args: []string{"testImage1", "testImage2"}, ++ wantErr: true, ++ errString: "oci image format now only supports saving single image", ++ format: "oci", ++ }, + } + + for _, tc := range testcases { +diff --git a/daemon/load.go b/daemon/load.go +index d756f9ed..b557d386 100644 +--- a/daemon/load.go ++++ b/daemon/load.go +@@ -147,7 +147,6 @@ func tryToParseImageFormatFromTarball(dataRoot string, opts *loadOptions) ([][]s + + func getDockerRepoTagFromImageTar(systemContext *types.SystemContext, path string) ([][]string, error) { + // tmp dir will be removed after NewSourceFromFileWithContext +- + tarfileSource, err := tarfile.NewSourceFromFileWithContext(systemContext, path) + if err != nil { + return nil, errors.Wrapf(err, "failed to get the source of loading tar file") +@@ -168,8 +167,7 @@ func getDockerRepoTagFromImageTar(systemContext *types.SystemContext, path strin + + func getOCIRepoTagFromImageTar(systemContext *types.SystemContext, path string) ([][]string, error) { + var ( +- allRepoTags [][]string +- err error ++ err error + ) + + srcRef, err := alltransports.ParseImageName(exporter.FormatTransport(exporter.OCIArchiveTransport, path)) +@@ -179,14 +177,13 @@ func getOCIRepoTagFromImageTar(systemContext *types.SystemContext, path string) + + tarManifest, err := ociarchive.LoadManifestDescriptorWithContext(systemContext, srcRef) + if err != nil { +- return nil, errors.Wrapf(err, "failed to loadmanifest descriptor of oci image format") ++ return nil, errors.Wrapf(err, "failed to load manifest descriptor of oci image format") + } + +- // If index.json has no reference name, compute the image digest instead + // For now, we only support load single image in archive file + if _, ok := tarManifest.Annotations[imgspecv1.AnnotationRefName]; ok { +- allRepoTags = [][]string{{tarManifest.Annotations[imgspecv1.AnnotationRefName]}} ++ return [][]string{{tarManifest.Annotations[imgspecv1.AnnotationRefName]}}, nil + } + +- return allRepoTags, nil ++ return [][]string{{}}, nil + } +diff --git a/daemon/load_test.go b/daemon/load_test.go +index 0513a889..cbcb5d8f 100644 +--- a/daemon/load_test.go ++++ b/daemon/load_test.go +@@ -30,6 +30,12 @@ import ( + "isula.org/isula-build/store" + ) + ++const ( ++ loadedTarFile = "load.tar" ++ manifestJSONFile = "manifest.json" ++ indexJSONFile = "index.json" ++) ++ + var ( + localStore store.Store + daemon *Daemon +@@ -51,10 +57,10 @@ func (x *controlLoadServer) Context() context.Context { + return context.Background() + } + +-func prepareLoadTar(dir *fs.Dir) error { +- manifest := dir.Join("manifest.json") ++func prepareLoadTar(dir *fs.Dir, jsonFile string) error { ++ manifest := dir.Join(jsonFile) + +- fi, err := os.Create(dir.Join("load.tar")) ++ fi, err := os.Create(dir.Join(loadedTarFile)) + if err != nil { + return nil + } +@@ -88,9 +94,9 @@ func prepareLoadTar(dir *fs.Dir) error { + + } + +-func prepareForLoad(t *testing.T, manifest string) *fs.Dir { +- tmpDir := fs.NewDir(t, t.Name(), fs.WithFile("manifest.json", manifest)) +- if err := prepareLoadTar(tmpDir); err != nil { ++func prepareForLoad(t *testing.T, jsonFile, manifest string) *fs.Dir { ++ tmpDir := fs.NewDir(t, t.Name(), fs.WithFile(jsonFile, manifest)) ++ if err := prepareLoadTar(tmpDir, jsonFile); err != nil { + tmpDir.Remove() + return nil + } +@@ -119,34 +125,152 @@ func clean(dir *fs.Dir) { + dir.Remove() + } + +-func TestLoad(t *testing.T) { +- manifestJSON := +- `[ +- { +- "Config":"76a4dd2d5d6a18323ac8d90f959c3c8562bf592e2a559bab9b462ab600e9e5fc.json", +- "RepoTags":[ +- "hello:latest" +- ], +- "Layers":[ +- "6eb4c21cc3fcb729a9df230ae522c1d3708ca66e5cf531713dbfa679837aa287.tar", +- "37841116ad3b1eeea972c75ab8bad05f48f721a7431924bc547fc91c9076c1c8.tar" +- ] ++func TestLoadSingleImage(t *testing.T) { ++ testcases := []struct { ++ name string ++ manifest string ++ format string ++ tarPath string ++ withTag bool ++ wantErr bool ++ errString string ++ }{ ++ { ++ name: "TC1 normal case load docker tar", ++ manifest: `[ ++ { ++ "Config":"76a4dd2d5d6a18323ac8d90f959c3c8562bf592e2a559bab9b462ab600e9e5fc.json", ++ "RepoTags":[ ++ "hello:latest" ++ ], ++ "Layers":[ ++ "6eb4c21cc3fcb729a9df230ae522c1d3708ca66e5cf531713dbfa679837aa287.tar", ++ "37841116ad3b1eeea972c75ab8bad05f48f721a7431924bc547fc91c9076c1c8.tar" ++ ] ++ } ++ ]`, ++ format: "docker", ++ withTag: true, ++ }, ++ { ++ name: "TC2 normal case load oci tar", ++ manifest: `{ ++ "schemaVersion": 2, ++ "manifests": [ ++ { ++ "mediaType": "application/vnd.oci.image.manifest.v1+json", ++ "digest": "sha256:a65db259a719d915df30c82ce554ab3880ea567e2150d6288580408c2629b802", ++ "size": 347, ++ "annotations": { ++ "org.opencontainers.image.ref.name": "hello:latest" ++ } ++ } ++ ] ++ }`, ++ format: "oci", ++ withTag: true, ++ }, ++ { ++ name: "TC3 normal case load docker tar with no RepoTags", ++ manifest: `[ ++ { ++ "Config":"76a4dd2d5d6a18323ac8d90f959c3c8562bf592e2a559bab9b462ab600e9e5fc.json", ++ "RepoTags":[], ++ "Layers":[ ++ "6eb4c21cc3fcb729a9df230ae522c1d3708ca66e5cf531713dbfa679837aa287.tar", ++ "37841116ad3b1eeea972c75ab8bad05f48f721a7431924bc547fc91c9076c1c8.tar" ++ ] ++ } ++ ]`, ++ format: "docker", ++ withTag: false, ++ }, ++ { ++ name: "TC4 normal case load oci tar with no annotations", ++ manifest: `{ ++ "schemaVersion": 2, ++ "manifests": [ ++ { ++ "mediaType": "application/vnd.oci.image.manifest.v1+json", ++ "digest": "sha256:a65db259a719d915df30c82ce554ab3880ea567e2150d6288580408c2629b802", ++ "size": 347 ++ } ++ ] ++ }`, ++ format: "oci", ++ withTag: false, ++ }, ++ { ++ name: "TC5 abnormal case load docker tar with wrong manifestJSON", ++ manifest: `[ ++ { ++ :"76a4dd2d5d6a18323ac8d90f959c3c8562bf592e2a559bab9b462ab600e9e5fc.json", ++ "RepoTags":[ ++ "hello:latest" ++ ], ++ "Layers":[ ++ "6eb4c21cc3fcb729a9df230ae522c1d3708ca66e5cf531713dbfa679837aa287.tar", ++ "37841116ad3b1eeea972c75ab8bad05f48f721a7431924bc547fc91c9076c1c8.tar" ++ ] ++ } ++ ]`, ++ format: "docker", ++ withTag: true, ++ wantErr: true, ++ errString: "error loading index", ++ }, ++ { ++ name: "TC6 abnormal case with wrong tar path", ++ manifest: `[ ++ { ++ "Config":"76a4dd2d5d6a18323ac8d90f959c3c8562bf592e2a559bab9b462ab600e9e5fc.json", ++ "RepoTags":[ ++ "hello:latest" ++ ], ++ "Layers":[ ++ "6eb4c21cc3fcb729a9df230ae522c1d3708ca66e5cf531713dbfa679837aa287.tar", ++ "37841116ad3b1eeea972c75ab8bad05f48f721a7431924bc547fc91c9076c1c8.tar" ++ ] ++ } ++ ]`, ++ ++ tarPath: "/path/that/not/exist/load.tar", ++ format: "docker", ++ withTag: true, ++ wantErr: true, ++ errString: "no such file or directory", ++ }, ++ } ++ ++ for _, tc := range testcases { ++ t.Run(tc.name, func(t *testing.T) { ++ var jsonFile string ++ if tc.format == "docker" { ++ jsonFile = manifestJSONFile + } +- ]` +- dir := prepareForLoad(t, manifestJSON) +- assert.Equal(t, dir != nil, true) +- defer clean(dir) ++ if tc.format == "oci" { ++ jsonFile = indexJSONFile ++ } ++ dir := prepareForLoad(t, jsonFile, tc.manifest) ++ assert.Equal(t, dir != nil, true) ++ defer clean(dir) + +- path := dir.Join("load.tar") +- repoTags, err := tryToParseImageFormatFromTarball(daemon.opts.DataRoot, &loadOptions{path: path}) +- assert.NilError(t, err) +- assert.Equal(t, repoTags[0][0], "hello:latest") ++ path := dir.Join(loadedTarFile) ++ if tc.tarPath == "" { ++ tc.tarPath = path ++ } ++ req := &pb.LoadRequest{Path: tc.tarPath} ++ stream := &controlLoadServer{} + +- req := &pb.LoadRequest{Path: path} +- stream := &controlLoadServer{} ++ err := daemon.backend.Load(req, stream) ++ if tc.wantErr { ++ assert.ErrorContains(t, err, tc.errString) ++ return ++ } ++ assert.ErrorContains(t, err, "failed to get the image") ++ }) ++ } + +- err = daemon.backend.Load(req, stream) +- assert.ErrorContains(t, err, "failed to get the image") + } + + func TestLoadMultipleImages(t *testing.T) { +@@ -181,11 +305,11 @@ func TestLoadMultipleImages(t *testing.T) { + ] + } + ]` +- dir := prepareForLoad(t, manifestJSON) ++ dir := prepareForLoad(t, manifestJSONFile, manifestJSON) + assert.Equal(t, dir != nil, true) + defer clean(dir) + +- path := dir.Join("load.tar") ++ path := dir.Join(loadedTarFile) + repoTags, err := tryToParseImageFormatFromTarball(daemon.opts.DataRoot, &loadOptions{path: path}) + assert.NilError(t, err) + assert.Equal(t, repoTags[0][0], "registry.example.com/sayhello:first") +-- +2.27.0 + diff --git a/patch/0045-fix-images-command-when-only-give-repository.patch b/patch/0045-fix-images-command-when-only-give-repository.patch new file mode 100644 index 0000000..4176d52 --- /dev/null +++ b/patch/0045-fix-images-command-when-only-give-repository.patch @@ -0,0 +1,409 @@ +From 4e71f4409e53eadea0aa39383fba3e249072a932 Mon Sep 17 00:00:00 2001 +From: meilier +Date: Tue, 2 Feb 2021 00:46:23 +0800 +Subject: [PATCH 07/10] fix images command when only give repository + +--- + daemon/images.go | 145 +++++++++++++++++++++------------- + daemon/images_test.go | 178 ++++++++++++++++++++++++++++++++++++++++++ + image/image.go | 9 ++- + 3 files changed, 277 insertions(+), 55 deletions(-) + create mode 100644 daemon/images_test.go + +diff --git a/daemon/images.go b/daemon/images.go +index 5560d18c..e61817cc 100644 +--- a/daemon/images.go ++++ b/daemon/images.go +@@ -15,9 +15,11 @@ package daemon + + import ( + "context" ++ "fmt" + "sort" + "strings" + ++ "github.com/containers/storage" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + +@@ -29,79 +31,114 @@ import ( + ) + + const ( +- none = "" +- decimalPrefixBase = 1000 ++ none = "" ++ decimalPrefixBase = 1000 ++ minImageFieldLenWithTag = 2 + ) + ++type listOptions struct { ++ localStore *store.Store ++ logEntry *logrus.Entry ++ imageName string ++} ++ ++func (b *Backend) getListOptions(req *pb.ListRequest) listOptions { ++ return listOptions{ ++ localStore: b.daemon.localStore, ++ logEntry: logrus.WithFields(logrus.Fields{"ImageName": req.GetImageName()}), ++ imageName: req.GetImageName(), ++ } ++} ++ + // List lists all images + func (b *Backend) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) { +- logEntry := logrus.WithFields(logrus.Fields{"ImageName": req.GetImageName()}) +- logEntry.Info("ListRequest received") +- +- var reqRepository, reqTag string +- const minImageFieldLenWithTag = 2 +- if req.ImageName != "" { +- imageName := req.ImageName +- _, img, err := image.FindImage(b.daemon.localStore, imageName) +- if err != nil { +- logEntry.Error(err) +- return nil, errors.Wrapf(err, "find local image %v error", imageName) +- } ++ logrus.WithFields(logrus.Fields{ ++ "ImageName": req.GetImageName(), ++ }).Info("ListRequest received") + +- parts := strings.Split(imageName, ":") +- if len(parts) >= minImageFieldLenWithTag { +- reqRepository, reqTag = strings.Join(parts[0:len(parts)-1], ":"), parts[len(parts)-1] +- } ++ opts := b.getListOptions(req) + +- imageInfo := &pb.ListResponse_ImageInfo{ +- Repository: reqRepository, +- Tag: reqTag, +- Id: img.ID, +- Created: img.Created.Format(constant.LayoutTime), +- Size_: getImageSize(b.daemon.localStore, img.ID), +- } ++ slashLastIndex := strings.LastIndex(opts.imageName, "/") ++ colonLastIndex := strings.LastIndex(opts.imageName, ":") ++ if opts.imageName != "" && strings.Contains(opts.imageName, ":") && colonLastIndex > slashLastIndex { ++ return listOneImage(opts) ++ } ++ return listImages(opts) ++} + +- return &pb.ListResponse{Images: []*pb.ListResponse_ImageInfo{imageInfo}}, nil ++func listOneImage(opts listOptions) (*pb.ListResponse, error) { ++ _, image, err := image.FindImage(opts.localStore, opts.imageName) ++ if err != nil { ++ opts.logEntry.Error(err) ++ return nil, errors.Wrapf(err, "find local image %v error", opts.imageName) + } + +- images, err := b.daemon.localStore.Images() ++ result := make([]*pb.ListResponse_ImageInfo, 0, len(image.Names)) ++ appendImageToResult(&result, image, opts.localStore) ++ ++ for _, info := range result { ++ if opts.imageName == fmt.Sprintf("%s:%s", info.Repository, info.Tag) { ++ result = []*pb.ListResponse_ImageInfo{info} ++ } ++ } ++ ++ return &pb.ListResponse{Images: result}, nil ++} ++ ++func listImages(opts listOptions) (*pb.ListResponse, error) { ++ images, err := opts.localStore.Images() + if err != nil { +- logEntry.Error(err) ++ opts.logEntry.Error(err) + return &pb.ListResponse{}, errors.Wrap(err, "failed list images from local storage") + } ++ + sort.Slice(images, func(i, j int) bool { + return images[i].Created.After(images[j].Created) + }) + result := make([]*pb.ListResponse_ImageInfo, 0, len(images)) +- for _, image := range images { +- names := image.Names +- if len(names) == 0 { +- names = []string{none} ++ for i := range images { ++ appendImageToResult(&result, &images[i], opts.localStore) ++ } ++ ++ if opts.imageName == "" { ++ return &pb.ListResponse{Images: result}, nil ++ } ++ ++ sameRepositoryResult := make([]*pb.ListResponse_ImageInfo, 0, len(images)) ++ for _, info := range result { ++ if opts.imageName == info.Repository || strings.HasPrefix(info.Id, opts.imageName) { ++ sameRepositoryResult = append(sameRepositoryResult, info) ++ } ++ } ++ ++ if len(sameRepositoryResult) == 0 { ++ return &pb.ListResponse{}, errors.Errorf("failed to list images with repository %q in local storage", opts.imageName) ++ } ++ return &pb.ListResponse{Images: sameRepositoryResult}, nil ++} ++ ++func appendImageToResult(result *[]*pb.ListResponse_ImageInfo, image *storage.Image, store *store.Store) { ++ names := image.Names ++ if len(names) == 0 { ++ names = []string{none} ++ } ++ ++ for _, name := range names { ++ repository, tag := name, none ++ parts := strings.Split(name, ":") ++ if len(parts) >= minImageFieldLenWithTag { ++ repository, tag = strings.Join(parts[0:len(parts)-1], ":"), parts[len(parts)-1] + } +- for _, name := range names { +- repository, tag := name, none +- parts := strings.Split(name, ":") +- if len(parts) >= minImageFieldLenWithTag { +- repository, tag = strings.Join(parts[0:len(parts)-1], ":"), parts[len(parts)-1] +- } +- if reqRepository != "" && reqRepository != repository { +- continue +- } +- if reqTag != "" && reqTag != tag { +- continue +- } +- +- imageInfo := &pb.ListResponse_ImageInfo{ +- Repository: repository, +- Tag: tag, +- Id: image.ID, +- Created: image.Created.Format(constant.LayoutTime), +- Size_: getImageSize(b.daemon.localStore, image.ID), +- } +- result = append(result, imageInfo) ++ ++ imageInfo := &pb.ListResponse_ImageInfo{ ++ Repository: repository, ++ Tag: tag, ++ Id: image.ID, ++ Created: image.Created.Format(constant.LayoutTime), ++ Size_: getImageSize(store, image.ID), + } ++ *result = append(*result, imageInfo) + } +- return &pb.ListResponse{Images: result}, nil + } + + func getImageSize(store *store.Store, id string) string { +diff --git a/daemon/images_test.go b/daemon/images_test.go +new file mode 100644 +index 00000000..a970ce0b +--- /dev/null ++++ b/daemon/images_test.go +@@ -0,0 +1,178 @@ ++// 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: 2021-02-03 ++// Description: This file tests List interface ++ ++package daemon ++ ++import ( ++ "context" ++ "fmt" ++ "testing" ++ ++ "github.com/bndr/gotabulate" ++ "github.com/containers/storage" ++ "github.com/containers/storage/pkg/stringid" ++ "gotest.tools/v3/assert" ++ ++ constant "isula.org/isula-build" ++ pb "isula.org/isula-build/api/services" ++) ++ ++func TestList(t *testing.T) { ++ d := prepare(t) ++ defer tmpClean(d) ++ ++ options := &storage.ImageOptions{} ++ img, err := d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"image:test1"}, "", "", options) ++ if err != nil { ++ t.Fatalf("create image with error: %v", err) ++ } ++ _, err = d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"image:test2"}, "", "", options) ++ if err != nil { ++ t.Fatalf("create image with error: %v", err) ++ } ++ _, err = d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"egami:test"}, "", "", options) ++ if err != nil { ++ t.Fatalf("create image with error: %v", err) ++ } ++ // image with no name and tag ++ _, err = d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{}, "", "", options) ++ if err != nil { ++ t.Fatalf("create image with error: %v", err) ++ } ++ d.Daemon.localStore.SetNames(img.ID, append(img.Names, "image:test1-backup")) ++ // image who's repo contains port ++ _, err = d.Daemon.localStore.CreateImage(stringid.GenerateRandomID(), []string{"hub.example.com:8080/image:test"}, "", "", options) ++ if err != nil { ++ t.Fatalf("create image with error: %v", err) ++ } ++ ++ testcases := []struct { ++ name string ++ req *pb.ListRequest ++ wantErr bool ++ errString string ++ }{ ++ { ++ name: "normal case list specific image with repository[:tag]", ++ req: &pb.ListRequest{ ++ ImageName: "image:test1", ++ }, ++ wantErr: false, ++ }, ++ { ++ name: "normal case list specific image with image id", ++ req: &pb.ListRequest{ ++ ImageName: img.ID, ++ }, ++ wantErr: false, ++ }, ++ { ++ name: "normal case list all images", ++ req: &pb.ListRequest{ ++ ImageName: "", ++ }, ++ wantErr: false, ++ }, ++ { ++ name: "normal case list all images with repository", ++ req: &pb.ListRequest{ ++ ImageName: "image", ++ }, ++ wantErr: false, ++ }, ++ { ++ name: "abnormal case no image found in local store", ++ req: &pb.ListRequest{ ++ ImageName: "coffee:costa", ++ }, ++ wantErr: true, ++ errString: "failed to parse image", ++ }, ++ { ++ name: "abnormal case no repository", ++ req: &pb.ListRequest{ ++ ImageName: "coffee", ++ }, ++ wantErr: true, ++ errString: "failed to list images with repository", ++ }, ++ { ++ name: "abnormal case ImageName only contains latest tag", ++ req: &pb.ListRequest{ ++ ImageName: ":latest", ++ }, ++ wantErr: true, ++ errString: "invalid reference format", ++ }, ++ { ++ name: "normal case ImageName contains port number and tag", ++ req: &pb.ListRequest{ ++ ImageName: "hub.example.com:8080/image:test", ++ }, ++ wantErr: false, ++ }, ++ { ++ name: "normal case ImageName contains port number", ++ req: &pb.ListRequest{ ++ ImageName: "hub.example.com:8080/image", ++ }, ++ wantErr: false, ++ }, ++ { ++ name: "abnormal case wrong ImageName", ++ req: &pb.ListRequest{ ++ ImageName: "hub.example.com:8080/", ++ }, ++ wantErr: true, ++ errString: "failed to list images with repository", ++ }, ++ } ++ ++ for _, tc := range testcases { ++ t.Run(tc.name, func(t *testing.T) { ++ ctx := context.TODO() ++ resp, err := d.Daemon.backend.List(ctx, tc.req) ++ ++ if tc.wantErr == true { ++ assert.ErrorContains(t, err, tc.errString) ++ } ++ if tc.wantErr == false { ++ assert.NilError(t, err) ++ formatAndPrint(resp.Images) ++ } ++ }) ++ } ++} ++ ++func formatAndPrint(images []*pb.ListResponse_ImageInfo) { ++ emptyStr := `----------- ---- --------- -------- ++ REPOSITORY TAG IMAGE ID CREATED ++ ----------- ---- --------- --------` ++ lines := make([][]string, 0, len(images)) ++ title := []string{"REPOSITORY", "TAG", "IMAGE ID", "CREATED", "SIZE"} ++ for _, image := range images { ++ if image == nil { ++ continue ++ } ++ line := []string{image.Repository, image.Tag, image.Id[:constant.DefaultIDLen], image.Created, image.Size_} ++ lines = append(lines, line) ++ } ++ if len(lines) == 0 { ++ fmt.Println(emptyStr) ++ return ++ } ++ tabulate := gotabulate.Create(lines) ++ tabulate.SetHeaders(title) ++ tabulate.SetAlign("left") ++ fmt.Print(tabulate.Render("simple")) ++} +diff --git a/image/image.go b/image/image.go +index bbbc7b94..36785bdf 100644 +--- a/image/image.go ++++ b/image/image.go +@@ -590,12 +590,19 @@ func ResolveName(name string, sc *types.SystemContext, store *store.Store) ([]st + } + + func tryResolveNameInStore(name string, store *store.Store) string { ++ defaultTag := "latest" ++ + logrus.Infof("Try to find image: %s in local storage", name) + img, err := store.Image(name) ++ if err == nil { ++ return img.ID ++ } ++ ++ logrus.Infof("Try to find image: %s:%s in local storage", name, defaultTag) ++ img, err = store.Image(fmt.Sprintf("%s:%s", name, defaultTag)) + if err != nil { + return "" + } +- + return img.ID + } + +-- +2.27.0 + diff --git a/patch/0046-check-if-add-default-tag-to-image-name-when-using-pu.patch b/patch/0046-check-if-add-default-tag-to-image-name-when-using-pu.patch new file mode 100644 index 0000000..70308be --- /dev/null +++ b/patch/0046-check-if-add-default-tag-to-image-name-when-using-pu.patch @@ -0,0 +1,336 @@ +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 + diff --git a/patch/0047-checkAndExpandTag-return-empty-when-tag-is-empty.patch b/patch/0047-checkAndExpandTag-return-empty-when-tag-is-empty.patch new file mode 100644 index 0000000..a3b9e30 --- /dev/null +++ b/patch/0047-checkAndExpandTag-return-empty-when-tag-is-empty.patch @@ -0,0 +1,55 @@ +From 04dc1756a397edcf99caffd22a85902973a7c40f Mon Sep 17 00:00:00 2001 +From: meilier +Date: Wed, 3 Feb 2021 01:05:37 +0800 +Subject: [PATCH 09/10] checkAndExpandTag return empty when tag is empty + +--- + builder/dockerfile/builder.go | 2 +- + builder/dockerfile/builder_test.go | 2 +- + daemon/import.go | 4 +++- + 3 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go +index f860e60b..42229746 100644 +--- a/builder/dockerfile/builder.go ++++ b/builder/dockerfile/builder.go +@@ -637,7 +637,7 @@ func parseOutputTag(output string) string { + // CheckAndExpandTag checks tag name. If it not include a tag, "latest" will be added. + func CheckAndExpandTag(tag string) (reference.Named, string, error) { + if tag == "" { +- return nil, ":", nil ++ return nil, "", nil + } + + newTag := tag +diff --git a/builder/dockerfile/builder_test.go b/builder/dockerfile/builder_test.go +index f8de41f1..3b7513be 100644 +--- a/builder/dockerfile/builder_test.go ++++ b/builder/dockerfile/builder_test.go +@@ -1533,7 +1533,7 @@ func TestCheckAndExpandTag(t *testing.T) { + { + name: "test 9", + tag: "", +- output: ":", ++ output: "", + wantErr: false, + }, + { +diff --git a/daemon/import.go b/daemon/import.go +index 21ffeaa3..3d7c0d03 100644 +--- a/daemon/import.go ++++ b/daemon/import.go +@@ -121,7 +121,9 @@ func (b *Backend) Import(req *pb.ImportRequest, stream pb.Control_ImportServer) + return errors.Wrapf(err, "error locating image %q in local storage after import", transports.ImageName(dstRef)) + } + imageID = img.ID +- img.Names = append(img.Names, reference) ++ if reference != "" { ++ img.Names = append(img.Names, reference) ++ } + newNames := util.CopyStringsWithoutSpecificElem(img.Names, tmpName) + if err = localStore.SetNames(img.ID, newNames); err != nil { + return errors.Wrapf(err, "failed to prune temporary name from image %q", imageID) +-- +2.27.0 + diff --git a/patch/0048-trim-space-when-counting-length-of-fields-to-avoid-p.patch b/patch/0048-trim-space-when-counting-length-of-fields-to-avoid-p.patch new file mode 100644 index 0000000..b335c4f --- /dev/null +++ b/patch/0048-trim-space-when-counting-length-of-fields-to-avoid-p.patch @@ -0,0 +1,111 @@ +From af2e9918063d2797ba9f16306a4e7d2bbb0e85f7 Mon Sep 17 00:00:00 2001 +From: DCCooper <1866858@gmail.com> +Date: Tue, 2 Mar 2021 15:48:52 +0800 +Subject: [PATCH 10/10] trim space when counting length of fields to avoid + panic + +Signed-off-by: DCCooper <1866858@gmail.com> +--- + builder/dockerfile/parser/parser.go | 2 +- + builder/dockerfile/parser/parser_test.go | 22 ++++++++++++++++++- + .../testfiles/preprocess/cmd_with_directive | 2 ++ + .../preprocess/cmd_with_directive_with_space | 2 ++ + .../preprocess/entrypoint_with_directive | 2 ++ + .../entrypoint_with_directive_with_space | 2 ++ + .../preprocess/run_with_directive_with_space | 2 ++ + 7 files changed, 32 insertions(+), 2 deletions(-) + create mode 100644 builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive + create mode 100644 builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive_with_space + create mode 100644 builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive + create mode 100644 builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive_with_space + create mode 100644 builder/dockerfile/parser/testfiles/preprocess/run_with_directive_with_space + +diff --git a/builder/dockerfile/parser/parser.go b/builder/dockerfile/parser/parser.go +index 3caa516a..a21a3f59 100644 +--- a/builder/dockerfile/parser/parser.go ++++ b/builder/dockerfile/parser/parser.go +@@ -161,7 +161,7 @@ func format(rows []*rowLine, d *directive) ([]*parser.Line, error) { + fields := strings.SplitN(logicLine, " ", 2) + const validLineLen = 2 + // we do not allow empty raw command been passed +- if len(fields) < validLineLen || len(fields[1]) == 0 { ++ if len(fields) < validLineLen || len(strings.TrimSpace(fields[1])) == 0 { + return nil, errors.Errorf("line %q should have at least two fields", logicLine) + } + line.Command = strings.ToUpper(fields[0]) +diff --git a/builder/dockerfile/parser/parser_test.go b/builder/dockerfile/parser/parser_test.go +index 3da5bea6..870132f1 100644 +--- a/builder/dockerfile/parser/parser_test.go ++++ b/builder/dockerfile/parser/parser_test.go +@@ -91,7 +91,27 @@ func TestFormat(t *testing.T) { + expect: 8, + }, + { +- name: "run_with_directive", ++ name: "run_with_directive", ++ wantErr: true, ++ }, ++ { ++ name: "run_with_directive_with_space", ++ wantErr: true, ++ }, ++ { ++ name: "cmd_with_directive", ++ wantErr: true, ++ }, ++ { ++ name: "cmd_with_directive_with_space", ++ wantErr: true, ++ }, ++ { ++ name: "entrypoint_with_directive", ++ wantErr: true, ++ }, ++ { ++ name: "entrypoint_with_directive_with_space", + wantErr: true, + }, + } +diff --git a/builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive b/builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive +new file mode 100644 +index 00000000..545c278c +--- /dev/null ++++ b/builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive +@@ -0,0 +1,2 @@ ++FROM scratch ++CMD \ +diff --git a/builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive_with_space b/builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive_with_space +new file mode 100644 +index 00000000..fc309502 +--- /dev/null ++++ b/builder/dockerfile/parser/testfiles/preprocess/cmd_with_directive_with_space +@@ -0,0 +1,2 @@ ++FROM scratch ++CMD \ +diff --git a/builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive b/builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive +new file mode 100644 +index 00000000..59369bea +--- /dev/null ++++ b/builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive +@@ -0,0 +1,2 @@ ++FROM scratch ++ENTRYPOINT \ +diff --git a/builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive_with_space b/builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive_with_space +new file mode 100644 +index 00000000..172aa714 +--- /dev/null ++++ b/builder/dockerfile/parser/testfiles/preprocess/entrypoint_with_directive_with_space +@@ -0,0 +1,2 @@ ++FROM scratch ++ENTRYPOINT \ +diff --git a/builder/dockerfile/parser/testfiles/preprocess/run_with_directive_with_space b/builder/dockerfile/parser/testfiles/preprocess/run_with_directive_with_space +new file mode 100644 +index 00000000..c742c4c3 +--- /dev/null ++++ b/builder/dockerfile/parser/testfiles/preprocess/run_with_directive_with_space +@@ -0,0 +1,2 @@ ++FROM scratch ++RUN \ +-- +2.27.0 + diff --git a/series.conf b/series.conf index f70cb7d..8dbc7bc 100644 --- a/series.conf +++ b/series.conf @@ -5,3 +5,10 @@ patch/0037-isula-build-fix-goroutine-leak-problem.patch patch/0039-bugfix-remove-Healthcheck-field-when-build-from-scra.patch patch/0040-vendor-update-tabulate-vendor-to-support-eliminate-s.patch patch/0041-enhancement-remove-empty-lines-when-showing-image-li.patch +patch/0042-fix-some-make-checkall-golangci-lint-flaws.patch +patch/0043-enhancement-add-go-test-for-RUN-panic-problem.patch +patch/0044-fix-load-oci-image-panic.patch +patch/0045-fix-images-command-when-only-give-repository.patch +patch/0046-check-if-add-default-tag-to-image-name-when-using-pu.patch +patch/0047-checkAndExpandTag-return-empty-when-tag-is-empty.patch +patch/0048-trim-space-when-counting-length-of-fields-to-avoid-p.patch