Skip to content

Instantly share code, notes, and snippets.

@erikh
Created October 6, 2016 06:17
Show Gist options
  • Save erikh/2ae5fd00a364242deadc7f8105f8ee17 to your computer and use it in GitHub Desktop.
Save erikh/2ae5fd00a364242deadc7f8105f8ee17 to your computer and use it in GitHub Desktop.
package main
import (
"context"
"fmt"
"io"
"os"
"github.com/docker/engine-api/client"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/container"
mruby "github.com/mitchellh/go-mruby"
)
type Definition struct {
Func Func
ArgSpec mruby.ArgSpec
}
func from(b *Builder, m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) {
args := m.GetArgs()
resp, err := b.client.ContainerCreate(
context.Background(),
&container.Config{Image: args[0].String(), Tty: true},
nil,
nil,
"",
)
if err != nil {
return mruby.String(err.Error()), nil
}
b.id = resp.ID
if err := b.client.ContainerStart(context.Background(), b.id, types.ContainerStartOptions{}); err != nil {
return mruby.String(err.Error()), nil
}
return mruby.String(fmt.Sprintf("Response: %v", resp.ID)), nil
}
func run(b *Builder, m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) {
if b.id == "" {
return mruby.String("`from` must be the first docker command`"), nil
}
stringArgs := []string{"/bin/sh", "-c"}
for _, arg := range m.GetArgs() {
stringArgs = append(stringArgs, arg.String())
}
cecresp, err := b.client.ContainerExecCreate(context.Background(), b.id, types.ExecConfig{
Tty: true,
AttachStdout: true,
AttachStderr: true,
Cmd: stringArgs,
})
if err != nil {
return mruby.String(fmt.Sprintf("Error executing: %v", err)), nil
}
cearesp, err := b.client.ContainerExecAttach(context.Background(), cecresp.ID, types.ExecConfig{Tty: true, AttachStdout: true, AttachStderr: true})
if err != nil {
return mruby.String(fmt.Sprintf("Error attaching to execution context %q: %v", cecresp.ID, err)), nil
}
err = b.client.ContainerExecStart(context.Background(), cecresp.ID, types.ExecStartCheck{})
if err != nil {
return mruby.String(fmt.Sprintf("Error attaching to execution context %q: %v", cecresp.ID, err)), nil
}
_, err = io.Copy(os.Stdout, cearesp.Reader)
if err != nil && err != io.EOF {
return mruby.String(err.Error()), nil
}
return nil, nil
}
var jumpTable = map[string]Definition{
"from": {from, mruby.ArgsReq(1)},
"run": {run, mruby.ArgsAny()},
}
type Func func(b *Builder, m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value)
type Builder struct {
imageID string
id string
mrb *mruby.Mrb
client *client.Client
config *container.Config
}
func NewBuilder() (*Builder, error) {
client, err := client.NewEnvClient()
if err != nil {
return nil, err
}
return &Builder{mrb: mruby.NewMrb(), client: client}, nil
}
func (b *Builder) AddFunc(name string, fn Func, args mruby.ArgSpec) {
builderFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) {
val1, val2 := fn(b, m, self)
if b.id != "" {
resp, err := b.client.ContainerCommit(context.Background(), b.id, types.ContainerCommitOptions{Config: b.config})
if err != nil {
return mruby.String(fmt.Sprintf("Error during commit: %v", err)), nil
}
if b.imageID != "" {
_, err := b.client.ImageRemove(context.Background(), b.imageID, types.ImageRemoveOptions{})
if err != nil {
fmt.Fprintf(os.Stderr, "Error removing parent image: %v", err)
}
}
b.imageID = resp.ID
fmt.Println("+++ Commit", b.imageID)
}
return val1, val2
}
b.mrb.TopSelf().SingletonClass().DefineMethod(name, builderFunc, args)
}
func (b *Builder) Run(script string) (*mruby.MrbValue, error) {
return b.mrb.LoadString(script)
}
func (b *Builder) Close() error {
b.mrb.Close()
return nil
}
func main() {
builder, err := NewBuilder()
if err != nil {
panic(err)
}
defer builder.Close()
for name, def := range jumpTable {
builder.AddFunc(name, def.Func, def.ArgSpec)
}
// Let's call it and inspect the result
result, err := builder.Run(`
from "debian"
3.times do
run "ls"
end
`)
if err != nil {
panic(err.Error())
}
// This will output "Result: 42"
fmt.Printf("Result: %s\n", result.String())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment