github.com/YousefHaggyHeroku/pack@v1.5.5/internal/build/container_ops_test.go (about) 1 package build_test 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "math/rand" 8 "path/filepath" 9 "runtime" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/docker/docker/api/types" 15 "github.com/docker/docker/api/types/mount" 16 17 dcontainer "github.com/docker/docker/api/types/container" 18 "github.com/docker/docker/client" 19 "github.com/heroku/color" 20 "github.com/sclevine/spec" 21 "github.com/sclevine/spec/report" 22 23 "github.com/YousefHaggyHeroku/pack/internal/build" 24 "github.com/YousefHaggyHeroku/pack/internal/builder" 25 "github.com/YousefHaggyHeroku/pack/internal/container" 26 h "github.com/YousefHaggyHeroku/pack/testhelpers" 27 ) 28 29 // TestContainerOperations are integration tests for the container operations against a docker daemon 30 func TestContainerOperations(t *testing.T) { 31 rand.Seed(time.Now().UTC().UnixNano()) 32 33 color.Disable(true) 34 defer color.Disable(false) 35 36 h.RequireDocker(t) 37 38 var err error 39 ctrClient, err = client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.38")) 40 h.AssertNil(t, err) 41 42 spec.Run(t, "container-ops", testContainerOps, spec.Report(report.Terminal{}), spec.Sequential()) 43 } 44 45 func testContainerOps(t *testing.T, when spec.G, it spec.S) { 46 var ( 47 imageName string 48 osType string 49 ) 50 51 it.Before(func() { 52 imageName = "container-ops.test-" + h.RandString(10) 53 54 info, err := ctrClient.Info(context.TODO()) 55 h.AssertNil(t, err) 56 osType = info.OSType 57 58 dockerfileContent := `FROM busybox` 59 if osType == "windows" { 60 dockerfileContent = `FROM mcr.microsoft.com/windows/nanoserver:1809` 61 } 62 63 h.CreateImage(t, ctrClient, imageName, dockerfileContent) 64 65 h.AssertNil(t, err) 66 }) 67 68 it.After(func() { 69 h.DockerRmi(ctrClient, imageName) 70 }) 71 72 when("#CopyDir", func() { 73 it("writes contents with proper owner/permissions", func() { 74 containerDir := "/some-vol" 75 if osType == "windows" { 76 containerDir = `c:\some-vol` 77 } 78 79 ctrCmd := []string{"ls", "-al", "/some-vol"} 80 if osType == "windows" { 81 ctrCmd = []string{"cmd", "/c", `dir /q /s c:\some-vol`} 82 } 83 84 ctx := context.Background() 85 ctr, err := createContainer(ctx, imageName, containerDir, osType, ctrCmd...) 86 h.AssertNil(t, err) 87 defer cleanupContainer(ctx, ctr.ID) 88 89 copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app"), containerDir, 123, 456, osType, nil) 90 91 var outBuf, errBuf bytes.Buffer 92 err = copyDirOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf) 93 h.AssertNil(t, err) 94 95 err = container.Run(ctx, ctrClient, ctr.ID, &outBuf, &errBuf) 96 h.AssertNil(t, err) 97 98 h.AssertEq(t, errBuf.String(), "") 99 if osType == "windows" { 100 h.AssertContainsMatch(t, strings.ReplaceAll(outBuf.String(), "\r", ""), ` 101 (.*) <DIR> ... . 102 (.*) <DIR> ... .. 103 (.*) 17 ... fake-app-file 104 (.*) <SYMLINK> ... fake-app-symlink \[fake-app-file\] 105 (.*) 0 ... file-to-ignore 106 `) 107 } else { 108 if runtime.GOOS == "windows" { 109 // LCOW does not currently support symlinks 110 h.AssertContainsMatch(t, outBuf.String(), ` 111 -rwxrwxrwx 1 123 456 (.*) fake-app-file 112 -rwxrwxrwx 1 123 456 (.*) fake-app-symlink 113 -rwxrwxrwx 1 123 456 (.*) file-to-ignore 114 `) 115 } else { 116 h.AssertContainsMatch(t, outBuf.String(), ` 117 -rw-r--r-- 1 123 456 (.*) fake-app-file 118 lrwxrwxrwx 1 123 456 (.*) fake-app-symlink -> fake-app-file 119 -rw-r--r-- 1 123 456 (.*) file-to-ignore 120 `) 121 } 122 } 123 }) 124 125 it("writes contents ignoring from file filter", func() { 126 containerDir := "/some-vol" 127 if osType == "windows" { 128 containerDir = `c:\some-vol` 129 } 130 131 ctrCmd := []string{"ls", "-al", "/some-vol"} 132 if osType == "windows" { 133 ctrCmd = []string{"cmd", "/c", `dir /q /s /n c:\some-vol`} 134 } 135 136 ctx := context.Background() 137 ctr, err := createContainer(ctx, imageName, containerDir, osType, ctrCmd...) 138 h.AssertNil(t, err) 139 defer cleanupContainer(ctx, ctr.ID) 140 141 copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app"), containerDir, 123, 456, osType, func(filename string) bool { 142 return filepath.Base(filename) != "file-to-ignore" 143 }) 144 145 var outBuf, errBuf bytes.Buffer 146 err = copyDirOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf) 147 h.AssertNil(t, err) 148 149 err = container.Run(ctx, ctrClient, ctr.ID, &outBuf, &errBuf) 150 h.AssertNil(t, err) 151 152 h.AssertEq(t, errBuf.String(), "") 153 h.AssertContains(t, outBuf.String(), "fake-app-file") 154 h.AssertNotContains(t, outBuf.String(), "file-to-ignore") 155 }) 156 157 it("writes contents from zip file", func() { 158 containerDir := "/some-vol" 159 if osType == "windows" { 160 containerDir = `c:\some-vol` 161 } 162 163 ctrCmd := []string{"ls", "-al", "/some-vol"} 164 if osType == "windows" { 165 ctrCmd = []string{"cmd", "/c", `dir /q /s /n c:\some-vol`} 166 } 167 168 ctx := context.Background() 169 ctr, err := createContainer(ctx, imageName, containerDir, osType, ctrCmd...) 170 h.AssertNil(t, err) 171 defer cleanupContainer(ctx, ctr.ID) 172 173 copyDirOp := build.CopyDir(filepath.Join("testdata", "fake-app.zip"), containerDir, 123, 456, osType, nil) 174 175 var outBuf, errBuf bytes.Buffer 176 err = copyDirOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf) 177 h.AssertNil(t, err) 178 179 err = container.Run(ctx, ctrClient, ctr.ID, &outBuf, &errBuf) 180 h.AssertNil(t, err) 181 182 h.AssertEq(t, errBuf.String(), "") 183 if osType == "windows" { 184 h.AssertContainsMatch(t, strings.ReplaceAll(outBuf.String(), "\r", ""), ` 185 (.*) <DIR> ... . 186 (.*) <DIR> ... .. 187 (.*) 17 ... fake-app-file 188 `) 189 } else { 190 h.AssertContainsMatch(t, outBuf.String(), ` 191 -rw-r--r-- 1 123 456 (.*) fake-app-file 192 `) 193 } 194 }) 195 }) 196 197 when("#WriteStackToml", func() { 198 it("writes file", func() { 199 containerDir := "/layers-vol" 200 containerPath := "/layers-vol/stack.toml" 201 if osType == "windows" { 202 containerDir = `c:\layers-vol` 203 containerPath = `c:\layers-vol\stack.toml` 204 } 205 206 ctrCmd := []string{"ls", "-al", "/layers-vol/stack.toml"} 207 if osType == "windows" { 208 ctrCmd = []string{"cmd", "/c", `dir /q /n c:\layers-vol\stack.toml`} 209 } 210 ctx := context.Background() 211 ctr, err := createContainer(ctx, imageName, containerDir, osType, ctrCmd...) 212 h.AssertNil(t, err) 213 defer cleanupContainer(ctx, ctr.ID) 214 215 writeOp := build.WriteStackToml(containerPath, builder.StackMetadata{ 216 RunImage: builder.RunImageMetadata{ 217 Image: "image-1", 218 Mirrors: []string{ 219 "mirror-1", 220 "mirror-2", 221 }, 222 }, 223 }, osType) 224 225 var outBuf, errBuf bytes.Buffer 226 err = writeOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf) 227 h.AssertNil(t, err) 228 229 err = container.Run(ctx, ctrClient, ctr.ID, &outBuf, &errBuf) 230 h.AssertNil(t, err) 231 232 h.AssertEq(t, errBuf.String(), "") 233 if osType == "windows" { 234 h.AssertContains(t, outBuf.String(), `01/01/1980 12:00 AM 69 ... stack.toml`) 235 } else { 236 h.AssertContains(t, outBuf.String(), `-rwxr-xr-x 1 root root 69 Jan 1 1980 /layers-vol/stack.toml`) 237 } 238 }) 239 240 it("has expected contents", func() { 241 containerDir := "/layers-vol" 242 containerPath := "/layers-vol/stack.toml" 243 if osType == "windows" { 244 containerDir = `c:\layers-vol` 245 containerPath = `c:\layers-vol\stack.toml` 246 } 247 248 ctrCmd := []string{"cat", "/layers-vol/stack.toml"} 249 if osType == "windows" { 250 ctrCmd = []string{"cmd", "/c", `type c:\layers-vol\stack.toml`} 251 } 252 253 ctx := context.Background() 254 ctr, err := createContainer(ctx, imageName, containerDir, osType, ctrCmd...) 255 h.AssertNil(t, err) 256 defer cleanupContainer(ctx, ctr.ID) 257 258 writeOp := build.WriteStackToml(containerPath, builder.StackMetadata{ 259 RunImage: builder.RunImageMetadata{ 260 Image: "image-1", 261 Mirrors: []string{ 262 "mirror-1", 263 "mirror-2", 264 }, 265 }, 266 }, osType) 267 268 var outBuf, errBuf bytes.Buffer 269 err = writeOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf) 270 h.AssertNil(t, err) 271 272 err = container.Run(ctx, ctrClient, ctr.ID, &outBuf, &errBuf) 273 h.AssertNil(t, err) 274 275 h.AssertEq(t, errBuf.String(), "") 276 h.AssertContains(t, outBuf.String(), `[run-image] 277 image = "image-1" 278 mirrors = ["mirror-1", "mirror-2"] 279 `) 280 }) 281 }) 282 283 when("#EnsureVolumeAccess", func() { 284 it("changes owner of volume", func() { 285 h.SkipIf(t, osType != "windows", "no-op for linux") 286 287 ctx := context.Background() 288 289 ctrCmd := []string{"ls", "-al", "/my-volume"} 290 containerDir := "/my-volume" 291 if osType == "windows" { 292 ctrCmd = []string{"cmd", "/c", `icacls c:\my-volume`} 293 containerDir = `c:\my-volume` 294 } 295 296 ctr, err := createContainer(ctx, imageName, containerDir, osType, ctrCmd...) 297 h.AssertNil(t, err) 298 defer cleanupContainer(ctx, ctr.ID) 299 300 inspect, err := ctrClient.ContainerInspect(ctx, ctr.ID) 301 if err != nil { 302 return 303 } 304 305 // use container's current volumes 306 var ctrVolumes []string 307 for _, m := range inspect.Mounts { 308 if m.Type == mount.TypeVolume { 309 ctrVolumes = append(ctrVolumes, m.Name) 310 } 311 } 312 313 var outBuf, errBuf bytes.Buffer 314 315 // reuse same volume twice to demonstrate multiple ops 316 initVolumeOp := build.EnsureVolumeAccess(123, 456, osType, ctrVolumes[0], ctrVolumes[0]) 317 err = initVolumeOp(ctrClient, ctx, ctr.ID, &outBuf, &errBuf) 318 h.AssertNil(t, err) 319 err = container.Run(ctx, ctrClient, ctr.ID, &outBuf, &errBuf) 320 h.AssertNil(t, err) 321 322 h.AssertEq(t, errBuf.String(), "") 323 h.AssertContains(t, outBuf.String(), `BUILTIN\Users:(OI)(CI)(F)`) 324 }) 325 }) 326 } 327 328 func createContainer(ctx context.Context, imageName, containerDir, osType string, cmd ...string) (dcontainer.ContainerCreateCreatedBody, error) { 329 isolationType := dcontainer.IsolationDefault 330 if osType == "windows" { 331 isolationType = dcontainer.IsolationProcess 332 } 333 334 return ctrClient.ContainerCreate(ctx, 335 &dcontainer.Config{ 336 Image: imageName, 337 Cmd: cmd, 338 }, 339 &dcontainer.HostConfig{ 340 Binds: []string{fmt.Sprintf("%s:%s", fmt.Sprintf("tests-volume-%s", h.RandString(5)), filepath.ToSlash(containerDir))}, 341 Isolation: isolationType, 342 }, nil, "", 343 ) 344 } 345 346 func cleanupContainer(ctx context.Context, ctrID string) { 347 inspect, err := ctrClient.ContainerInspect(ctx, ctrID) 348 if err != nil { 349 return 350 } 351 352 // remove container 353 ctrClient.ContainerRemove(ctx, ctrID, types.ContainerRemoveOptions{}) 354 355 // remove volumes 356 for _, m := range inspect.Mounts { 357 if m.Type == mount.TypeVolume { 358 ctrClient.VolumeRemove(ctx, m.Name, true) 359 } 360 } 361 }