2013-10-21 12:04:42 -04:00
package engine
import (
2014-02-15 18:05:28 -05:00
"bufio"
2013-10-21 12:04:42 -04:00
"fmt"
2013-11-07 15:19:24 -05:00
"github.com/dotcloud/docker/utils"
2013-12-05 19:22:49 -05:00
"io"
2013-10-23 02:53:40 -04:00
"log"
2013-11-07 15:19:24 -05:00
"os"
2013-10-23 02:53:40 -04:00
"runtime"
2014-02-15 18:06:21 -05:00
"sort"
2013-10-27 03:01:15 -04:00
"strings"
2013-10-21 12:04:42 -04:00
)
2013-11-20 02:37:03 -05:00
type Handler func ( * Job ) Status
2013-10-21 12:04:42 -04:00
var globalHandlers map [ string ] Handler
2013-10-26 22:24:01 -04:00
func init ( ) {
globalHandlers = make ( map [ string ] Handler )
}
2013-10-21 12:04:42 -04:00
func Register ( name string , handler Handler ) error {
2013-10-27 02:54:51 -04:00
_ , exists := globalHandlers [ name ]
if exists {
return fmt . Errorf ( "Can't overwrite global handler for command %s" , name )
2013-10-21 12:04:42 -04:00
}
globalHandlers [ name ] = handler
return nil
}
2014-02-24 15:26:56 -05:00
func unregister ( name string ) {
delete ( globalHandlers , name )
}
2013-10-21 12:04:42 -04:00
// 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 {
2013-11-13 14:25:55 -05:00
root string
handlers map [ string ] Handler
hack Hack // data for temporary hackery (see hack.go)
id string
2013-12-05 19:22:49 -05:00
Stdout io . Writer
Stderr io . Writer
Stdin io . Reader
2013-10-27 03:01:15 -04:00
}
func ( eng * Engine ) Root ( ) string {
return eng . root
2013-10-21 12:04:42 -04:00
}
2013-10-27 02:54:51 -04:00
func ( eng * Engine ) Register ( name string , handler Handler ) error {
_ , exists := eng . handlers [ name ]
if exists {
return fmt . Errorf ( "Can't overwrite handler for command %s" , name )
}
eng . handlers [ name ] = handler
return nil
2013-10-21 12:04:42 -04:00
}
// 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 ) {
2013-10-23 02:53:40 -04:00
// Check for unsupported architectures
if runtime . GOARCH != "amd64" {
return nil , fmt . Errorf ( "The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting." , runtime . GOARCH )
}
// Check for unsupported kernel versions
// FIXME: it would be cleaner to not test for specific versions, but rather
// test for specific functionalities.
// Unfortunately we can't test for the feature "does not cause a kernel panic"
// without actually causing a kernel panic, so we need this workaround until
// the circumstances of pre-3.8 crashes are clearer.
// For details see http://github.com/dotcloud/docker/issues/407
if k , err := utils . GetKernelVersion ( ) ; err != nil {
log . Printf ( "WARNING: %s\n" , err )
} else {
if utils . CompareKernelVersion ( k , & utils . KernelVersionInfo { Kernel : 3 , Major : 8 , Minor : 0 } ) < 0 {
2013-12-02 12:43:56 -05:00
if os . Getenv ( "DOCKER_NOWARN_KERNEL_VERSION" ) == "" {
log . Printf ( "WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0." , k . String ( ) )
}
2013-10-23 02:53:40 -04:00
}
}
2013-12-19 01:03:34 -05:00
2013-10-21 12:04:42 -04:00
if err := os . MkdirAll ( root , 0700 ) ; err != nil && ! os . IsExist ( err ) {
return nil , err
}
2013-12-19 01:03:34 -05:00
2013-10-21 12:04:42 -04:00
eng := & Engine {
2013-11-13 14:25:55 -05:00
root : root ,
handlers : make ( map [ string ] Handler ) ,
id : utils . RandomString ( ) ,
2013-12-05 19:22:49 -05:00
Stdout : os . Stdout ,
Stderr : os . Stderr ,
Stdin : os . Stdin ,
2013-10-27 02:54:51 -04:00
}
2014-02-15 18:06:21 -05:00
eng . Register ( "commands" , func ( job * Job ) Status {
for _ , name := range eng . commands ( ) {
job . Printf ( "%s\n" , name )
}
return StatusOK
} )
2013-10-27 02:54:51 -04:00
// Copy existing global handlers
for k , v := range globalHandlers {
eng . handlers [ k ] = v
2013-10-21 12:04:42 -04:00
}
return eng , nil
}
2013-10-27 03:01:15 -04:00
func ( eng * Engine ) String ( ) string {
return fmt . Sprintf ( "%s|%s" , eng . Root ( ) , eng . id [ : 8 ] )
}
2014-02-15 18:06:21 -05:00
// Commands returns a list of all currently registered commands,
// sorted alphabetically.
func ( eng * Engine ) commands ( ) [ ] string {
names := make ( [ ] string , 0 , len ( eng . handlers ) )
for name := range eng . handlers {
names = append ( names , name )
}
sort . Strings ( names )
return names
}
2013-10-21 12:04:42 -04:00
// Job creates a new job which can later be executed.
// This function mimics `Command` from the standard os/exec package.
2013-10-26 20:49:16 -04:00
func ( eng * Engine ) Job ( name string , args ... string ) * Job {
2013-10-21 12:04:42 -04:00
job := & Job {
2013-11-13 14:25:55 -05:00
Eng : eng ,
Name : name ,
Args : args ,
2013-11-20 02:37:03 -05:00
Stdin : NewInput ( ) ,
Stdout : NewOutput ( ) ,
Stderr : NewOutput ( ) ,
2013-12-08 01:06:05 -05:00
env : & Env { } ,
2013-10-21 12:04:42 -04:00
}
2013-12-05 19:22:49 -05:00
job . Stderr . Add ( utils . NopWriteCloser ( eng . Stderr ) )
2013-10-26 20:49:16 -04:00
handler , exists := eng . handlers [ name ]
if exists {
job . handler = handler
}
return job
2013-10-21 12:04:42 -04:00
}
2014-02-15 18:05:28 -05:00
// ParseJob creates a new job from a text description using a shell-like syntax.
//
// The following syntax is used to parse `input`:
//
// * Words are separated using standard whitespaces as separators.
// * Quotes and backslashes are not interpreted.
// * Words of the form 'KEY=[VALUE]' are added to the job environment.
// * All other words are added to the job arguments.
//
// For example:
//
// job, _ := eng.ParseJob("VERBOSE=1 echo hello TEST=true world")
//
// The resulting job will have:
// job.Args={"echo", "hello", "world"}
// job.Env={"VERBOSE":"1", "TEST":"true"}
//
func ( eng * Engine ) ParseJob ( input string ) ( * Job , error ) {
// FIXME: use a full-featured command parser
scanner := bufio . NewScanner ( strings . NewReader ( input ) )
scanner . Split ( bufio . ScanWords )
var (
cmd [ ] string
env Env
)
for scanner . Scan ( ) {
word := scanner . Text ( )
kv := strings . SplitN ( word , "=" , 2 )
if len ( kv ) == 2 {
env . Set ( kv [ 0 ] , kv [ 1 ] )
} else {
cmd = append ( cmd , word )
}
}
if len ( cmd ) == 0 {
return nil , fmt . Errorf ( "empty command: '%s'" , input )
}
job := eng . Job ( cmd [ 0 ] , cmd [ 1 : ] ... )
job . Env ( ) . Init ( & env )
return job , nil
}
2013-10-27 03:01:15 -04:00
func ( eng * Engine ) Logf ( format string , args ... interface { } ) ( n int , err error ) {
2014-01-22 16:35:35 -05:00
if os . Getenv ( "TEST" ) == "" {
prefixedFormat := fmt . Sprintf ( "[%s] %s\n" , eng , strings . TrimRight ( format , "\n" ) )
return fmt . Fprintf ( eng . Stderr , prefixedFormat , args ... )
}
return 0 , nil
2013-10-27 03:01:15 -04:00
}