From 2b845fdb3e3c9d23b0fec856bcd5ce8ced868683 Mon Sep 17 00:00:00 2001 From: DCCooper <1866858@gmail.com> Date: Thu, 4 Nov 2021 14:38:20 +0800 Subject: [PATCH] isula-build:fix panic when using image ID to save separated image Signed-off-by: DCCooper <1866858@gmail.com> --- daemon/save.go | 120 +++++++++++++-------- .../src/cover_test_save_separated_image_failed.sh | 28 +++++ .../src/cover_test_save_separated_image_success.sh | 24 ++++- 3 files changed, 127 insertions(+), 45 deletions(-) diff --git a/daemon/save.go b/daemon/save.go index 9ad4e03..9c5e563 100644 --- a/daemon/save.go +++ b/daemon/save.go @@ -83,6 +83,7 @@ type separatorSave struct { base string lib string dest string + renameFile string enabled bool } @@ -141,15 +142,7 @@ type tarballInfo struct { BaseLayers []string `json:"baseLayer"` } -func (b *Backend) getSaveOptions(req *pb.SaveRequest) (saveOptions, error) { - var sep = separatorSave{ - base: req.GetSep().GetBase(), - lib: req.GetSep().GetLib(), - dest: req.GetSep().GetDest(), - log: logrus.WithFields(logrus.Fields{"SaveID": req.GetSaveID()}), - enabled: req.GetSep().GetEnabled(), - } - +func (b *Backend) getSaveOptions(req *pb.SaveRequest) saveOptions { var opt = saveOptions{ sysCtx: image.GetSystemContext(), localStore: b.daemon.localStore, @@ -161,11 +154,19 @@ func (b *Backend) getSaveOptions(req *pb.SaveRequest) (saveOptions, error) { outputPath: req.GetPath(), logger: logger.NewCliLogger(constant.CliLogBufferLen), logEntry: logrus.WithFields(logrus.Fields{"SaveID": req.GetSaveID(), "Format": req.GetFormat()}), - sep: sep, } // normal save - if !sep.enabled { - return opt, nil + if !req.GetSep().GetEnabled() { + return opt + } + + opt.sep = separatorSave{ + base: req.GetSep().GetBase(), + lib: req.GetSep().GetLib(), + dest: req.GetSep().GetDest(), + log: logrus.WithFields(logrus.Fields{"SaveID": req.GetSaveID()}), + enabled: req.GetSep().GetEnabled(), + renameFile: req.GetSep().GetRename(), } // save separated image @@ -175,44 +176,22 @@ func (b *Backend) getSaveOptions(req *pb.SaveRequest) (saveOptions, error) { baseDir := filepath.Join(tmpRoot, baseUntarTempDirName) libDir := filepath.Join(tmpRoot, libUntarTempDirName) - opt.sep.tmpDir = imageTmpDir{ - app: appDir, - base: baseDir, - lib: libDir, - untar: untar, - root: tmpRoot, - } + opt.sep.tmpDir = imageTmpDir{app: appDir, base: baseDir, lib: libDir, untar: untar, root: tmpRoot} opt.outputPath = filepath.Join(untar, unionTarName) - renameFile := req.GetSep().GetRename() - if len(renameFile) != 0 { - var reName []renames - if err := util.LoadJSONFile(renameFile, &reName); err != nil { - return saveOptions{}, err - } - opt.sep.renameData = reName - } - return opt, nil + return opt } // Save receives a save request and save the image(s) into tarball -func (b *Backend) Save(req *pb.SaveRequest, stream pb.Control_SaveServer) error { +func (b *Backend) Save(req *pb.SaveRequest, stream pb.Control_SaveServer) (err error) { logrus.WithFields(logrus.Fields{ "SaveID": req.GetSaveID(), "Format": req.GetFormat(), }).Info("SaveRequest received") - var err error - opts, err := b.getSaveOptions(req) - if err != nil { - return errors.Wrap(err, "process save options failed") - } - - if err = checkFormat(&opts); err != nil { - return err - } - if err = filterImageName(&opts); err != nil { - return err + opts := b.getSaveOptions(req) + if err = opts.check(); err != nil { + return errors.Wrap(err, "check save options failed") } defer func() { @@ -299,7 +278,47 @@ func messageHandler(stream pb.Control_SaveServer, cliLogger *logger.Logger) func } } -func checkFormat(opts *saveOptions) error { +func (opts *saveOptions) check() error { + if err := opts.checkImageNameIsID(); err != nil { + return err + } + if err := opts.checkFormat(); err != nil { + return err + } + if err := opts.filterImageName(); err != nil { + return err + } + if err := opts.checkRenameFile(); err != nil { + return err + } + + return nil +} + +func (opts *saveOptions) checkImageNameIsID() error { + imageNames := opts.oriImgList + if opts.sep.enabled { + if len(opts.sep.base) != 0 { + imageNames = append(imageNames, opts.sep.base) + } + if len(opts.sep.lib) != 0 { + imageNames = append(imageNames, opts.sep.lib) + } + } + for _, name := range imageNames { + _, img, err := image.FindImage(opts.localStore, name) + if err != nil { + return errors.Wrapf(err, "check image name failed when finding image name %q", name) + } + if strings.HasPrefix(img.ID, name) && opts.sep.enabled { + return errors.Errorf("using image ID %q as image name to save separated image is not allowed", name) + } + } + + return nil +} + +func (opts *saveOptions) checkFormat() error { switch opts.format { case constant.DockerTransport: opts.format = constant.DockerArchiveTransport @@ -312,7 +331,7 @@ func checkFormat(opts *saveOptions) error { return nil } -func filterImageName(opts *saveOptions) error { +func (opts *saveOptions) filterImageName() error { if opts.format == constant.OCIArchiveTransport { opts.finalImageOrdered = opts.oriImgList return nil @@ -350,6 +369,18 @@ func filterImageName(opts *saveOptions) error { return nil } +func (opts *saveOptions) checkRenameFile() error { + if len(opts.sep.renameFile) != 0 { + var reName []renames + if err := util.LoadJSONFile(opts.sep.renameFile, &reName); err != nil { + return errors.Wrap(err, "check rename file failed") + } + opts.sep.renameData = reName + } + + return nil +} + func getLayerHashFromStorage(store *store.Store, name string) ([]string, error) { if len(name) == 0 { return nil, nil @@ -770,6 +801,11 @@ func getLayersID(layer []string) []string { func (s *separatorSave) constructSingleImgInfo(mani imageManifest, store *store.Store) (imageInfo, error) { var libLayers, appLayers []string + // image name should not be empty here + if len(mani.RepoTags) == 0 { + return imageInfo{}, errors.New("image name and tag is empty") + } + // if there is more than one repoTag, will use first one as image name imageRepoFields := strings.Split(mani.RepoTags[0], ":") imageLayers := getLayersID(mani.Layers) diff --git a/tests/src/cover_test_save_separated_image_failed.sh b/tests/src/cover_test_save_separated_image_failed.sh index c64dcf5..66db580 100644 --- a/tests/src/cover_test_save_separated_image_failed.sh +++ b/tests/src/cover_test_save_separated_image_failed.sh @@ -89,6 +89,33 @@ function test_run6() { rm -rf "${workspace}"/Images } +# using image id to save +function test_run7() { + base_image_id=$(isula-build ctr-img images ${base_image_name} | tail -n 2 | head -n 1 | awk '{print $3}') + lib_image_id=$(isula-build ctr-img images ${lib_image_name} | tail -n 2 | head -n 1 | awk '{print $3}') + app_image_id=$(isula-build ctr-img images ${app1_image_name} | tail -n 2 | head -n 1 | awk '{print $3}') + app1_image_id=$(isula-build ctr-img images ${app2_image_name} | tail -n 2 | head -n 1 | awk '{print $3}') + # all name is image id + isula-build ctr-img save -b "${base_image_id}" -l "${lib_image_id}" -d "${workspace}"/Images "${app1_image_id}" "${app2_image_id}" + check_result_not_equal $? 0 + rm -rf "${workspace}"/Images + + # app name is image id + isula-build ctr-img save -b "${base_image_name}" -l "${lib_image_name}" -d "${workspace}"/Images "${app1_image_id}" "${app2_image_name}" + check_result_not_equal $? 0 + rm -rf "${workspace}"/Images + + # lib name is image id + isula-build ctr-img save -b "${base_image_name}" -l "${lib_image_id}" -d "${workspace}"/Images "${app1_image_name}" "${app2_image_name}" + check_result_not_equal $? 0 + rm -rf "${workspace}"/Images + + # base name is image id + isula-build ctr-img save -b "${base_image_id}" -l "${lib_image_name}" -d "${workspace}"/Images "${app1_image_name}" "${app2_image_name}" + check_result_not_equal $? 0 + rm -rf "${workspace}"/Images +} + function cleanup() { rm -rf "${workspace}" isula-build ctr-img rm "${bad_lib_image_name}" "${bad_app1_image_name}" "${bad_app2_image_name}" "${lib_image_name}" "${app1_image_name}" "${app2_image_name}" @@ -102,6 +129,7 @@ test_run3 test_run4 test_run5 test_run6 +test_run7 cleanup # shellcheck disable=SC2154 exit "${exit_flag}" diff --git a/tests/src/cover_test_save_separated_image_success.sh b/tests/src/cover_test_save_separated_image_success.sh index 2095bd3..68d2cae 100644 --- a/tests/src/cover_test_save_separated_image_success.sh +++ b/tests/src/cover_test_save_separated_image_success.sh @@ -26,6 +26,11 @@ function pre_run() { lib_image_name="lib:latest" app1_image_name="app1:latest" app2_image_name="app2:latest" + base_image_short_name="b:latest" + lib_image_short_name="l:latest" + app1_image_short_name="a:latest" + app2_image_short_name="c:latest" + lib_layer_number=5 app1_layer_number=4 app2_layer_number=3 @@ -37,18 +42,31 @@ function pre_run() { build_image "${app2_image_name}" "${workspace}" } -function test_run() { +function test_run1() { isula-build ctr-img save -b "${base_image_name}" -l "${lib_image_name}" -d "${workspace}"/Images "${app1_image_name}" "${app2_image_name}" check_result_equal $? 0 + rm -rf "${workspace}" +} + +# use short image name +function test_run2() { + isula-build ctr-img tag "${base_image_name}" "${base_image_short_name}" + isula-build ctr-img tag "${lib_image_name}" "${lib_image_short_name}" + isula-build ctr-img tag "${app1_image_name}" "${app1_image_short_name}" + isula-build ctr-img tag "${app2_image_name}" "${app2_image_short_name}" + isula-build ctr-img save -b "${base_image_short_name}" -l "${lib_image_short_name}" -d "${workspace}"/Images "${app1_image_short_name}" "${app2_image_short_name}" + check_result_equal $? 0 + rm -rf "${workspace}" } function cleanup() { rm -rf "${workspace}" - isula-build ctr-img rm "${lib_image_name}" "${app1_image_name}" "${app2_image_name}" + isula-build ctr-img rm "${lib_image_name}" "${app1_image_name}" "${app2_image_name}" "${base_image_short_name}" "${lib_image_short_name}" "${app1_image_short_name}" "${app2_image_short_name}" } pre_run -test_run +test_run1 +test_run2 cleanup # shellcheck disable=SC2154 exit "${exit_flag}" -- 1.8.3.1