github.com/titanous/docker@v1.4.1/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 }