Minimal, unintrusive implementation of a cleaner Job API.
* Implement a new package: engine. It exposes a useful but minimalist job API. * Refactor main() to instanciate an Engine instead of a Server directly. * Refactor server.go to register an engine job. This is the smallest possible refactor which can include the new Engine design into master. More gradual refactoring will follow. Upstream-commit: 0d1a825137448e2f41e5aaa5ecae8094c8ab6817 Component: engine
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
@@ -0,0 +1,62 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
|
||||
type Handler func(*Job) string
|
||||
|
||||
var globalHandlers map[string]Handler
|
||||
|
||||
func Register(name string, handler Handler) error {
|
||||
if globalHandlers == nil {
|
||||
globalHandlers = make(map[string]Handler)
|
||||
}
|
||||
globalHandlers[name] = handler
|
||||
return nil
|
||||
}
|
||||
|
||||
// The Engine is the core of Docker.
|
||||
// It acts as a store for *containers*, and allows manipulation of these
|
||||
// containers by executing *jobs*.
|
||||
type Engine struct {
|
||||
root string
|
||||
handlers map[string]Handler
|
||||
}
|
||||
|
||||
// New initializes a new engine managing the directory specified at `root`.
|
||||
// `root` is used to store containers and any other state private to the engine.
|
||||
// Changing the contents of the root without executing a job will cause unspecified
|
||||
// behavior.
|
||||
func New(root string) (*Engine, error) {
|
||||
if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
eng := &Engine{
|
||||
root: root,
|
||||
handlers: globalHandlers,
|
||||
}
|
||||
return eng, nil
|
||||
}
|
||||
|
||||
// Job creates a new job which can later be executed.
|
||||
// This function mimics `Command` from the standard os/exec package.
|
||||
func (eng *Engine) Job(name string, args ...string) (*Job, error) {
|
||||
handler, exists := eng.handlers[name]
|
||||
if !exists || handler == nil {
|
||||
return nil, fmt.Errorf("Undefined command; %s", name)
|
||||
}
|
||||
job := &Job{
|
||||
eng: eng,
|
||||
Name: name,
|
||||
Args: args,
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
handler: handler,
|
||||
}
|
||||
return job, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// A job is the fundamental unit of work in the docker engine.
|
||||
// Everything docker can do should eventually be exposed as a job.
|
||||
// For example: execute a process in a container, create a new container,
|
||||
// download an archive from the internet, serve the http api, etc.
|
||||
//
|
||||
// The job API is designed after unix processes: a job has a name, arguments,
|
||||
// environment variables, standard streams for input, output and error, and
|
||||
// an exit status which can indicate success (0) or error (anything else).
|
||||
//
|
||||
// One slight variation is that jobs report their status as a string. The
|
||||
// string "0" indicates success, and any other strings indicates an error.
|
||||
// This allows for richer error reporting.
|
||||
//
|
||||
type Job struct {
|
||||
eng *Engine
|
||||
Name string
|
||||
Args []string
|
||||
env []string
|
||||
Stdin io.ReadCloser
|
||||
Stdout io.WriteCloser
|
||||
Stderr io.WriteCloser
|
||||
handler func(*Job) string
|
||||
status string
|
||||
}
|
||||
|
||||
// Run executes the job and blocks until the job completes.
|
||||
// If the job returns a failure status, an error is returned
|
||||
// which includes the status.
|
||||
func (job *Job) Run() error {
|
||||
if job.handler == nil {
|
||||
return fmt.Errorf("Undefined job handler")
|
||||
}
|
||||
status := job.handler(job)
|
||||
job.status = status
|
||||
if status != "0" {
|
||||
return fmt.Errorf("Job failed with status %s", status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (job *Job) Getenv(key string) (value string) {
|
||||
for _, kv := range job.env {
|
||||
if strings.Index(kv, "=") == -1 {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if parts[0] != key {
|
||||
continue
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
value = ""
|
||||
} else {
|
||||
value = parts[1]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (job *Job) GetenvBool(key string) (value bool) {
|
||||
s := strings.ToLower(strings.Trim(job.Getenv(key), " \t"))
|
||||
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (job *Job) SetenvBool(key string, value bool) {
|
||||
if value {
|
||||
job.Setenv(key, "1")
|
||||
} else {
|
||||
job.Setenv(key, "0")
|
||||
}
|
||||
}
|
||||
|
||||
func (job *Job) GetenvList(key string) []string {
|
||||
sval := job.Getenv(key)
|
||||
l := make([]string, 0, 1)
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (job *Job) SetenvList(key string, value []string) error {
|
||||
sval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
job.Setenv(key, string(sval))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (job *Job) Setenv(key, value string) {
|
||||
job.env = append(job.env, key + "=" + value)
|
||||
}
|
||||
Reference in New Issue
Block a user