2013-10-21 12:04:42 -04:00
package engine
import (
"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-12-19 01:03:34 -05:00
"path/filepath"
2013-10-23 02:53:40 -04:00
"runtime"
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
}
// 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
// Docker makes some assumptions about the "absoluteness" of root
// ... so let's make sure it has no symlinks
if p , err := filepath . Abs ( root ) ; err != nil {
log . Fatalf ( "Unable to get absolute root (%s): %s" , root , err )
} else {
root = p
}
if p , err := filepath . EvalSymlinks ( root ) ; err != nil {
log . Fatalf ( "Unable to canonicalize root (%s): %s" , root , err )
} else {
root = p
}
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
}
// 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 ] )
}
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
}
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
}