diff --git a/pkg/bootstrap/ephemeral/command.go b/pkg/bootstrap/ephemeral/command.go index ef569a126..dd9281c94 100644 --- a/pkg/bootstrap/ephemeral/command.go +++ b/pkg/bootstrap/ephemeral/command.go @@ -123,7 +123,7 @@ func (options *BootstrapContainerOptions) GetContainerStatus() (container.Status var exitCode int exitCode = state.ExitCode if exitCode > 0 { - reader, err := options.Container.GetContainerLogs() + reader, err := options.Container.GetContainerLogs(container.GetLogOptions{Stderr: true, Follow: true}) if err != nil { log.Printf("Error while trying to retrieve the container logs") return BootNullString, err @@ -197,7 +197,7 @@ func (options *BootstrapContainerOptions) CreateBootstrapContainer() error { fmt.Sprintf("%s=%s", envBootstrapVolume, containerVolMount), } - err := options.Container.RunCommand(container.RunCommandOptions{EnvVars: envVars, VolumeMounts: vols}) + err := options.Container.RunCommand(container.RunCommandOptions{EnvVars: envVars, Binds: vols}) if err != nil { return err } diff --git a/pkg/bootstrap/isogen/command.go b/pkg/bootstrap/isogen/command.go index b10306395..fda3c0a8c 100644 --- a/pkg/bootstrap/isogen/command.go +++ b/pkg/bootstrap/isogen/command.go @@ -134,7 +134,7 @@ func (opts BootstrapIsoOptions) CreateBootstrapIso() error { fmt.Sprintf("NO_PROXY=%s", os.Getenv("NO_PROXY")), } - err = opts.Builder.RunCommand(container.RunCommandOptions{EnvVars: envVars, VolumeMounts: vols}) + err = opts.Builder.RunCommand(container.RunCommandOptions{EnvVars: envVars, Binds: vols}) if err != nil { return err } @@ -143,7 +143,7 @@ func (opts BootstrapIsoOptions) CreateBootstrapIso() error { if log.DebugEnabled() { var cLogs io.ReadCloser - cLogs, err = opts.Builder.GetContainerLogs() + cLogs, err = opts.Builder.GetContainerLogs(container.GetLogOptions{Stderr: true, Follow: true}) if err != nil { log.Printf("failed to read container logs %s", err) } else { diff --git a/pkg/container/container.go b/pkg/container/container.go index 332c78886..cdc03488e 100644 --- a/pkg/container/container.go +++ b/pkg/container/container.go @@ -19,6 +19,11 @@ import ( "io" ) +const ( + // ContainerDriverDocker indicates that docker driver should be used in container constructor + ContainerDriverDocker = "docker" +) + // Status type provides container status type Status string @@ -36,7 +41,7 @@ type State struct { type Container interface { ImagePull() error RunCommand(RunCommandOptions) error - GetContainerLogs() (io.ReadCloser, error) + GetContainerLogs(GetLogOptions) (io.ReadCloser, error) InspectContainer() (State, error) WaitUntilFinished() error RmContainer() error @@ -45,13 +50,31 @@ type Container interface { // RunCommandOptions options for RunCommand type RunCommandOptions struct { - Privileged bool + Privileged bool + HostNewtork bool - Cmd []string - EnvVars []string - VolumeMounts []string + Cmd []string + EnvVars []string + Binds []string - Input io.Reader + Mounts []Mount + Input io.Reader +} + +// Mount describes mount settings +type Mount struct { + ReadOnly bool + Type string + Dst string + Src string +} + +// GetLogOptions options for getting logs +// If both Stderr and Stdout are specified the logs will contain both stderr and stdout +type GetLogOptions struct { + Stderr bool + Stdout bool + Follow bool } // NewContainer returns instance of Container interface implemented by particular driver @@ -63,7 +86,7 @@ func NewContainer(ctx context.Context, driver string, url string) (Container, er switch driver { case "": return nil, ErrNoContainerDriver{} - case "docker": + case ContainerDriverDocker: cli, err := NewDockerClient(ctx) if err != nil { return nil, err diff --git a/pkg/container/container_docker.go b/pkg/container/container_docker.go index 55070ce62..694d876f5 100644 --- a/pkg/container/container_docker.go +++ b/pkg/container/container_docker.go @@ -24,6 +24,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" @@ -174,18 +175,36 @@ func (c *DockerContainer) getConfig(opts RunCommandOptions) (container.Config, c if err != nil { return container.Config{}, container.HostConfig{}, err } + + mounts := []mount.Mount{} + for _, mnt := range opts.Mounts { + mounts = append(mounts, mount.Mount{ + Type: mount.Type(mnt.Type), + Source: mnt.Src, + Target: mnt.Dst, + ReadOnly: mnt.ReadOnly, + }) + } + cCfg := container.Config{ - Image: c.imageURL, - Cmd: cmd, - AttachStdin: true, - OpenStdin: true, - Env: opts.EnvVars, - Tty: true, + Image: c.imageURL, + Cmd: cmd, + + AttachStdin: true, + StdinOnce: true, + OpenStdin: true, + AttachStderr: true, + AttachStdout: true, + Env: opts.EnvVars, } hCfg := container.HostConfig{ - Binds: opts.VolumeMounts, + Binds: opts.Binds, + Mounts: mounts, Privileged: opts.Privileged, } + if opts.HostNewtork { + hCfg.NetworkMode = "host" + } return cCfg, hCfg, nil } @@ -268,6 +287,8 @@ func (c *DockerContainer) RunCommand(opts RunCommandOptions) (err error) { if attachErr != nil { return attachErr } + defer conn.Close() + if _, err = io.Copy(conn.Conn, opts.Input); err != nil { return err } @@ -282,8 +303,12 @@ func (c *DockerContainer) RunCommand(opts RunCommandOptions) (err error) { } // GetContainerLogs returns logs from the container as io.ReadCloser -func (c *DockerContainer) GetContainerLogs() (io.ReadCloser, error) { - return c.dockerClient.ContainerLogs(c.ctx, c.id, types.ContainerLogsOptions{ShowStdout: true, Follow: true}) +func (c *DockerContainer) GetContainerLogs(opts GetLogOptions) (io.ReadCloser, error) { + return c.dockerClient.ContainerLogs(c.ctx, c.id, types.ContainerLogsOptions{ + ShowStderr: opts.Stderr, + Follow: opts.Follow, + ShowStdout: opts.Stdout, + }) } // RmContainer kills and removes a container from the docker host. diff --git a/pkg/container/container_docker_test.go b/pkg/container/container_docker_test.go index e3d53c75b..b28eab930 100644 --- a/pkg/container/container_docker_test.go +++ b/pkg/container/container_docker_test.go @@ -286,6 +286,7 @@ func TestRunCommand(t *testing.T) { cmd []string containerInput io.Reader volumeMounts []string + mounts []Mount debug bool mockDockerClient mockDockerClient expectedRunErr error @@ -329,7 +330,15 @@ func TestRunCommand(t *testing.T) { return conn, nil }, }, - expectedRunErr: nil, + expectedRunErr: nil, + mounts: []Mount{ + { + ReadOnly: true, + Type: "bind", + Dst: "/dev/vda0", + Src: "/dev/vd3", + }, + }, expectedWaitErr: nil, assertF: func(t *testing.T) {}, }, @@ -422,9 +431,10 @@ func TestRunCommand(t *testing.T) { for _, tt := range tests { cnt := getDockerContainerMock(tt.mockDockerClient) actualErr := cnt.RunCommand(RunCommandOptions{ - Input: tt.containerInput, - Cmd: tt.cmd, - VolumeMounts: tt.volumeMounts, + Input: tt.containerInput, + Cmd: tt.cmd, + Binds: tt.volumeMounts, + Mounts: tt.mounts, }) assert.Equal(t, tt.expectedRunErr, actualErr) actualErr = cnt.WaitUntilFinished() @@ -468,12 +478,12 @@ func TestRunCommandOutput(t *testing.T) { for _, tt := range tests { cnt := getDockerContainerMock(tt.mockDockerClient) actualErr := cnt.RunCommand(RunCommandOptions{ - Input: tt.containerInput, - Cmd: tt.cmd, - VolumeMounts: tt.volumeMounts, + Input: tt.containerInput, + Cmd: tt.cmd, + Binds: tt.volumeMounts, }) assert.Equal(t, tt.expectedErr, actualErr) - actualRes, actualErr := cnt.GetContainerLogs() + actualRes, actualErr := cnt.GetContainerLogs(GetLogOptions{Stdout: true, Follow: true}) require.NoError(t, actualErr) var actualResBytes []byte diff --git a/testutil/container/container.go b/testutil/container/container.go index dd01b8077..06ade22b4 100755 --- a/testutil/container/container.go +++ b/testutil/container/container.go @@ -42,7 +42,7 @@ func (mc *MockContainer) RunCommand(container.RunCommandOptions) error { } // GetContainerLogs Container interface implementation for unit test purposes -func (mc *MockContainer) GetContainerLogs() (io.ReadCloser, error) { +func (mc *MockContainer) GetContainerLogs(container.GetLogOptions) (io.ReadCloser, error) { return mc.MockGetContainerLogs() }