diff --git a/api/handlers.go b/api/handlers.go index d75c6d3e6b..151b359787 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -489,7 +489,6 @@ func postContainersCreate(c *context, w http.ResponseWriter, r *http.Request) { httpError(w, err.Error(), http.StatusBadRequest) return } - // Pass auth information along if present var authConfig *dockerclient.AuthConfig buf, err := base64.URLEncoding.DecodeString(r.Header.Get("X-Registry-Auth")) @@ -498,7 +497,6 @@ func postContainersCreate(c *context, w http.ResponseWriter, r *http.Request) { json.Unmarshal(buf, authConfig) } containerConfig := cluster.BuildContainerConfig(config) - if err := containerConfig.Validate(); err != nil { httpError(w, err.Error(), http.StatusInternalServerError) return @@ -712,6 +710,22 @@ func getEvents(c *context, w http.ResponseWriter, r *http.Request) { c.eventsHandler.Wait(r.RemoteAddr, until) } +// POST /containers/{name:.*}/start +func postContainersStart(c *context, w http.ResponseWriter, r *http.Request) { + name := mux.Vars(r)["name"] + container := c.cluster.Container(name) + if container == nil { + httpError(w, fmt.Sprintf("No such container %s", name), http.StatusNotFound) + return + } + + if err := c.cluster.StartContainer(container); err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusNoContent) +} + // POST /exec/{execid:.*}/start func postExecStart(c *context, w http.ResponseWriter, r *http.Request) { if r.Header.Get("Connection") == "" { diff --git a/api/primary.go b/api/primary.go index d1c62dd06f..bbfbcfe17a 100644 --- a/api/primary.go +++ b/api/primary.go @@ -69,7 +69,7 @@ var routes = map[string]map[string]handler{ "/containers/{name:.*}/unpause": proxyContainerAndForceRefresh, "/containers/{name:.*}/rename": postRenameContainer, "/containers/{name:.*}/restart": proxyContainerAndForceRefresh, - "/containers/{name:.*}/start": proxyContainerAndForceRefresh, + "/containers/{name:.*}/start": postContainersStart, "/containers/{name:.*}/stop": proxyContainerAndForceRefresh, "/containers/{name:.*}/update": proxyContainerAndForceRefresh, "/containers/{name:.*}/wait": proxyContainerAndForceRefresh, diff --git a/cluster/cluster.go b/cluster/cluster.go index 2c2627fcbd..041ee7fca2 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -26,6 +26,9 @@ type Cluster interface { // Return all containers Containers() Containers + // Start a container + StartContainer(container *Container) error + // Return container the matching `IDOrName` // TODO: remove this method from the interface as we can use // cluster.Containers().Get(IDOrName) diff --git a/cluster/container.go b/cluster/container.go index ac3547bf23..c9c13cd4b8 100644 --- a/cluster/container.go +++ b/cluster/container.go @@ -21,11 +21,6 @@ func (c *Container) Refresh() (*Container, error) { return c.Engine.refreshContainer(c.Id, true) } -// Start a container -func (c *Container) Start() error { - return c.Engine.client.StartContainer(c.Id, nil) -} - // Containers represents a list a containers type Containers []*Container diff --git a/cluster/engine.go b/cluster/engine.go index 5b7b5e58f1..c9275c9674 100644 --- a/cluster/engine.go +++ b/cluster/engine.go @@ -959,6 +959,19 @@ func (e *Engine) cleanupContainers() { e.Unlock() } +// StartContainer starts a container +func (e *Engine) StartContainer(id string) error { + err := e.client.StartContainer(id, nil) + e.CheckConnectionErr(err) + if err != nil { + return err + } + + // refresh container + _, err = e.refreshContainer(id, true) + return err +} + // RenameContainer rename a container func (e *Engine) RenameContainer(container *Container, newName string) error { // send rename request diff --git a/cluster/mesos/cluster.go b/cluster/mesos/cluster.go index d268117d93..4e11b28c8b 100644 --- a/cluster/mesos/cluster.go +++ b/cluster/mesos/cluster.go @@ -177,6 +177,15 @@ func (c *Cluster) UnregisterEventHandler(h cluster.EventHandler) { c.eventHandlers.UnregisterEventHandler(h) } +// StartContainer starts a container +func (c *Cluster) StartContainer(container *cluster.Container) error { + // if the container was started less than a second ago in detach mode, do not start it + if time.Now().Unix()-container.Created > 1 || container.Config.Labels[cluster.SwarmLabelNamespace+".mesos.detach"] != "true" { + return container.Engine.StartContainer(container.Id) + } + return nil +} + // CreateContainer for container creation in Mesos task func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (*cluster.Container, error) { if config.Memory == 0 && config.CpuShares == 0 { diff --git a/cluster/mesos/task/task.go b/cluster/mesos/task/task.go index 7bb9769bfa..9edb25c8a6 100644 --- a/cluster/mesos/task/task.go +++ b/cluster/mesos/task/task.go @@ -159,6 +159,10 @@ func (t *Task) Build(slaveID string, offers map[string]*mesosproto.Offer) { t.Container.Docker.Parameters = append(t.Container.Docker.Parameters, &mesosproto.Parameter{Key: proto.String("env"), Value: proto.String(value)}) } + if !t.config.AttachStdin && !t.config.AttachStdout && !t.config.AttachStderr { + t.Container.Docker.Parameters = append(t.Container.Docker.Parameters, &mesosproto.Parameter{Key: proto.String("label"), Value: proto.String(fmt.Sprintf("%s=true", cluster.SwarmLabelNamespace+".mesos.detach"))}) + } + t.SlaveId = &mesosproto.SlaveID{Value: &slaveID} } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 9fa08b71f2..5eb4a49b6d 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -124,6 +124,11 @@ func (c *Cluster) generateUniqueID() string { } } +// StartContainer starts a container +func (c *Cluster) StartContainer(container *cluster.Container) error { + return container.Engine.StartContainer(container.Id) +} + // CreateContainer aka schedule a brand new container into the cluster. func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (*cluster.Container, error) { container, err := c.createContainer(config, name, false, authConfig) diff --git a/cluster/watchdog.go b/cluster/watchdog.go index 28b6708cf4..0f360bff71 100644 --- a/cluster/watchdog.go +++ b/cluster/watchdog.go @@ -83,7 +83,7 @@ func (w *Watchdog) rescheduleContainers(e *Engine) { log.Infof("Rescheduled container %s from %s to %s as %s", c.Id, c.Engine.Name, newContainer.Engine.Name, newContainer.Id) if c.Info.State.Running { log.Infof("Container %s was running, starting container %s", c.Id, newContainer.Id) - if err := newContainer.Start(); err != nil { + if err := w.cluster.StartContainer(newContainer); err != nil { log.Errorf("Failed to start rescheduled container %s", newContainer.Id) } } diff --git a/test/integration/mesos/api/logs.bats b/test/integration/mesos/api/logs.bats index 71b242cc60..3b6c6182f2 100644 --- a/test/integration/mesos/api/logs.bats +++ b/test/integration/mesos/api/logs.bats @@ -29,6 +29,7 @@ function teardown() { # verify run docker_swarm logs test_container [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] [[ "${lines[0]}" == *"hello world"* ]] [[ "${lines[1]}" == *"hello docker"* ]] [[ "${lines[2]}" == *"hello swarm"* ]]