github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/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.Provisioner{ 71 upload, 72 download, 73 }, 74 ProvisionerTypes: []string{"", ""}, 75 }, 76 } 77 hook := &packer.DispatchHook{Mapping: hooks} 78 79 // Run things 80 artifact, err := builder.Run(ui, hook, cache) 81 if err != nil { 82 t.Fatalf("Error running build %s", err) 83 } 84 // Preemptive cleanup 85 defer artifact.Destroy() 86 87 // Verify that the thing we downloaded is the same thing we sent up. 88 // Complain loudly if it isn't. 89 inputFile, err := ioutil.ReadFile("test-fixtures/onecakes/strawberry") 90 if err != nil { 91 t.Fatalf("Unable to read input file: %s", err) 92 } 93 outputFile, err := ioutil.ReadFile("my-strawberry-cake") 94 if err != nil { 95 t.Fatalf("Unable to read output file: %s", err) 96 } 97 if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) { 98 t.Fatalf("Input and output files do not match\n"+ 99 "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile) 100 } 101 } 102 103 // TestLargeDownload verifies that files are the appropriate size after being 104 // downloaded. This is to identify and fix the race condition in #2793. You may 105 // need to use github.com/cbednarski/rerun to verify since this problem occurs 106 // only intermittently. 107 func TestLargeDownload(t *testing.T) { 108 ui := packer.TestUi(t) 109 cache := &packer.FileCache{CacheDir: os.TempDir()} 110 111 tpl, err := template.Parse(strings.NewReader(dockerLargeBuilderConfig)) 112 if err != nil { 113 t.Fatalf("Unable to parse config: %s", err) 114 } 115 116 if os.Getenv("PACKER_ACC") == "" { 117 t.Skip("This test is only run with PACKER_ACC=1") 118 } 119 cmd := exec.Command("docker", "-v") 120 cmd.Run() 121 if !cmd.ProcessState.Success() { 122 t.Error("docker command not found; please make sure docker is installed") 123 } 124 125 // Setup the builder 126 builder := &Builder{} 127 warnings, err := builder.Prepare(tpl.Builders["docker"].Config) 128 if err != nil { 129 t.Fatalf("Error preparing configuration %s", err) 130 } 131 if len(warnings) > 0 { 132 t.Fatal("Encountered configuration warnings; aborting") 133 } 134 135 // Setup the provisioners 136 shell := &shell.Provisioner{} 137 err = shell.Prepare(tpl.Provisioners[0].Config) 138 if err != nil { 139 t.Fatalf("Error preparing shell provisioner: %s", err) 140 } 141 downloadCupcake := &file.Provisioner{} 142 err = downloadCupcake.Prepare(tpl.Provisioners[1].Config) 143 if err != nil { 144 t.Fatalf("Error preparing downloadCupcake: %s", err) 145 } 146 downloadBigcake := &file.Provisioner{} 147 err = downloadBigcake.Prepare(tpl.Provisioners[2].Config) 148 if err != nil { 149 t.Fatalf("Error preparing downloadBigcake: %s", err) 150 } 151 152 // Preemptive cleanup. 153 defer os.Remove("cupcake") 154 defer os.Remove("bigcake") 155 156 // Add hooks so the provisioners run during the build 157 hooks := map[string][]packer.Hook{} 158 hooks[packer.HookProvision] = []packer.Hook{ 159 &packer.ProvisionHook{ 160 Provisioners: []packer.Provisioner{ 161 shell, 162 downloadCupcake, 163 downloadBigcake, 164 }, 165 ProvisionerTypes: []string{"", "", ""}, 166 }, 167 } 168 hook := &packer.DispatchHook{Mapping: hooks} 169 170 // Run things 171 artifact, err := builder.Run(ui, hook, cache) 172 if err != nil { 173 t.Fatalf("Error running build %s", err) 174 } 175 // Preemptive cleanup 176 defer artifact.Destroy() 177 178 // Verify that the things we downloaded are the right size. Complain loudly 179 // if they are not. 180 // 181 // cupcake should be 2097152 bytes 182 // bigcake should be 104857600 bytes 183 cupcake, err := os.Stat("cupcake") 184 if err != nil { 185 t.Fatalf("Unable to stat cupcake file: %s", err) 186 } 187 cupcakeExpected := int64(2097152) 188 if cupcake.Size() != cupcakeExpected { 189 t.Errorf("Expected cupcake to be %d bytes; found %d", cupcakeExpected, cupcake.Size()) 190 } 191 192 bigcake, err := os.Stat("bigcake") 193 if err != nil { 194 t.Fatalf("Unable to stat bigcake file: %s", err) 195 } 196 bigcakeExpected := int64(104857600) 197 if bigcake.Size() != bigcakeExpected { 198 t.Errorf("Expected bigcake to be %d bytes; found %d", bigcakeExpected, bigcake.Size()) 199 } 200 201 // TODO if we can, calculate a sha inside the container and compare to the 202 // one we get after we pull it down. We will probably have to parse the log 203 // or ui output to do this because we use /dev/urandom to create the file. 204 205 // if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) { 206 // t.Fatalf("Input and output files do not match\n"+ 207 // "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile) 208 // } 209 210 } 211 212 const dockerBuilderConfig = ` 213 { 214 "builders": [ 215 { 216 "type": "docker", 217 "image": "ubuntu", 218 "discard": true, 219 "run_command": ["-d", "-i", "-t", "{{.Image}}", "/bin/sh"] 220 } 221 ], 222 "provisioners": [ 223 { 224 "type": "file", 225 "source": "test-fixtures/onecakes/strawberry", 226 "destination": "/strawberry-cake" 227 }, 228 { 229 "type": "file", 230 "source": "/strawberry-cake", 231 "destination": "my-strawberry-cake", 232 "direction": "download" 233 } 234 ] 235 } 236 ` 237 238 const dockerLargeBuilderConfig = ` 239 { 240 "builders": [ 241 { 242 "type": "docker", 243 "image": "ubuntu", 244 "discard": true 245 } 246 ], 247 "provisioners": [ 248 { 249 "type": "shell", 250 "inline": [ 251 "dd if=/dev/urandom of=/tmp/cupcake bs=1M count=2", 252 "dd if=/dev/urandom of=/tmp/bigcake bs=1M count=100", 253 "sync", 254 "md5sum /tmp/cupcake /tmp/bigcake" 255 ] 256 }, 257 { 258 "type": "file", 259 "source": "/tmp/cupcake", 260 "destination": "cupcake", 261 "direction": "download" 262 }, 263 { 264 "type": "file", 265 "source": "/tmp/bigcake", 266 "destination": "bigcake", 267 "direction": "download" 268 } 269 ] 270 } 271 `