diff --git a/client/client.go b/client/client.go index ede85c56e..5a3323f8c 100644 --- a/client/client.go +++ b/client/client.go @@ -342,8 +342,9 @@ func (c *Client) fingerprint() error { // setupDrivers is used to find the available drivers func (c *Client) setupDrivers() error { var avail []string + driverCtx := driver.NewDriverContext(c.config, c.config.Node, c.logger) for name := range driver.BuiltinDrivers { - d, err := driver.NewDriver(name, c.logger) + d, err := driver.NewDriver(name, driverCtx) if err != nil { return err } diff --git a/client/driver/docker.go b/client/driver/docker.go index 3a4e5896b..e4f238d5e 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -8,6 +8,8 @@ import ( "regexp" "strings" + docker "github.com/fsouza/go-dockerclient" + "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/nomad/structs" ) @@ -18,7 +20,7 @@ var ( ) type DockerDriver struct { - logger *log.Logger + DriverContext } type dockerPID struct { @@ -34,11 +36,8 @@ type dockerHandle struct { doneCh chan struct{} } -func NewDockerDriver(logger *log.Logger) Driver { - d := &DockerDriver{ - logger: logger, - } - return d +func NewDockerDriver(ctx *DriverContext) Driver { + return &DockerDriver{*ctx} } func (d *DockerDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { @@ -111,6 +110,10 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle // nomad process is restarted. Also, you will need to parse the containerID // out of the run command output since run combines pull, create and start // into a single command. + + client, err := docker.NewClient(d.config.ReadDefault("docker.endpoint", "unix:///var/run/docker.sock")) + client.ListImages(docker.ListImagesOptions{All: false}) + startBytes, err := exec.Command("docker", "start", containerID).CombinedOutput() if err != nil { d.logger.Printf("[ERROR] driver.docker %s", strings.TrimSpace(string(startBytes))) diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 1a748843b..dbff3caa7 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -26,7 +26,7 @@ func TestDockerDriver_Handle(t *testing.T) { } func TestDockerDriver_Fingerprint(t *testing.T) { - d := NewDockerDriver(testLogger()) + d := NewDockerDriver(testDriverContext()) node := &structs.Node{ Attributes: make(map[string]string), } @@ -49,7 +49,7 @@ func TestDockerDriver_StartOpen_Wait(t *testing.T) { t.SkipNow() } ctx := NewExecContext() - d := NewDockerDriver(testLogger()) + d := NewDockerDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ @@ -80,7 +80,7 @@ func TestDockerDriver_Start_Wait(t *testing.T) { t.SkipNow() } ctx := NewExecContext() - d := NewDockerDriver(testLogger()) + d := NewDockerDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ @@ -117,7 +117,7 @@ func TestDockerDriver_Start_Kill_Wait(t *testing.T) { t.SkipNow() } ctx := NewExecContext() - d := NewDockerDriver(testLogger()) + d := NewDockerDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ diff --git a/client/driver/driver.go b/client/driver/driver.go index 6e35adcf1..63d8d827a 100644 --- a/client/driver/driver.go +++ b/client/driver/driver.go @@ -5,6 +5,7 @@ import ( "log" "sync" + "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad/structs" ) @@ -12,14 +13,15 @@ import ( // BuiltinDrivers contains the built in registered drivers // which are available for allocation handling var BuiltinDrivers = map[string]Factory{ - "exec": NewExecDriver, - "java": NewJavaDriver, - "qemu": NewQemuDriver, + "docker": NewDockerDriver, + "exec": NewExecDriver, + "java": NewJavaDriver, + "qemu": NewQemuDriver, } // NewDriver is used to instantiate and return a new driver // given the name and a logger -func NewDriver(name string, logger *log.Logger) (Driver, error) { +func NewDriver(name string, ctx *DriverContext) (Driver, error) { // Lookup the factory function factory, ok := BuiltinDrivers[name] if !ok { @@ -27,12 +29,12 @@ func NewDriver(name string, logger *log.Logger) (Driver, error) { } // Instantiate the driver - f := factory(logger) + f := factory(ctx) return f, nil } // Factory is used to instantiate a new Driver -type Factory func(*log.Logger) Driver +type Factory func(*DriverContext) Driver // Driver is used for execution of tasks. This allows Nomad // to support many pluggable implementations of task drivers. @@ -48,6 +50,27 @@ type Driver interface { Open(ctx *ExecContext, handleID string) (DriverHandle, error) } +// DriverContext is a means to inject dependencies such as loggers, configs, and +// node attributes into a Driver without having to change the Driver interface +// each time we do it. Used in conjection with Factory, above. +type DriverContext struct { + config *config.Config + logger *log.Logger + node *structs.Node +} + +// NewDriverContext initializes a new DriverContext with the specified fields. +// This enables other packages to create DriverContexts but keeps the fields +// private to the driver. If we want to change this later we can gorename all of +// the fields in DriverContext. +func NewDriverContext(config *config.Config, node *structs.Node, logger *log.Logger) *DriverContext { + return &DriverContext{ + config: config, + node: node, + logger: logger, + } +} + // DriverHandle is an opaque handle into a driver used for task // manipulation type DriverHandle interface { diff --git a/client/driver/driver_test.go b/client/driver/driver_test.go index b3c362b78..09a87b599 100644 --- a/client/driver/driver_test.go +++ b/client/driver/driver_test.go @@ -3,8 +3,20 @@ package driver import ( "log" "os" + + "github.com/hashicorp/nomad/client/config" ) func testLogger() *log.Logger { return log.New(os.Stderr, "", log.LstdFlags) } + +func testConfig() *config.Config { + return &config.Config{} +} + +func testDriverContext() *DriverContext { + cfg := testConfig() + ctx := NewDriverContext(cfg, cfg.Node, testLogger()) + return ctx +} diff --git a/client/driver/exec.go b/client/driver/exec.go index 335817f83..5e4564923 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -2,7 +2,6 @@ package driver import ( "fmt" - "log" "os" "os/exec" "strconv" @@ -19,7 +18,7 @@ import ( // fork/execs tasks. It should probably not be used for most things, // but is useful for testing purposes or for very simple tasks. type ExecDriver struct { - logger *log.Logger + DriverContext } // execHandle is returned from Start/Open as a handle to the PID @@ -30,11 +29,8 @@ type execHandle struct { } // NewExecDriver is used to create a new exec driver -func NewExecDriver(logger *log.Logger) Driver { - d := &ExecDriver{ - logger: logger, - } - return d +func NewExecDriver(ctx *DriverContext) Driver { + return &ExecDriver{*ctx} } func (d *ExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { diff --git a/client/driver/exec_test.go b/client/driver/exec_test.go index ec4f3edfb..af5533d6b 100644 --- a/client/driver/exec_test.go +++ b/client/driver/exec_test.go @@ -9,7 +9,7 @@ import ( ) func TestExecDriver_Fingerprint(t *testing.T) { - d := NewExecDriver(testLogger()) + d := NewExecDriver(testDriverContext()) node := &structs.Node{ Attributes: make(map[string]string), } @@ -27,7 +27,7 @@ func TestExecDriver_Fingerprint(t *testing.T) { func TestExecDriver_StartOpen_Wait(t *testing.T) { ctx := NewExecContext() - d := NewExecDriver(testLogger()) + d := NewExecDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ @@ -55,7 +55,7 @@ func TestExecDriver_StartOpen_Wait(t *testing.T) { func TestExecDriver_Start_Wait(t *testing.T) { ctx := NewExecContext() - d := NewExecDriver(testLogger()) + d := NewExecDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ @@ -90,7 +90,7 @@ func TestExecDriver_Start_Wait(t *testing.T) { func TestExecDriver_Start_Kill_Wait(t *testing.T) { ctx := NewExecContext() - d := NewExecDriver(testLogger()) + d := NewExecDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ diff --git a/client/driver/java.go b/client/driver/java.go index c02a13521..b5e166205 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "log" "net/http" "os" "os/exec" @@ -23,7 +22,7 @@ import ( // JavaDriver is a simple driver to execute applications packaged in Jars. // It literally just fork/execs tasks with the java command. type JavaDriver struct { - logger *log.Logger + DriverContext } // javaHandle is returned from Start/Open as a handle to the PID @@ -34,11 +33,8 @@ type javaHandle struct { } // NewJavaDriver is used to create a new exec driver -func NewJavaDriver(logger *log.Logger) Driver { - d := &JavaDriver{ - logger: logger, - } - return d +func NewJavaDriver(ctx *DriverContext) Driver { + return &JavaDriver{*ctx} } func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { diff --git a/client/driver/java_test.go b/client/driver/java_test.go index afa2bad3d..b7e5dbaa8 100644 --- a/client/driver/java_test.go +++ b/client/driver/java_test.go @@ -10,7 +10,7 @@ import ( ) func TestJavaDriver_Fingerprint(t *testing.T) { - d := NewJavaDriver(testLogger()) + d := NewJavaDriver(testDriverContext()) node := &structs.Node{ Attributes: make(map[string]string), } @@ -34,7 +34,7 @@ func TestJavaDriver_Fingerprint(t *testing.T) { func TestJavaDriver_StartOpen_Wait(t *testing.T) { ctx := NewExecContext() ctx.AllocDir = os.TempDir() - d := NewJavaDriver(testLogger()) + d := NewJavaDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ @@ -71,7 +71,7 @@ func TestJavaDriver_StartOpen_Wait(t *testing.T) { func TestJavaDriver_Start_Wait(t *testing.T) { ctx := NewExecContext() ctx.AllocDir = os.TempDir() - d := NewJavaDriver(testLogger()) + d := NewJavaDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ @@ -109,7 +109,7 @@ func TestJavaDriver_Start_Wait(t *testing.T) { func TestJavaDriver_Start_Kill_Wait(t *testing.T) { ctx := NewExecContext() ctx.AllocDir = os.TempDir() - d := NewJavaDriver(testLogger()) + d := NewJavaDriver(testDriverContext()) task := &structs.Task{ Config: map[string]string{ diff --git a/client/driver/qemu.go b/client/driver/qemu.go index e97bfa7d9..e4dff2696 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -21,14 +21,14 @@ import ( ) var ( - reQemuVersion = regexp.MustCompile("QEMU emulator version ([\\d\\.]+),.+") + reQemuVersion = regexp.MustCompile("QEMU emulator version ([\\d\\.]+).+") ) // QemuDriver is a driver for running images via Qemu // We attempt to chose sane defaults for now, with more configuration available // planned in the future type QemuDriver struct { - logger *log.Logger + DriverContext } // qemuHandle is returned from Start/Open as a handle to the PID @@ -47,11 +47,8 @@ type qemuPID struct { } // NewQemuDriver is used to create a new exec driver -func NewQemuDriver(logger *log.Logger) Driver { - d := &QemuDriver{ - logger: logger, - } - return d +func NewQemuDriver(ctx *DriverContext) Driver { + return &QemuDriver{*ctx} } func (d *QemuDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { diff --git a/client/driver/qemu_test.go b/client/driver/qemu_test.go index c60b3e2dc..23a5cf206 100644 --- a/client/driver/qemu_test.go +++ b/client/driver/qemu_test.go @@ -25,7 +25,7 @@ func TestQemuDriver_Handle(t *testing.T) { } func TestQemuDriver_Fingerprint(t *testing.T) { - d := NewQemuDriver(testLogger()) + d := NewQemuDriver(testDriverContext()) node := &structs.Node{ Attributes: make(map[string]string), } @@ -47,7 +47,7 @@ func TestQemuDriver_Fingerprint(t *testing.T) { func TestQemuDriver_Start(t *testing.T) { ctx := NewExecContext() ctx.AllocDir = os.TempDir() - d := NewQemuDriver(testLogger()) + d := NewQemuDriver(testDriverContext()) // TODO: use test server to load from a fixture task := &structs.Task{ @@ -92,7 +92,7 @@ func TestQemuDriver_Start(t *testing.T) { func TestQemuDriver_RequiresMemory(t *testing.T) { ctx := NewExecContext() ctx.AllocDir = os.TempDir() - d := NewQemuDriver(testLogger()) + d := NewQemuDriver(testDriverContext()) // TODO: use test server to load from a fixture task := &structs.Task{ diff --git a/client/task_runner.go b/client/task_runner.go index b168720d9..6bdb6e5e1 100644 --- a/client/task_runner.go +++ b/client/task_runner.go @@ -129,7 +129,8 @@ func (r *TaskRunner) setStatus(status, desc string) { // createDriver makes a driver for the task func (r *TaskRunner) createDriver() (driver.Driver, error) { - driver, err := driver.NewDriver(r.task.Driver, r.logger) + driverCtx := driver.NewDriverContext(r.config, r.config.Node, r.logger) + driver, err := driver.NewDriver(r.task.Driver, driverCtx) if err != nil { err = fmt.Errorf("failed to create driver '%s' for alloc %s: %v", r.task.Driver, r.allocID, err)