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