Skip to content

Commit

Permalink
Merge pull request #14 from synalice/dev/sdtin
Browse files Browse the repository at this point in the history
Merge dev/sdtin
  • Loading branch information
synalice authored Jun 13, 2022
2 parents 447063a + 55c1483 commit a0c9a25
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ Build, run & test untrusted code in docker containers using Go
- [x] Volume Creation
- [x] Container limits
- [x] Copying files to container
- [ ] Writing into container's stdin
- [x] Writing into container's stdin
- [ ] Scoring system
4 changes: 2 additions & 2 deletions docker/config/create_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func NewConfigBuilder(controller *controller.Controller) *Builder {
//AttachStdin: true,
//AttachStdout: true,
//AttachStderr: true,
Tty: true,
//OpenStdin: true,
Tty: true,
OpenStdin: true,
//StdinOnce: false,
},
HostConfig: container.HostConfig{},
Expand Down
31 changes: 31 additions & 0 deletions docker/container/attach.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package container

import (
"context"
"fmt"

"github.com/docker/docker/api/types"

"github.com/synalice/gobox/docker/controller"
)

// attach is basically an equivalent of `$ docker attach <container>`
// It returns a connection to a container, allowing you to read from stdout and
// write into stdin.
func attach(controller *controller.Controller, containerID string) (types.HijackedResponse, error) {
hijackedResponse, err := controller.Cli.ContainerAttach(
context.Background(),
containerID,
types.ContainerAttachOptions{
Stream: true,
Stdin: true,
Stdout: true,
Stderr: true,
},
)
if err != nil {
return hijackedResponse, fmt.Errorf("error attaching to a container: %w", err)
}

return hijackedResponse, nil
}
13 changes: 11 additions & 2 deletions docker/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ package container

import (
"time"

"github.com/docker/docker/api/types"
)

// Container struct holds information about newly created container
type Container struct {
ID string // ID of the container
TimeLimit time.Duration // Maximum allowed time for the container to run
// ID of the container
ID string

// Maximum allowed time for the container to run
TimeLimit time.Duration

// A connection to a container that allows you to read from stdout and
// write into stdin.
Connection types.HijackedResponse
}
9 changes: 6 additions & 3 deletions docker/container/container_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ func TestContainerLifecycle(t *testing.T) {

myFile := file.File{
Name: "main.py",
Body: "print(\"Hello, World!\")",
Body: `
user = input("Enter your name: ")
print(f"Hello, {user}!")
print("Have a great day!")`,
}

mount1, err := mount.NewMount(ctrl, "", "/theFolder1")
Expand Down Expand Up @@ -53,7 +56,7 @@ func TestContainerLifecycle(t *testing.T) {
t.Errorf("%v", err)
}

err = container.Start(ctrl, builtContainer.ID)
err = container.Start(ctrl, builtContainer, "John")
if err != nil {
t.Errorf("%v", err)
}
Expand All @@ -65,7 +68,7 @@ func TestContainerLifecycle(t *testing.T) {
t.Errorf("%v", err)
}

logs, err := container.GetLogs(ctrl, builtContainer.ID)
logs, err := container.GetLogs(ctrl, builtContainer)
if err != nil {
t.Errorf("%v", err)
}
Expand Down
4 changes: 2 additions & 2 deletions docker/container/container_load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestOnLoad(t *testing.T) {
}

log.Println("Starting container " + strconv.Itoa(iter))
err = container.Start(ctrl, builtContainer.ID)
err = container.Start(ctrl, builtContainer, "")
if err != nil {
t.Errorf("%v", err)
}
Expand All @@ -69,7 +69,7 @@ func TestOnLoad(t *testing.T) {
t.Errorf("%v", err)
}

logs, err := container.GetLogs(ctrl, builtContainer.ID)
logs, err := container.GetLogs(ctrl, builtContainer)
if err != nil {
t.Errorf("%v", err)
}
Expand Down
5 changes: 5 additions & 0 deletions docker/container/create_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ func (b *Builder) Build() (*Container, error) {

b.setTimeLimit()

b.container.Connection, err = attach(b.controller, b.container.ID)
if err != nil {
return nil, fmt.Errorf("couldn't create container: %w", err)
}

return b.container, nil
}

Expand Down
30 changes: 13 additions & 17 deletions docker/container/get_logs.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
package container

import (
"context"
"bytes"
"fmt"
"io"
"time"

"github.com/docker/docker/api/types"

"github.com/synalice/gobox/docker/controller"
)

// GetLogs returns logs of a specific container
func GetLogs(controller *controller.Controller, containerID string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
func GetLogs(controller *controller.Controller, container *Container) (string, error) {
// GetLogs doesn't really need a controller, but I think it's better to
// still require one to make the user API more unified.
//
// This line is given here only so that the compiler doesn't yell at
// you because of an unused parameter.
_ = controller

r := container.Connection.Reader
var buf bytes.Buffer

reader, err := controller.Cli.ContainerLogs(ctx, containerID, types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
})
_, err := io.Copy(&buf, r)
if err != nil {
return "", fmt.Errorf("error getting contaner's logs: %w", err)
}

buffer, err := io.ReadAll(reader)
if err != nil && err != io.EOF {
return "", fmt.Errorf("error while executing io.ReadAll(): %w", err)
}

return string(buffer), nil
return buf.String(), nil
}
45 changes: 43 additions & 2 deletions docker/container/start_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,52 @@ import (
)

// Start starts a container
func Start(controller *controller.Controller, containerID string) error {
err := controller.Cli.ContainerStart(context.Background(), containerID, types.ContainerStartOptions{})
func Start(controller *controller.Controller, container *Container, stdin string) error {
if stdin != "" {
err := startWithStdin(controller, container, stdin)
if err != nil {
return fmt.Errorf("error starting container: %w", err)
}

return nil
}

err := startWithoutStdin(controller, container)
if err != nil {
return fmt.Errorf("error starting container: %w", err)
}

return nil
}

func startWithStdin(controller *controller.Controller, container *Container, stdin string) error {
err := controller.Cli.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{})
if err != nil {
return err
}

err = writeToStdin(container.Connection, stdin)
if err != nil {
return err
}

return nil
}

func startWithoutStdin(controller *controller.Controller, container *Container) error {
err := controller.Cli.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{})
if err != nil {
return err
}

return nil
}

func writeToStdin(connection types.HijackedResponse, input string) error {
_, err := connection.Conn.Write([]byte(input + "\n"))
if err != nil {
return fmt.Errorf("error writing into stdin: %w", err)
}

return nil
}
12 changes: 8 additions & 4 deletions examples/container_creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ func main() {
// run.
myFile := file.File{
Name: "main.py",
Body: "print(\"Hello, World!\")",
Body: `
user = input("Enter your name: ")
print(f"Hello, {user}!")
print("Have a great day!")`,
}

// Now, let's create a mount. It specifies where do we want to create
Expand Down Expand Up @@ -100,8 +103,9 @@ func main() {
log.Println(err)
}

// Let's start this container.
err = container.Start(ctrl, builtContainer.ID)
// Let's start this container. Leave `stdin` argument empty if you don't
// want to write anything in it.
err = container.Start(ctrl, builtContainer, "John")
if err != nil {
log.Println(err)
}
Expand All @@ -120,7 +124,7 @@ func main() {
}

// Let's get the logs from the container's stdout.
logs, err := container.GetLogs(ctrl, builtContainer.ID)
logs, err := container.GetLogs(ctrl, builtContainer)
if err != nil {
log.Println(err)
}
Expand Down

0 comments on commit a0c9a25

Please sign in to comment.