github.com/rothwerx/packer@v0.9.0/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/mitchellh/packer/packer" 12 "github.com/mitchellh/packer/provisioner/file" 13 "github.com/mitchellh/packer/provisioner/shell" 14 "github.com/mitchellh/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 }, 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 apporpriate 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.Provisioner{ 160 shell, 161 downloadCupcake, 162 downloadBigcake, 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 const dockerBuilderConfig = ` 211 { 212 "builders": [ 213 { 214 "type": "docker", 215 "image": "ubuntu", 216 "discard": true, 217 "run_command": ["-d", "-i", "-t", "{{.Image}}", "/bin/sh"] 218 } 219 ], 220 "provisioners": [ 221 { 222 "type": "file", 223 "source": "test-fixtures/onecakes/strawberry", 224 "destination": "/strawberry-cake" 225 }, 226 { 227 "type": "file", 228 "source": "/strawberry-cake", 229 "destination": "my-strawberry-cake", 230 "direction": "download" 231 } 232 ] 233 } 234 ` 235 236 const dockerLargeBuilderConfig = ` 237 { 238 "builders": [ 239 { 240 "type": "docker", 241 "image": "ubuntu", 242 "discard": true 243 } 244 ], 245 "provisioners": [ 246 { 247 "type": "shell", 248 "inline": [ 249 "dd if=/dev/urandom of=/tmp/cupcake bs=1M count=2", 250 "dd if=/dev/urandom of=/tmp/bigcake bs=1M count=100", 251 "sync", 252 "md5sum /tmp/cupcake /tmp/bigcake" 253 ] 254 }, 255 { 256 "type": "file", 257 "source": "/tmp/cupcake", 258 "destination": "cupcake", 259 "direction": "download" 260 }, 261 { 262 "type": "file", 263 "source": "/tmp/bigcake", 264 "destination": "bigcake", 265 "direction": "download" 266 } 267 ] 268 } 269 `