github.com/xuyutom/docker@v1.6.0/pkg/archive/changes_test.go (about)

     1  package archive
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"os/exec"
     7  	"path"
     8  	"sort"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  func max(x, y int) int {
    14  	if x >= y {
    15  		return x
    16  	}
    17  	return y
    18  }
    19  
    20  func copyDir(src, dst string) error {
    21  	cmd := exec.Command("cp", "-a", src, dst)
    22  	if err := cmd.Run(); err != nil {
    23  		return err
    24  	}
    25  	return nil
    26  }
    27  
    28  type FileType uint32
    29  
    30  const (
    31  	Regular FileType = iota
    32  	Dir
    33  	Symlink
    34  )
    35  
    36  type FileData struct {
    37  	filetype    FileType
    38  	path        string
    39  	contents    string
    40  	permissions os.FileMode
    41  }
    42  
    43  func createSampleDir(t *testing.T, root string) {
    44  	files := []FileData{
    45  		{Regular, "file1", "file1\n", 0600},
    46  		{Regular, "file2", "file2\n", 0666},
    47  		{Regular, "file3", "file3\n", 0404},
    48  		{Regular, "file4", "file4\n", 0600},
    49  		{Regular, "file5", "file5\n", 0600},
    50  		{Regular, "file6", "file6\n", 0600},
    51  		{Regular, "file7", "file7\n", 0600},
    52  		{Dir, "dir1", "", 0740},
    53  		{Regular, "dir1/file1-1", "file1-1\n", 01444},
    54  		{Regular, "dir1/file1-2", "file1-2\n", 0666},
    55  		{Dir, "dir2", "", 0700},
    56  		{Regular, "dir2/file2-1", "file2-1\n", 0666},
    57  		{Regular, "dir2/file2-2", "file2-2\n", 0666},
    58  		{Dir, "dir3", "", 0700},
    59  		{Regular, "dir3/file3-1", "file3-1\n", 0666},
    60  		{Regular, "dir3/file3-2", "file3-2\n", 0666},
    61  		{Dir, "dir4", "", 0700},
    62  		{Regular, "dir4/file3-1", "file4-1\n", 0666},
    63  		{Regular, "dir4/file3-2", "file4-2\n", 0666},
    64  		{Symlink, "symlink1", "target1", 0666},
    65  		{Symlink, "symlink2", "target2", 0666},
    66  	}
    67  
    68  	now := time.Now()
    69  	for _, info := range files {
    70  		p := path.Join(root, info.path)
    71  		if info.filetype == Dir {
    72  			if err := os.MkdirAll(p, info.permissions); err != nil {
    73  				t.Fatal(err)
    74  			}
    75  		} else if info.filetype == Regular {
    76  			if err := ioutil.WriteFile(p, []byte(info.contents), info.permissions); err != nil {
    77  				t.Fatal(err)
    78  			}
    79  		} else if info.filetype == Symlink {
    80  			if err := os.Symlink(info.contents, p); err != nil {
    81  				t.Fatal(err)
    82  			}
    83  		}
    84  
    85  		if info.filetype != Symlink {
    86  			// Set a consistent ctime, atime for all files and dirs
    87  			if err := os.Chtimes(p, now, now); err != nil {
    88  				t.Fatal(err)
    89  			}
    90  		}
    91  	}
    92  }
    93  
    94  // Create an directory, copy it, make sure we report no changes between the two
    95  func TestChangesDirsEmpty(t *testing.T) {
    96  	src, err := ioutil.TempDir("", "docker-changes-test")
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  	createSampleDir(t, src)
   101  	dst := src + "-copy"
   102  	if err := copyDir(src, dst); err != nil {
   103  		t.Fatal(err)
   104  	}
   105  	changes, err := ChangesDirs(dst, src)
   106  	if err != nil {
   107  		t.Fatal(err)
   108  	}
   109  
   110  	if len(changes) != 0 {
   111  		t.Fatalf("Reported changes for identical dirs: %v", changes)
   112  	}
   113  	os.RemoveAll(src)
   114  	os.RemoveAll(dst)
   115  }
   116  
   117  func mutateSampleDir(t *testing.T, root string) {
   118  	// Remove a regular file
   119  	if err := os.RemoveAll(path.Join(root, "file1")); err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	// Remove a directory
   124  	if err := os.RemoveAll(path.Join(root, "dir1")); err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	// Remove a symlink
   129  	if err := os.RemoveAll(path.Join(root, "symlink1")); err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	// Rewrite a file
   134  	if err := ioutil.WriteFile(path.Join(root, "file2"), []byte("fileNN\n"), 0777); err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	// Replace a file
   139  	if err := os.RemoveAll(path.Join(root, "file3")); err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	if err := ioutil.WriteFile(path.Join(root, "file3"), []byte("fileMM\n"), 0404); err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	// Touch file
   147  	if err := os.Chtimes(path.Join(root, "file4"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	// Replace file with dir
   152  	if err := os.RemoveAll(path.Join(root, "file5")); err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	if err := os.MkdirAll(path.Join(root, "file5"), 0666); err != nil {
   156  		t.Fatal(err)
   157  	}
   158  
   159  	// Create new file
   160  	if err := ioutil.WriteFile(path.Join(root, "filenew"), []byte("filenew\n"), 0777); err != nil {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	// Create new dir
   165  	if err := os.MkdirAll(path.Join(root, "dirnew"), 0766); err != nil {
   166  		t.Fatal(err)
   167  	}
   168  
   169  	// Create a new symlink
   170  	if err := os.Symlink("targetnew", path.Join(root, "symlinknew")); err != nil {
   171  		t.Fatal(err)
   172  	}
   173  
   174  	// Change a symlink
   175  	if err := os.RemoveAll(path.Join(root, "symlink2")); err != nil {
   176  		t.Fatal(err)
   177  	}
   178  	if err := os.Symlink("target2change", path.Join(root, "symlink2")); err != nil {
   179  		t.Fatal(err)
   180  	}
   181  
   182  	// Replace dir with file
   183  	if err := os.RemoveAll(path.Join(root, "dir2")); err != nil {
   184  		t.Fatal(err)
   185  	}
   186  	if err := ioutil.WriteFile(path.Join(root, "dir2"), []byte("dir2\n"), 0777); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	// Touch dir
   191  	if err := os.Chtimes(path.Join(root, "dir3"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil {
   192  		t.Fatal(err)
   193  	}
   194  }
   195  
   196  func TestChangesDirsMutated(t *testing.T) {
   197  	src, err := ioutil.TempDir("", "docker-changes-test")
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	createSampleDir(t, src)
   202  	dst := src + "-copy"
   203  	if err := copyDir(src, dst); err != nil {
   204  		t.Fatal(err)
   205  	}
   206  	defer os.RemoveAll(src)
   207  	defer os.RemoveAll(dst)
   208  
   209  	mutateSampleDir(t, dst)
   210  
   211  	changes, err := ChangesDirs(dst, src)
   212  	if err != nil {
   213  		t.Fatal(err)
   214  	}
   215  
   216  	sort.Sort(changesByPath(changes))
   217  
   218  	expectedChanges := []Change{
   219  		{"/dir1", ChangeDelete},
   220  		{"/dir2", ChangeModify},
   221  		{"/dirnew", ChangeAdd},
   222  		{"/file1", ChangeDelete},
   223  		{"/file2", ChangeModify},
   224  		{"/file3", ChangeModify},
   225  		{"/file4", ChangeModify},
   226  		{"/file5", ChangeModify},
   227  		{"/filenew", ChangeAdd},
   228  		{"/symlink1", ChangeDelete},
   229  		{"/symlink2", ChangeModify},
   230  		{"/symlinknew", ChangeAdd},
   231  	}
   232  
   233  	for i := 0; i < max(len(changes), len(expectedChanges)); i++ {
   234  		if i >= len(expectedChanges) {
   235  			t.Fatalf("unexpected change %s\n", changes[i].String())
   236  		}
   237  		if i >= len(changes) {
   238  			t.Fatalf("no change for expected change %s\n", expectedChanges[i].String())
   239  		}
   240  		if changes[i].Path == expectedChanges[i].Path {
   241  			if changes[i] != expectedChanges[i] {
   242  				t.Fatalf("Wrong change for %s, expected %s, got %s\n", changes[i].Path, changes[i].String(), expectedChanges[i].String())
   243  			}
   244  		} else if changes[i].Path < expectedChanges[i].Path {
   245  			t.Fatalf("unexpected change %s\n", changes[i].String())
   246  		} else {
   247  			t.Fatalf("no change for expected change %s != %s\n", expectedChanges[i].String(), changes[i].String())
   248  		}
   249  	}
   250  }
   251  
   252  func TestApplyLayer(t *testing.T) {
   253  	src, err := ioutil.TempDir("", "docker-changes-test")
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  	createSampleDir(t, src)
   258  	defer os.RemoveAll(src)
   259  	dst := src + "-copy"
   260  	if err := copyDir(src, dst); err != nil {
   261  		t.Fatal(err)
   262  	}
   263  	mutateSampleDir(t, dst)
   264  	defer os.RemoveAll(dst)
   265  
   266  	changes, err := ChangesDirs(dst, src)
   267  	if err != nil {
   268  		t.Fatal(err)
   269  	}
   270  
   271  	layer, err := ExportChanges(dst, changes)
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  
   276  	layerCopy, err := NewTempArchive(layer, "")
   277  	if err != nil {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	if _, err := ApplyLayer(src, layerCopy); err != nil {
   282  		t.Fatal(err)
   283  	}
   284  
   285  	changes2, err := ChangesDirs(src, dst)
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	if len(changes2) != 0 {
   291  		t.Fatalf("Unexpected differences after reapplying mutation: %v", changes2)
   292  	}
   293  }