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 }