github.phpd.cn/hashicorp/packer@v1.3.2/builder/docker/communicator_test.go (about) 1 package docker 2 3 import ( 4 "crypto/sha256" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "strings" 9 "testing" 10 11 "github.com/hashicorp/packer/packer" 12 "github.com/hashicorp/packer/provisioner/file" 13 "github.com/hashicorp/packer/provisioner/shell" 14 "github.com/hashicorp/packer/template" 15 ) 16 17 func TestCommunicator_impl(t *testing.T) { 18 var _ packer.Communicator = new(Communicator) 19 } 20 21 // TestUploadDownload verifies that basic upload / download functionality works 22 func TestUploadDownload(t *testing.T) { 23 ui := packer.TestUi(t) 24 cache := &packer.FileCache{CacheDir: os.TempDir()} 25 26 tpl, err := template.Parse(strings.NewReader(dockerBuilderConfig)) 27 if err != nil { 28 t.Fatalf("Unable to parse config: %s", err) 29 } 30 31 if os.Getenv("PACKER_ACC") == "" { 32 t.Skip("This test is only run with PACKER_ACC=1") 33 } 34 cmd := exec.Command("docker", "-v") 35 cmd.Run() 36 if !cmd.ProcessState.Success() { 37 t.Error("docker command not found; please make sure docker is installed") 38 } 39 40 // Setup the builder 41 builder := &Builder{} 42 warnings, err := builder.Prepare(tpl.Builders["docker"].Config) 43 if err != nil { 44 t.Fatalf("Error preparing configuration %s", err) 45 } 46 if len(warnings) > 0 { 47 t.Fatal("Encountered configuration warnings; aborting") 48 } 49 50 // Setup the provisioners 51 upload := &file.Provisioner{} 52 err = upload.Prepare(tpl.Provisioners[0].Config) 53 if err != nil { 54 t.Fatalf("Error preparing upload: %s", err) 55 } 56 download := &file.Provisioner{} 57 err = download.Prepare(tpl.Provisioners[1].Config) 58 if err != nil { 59 t.Fatalf("Error preparing download: %s", err) 60 } 61 // Preemptive cleanup. Honestly I don't know why you would want to get rid 62 // of my strawberry cake. It's so tasty! Do you not like cake? Are you a 63 // cake-hater? Or are you keeping all the cake all for yourself? So selfish! 64 defer os.Remove("my-strawberry-cake") 65 66 // Add hooks so the provisioners run during the build 67 hooks := map[string][]packer.Hook{} 68 hooks[packer.HookProvision] = []packer.Hook{ 69 &packer.ProvisionHook{ 70 Provisioners: []*packer.HookedProvisioner{ 71 {Provisioner: upload, Config: nil, TypeName: ""}, 72 {Provisioner: download, Config: nil, TypeName: ""}, 73 }, 74 }, 75 } 76 hook := &packer.DispatchHook{Mapping: hooks} 77 78 // Run things 79 artifact, err := builder.Run(ui, hook, cache) 80 if err != nil { 81 t.Fatalf("Error running build %s", err) 82 } 83 // Preemptive cleanup 84 defer artifact.Destroy() 85 86 // Verify that the thing we downloaded is the same thing we sent up. 87 // Complain loudly if it isn't. 88 inputFile, err := ioutil.ReadFile("test-fixtures/onecakes/strawberry") 89 if err != nil { 90 t.Fatalf("Unable to read input file: %s", err) 91 } 92 outputFile, err := ioutil.ReadFile("my-strawberry-cake") 93 if err != nil { 94 t.Fatalf("Unable to read output file: %s", err) 95 } 96 if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) { 97 t.Fatalf("Input and output files do not match\n"+ 98 "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile) 99 } 100 } 101 102 // TestLargeDownload verifies that files are the appropriate size after being 103 // downloaded. This is to identify and fix the race condition in #2793. You may 104 // need to use github.com/cbednarski/rerun to verify since this problem occurs 105 // only intermittently. 106 func TestLargeDownload(t *testing.T) { 107 ui := packer.TestUi(t) 108 cache := &packer.FileCache{CacheDir: os.TempDir()} 109 110 tpl, err := template.Parse(strings.NewReader(dockerLargeBuilderConfig)) 111 if err != nil { 112 t.Fatalf("Unable to parse config: %s", err) 113 } 114 115 if os.Getenv("PACKER_ACC") == "" { 116 t.Skip("This test is only run with PACKER_ACC=1") 117 } 118 cmd := exec.Command("docker", "-v") 119 cmd.Run() 120 if !cmd.ProcessState.Success() { 121 t.Error("docker command not found; please make sure docker is installed") 122 } 123 124 // Setup the builder 125 builder := &Builder{} 126 warnings, err := builder.Prepare(tpl.Builders["docker"].Config) 127 if err != nil { 128 t.Fatalf("Error preparing configuration %s", err) 129 } 130 if len(warnings) > 0 { 131 t.Fatal("Encountered configuration warnings; aborting") 132 } 133 134 // Setup the provisioners 135 shell := &shell.Provisioner{} 136 err = shell.Prepare(tpl.Provisioners[0].Config) 137 if err != nil { 138 t.Fatalf("Error preparing shell provisioner: %s", err) 139 } 140 downloadCupcake := &file.Provisioner{} 141 err = downloadCupcake.Prepare(tpl.Provisioners[1].Config) 142 if err != nil { 143 t.Fatalf("Error preparing downloadCupcake: %s", err) 144 } 145 downloadBigcake := &file.Provisioner{} 146 err = downloadBigcake.Prepare(tpl.Provisioners[2].Config) 147 if err != nil { 148 t.Fatalf("Error preparing downloadBigcake: %s", err) 149 } 150 151 // Preemptive cleanup. 152 defer os.Remove("cupcake") 153 defer os.Remove("bigcake") 154 155 // Add hooks so the provisioners run during the build 156 hooks := map[string][]packer.Hook{} 157 hooks[packer.HookProvision] = []packer.Hook{ 158 &packer.ProvisionHook{ 159 Provisioners: []*packer.HookedProvisioner{ 160 {Provisioner: shell, Config: nil, TypeName: ""}, 161 {Provisioner: downloadCupcake, Config: nil, TypeName: ""}, 162 {Provisioner: downloadBigcake, Config: nil, TypeName: ""}, 163 }, 164 }, 165 } 166 hook := &packer.DispatchHook{Mapping: hooks} 167 168 // Run things 169 artifact, err := builder.Run(ui, hook, cache) 170 if err != nil { 171 t.Fatalf("Error running build %s", err) 172 } 173 // Preemptive cleanup 174 defer artifact.Destroy() 175 176 // Verify that the things we downloaded are the right size. Complain loudly 177 // if they are not. 178 // 179 // cupcake should be 2097152 bytes 180 // bigcake should be 104857600 bytes 181 cupcake, err := os.Stat("cupcake") 182 if err != nil { 183 t.Fatalf("Unable to stat cupcake file: %s", err) 184 } 185 cupcakeExpected := int64(2097152) 186 if cupcake.Size() != cupcakeExpected { 187 t.Errorf("Expected cupcake to be %d bytes; found %d", cupcakeExpected, cupcake.Size()) 188 } 189 190 bigcake, err := os.Stat("bigcake") 191 if err != nil { 192 t.Fatalf("Unable to stat bigcake file: %s", err) 193 } 194 bigcakeExpected := int64(104857600) 195 if bigcake.Size() != bigcakeExpected { 196 t.Errorf("Expected bigcake to be %d bytes; found %d", bigcakeExpected, bigcake.Size()) 197 } 198 199 // TODO if we can, calculate a sha inside the container and compare to the 200 // one we get after we pull it down. We will probably have to parse the log 201 // or ui output to do this because we use /dev/urandom to create the file. 202 203 // if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) { 204 // t.Fatalf("Input and output files do not match\n"+ 205 // "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile) 206 // } 207 208 } 209 210 // TestFixUploadOwner verifies that owner of uploaded files is the user the container is running as. 211 func TestFixUploadOwner(t *testing.T) { 212 ui := packer.TestUi(t) 213 cache := &packer.FileCache{CacheDir: os.TempDir()} 214 215 tpl, err := template.Parse(strings.NewReader(testFixUploadOwnerTemplate)) 216 if err != nil { 217 t.Fatalf("Unable to parse config: %s", err) 218 } 219 220 if os.Getenv("PACKER_ACC") == "" { 221 t.Skip("This test is only run with PACKER_ACC=1") 222 } 223 cmd := exec.Command("docker", "-v") 224 cmd.Run() 225 if !cmd.ProcessState.Success() { 226 t.Error("docker command not found; please make sure docker is installed") 227 } 228 229 // Setup the builder 230 builder := &Builder{} 231 warnings, err := builder.Prepare(tpl.Builders["docker"].Config) 232 if err != nil { 233 t.Fatalf("Error preparing configuration %s", err) 234 } 235 if len(warnings) > 0 { 236 t.Fatal("Encountered configuration warnings; aborting") 237 } 238 239 // Setup the provisioners 240 fileProvisioner := &file.Provisioner{} 241 err = fileProvisioner.Prepare(tpl.Provisioners[0].Config) 242 if err != nil { 243 t.Fatalf("Error preparing single file upload provisioner: %s", err) 244 } 245 246 dirProvisioner := &file.Provisioner{} 247 err = dirProvisioner.Prepare(tpl.Provisioners[1].Config) 248 if err != nil { 249 t.Fatalf("Error preparing directory upload provisioner: %s", err) 250 } 251 252 shellProvisioner := &shell.Provisioner{} 253 err = shellProvisioner.Prepare(tpl.Provisioners[2].Config) 254 if err != nil { 255 t.Fatalf("Error preparing shell provisioner: %s", err) 256 } 257 258 verifyProvisioner := &shell.Provisioner{} 259 err = verifyProvisioner.Prepare(tpl.Provisioners[3].Config) 260 if err != nil { 261 t.Fatalf("Error preparing verification provisioner: %s", err) 262 } 263 264 // Add hooks so the provisioners run during the build 265 hooks := map[string][]packer.Hook{} 266 hooks[packer.HookProvision] = []packer.Hook{ 267 &packer.ProvisionHook{ 268 Provisioners: []*packer.HookedProvisioner{ 269 {Provisioner: fileProvisioner, Config: nil, TypeName: ""}, 270 {Provisioner: dirProvisioner, Config: nil, TypeName: ""}, 271 {Provisioner: shellProvisioner, Config: nil, TypeName: ""}, 272 {Provisioner: verifyProvisioner, Config: nil, TypeName: ""}, 273 }, 274 }, 275 } 276 hook := &packer.DispatchHook{Mapping: hooks} 277 278 artifact, err := builder.Run(ui, hook, cache) 279 if err != nil { 280 t.Fatalf("Error running build %s", err) 281 } 282 defer artifact.Destroy() 283 } 284 285 const dockerBuilderConfig = ` 286 { 287 "builders": [ 288 { 289 "type": "docker", 290 "image": "ubuntu", 291 "discard": true, 292 "run_command": ["-d", "-i", "-t", "{{.Image}}", "/bin/sh"] 293 } 294 ], 295 "provisioners": [ 296 { 297 "type": "file", 298 "source": "test-fixtures/onecakes/strawberry", 299 "destination": "/strawberry-cake" 300 }, 301 { 302 "type": "file", 303 "source": "/strawberry-cake", 304 "destination": "my-strawberry-cake", 305 "direction": "download" 306 } 307 ] 308 } 309 ` 310 311 const dockerLargeBuilderConfig = ` 312 { 313 "builders": [ 314 { 315 "type": "docker", 316 "image": "ubuntu", 317 "discard": true 318 } 319 ], 320 "provisioners": [ 321 { 322 "type": "shell", 323 "inline": [ 324 "dd if=/dev/urandom of=/tmp/cupcake bs=1M count=2", 325 "dd if=/dev/urandom of=/tmp/bigcake bs=1M count=100", 326 "sync", 327 "md5sum /tmp/cupcake /tmp/bigcake" 328 ] 329 }, 330 { 331 "type": "file", 332 "source": "/tmp/cupcake", 333 "destination": "cupcake", 334 "direction": "download" 335 }, 336 { 337 "type": "file", 338 "source": "/tmp/bigcake", 339 "destination": "bigcake", 340 "direction": "download" 341 } 342 ] 343 } 344 ` 345 346 const testFixUploadOwnerTemplate = ` 347 { 348 "builders": [ 349 { 350 "type": "docker", 351 "image": "ubuntu", 352 "discard": true, 353 "run_command": ["-d", "-i", "-t", "-u", "42", "{{.Image}}", "/bin/sh"] 354 } 355 ], 356 "provisioners": [ 357 { 358 "type": "file", 359 "source": "test-fixtures/onecakes/strawberry", 360 "destination": "/tmp/strawberry-cake" 361 }, 362 { 363 "type": "file", 364 "source": "test-fixtures/manycakes", 365 "destination": "/tmp/" 366 }, 367 { 368 "type": "shell", 369 "inline": "touch /tmp/testUploadOwner" 370 }, 371 { 372 "type": "shell", 373 "inline": [ 374 "[ $(stat -c %u /tmp/strawberry-cake) -eq 42 ] || (echo 'Invalid owner of /tmp/strawberry-cake' && exit 1)", 375 "[ $(stat -c %u /tmp/testUploadOwner) -eq 42 ] || (echo 'Invalid owner of /tmp/testUploadOwner' && exit 1)", 376 "find /tmp/manycakes | xargs -n1 -IFILE /bin/sh -c '[ $(stat -c %u FILE) -eq 42 ] || (echo \"Invalid owner of FILE\" && exit 1)'" 377 ] 378 } 379 ] 380 } 381 `