2014-04-22 00:31:08 +00:00
// DEPRECATION NOTICE. PLEASE DO NOT ADD ANYTHING TO THIS FILE.
//
// server/server.go is deprecated. We are working on breaking it up into smaller, cleaner
// pieces which will be easier to find and test. This will help make the code less
// redundant and more readable.
//
// Contributors, please don't add anything to server/server.go, unless it has the explicit
// goal of helping the deprecation effort.
//
// Maintainers, please refuse patches which add code to server/server.go.
//
// Instead try the following files:
// * For code related to local image management, try graph/
// * For code related to image downloading, uploading, remote search etc, try registry/
// * For code related to the docker daemon, try daemon/
// * For small utilities which could potentially be useful outside of Docker, try pkg/
// * For miscalleneous "util" functions which are docker-specific, try encapsulating them
// inside one of the subsystems above. If you really think they should be more widely
// available, are you sure you can't remove the docker dependencies and move them to
// pkg? In last resort, you can add them to utils/ (but please try not to).
2014-03-11 17:40:06 +00:00
package server
2013-05-06 09:31:22 +00:00
import (
2014-06-03 11:09:33 +00:00
"bytes"
2013-07-15 16:17:58 +00:00
"encoding/json"
2014-06-30 06:52:11 +00:00
"errors"
2013-05-06 09:31:22 +00:00
"fmt"
"io"
2013-05-15 18:30:40 +00:00
"io/ioutil"
2013-05-06 09:31:22 +00:00
"log"
2014-05-23 06:58:56 +00:00
"net"
2013-05-06 09:31:22 +00:00
"net/http"
"net/url"
"os"
2013-06-28 15:51:58 +00:00
"os/exec"
2014-03-10 21:22:27 +00:00
gosignal "os/signal"
2013-05-15 18:30:40 +00:00
"path"
2013-10-08 16:35:47 +00:00
"path/filepath"
2014-05-09 07:09:33 +00:00
"runtime"
2013-11-17 00:26:04 +00:00
"strconv"
2013-05-06 09:31:22 +00:00
"strings"
2013-06-17 23:10:00 +00:00
"sync"
2014-05-29 12:26:52 +00:00
"sync/atomic"
2013-10-21 16:04:42 +00:00
"syscall"
2013-10-31 23:57:45 +00:00
"time"
2014-04-28 19:03:31 +00:00
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/archive"
"github.com/docker/docker/daemon"
"github.com/docker/docker/daemonconfig"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/engine"
"github.com/docker/docker/graph"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/graphdb"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/pkg/tailfile"
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
"github.com/docker/docker/utils/filters"
2013-05-06 09:31:22 +00:00
)
2014-05-22 10:39:00 +00:00
func ( srv * Server ) handlerWrap ( h engine . Handler ) engine . Handler {
return func ( job * engine . Job ) engine . Status {
if ! srv . IsRunning ( ) {
return job . Errorf ( "Server is not running" )
}
srv . tasks . Add ( 1 )
defer srv . tasks . Done ( )
return h ( job )
}
}
2014-07-11 18:22:01 +00:00
func InitPidfile ( job * engine . Job ) engine . Status {
if len ( job . Args ) == 0 {
return job . Error ( fmt . Errorf ( "no pidfile provided to initialize" ) )
}
job . Logf ( "Creating pidfile" )
if err := utils . CreatePidFile ( job . Args [ 0 ] ) ; err != nil {
return job . Error ( err )
}
return engine . StatusOK
}
2013-10-27 00:19:35 +00:00
// jobInitApi 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.
2013-11-29 22:13:00 +00:00
// The signals SIGINT, SIGQUIT and SIGTERM are intercepted for cleanup.
2014-02-19 23:27:57 +00:00
func InitServer ( job * engine . Job ) engine . Status {
2013-10-27 07:16:32 +00:00
job . Logf ( "Creating server" )
2014-03-07 23:22:23 +00:00
srv , err := NewServer ( job . Eng , daemonconfig . ConfigFromJob ( job ) )
2013-10-21 16:04:42 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-10-21 16:04:42 +00:00
}
2013-10-27 07:16:32 +00:00
job . Logf ( "Setting up signal traps" )
2013-10-25 06:30:34 +00:00
c := make ( chan os . Signal , 1 )
2014-07-25 01:08:04 +00:00
signals := [ ] os . Signal { os . Interrupt , syscall . SIGTERM }
if os . Getenv ( "DEBUG" ) == "" {
signals = append ( signals , syscall . SIGQUIT )
}
gosignal . Notify ( c , signals ... )
2013-10-25 06:30:34 +00:00
go func ( ) {
2014-05-29 12:26:52 +00:00
interruptCount := uint32 ( 0 )
2014-04-02 12:56:11 +00:00
for sig := range c {
2014-05-29 12:26:52 +00:00
go func ( sig os . Signal ) {
2014-04-03 01:00:13 +00:00
log . Printf ( "Received signal '%v', starting shutdown of docker...\n" , sig )
switch sig {
case os . Interrupt , syscall . SIGTERM :
// If the user really wants to interrupt, let him do so.
2014-05-29 12:26:52 +00:00
if atomic . LoadUint32 ( & interruptCount ) < 3 {
atomic . AddUint32 ( & interruptCount , 1 )
2014-04-03 01:00:13 +00:00
// Initiate the cleanup only once
2014-05-29 12:26:52 +00:00
if atomic . LoadUint32 ( & interruptCount ) == 1 {
2014-04-17 21:43:01 +00:00
utils . RemovePidFile ( srv . daemon . Config ( ) . Pidfile )
2014-04-03 01:00:13 +00:00
srv . Close ( )
} else {
return
}
} else {
log . Printf ( "Force shutdown of docker, interrupting cleanup\n" )
}
case syscall . SIGQUIT :
}
os . Exit ( 128 + int ( sig . ( syscall . Signal ) ) )
2014-05-29 12:26:52 +00:00
} ( sig )
2014-04-02 12:56:11 +00:00
}
2013-10-25 06:30:34 +00:00
} ( )
2013-10-27 02:24:01 +00:00
job . Eng . Hack_SetGlobalVar ( "httpapi.server" , srv )
2014-04-17 21:43:01 +00:00
job . Eng . Hack_SetGlobalVar ( "httpapi.daemon" , srv . daemon )
2014-01-30 02:34:43 +00:00
2014-01-18 00:57:43 +00:00
for name , handler := range map [ string ] engine . Handler {
"export" : srv . ContainerExport ,
"create" : srv . ContainerCreate ,
"stop" : srv . ContainerStop ,
"restart" : srv . ContainerRestart ,
"start" : srv . ContainerStart ,
"kill" : srv . ContainerKill ,
2014-05-21 21:06:18 +00:00
"pause" : srv . ContainerPause ,
"unpause" : srv . ContainerUnpause ,
2014-01-18 00:57:43 +00:00
"wait" : srv . ContainerWait ,
2014-04-28 06:59:46 +00:00
"tag" : srv . ImageTag , // FIXME merge with "image_tag"
2014-01-18 00:57:43 +00:00
"resize" : srv . ContainerResize ,
"commit" : srv . ContainerCommit ,
"info" : srv . DockerInfo ,
"container_delete" : srv . ContainerDestroy ,
"image_export" : srv . ImageExport ,
"images" : srv . Images ,
"history" : srv . ImageHistory ,
"viz" : srv . ImagesViz ,
"container_copy" : srv . ContainerCopy ,
"attach" : srv . ContainerAttach ,
2014-04-02 19:26:06 +00:00
"logs" : srv . ContainerLogs ,
2014-01-18 00:57:43 +00:00
"changes" : srv . ContainerChanges ,
"top" : srv . ContainerTop ,
2014-01-21 00:09:17 +00:00
"load" : srv . ImageLoad ,
2014-01-23 20:19:52 +00:00
"build" : srv . Build ,
2014-01-22 21:35:35 +00:00
"pull" : srv . ImagePull ,
"import" : srv . ImageImport ,
2014-01-20 22:10:23 +00:00
"image_delete" : srv . ImageDelete ,
2014-01-23 19:12:17 +00:00
"events" : srv . Events ,
2014-01-23 01:33:29 +00:00
"push" : srv . ImagePush ,
2014-01-16 22:00:18 +00:00
"containers" : srv . Containers ,
2014-01-18 00:57:43 +00:00
} {
2014-05-22 10:39:00 +00:00
if err := job . Eng . Register ( name , srv . handlerWrap ( handler ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-18 00:57:43 +00:00
}
2014-01-16 22:58:20 +00:00
}
2014-04-28 06:59:46 +00:00
// Install image-related commands from the image subsystem.
// See `graph/service.go`
if err := srv . daemon . Repositories ( ) . Install ( job . Eng ) ; err != nil {
return job . Error ( err )
}
2014-05-20 19:36:15 +00:00
// Install daemon-related commands from the daemon subsystem.
// See `daemon/`
if err := srv . daemon . Install ( job . Eng ) ; err != nil {
return job . Error ( err )
}
2014-05-22 10:39:00 +00:00
srv . SetRunning ( true )
2013-11-20 07:37:03 +00:00
return engine . StatusOK
2013-10-27 00:19:35 +00:00
}
2014-05-21 21:06:18 +00:00
func ( srv * Server ) ContainerPause ( job * engine . Job ) engine . Status {
2014-06-03 15:44:21 +00:00
if len ( job . Args ) != 1 {
2014-05-21 21:06:18 +00:00
return job . Errorf ( "Usage: %s CONTAINER" , job . Name )
}
2014-06-03 15:44:21 +00:00
name := job . Args [ 0 ]
container := srv . daemon . Get ( name )
if container == nil {
2014-05-21 21:06:18 +00:00
return job . Errorf ( "No such container: %s" , name )
}
2014-06-03 15:44:21 +00:00
if err := container . Pause ( ) ; err != nil {
return job . Errorf ( "Cannot pause container %s: %s" , name , err )
}
2014-07-07 15:47:06 +00:00
srv . LogEvent ( "pause" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2014-05-21 21:06:18 +00:00
return engine . StatusOK
}
func ( srv * Server ) ContainerUnpause ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n < 1 || n > 2 {
return job . Errorf ( "Usage: %s CONTAINER" , job . Name )
}
2014-06-03 15:44:21 +00:00
name := job . Args [ 0 ]
container := srv . daemon . Get ( name )
if container == nil {
2014-05-21 21:06:18 +00:00
return job . Errorf ( "No such container: %s" , name )
}
2014-06-03 15:44:21 +00:00
if err := container . Unpause ( ) ; err != nil {
return job . Errorf ( "Cannot unpause container %s: %s" , name , err )
}
2014-07-07 15:47:06 +00:00
srv . LogEvent ( "unpause" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2014-05-21 21:06:18 +00:00
return engine . StatusOK
}
2013-10-08 19:15:29 +00:00
// ContainerKill send signal to the container
// If no signal is given (sig 0), then Kill with SIGKILL and wait
// for the container to exit.
// If a signal is given, then just send it to the container and return.
2013-11-22 03:41:17 +00:00
func ( srv * Server ) ContainerKill ( job * engine . Job ) engine . Status {
2013-11-17 00:26:04 +00:00
if n := len ( job . Args ) ; n < 1 || n > 2 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s CONTAINER [SIGNAL]" , job . Name )
2013-11-17 00:26:04 +00:00
}
2014-03-10 21:22:27 +00:00
var (
name = job . Args [ 0 ]
sig uint64
err error
)
// If we have a signal, look at it. Otherwise, do nothing
2013-11-17 00:26:04 +00:00
if len ( job . Args ) == 2 && job . Args [ 1 ] != "" {
2014-03-11 02:51:19 +00:00
// Check if we passed the signal as a number:
2014-03-10 21:22:27 +00:00
// The largest legal signal is 31, so let's parse on 5 bits
sig , err = strconv . ParseUint ( job . Args [ 1 ] , 10 , 5 )
if err != nil {
2014-04-01 19:33:46 +00:00
// The signal is not a number, treat it as a string (either like "KILL" or like "SIGKILL")
sig = uint64 ( signal . SignalMap [ strings . TrimPrefix ( job . Args [ 1 ] , "SIG" ) ] )
2014-07-24 13:48:20 +00:00
}
2014-03-10 21:22:27 +00:00
2014-07-24 13:48:20 +00:00
if sig == 0 {
return job . Errorf ( "Invalid signal: %s" , job . Args [ 1 ] )
2013-11-17 00:26:04 +00:00
}
}
2014-03-10 21:22:27 +00:00
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2014-01-22 04:42:36 +00:00
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
if sig == 0 || syscall . Signal ( sig ) == syscall . SIGKILL {
2013-10-08 19:15:29 +00:00
if err := container . Kill ( ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Cannot kill container %s: %s" , name , err )
2013-10-08 19:15:29 +00:00
}
2014-04-17 21:43:01 +00:00
srv . LogEvent ( "kill" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2013-10-08 19:15:29 +00:00
} else {
// Otherwise, just send the requested signal
2014-03-08 02:42:29 +00:00
if err := container . KillSig ( int ( sig ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Cannot kill container %s: %s" , name , err )
2013-10-08 19:15:29 +00:00
}
// FIXME: Add event for signals
2013-05-06 09:31:22 +00:00
}
} else {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-05-06 09:31:22 +00:00
}
2013-11-22 03:41:17 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-25 00:39:41 +00:00
2014-01-23 19:12:17 +00:00
func ( srv * Server ) Events ( job * engine . Job ) engine . Status {
2014-05-15 23:27:36 +00:00
if len ( job . Args ) != 0 {
return job . Errorf ( "Usage: %s" , job . Name )
2014-01-23 19:12:17 +00:00
}
var (
2014-03-23 23:14:40 +00:00
since = job . GetenvInt64 ( "since" )
until = job . GetenvInt64 ( "until" )
timeout = time . NewTimer ( time . Unix ( until , 0 ) . Sub ( time . Now ( ) ) )
2014-01-23 19:12:17 +00:00
)
2014-06-04 18:47:09 +00:00
// If no until, disable timeout
if until == 0 {
timeout . Stop ( )
2014-01-23 19:12:17 +00:00
}
listener := make ( chan utils . JSONMessage )
2014-06-04 18:47:09 +00:00
srv . eventPublisher . Subscribe ( listener )
defer srv . eventPublisher . Unsubscribe ( listener )
// When sending an event JSON serialization errors are ignored, but all
// other errors lead to the eviction of the listener.
sendEvent := func ( event * utils . JSONMessage ) error {
if b , err := json . Marshal ( event ) ; err == nil {
if _ , err = job . Stdout . Write ( b ) ; err != nil {
return err
}
}
return nil
2014-03-24 18:31:05 +00:00
}
2014-06-04 18:47:09 +00:00
job . Stdout . Write ( nil )
// Resend every event in the [since, until] time interval.
2014-01-23 19:12:17 +00:00
if since != 0 {
for _ , event := range srv . GetEvents ( ) {
2014-03-23 23:14:40 +00:00
if event . Time >= since && ( event . Time <= until || until == 0 ) {
2014-06-04 18:47:09 +00:00
if err := sendEvent ( & event ) ; err != nil {
2014-05-09 18:48:55 +00:00
return job . Error ( err )
2014-01-23 19:12:17 +00:00
}
}
}
}
2014-03-23 23:14:40 +00:00
for {
select {
2014-05-14 20:24:11 +00:00
case event , ok := <- listener :
2014-06-04 18:47:09 +00:00
if ! ok {
2014-05-14 20:24:11 +00:00
return engine . StatusOK
}
2014-06-04 18:47:09 +00:00
if err := sendEvent ( & event ) ; err != nil {
2014-03-23 23:14:40 +00:00
return job . Error ( err )
}
case <- timeout . C :
return engine . StatusOK
2014-01-23 19:12:17 +00:00
}
}
}
2013-05-06 09:31:22 +00:00
2013-12-08 01:33:37 +00:00
func ( srv * Server ) ContainerExport ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s container_id" , job . Name )
2013-12-08 01:33:37 +00:00
}
name := job . Args [ 0 ]
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2013-05-06 09:31:22 +00:00
data , err := container . Export ( )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "%s: %s" , name , err )
2013-05-06 09:31:22 +00:00
}
2014-02-14 11:41:46 +00:00
defer data . Close ( )
2013-05-06 09:31:22 +00:00
// Stream the entire contents of the container (basically a volatile snapshot)
2013-12-08 01:33:37 +00:00
if _ , err := io . Copy ( job . Stdout , data ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "%s: %s" , name , err )
2013-05-06 09:31:22 +00:00
}
2013-12-08 01:33:37 +00:00
// FIXME: factor job-specific LogEvent to engine.Job.Run()
2014-04-17 21:43:01 +00:00
srv . LogEvent ( "export" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2013-12-08 01:33:37 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-05-06 09:31:22 +00:00
}
2013-09-02 16:06:17 +00:00
// ImageExport exports all images with the given tag. All versions
// containing the same tag are exported. The resulting output is an
// uncompressed tar ball.
// name is the set of tags to export.
// out is the writer where the images are written to.
2014-01-07 20:39:15 +00:00
func ( srv * Server ) ImageExport ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
2014-05-01 01:51:03 +00:00
return job . Errorf ( "Usage: %s IMAGE\n" , job . Name )
2014-01-07 20:39:15 +00:00
}
name := job . Args [ 0 ]
2013-09-02 16:06:17 +00:00
// get image json
tempdir , err := ioutil . TempDir ( "" , "docker-export-" )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-09-02 16:06:17 +00:00
}
2013-11-18 20:34:34 +00:00
defer os . RemoveAll ( tempdir )
2013-09-02 16:06:17 +00:00
utils . Debugf ( "Serializing %s" , name )
2014-07-05 20:20:14 +00:00
rootRepoMap := map [ string ] graph . Repository { }
2014-04-17 21:43:01 +00:00
rootRepo , err := srv . daemon . Repositories ( ) . Get ( name )
2013-11-21 01:28:19 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-21 01:28:19 +00:00
}
if rootRepo != nil {
2014-07-05 20:20:14 +00:00
// this is a base repo name, like 'busybox'
2013-11-21 01:28:19 +00:00
for _ , id := range rootRepo {
2014-05-20 19:36:15 +00:00
if err := srv . exportImage ( job . Eng , id , tempdir ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-09-02 16:06:17 +00:00
}
}
2013-11-21 01:28:19 +00:00
rootRepoMap [ name ] = rootRepo
2014-07-05 20:20:14 +00:00
} else {
img , err := srv . daemon . Repositories ( ) . LookupImage ( name )
if err != nil {
return job . Error ( err )
}
if img != nil {
// This is a named image like 'busybox:latest'
repoName , repoTag := utils . ParseRepositoryTag ( name )
if err := srv . exportImage ( job . Eng , img . ID , tempdir ) ; err != nil {
return job . Error ( err )
}
// check this length, because a lookup of a truncated has will not have a tag
// and will not need to be added to this map
if len ( repoTag ) > 0 {
rootRepoMap [ repoName ] = graph . Repository { repoTag : img . ID }
}
} else {
// this must be an ID that didn't get looked up just right?
if err := srv . exportImage ( job . Eng , name , tempdir ) ; err != nil {
return job . Error ( err )
}
}
}
// write repositories, if there is something to write
if len ( rootRepoMap ) > 0 {
2013-11-21 01:28:19 +00:00
rootRepoJson , _ := json . Marshal ( rootRepoMap )
2013-09-02 16:06:17 +00:00
2014-04-08 16:39:25 +00:00
if err := ioutil . WriteFile ( path . Join ( tempdir , "repositories" ) , rootRepoJson , os . FileMode ( 0644 ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-21 01:28:19 +00:00
}
} else {
2014-07-05 20:20:14 +00:00
utils . Debugf ( "There were no repositories to write" )
2013-11-18 19:48:55 +00:00
}
2013-09-02 16:06:17 +00:00
2013-11-13 23:41:42 +00:00
fs , err := archive . Tar ( tempdir , archive . Uncompressed )
2013-09-02 16:06:17 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-09-02 16:06:17 +00:00
}
2014-02-14 11:41:46 +00:00
defer fs . Close ( )
2013-11-18 19:48:55 +00:00
2014-01-07 20:39:15 +00:00
if _ , err := io . Copy ( job . Stdout , fs ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-09-02 16:06:17 +00:00
}
2014-05-20 19:36:15 +00:00
utils . Debugf ( "End Serializing %s" , name )
2014-01-07 20:39:15 +00:00
return engine . StatusOK
2013-09-02 16:06:17 +00:00
}
2014-05-20 19:36:15 +00:00
func ( srv * Server ) exportImage ( eng * engine . Engine , name , tempdir string ) error {
for n := name ; n != "" ; {
2013-11-21 01:28:19 +00:00
// temporary directory
2014-05-20 19:36:15 +00:00
tmpImageDir := path . Join ( tempdir , n )
2014-04-08 16:39:25 +00:00
if err := os . Mkdir ( tmpImageDir , os . FileMode ( 0755 ) ) ; err != nil {
2013-12-04 19:55:42 +00:00
if os . IsExist ( err ) {
return nil
}
2013-11-21 01:28:19 +00:00
return err
}
var version = "1.0"
var versionBuf = [ ] byte ( version )
2014-04-08 16:39:25 +00:00
if err := ioutil . WriteFile ( path . Join ( tmpImageDir , "VERSION" ) , versionBuf , os . FileMode ( 0644 ) ) ; err != nil {
2013-11-21 01:28:19 +00:00
return err
}
// serialize json
2014-05-20 19:36:15 +00:00
json , err := os . Create ( path . Join ( tmpImageDir , "json" ) )
2013-11-21 01:28:19 +00:00
if err != nil {
return err
}
2014-05-20 19:36:15 +00:00
job := eng . Job ( "image_inspect" , n )
2014-06-17 00:06:21 +00:00
job . SetenvBool ( "raw" , true )
2014-05-20 19:36:15 +00:00
job . Stdout . Add ( json )
if err := job . Run ( ) ; err != nil {
2013-11-21 01:28:19 +00:00
return err
}
// serialize filesystem
fsTar , err := os . Create ( path . Join ( tmpImageDir , "layer.tar" ) )
if err != nil {
return err
}
2014-05-20 19:36:15 +00:00
job = eng . Job ( "image_tarlayer" , n )
job . Stdout . Add ( fsTar )
if err := job . Run ( ) ; err != nil {
2013-11-21 01:28:19 +00:00
return err
}
// find parent
2014-05-20 19:36:15 +00:00
job = eng . Job ( "image_get" , n )
info , _ := job . Stdout . AddEnv ( )
if err := job . Run ( ) ; err != nil {
return err
2013-11-21 01:28:19 +00:00
}
2014-05-20 19:36:15 +00:00
n = info . Get ( "Parent" )
2013-11-21 01:28:19 +00:00
}
return nil
}
2014-01-23 20:19:52 +00:00
func ( srv * Server ) Build ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 0 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s\n" , job . Name )
2014-01-23 20:19:52 +00:00
}
var (
remoteURL = job . Getenv ( "remote" )
repoName = job . Getenv ( "t" )
suppressOutput = job . GetenvBool ( "q" )
noCache = job . GetenvBool ( "nocache" )
rm = job . GetenvBool ( "rm" )
2014-05-16 11:47:33 +00:00
forceRm = job . GetenvBool ( "forcerm" )
2014-05-07 22:59:13 +00:00
authConfig = & registry . AuthConfig { }
2014-03-11 00:16:58 +00:00
configFile = & registry . ConfigFile { }
2014-01-23 20:19:52 +00:00
tag string
2014-02-14 11:41:46 +00:00
context io . ReadCloser
2014-01-23 20:19:52 +00:00
)
2014-05-07 22:59:13 +00:00
job . GetenvJson ( "authConfig" , authConfig )
2014-05-07 22:58:09 +00:00
job . GetenvJson ( "configFile" , configFile )
2014-01-23 20:19:52 +00:00
repoName , tag = utils . ParseRepositoryTag ( repoName )
if remoteURL == "" {
2014-02-14 11:41:46 +00:00
context = ioutil . NopCloser ( job . Stdin )
2014-01-23 20:19:52 +00:00
} else if utils . IsGIT ( remoteURL ) {
if ! strings . HasPrefix ( remoteURL , "git://" ) {
remoteURL = "https://" + remoteURL
}
root , err := ioutil . TempDir ( "" , "docker-build-git" )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-23 20:19:52 +00:00
}
defer os . RemoveAll ( root )
2014-01-06 18:06:05 +00:00
if output , err := exec . Command ( "git" , "clone" , "--recursive" , remoteURL , root ) . CombinedOutput ( ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Error trying to use git: %s (%s)" , err , output )
2014-01-23 20:19:52 +00:00
}
c , err := archive . Tar ( root , archive . Uncompressed )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-23 20:19:52 +00:00
}
context = c
} else if utils . IsURL ( remoteURL ) {
f , err := utils . Download ( remoteURL )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-23 20:19:52 +00:00
}
defer f . Body . Close ( )
dockerFile , err := ioutil . ReadAll ( f . Body )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-23 20:19:52 +00:00
}
2014-02-12 23:10:41 +00:00
c , err := archive . Generate ( "Dockerfile" , string ( dockerFile ) )
2014-01-23 20:19:52 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-23 20:19:52 +00:00
}
context = c
}
2014-02-14 11:41:46 +00:00
defer context . Close ( )
2014-01-23 20:19:52 +00:00
sf := utils . NewStreamFormatter ( job . GetenvBool ( "json" ) )
b := NewBuildFile ( srv ,
2014-03-11 17:40:06 +00:00
& utils . StdoutFormater {
2014-01-23 20:19:52 +00:00
Writer : job . Stdout ,
StreamFormatter : sf ,
} ,
2014-03-11 17:40:06 +00:00
& utils . StderrFormater {
2014-01-23 20:19:52 +00:00
Writer : job . Stdout ,
StreamFormatter : sf ,
} ,
2014-05-16 11:47:33 +00:00
! suppressOutput , ! noCache , rm , forceRm , job . Stdout , sf , authConfig , configFile )
2014-01-23 20:19:52 +00:00
id , err := b . Build ( context )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-23 20:19:52 +00:00
}
if repoName != "" {
2014-04-17 21:43:01 +00:00
srv . daemon . Repositories ( ) . Set ( repoName , tag , id , false )
2014-01-23 20:19:52 +00:00
}
return engine . StatusOK
}
2013-09-02 16:06:17 +00:00
// Loads a set of images into the repository. This is the complementary of ImageExport.
// The input stream is an uncompressed tar ball containing images and metadata.
2014-01-21 00:09:17 +00:00
func ( srv * Server ) ImageLoad ( job * engine . Job ) engine . Status {
2013-11-18 19:48:55 +00:00
tmpImageDir , err := ioutil . TempDir ( "" , "docker-import-" )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
2013-10-12 05:11:49 +00:00
defer os . RemoveAll ( tmpImageDir )
2013-11-18 19:48:55 +00:00
var (
repoTarFile = path . Join ( tmpImageDir , "repo.tar" )
repoDir = path . Join ( tmpImageDir , "repo" )
)
tarFile , err := os . Create ( repoTarFile )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
2014-01-21 00:09:17 +00:00
if _ , err := io . Copy ( tarFile , job . Stdin ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
2013-09-02 16:06:17 +00:00
tarFile . Close ( )
2013-11-18 19:48:55 +00:00
repoFile , err := os . Open ( repoTarFile )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
if err := os . Mkdir ( repoDir , os . ModeDir ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
2013-11-20 19:07:42 +00:00
if err := archive . Untar ( repoFile , repoDir , nil ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
2013-11-21 01:28:19 +00:00
dirs , err := ioutil . ReadDir ( repoDir )
2013-11-18 19:48:55 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
2013-09-02 16:06:17 +00:00
2013-11-21 01:28:19 +00:00
for _ , d := range dirs {
if d . IsDir ( ) {
2014-05-20 19:36:15 +00:00
if err := srv . recursiveLoad ( job . Eng , d . Name ( ) , tmpImageDir ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-18 19:48:55 +00:00
}
2013-11-21 01:28:19 +00:00
}
}
repositoriesJson , err := ioutil . ReadFile ( path . Join ( tmpImageDir , "repo" , "repositories" ) )
if err == nil {
2014-03-08 02:04:38 +00:00
repositories := map [ string ] graph . Repository { }
2013-11-21 01:28:19 +00:00
if err := json . Unmarshal ( repositoriesJson , & repositories ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-21 01:28:19 +00:00
}
for imageName , tagMap := range repositories {
for tag , address := range tagMap {
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Repositories ( ) . Set ( imageName , tag , address , true ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-21 01:28:19 +00:00
}
2013-09-02 16:06:17 +00:00
}
}
2013-11-21 01:28:19 +00:00
} else if ! os . IsNotExist ( err ) {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-09-02 16:06:17 +00:00
}
2013-11-21 01:28:19 +00:00
2014-01-21 00:09:17 +00:00
return engine . StatusOK
2013-09-02 16:06:17 +00:00
}
2014-05-20 19:36:15 +00:00
func ( srv * Server ) recursiveLoad ( eng * engine . Engine , address , tmpImageDir string ) error {
if err := eng . Job ( "image_get" , address ) . Run ( ) ; err != nil {
2013-09-02 16:06:17 +00:00
utils . Debugf ( "Loading %s" , address )
2013-11-18 19:48:55 +00:00
2013-09-02 16:06:17 +00:00
imageJson , err := ioutil . ReadFile ( path . Join ( tmpImageDir , "repo" , address , "json" ) )
if err != nil {
utils . Debugf ( "Error reading json" , err )
2013-11-30 00:53:20 +00:00
return err
2013-09-02 16:06:17 +00:00
}
2013-11-18 19:48:55 +00:00
2013-09-02 16:06:17 +00:00
layer , err := os . Open ( path . Join ( tmpImageDir , "repo" , address , "layer.tar" ) )
if err != nil {
utils . Debugf ( "Error reading embedded tar" , err )
return err
}
2014-03-08 01:36:47 +00:00
img , err := image . NewImgJSON ( imageJson )
2013-09-02 16:06:17 +00:00
if err != nil {
utils . Debugf ( "Error unmarshalling json" , err )
return err
}
if img . Parent != "" {
2014-04-17 21:43:01 +00:00
if ! srv . daemon . Graph ( ) . Exists ( img . Parent ) {
2014-05-20 19:36:15 +00:00
if err := srv . recursiveLoad ( eng , img . Parent , tmpImageDir ) ; err != nil {
2013-11-18 19:48:55 +00:00
return err
}
2013-09-02 16:06:17 +00:00
}
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Graph ( ) . Register ( imageJson , layer , img ) ; err != nil {
2013-11-18 19:48:55 +00:00
return err
2013-09-02 16:06:17 +00:00
}
}
utils . Debugf ( "Completed processing %s" , address )
2013-11-21 01:28:19 +00:00
2013-09-02 16:06:17 +00:00
return nil
}
2014-01-15 21:52:35 +00:00
func ( srv * Server ) ImagesViz ( job * engine . Job ) engine . Status {
2014-04-17 21:43:01 +00:00
images , _ := srv . daemon . Graph ( ) . Map ( )
2013-10-08 13:52:36 +00:00
if images == nil {
2014-01-15 21:52:35 +00:00
return engine . StatusOK
2013-10-08 13:52:36 +00:00
}
2014-01-15 21:52:35 +00:00
job . Stdout . Write ( [ ] byte ( "digraph docker {\n" ) )
2013-10-08 13:52:36 +00:00
var (
2014-03-08 01:36:47 +00:00
parentImage * image . Image
2013-10-08 13:52:36 +00:00
err error
)
for _ , image := range images {
parentImage , err = image . GetParent ( )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Error while getting parent image: %v" , err )
2013-10-08 13:52:36 +00:00
}
if parentImage != nil {
2014-01-15 21:52:35 +00:00
job . Stdout . Write ( [ ] byte ( " \"" + parentImage . ID + "\" -> \"" + image . ID + "\"\n" ) )
2013-10-08 13:52:36 +00:00
} else {
2014-01-15 21:52:35 +00:00
job . Stdout . Write ( [ ] byte ( " base -> \"" + image . ID + "\" [style=invis]\n" ) )
2013-10-08 13:52:36 +00:00
}
}
2014-05-29 12:22:13 +00:00
for id , repos := range srv . daemon . Repositories ( ) . GetRepoRefs ( ) {
2014-01-15 21:52:35 +00:00
job . Stdout . Write ( [ ] byte ( " \"" + id + "\" [label=\"" + id + "\\n" + strings . Join ( repos , "\\n" ) + "\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n" ) )
2013-10-08 13:52:36 +00:00
}
2014-01-15 21:52:35 +00:00
job . Stdout . Write ( [ ] byte ( " base [style=invisible]\n}\n" ) )
return engine . StatusOK
2013-10-08 13:52:36 +00:00
}
2013-12-12 22:39:35 +00:00
func ( srv * Server ) Images ( job * engine . Job ) engine . Status {
2013-05-19 17:46:24 +00:00
var (
2014-02-26 22:04:11 +00:00
allImages map [ string ] * image . Image
err error
filt_tagged = true
2013-05-19 17:46:24 +00:00
)
2014-02-26 22:04:11 +00:00
2014-05-19 20:31:15 +00:00
imageFilters , err := filters . FromParam ( job . Getenv ( "filters" ) )
2014-02-26 22:04:11 +00:00
if err != nil {
return job . Error ( err )
}
2014-05-21 22:44:31 +00:00
if i , ok := imageFilters [ "dangling" ] ; ok {
2014-05-19 20:31:15 +00:00
for _ , value := range i {
if strings . ToLower ( value ) == "true" {
filt_tagged = false
}
}
2014-02-26 22:04:11 +00:00
}
2014-05-30 20:42:37 +00:00
if job . GetenvBool ( "all" ) && filt_tagged {
2014-04-17 21:43:01 +00:00
allImages , err = srv . daemon . Graph ( ) . Map ( )
2013-05-06 09:31:22 +00:00
} else {
2014-04-17 21:43:01 +00:00
allImages , err = srv . daemon . Graph ( ) . Heads ( )
2013-05-06 09:31:22 +00:00
}
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
2013-12-12 22:39:35 +00:00
lookup := make ( map [ string ] * engine . Env )
2014-05-29 12:22:13 +00:00
srv . daemon . Repositories ( ) . Lock ( )
2014-04-17 21:43:01 +00:00
for name , repository := range srv . daemon . Repositories ( ) . Repositories {
2013-12-12 22:39:35 +00:00
if job . Getenv ( "filter" ) != "" {
if match , _ := path . Match ( job . Getenv ( "filter" ) , name ) ; ! match {
2013-09-06 19:51:49 +00:00
continue
}
2013-05-06 09:31:22 +00:00
}
for tag , id := range repository {
2014-04-17 21:43:01 +00:00
image , err := srv . daemon . Graph ( ) . Get ( id )
2013-05-06 09:31:22 +00:00
if err != nil {
log . Printf ( "Warning: couldn't load %s from %s/%s: %s" , id , name , tag , err )
continue
}
2013-10-06 05:44:04 +00:00
if out , exists := lookup [ id ] ; exists {
2014-02-26 22:04:11 +00:00
if filt_tagged {
out . SetList ( "RepoTags" , append ( out . GetList ( "RepoTags" ) , fmt . Sprintf ( "%s:%s" , name , tag ) ) )
}
2013-10-06 05:44:04 +00:00
} else {
2014-02-26 22:04:11 +00:00
// get the boolean list for if only the untagged images are requested
2013-10-06 05:44:04 +00:00
delete ( allImages , id )
2014-02-26 22:04:11 +00:00
if filt_tagged {
out := & engine . Env { }
out . Set ( "ParentId" , image . Parent )
out . SetList ( "RepoTags" , [ ] string { fmt . Sprintf ( "%s:%s" , name , tag ) } )
out . Set ( "Id" , image . ID )
out . SetInt64 ( "Created" , image . Created . Unix ( ) )
out . SetInt64 ( "Size" , image . Size )
out . SetInt64 ( "VirtualSize" , image . GetParentsSize ( 0 ) + image . Size )
lookup [ id ] = out
}
2013-10-06 05:44:04 +00:00
}
2013-05-06 09:31:22 +00:00
}
}
2014-05-29 12:22:13 +00:00
srv . daemon . Repositories ( ) . Unlock ( )
2013-10-06 05:44:04 +00:00
2013-12-12 22:39:35 +00:00
outs := engine . NewTable ( "Created" , len ( lookup ) )
2013-10-06 05:44:04 +00:00
for _ , value := range lookup {
2013-12-12 22:39:35 +00:00
outs . Add ( value )
2013-10-06 05:44:04 +00:00
}
// Display images which aren't part of a repository/tag
2013-12-12 22:39:35 +00:00
if job . Getenv ( "filter" ) == "" {
2013-05-13 10:18:55 +00:00
for _ , image := range allImages {
2013-12-12 22:39:35 +00:00
out := & engine . Env { }
2014-01-13 22:55:31 +00:00
out . Set ( "ParentId" , image . Parent )
out . SetList ( "RepoTags" , [ ] string { "<none>:<none>" } )
2014-01-29 20:31:49 +00:00
out . Set ( "Id" , image . ID )
2013-12-12 22:39:35 +00:00
out . SetInt64 ( "Created" , image . Created . Unix ( ) )
out . SetInt64 ( "Size" , image . Size )
2014-03-08 01:36:47 +00:00
out . SetInt64 ( "VirtualSize" , image . GetParentsSize ( 0 ) + image . Size )
2013-12-12 22:39:35 +00:00
outs . Add ( out )
2013-05-06 09:31:22 +00:00
}
}
2013-08-03 22:33:51 +00:00
2013-12-13 18:26:00 +00:00
outs . ReverseSort ( )
2014-01-21 23:06:23 +00:00
if _ , err := outs . WriteListTo ( job . Stdout ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-12-12 22:39:35 +00:00
}
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-12-11 18:35:21 +00:00
func ( srv * Server ) DockerInfo ( job * engine . Job ) engine . Status {
2014-04-17 21:43:01 +00:00
images , _ := srv . daemon . Graph ( ) . Map ( )
2013-12-12 21:35:50 +00:00
var imgcount int
2013-05-06 09:31:22 +00:00
if images == nil {
imgcount = 0
} else {
2013-12-12 21:35:50 +00:00
imgcount = len ( images )
2013-05-06 09:31:22 +00:00
}
2013-07-24 13:35:38 +00:00
kernelVersion := "<unknown>"
if kv , err := utils . GetKernelVersion ( ) ; err == nil {
kernelVersion = kv . String ( )
}
2013-07-19 16:36:23 +00:00
2013-12-05 09:14:15 +00:00
// if we still have the original dockerinit binary from before we copied it locally, let's return the path to that, since that's more intuitive (the copied path is trivial to derive by hand given VERSION)
initPath := utils . DockerInitPath ( "" )
if initPath == "" {
2014-04-17 21:43:01 +00:00
// if that fails, we'll just return the path from the daemon
initPath = srv . daemon . SystemInitPath ( )
2013-12-05 09:14:15 +00:00
}
2013-12-11 18:35:21 +00:00
v := & engine . Env { }
2014-04-17 21:43:01 +00:00
v . SetInt ( "Containers" , len ( srv . daemon . List ( ) ) )
2013-12-11 18:35:21 +00:00
v . SetInt ( "Images" , imgcount )
2014-04-17 21:43:01 +00:00
v . Set ( "Driver" , srv . daemon . GraphDriver ( ) . String ( ) )
v . SetJson ( "DriverStatus" , srv . daemon . GraphDriver ( ) . Status ( ) )
v . SetBool ( "MemoryLimit" , srv . daemon . SystemConfig ( ) . MemoryLimit )
v . SetBool ( "SwapLimit" , srv . daemon . SystemConfig ( ) . SwapLimit )
v . SetBool ( "IPv4Forwarding" , ! srv . daemon . SystemConfig ( ) . IPv4ForwardingDisabled )
2013-12-11 18:35:21 +00:00
v . SetBool ( "Debug" , os . Getenv ( "DEBUG" ) != "" )
2013-12-12 21:35:50 +00:00
v . SetInt ( "NFd" , utils . GetTotalUsedFds ( ) )
2014-05-09 07:09:33 +00:00
v . SetInt ( "NGoroutines" , runtime . NumGoroutine ( ) )
2014-04-17 21:43:01 +00:00
v . Set ( "ExecutionDriver" , srv . daemon . ExecutionDriver ( ) . Name ( ) )
2014-06-04 18:47:09 +00:00
v . SetInt ( "NEventsListener" , srv . eventPublisher . SubscribersCount ( ) )
2013-12-11 18:35:21 +00:00
v . Set ( "KernelVersion" , kernelVersion )
2014-03-11 00:16:58 +00:00
v . Set ( "IndexServerAddress" , registry . IndexServerAddress ( ) )
2014-02-12 00:26:54 +00:00
v . Set ( "InitSha1" , dockerversion . INITSHA1 )
2013-12-05 09:14:15 +00:00
v . Set ( "InitPath" , initPath )
2014-04-26 10:22:05 +00:00
v . SetList ( "Sockets" , srv . daemon . Sockets )
2013-12-11 18:35:21 +00:00
if _ , err := v . WriteTo ( job . Stdout ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-31 22:53:57 +00:00
}
2013-12-11 18:35:21 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-14 00:50:10 +00:00
func ( srv * Server ) ImageHistory ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s IMAGE" , job . Name )
2014-01-14 00:50:10 +00:00
}
name := job . Args [ 0 ]
2014-04-17 21:43:01 +00:00
foundImage , err := srv . daemon . Repositories ( ) . LookupImage ( name )
2013-05-06 09:31:22 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
2013-06-18 17:31:07 +00:00
lookupMap := make ( map [ string ] [ ] string )
2014-04-17 21:43:01 +00:00
for name , repository := range srv . daemon . Repositories ( ) . Repositories {
2013-06-18 01:39:30 +00:00
for tag , id := range repository {
// If the ID already has a reverse lookup, do not update it unless for "latest"
2013-06-18 17:31:07 +00:00
if _ , exists := lookupMap [ id ] ; ! exists {
lookupMap [ id ] = [ ] string { }
2013-06-18 01:39:30 +00:00
}
2013-06-18 17:31:07 +00:00
lookupMap [ id ] = append ( lookupMap [ id ] , name + ":" + tag )
2013-06-18 01:39:30 +00:00
}
}
2014-01-14 00:50:10 +00:00
outs := engine . NewTable ( "Created" , 0 )
2014-03-08 01:36:47 +00:00
err = foundImage . WalkHistory ( func ( img * image . Image ) error {
2014-01-14 00:50:10 +00:00
out := & engine . Env { }
2014-01-29 20:31:49 +00:00
out . Set ( "Id" , img . ID )
2014-01-14 00:50:10 +00:00
out . SetInt64 ( "Created" , img . Created . Unix ( ) )
out . Set ( "CreatedBy" , strings . Join ( img . ContainerConfig . Cmd , " " ) )
out . SetList ( "Tags" , lookupMap [ img . ID ] )
out . SetInt64 ( "Size" , img . Size )
outs . Add ( out )
2013-05-06 09:31:22 +00:00
return nil
} )
2014-01-21 23:06:23 +00:00
if _ , err := outs . WriteListTo ( job . Stdout ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-14 00:50:10 +00:00
}
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-16 22:58:20 +00:00
func ( srv * Server ) ContainerTop ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 && len ( job . Args ) != 2 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Not enough arguments. Usage: %s CONTAINER [PS_ARGS]\n" , job . Name )
2014-01-16 22:58:20 +00:00
}
var (
name = job . Args [ 0 ]
psArgs = "-ef"
)
if len ( job . Args ) == 2 && job . Args [ 1 ] != "" {
psArgs = job . Args [ 1 ]
}
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2013-12-23 22:42:04 +00:00
if ! container . State . IsRunning ( ) {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Container %s is not running" , name )
2013-12-23 22:42:04 +00:00
}
2014-04-17 21:43:01 +00:00
pids , err := srv . daemon . ExecutionDriver ( ) . GetPidsForContainer ( container . ID )
2013-06-28 15:51:58 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-12-11 14:56:44 +00:00
}
output , err := exec . Command ( "ps" , psArgs ) . Output ( )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Error running ps: %s" , err )
2013-12-11 14:56:44 +00:00
}
lines := strings . Split ( string ( output ) , "\n" )
header := strings . Fields ( lines [ 0 ] )
2014-01-16 22:58:20 +00:00
out := & engine . Env { }
out . SetList ( "Titles" , header )
2013-12-11 14:56:44 +00:00
pidIndex := - 1
for i , name := range header {
if name == "PID" {
pidIndex = i
}
}
if pidIndex == - 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Couldn't find PID field in ps output" )
2013-12-11 14:56:44 +00:00
}
2014-01-16 22:58:20 +00:00
processes := [ ] [ ] string { }
2013-12-11 14:56:44 +00:00
for _ , line := range lines [ 1 : ] {
2013-07-19 10:06:32 +00:00
if len ( line ) == 0 {
2013-06-28 15:51:58 +00:00
continue
}
2013-12-11 14:56:44 +00:00
fields := strings . Fields ( line )
p , err := strconv . Atoi ( fields [ pidIndex ] )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Unexpected pid '%s': %s" , fields [ pidIndex ] , err )
2013-06-28 15:51:58 +00:00
}
2013-12-11 14:56:44 +00:00
for _ , pid := range pids {
if pid == p {
// Make sure number of fields equals number of header titles
// merging "overhanging" fields
2014-01-16 22:58:20 +00:00
process := fields [ : len ( header ) - 1 ]
process = append ( process , strings . Join ( fields [ len ( header ) - 1 : ] , " " ) )
processes = append ( processes , process )
2013-10-30 02:03:41 +00:00
}
2013-07-19 10:06:32 +00:00
}
2013-06-28 15:51:58 +00:00
}
2014-01-16 22:58:20 +00:00
out . SetJson ( "Processes" , processes )
out . WriteTo ( job . Stdout )
return engine . StatusOK
2013-06-28 15:51:58 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-06-28 15:51:58 +00:00
}
2014-01-15 00:51:59 +00:00
func ( srv * Server ) ContainerChanges ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s CONTAINER" , job . Name )
2014-01-15 00:51:59 +00:00
}
name := job . Args [ 0 ]
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2014-01-15 00:51:59 +00:00
outs := engine . NewTable ( "" , 0 )
changes , err := container . Changes ( )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-15 00:51:59 +00:00
}
for _ , change := range changes {
out := & engine . Env { }
if err := out . Import ( change ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-15 00:51:59 +00:00
}
outs . Add ( out )
}
2014-01-21 23:06:23 +00:00
if _ , err := outs . WriteListTo ( job . Stdout ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-15 00:51:59 +00:00
}
} else {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-05-06 09:31:22 +00:00
}
2014-01-15 00:51:59 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-16 22:00:18 +00:00
func ( srv * Server ) Containers ( job * engine . Job ) engine . Status {
var (
foundBefore bool
displayed int
all = job . GetenvBool ( "all" )
since = job . Getenv ( "since" )
before = job . Getenv ( "before" )
n = job . GetenvInt ( "limit" )
size = job . GetenvBool ( "size" )
)
outs := engine . NewTable ( "Created" , 0 )
2013-05-10 00:50:56 +00:00
2013-11-19 19:02:10 +00:00
names := map [ string ] [ ] string { }
2014-04-17 21:43:01 +00:00
srv . daemon . ContainerGraph ( ) . Walk ( "/" , func ( p string , e * graphdb . Entity ) error {
2013-11-19 19:02:10 +00:00
names [ e . ID ( ) ] = append ( names [ e . ID ( ) ] , p )
return nil
} , - 1 )
2014-04-17 21:43:01 +00:00
var beforeCont , sinceCont * daemon . Container
2014-03-18 17:38:56 +00:00
if before != "" {
2014-04-17 21:43:01 +00:00
beforeCont = srv . daemon . Get ( before )
2014-03-18 17:38:56 +00:00
if beforeCont == nil {
return job . Error ( fmt . Errorf ( "Could not find container with name or id %s" , before ) )
}
}
if since != "" {
2014-04-17 21:43:01 +00:00
sinceCont = srv . daemon . Get ( since )
2014-03-18 17:38:56 +00:00
if sinceCont == nil {
return job . Error ( fmt . Errorf ( "Could not find container with name or id %s" , since ) )
}
}
2014-06-30 06:52:11 +00:00
errLast := errors . New ( "last container" )
writeCont := func ( container * daemon . Container ) error {
container . Lock ( )
defer container . Unlock ( )
2014-01-31 02:06:08 +00:00
if ! container . State . IsRunning ( ) && ! all && n <= 0 && since == "" && before == "" {
2014-06-30 06:52:11 +00:00
return nil
2013-05-06 09:31:22 +00:00
}
2013-11-13 09:29:00 +00:00
if before != "" && ! foundBefore {
2014-03-18 17:38:56 +00:00
if container . ID == beforeCont . ID {
2013-05-08 16:28:11 +00:00
foundBefore = true
}
2014-06-30 06:52:11 +00:00
return nil
2013-05-08 16:28:11 +00:00
}
2014-01-31 02:06:08 +00:00
if n > 0 && displayed == n {
2014-06-30 06:52:11 +00:00
return errLast
2013-05-08 16:28:11 +00:00
}
2014-03-18 17:38:56 +00:00
if since != "" {
if container . ID == sinceCont . ID {
2014-06-30 06:52:11 +00:00
return errLast
2014-03-18 17:38:56 +00:00
}
2013-05-06 09:31:22 +00:00
}
2013-05-10 00:50:56 +00:00
displayed ++
2014-01-16 22:00:18 +00:00
out := & engine . Env { }
2014-01-29 20:31:49 +00:00
out . Set ( "Id" , container . ID )
2014-01-16 22:00:18 +00:00
out . SetList ( "Names" , names [ container . ID ] )
2014-04-17 21:43:01 +00:00
out . Set ( "Image" , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2014-03-07 20:07:17 +00:00
if len ( container . Args ) > 0 {
2014-04-14 07:08:54 +00:00
args := [ ] string { }
for _ , arg := range container . Args {
if strings . Contains ( arg , " " ) {
args = append ( args , fmt . Sprintf ( "'%s'" , arg ) )
} else {
args = append ( args , arg )
}
}
argsAsString := strings . Join ( args , " " )
out . Set ( "Command" , fmt . Sprintf ( "\"%s %s\"" , container . Path , argsAsString ) )
2014-03-07 20:07:17 +00:00
} else {
out . Set ( "Command" , fmt . Sprintf ( "\"%s\"" , container . Path ) )
}
2014-01-16 22:00:18 +00:00
out . SetInt64 ( "Created" , container . Created . Unix ( ) )
out . Set ( "Status" , container . State . String ( ) )
str , err := container . NetworkSettings . PortMappingAPI ( ) . ToListString ( )
if err != nil {
2014-06-30 06:52:11 +00:00
return err
2014-01-16 22:00:18 +00:00
}
out . Set ( "Ports" , str )
if size {
sizeRw , sizeRootFs := container . GetSize ( )
out . SetInt64 ( "SizeRw" , sizeRw )
out . SetInt64 ( "SizeRootFs" , sizeRootFs )
}
outs . Add ( out )
2014-06-30 06:52:11 +00:00
return nil
}
for _ , container := range srv . daemon . List ( ) {
if err := writeCont ( container ) ; err != nil {
if err != errLast {
return job . Error ( err )
}
break
}
2013-10-05 02:25:15 +00:00
}
2014-01-16 22:00:18 +00:00
outs . ReverseSort ( )
if _ , err := outs . WriteListTo ( job . Stdout ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-10-05 02:25:15 +00:00
}
2014-01-16 22:00:18 +00:00
return engine . StatusOK
2013-10-05 02:25:15 +00:00
}
2014-01-16 22:00:18 +00:00
2013-12-12 01:03:48 +00:00
func ( srv * Server ) ContainerCommit ( job * engine . Job ) engine . Status {
2013-12-13 22:29:27 +00:00
if len ( job . Args ) != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Not enough arguments. Usage: %s CONTAINER\n" , job . Name )
2013-12-12 01:03:48 +00:00
}
2013-12-13 22:29:27 +00:00
name := job . Args [ 0 ]
2013-12-12 01:03:48 +00:00
2014-04-17 21:43:01 +00:00
container := srv . daemon . Get ( name )
2013-05-07 17:23:50 +00:00
if container == nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-12-12 01:03:48 +00:00
}
2014-05-19 22:04:51 +00:00
var (
config = container . Config
newConfig runconfig . Config
)
2014-01-30 19:00:18 +00:00
if err := job . GetenvJson ( "config" , & newConfig ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-07 17:23:50 +00:00
}
2013-12-12 01:03:48 +00:00
2014-01-30 19:00:18 +00:00
if err := runconfig . Merge ( & newConfig , config ) ; err != nil {
return job . Error ( err )
}
2014-06-08 06:37:31 +00:00
img , err := srv . daemon . Commit ( container , job . Getenv ( "repo" ) , job . Getenv ( "tag" ) , job . Getenv ( "comment" ) , job . Getenv ( "author" ) , job . GetenvBool ( "pause" ) , & newConfig )
2013-05-06 09:31:22 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
2013-12-12 01:03:48 +00:00
job . Printf ( "%s\n" , img . ID )
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-12-12 01:52:41 +00:00
func ( srv * Server ) ImageTag ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 2 && len ( job . Args ) != 3 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s IMAGE REPOSITORY [TAG]\n" , job . Name )
2013-05-06 09:31:22 +00:00
}
2013-12-12 01:52:41 +00:00
var tag string
if len ( job . Args ) == 3 {
tag = job . Args [ 2 ]
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Repositories ( ) . Set ( job . Args [ 1 ] , tag , job . Args [ 0 ] , job . GetenvBool ( "force" ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-12-12 01:52:41 +00:00
}
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-07-02 22:27:22 +00:00
func ( srv * Server ) pullImage ( r * registry . Registry , out io . Writer , imgID , endpoint string , token [ ] string , sf * utils . StreamFormatter ) error {
history , err := r . GetRemoteHistory ( imgID , endpoint , token )
2013-05-15 01:41:39 +00:00
if err != nil {
2013-05-06 09:31:22 +00:00
return err
}
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( imgID ) , "Pulling dependent layers" , nil ) )
2013-05-15 01:41:39 +00:00
// FIXME: Try to stream the images?
// FIXME: Launch the getRemoteImage() in goroutines
2013-08-22 11:23:05 +00:00
2013-11-12 19:48:35 +00:00
for i := len ( history ) - 1 ; i >= 0 ; i -- {
id := history [ i ]
2013-08-22 11:23:05 +00:00
// ensure no two downloads of the same layer happen at the same time
2013-11-20 21:51:05 +00:00
if c , err := srv . poolAdd ( "pull" , "layer:" + id ) ; err != nil {
2014-06-02 20:47:07 +00:00
utils . Debugf ( "Image (id: %s) pull is already running, skipping: %v" , id , err )
2013-11-20 21:51:05 +00:00
<- c
2013-08-22 11:23:05 +00:00
}
defer srv . poolRemove ( "pull" , "layer:" + id )
2014-04-17 21:43:01 +00:00
if ! srv . daemon . Graph ( ) . Exists ( id ) {
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Pulling metadata" , nil ) )
2014-02-28 16:42:20 +00:00
var (
imgJSON [ ] byte
imgSize int
err error
img * image . Image
)
retries := 5
for j := 1 ; j <= retries ; j ++ {
imgJSON , imgSize , err = r . GetRemoteImageJSON ( id , endpoint , token )
if err != nil && j == retries {
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error pulling dependent layers" , nil ) )
return err
} else if err != nil {
time . Sleep ( time . Duration ( j ) * 500 * time . Millisecond )
continue
}
img , err = image . NewImgJSON ( imgJSON )
if err != nil && j == retries {
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error pulling dependent layers" , nil ) )
return fmt . Errorf ( "Failed to parse json: %s" , err )
} else if err != nil {
time . Sleep ( time . Duration ( j ) * 500 * time . Millisecond )
continue
} else {
break
}
2013-05-15 01:41:39 +00:00
}
2014-05-23 06:58:56 +00:00
for j := 1 ; j <= retries ; j ++ {
// Get the layer
status := "Pulling fs layer"
if j > 1 {
status = fmt . Sprintf ( "Pulling fs layer [retries: %d]" , j )
}
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , status , nil ) )
2014-03-26 00:33:17 +00:00
layer , err := r . GetRemoteImageLayer ( img . ID , endpoint , token , int64 ( imgSize ) )
2014-05-23 06:58:56 +00:00
if uerr , ok := err . ( * url . Error ) ; ok {
err = uerr . Err
}
if terr , ok := err . ( net . Error ) ; ok && terr . Timeout ( ) && j < retries {
time . Sleep ( time . Duration ( j ) * 500 * time . Millisecond )
continue
} else if err != nil {
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error pulling dependent layers" , nil ) )
return err
}
defer layer . Close ( )
err = srv . daemon . Graph ( ) . Register ( imgJSON ,
utils . ProgressReader ( layer , imgSize , out , sf , false , utils . TruncateID ( id ) , "Downloading" ) ,
img )
if terr , ok := err . ( net . Error ) ; ok && terr . Timeout ( ) && j < retries {
time . Sleep ( time . Duration ( j ) * 500 * time . Millisecond )
continue
} else if err != nil {
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error downloading dependent layers" , nil ) )
return err
} else {
break
}
2013-05-15 01:41:39 +00:00
}
}
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Download complete" , nil ) )
2013-08-22 11:23:05 +00:00
2013-05-15 01:41:39 +00:00
}
2013-05-06 09:31:22 +00:00
return nil
}
2013-10-22 18:49:13 +00:00
func ( srv * Server ) pullRepository ( r * registry . Registry , out io . Writer , localName , remoteName , askedTag string , sf * utils . StreamFormatter , parallel bool ) error {
2013-07-24 17:10:59 +00:00
out . Write ( sf . FormatStatus ( "" , "Pulling repository %s" , localName ) )
2013-05-15 01:41:39 +00:00
2013-10-22 18:49:13 +00:00
repoData , err := r . GetRepositoryData ( remoteName )
2013-05-06 11:34:31 +00:00
if err != nil {
2014-07-15 21:56:48 +00:00
if strings . Contains ( err . Error ( ) , "HTTP code: 404" ) {
return fmt . Errorf ( "Error: image %s not found" , remoteName )
} else {
// Unexpected HTTP error
return err
}
2013-05-15 01:41:39 +00:00
}
utils . Debugf ( "Retrieving the tag list" )
2013-07-09 00:26:50 +00:00
tagsList , err := r . GetRemoteTags ( repoData . Endpoints , remoteName , repoData . Tokens )
2013-05-15 01:41:39 +00:00
if err != nil {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "%v" , err )
2013-05-15 01:41:39 +00:00
return err
}
2013-05-24 17:37:34 +00:00
2013-07-05 19:20:58 +00:00
for tag , id := range tagsList {
repoData . ImgList [ id ] = & registry . ImgData {
ID : id ,
Tag : tag ,
Checksum : "" ,
2013-05-24 17:37:34 +00:00
}
}
2013-05-16 19:29:16 +00:00
utils . Debugf ( "Registering tags" )
2013-05-24 17:37:34 +00:00
// If no tag has been specified, pull them all
2013-05-16 19:29:16 +00:00
if askedTag == "" {
for tag , id := range tagsList {
repoData . ImgList [ id ] . Tag = tag
}
} else {
// Otherwise, check that the tag exists and use only that one
2013-06-04 13:51:12 +00:00
id , exists := tagsList [ askedTag ]
if ! exists {
2013-07-09 00:26:50 +00:00
return fmt . Errorf ( "Tag %s not found in repository %s" , askedTag , localName )
2013-05-16 19:29:16 +00:00
}
2013-06-04 13:51:12 +00:00
repoData . ImgList [ id ] . Tag = askedTag
2013-05-15 01:41:39 +00:00
}
2013-07-24 15:41:34 +00:00
errors := make ( chan error )
for _ , image := range repoData . ImgList {
2013-07-30 12:09:07 +00:00
downloadImage := func ( img * registry . ImgData ) {
2013-07-24 15:41:34 +00:00
if askedTag != "" && img . Tag != askedTag {
utils . Debugf ( "(%s) does not match %s (id: %s), skipping" , img . Tag , askedTag , img . ID )
2013-08-22 11:23:05 +00:00
if parallel {
errors <- nil
}
2013-07-24 15:41:34 +00:00
return
}
2013-07-02 16:25:06 +00:00
2013-07-24 15:41:34 +00:00
if img . Tag == "" {
utils . Debugf ( "Image (id: %s) present in this repository but untagged, skipping" , img . ID )
2013-08-22 11:23:05 +00:00
if parallel {
errors <- nil
}
return
}
// ensure no two downloads of the same image happen at the same time
2013-12-05 22:41:56 +00:00
if c , err := srv . poolAdd ( "pull" , "img:" + img . ID ) ; err != nil {
if c != nil {
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , "Layer already being pulled by another client. Waiting." , nil ) )
<- c
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , "Download complete" , nil ) )
} else {
2014-06-02 20:47:07 +00:00
utils . Debugf ( "Image (id: %s) pull is already running, skipping: %v" , img . ID , err )
2013-12-05 22:41:56 +00:00
}
2013-08-22 11:23:05 +00:00
if parallel {
errors <- nil
}
2013-07-24 15:41:34 +00:00
return
2013-05-15 01:41:39 +00:00
}
2013-08-22 11:23:05 +00:00
defer srv . poolRemove ( "pull" , "img:" + img . ID )
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , fmt . Sprintf ( "Pulling image (%s) from %s" , img . Tag , localName ) , nil ) )
2013-07-24 15:41:34 +00:00
success := false
2013-08-22 11:23:05 +00:00
var lastErr error
2013-07-24 15:41:34 +00:00
for _ , ep := range repoData . Endpoints {
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , fmt . Sprintf ( "Pulling image (%s) from %s, endpoint: %s" , img . Tag , localName , ep ) , nil ) )
2013-07-24 15:41:34 +00:00
if err := srv . pullImage ( r , out , img . ID , ep , repoData . Tokens , sf ) ; err != nil {
2014-04-15 21:35:36 +00:00
// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
2013-08-22 11:23:05 +00:00
// As the error is also given to the output stream the user will see the error.
lastErr = err
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , fmt . Sprintf ( "Error pulling image (%s) from %s, endpoint: %s, %s" , img . Tag , localName , ep , err ) , nil ) )
2013-07-24 15:41:34 +00:00
continue
}
success = true
break
}
if ! success {
2014-06-18 11:08:33 +00:00
err := fmt . Errorf ( "Error pulling image (%s) from %s, %v" , img . Tag , localName , lastErr )
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , err . Error ( ) , nil ) )
2013-08-22 11:23:05 +00:00
if parallel {
2014-06-18 11:08:33 +00:00
errors <- err
2013-08-22 11:23:05 +00:00
return
}
}
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , "Download complete" , nil ) )
2013-08-22 11:23:05 +00:00
if parallel {
errors <- nil
2013-07-24 15:41:34 +00:00
}
2013-05-15 01:41:39 +00:00
}
2013-07-02 16:25:06 +00:00
2013-07-30 12:09:07 +00:00
if parallel {
go downloadImage ( image )
} else {
downloadImage ( image )
2013-07-02 16:25:06 +00:00
}
2013-07-24 15:41:34 +00:00
}
2013-07-30 12:09:07 +00:00
if parallel {
2013-08-22 11:23:05 +00:00
var lastError error
2013-07-30 12:09:07 +00:00
for i := 0 ; i < len ( repoData . ImgList ) ; i ++ {
if err := <- errors ; err != nil {
2013-08-22 11:23:05 +00:00
lastError = err
2013-05-15 01:41:39 +00:00
}
}
2013-08-22 11:23:05 +00:00
if lastError != nil {
return lastError
}
2013-07-24 15:41:34 +00:00
2013-08-22 11:23:05 +00:00
}
2013-05-15 01:41:39 +00:00
for tag , id := range tagsList {
2013-05-21 00:30:33 +00:00
if askedTag != "" && tag != askedTag {
continue
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Repositories ( ) . Set ( localName , tag , id , true ) ; err != nil {
2013-05-15 01:41:39 +00:00
return err
}
}
return nil
}
2013-11-20 21:51:05 +00:00
func ( srv * Server ) poolAdd ( kind , key string ) ( chan struct { } , error ) {
2013-07-02 22:46:32 +00:00
srv . Lock ( )
defer srv . Unlock ( )
2013-06-17 23:10:00 +00:00
2013-11-20 21:51:05 +00:00
if c , exists := srv . pullingPool [ key ] ; exists {
return c , fmt . Errorf ( "pull %s is already in progress" , key )
2013-07-17 20:39:36 +00:00
}
2013-11-20 21:51:05 +00:00
if c , exists := srv . pushingPool [ key ] ; exists {
return c , fmt . Errorf ( "push %s is already in progress" , key )
2013-06-17 23:10:00 +00:00
}
2013-11-20 21:51:05 +00:00
c := make ( chan struct { } )
2013-06-17 23:10:00 +00:00
switch kind {
case "pull" :
2013-11-20 21:51:05 +00:00
srv . pullingPool [ key ] = c
2013-06-17 23:10:00 +00:00
case "push" :
2013-11-20 21:51:05 +00:00
srv . pushingPool [ key ] = c
2013-06-17 23:10:00 +00:00
default :
2013-11-20 21:51:05 +00:00
return nil , fmt . Errorf ( "Unknown pool type" )
2013-06-17 23:10:00 +00:00
}
2013-11-20 21:51:05 +00:00
return c , nil
2013-06-17 23:10:00 +00:00
}
func ( srv * Server ) poolRemove ( kind , key string ) error {
2013-11-25 18:58:17 +00:00
srv . Lock ( )
defer srv . Unlock ( )
2013-06-17 23:10:00 +00:00
switch kind {
case "pull" :
2013-11-20 21:51:05 +00:00
if c , exists := srv . pullingPool [ key ] ; exists {
close ( c )
delete ( srv . pullingPool , key )
}
2013-06-17 23:10:00 +00:00
case "push" :
2013-11-20 21:51:05 +00:00
if c , exists := srv . pushingPool [ key ] ; exists {
close ( c )
delete ( srv . pushingPool , key )
}
2013-06-17 23:10:00 +00:00
default :
2013-08-12 17:53:06 +00:00
return fmt . Errorf ( "Unknown pool type" )
2013-06-17 23:10:00 +00:00
}
return nil
}
2014-01-22 21:35:35 +00:00
func ( srv * Server ) ImagePull ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 1 && n != 2 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s IMAGE [TAG]" , job . Name )
2014-01-22 21:35:35 +00:00
}
var (
localName = job . Args [ 0 ]
tag string
sf = utils . NewStreamFormatter ( job . GetenvBool ( "json" ) )
2014-05-07 22:59:13 +00:00
authConfig = & registry . AuthConfig { }
2014-01-22 21:35:35 +00:00
metaHeaders map [ string ] [ ] string
)
if len ( job . Args ) > 1 {
tag = job . Args [ 1 ]
}
2014-05-07 22:59:13 +00:00
job . GetenvJson ( "authConfig" , authConfig )
2014-06-04 09:54:06 +00:00
job . GetenvJson ( "metaHeaders" , & metaHeaders )
2013-11-27 12:18:01 +00:00
2013-11-27 16:53:36 +00:00
c , err := srv . poolAdd ( "pull" , localName + ":" + tag )
if err != nil {
if c != nil {
// Another pull of the same repository is already taking place; just wait for it to finish
2014-01-24 00:00:07 +00:00
job . Stdout . Write ( sf . FormatStatus ( "" , "Repository %s already being pulled by another client. Waiting." , localName ) )
2013-11-27 16:53:36 +00:00
<- c
2014-01-22 21:35:35 +00:00
return engine . StatusOK
2013-11-27 16:53:36 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-06-17 23:10:00 +00:00
}
2013-07-09 00:26:50 +00:00
defer srv . poolRemove ( "pull" , localName + ":" + tag )
2013-06-17 23:10:00 +00:00
2013-07-05 19:20:58 +00:00
// Resolve the Repository name from fqn to endpoint + name
2014-02-20 22:57:58 +00:00
hostname , remoteName , err := registry . ResolveRepositoryName ( localName )
if err != nil {
return job . Error ( err )
}
2014-05-07 22:59:13 +00:00
endpoint , err := registry . ExpandAndVerifyRegistryUrl ( hostname )
2013-07-05 19:20:58 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-06-17 23:10:00 +00:00
}
2013-07-05 19:20:58 +00:00
2014-06-07 23:48:25 +00:00
r , err := registry . NewRegistry ( authConfig , registry . HTTPRequestFactory ( metaHeaders ) , endpoint , true )
2013-10-22 18:49:13 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-10-22 18:49:13 +00:00
}
2014-03-11 00:16:58 +00:00
if endpoint == registry . IndexServerAddress ( ) {
2013-07-09 18:30:12 +00:00
// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar"
localName = remoteName
}
2013-06-17 23:10:00 +00:00
2014-01-24 00:00:07 +00:00
if err = srv . pullRepository ( r , job . Stdout , localName , remoteName , tag , sf , job . GetenvBool ( "parallel" ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-09-03 18:45:49 +00:00
}
2013-06-05 22:13:01 +00:00
2014-01-22 21:35:35 +00:00
return engine . StatusOK
2013-05-15 01:41:39 +00:00
}
2013-05-15 03:27:15 +00:00
// Retrieve the all the images to be uploaded in the correct order
2014-04-01 01:50:10 +00:00
func ( srv * Server ) getImageList ( localRepo map [ string ] string , requestedTag string ) ( [ ] string , map [ string ] [ ] string , error ) {
2013-12-09 14:58:04 +00:00
var (
2014-01-08 10:37:22 +00:00
imageList [ ] string
imagesSeen map [ string ] bool = make ( map [ string ] bool )
2013-12-09 14:58:04 +00:00
tagsByImage map [ string ] [ ] string = make ( map [ string ] [ ] string )
)
2013-05-15 03:27:15 +00:00
for tag , id := range localRepo {
2014-04-01 01:50:10 +00:00
if requestedTag != "" && requestedTag != tag {
continue
}
2013-12-09 17:01:53 +00:00
var imageListForThisTag [ ] string
2013-12-09 14:58:04 +00:00
tagsByImage [ id ] = append ( tagsByImage [ id ] , tag )
2014-04-17 21:43:01 +00:00
for img , err := srv . daemon . Graph ( ) . Get ( id ) ; img != nil ; img , err = img . GetParent ( ) {
2013-09-04 00:20:50 +00:00
if err != nil {
2013-12-09 17:01:53 +00:00
return nil , nil , err
2013-09-04 00:20:50 +00:00
}
2013-12-09 14:58:04 +00:00
if imagesSeen [ img . ID ] {
// This image is already on the list, we can ignore it and all its parents
break
2013-05-15 03:27:15 +00:00
}
2013-09-04 00:20:50 +00:00
2013-12-09 14:58:04 +00:00
imagesSeen [ img . ID ] = true
imageListForThisTag = append ( imageListForThisTag , img . ID )
2013-12-09 17:01:32 +00:00
}
2013-09-04 00:20:50 +00:00
2013-12-09 17:01:53 +00:00
// reverse the image list for this tag (so the "most"-parent image is first)
2014-01-08 10:37:22 +00:00
for i , j := 0 , len ( imageListForThisTag ) - 1 ; i < j ; i , j = i + 1 , j - 1 {
2013-12-09 17:01:53 +00:00
imageListForThisTag [ i ] , imageListForThisTag [ j ] = imageListForThisTag [ j ] , imageListForThisTag [ i ]
2013-09-04 00:20:50 +00:00
}
2013-12-09 17:01:53 +00:00
// append to main image list
imageList = append ( imageList , imageListForThisTag ... )
2013-09-04 00:20:50 +00:00
}
2014-04-01 01:50:10 +00:00
if len ( imageList ) == 0 {
return nil , nil , fmt . Errorf ( "No images found for the requested repository / tag" )
}
2013-12-09 14:58:04 +00:00
utils . Debugf ( "Image list: %v" , imageList )
utils . Debugf ( "Tags by image: %v" , tagsByImage )
return imageList , tagsByImage , nil
2013-05-15 03:27:15 +00:00
}
2014-04-01 01:50:10 +00:00
func ( srv * Server ) pushRepository ( r * registry . Registry , out io . Writer , localName , remoteName string , localRepo map [ string ] string , tag string , sf * utils . StreamFormatter ) error {
2013-05-20 17:58:35 +00:00
out = utils . NewWriteFlusher ( out )
2013-12-09 14:58:04 +00:00
utils . Debugf ( "Local repo: %s" , localRepo )
2014-04-01 01:50:10 +00:00
imgList , tagsByImage , err := srv . getImageList ( localRepo , tag )
2013-05-15 03:27:15 +00:00
if err != nil {
return err
}
2013-12-09 14:58:04 +00:00
2013-07-24 17:10:59 +00:00
out . Write ( sf . FormatStatus ( "" , "Sending image list" ) )
2013-05-15 03:27:15 +00:00
2014-04-01 01:50:10 +00:00
var (
repoData * registry . RepositoryData
imageIndex [ ] * registry . ImgData
)
2013-12-09 14:58:04 +00:00
2014-01-20 21:39:35 +00:00
for _ , imgId := range imgList {
if tags , exists := tagsByImage [ imgId ] ; exists {
// If an image has tags you must add an entry in the image index
// for each tag
for _ , tag := range tags {
imageIndex = append ( imageIndex , & registry . ImgData {
ID : imgId ,
Tag : tag ,
} )
}
} else {
// If the image does not have a tag it still needs to be sent to the
// registry with an empty tag so that it is accociated with the repository
2013-12-09 14:58:04 +00:00
imageIndex = append ( imageIndex , & registry . ImgData {
2014-01-08 10:37:22 +00:00
ID : imgId ,
2014-01-20 21:39:35 +00:00
Tag : "" ,
2013-12-09 14:58:04 +00:00
} )
2014-01-20 21:39:35 +00:00
2013-12-09 14:58:04 +00:00
}
}
2014-01-20 21:39:35 +00:00
utils . Debugf ( "Preparing to push %s with the following images and tags\n" , localRepo )
for _ , data := range imageIndex {
utils . Debugf ( "Pushing ID: %s with Tag: %s\n" , data . ID , data . Tag )
}
// Register all the images in a repository with the registry
// If an image is not in this list it will not be associated with the repository
2013-12-09 14:58:04 +00:00
repoData , err = r . PushImageJSONIndex ( remoteName , imageIndex , false , nil )
2013-05-15 03:27:15 +00:00
if err != nil {
return err
}
2014-04-01 01:50:10 +00:00
nTag := 1
if tag == "" {
nTag = len ( localRepo )
}
2013-05-15 03:27:15 +00:00
for _ , ep := range repoData . Endpoints {
2014-04-01 01:50:10 +00:00
out . Write ( sf . FormatStatus ( "" , "Pushing repository %s (%d tags)" , localName , nTag ) )
2013-12-09 14:58:04 +00:00
for _ , imgId := range imgList {
if r . LookupRemoteImage ( imgId , ep , repoData . Tokens ) {
out . Write ( sf . FormatStatus ( "" , "Image %s already pushed, skipping" , utils . TruncateID ( imgId ) ) )
2013-12-09 17:01:32 +00:00
} else {
2013-12-09 17:01:53 +00:00
if _ , err := srv . pushImage ( r , out , remoteName , imgId , ep , repoData . Tokens , sf ) ; err != nil {
2013-09-04 00:20:50 +00:00
// FIXME: Continue on error?
return err
}
2013-12-09 17:01:53 +00:00
}
2013-11-18 23:35:56 +00:00
2013-12-09 17:01:53 +00:00
for _ , tag := range tagsByImage [ imgId ] {
out . Write ( sf . FormatStatus ( "" , "Pushing tag for rev [%s] on {%s}" , utils . TruncateID ( imgId ) , ep + "repositories/" + remoteName + "/tags/" + tag ) )
2013-11-18 23:35:56 +00:00
2013-12-09 17:01:53 +00:00
if err := r . PushRegistryTag ( remoteName , imgId , tag , ep , repoData . Tokens ) ; err != nil {
2013-09-11 17:39:33 +00:00
return err
}
2013-05-15 18:30:40 +00:00
}
2013-05-15 03:27:15 +00:00
}
}
2013-12-09 14:58:04 +00:00
if _ , err := r . PushImageJSONIndex ( remoteName , imageIndex , true , repoData . Endpoints ) ; err != nil {
2013-05-15 03:27:15 +00:00
return err
}
2013-05-24 17:37:34 +00:00
2013-05-15 18:30:40 +00:00
return nil
2013-05-15 03:27:15 +00:00
}
2013-07-22 23:44:34 +00:00
func ( srv * Server ) pushImage ( r * registry . Registry , out io . Writer , remote , imgID , ep string , token [ ] string , sf * utils . StreamFormatter ) ( checksum string , err error ) {
2013-05-20 17:58:35 +00:00
out = utils . NewWriteFlusher ( out )
2014-04-17 21:43:01 +00:00
jsonRaw , err := ioutil . ReadFile ( path . Join ( srv . daemon . Graph ( ) . Root , imgID , "json" ) )
2013-05-15 03:27:15 +00:00
if err != nil {
2013-10-30 18:45:11 +00:00
return "" , fmt . Errorf ( "Cannot retrieve the path for {%s}: %s" , imgID , err )
2013-05-15 03:27:15 +00:00
}
2013-12-10 18:57:16 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( imgID ) , "Pushing" , nil ) )
2013-05-15 03:27:15 +00:00
imgData := & registry . ImgData {
2013-07-17 19:13:22 +00:00
ID : imgID ,
2013-05-15 03:27:15 +00:00
}
2013-05-15 20:22:57 +00:00
// Send the json
2013-06-04 18:00:22 +00:00
if err := r . PushImageJSONRegistry ( imgData , jsonRaw , ep , token ) ; err != nil {
2013-05-15 20:22:57 +00:00
if err == registry . ErrAlreadyExists {
2013-12-10 18:57:16 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( imgData . ID ) , "Image already pushed, skipping" , nil ) )
2013-07-22 23:44:34 +00:00
return "" , nil
2013-05-15 20:22:57 +00:00
}
2013-07-22 23:44:34 +00:00
return "" , err
2013-05-15 20:22:57 +00:00
}
2014-04-17 21:43:01 +00:00
layerData , err := srv . daemon . Graph ( ) . TempLayerArchive ( imgID , archive . Uncompressed , sf , out )
2013-05-15 03:27:15 +00:00
if err != nil {
2013-07-22 23:44:34 +00:00
return "" , fmt . Errorf ( "Failed to generate layer archive: %s" , err )
2013-05-15 03:27:15 +00:00
}
2013-11-16 00:23:55 +00:00
defer os . RemoveAll ( layerData . Name ( ) )
2013-05-15 18:30:40 +00:00
// Send the layer
2014-03-19 18:34:12 +00:00
utils . Debugf ( "rendered layer for %s of [%d] size" , imgData . ID , layerData . Size )
2014-02-24 02:33:46 +00:00
checksum , checksumPayload , err := r . PushImageLayerRegistry ( imgData . ID , utils . ProgressReader ( layerData , int ( layerData . Size ) , out , sf , false , utils . TruncateID ( imgData . ID ) , "Pushing" ) , ep , token , jsonRaw )
2013-11-18 23:35:56 +00:00
if err != nil {
2013-07-22 23:44:34 +00:00
return "" , err
2013-05-06 09:31:22 +00:00
}
2013-11-18 23:35:56 +00:00
imgData . Checksum = checksum
2014-02-26 00:06:04 +00:00
imgData . ChecksumPayload = checksumPayload
2013-07-17 19:13:22 +00:00
// Send the checksum
if err := r . PushImageChecksumRegistry ( imgData , ep , token ) ; err != nil {
2013-07-22 23:44:34 +00:00
return "" , err
2013-07-17 19:13:22 +00:00
}
2013-12-19 20:32:58 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( imgData . ID ) , "Image successfully pushed" , nil ) )
2013-07-22 23:44:34 +00:00
return imgData . Checksum , nil
2013-05-06 09:31:22 +00:00
}
2013-08-12 17:53:06 +00:00
// FIXME: Allow to interrupt current push when new push of same image is done.
2014-01-23 01:33:29 +00:00
func ( srv * Server ) ImagePush ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s IMAGE" , job . Name )
2013-06-17 23:10:00 +00:00
}
2014-01-23 01:33:29 +00:00
var (
localName = job . Args [ 0 ]
sf = utils . NewStreamFormatter ( job . GetenvBool ( "json" ) )
2014-03-11 00:16:58 +00:00
authConfig = & registry . AuthConfig { }
2014-01-23 01:33:29 +00:00
metaHeaders map [ string ] [ ] string
)
2013-06-17 23:10:00 +00:00
2014-04-01 01:50:10 +00:00
tag := job . Getenv ( "tag" )
2014-01-23 01:33:29 +00:00
job . GetenvJson ( "authConfig" , authConfig )
2014-06-04 09:54:06 +00:00
job . GetenvJson ( "metaHeaders" , & metaHeaders )
2014-01-23 01:33:29 +00:00
if _ , err := srv . poolAdd ( "push" , localName ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2014-01-23 01:33:29 +00:00
}
2014-01-27 15:22:59 +00:00
defer srv . poolRemove ( "push" , localName )
2013-07-05 19:20:58 +00:00
// Resolve the Repository name from fqn to endpoint + name
2014-02-20 22:57:58 +00:00
hostname , remoteName , err := registry . ResolveRepositoryName ( localName )
if err != nil {
return job . Error ( err )
}
endpoint , err := registry . ExpandAndVerifyRegistryUrl ( hostname )
2013-07-05 22:26:08 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-06-17 23:10:00 +00:00
}
2014-04-17 21:43:01 +00:00
img , err := srv . daemon . Graph ( ) . Get ( localName )
2014-06-07 23:48:25 +00:00
r , err2 := registry . NewRegistry ( authConfig , registry . HTTPRequestFactory ( metaHeaders ) , endpoint , false )
2013-06-17 18:13:40 +00:00
if err2 != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err2 )
2013-06-17 18:13:40 +00:00
}
2013-06-05 22:12:50 +00:00
2013-05-06 11:34:31 +00:00
if err != nil {
2014-04-01 01:50:10 +00:00
reposLen := 1
if tag == "" {
2014-04-17 21:43:01 +00:00
reposLen = len ( srv . daemon . Repositories ( ) . Repositories [ localName ] )
2014-04-01 01:50:10 +00:00
}
2014-01-23 01:33:29 +00:00
job . Stdout . Write ( sf . FormatStatus ( "" , "The push refers to a repository [%s] (len: %d)" , localName , reposLen ) )
2013-05-07 23:33:12 +00:00
// If it fails, try to get the repository
2014-04-17 21:43:01 +00:00
if localRepo , exists := srv . daemon . Repositories ( ) . Repositories [ localName ] ; exists {
2014-04-01 01:50:10 +00:00
if err := srv . pushRepository ( r , job . Stdout , localName , remoteName , localRepo , tag , sf ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 11:34:31 +00:00
}
2014-01-23 01:33:29 +00:00
return engine . StatusOK
2013-05-06 11:34:31 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 11:34:31 +00:00
}
2013-07-05 19:20:58 +00:00
var token [ ] string
2014-01-23 01:33:29 +00:00
job . Stdout . Write ( sf . FormatStatus ( "" , "The push refers to an image: [%s]" , localName ) )
if _ , err := srv . pushImage ( r , job . Stdout , remoteName , img . ID , endpoint , token , sf ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 11:34:31 +00:00
}
2014-01-23 01:33:29 +00:00
return engine . StatusOK
2013-05-06 11:34:31 +00:00
}
2014-01-22 21:35:35 +00:00
func ( srv * Server ) ImageImport ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 2 && n != 3 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s SRC REPO [TAG]" , job . Name )
2014-01-22 21:35:35 +00:00
}
var (
src = job . Args [ 0 ]
repo = job . Args [ 1 ]
tag string
sf = utils . NewStreamFormatter ( job . GetenvBool ( "json" ) )
2014-02-14 11:41:46 +00:00
archive archive . ArchiveReader
2014-01-22 21:35:35 +00:00
resp * http . Response
)
if len ( job . Args ) > 2 {
tag = job . Args [ 2 ]
}
2013-05-06 09:31:22 +00:00
if src == "-" {
2014-01-22 21:35:35 +00:00
archive = job . Stdin
2013-05-06 09:31:22 +00:00
} else {
u , err := url . Parse ( src )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
if u . Scheme == "" {
u . Scheme = "http"
u . Host = src
u . Path = ""
}
2014-01-24 00:00:07 +00:00
job . Stdout . Write ( sf . FormatStatus ( "" , "Downloading from %s" , u ) )
2013-12-04 19:54:11 +00:00
resp , err = utils . Download ( u . String ( ) )
2013-05-06 09:31:22 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
2014-02-14 11:41:46 +00:00
progressReader := utils . ProgressReader ( resp . Body , int ( resp . ContentLength ) , job . Stdout , sf , true , "" , "Importing" )
defer progressReader . Close ( )
archive = progressReader
2013-05-06 09:31:22 +00:00
}
2014-04-17 21:43:01 +00:00
img , err := srv . daemon . Graph ( ) . Create ( archive , "" , "" , "Imported from " + src , "" , nil , nil )
2013-05-06 09:31:22 +00:00
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
2013-05-07 23:33:12 +00:00
// Optionally register the image at REPO/TAG
2013-05-06 09:31:22 +00:00
if repo != "" {
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Repositories ( ) . Set ( repo , tag , img . ID , true ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
}
2014-01-24 00:00:07 +00:00
job . Stdout . Write ( sf . FormatStatus ( "" , img . ID ) )
2014-01-22 21:35:35 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-11-20 07:37:03 +00:00
func ( srv * Server ) ContainerCreate ( job * engine . Job ) engine . Status {
2013-10-28 02:20:00 +00:00
var name string
if len ( job . Args ) == 1 {
name = job . Args [ 0 ]
} else if len ( job . Args ) > 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s" , job . Name )
2013-10-28 02:20:00 +00:00
}
2014-02-12 04:04:39 +00:00
config := runconfig . ContainerConfigFromJob ( job )
2013-06-14 16:46:04 +00:00
if config . Memory != 0 && config . Memory < 524288 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Minimum memory limit allowed is 512k" )
2013-06-14 16:46:04 +00:00
}
2014-04-17 21:43:01 +00:00
if config . Memory > 0 && ! srv . daemon . SystemConfig ( ) . MemoryLimit {
2014-02-28 02:40:15 +00:00
job . Errorf ( "Your kernel does not support memory limit capabilities. Limitation discarded.\n" )
2013-05-06 09:31:22 +00:00
config . Memory = 0
}
2014-04-17 21:43:01 +00:00
if config . Memory > 0 && ! srv . daemon . SystemConfig ( ) . SwapLimit {
2014-02-28 02:40:15 +00:00
job . Errorf ( "Your kernel does not support swap limit capabilities. Limitation discarded.\n" )
2013-05-06 09:31:22 +00:00
config . MemorySwap = - 1
}
2014-04-17 21:43:01 +00:00
container , buildWarnings , err := srv . daemon . Create ( config , name )
2013-05-06 09:31:22 +00:00
if err != nil {
2014-04-17 21:43:01 +00:00
if srv . daemon . Graph ( ) . IsNotExist ( err ) {
2013-08-18 03:03:54 +00:00
_ , tag := utils . ParseRepositoryTag ( config . Image )
if tag == "" {
2014-03-08 02:04:38 +00:00
tag = graph . DEFAULTTAG
2013-08-18 03:03:54 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such image: %s (tag: %s)" , config . Image , tag )
2013-05-06 09:31:22 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-05-06 09:31:22 +00:00
}
2014-04-17 21:43:01 +00:00
if ! container . Config . NetworkDisabled && srv . daemon . SystemConfig ( ) . IPv4ForwardingDisabled {
2014-02-28 02:40:15 +00:00
job . Errorf ( "IPv4 forwarding is disabled.\n" )
2014-01-29 01:17:55 +00:00
}
2014-04-17 21:43:01 +00:00
srv . LogEvent ( "create" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
// FIXME: this is necessary because daemon.Create might return a nil container
2013-11-14 06:08:08 +00:00
// with a non-nil error. This should not happen! Once it's fixed we
// can remove this workaround.
if container != nil {
job . Printf ( "%s\n" , container . ID )
}
2013-10-28 02:20:00 +00:00
for _ , warning := range buildWarnings {
2014-03-03 22:07:42 +00:00
job . Errorf ( "%s\n" , warning )
2013-10-28 02:20:00 +00:00
}
2013-11-20 07:37:03 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-07 01:34:51 +00:00
func ( srv * Server ) ContainerRestart ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
2014-01-07 01:34:51 +00:00
}
2014-01-31 02:21:59 +00:00
var (
name = job . Args [ 0 ]
t = 10
)
if job . EnvExists ( "t" ) {
t = job . GetenvInt ( "t" )
2014-01-07 01:34:51 +00:00
}
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2014-01-07 01:34:51 +00:00
if err := container . Restart ( int ( t ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Cannot restart container %s: %s\n" , name , err )
2013-05-06 09:31:22 +00:00
}
2014-04-17 21:43:01 +00:00
srv . LogEvent ( "restart" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2013-05-06 09:31:22 +00:00
} else {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s\n" , name )
2013-05-06 09:31:22 +00:00
}
2014-01-07 01:34:51 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-12-11 23:23:38 +00:00
func ( srv * Server ) ContainerDestroy ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Not enough arguments. Usage: %s CONTAINER\n" , job . Name )
2013-12-11 23:23:38 +00:00
}
name := job . Args [ 0 ]
removeVolume := job . GetenvBool ( "removeVolume" )
removeLink := job . GetenvBool ( "removeLink" )
2014-07-06 20:43:24 +00:00
stop := job . GetenvBool ( "stop" )
kill := job . GetenvBool ( "kill" )
2013-12-11 23:23:38 +00:00
2014-04-17 21:43:01 +00:00
container := srv . daemon . Get ( name )
2013-10-25 23:49:49 +00:00
2013-10-05 02:25:15 +00:00
if removeLink {
2013-10-25 23:49:49 +00:00
if container == nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such link: %s" , name )
2013-10-05 02:25:15 +00:00
}
2014-04-17 21:43:01 +00:00
name , err := daemon . GetFullContainerName ( name )
2013-11-04 17:28:40 +00:00
if err != nil {
2013-12-11 23:23:38 +00:00
job . Error ( err )
2013-11-04 17:28:40 +00:00
}
2013-10-25 23:49:49 +00:00
parent , n := path . Split ( name )
2013-10-30 18:45:11 +00:00
if parent == "/" {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Conflict, cannot remove the default name of the container" )
2013-10-30 18:45:11 +00:00
}
2014-04-17 21:43:01 +00:00
pe := srv . daemon . ContainerGraph ( ) . Get ( parent )
2013-10-29 02:19:31 +00:00
if pe == nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Cannot get parent %s for name %s" , parent , name )
2013-10-05 02:25:15 +00:00
}
2014-04-17 21:43:01 +00:00
parentContainer := srv . daemon . Get ( pe . ID ( ) )
2013-10-05 02:25:15 +00:00
2014-03-08 02:42:29 +00:00
if parentContainer != nil {
parentContainer . DisableLink ( n )
2013-10-25 23:49:49 +00:00
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . ContainerGraph ( ) . Delete ( name ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-10-05 02:25:15 +00:00
}
2013-12-11 23:23:38 +00:00
return engine . StatusOK
2013-10-05 02:25:15 +00:00
}
2013-10-29 02:19:31 +00:00
2013-10-25 23:49:49 +00:00
if container != nil {
2013-11-21 20:21:03 +00:00
if container . State . IsRunning ( ) {
2014-07-06 20:43:24 +00:00
if stop {
2014-02-06 04:10:57 +00:00
if err := container . Stop ( 5 ) ; err != nil {
return job . Errorf ( "Could not stop running container, cannot remove - %v" , err )
}
2014-07-06 20:43:24 +00:00
} else if kill {
if err := container . Kill ( ) ; err != nil {
return job . Errorf ( "Could not kill running container, cannot remove - %v" , err )
}
2014-02-06 04:10:57 +00:00
} else {
2014-07-06 20:43:24 +00:00
return job . Errorf ( "You cannot remove a running container. Stop the container before attempting removal or use -s or -k" )
2014-02-06 04:10:57 +00:00
}
2013-06-20 15:45:30 +00:00
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Destroy ( container ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Cannot destroy container %s: %s" , name , err )
2013-05-06 09:31:22 +00:00
}
2014-04-17 21:43:01 +00:00
srv . LogEvent ( "destroy" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2013-05-06 09:52:15 +00:00
2013-05-10 02:19:55 +00:00
if removeVolume {
2014-02-03 21:18:10 +00:00
var (
volumes = make ( map [ string ] struct { } )
binds = make ( map [ string ] struct { } )
2014-04-17 21:43:01 +00:00
usedVolumes = make ( map [ string ] * daemon . Container )
2014-02-03 21:18:10 +00:00
)
// the volume id is always the base of the path
getVolumeId := func ( p string ) string {
return filepath . Base ( strings . TrimSuffix ( p , "/layer" ) )
}
// populate bind map so that they can be skipped and not removed
2014-03-08 02:42:29 +00:00
for _ , bind := range container . HostConfig ( ) . Binds {
2014-02-03 21:18:10 +00:00
source := strings . Split ( bind , ":" ) [ 0 ]
// TODO: refactor all volume stuff, all of it
2014-04-14 12:43:01 +00:00
// it is very important that we eval the link or comparing the keys to container.Volumes will not work
//
// eval symlink can fail, ref #5244 if we receive an is not exist error we can ignore it
2014-02-03 21:18:10 +00:00
p , err := filepath . EvalSymlinks ( source )
2014-04-14 12:43:01 +00:00
if err != nil && ! os . IsNotExist ( err ) {
2014-02-03 21:18:10 +00:00
return job . Error ( err )
}
2014-04-14 12:43:01 +00:00
if p != "" {
source = p
}
2014-02-03 21:18:10 +00:00
binds [ source ] = struct { } { }
}
// Store all the deleted containers volumes
for _ , volumeId := range container . Volumes {
// Skip the volumes mounted from external
// bind mounts here will will be evaluated for a symlink
if _ , exists := binds [ volumeId ] ; exists {
continue
}
volumeId = getVolumeId ( volumeId )
volumes [ volumeId ] = struct { } { }
}
2013-05-06 09:52:15 +00:00
// Retrieve all volumes from all remaining containers
2014-04-17 21:43:01 +00:00
for _ , container := range srv . daemon . List ( ) {
2013-05-06 09:52:15 +00:00
for _ , containerVolumeId := range container . Volumes {
2014-02-03 21:18:10 +00:00
containerVolumeId = getVolumeId ( containerVolumeId )
2013-05-06 09:52:15 +00:00
usedVolumes [ containerVolumeId ] = container
}
}
for volumeId := range volumes {
// If the requested volu
if c , exists := usedVolumes [ volumeId ] ; exists {
2013-06-04 18:00:22 +00:00
log . Printf ( "The volume %s is used by the container %s. Impossible to remove it. Skipping.\n" , volumeId , c . ID )
2013-05-06 09:52:15 +00:00
continue
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Volumes ( ) . Delete ( volumeId ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Error calling volumes.Delete(%q): %v" , volumeId , err )
2013-05-06 09:52:15 +00:00
}
}
}
2013-05-06 09:31:22 +00:00
} else {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-05-06 09:31:22 +00:00
}
2013-12-11 23:23:38 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-03-14 01:02:28 +00:00
func ( srv * Server ) DeleteImage ( name string , imgs * engine . Table , first , force , noprune bool ) error {
2013-12-16 21:29:43 +00:00
var (
2014-01-17 02:40:33 +00:00
repoName , tag string
tags = [ ] string { }
2014-04-08 01:00:32 +00:00
tagDeleted bool
2013-12-16 21:29:43 +00:00
)
2013-07-17 15:48:53 +00:00
2014-02-18 03:03:19 +00:00
repoName , tag = utils . ParseRepositoryTag ( name )
if tag == "" {
2014-03-08 02:04:38 +00:00
tag = graph . DEFAULTTAG
2014-02-18 03:03:19 +00:00
}
2014-04-17 21:43:01 +00:00
img , err := srv . daemon . Repositories ( ) . LookupImage ( name )
2014-01-17 02:40:33 +00:00
if err != nil {
2014-04-17 21:43:01 +00:00
if r , _ := srv . daemon . Repositories ( ) . Get ( repoName ) ; r != nil {
2014-02-18 03:03:19 +00:00
return fmt . Errorf ( "No such image: %s:%s" , repoName , tag )
}
2014-02-11 02:44:17 +00:00
return fmt . Errorf ( "No such image: %s" , name )
2014-01-17 02:40:33 +00:00
}
2014-02-18 03:03:19 +00:00
if strings . Contains ( img . ID , name ) {
repoName = ""
tag = ""
2014-01-17 02:40:33 +00:00
}
2014-04-17 21:43:01 +00:00
byParents , err := srv . daemon . Graph ( ) . ByParent ( )
2014-02-11 02:44:17 +00:00
if err != nil {
return err
2014-01-17 02:40:33 +00:00
}
2013-07-17 15:48:53 +00:00
//If delete by id, see if the id belong only to one repository
2013-10-21 23:54:02 +00:00
if repoName == "" {
2014-04-17 21:43:01 +00:00
for _ , repoAndTag := range srv . daemon . Repositories ( ) . ByID ( ) [ img . ID ] {
2013-08-14 16:59:21 +00:00
parsedRepo , parsedTag := utils . ParseRepositoryTag ( repoAndTag )
2013-10-21 23:54:02 +00:00
if repoName == "" || repoName == parsedRepo {
2013-07-17 15:48:53 +00:00
repoName = parsedRepo
2013-10-21 23:54:02 +00:00
if parsedTag != "" {
tags = append ( tags , parsedTag )
2013-07-26 09:19:26 +00:00
}
2014-02-11 02:44:17 +00:00
} else if repoName != parsedRepo && ! force {
2013-07-17 15:48:53 +00:00
// the id belongs to multiple repos, like base:latest and user:test,
// in that case return conflict
2014-02-11 02:44:17 +00:00
return fmt . Errorf ( "Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force" , name )
2013-07-17 15:48:53 +00:00
}
}
2013-10-21 23:54:02 +00:00
} else {
tags = append ( tags , tag )
2013-07-17 15:48:53 +00:00
}
2013-12-16 21:29:43 +00:00
2014-02-19 01:34:37 +00:00
if ! first && len ( tags ) > 0 {
return nil
}
2013-07-17 15:48:53 +00:00
//Untag the current image
2013-10-21 23:54:02 +00:00
for _ , tag := range tags {
2014-04-08 01:00:32 +00:00
tagDeleted , err = srv . daemon . Repositories ( ) . Delete ( repoName , tag )
2013-10-21 23:54:02 +00:00
if err != nil {
2014-02-11 02:44:17 +00:00
return err
2013-10-21 23:54:02 +00:00
}
if tagDeleted {
2014-01-17 02:40:33 +00:00
out := & engine . Env { }
2014-02-11 02:44:17 +00:00
out . Set ( "Untagged" , repoName + ":" + tag )
2014-01-17 02:40:33 +00:00
imgs . Add ( out )
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "untag" , img . ID , "" )
2013-10-21 23:54:02 +00:00
}
2013-05-30 19:30:21 +00:00
}
2014-04-17 21:43:01 +00:00
tags = srv . daemon . Repositories ( ) . ByID ( ) [ img . ID ]
2014-02-11 02:44:17 +00:00
if ( len ( tags ) <= 1 && repoName == "" ) || len ( tags ) == 0 {
if len ( byParents [ img . ID ] ) == 0 {
2014-04-08 01:00:32 +00:00
if err := srv . canDeleteImage ( img . ID , force , tagDeleted ) ; err != nil {
2014-02-11 02:44:17 +00:00
return err
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Repositories ( ) . DeleteAll ( img . ID ) ; err != nil {
2014-02-11 02:44:17 +00:00
return err
2013-05-30 22:53:45 +00:00
}
2014-04-17 21:43:01 +00:00
if err := srv . daemon . Graph ( ) . Delete ( img . ID ) ; err != nil {
2014-02-11 02:44:17 +00:00
return err
2013-05-30 19:30:21 +00:00
}
2014-02-11 02:44:17 +00:00
out := & engine . Env { }
out . Set ( "Deleted" , img . ID )
imgs . Add ( out )
srv . LogEvent ( "delete" , img . ID , "" )
2014-03-14 01:02:28 +00:00
if img . Parent != "" && ! noprune {
err := srv . DeleteImage ( img . Parent , imgs , false , force , noprune )
2014-02-11 02:44:17 +00:00
if first {
return err
}
}
2013-05-30 19:30:21 +00:00
}
}
2014-02-11 02:44:17 +00:00
return nil
2013-05-30 19:30:21 +00:00
}
2014-01-17 02:40:33 +00:00
func ( srv * Server ) ImageDelete ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s IMAGE" , job . Name )
2013-05-30 22:53:45 +00:00
}
2014-02-15 00:10:57 +00:00
imgs := engine . NewTable ( "" , 0 )
2014-03-14 01:02:28 +00:00
if err := srv . DeleteImage ( job . Args [ 0 ] , imgs , true , job . GetenvBool ( "force" ) , job . GetenvBool ( "noprune" ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-12-16 21:29:43 +00:00
}
2014-01-20 22:10:23 +00:00
if len ( imgs . Data ) == 0 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Conflict, %s wasn't deleted" , job . Args [ 0 ] )
2014-01-20 22:10:23 +00:00
}
if _ , err := imgs . WriteListTo ( job . Stdout ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-11-08 23:01:01 +00:00
}
2014-01-17 02:40:33 +00:00
return engine . StatusOK
2013-12-21 00:26:02 +00:00
}
2013-11-08 23:01:01 +00:00
2014-04-08 01:00:32 +00:00
func ( srv * Server ) canDeleteImage ( imgID string , force , untagged bool ) error {
var message string
if untagged {
2014-04-28 19:03:31 +00:00
message = " (docker untagged the image)"
2014-04-08 01:00:32 +00:00
}
2014-04-17 21:43:01 +00:00
for _ , container := range srv . daemon . List ( ) {
parent , err := srv . daemon . Repositories ( ) . LookupImage ( container . Image )
2013-12-21 00:26:02 +00:00
if err != nil {
return err
}
2013-12-16 21:29:43 +00:00
2014-03-08 01:36:47 +00:00
if err := parent . WalkHistory ( func ( p * image . Image ) error {
2013-12-21 00:26:02 +00:00
if imgID == p . ID {
2014-04-08 01:00:32 +00:00
if container . State . IsRunning ( ) {
if force {
return fmt . Errorf ( "Conflict, cannot force delete %s because the running container %s is using it%s, stop it and retry" , utils . TruncateID ( imgID ) , utils . TruncateID ( container . ID ) , message )
}
return fmt . Errorf ( "Conflict, cannot delete %s because the running container %s is using it%s, stop it and use -f to force" , utils . TruncateID ( imgID ) , utils . TruncateID ( container . ID ) , message )
} else if ! force {
return fmt . Errorf ( "Conflict, cannot delete %s because the container %s is using it%s, use -f to force" , utils . TruncateID ( imgID ) , utils . TruncateID ( container . ID ) , message )
}
2013-12-16 21:29:43 +00:00
}
2013-12-21 00:26:02 +00:00
return nil
} ) ; err != nil {
return err
2013-12-16 21:29:43 +00:00
}
2013-10-21 23:54:02 +00:00
}
2013-12-21 00:26:02 +00:00
return nil
2013-05-06 09:31:22 +00:00
}
2014-03-08 01:36:47 +00:00
func ( srv * Server ) ImageGetCached ( imgID string , config * runconfig . Config ) ( * image . Image , error ) {
2013-05-19 17:46:24 +00:00
// Retrieve all images
2014-04-17 21:43:01 +00:00
images , err := srv . daemon . Graph ( ) . Map ( )
2013-05-19 17:46:24 +00:00
if err != nil {
return nil , err
}
// Store the tree in a map of map (map[parentId][childId])
2014-01-07 08:15:04 +00:00
imageMap := make ( map [ string ] map [ string ] struct { } )
2013-05-19 17:46:24 +00:00
for _ , img := range images {
2014-01-07 08:15:04 +00:00
if _ , exists := imageMap [ img . Parent ] ; ! exists {
imageMap [ img . Parent ] = make ( map [ string ] struct { } )
}
imageMap [ img . Parent ] [ img . ID ] = struct { } { }
2013-05-19 17:46:24 +00:00
}
2014-01-07 08:15:04 +00:00
2013-05-19 17:46:24 +00:00
// Loop on the children of the given image and check the config
2014-03-08 01:36:47 +00:00
var match * image . Image
2014-01-07 08:15:04 +00:00
for elem := range imageMap [ imgID ] {
2014-04-17 21:43:01 +00:00
img , err := srv . daemon . Graph ( ) . Get ( elem )
2013-05-19 17:46:24 +00:00
if err != nil {
return nil , err
}
2014-02-12 04:04:39 +00:00
if runconfig . Compare ( & img . ContainerConfig , config ) {
2014-01-01 16:38:25 +00:00
if match == nil || match . Created . Before ( img . Created ) {
match = img
}
2013-05-19 17:46:24 +00:00
}
}
2014-01-01 16:38:25 +00:00
return match , nil
2013-05-19 17:46:24 +00:00
}
2014-03-17 16:03:22 +00:00
func ( srv * Server ) setHostConfig ( container * daemon . Container , hostConfig * runconfig . HostConfig ) error {
// Validate the HostConfig binds. Make sure that:
// the source exists
for _ , bind := range hostConfig . Binds {
splitBind := strings . Split ( bind , ":" )
source := splitBind [ 0 ]
// ensure the source exists on the host
_ , err := os . Stat ( source )
if err != nil && os . IsNotExist ( err ) {
err = os . MkdirAll ( source , 0755 )
if err != nil {
return fmt . Errorf ( "Could not create local directory '%s' for bind mount: %s!" , source , err . Error ( ) )
}
}
}
// Register any links from the host config before starting the container
if err := srv . daemon . RegisterLinks ( container , hostConfig ) ; err != nil {
return err
}
container . SetHostConfig ( hostConfig )
container . ToDisk ( )
return nil
}
2013-11-20 07:37:03 +00:00
func ( srv * Server ) ContainerStart ( job * engine . Job ) engine . Status {
2013-10-27 02:24:01 +00:00
if len ( job . Args ) < 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s container_id" , job . Name )
2013-10-27 02:24:01 +00:00
}
2014-03-30 05:12:43 +00:00
var (
name = job . Args [ 0 ]
2014-04-17 21:43:01 +00:00
daemon = srv . daemon
container = daemon . Get ( name )
2014-03-30 05:12:43 +00:00
)
2013-10-18 22:56:52 +00:00
2013-10-28 23:58:59 +00:00
if container == nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-10-28 23:58:59 +00:00
}
2014-06-24 18:31:56 +00:00
if container . State . IsRunning ( ) {
return job . Errorf ( "Container already started" )
}
2013-10-27 02:24:01 +00:00
// If no environment was set, then no hostconfig was passed.
if len ( job . Environ ( ) ) > 0 {
2014-02-12 04:04:39 +00:00
hostConfig := runconfig . ContainerHostConfigFromJob ( job )
2014-03-17 16:03:22 +00:00
if err := srv . setHostConfig ( container , hostConfig ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-10-27 02:24:01 +00:00
}
2013-10-31 21:58:43 +00:00
}
if err := container . Start ( ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Cannot start container %s: %s" , name , err )
2013-10-05 02:25:15 +00:00
}
2014-04-17 21:43:01 +00:00
srv . LogEvent ( "start" , container . ID , daemon . Repositories ( ) . ImageName ( container . Image ) )
2013-10-05 02:25:15 +00:00
2013-11-20 07:37:03 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-11-17 03:00:16 +00:00
func ( srv * Server ) ContainerStop ( job * engine . Job ) engine . Status {
2013-12-11 23:36:50 +00:00
if len ( job . Args ) != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
2013-11-17 03:00:16 +00:00
}
2014-01-31 02:21:59 +00:00
var (
name = job . Args [ 0 ]
t = 10
)
if job . EnvExists ( "t" ) {
t = job . GetenvInt ( "t" )
2013-11-17 03:00:16 +00:00
}
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2014-06-24 18:31:56 +00:00
if ! container . State . IsRunning ( ) {
return job . Errorf ( "Container already stopped" )
}
2013-11-17 03:00:16 +00:00
if err := container . Stop ( int ( t ) ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Cannot stop container %s: %s\n" , name , err )
2013-05-06 09:31:22 +00:00
}
2014-04-17 21:43:01 +00:00
srv . LogEvent ( "stop" , container . ID , srv . daemon . Repositories ( ) . ImageName ( container . Image ) )
2013-05-06 09:31:22 +00:00
} else {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s\n" , name )
2013-05-06 09:31:22 +00:00
}
2013-11-17 03:00:16 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-11-25 01:05:59 +00:00
func ( srv * Server ) ContainerWait ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s" , job . Name )
2013-11-25 01:05:59 +00:00
}
name := job . Args [ 0 ]
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2014-06-06 11:30:04 +00:00
status , _ := container . State . WaitStop ( - 1 * time . Second )
2013-11-25 01:05:59 +00:00
job . Printf ( "%d\n" , status )
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Errorf ( "%s: no such container: %s" , job . Name , name )
2013-05-06 09:31:22 +00:00
}
2013-12-12 02:25:30 +00:00
func ( srv * Server ) ContainerResize ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 3 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Not enough arguments. Usage: %s CONTAINER HEIGHT WIDTH\n" , job . Name )
2013-12-12 02:25:30 +00:00
}
name := job . Args [ 0 ]
height , err := strconv . Atoi ( job . Args [ 1 ] )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-12-12 02:25:30 +00:00
}
width , err := strconv . Atoi ( job . Args [ 2 ] )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-12-12 02:25:30 +00:00
}
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2013-12-12 02:25:30 +00:00
if err := container . Resize ( height , width ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-12-12 02:25:30 +00:00
}
return engine . StatusOK
2013-05-24 02:33:28 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-05-24 02:33:28 +00:00
}
2014-04-02 19:26:06 +00:00
func ( srv * Server ) ContainerLogs ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
return job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
}
var (
name = job . Args [ 0 ]
stdout = job . GetenvBool ( "stdout" )
stderr = job . GetenvBool ( "stderr" )
2014-06-03 11:09:33 +00:00
tail = job . Getenv ( "tail" )
2014-04-02 19:26:06 +00:00
follow = job . GetenvBool ( "follow" )
times = job . GetenvBool ( "timestamps" )
2014-06-03 11:09:33 +00:00
lines = - 1
2014-04-02 19:26:06 +00:00
format string
)
if ! ( stdout || stderr ) {
return job . Errorf ( "You must choose at least one stream" )
}
if times {
2014-05-10 16:51:16 +00:00
format = time . RFC3339Nano
2014-04-02 19:26:06 +00:00
}
2014-06-03 11:09:33 +00:00
if tail == "" {
tail = "all"
}
2014-04-02 19:26:06 +00:00
container := srv . daemon . Get ( name )
if container == nil {
return job . Errorf ( "No such container: %s" , name )
}
cLog , err := container . ReadLog ( "json" )
if err != nil && os . IsNotExist ( err ) {
// Legacy logs
utils . Debugf ( "Old logs format" )
if stdout {
cLog , err := container . ReadLog ( "stdout" )
if err != nil {
utils . Errorf ( "Error reading logs (stdout): %s" , err )
} else if _ , err := io . Copy ( job . Stdout , cLog ) ; err != nil {
utils . Errorf ( "Error streaming logs (stdout): %s" , err )
}
}
if stderr {
cLog , err := container . ReadLog ( "stderr" )
if err != nil {
utils . Errorf ( "Error reading logs (stderr): %s" , err )
} else if _ , err := io . Copy ( job . Stderr , cLog ) ; err != nil {
utils . Errorf ( "Error streaming logs (stderr): %s" , err )
}
}
} else if err != nil {
utils . Errorf ( "Error reading logs (json): %s" , err )
} else {
2014-06-03 11:09:33 +00:00
if tail != "all" {
var err error
lines , err = strconv . Atoi ( tail )
if err != nil {
utils . Errorf ( "Failed to parse tail %s, error: %v, show all logs" , err )
lines = - 1
2014-04-02 19:26:06 +00:00
}
2014-06-03 11:09:33 +00:00
}
if lines != 0 {
if lines > 0 {
f := cLog . ( * os . File )
ls , err := tailfile . TailFile ( f , lines )
if err != nil {
return job . Error ( err )
}
tmp := bytes . NewBuffer ( [ ] byte { } )
for _ , l := range ls {
fmt . Fprintf ( tmp , "%s\n" , l )
}
cLog = tmp
2014-04-02 19:26:06 +00:00
}
2014-06-03 11:09:33 +00:00
dec := json . NewDecoder ( cLog )
for {
l := & utils . JSONLog { }
if err := dec . Decode ( l ) ; err == io . EOF {
break
} else if err != nil {
utils . Errorf ( "Error streaming logs: %s" , err )
break
}
logLine := l . Log
if times {
2014-05-10 16:51:16 +00:00
logLine = fmt . Sprintf ( "%s %s" , l . Created . Format ( format ) , logLine )
2014-06-03 11:09:33 +00:00
}
if l . Stream == "stdout" && stdout {
fmt . Fprintf ( job . Stdout , "%s" , logLine )
}
if l . Stream == "stderr" && stderr {
fmt . Fprintf ( job . Stderr , "%s" , logLine )
}
2014-04-02 19:26:06 +00:00
}
}
}
if follow {
errors := make ( chan error , 2 )
if stdout {
stdoutPipe := container . StdoutLogPipe ( )
go func ( ) {
errors <- utils . WriteLog ( stdoutPipe , job . Stdout , format )
} ( )
}
if stderr {
stderrPipe := container . StderrLogPipe ( )
go func ( ) {
errors <- utils . WriteLog ( stderrPipe , job . Stderr , format )
} ( )
}
err := <- errors
if err != nil {
utils . Errorf ( "%s" , err )
}
}
return engine . StatusOK
}
2014-01-08 22:05:05 +00:00
func ( srv * Server ) ContainerAttach ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
2014-01-08 22:05:05 +00:00
}
var (
name = job . Args [ 0 ]
logs = job . GetenvBool ( "logs" )
stream = job . GetenvBool ( "stream" )
stdin = job . GetenvBool ( "stdin" )
stdout = job . GetenvBool ( "stdout" )
stderr = job . GetenvBool ( "stderr" )
)
2014-04-17 21:43:01 +00:00
container := srv . daemon . Get ( name )
2013-05-08 06:32:17 +00:00
if container == nil {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-05-08 06:32:17 +00:00
}
2013-09-11 18:35:09 +00:00
2013-05-08 06:32:17 +00:00
//logs
if logs {
2013-07-15 16:17:58 +00:00
cLog , err := container . ReadLog ( "json" )
2013-07-18 13:25:47 +00:00
if err != nil && os . IsNotExist ( err ) {
// Legacy logs
2014-01-08 22:05:05 +00:00
utils . Debugf ( "Old logs format" )
2013-07-18 13:25:47 +00:00
if stdout {
cLog , err := container . ReadLog ( "stdout" )
if err != nil {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "Error reading logs (stdout): %s" , err )
2014-01-08 22:05:05 +00:00
} else if _ , err := io . Copy ( job . Stdout , cLog ) ; err != nil {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "Error streaming logs (stdout): %s" , err )
2013-07-18 13:25:47 +00:00
}
2013-05-06 09:31:22 +00:00
}
2013-07-18 13:25:47 +00:00
if stderr {
cLog , err := container . ReadLog ( "stderr" )
if err != nil {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "Error reading logs (stderr): %s" , err )
2014-01-08 22:05:05 +00:00
} else if _ , err := io . Copy ( job . Stderr , cLog ) ; err != nil {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "Error streaming logs (stderr): %s" , err )
2013-07-18 13:25:47 +00:00
}
2013-05-06 09:31:22 +00:00
}
2013-07-18 13:25:47 +00:00
} else if err != nil {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "Error reading logs (json): %s" , err )
2013-07-18 13:25:47 +00:00
} else {
dec := json . NewDecoder ( cLog )
for {
2013-09-26 22:59:02 +00:00
l := & utils . JSONLog { }
if err := dec . Decode ( l ) ; err == io . EOF {
2013-07-18 13:25:47 +00:00
break
} else if err != nil {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "Error streaming logs: %s" , err )
2013-07-18 13:25:47 +00:00
break
}
2013-09-26 22:59:02 +00:00
if l . Stream == "stdout" && stdout {
2014-01-08 22:05:05 +00:00
fmt . Fprintf ( job . Stdout , "%s" , l . Log )
2013-07-18 13:25:47 +00:00
}
2013-09-26 22:59:02 +00:00
if l . Stream == "stderr" && stderr {
2014-01-08 22:05:05 +00:00
fmt . Fprintf ( job . Stderr , "%s" , l . Log )
2013-07-18 13:25:47 +00:00
}
2013-05-06 09:31:22 +00:00
}
}
2013-05-08 06:32:17 +00:00
}
2013-05-06 09:31:22 +00:00
2013-05-08 06:32:17 +00:00
//stream
if stream {
var (
cStdin io . ReadCloser
cStdout , cStderr io . Writer
cStdinCloser io . Closer
)
2013-05-06 09:31:22 +00:00
2013-05-08 06:32:17 +00:00
if stdin {
r , w := io . Pipe ( )
go func ( ) {
defer w . Close ( )
2013-05-14 22:37:35 +00:00
defer utils . Debugf ( "Closing buffered stdin pipe" )
2014-01-08 22:05:05 +00:00
io . Copy ( w , job . Stdin )
2013-05-08 06:32:17 +00:00
} ( )
cStdin = r
2014-01-08 22:05:05 +00:00
cStdinCloser = job . Stdin
2013-05-08 06:32:17 +00:00
}
if stdout {
2014-01-08 22:05:05 +00:00
cStdout = job . Stdout
2013-05-08 06:32:17 +00:00
}
if stderr {
2014-01-08 22:05:05 +00:00
cStderr = job . Stderr
2013-05-08 06:32:17 +00:00
}
2013-05-07 21:15:42 +00:00
2014-05-05 23:48:56 +00:00
<- srv . daemon . Attach ( container , cStdin , cStdinCloser , cStdout , cStderr )
2013-05-08 06:32:17 +00:00
// If we are in stdinonce mode, wait for the process to end
// otherwise, simply return
if container . Config . StdinOnce && ! container . Config . Tty {
2014-06-06 11:30:04 +00:00
container . State . WaitStop ( - 1 * time . Second )
2013-05-06 09:31:22 +00:00
}
}
2014-01-08 22:05:05 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2014-01-10 00:37:56 +00:00
func ( srv * Server ) ContainerCopy ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 2 {
2014-01-28 00:27:02 +00:00
return job . Errorf ( "Usage: %s CONTAINER RESOURCE\n" , job . Name )
2014-01-10 00:37:56 +00:00
}
var (
name = job . Args [ 0 ]
resource = job . Args [ 1 ]
)
2014-04-17 21:43:01 +00:00
if container := srv . daemon . Get ( name ) ; container != nil {
2013-07-17 04:07:41 +00:00
data , err := container . Copy ( resource )
if err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-07-17 04:07:41 +00:00
}
2014-02-11 14:21:33 +00:00
defer data . Close ( )
2013-07-17 04:07:41 +00:00
2014-01-10 00:37:56 +00:00
if _ , err := io . Copy ( job . Stdout , data ) ; err != nil {
2014-01-28 00:27:02 +00:00
return job . Error ( err )
2013-07-17 04:07:41 +00:00
}
2014-01-10 00:37:56 +00:00
return engine . StatusOK
2013-07-17 04:07:41 +00:00
}
2014-01-28 00:27:02 +00:00
return job . Errorf ( "No such container: %s" , name )
2013-07-17 04:07:41 +00:00
}
2014-03-07 23:22:23 +00:00
func NewServer ( eng * engine . Engine , config * daemonconfig . Config ) ( * Server , error ) {
2014-04-17 21:43:01 +00:00
daemon , err := daemon . NewDaemon ( config , eng )
2013-05-06 09:31:22 +00:00
if err != nil {
return nil , err
}
srv := & Server {
2014-06-04 18:47:09 +00:00
Eng : eng ,
daemon : daemon ,
pullingPool : make ( map [ string ] chan struct { } ) ,
pushingPool : make ( map [ string ] chan struct { } ) ,
events : make ( [ ] utils . JSONMessage , 0 , 64 ) , //only keeps the 64 last events
eventPublisher : utils . NewJSONMessagePublisher ( ) ,
2013-05-06 09:31:22 +00:00
}
2014-04-17 21:43:01 +00:00
daemon . SetServer ( srv )
2013-05-06 09:31:22 +00:00
return srv , nil
}
2013-11-14 06:08:08 +00:00
func ( srv * Server ) LogEvent ( action , id , from string ) * utils . JSONMessage {
2013-11-22 00:41:41 +00:00
now := time . Now ( ) . UTC ( ) . Unix ( )
2013-08-12 11:50:03 +00:00
jm := utils . JSONMessage { Status : action , ID : id , From : from , Time : now }
2013-11-25 18:58:17 +00:00
srv . AddEvent ( jm )
2014-06-04 18:47:09 +00:00
srv . eventPublisher . Publish ( jm )
2013-11-14 06:08:08 +00:00
return & jm
2013-07-10 12:55:05 +00:00
}
2013-11-25 18:58:17 +00:00
func ( srv * Server ) AddEvent ( jm utils . JSONMessage ) {
srv . Lock ( )
2014-07-04 02:47:28 +00:00
if len ( srv . events ) == cap ( srv . events ) {
// discard oldest event
copy ( srv . events , srv . events [ 1 : ] )
srv . events [ len ( srv . events ) - 1 ] = jm
} else {
srv . events = append ( srv . events , jm )
}
srv . Unlock ( )
2013-11-25 18:58:17 +00:00
}
func ( srv * Server ) GetEvents ( ) [ ] utils . JSONMessage {
srv . RLock ( )
defer srv . RUnlock ( )
return srv . events
}
2014-03-07 19:51:28 +00:00
func ( srv * Server ) SetRunning ( status bool ) {
srv . Lock ( )
defer srv . Unlock ( )
srv . running = status
}
func ( srv * Server ) IsRunning ( ) bool {
srv . RLock ( )
defer srv . RUnlock ( )
return srv . running
}
func ( srv * Server ) Close ( ) error {
2014-03-11 18:39:28 +00:00
if srv == nil {
return nil
}
2014-03-07 19:51:28 +00:00
srv . SetRunning ( false )
2014-05-22 10:39:00 +00:00
done := make ( chan struct { } )
go func ( ) {
srv . tasks . Wait ( )
close ( done )
} ( )
select {
// Waiting server jobs for 15 seconds, shutdown immediately after that time
case <- time . After ( time . Second * 15 ) :
case <- done :
}
2014-04-17 21:43:01 +00:00
if srv . daemon == nil {
2014-03-11 18:39:28 +00:00
return nil
}
2014-04-17 21:43:01 +00:00
return srv . daemon . Close ( )
2014-03-07 19:51:28 +00:00
}
2013-05-06 09:31:22 +00:00
type Server struct {
2013-11-25 18:58:17 +00:00
sync . RWMutex
2014-06-04 18:47:09 +00:00
daemon * daemon . Daemon
pullingPool map [ string ] chan struct { }
pushingPool map [ string ] chan struct { }
events [ ] utils . JSONMessage
eventPublisher * utils . JSONMessagePublisher
Eng * engine . Engine
running bool
tasks sync . WaitGroup
2013-05-06 09:31:22 +00:00
}