Skip to content

Commit

Permalink
feat: --platform flag (#380)
Browse files Browse the repository at this point in the history
* feat: --platform flag

- for images under test, pass the new --platform flag to pull the
  corresponding image if it is multi-platform capable
- test execution via CreateContainerOptions passes in platform. If
  unset, will default to linux/amd64

* add e2e tests for --platform usage

gofmt tidy

specify correct folder for platform tests

* set default value from host runtime

* update flags section in docs

* only need to support linux for running containers

MacOS cannot run containers natively. M1/M2 defaults to linux/arm64 so
the "os" field as part of --platform only needs to use linux/ as a
default with GOARCH read in for the particular CPU architecture. Same
deal with Windows, Docker on windows is virtualized Linux (AFAIU)
  • Loading branch information
coopernetes authored Oct 18, 2023
1 parent 8212f6e commit 0db4700
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 23 deletions.
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,19 +378,22 @@ container_structure_test(
### Flags:
`container-structure-test test -h`
```
-c, --config stringArray test config files
-d, --driver string driver to use when running tests (default "docker")
-f, --force force run of host driver (without user prompt)
-h, --help help for test
-i, --image string path to test image
--metadata string path to image metadata file
--no-color no color in the output
-o, --output string output format for the test report (available format: text, json, junit) (default "text")
--pull force a pull of the image before running tests
-q, --quiet flag to suppress output
--runtime string runtime to use with docker driver
--save preserve created containers after test run
--test-report string generate test report and write it to specified file (supported format: json, junit; default: json)
-c, --config stringArray test config files
--default-image-tag string default image tag to used when loading images to the daemon. required when --image-from-oci-layout refers to a oci layout lacking the reference annotation.
-d, --driver string driver to use when running tests (default "docker")
-f, --force force run of host driver (without user prompt)
-h, --help help for test
-i, --image string path to test image
--image-from-oci-layout string path to the oci layout to test against
--metadata string path to image metadata file
--no-color no color in the output
-o, --output string output format for the test report (available format: text, json, junit) (default "text")
--platform string Set platform if host is multi-platform capable (default "linux/amd64")
--pull force a pull of the image before running tests
-q, --quiet flag to suppress output
--runtime string runtime to use with docker driver
--save preserve created containers after test run
--test-report string generate test report and write it to specified file (supported format: json, junit; default: json)
```
See this [example repo](https://github.com/nkubala/structure-test-examples) for a full working example.
Expand Down
6 changes: 4 additions & 2 deletions cmd/container-structure-test/app/cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"fmt"
"io"
"os"

"runtime"
"github.com/GoogleContainerTools/container-structure-test/cmd/container-structure-test/app/cmd/test"
v1 "github.com/opencontainers/image-spec/specs-go/v1"

Expand Down Expand Up @@ -99,6 +99,7 @@ func run(out io.Writer) error {
Save: opts.Save,
Metadata: opts.Metadata,
Runtime: opts.Runtime,
Platform: opts.Platform,
}

var err error
Expand Down Expand Up @@ -170,6 +171,7 @@ func run(out io.Writer) error {
logrus.Fatalf("error connecting to daemon: %v", err)
}
if err = client.PullImage(docker.PullImageOptions{
Platform: opts.Platform,
Repository: ref.Context().RepositoryStr(),
Tag: ref.Identifier(),
Registry: ref.Context().RegistryStr(),
Expand Down Expand Up @@ -223,7 +225,7 @@ func AddTestFlags(cmd *cobra.Command) {
cmd.Flags().StringVarP(&opts.Driver, "driver", "d", "docker", "driver to use when running tests")
cmd.Flags().StringVar(&opts.Metadata, "metadata", "", "path to image metadata file")
cmd.Flags().StringVar(&opts.Runtime, "runtime", "", "runtime to use with docker driver")

cmd.Flags().StringVar(&opts.Platform, "platform", fmt.Sprintf("linux/%s", runtime.GOARCH), "Set platform if host is multi-platform capable")
cmd.Flags().BoolVar(&opts.Pull, "pull", false, "force a pull of the image before running tests")
cmd.MarkFlagsMutuallyExclusive("image-from-oci-layout", "pull")
cmd.Flags().BoolVar(&opts.Save, "save", false, "preserve created containers after test run")
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.19

require (
github.com/GoogleContainerTools/container-diff v0.17.1-0.20230727210151-35d9770aeea3
github.com/fsouza/go-dockerclient v1.9.7
github.com/fsouza/go-dockerclient v1.10.0
github.com/google/go-cmp v0.5.9
github.com/google/go-containerregistry v0.15.2
github.com/joho/godotenv v1.5.1
Expand All @@ -16,6 +16,8 @@ require (
gopkg.in/yaml.v2 v2.4.0
)

exclude github.com/docker/docker v24.0.6+incompatible // indirect

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
Expand All @@ -32,7 +34,7 @@ require (
github.com/klauspost/compress v1.16.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/morikuni/aec v1.0.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsouza/go-dockerclient v1.3.6/go.mod h1:ptN6nXBwrXuiHAz2TYGOFCBB1aKGr371sGjMFdJEr1A=
github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw=
github.com/fsouza/go-dockerclient v1.9.7/go.mod h1:vx9C32kE2D15yDSOMCDaAEIARZpDQDFBHeqL3MgQy/U=
github.com/fsouza/go-dockerclient v1.10.0 h1:ppSBsbR60I1DFbV4Ag7LlHlHakHFRNLk9XakATW1yVQ=
github.com/fsouza/go-dockerclient v1.10.0/go.mod h1:+iNzAW78AzClIBTZ6WFjkaMvOgz68GyCJ236b1opLTs=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
Expand Down Expand Up @@ -148,8 +148,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
Expand Down
1 change: 1 addition & 0 deletions pkg/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type StructureTestOptions struct {
DefaultImageTag string
Driver string
Runtime string
Platform string
Metadata string
TestReport string
ConfigFiles []string
Expand Down
6 changes: 6 additions & 0 deletions pkg/drivers/docker_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type DockerDriver struct {
env map[string]string
save bool
runtime string
platform string
runOpts unversioned.ContainerRunOptions
}

Expand All @@ -57,6 +58,7 @@ func NewDockerDriver(args DriverConfig) (Driver, error) {
env: nil,
save: args.Save,
runtime: args.Runtime,
platform: args.Platform,
runOpts: args.RunOpts,
}, nil
}
Expand Down Expand Up @@ -98,6 +100,7 @@ func (d *DockerDriver) Destroy() {
func (d *DockerDriver) SetEnv(envVars []unversioned.EnvVar) error {
env := d.processEnvVars(envVars)
container, err := d.cli.CreateContainer(docker.CreateContainerOptions{
Platform: d.platform,
Config: &docker.Config{
Image: d.currentImage,
Env: env,
Expand Down Expand Up @@ -205,6 +208,7 @@ func (d *DockerDriver) retrieveTar(path string) (*tar.Reader, error) {
// this contains a placeholder command which does not get run, since
// the client doesn't allow creating a container without a command.
container, err := d.cli.CreateContainer(docker.CreateContainerOptions{
Platform: d.platform,
Config: &docker.Config{
Image: d.currentImage,
Cmd: []string{utils.NoopCommand},
Expand Down Expand Up @@ -316,6 +320,7 @@ func (d *DockerDriver) ReadDir(target string) ([]os.FileInfo, error) {
// and sets that image as the new "current image"
func (d *DockerDriver) runAndCommit(env []string, command []string) (string, error) {
createOpts := docker.CreateContainerOptions{
Platform: d.platform,
Config: &docker.Config{
Image: d.currentImage,
Env: env,
Expand Down Expand Up @@ -365,6 +370,7 @@ func (d *DockerDriver) runAndCommit(env []string, command []string) (string, err

func (d *DockerDriver) exec(env []string, command []string) (string, string, int, error) {
createOpts := docker.CreateContainerOptions{
Platform: d.platform,
Config: &docker.Config{
Image: d.currentImage,
Env: env,
Expand Down
1 change: 1 addition & 0 deletions pkg/drivers/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type DriverConfig struct {
Save bool // used by Docker/Tar drivers
Metadata string // used by Host driver
Runtime string // used by Docker driver
Platform string // used by Docker driver
RunOpts unversioned.ContainerRunOptions // used by Docker driver
}

Expand Down
45 changes: 43 additions & 2 deletions tests/structure_test_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ then
echo "$res"
echo "$code"
failures=$((failures +1))
else
else
echo "PASS: oci failing test case"
fi

Expand All @@ -242,10 +242,51 @@ then
echo "$res"
echo "$code"
failures=$((failures +1))
else
else
echo "PASS: oci success test case"
fi

HEADER "Platform test cases"

docker run --rm --privileged tonistiigi/binfmt --install all > /dev/null
res=$(./out/container-structure-test test --image "$test_image" --platform="linux/$go_architecture" --config "${test_config_dir}/ubuntu_20_04_test.yaml" 2>&1)
code=$?
if ! [[ ("$res" =~ "PASS" && "$code" == "0") ]];
then
echo "FAIL: current host platform test case"
echo "$res"
echo "$code"
failures=$((failures +1))
else
echo "PASS: current host platform test case"
fi

res=$(./out/container-structure-test test --image "$test_image" --platform="linux/riscv64" --config "${test_config_dir}/ubuntu_20_04_test.yaml" 2>&1)
code=$?
if ! [[ ("$res" =~ image\ with\ reference.+was\ found\ but\ does\ not\ match\ the\ specified\ platform:\ wanted\ linux\/\riscv64,\ actual:\ linux\/$go_architecture && "$code" == "1") ]];
then
echo "FAIL: platform failing test case"
echo "$res"
echo "$code"
failures=$((failures +1))
else
echo "PASS: platform failing test case"
fi

test_config_dir="${test_dir}/s390x"
res=$(./out/container-structure-test test --image "$test_image" --platform="linux/s390x" --pull --config "${test_config_dir}/ubuntu_20_04_test.yaml" 2>&1)
code=$?
if ! [[ ("$res" =~ "PASS" && "$code" == "0") ]];
then
echo "FAIL: platform w/ --pull test case"
echo "$res"
echo "$code"
failures=$((failures +1))
else
echo "PASS: platform w/ --pull test case"
fi


if [ $failures -gt 0 ]; then
echo "Some tests did not pass. $failures"
exit 1
Expand Down

0 comments on commit 0db4700

Please sign in to comment.