mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Engine: 'start' starts the specified container
This commit is contained in:
parent
433c8e9c7d
commit
958b4a8757
9 changed files with 186 additions and 60 deletions
24
api.go
24
api.go
|
@ -639,26 +639,20 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
|
|||
}
|
||||
|
||||
func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var hostConfig *HostConfig
|
||||
// allow a nil body for backwards compatibility
|
||||
if r.Body != nil {
|
||||
if matchesContentType(r.Header.Get("Content-Type"), "application/json") {
|
||||
hostConfig = &HostConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(hostConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
// Register any links from the host config before starting the container
|
||||
if err := srv.RegisterLinks(name, hostConfig); err != nil {
|
||||
return err
|
||||
job := srv.Eng.Job("start", name)
|
||||
// allow a nil body for backwards compatibility
|
||||
if r.Body != nil {
|
||||
if matchesContentType(r.Header.Get("Content-Type"), "application/json") {
|
||||
if err := job.DecodeEnv(r.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := srv.ContainerStart(name, hostConfig); err != nil {
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
|
|
@ -781,11 +781,11 @@ func TestPostContainersRestart(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPostContainersStart(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
eng := NewTestEngine(t)
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
runtime := srv.runtime
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
container, _, err := runtime.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).ID,
|
||||
|
|
|
@ -13,10 +13,11 @@ type Handler func(*Job) string
|
|||
|
||||
var globalHandlers map[string]Handler
|
||||
|
||||
func init() {
|
||||
globalHandlers = make(map[string]Handler)
|
||||
}
|
||||
|
||||
func Register(name string, handler Handler) error {
|
||||
if globalHandlers == nil {
|
||||
globalHandlers = make(map[string]Handler)
|
||||
}
|
||||
globalHandlers[name] = handler
|
||||
return nil
|
||||
}
|
||||
|
@ -27,6 +28,7 @@ func Register(name string, handler Handler) error {
|
|||
type Engine struct {
|
||||
root string
|
||||
handlers map[string]Handler
|
||||
hack Hack // data for temporary hackery (see hack.go)
|
||||
}
|
||||
|
||||
// New initializes a new engine managing the directory specified at `root`.
|
||||
|
@ -66,7 +68,7 @@ func New(root string) (*Engine, error) {
|
|||
// This function mimics `Command` from the standard os/exec package.
|
||||
func (eng *Engine) Job(name string, args ...string) *Job {
|
||||
job := &Job{
|
||||
eng: eng,
|
||||
Eng: eng,
|
||||
Name: name,
|
||||
Args: args,
|
||||
Stdin: os.Stdin,
|
||||
|
|
23
engine/hack.go
Normal file
23
engine/hack.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package engine
|
||||
|
||||
|
||||
type Hack map[string]interface{}
|
||||
|
||||
|
||||
func (eng *Engine) Hack_GetGlobalVar(key string) interface{} {
|
||||
if eng.hack == nil {
|
||||
return nil
|
||||
}
|
||||
val, exists := eng.hack[key]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (eng *Engine) Hack_SetGlobalVar(key string, val interface{}) {
|
||||
if eng.hack == nil {
|
||||
eng.hack = make(Hack)
|
||||
}
|
||||
eng.hack[key] = val
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"fmt"
|
||||
|
@ -22,7 +23,7 @@ import (
|
|||
// This allows for richer error reporting.
|
||||
//
|
||||
type Job struct {
|
||||
eng *Engine
|
||||
Eng *Engine
|
||||
Name string
|
||||
Args []string
|
||||
env []string
|
||||
|
@ -111,3 +112,60 @@ func (job *Job) SetenvList(key string, value []string) error {
|
|||
func (job *Job) Setenv(key, value string) {
|
||||
job.env = append(job.env, key + "=" + value)
|
||||
}
|
||||
|
||||
// DecodeEnv decodes `src` as a json dictionary, and adds
|
||||
// each decoded key-value pair to the environment.
|
||||
//
|
||||
// If `text` cannot be decoded as a json dictionary, an error
|
||||
// is returned.
|
||||
func (job *Job) DecodeEnv(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(src).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
if sval, ok := v.(string); ok {
|
||||
job.Setenv(k, sval)
|
||||
} else if val, err := json.Marshal(v); err == nil {
|
||||
job.Setenv(k, string(val))
|
||||
} else {
|
||||
job.Setenv(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (job *Job) EncodeEnv(dst io.Writer) error {
|
||||
return json.NewEncoder(dst).Encode(job.Environ())
|
||||
}
|
||||
|
||||
func (job *Job) ExportEnv(dst interface{}) error {
|
||||
var buf bytes.Buffer
|
||||
if err := job.EncodeEnv(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.NewDecoder(&buf).Decode(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (job *Job) ImportEnv(src interface{}) error {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(src); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := job.DecodeEnv(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (job *Job) Environ() map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, kv := range job.env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
m[parts[0]] = parts[1]
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func init() {
|
|||
Register("dummy", func(job *Job) string { return ""; })
|
||||
}
|
||||
|
||||
func mkEngine(t *testing.T) *Engine {
|
||||
func NewTestEngine(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)
|
||||
|
@ -38,5 +38,5 @@ func mkEngine(t *testing.T) *Engine {
|
|||
}
|
||||
|
||||
func mkJob(t *testing.T, name string, args ...string) *Job {
|
||||
return mkEngine(t).Job(name, args...)
|
||||
return NewTestEngine(t).Job(name, args...)
|
||||
}
|
54
server.go
54
server.go
|
@ -40,7 +40,7 @@ func init() {
|
|||
// 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 jobInitApi(job *engine.Job) string {
|
||||
srv, err := NewServer(ConfigFromJob(job))
|
||||
srv, err := NewServer(job.Eng, ConfigFromJob(job))
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
@ -56,17 +56,19 @@ func jobInitApi(job *engine.Job) string {
|
|||
srv.Close()
|
||||
os.Exit(0)
|
||||
}()
|
||||
err = engine.Register("serveapi", func(job *engine.Job) string {
|
||||
return srv.ListenAndServe(job.Args...).Error()
|
||||
})
|
||||
if err != nil {
|
||||
job.Eng.Hack_SetGlobalVar("httpapi.server", srv)
|
||||
if err := engine.Register("start", srv.ContainerStart); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if err := engine.Register("serveapi", srv.ListenAndServe); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
|
||||
func (srv *Server) ListenAndServe(protoAddrs ...string) error {
|
||||
func (srv *Server) ListenAndServe(job *engine.Job) string {
|
||||
protoAddrs := job.Args
|
||||
chErrors := make(chan error, len(protoAddrs))
|
||||
for _, protoAddr := range protoAddrs {
|
||||
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
||||
|
@ -80,7 +82,7 @@ func (srv *Server) ListenAndServe(protoAddrs ...string) error {
|
|||
log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Invalid protocol format.")
|
||||
return "Invalid protocol format."
|
||||
}
|
||||
go func() {
|
||||
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, true)
|
||||
|
@ -89,10 +91,10 @@ func (srv *Server) ListenAndServe(protoAddrs ...string) error {
|
|||
for i := 0; i < len(protoAddrs); i += 1 {
|
||||
err := <-chErrors
|
||||
if err != nil {
|
||||
return err
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return "0"
|
||||
}
|
||||
|
||||
func (srv *Server) DockerVersion() APIVersion {
|
||||
|
@ -1282,8 +1284,7 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
|
|||
return fmt.Errorf("No such container: %s", name)
|
||||
}
|
||||
|
||||
// Register links
|
||||
if hostConfig != nil && hostConfig.Links != nil {
|
||||
if hostConfig.Links != nil {
|
||||
for _, l := range hostConfig.Links {
|
||||
parts, err := parseLink(l)
|
||||
if err != nil {
|
||||
|
@ -1296,7 +1297,6 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
|
|||
if child == nil {
|
||||
return fmt.Errorf("Could not get container for %s", parts["name"])
|
||||
}
|
||||
|
||||
if err := runtime.RegisterLink(container, child, parts["alias"]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1312,22 +1312,36 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
|
||||
func (srv *Server) ContainerStart(job *engine.Job) string {
|
||||
if len(job.Args) < 1 {
|
||||
return fmt.Sprintf("Usage: %s container_id", job.Name)
|
||||
}
|
||||
name := job.Args[0]
|
||||
runtime := srv.runtime
|
||||
container := runtime.Get(name)
|
||||
if container == nil {
|
||||
return fmt.Errorf("No such container: %s", name)
|
||||
return fmt.Sprintf("No such container: %s", name)
|
||||
}
|
||||
if hostConfig != nil {
|
||||
container.hostConfig = hostConfig
|
||||
// If no environment was set, then no hostconfig was passed.
|
||||
if len(job.Environ()) > 0 {
|
||||
var hostConfig HostConfig
|
||||
if err := job.ExportEnv(&hostConfig); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
// Register any links from the host config before starting the container
|
||||
// FIXME: we could just pass the container here, no need to lookup by name again.
|
||||
if err := srv.RegisterLinks(name, &hostConfig); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
container.hostConfig = &hostConfig
|
||||
container.ToDisk()
|
||||
}
|
||||
if err := container.Start(); err != nil {
|
||||
return fmt.Errorf("Cannot start container %s: %s", name, err)
|
||||
return fmt.Sprintf("Cannot start container %s: %s", name, err)
|
||||
}
|
||||
srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
|
||||
|
||||
return nil
|
||||
return "0"
|
||||
}
|
||||
|
||||
func (srv *Server) ContainerStop(name string, t int) error {
|
||||
|
@ -1478,12 +1492,13 @@ func (srv *Server) ContainerCopy(name string, resource string, out io.Writer) er
|
|||
|
||||
}
|
||||
|
||||
func NewServer(config *DaemonConfig) (*Server, error) {
|
||||
func NewServer(eng *engine.Engine, config *DaemonConfig) (*Server, error) {
|
||||
runtime, err := NewRuntime(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srv := &Server{
|
||||
Eng: eng,
|
||||
runtime: runtime,
|
||||
pullingPool: make(map[string]struct{}),
|
||||
pushingPool: make(map[string]struct{}),
|
||||
|
@ -1527,4 +1542,5 @@ type Server struct {
|
|||
events []utils.JSONMessage
|
||||
listeners map[string]chan utils.JSONMessage
|
||||
reqFactory *utils.HTTPRequestFactory
|
||||
Eng *engine.Engine
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -109,10 +110,11 @@ func TestCreateRm(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateRmVolumes(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
defer nuke(runtime)
|
||||
eng := engine.NewTestEngine(t)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
runtime := srv.runtime
|
||||
defer nuke(runtime)
|
||||
|
||||
config, hostConfig, _, err := ParseRun([]string{"-v", "/srv", GetTestImage(runtime).ID, "echo test"}, nil)
|
||||
if err != nil {
|
||||
|
@ -128,8 +130,11 @@ func TestCreateRmVolumes(t *testing.T) {
|
|||
t.Errorf("Expected 1 container, %v found", len(runtime.List()))
|
||||
}
|
||||
|
||||
err = srv.ContainerStart(id, hostConfig)
|
||||
if err != nil {
|
||||
job := eng.Job("start", id)
|
||||
if err := job.ImportEnv(hostConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -169,11 +174,11 @@ func TestCommit(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateStartRestartStopStartKillRm(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
eng := engine.NewTestEngine(t)
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
runtime := srv.runtime
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
config, hostConfig, _, err := ParseRun([]string{GetTestImage(runtime).ID, "/bin/cat"}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -188,7 +193,11 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
|
|||
t.Errorf("Expected 1 container, %v found", len(runtime.List()))
|
||||
}
|
||||
|
||||
if err := srv.ContainerStart(id, hostConfig); err != nil {
|
||||
job := eng.Job("start", id)
|
||||
if err := job.ImportEnv(hostConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -200,7 +209,11 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := srv.ContainerStart(id, hostConfig); err != nil {
|
||||
job = eng.Job("start", id)
|
||||
if err := job.ImportEnv(hostConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -384,9 +397,10 @@ func TestLogEvent(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRmi(t *testing.T) {
|
||||
runtime := mkRuntime(t)
|
||||
eng := engine.NewTestEngine(t)
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
runtime := srv.runtime
|
||||
defer nuke(runtime)
|
||||
srv := &Server{runtime: runtime}
|
||||
|
||||
initialImages, err := srv.Images(false, "")
|
||||
if err != nil {
|
||||
|
@ -404,8 +418,11 @@ func TestRmi(t *testing.T) {
|
|||
}
|
||||
|
||||
//To remove
|
||||
err = srv.ContainerStart(containerID, hostConfig)
|
||||
if err != nil {
|
||||
job := eng.Job("start", containerID)
|
||||
if err := job.ImportEnv(hostConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -425,8 +442,11 @@ func TestRmi(t *testing.T) {
|
|||
}
|
||||
|
||||
//To remove
|
||||
err = srv.ContainerStart(containerID, hostConfig)
|
||||
if err != nil {
|
||||
job = eng.Job("start", containerID)
|
||||
if err := job.ImportEnv(hostConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package docker
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -40,6 +41,18 @@ func mkRuntime(f Fataler) *Runtime {
|
|||
return runtime
|
||||
}
|
||||
|
||||
func mkServerFromEngine(eng *engine.Engine, t Fataler) *Server {
|
||||
iSrv := eng.Hack_GetGlobalVar("httpapi.server")
|
||||
if iSrv == nil {
|
||||
t.Fatal("Legacy server field not set in engine")
|
||||
}
|
||||
srv, ok := iSrv.(*Server)
|
||||
if !ok {
|
||||
t.Fatal("Legacy server field in engine does not cast to *Server")
|
||||
}
|
||||
return srv
|
||||
}
|
||||
|
||||
// A common interface to access the Fatal method of
|
||||
// both testing.B and testing.T.
|
||||
type Fataler interface {
|
||||
|
|
Loading…
Add table
Reference in a new issue