github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/multi_platform_linux_test.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "fmt" 21 "io" 22 "os" 23 "strings" 24 "testing" 25 26 "github.com/containerd/nerdctl/pkg/testutil" 27 "github.com/containerd/nerdctl/pkg/testutil/nettestutil" 28 "github.com/containerd/nerdctl/pkg/testutil/testregistry" 29 "gotest.tools/v3/assert" 30 ) 31 32 func testMultiPlatformRun(base *testutil.Base, alpineImage string) { 33 t := base.T 34 testutil.RequireExecPlatform(t, "linux/amd64", "linux/arm64", "linux/arm/v7") 35 testCases := map[string]string{ 36 "amd64": "x86_64", 37 "arm64": "aarch64", 38 "arm": "armv7l", 39 "linux/arm": "armv7l", 40 "linux/arm/v7": "armv7l", 41 } 42 for plat, expectedUnameM := range testCases { 43 t.Logf("Testing %q (%q)", plat, expectedUnameM) 44 cmd := base.Cmd("run", "--rm", "--platform="+plat, alpineImage, "uname", "-m") 45 cmd.AssertOutExactly(expectedUnameM + "\n") 46 } 47 } 48 49 func TestMultiPlatformRun(t *testing.T) { 50 base := testutil.NewBase(t) 51 testMultiPlatformRun(base, testutil.AlpineImage) 52 } 53 54 func TestMultiPlatformBuildPush(t *testing.T) { 55 testutil.DockerIncompatible(t) // non-buildx version of `docker build` lacks multi-platform. Also, `docker push` lacks --platform. 56 testutil.RequiresBuild(t) 57 testutil.RequireExecPlatform(t, "linux/amd64", "linux/arm64", "linux/arm/v7") 58 base := testutil.NewBase(t) 59 defer base.Cmd("builder", "prune").Run() 60 tID := testutil.Identifier(t) 61 reg := testregistry.NewPlainHTTP(base, 5000) 62 defer reg.Cleanup() 63 64 imageName := fmt.Sprintf("localhost:%d/%s:latest", reg.ListenPort, tID) 65 defer base.Cmd("rmi", imageName).Run() 66 67 dockerfile := fmt.Sprintf(`FROM %s 68 RUN echo dummy 69 `, testutil.AlpineImage) 70 71 buildCtx, err := createBuildContext(dockerfile) 72 assert.NilError(t, err) 73 defer os.RemoveAll(buildCtx) 74 75 base.Cmd("build", "-t", imageName, "--platform=amd64,arm64,linux/arm/v7", buildCtx).AssertOK() 76 testMultiPlatformRun(base, imageName) 77 base.Cmd("push", "--platform=amd64,arm64,linux/arm/v7", imageName).AssertOK() 78 } 79 80 // TestMultiPlatformBuildPushNoRun tests if the push succeeds in a situation where nerdctl builds 81 // a Dockerfile without RUN, COPY, etc commands. In such situation, BuildKit doesn't download the base image 82 // so nerdctl needs to ensure these blobs to be locally available. 83 func TestMultiPlatformBuildPushNoRun(t *testing.T) { 84 testutil.DockerIncompatible(t) // non-buildx version of `docker build` lacks multi-platform. Also, `docker push` lacks --platform. 85 testutil.RequiresBuild(t) 86 testutil.RequireExecPlatform(t, "linux/amd64", "linux/arm64", "linux/arm/v7") 87 base := testutil.NewBase(t) 88 defer base.Cmd("builder", "prune").Run() 89 tID := testutil.Identifier(t) 90 reg := testregistry.NewPlainHTTP(base, 5000) 91 defer reg.Cleanup() 92 93 imageName := fmt.Sprintf("localhost:%d/%s:latest", reg.ListenPort, tID) 94 defer base.Cmd("rmi", imageName).Run() 95 96 dockerfile := fmt.Sprintf(`FROM %s 97 CMD echo dummy 98 `, testutil.AlpineImage) 99 100 buildCtx, err := createBuildContext(dockerfile) 101 assert.NilError(t, err) 102 defer os.RemoveAll(buildCtx) 103 104 base.Cmd("build", "-t", imageName, "--platform=amd64,arm64,linux/arm/v7", buildCtx).AssertOK() 105 testMultiPlatformRun(base, imageName) 106 base.Cmd("push", "--platform=amd64,arm64,linux/arm/v7", imageName).AssertOK() 107 } 108 109 func TestMultiPlatformPullPushAllPlatforms(t *testing.T) { 110 testutil.DockerIncompatible(t) 111 base := testutil.NewBase(t) 112 tID := testutil.Identifier(t) 113 reg := testregistry.NewPlainHTTP(base, 5000) 114 defer reg.Cleanup() 115 116 pushImageName := fmt.Sprintf("localhost:%d/%s:latest", reg.ListenPort, tID) 117 defer base.Cmd("rmi", pushImageName).Run() 118 119 base.Cmd("pull", "--all-platforms", testutil.AlpineImage).AssertOK() 120 base.Cmd("tag", testutil.AlpineImage, pushImageName).AssertOK() 121 base.Cmd("push", "--all-platforms", pushImageName).AssertOK() 122 testMultiPlatformRun(base, pushImageName) 123 } 124 125 func TestMultiPlatformComposeUpBuild(t *testing.T) { 126 testutil.DockerIncompatible(t) 127 testutil.RequiresBuild(t) 128 testutil.RequireExecPlatform(t, "linux/amd64", "linux/arm64", "linux/arm/v7") 129 base := testutil.NewBase(t) 130 defer base.Cmd("builder", "prune").Run() 131 132 const dockerComposeYAML = ` 133 services: 134 svc0: 135 build: . 136 platform: amd64 137 ports: 138 - 8080:80 139 svc1: 140 build: . 141 platform: arm64 142 ports: 143 - 8081:80 144 svc2: 145 build: . 146 platform: linux/arm/v7 147 ports: 148 - 8082:80 149 ` 150 dockerfile := fmt.Sprintf(`FROM %s 151 RUN uname -m > /usr/share/nginx/html/index.html 152 `, testutil.NginxAlpineImage) 153 154 comp := testutil.NewComposeDir(t, dockerComposeYAML) 155 defer comp.CleanUp() 156 157 comp.WriteFile("Dockerfile", dockerfile) 158 159 base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d", "--build").AssertOK() 160 defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run() 161 162 testCases := map[string]string{ 163 "http://127.0.0.1:8080": "x86_64", 164 "http://127.0.0.1:8081": "aarch64", 165 "http://127.0.0.1:8082": "armv7l", 166 } 167 168 for testURL, expectedIndexHTML := range testCases { 169 resp, err := nettestutil.HTTPGet(testURL, 50, false) 170 assert.NilError(t, err) 171 respBody, err := io.ReadAll(resp.Body) 172 assert.NilError(t, err) 173 t.Logf("respBody=%q", respBody) 174 assert.Assert(t, strings.Contains(string(respBody), expectedIndexHTML)) 175 } 176 }