From b24f34bdb6e73b8bf83639483ebec3354b999c18 Mon Sep 17 00:00:00 2001 From: Morgan Jones Date: Mon, 26 Feb 2018 23:01:43 +0000 Subject: [PATCH] Add containerRunOptions, add support for privielged, mounts and capabilities --- .gitignore | 4 ++- cmd/test.go | 37 ++++++++++++++++++--------- pkg/drivers/BUILD.bazel | 1 + pkg/drivers/docker_driver.go | 48 ++++++++++++++++++++++++------------ pkg/drivers/driver.go | 11 ++++++--- pkg/drivers/options.go | 7 ++++++ pkg/types/types.go | 5 ++++ pkg/types/v2/structure.go | 18 ++++++++------ tests/debian_test.yaml | 24 ++++++++++++++++++ 9 files changed, 114 insertions(+), 41 deletions(-) create mode 100644 pkg/drivers/options.go diff --git a/.gitignore b/.gitignore index 284686f9..90ff2422 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ structure_test structure-test structure_tests.test out/ -**/bazel-* \ No newline at end of file +**/bazel-* +.idea +container-structure-test diff --git a/cmd/test.go b/cmd/test.go index 287fb91a..18e0ec8d 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -109,6 +109,7 @@ func Parse(fp string) (types.StructureTest, error) { var unmarshal types.Unmarshaller var strictUnmarshal types.Unmarshaller var versionHolder types.SchemaVersion + var driverOptionsHolder types.DriverOptions switch { case strings.HasSuffix(fp, ".json"): @@ -124,12 +125,21 @@ func Parse(fp string) (types.StructureTest, error) { if err := unmarshal(testContents, &versionHolder); err != nil { return nil, err } + if err := unmarshal(testContents, &driverOptionsHolder); err != nil { + return nil, err + } version := versionHolder.SchemaVersion if version == "" { return nil, errors.New("Please provide JSON schema version") } + // We create the drivers.NewDriverFunc for the specified driver + driverImplFunc := NewDriverImplFunc(driverOptionsHolder.Driver) + + // args is a global variable, need to pass in the containerRunOpts + args.ContainerRunOpts = driverOptionsHolder.ContainerRunOpts + var st types.StructureTest if schemaVersion, ok := types.SchemaVersions[version]; ok { st = schemaVersion() @@ -140,10 +150,24 @@ func Parse(fp string) (types.StructureTest, error) { strictUnmarshal(testContents, st) tests, _ := st.(types.StructureTest) //type assertion - tests.SetDriverImpl(driverImpl, *args) + tests.SetDriverImpl(driverImplFunc, *args) return tests, nil } +func NewDriverImplFunc(driver string) drivers.NewDriverFunc { + // let's default to docker in the case of no driver + if driver == "" { + driver = "docker" + + } + driverImpl = drivers.InitDriverImpl(driver) + if driverImpl == nil { + logrus.Fatalf("Unsupported driver type: %s", driver) + } + logrus.Infof("Using driver %s\n", driver) + return driverImpl +} + func Run() []*unversioned.FullResult { args = &drivers.DriverConfig{ Image: imagePath, @@ -151,8 +175,6 @@ func Run() []*unversioned.FullResult { Metadata: metadata, } - var err error - if pull { if driver != drivers.Docker { logrus.Fatal("Image pull not supported when not using Docker driver") @@ -178,21 +200,12 @@ func Run() []*unversioned.FullResult { os.Exit(1) } - driverImpl = drivers.InitDriverImpl(driver) - if driverImpl == nil { - logrus.Fatalf("Unsupported driver type: %s", driver) - } - if err != nil { - logrus.Fatal(err.Error()) - } - logrus.Infof("Using driver %s\n", driver) return RunTests() } func init() { RootCmd.AddCommand(TestCmd) TestCmd.Flags().StringVar(&imagePath, "image", "", "path to test image") - TestCmd.Flags().StringVar(&driver, "driver", "docker", "driver to use when running tests") TestCmd.Flags().StringVar(&metadata, "metadata", "", "path to image metadata file") TestCmd.Flags().BoolVar(&pull, "pull", false, "force a pull of the image before running tests") TestCmd.Flags().BoolVar(&save, "save", false, "preserve created containers after test run") diff --git a/pkg/drivers/BUILD.bazel b/pkg/drivers/BUILD.bazel index 768dc2aa..7a290479 100644 --- a/pkg/drivers/BUILD.bazel +++ b/pkg/drivers/BUILD.bazel @@ -6,6 +6,7 @@ go_library( "docker_driver.go", "driver.go", "host_driver.go", + "options.go", "tar_driver.go", ], importpath = "github.com/GoogleCloudPlatform/container-structure-test/pkg/drivers", diff --git a/pkg/drivers/docker_driver.go b/pkg/drivers/docker_driver.go index f75eabab..cbab94bf 100644 --- a/pkg/drivers/docker_driver.go +++ b/pkg/drivers/docker_driver.go @@ -19,25 +19,27 @@ import ( "bufio" "bytes" "fmt" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" "io" "os" "path" "path/filepath" "strings" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/GoogleCloudPlatform/container-structure-test/pkg/types/unversioned" "github.com/GoogleCloudPlatform/container-structure-test/pkg/utils" docker "github.com/fsouza/go-dockerclient" ) type DockerDriver struct { - originalImage string - currentImage string - cli docker.Client - env map[string]string - save bool + originalImage string + currentImage string + cli docker.Client + env map[string]string + save bool + containerRunOpts ContainerRunOpts } func NewDockerDriver(args DriverConfig) (Driver, error) { @@ -45,12 +47,14 @@ func NewDockerDriver(args DriverConfig) (Driver, error) { if err != nil { return nil, err } + return &DockerDriver{ - originalImage: args.Image, - currentImage: args.Image, - cli: *newCli, - env: nil, - save: args.Save, + originalImage: args.Image, + currentImage: args.Image, + cli: *newCli, + env: nil, + save: args.Save, + containerRunOpts: args.ContainerRunOpts, }, nil } @@ -251,6 +255,11 @@ func (d *DockerDriver) ReadDir(target string) ([]os.FileInfo, error) { // 3) commits the container with its changes to a new image, // and sets that image as the new "current image" func (d *DockerDriver) runAndCommit(env []string, command []string) (string, error) { + hostConfig := &docker.HostConfig{ + Binds: d.containerRunOpts.BindMounts, + CapAdd: d.containerRunOpts.Capabilities, + Privileged: d.containerRunOpts.Privileged, + } container, err := d.cli.CreateContainer(docker.CreateContainerOptions{ Config: &docker.Config{ Image: d.currentImage, @@ -260,14 +269,15 @@ func (d *DockerDriver) runAndCommit(env []string, command []string) (string, err AttachStdout: true, AttachStderr: true, }, - HostConfig: nil, + HostConfig: hostConfig, NetworkingConfig: nil, }) if err != nil { return "", errors.Wrap(err, "Error creating container") } - if err = d.cli.StartContainer(container.ID, nil); err != nil { + err = d.cli.StartContainer(container.ID, hostConfig) + if err != nil { return "", errors.Wrap(err, "Error creating container") } @@ -296,6 +306,12 @@ func (d *DockerDriver) runAndCommit(env []string, command []string) (string, err } func (d *DockerDriver) exec(env []string, command []string) (string, string, int, error) { + hostConfig := &docker.HostConfig{ + Binds: d.containerRunOpts.BindMounts, + CapAdd: d.containerRunOpts.Capabilities, + Privileged: d.containerRunOpts.Privileged, + } + // first, start container from the current image container, err := d.cli.CreateContainer(docker.CreateContainerOptions{ Config: &docker.Config{ @@ -306,7 +322,7 @@ func (d *DockerDriver) exec(env []string, command []string) (string, string, int AttachStdout: true, AttachStderr: true, }, - HostConfig: nil, + HostConfig: hostConfig, NetworkingConfig: nil, }) if err != nil { @@ -317,7 +333,7 @@ func (d *DockerDriver) exec(env []string, command []string) (string, string, int stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) - if err = d.cli.StartContainer(container.ID, nil); err != nil { + if err = d.cli.StartContainer(container.ID, hostConfig); err != nil { return "", "", -1, errors.Wrap(err, "Error creating container") } diff --git a/pkg/drivers/driver.go b/pkg/drivers/driver.go index 220d5d26..1bca11dd 100644 --- a/pkg/drivers/driver.go +++ b/pkg/drivers/driver.go @@ -28,9 +28,10 @@ const ( ) type DriverConfig struct { - Image string // used by Docker/Tar drivers - Save bool // used by Docker/Tar drivers - Metadata string // used by Host driver + Image string // used by Docker/Tar drivers + Save bool // used by Docker/Tar drivers + Metadata string // used by Host driver + ContainerRunOpts ContainerRunOpts // used by the Docker driver } type Driver interface { @@ -56,7 +57,9 @@ type Driver interface { Destroy() } -func InitDriverImpl(driver string) func(DriverConfig) (Driver, error) { +type NewDriverFunc func(DriverConfig) (Driver, error) + +func InitDriverImpl(driver string) NewDriverFunc { switch driver { // future drivers will be added here case Docker: diff --git a/pkg/drivers/options.go b/pkg/drivers/options.go new file mode 100644 index 00000000..e70b64bd --- /dev/null +++ b/pkg/drivers/options.go @@ -0,0 +1,7 @@ +package drivers + +type ContainerRunOpts struct { + BindMounts []string `yaml:"bindMounts"` + Privileged bool `yaml:"privileged"` + Capabilities []string `yaml:"capabilities"` +} diff --git a/pkg/types/types.go b/pkg/types/types.go index 0fa23276..e616f765 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -36,4 +36,9 @@ type SchemaVersion struct { SchemaVersion string `yaml:"schemaVersion"` } +type DriverOptions struct { + Driver string `yaml:"driver"` + ContainerRunOpts drivers.ContainerRunOpts `yaml:"containerRunOpts"` +} + type Unmarshaller func([]byte, interface{}) error diff --git a/pkg/types/v2/structure.go b/pkg/types/v2/structure.go index 57830f98..e05da6f2 100644 --- a/pkg/types/v2/structure.go +++ b/pkg/types/v2/structure.go @@ -22,14 +22,16 @@ import ( ) type StructureTest struct { - DriverImpl func(drivers.DriverConfig) (drivers.Driver, error) - DriverArgs drivers.DriverConfig - GlobalEnvVars []types.EnvVar `yaml:"globalEnvVars"` - CommandTests []CommandTest `yaml:"commandTests"` - FileExistenceTests []FileExistenceTest `yaml:"fileExistenceTests"` - FileContentTests []FileContentTest `yaml:"fileContentTests"` - MetadataTest MetadataTest `yaml:"metadataTest"` - LicenseTests []LicenseTest `yaml:"licenseTests"` + DriverImpl func(drivers.DriverConfig) (drivers.Driver, error) + DriverArgs drivers.DriverConfig + // Used to pass mount/privilege arguments to the container runtime + ContainerRunOpts drivers.ContainerRunOpts `yaml:"containerRunOpts"` + GlobalEnvVars []types.EnvVar `yaml:"globalEnvVars"` + CommandTests []CommandTest `yaml:"commandTests"` + FileExistenceTests []FileExistenceTest `yaml:"fileExistenceTests"` + FileContentTests []FileContentTest `yaml:"fileContentTests"` + MetadataTest MetadataTest `yaml:"metadataTest"` + LicenseTests []LicenseTest `yaml:"licenseTests"` } func (st *StructureTest) NewDriver() (drivers.Driver, error) { diff --git a/tests/debian_test.yaml b/tests/debian_test.yaml index c4c6e94c..bb439303 100644 --- a/tests/debian_test.yaml +++ b/tests/debian_test.yaml @@ -1,4 +1,14 @@ schemaVersion: '2.0.0' # Make sure to test the latest schema version + +driver: "docker" + +containerRunOpts: + bindMounts: + - "/tmp/test:/tmp/test" + privileged: true + capabilities: + - "sys_admin" + commandTests: - name: 'apt-get' command: 'apt-get' @@ -13,6 +23,18 @@ commandTests: command: 'sh' args: ['-c', 'echo $PATH'] expectedOutput: ['/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'] +- name: "Test Capabilities ContainerRunOptions" + command: "capsh" + args: ["--print"] + expectedOutput: + - ".*cap_sys_admin.*" +- name: "Test bindMounts containerRunOptions" + command: "test" + args: + - "-d" + - "/tmp/test" + exitCode: 0 + fileContentTests: - name: 'Debian Sources' excludedContents: ['.*gce_debian_mirror.*'] @@ -21,6 +43,7 @@ fileContentTests: - name: 'Retry Policy' expectedContents: ['Acquire::Retries 3;'] path: '/etc/apt/apt.conf.d/apt-retry' + fileExistenceTests: - name: 'Root' isDirectory: true @@ -34,6 +57,7 @@ fileExistenceTests: isDirectory: false path: '/etc/machine-id' shouldExist: true + licenseTests: - debian: true files: