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  }