github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration-cli/docker_cli_save_load_test.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/Prakhar-Agarwal-byte/moby/api/types"
    14  	"github.com/Prakhar-Agarwal-byte/moby/integration-cli/cli"
    15  	"github.com/Prakhar-Agarwal-byte/moby/integration-cli/cli/build"
    16  	"gotest.tools/v3/assert"
    17  	is "gotest.tools/v3/assert/cmp"
    18  	"gotest.tools/v3/icmd"
    19  	"gotest.tools/v3/skip"
    20  )
    21  
    22  type DockerCLISaveLoadSuite struct {
    23  	ds *DockerSuite
    24  }
    25  
    26  func (s *DockerCLISaveLoadSuite) TearDownTest(ctx context.Context, c *testing.T) {
    27  	s.ds.TearDownTest(ctx, c)
    28  }
    29  
    30  func (s *DockerCLISaveLoadSuite) OnTimeout(c *testing.T) {
    31  	s.ds.OnTimeout(c)
    32  }
    33  
    34  // save a repo using gz compression and try to load it using stdout
    35  func (s *DockerCLISaveLoadSuite) TestSaveXzAndLoadRepoStdout(c *testing.T) {
    36  	testRequires(c, DaemonIsLinux)
    37  	name := "test-save-xz-and-load-repo-stdout"
    38  	cli.DockerCmd(c, "run", "--name", name, "busybox", "true")
    39  
    40  	imgRepoName := "foobar-save-load-test-xz-gz"
    41  	out := cli.DockerCmd(c, "commit", name, imgRepoName).Combined()
    42  
    43  	cli.DockerCmd(c, "inspect", imgRepoName)
    44  
    45  	repoTarball, err := RunCommandPipelineWithOutput(
    46  		exec.Command(dockerBinary, "save", imgRepoName),
    47  		exec.Command("xz", "-c"),
    48  		exec.Command("gzip", "-c"))
    49  	assert.NilError(c, err, "failed to save repo: %v %v", out, err)
    50  	deleteImages(imgRepoName)
    51  
    52  	icmd.RunCmd(icmd.Cmd{
    53  		Command: []string{dockerBinary, "load"},
    54  		Stdin:   strings.NewReader(repoTarball),
    55  	}).Assert(c, icmd.Expected{
    56  		ExitCode: 1,
    57  	})
    58  
    59  	after, _, err := dockerCmdWithError("inspect", imgRepoName)
    60  	assert.ErrorContains(c, err, "", "the repo should not exist: %v", after)
    61  }
    62  
    63  // save a repo using xz+gz compression and try to load it using stdout
    64  func (s *DockerCLISaveLoadSuite) TestSaveXzGzAndLoadRepoStdout(c *testing.T) {
    65  	testRequires(c, DaemonIsLinux)
    66  	name := "test-save-xz-gz-and-load-repo-stdout"
    67  	cli.DockerCmd(c, "run", "--name", name, "busybox", "true")
    68  
    69  	repoName := "foobar-save-load-test-xz-gz"
    70  	cli.DockerCmd(c, "commit", name, repoName)
    71  
    72  	cli.DockerCmd(c, "inspect", repoName)
    73  
    74  	out, err := RunCommandPipelineWithOutput(
    75  		exec.Command(dockerBinary, "save", repoName),
    76  		exec.Command("xz", "-c"),
    77  		exec.Command("gzip", "-c"))
    78  	assert.NilError(c, err, "failed to save repo: %v %v", out, err)
    79  
    80  	deleteImages(repoName)
    81  
    82  	icmd.RunCmd(icmd.Cmd{
    83  		Command: []string{dockerBinary, "load"},
    84  		Stdin:   strings.NewReader(out),
    85  	}).Assert(c, icmd.Expected{
    86  		ExitCode: 1,
    87  	})
    88  
    89  	after, _, err := dockerCmdWithError("inspect", repoName)
    90  	assert.ErrorContains(c, err, "", "the repo should not exist: %v", after)
    91  }
    92  
    93  func (s *DockerCLISaveLoadSuite) TestSaveSingleTag(c *testing.T) {
    94  	testRequires(c, DaemonIsLinux)
    95  	imgRepoName := "foobar-save-single-tag-test"
    96  	cli.DockerCmd(c, "tag", "busybox:latest", fmt.Sprintf("%v:latest", imgRepoName))
    97  
    98  	out := cli.DockerCmd(c, "images", "-q", "--no-trunc", imgRepoName).Stdout()
    99  	cleanedImageID := strings.TrimSpace(out)
   100  
   101  	filesFilter := fmt.Sprintf("(^manifest.json$|%v)", cleanedImageID)
   102  	if testEnv.UsingSnapshotter() {
   103  		filesFilter = fmt.Sprintf("(^index.json$|^manifest.json$|%v)", cleanedImageID)
   104  	}
   105  	out, err := RunCommandPipelineWithOutput(
   106  		exec.Command(dockerBinary, "save", fmt.Sprintf("%v:latest", imgRepoName)),
   107  		exec.Command("tar", "t"),
   108  		exec.Command("grep", "-E", filesFilter))
   109  	assert.NilError(c, err, "failed to save repo with image ID and index files: %s, %v", out, err)
   110  }
   111  
   112  func (s *DockerCLISaveLoadSuite) TestSaveImageId(c *testing.T) {
   113  	testRequires(c, DaemonIsLinux)
   114  	imgRepoName := "foobar-save-image-id-test"
   115  	cli.DockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", imgRepoName))
   116  
   117  	out := cli.DockerCmd(c, "images", "-q", "--no-trunc", imgRepoName).Stdout()
   118  	cleanedLongImageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:")
   119  
   120  	out = cli.DockerCmd(c, "images", "-q", imgRepoName).Stdout()
   121  	cleanedShortImageID := strings.TrimSpace(out)
   122  
   123  	// Make sure IDs are not empty
   124  	assert.Assert(c, cleanedLongImageID != "", "Id should not be empty.")
   125  	assert.Assert(c, cleanedShortImageID != "", "Id should not be empty.")
   126  
   127  	saveCmd := exec.Command(dockerBinary, "save", cleanedShortImageID)
   128  	tarCmd := exec.Command("tar", "t")
   129  
   130  	var err error
   131  	tarCmd.Stdin, err = saveCmd.StdoutPipe()
   132  	assert.Assert(c, err == nil, "cannot set stdout pipe for tar: %v", err)
   133  	grepCmd := exec.Command("grep", cleanedLongImageID)
   134  	grepCmd.Stdin, err = tarCmd.StdoutPipe()
   135  	assert.Assert(c, err == nil, "cannot set stdout pipe for grep: %v", err)
   136  
   137  	assert.Assert(c, tarCmd.Start() == nil, "tar failed with error: %v", err)
   138  	assert.Assert(c, saveCmd.Start() == nil, "docker save failed with error: %v", err)
   139  	defer func() {
   140  		saveCmd.Wait()
   141  		tarCmd.Wait()
   142  		cli.DockerCmd(c, "rmi", imgRepoName)
   143  	}()
   144  
   145  	out, _, err = runCommandWithOutput(grepCmd)
   146  
   147  	assert.Assert(c, err == nil, "failed to save repo with image ID: %s, %v", out, err)
   148  }
   149  
   150  // save a repo and try to load it using flags
   151  func (s *DockerCLISaveLoadSuite) TestSaveAndLoadRepoFlags(c *testing.T) {
   152  	testRequires(c, DaemonIsLinux)
   153  	const name = "test-save-and-load-repo-flags"
   154  	cli.DockerCmd(c, "run", "--name", name, "busybox", "true")
   155  
   156  	const imgRepoName = "foobar-save-load-test"
   157  
   158  	deleteImages(imgRepoName)
   159  	cli.DockerCmd(c, "commit", name, imgRepoName)
   160  
   161  	beforeStr := cli.DockerCmd(c, "inspect", imgRepoName).Stdout()
   162  
   163  	out, err := RunCommandPipelineWithOutput(
   164  		exec.Command(dockerBinary, "save", imgRepoName),
   165  		exec.Command(dockerBinary, "load"))
   166  	assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err)
   167  
   168  	afterStr := cli.DockerCmd(c, "inspect", imgRepoName).Stdout()
   169  
   170  	var before, after []types.ImageInspect
   171  	err = json.Unmarshal([]byte(beforeStr), &before)
   172  	assert.NilError(c, err, "failed to parse inspect 'before' output")
   173  	err = json.Unmarshal([]byte(afterStr), &after)
   174  	assert.NilError(c, err, "failed to parse inspect 'after' output")
   175  
   176  	assert.Assert(c, is.Len(before, 1))
   177  	assert.Assert(c, is.Len(after, 1))
   178  
   179  	if testEnv.UsingSnapshotter() {
   180  		// Ignore LastTagTime difference with c8d.
   181  		// It is not stored in the image archive, but in the imageStore
   182  		// which is a graphdrivers implementation detail.
   183  		//
   184  		// It works because we load the image into the same daemon which saved
   185  		// the image. It would still fail with the graphdrivers if the image
   186  		// was loaded into a different daemon (which should be the case in a
   187  		// real-world scenario).
   188  		before[0].Metadata.LastTagTime = after[0].Metadata.LastTagTime
   189  	}
   190  
   191  	assert.Check(c, is.DeepEqual(before, after), "inspect is not the same after a save / load")
   192  }
   193  
   194  func (s *DockerCLISaveLoadSuite) TestSaveWithNoExistImage(c *testing.T) {
   195  	testRequires(c, DaemonIsLinux)
   196  
   197  	imgName := "foobar-non-existing-image"
   198  
   199  	out, _, err := dockerCmdWithError("save", "-o", "test-img.tar", imgName)
   200  	assert.ErrorContains(c, err, "", "save image should fail for non-existing image")
   201  	assert.Assert(c, strings.Contains(out, fmt.Sprintf("No such image: %s", imgName)))
   202  }
   203  
   204  func (s *DockerCLISaveLoadSuite) TestSaveMultipleNames(c *testing.T) {
   205  	testRequires(c, DaemonIsLinux)
   206  	const imgRepoName = "foobar-save-multi-name-test"
   207  
   208  	oneTag := fmt.Sprintf("%v-one:latest", imgRepoName)
   209  	twoTag := fmt.Sprintf("%v-two:latest", imgRepoName)
   210  
   211  	cli.DockerCmd(c, "tag", "emptyfs:latest", oneTag)
   212  	cli.DockerCmd(c, "tag", "emptyfs:latest", twoTag)
   213  
   214  	out, err := RunCommandPipelineWithOutput(
   215  		exec.Command(dockerBinary, "save", strings.TrimSuffix(oneTag, ":latest"), twoTag),
   216  		exec.Command("tar", "xO", "index.json"),
   217  	)
   218  	assert.NilError(c, err, "failed to save multiple repos: %s, %v", out, err)
   219  
   220  	assert.Check(c, is.Contains(out, oneTag))
   221  	assert.Check(c, is.Contains(out, twoTag))
   222  }
   223  
   224  // Test loading a weird image where one of the layers is of zero size.
   225  // The layer.tar file is actually zero bytes, no padding or anything else.
   226  // See issue: 18170
   227  func (s *DockerCLISaveLoadSuite) TestLoadZeroSizeLayer(c *testing.T) {
   228  	// TODO(vvoland): Create an OCI image with 0 bytes layer.
   229  	skip.If(c, testEnv.UsingSnapshotter(), "input archive is not OCI compatible")
   230  
   231  	// this will definitely not work if using remote daemon
   232  	// very weird test
   233  	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
   234  
   235  	cli.DockerCmd(c, "load", "-i", "testdata/emptyLayer.tar")
   236  }
   237  
   238  func (s *DockerCLISaveLoadSuite) TestSaveLoadParents(c *testing.T) {
   239  	testRequires(c, DaemonIsLinux)
   240  	skip.If(c, testEnv.UsingSnapshotter(), "Parent image property is not supported with containerd")
   241  
   242  	makeImage := func(from string, addfile string) string {
   243  		id := cli.DockerCmd(c, "run", "-d", from, "touch", addfile).Stdout()
   244  		id = strings.TrimSpace(id)
   245  
   246  		imageID := cli.DockerCmd(c, "commit", id).Stdout()
   247  		imageID = strings.TrimSpace(imageID)
   248  
   249  		cli.DockerCmd(c, "rm", "-f", id)
   250  		return imageID
   251  	}
   252  
   253  	idFoo := makeImage("busybox", "foo")
   254  	idBar := makeImage(idFoo, "bar")
   255  
   256  	tmpDir, err := os.MkdirTemp("", "save-load-parents")
   257  	assert.NilError(c, err)
   258  	defer os.RemoveAll(tmpDir)
   259  
   260  	c.Log("tmpdir", tmpDir)
   261  
   262  	outfile := filepath.Join(tmpDir, "out.tar")
   263  
   264  	cli.DockerCmd(c, "save", "-o", outfile, idBar, idFoo)
   265  	cli.DockerCmd(c, "rmi", idBar)
   266  	cli.DockerCmd(c, "load", "-i", outfile)
   267  
   268  	inspectOut := inspectField(c, idBar, "Parent")
   269  	assert.Equal(c, inspectOut, idFoo)
   270  
   271  	inspectOut = inspectField(c, idFoo, "Parent")
   272  	assert.Equal(c, inspectOut, "")
   273  }
   274  
   275  func (s *DockerCLISaveLoadSuite) TestSaveLoadNoTag(c *testing.T) {
   276  	testRequires(c, DaemonIsLinux)
   277  
   278  	name := "saveloadnotag"
   279  
   280  	buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nENV foo=bar"))
   281  	id := inspectField(c, name, "Id")
   282  
   283  	// Test to make sure that save w/o name just shows imageID during load
   284  	out, err := RunCommandPipelineWithOutput(
   285  		exec.Command(dockerBinary, "save", id),
   286  		exec.Command(dockerBinary, "load"))
   287  	assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err)
   288  
   289  	// Should not show 'name' but should show the image ID during the load
   290  	assert.Assert(c, !strings.Contains(out, "Loaded image: "))
   291  	assert.Assert(c, strings.Contains(out, "Loaded image ID:"))
   292  	assert.Assert(c, strings.Contains(out, id))
   293  	// Test to make sure that save by name shows that name during load
   294  	out, err = RunCommandPipelineWithOutput(
   295  		exec.Command(dockerBinary, "save", name),
   296  		exec.Command(dockerBinary, "load"))
   297  	assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err)
   298  
   299  	assert.Assert(c, strings.Contains(out, "Loaded image: "+name+":latest"))
   300  	assert.Assert(c, !strings.Contains(out, "Loaded image ID:"))
   301  }