diff --git a/components/engine/config.go b/components/engine/config.go index 8f2d22c255..40c47e692c 100644 --- a/components/engine/config.go +++ b/components/engine/config.go @@ -2,11 +2,13 @@ package docker import ( "net" + "github.com/dotcloud/docker/engine" ) +// FIXME: separate runtime configuration from http api configuration type DaemonConfig struct { Pidfile string - GraphPath string + Root string ProtoAddresses []string AutoRestart bool EnableCors bool @@ -16,3 +18,26 @@ type DaemonConfig struct { DefaultIp net.IP InterContainerCommunication bool } + +// ConfigFromJob creates and returns a new DaemonConfig object +// by parsing the contents of a job's environment. +func ConfigFromJob(job *engine.Job) *DaemonConfig { + var config DaemonConfig + config.Pidfile = job.Getenv("Pidfile") + config.Root = job.Getenv("Root") + config.AutoRestart = job.GetenvBool("AutoRestart") + config.EnableCors = job.GetenvBool("EnableCors") + if dns := job.Getenv("Dns"); dns != "" { + config.Dns = []string{dns} + } + config.EnableIptables = job.GetenvBool("EnableIptables") + if br := job.Getenv("BridgeIface"); br != "" { + config.BridgeIface = br + } else { + config.BridgeIface = DefaultNetworkBridge + } + config.ProtoAddresses = job.GetenvList("ProtoAddresses") + config.DefaultIp = net.ParseIP(job.Getenv("DefaultIp")) + config.InterContainerCommunication = job.GetenvBool("InterContainerCommunication") + return &config +} diff --git a/components/engine/docker/docker.go b/components/engine/docker/docker.go index d1c59894d6..c500633a71 100644 --- a/components/engine/docker/docker.go +++ b/components/engine/docker/docker.go @@ -6,14 +6,10 @@ import ( "github.com/dotcloud/docker" "github.com/dotcloud/docker/sysinit" "github.com/dotcloud/docker/utils" - "io/ioutil" + "github.com/dotcloud/docker/engine" "log" - "net" "os" - "os/signal" - "strconv" "strings" - "syscall" ) var ( @@ -34,7 +30,7 @@ func main() { flAutoRestart := flag.Bool("r", true, "Restart previously running containers") bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking") pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID") - flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.") + flRoot := flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime.") flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.") flDns := flag.String("dns", "", "Set custom dns servers") flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)} @@ -61,10 +57,6 @@ func main() { } } - bridge := docker.DefaultNetworkBridge - if *bridgeName != "" { - bridge = *bridgeName - } if *flDebug { os.Setenv("DEBUG", "1") } @@ -75,26 +67,22 @@ func main() { flag.Usage() return } - var dns []string - if *flDns != "" { - dns = []string{*flDns} + eng, err := engine.New(*flRoot) + if err != nil { + log.Fatal(err) } - - ip := net.ParseIP(*flDefaultIp) - - config := &docker.DaemonConfig{ - Pidfile: *pidfile, - GraphPath: *flGraphPath, - AutoRestart: *flAutoRestart, - EnableCors: *flEnableCors, - Dns: dns, - EnableIptables: *flEnableIptables, - BridgeIface: bridge, - ProtoAddresses: flHosts, - DefaultIp: ip, - InterContainerCommunication: *flInterContainerComm, - } - if err := daemon(config); err != nil { + job := eng.Job("serveapi") + job.Setenv("Pidfile", *pidfile) + job.Setenv("Root", *flRoot) + job.SetenvBool("AutoRestart", *flAutoRestart) + job.SetenvBool("EnableCors", *flEnableCors) + job.Setenv("Dns", *flDns) + job.SetenvBool("EnableIptables", *flEnableIptables) + job.Setenv("BridgeIface", *bridgeName) + job.SetenvList("ProtoAddresses", flHosts) + job.Setenv("DefaultIp", *flDefaultIp) + job.SetenvBool("InterContainerCommunication", *flInterContainerComm) + if err := job.Run(); err != nil { log.Fatal(err) } } else { @@ -114,79 +102,3 @@ func main() { func showVersion() { fmt.Printf("Docker version %s, build %s\n", VERSION, GITCOMMIT) } - -func createPidFile(pidfile string) error { - if pidString, err := ioutil.ReadFile(pidfile); err == nil { - pid, err := strconv.Atoi(string(pidString)) - if err == nil { - if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil { - return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile) - } - } - } - - file, err := os.Create(pidfile) - if err != nil { - return err - } - - defer file.Close() - - _, err = fmt.Fprintf(file, "%d", os.Getpid()) - return err -} - -func removePidFile(pidfile string) { - if err := os.Remove(pidfile); err != nil { - log.Printf("Error removing %s: %s", pidfile, err) - } -} - -func daemon(config *docker.DaemonConfig) error { - if err := createPidFile(config.Pidfile); err != nil { - log.Fatal(err) - } - defer removePidFile(config.Pidfile) - - server, err := docker.NewServer(config) - if err != nil { - return err - } - defer server.Close() - - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) - go func() { - sig := <-c - log.Printf("Received signal '%v', exiting\n", sig) - server.Close() - removePidFile(config.Pidfile) - os.Exit(0) - }() - - chErrors := make(chan error, len(config.ProtoAddresses)) - for _, protoAddr := range config.ProtoAddresses { - protoAddrParts := strings.SplitN(protoAddr, "://", 2) - if protoAddrParts[0] == "unix" { - syscall.Unlink(protoAddrParts[1]) - } else if protoAddrParts[0] == "tcp" { - if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") { - log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") - } - } else { - server.Close() - removePidFile(config.Pidfile) - log.Fatal("Invalid protocol format.") - } - go func() { - chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true) - }() - } - for i := 0; i < len(config.ProtoAddresses); i += 1 { - err := <-chErrors - if err != nil { - return err - } - } - return nil -} diff --git a/components/engine/engine/MAINTAINERS b/components/engine/engine/MAINTAINERS new file mode 100644 index 0000000000..e1c6f2ccfc --- /dev/null +++ b/components/engine/engine/MAINTAINERS @@ -0,0 +1 @@ +Solomon Hykes diff --git a/components/engine/engine/engine.go b/components/engine/engine/engine.go new file mode 100644 index 0000000000..8d67242ca2 --- /dev/null +++ b/components/engine/engine/engine.go @@ -0,0 +1,82 @@ +package engine + +import ( + "fmt" + "os" + "log" + "runtime" + "github.com/dotcloud/docker/utils" +) + + +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) { + // 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 { + 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()) + } + } + 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 { + job := &Job{ + eng: eng, + Name: name, + Args: args, + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + handler, exists := eng.handlers[name] + if exists { + job.handler = handler + } + return job +} + diff --git a/components/engine/engine/env_test.go b/components/engine/engine/env_test.go new file mode 100644 index 0000000000..3acbddad77 --- /dev/null +++ b/components/engine/engine/env_test.go @@ -0,0 +1,29 @@ +package engine + +import ( + "testing" +) + +func TestNewJob(t *testing.T) { + job := mkJob(t, "dummy", "--level=awesome") + if job.Name != "dummy" { + t.Fatalf("Wrong job name: %s", job.Name) + } + if len(job.Args) != 1 { + t.Fatalf("Wrong number of job arguments: %d", len(job.Args)) + } + if job.Args[0] != "--level=awesome" { + t.Fatalf("Wrong job arguments: %s", job.Args[0]) + } +} + +func TestSetenv(t *testing.T) { + job := mkJob(t, "dummy") + job.Setenv("foo", "bar") + if val := job.Getenv("foo"); val != "bar" { + t.Fatalf("Getenv returns incorrect value: %s", val) + } + if val := job.Getenv("nonexistent"); val != "" { + t.Fatalf("Getenv returns incorrect value: %s", val) + } +} diff --git a/components/engine/engine/init_test.go b/components/engine/engine/init_test.go new file mode 100644 index 0000000000..5c03ded87d --- /dev/null +++ b/components/engine/engine/init_test.go @@ -0,0 +1,42 @@ +package engine + +import ( + "testing" + "runtime" + "strings" + "fmt" + "io/ioutil" + "github.com/dotcloud/docker/utils" +) + +var globalTestID string + +func init() { + Register("dummy", func(job *Job) string { return ""; }) +} + +func mkEngine(t *testing.T) *Engine { + // Use the caller function name as a prefix. + // This helps trace temp directories back to their test. + pc, _, _, _ := runtime.Caller(1) + callerLongName := runtime.FuncForPC(pc).Name() + parts := strings.Split(callerLongName, ".") + callerShortName := parts[len(parts)-1] + if globalTestID == "" { + globalTestID = utils.RandomString()[:4] + } + prefix := fmt.Sprintf("docker-test%s-%s-", globalTestID, callerShortName) + root, err := ioutil.TempDir("", prefix) + if err != nil { + t.Fatal(err) + } + eng, err := New(root) + if err != nil { + t.Fatal(err) + } + return eng +} + +func mkJob(t *testing.T, name string, args ...string) *Job { + return mkEngine(t).Job(name, args...) +} diff --git a/components/engine/engine/job.go b/components/engine/engine/job.go new file mode 100644 index 0000000000..0bde2a0beb --- /dev/null +++ b/components/engine/engine/job.go @@ -0,0 +1,113 @@ +package engine + +import ( + "io" + "strings" + "fmt" + "encoding/json" + "github.com/dotcloud/docker/utils" +) + +// 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 { + randId := utils.RandomString()[:4] + fmt.Printf("Job #%s: %s\n", randId, job) + defer fmt.Printf("Job #%s: %s = '%s'", randId, job, job.status) + if job.handler == nil { + job.status = "command not found" + } else { + job.status = job.handler(job) + } + if job.status != "0" { + return fmt.Errorf("%s: %s", job.Name, job.status) + } + return nil +} + +// String returns a human-readable description of `job` +func (job *Job) String() string { + return strings.Join(append([]string{job.Name}, job.Args...), " ") +} + +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) +} diff --git a/components/engine/netlink/netlink_darwin.go b/components/engine/netlink/netlink_darwin.go new file mode 100644 index 0000000000..becfb87759 --- /dev/null +++ b/components/engine/netlink/netlink_darwin.go @@ -0,0 +1,29 @@ +package netlink + +import ( + "fmt" + "net" +) + +func NetworkGetRoutes() ([]*net.IPNet, error) { + return nil, fmt.Errorf("Not implemented") +} + + +func NetworkLinkAdd(name string, linkType string) error { + return fmt.Errorf("Not implemented") +} + +func NetworkLinkUp(iface *net.Interface) error { + return fmt.Errorf("Not implemented") +} + + +func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { + return fmt.Errorf("Not implemented") +} + +func AddDefaultGw(ip net.IP) error { + return fmt.Errorf("Not implemented") + +} diff --git a/components/engine/netlink/netlink.go b/components/engine/netlink/netlink_linux.go similarity index 100% rename from components/engine/netlink/netlink.go rename to components/engine/netlink/netlink_linux.go diff --git a/components/engine/runtime.go b/components/engine/runtime.go index 10ab7a249f..6d5139390a 100644 --- a/components/engine/runtime.go +++ b/components/engine/runtime.go @@ -563,34 +563,26 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) { if err != nil { return nil, err } - - 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 { - 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()) - } - } runtime.UpdateCapabilities(false) return runtime, nil } func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { - runtimeRepo := path.Join(config.GraphPath, "containers") + runtimeRepo := path.Join(config.Root, "containers") if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) { return nil, err } - g, err := NewGraph(path.Join(config.GraphPath, "graph")) + g, err := NewGraph(path.Join(config.Root, "graph")) if err != nil { return nil, err } - volumes, err := NewGraph(path.Join(config.GraphPath, "volumes")) + volumes, err := NewGraph(path.Join(config.Root, "volumes")) if err != nil { return nil, err } - repositories, err := NewTagStore(path.Join(config.GraphPath, "repositories"), g) + repositories, err := NewTagStore(path.Join(config.Root, "repositories"), g) if err != nil { return nil, fmt.Errorf("Couldn't create Tag store: %s", err) } @@ -602,7 +594,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { return nil, err } - gographPath := path.Join(config.GraphPath, "linkgraph.db") + gographPath := path.Join(config.Root, "linkgraph.db") initDatabase := false if _, err := os.Stat(gographPath); err != nil { if os.IsNotExist(err) { diff --git a/components/engine/runtime_test.go b/components/engine/runtime_test.go index cac04dfcc6..0c65188a38 100644 --- a/components/engine/runtime_test.go +++ b/components/engine/runtime_test.go @@ -46,8 +46,8 @@ func nuke(runtime *Runtime) error { wg.Wait() runtime.Close() - os.Remove(filepath.Join(runtime.config.GraphPath, "linkgraph.db")) - return os.RemoveAll(runtime.config.GraphPath) + os.Remove(filepath.Join(runtime.config.Root, "linkgraph.db")) + return os.RemoveAll(runtime.config.Root) } func cleanup(runtime *Runtime) error { @@ -119,7 +119,7 @@ func init() { func setupBaseImage() { config := &DaemonConfig{ - GraphPath: unitTestStoreBase, + Root: unitTestStoreBase, AutoRestart: false, BridgeIface: unitTestNetworkBridge, } diff --git a/components/engine/server.go b/components/engine/server.go index 6f28e0e023..815ed3fe7c 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -9,6 +9,7 @@ import ( "github.com/dotcloud/docker/gograph" "github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/utils" + "github.com/dotcloud/docker/engine" "io" "io/ioutil" "log" @@ -22,12 +23,76 @@ import ( "strings" "sync" "time" + "syscall" + "os/signal" ) func (srv *Server) Close() error { return srv.runtime.Close() } +func init() { + engine.Register("serveapi", JobServeApi) +} + +func JobServeApi(job *engine.Job) string { + srv, err := NewServer(ConfigFromJob(job)) + if err != nil { + return err.Error() + } + defer srv.Close() + if err := srv.Daemon(); err != nil { + return err.Error() + } + return "0" +} + +// Daemon runs the remote api server `srv` as a daemon, +// Only one api server can run at the same time - this is enforced by a pidfile. +// The signals SIGINT, SIGKILL and SIGTERM are intercepted for cleanup. +func (srv *Server) Daemon() error { + if err := utils.CreatePidFile(srv.runtime.config.Pidfile); err != nil { + log.Fatal(err) + } + defer utils.RemovePidFile(srv.runtime.config.Pidfile) + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) + go func() { + sig := <-c + log.Printf("Received signal '%v', exiting\n", sig) + utils.RemovePidFile(srv.runtime.config.Pidfile) + srv.Close() + os.Exit(0) + }() + + protoAddrs := srv.runtime.config.ProtoAddresses + chErrors := make(chan error, len(protoAddrs)) + for _, protoAddr := range protoAddrs { + protoAddrParts := strings.SplitN(protoAddr, "://", 2) + if protoAddrParts[0] == "unix" { + syscall.Unlink(protoAddrParts[1]) + } else if protoAddrParts[0] == "tcp" { + if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") { + log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") + } + } else { + return fmt.Errorf("Invalid protocol format.") + } + go func() { + chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, true) + }() + } + for i := 0; i < len(protoAddrs); i += 1 { + err := <-chErrors + if err != nil { + return err + } + } + return nil +} + + func (srv *Server) DockerVersion() APIVersion { return APIVersion{ Version: VERSION, @@ -119,7 +184,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { } func (srv *Server) ImagesSearch(term string) ([]APISearch, error) { - r, err := registry.NewRegistry(srv.runtime.config.GraphPath, nil, srv.HTTPRequestFactory(nil)) + r, err := registry.NewRegistry(srv.runtime.config.Root, nil, srv.HTTPRequestFactory(nil)) if err != nil { return nil, err } @@ -664,7 +729,7 @@ func (srv *Server) poolRemove(kind, key string) error { } func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string, parallel bool) error { - r, err := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders)) + r, err := registry.NewRegistry(srv.runtime.config.Root, authConfig, srv.HTTPRequestFactory(metaHeaders)) if err != nil { return err } @@ -873,7 +938,7 @@ func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFo out = utils.NewWriteFlusher(out) img, err := srv.runtime.graph.Get(localName) - r, err2 := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders)) + r, err2 := registry.NewRegistry(srv.runtime.config.Root, authConfig, srv.HTTPRequestFactory(metaHeaders)) if err2 != nil { return err2 } @@ -1410,9 +1475,6 @@ func (srv *Server) ContainerCopy(name string, resource string, out io.Writer) er } func NewServer(config *DaemonConfig) (*Server, error) { - if runtime.GOARCH != "amd64" { - log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH) - } runtime, err := NewRuntime(config) if err != nil { return nil, err diff --git a/components/engine/utils/daemon.go b/components/engine/utils/daemon.go new file mode 100644 index 0000000000..179ff90e10 --- /dev/null +++ b/components/engine/utils/daemon.go @@ -0,0 +1,36 @@ +package utils + +import ( + "os" + "fmt" + "io/ioutil" + "log" + "strconv" +) + +func CreatePidFile(pidfile string) error { + if pidString, err := ioutil.ReadFile(pidfile); err == nil { + pid, err := strconv.Atoi(string(pidString)) + if err == nil { + if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil { + return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile) + } + } + } + + file, err := os.Create(pidfile) + if err != nil { + return err + } + + defer file.Close() + + _, err = fmt.Fprintf(file, "%d", os.Getpid()) + return err +} + +func RemovePidFile(pidfile string) { + if err := os.Remove(pidfile); err != nil { + log.Printf("Error removing %s: %s", pidfile, err) + } +} diff --git a/components/engine/utils/random.go b/components/engine/utils/random.go new file mode 100644 index 0000000000..d5c37e44d0 --- /dev/null +++ b/components/engine/utils/random.go @@ -0,0 +1,16 @@ +package utils + +import ( + "io" + "crypto/rand" + "encoding/hex" +) + +func RandomString() string { + id := make([]byte, 32) + _, err := io.ReadFull(rand.Reader, id) + if err != nil { + panic(err) // This shouldn't happen + } + return hex.EncodeToString(id) +} diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index 439cf0a7d1..d747614910 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -67,7 +67,7 @@ func newTestRuntime(prefix string) (runtime *Runtime, err error) { } config := &DaemonConfig{ - GraphPath: root, + Root: root, AutoRestart: false, } runtime, err = NewRuntimeFromDirectory(config)