2013-05-06 09:31:22 +00:00
package docker
import (
2013-07-15 16:17:58 +00:00
"encoding/json"
2013-05-30 19:30:21 +00:00
"errors"
2013-05-06 09:31:22 +00:00
"fmt"
2013-10-31 23:57:45 +00:00
"github.com/dotcloud/docker/archive"
2013-05-16 19:09:06 +00:00
"github.com/dotcloud/docker/auth"
2013-10-31 23:57:45 +00:00
"github.com/dotcloud/docker/engine"
2014-01-20 19:15:44 +00:00
"github.com/dotcloud/docker/pkg/cgroups"
2013-12-23 23:33:06 +00:00
"github.com/dotcloud/docker/pkg/graphdb"
2013-05-15 01:41:39 +00:00
"github.com/dotcloud/docker/registry"
2013-05-14 22:37:35 +00:00
"github.com/dotcloud/docker/utils"
2013-05-06 09:31:22 +00:00
"io"
2013-05-15 18:30:40 +00:00
"io/ioutil"
2013-05-06 09:31:22 +00:00
"log"
"net/http"
"net/url"
"os"
2013-06-28 15:51:58 +00:00
"os/exec"
2013-10-31 23:57:45 +00:00
"os/signal"
2013-05-15 18:30:40 +00:00
"path"
2013-10-08 16:35:47 +00:00
"path/filepath"
2013-05-06 09:31:22 +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"
2013-10-21 16:04:42 +00:00
"syscall"
2013-10-31 23:57:45 +00:00
"time"
2013-05-06 09:31:22 +00:00
)
2013-10-22 23:23:52 +00:00
func ( srv * Server ) Close ( ) error {
return srv . runtime . Close ( )
}
2013-10-21 16:04:42 +00:00
func init ( ) {
2013-10-27 00:19:35 +00:00
engine . Register ( "initapi" , jobInitApi )
2013-10-21 16:04:42 +00:00
}
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.
2013-11-20 07:37:03 +00:00
func jobInitApi ( job * engine . Job ) engine . Status {
2013-10-27 07:16:32 +00:00
job . Logf ( "Creating server" )
2013-11-20 07:37:03 +00:00
// FIXME: ImportEnv deprecates ConfigFromJob
2013-10-27 02:24:01 +00:00
srv , err := NewServer ( job . Eng , ConfigFromJob ( job ) )
2013-10-21 16:04:42 +00:00
if err != nil {
2013-11-20 07:37:03 +00:00
job . Error ( err )
return engine . StatusErr
2013-10-21 16:04:42 +00:00
}
2013-10-27 06:51:43 +00:00
if srv . runtime . config . Pidfile != "" {
job . Logf ( "Creating pidfile" )
if err := utils . CreatePidFile ( srv . runtime . config . Pidfile ) ; err != nil {
2013-11-20 07:37:03 +00:00
// FIXME: do we need fatal here instead of returning a job error?
2013-10-27 06:51:43 +00:00
log . Fatal ( err )
}
2013-10-25 06:30:34 +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 )
2013-11-29 22:13:00 +00:00
signal . Notify ( c , os . Interrupt , syscall . SIGTERM , syscall . SIGQUIT )
2013-10-25 06:30:34 +00:00
go func ( ) {
sig := <- c
log . Printf ( "Received signal '%v', exiting\n" , sig )
utils . RemovePidFile ( srv . runtime . config . Pidfile )
2013-10-26 21:28:53 +00:00
srv . Close ( )
2013-10-25 06:30:34 +00:00
os . Exit ( 0 )
} ( )
2013-10-27 02:24:01 +00:00
job . Eng . Hack_SetGlobalVar ( "httpapi.server" , srv )
2013-11-14 06:08:08 +00:00
job . Eng . Hack_SetGlobalVar ( "httpapi.runtime" , srv . runtime )
2013-11-19 14:41:10 +00:00
// https://github.com/dotcloud/docker/issues/2768
if srv . runtime . networkManager . bridgeNetwork != nil {
job . Eng . Hack_SetGlobalVar ( "httpapi.bridgeIP" , srv . runtime . networkManager . bridgeNetwork . IP )
}
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 ,
"serveapi" : srv . ListenAndServe ,
"wait" : srv . ContainerWait ,
"tag" : srv . ImageTag ,
"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 ,
"insert" : srv . ImageInsert ,
"attach" : srv . ContainerAttach ,
"search" : srv . ImagesSearch ,
"changes" : srv . ContainerChanges ,
"top" : srv . ContainerTop ,
2014-01-21 00:09:17 +00:00
"load" : srv . ImageLoad ,
2014-01-18 00:57:43 +00:00
} {
if err := job . Eng . Register ( name , handler ) ; err != nil {
job . Error ( err )
return engine . StatusErr
}
2014-01-16 22:58:20 +00:00
}
2013-11-20 07:37:03 +00:00
return engine . StatusOK
2013-10-27 00:19:35 +00:00
}
2013-11-20 07:37:03 +00:00
func ( srv * Server ) ListenAndServe ( job * engine . Job ) engine . Status {
2013-10-27 02:24:01 +00:00
protoAddrs := job . Args
2013-10-21 16:04:42 +00:00
chErrors := make ( chan error , len ( protoAddrs ) )
for _ , protoAddr := range protoAddrs {
protoAddrParts := strings . SplitN ( protoAddr , "://" , 2 )
2013-11-01 01:42:44 +00:00
switch protoAddrParts [ 0 ] {
case "unix" :
if err := syscall . Unlink ( protoAddrParts [ 1 ] ) ; err != nil && ! os . IsNotExist ( err ) {
log . Fatal ( err )
}
case "tcp" :
2013-10-21 16:04:42 +00:00
if ! strings . HasPrefix ( protoAddrParts [ 1 ] , "127.0.0.1" ) {
log . Println ( "/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\" )
}
2013-11-01 01:42:44 +00:00
default :
2013-11-20 07:37:03 +00:00
job . Errorf ( "Invalid protocol format." )
return engine . StatusErr
2013-10-21 16:04:42 +00:00
}
go func ( ) {
2013-10-27 07:06:43 +00:00
// FIXME: merge Server.ListenAndServe with ListenAndServe
chErrors <- ListenAndServe ( protoAddrParts [ 0 ] , protoAddrParts [ 1 ] , srv , job . GetenvBool ( "Logging" ) )
2013-10-21 16:04:42 +00:00
} ( )
}
for i := 0 ; i < len ( protoAddrs ) ; i += 1 {
err := <- chErrors
if err != nil {
2013-11-20 07:37:03 +00:00
job . Error ( err )
return engine . StatusErr
2013-10-21 16:04:42 +00:00
}
}
2013-11-20 07:37:03 +00:00
return engine . StatusOK
2013-10-21 16:04:42 +00:00
}
2013-07-23 21:05:13 +00:00
// simpleVersionInfo is a simple implementation of
// the interface VersionInfo, which is used
2013-07-18 18:22:49 +00:00
// to provide version information for some product,
// component, etc. It stores the product name and the version
// in string and returns them on calls to Name() and Version().
2013-07-23 21:05:13 +00:00
type simpleVersionInfo struct {
2013-06-28 21:42:04 +00:00
name string
version string
}
2013-07-23 21:05:13 +00:00
func ( v * simpleVersionInfo ) Name ( ) string {
2013-06-28 21:42:04 +00:00
return v . name
}
2013-07-23 21:05:13 +00:00
func ( v * simpleVersionInfo ) Version ( ) string {
2013-06-28 21:42:04 +00:00
return v . version
}
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 {
2013-11-22 03:41:17 +00:00
job . Errorf ( "Usage: %s CONTAINER [SIGNAL]" , job . Name )
return engine . StatusErr
2013-11-17 00:26:04 +00:00
}
name := job . Args [ 0 ]
var sig uint64
if len ( job . Args ) == 2 && job . Args [ 1 ] != "" {
var err error
// 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 {
2013-11-22 03:41:17 +00:00
job . Errorf ( "Invalid signal: %s" , job . Args [ 1 ] )
return engine . StatusErr
2013-11-17 00:26:04 +00:00
}
}
2013-05-06 09:31:22 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
2013-10-08 19:15:29 +00:00
// If no signal is passed, perform regular Kill (SIGKILL + wait())
if sig == 0 {
if err := container . Kill ( ) ; err != nil {
2013-11-22 03:41:17 +00:00
job . Errorf ( "Cannot kill container %s: %s" , name , err )
return engine . StatusErr
2013-10-08 19:15:29 +00:00
}
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "kill" , container . ID , srv . runtime . repositories . ImageName ( container . Image ) )
2013-10-08 19:15:29 +00:00
} else {
// Otherwise, just send the requested signal
2013-11-17 00:26:04 +00:00
if err := container . kill ( int ( sig ) ) ; err != nil {
2013-11-22 03:41:17 +00:00
job . Errorf ( "Cannot kill container %s: %s" , name , err )
return engine . StatusErr
2013-10-08 19:15:29 +00:00
}
// FIXME: Add event for signals
2013-05-06 09:31:22 +00:00
}
} else {
2013-11-22 03:41:17 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
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
}
2013-12-08 01:33:37 +00:00
func ( srv * Server ) ContainerExport ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
job . Errorf ( "Usage: %s container_id" , job . Name )
return engine . StatusErr
}
name := job . Args [ 0 ]
2013-05-06 09:31:22 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
data , err := container . Export ( )
if err != nil {
2013-12-08 01:33:37 +00:00
job . Errorf ( "%s: %s" , name , err )
return engine . StatusErr
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 {
job . Errorf ( "%s: %s" , name , err )
return engine . StatusErr
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()
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "export" , container . ID , srv . runtime . repositories . ImageName ( container . Image ) )
2013-12-08 01:33:37 +00:00
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-12-08 01:33:37 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
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 {
job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
return engine . StatusErr
}
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-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
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 )
2013-11-21 01:28:19 +00:00
rootRepo , err := srv . runtime . repositories . Get ( name )
if err != nil {
2014-01-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
2013-11-21 01:28:19 +00:00
}
if rootRepo != nil {
for _ , id := range rootRepo {
image , err := srv . ImageInspect ( id )
2013-09-02 16:06:17 +00:00
if err != nil {
2014-01-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
2013-09-02 16:06:17 +00:00
}
2013-11-18 19:48:55 +00:00
2013-11-21 01:28:19 +00:00
if err := srv . exportImage ( image , tempdir ) ; err != nil {
2014-01-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
2013-09-02 16:06:17 +00:00
}
}
2013-11-21 01:28:19 +00:00
// write repositories
rootRepoMap := map [ string ] Repository { }
rootRepoMap [ name ] = rootRepo
rootRepoJson , _ := json . Marshal ( rootRepoMap )
2013-09-02 16:06:17 +00:00
2013-11-21 01:28:19 +00:00
if err := ioutil . WriteFile ( path . Join ( tempdir , "repositories" ) , rootRepoJson , os . ModeAppend ) ; err != nil {
2014-01-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
2013-11-21 01:28:19 +00:00
}
} else {
image , err := srv . ImageInspect ( name )
if err != nil {
2014-01-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
2013-11-21 01:28:19 +00:00
}
if err := srv . exportImage ( image , tempdir ) ; err != nil {
2014-01-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
2013-11-21 01:28:19 +00:00
}
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-07 20:39:15 +00:00
job . Error ( err )
return engine . StatusErr
2013-09-02 16:06:17 +00:00
}
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 {
job . Error ( err )
return engine . StatusErr
2013-09-02 16:06:17 +00:00
}
2014-01-07 20:39:15 +00:00
return engine . StatusOK
2013-09-02 16:06:17 +00:00
}
2013-11-21 01:28:19 +00:00
func ( srv * Server ) exportImage ( image * Image , tempdir string ) error {
for i := image ; i != nil ; {
// temporary directory
tmpImageDir := path . Join ( tempdir , i . ID )
if err := os . Mkdir ( tmpImageDir , os . ModeDir ) ; 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 )
if err := ioutil . WriteFile ( path . Join ( tmpImageDir , "VERSION" ) , versionBuf , os . ModeAppend ) ; err != nil {
return err
}
// serialize json
b , err := json . Marshal ( i )
if err != nil {
return err
}
if err := ioutil . WriteFile ( path . Join ( tmpImageDir , "json" ) , b , os . ModeAppend ) ; err != nil {
return err
}
// serialize filesystem
2013-11-21 18:26:21 +00:00
fs , err := i . TarLayer ( )
2013-11-21 01:28:19 +00:00
if err != nil {
return err
}
fsTar , err := os . Create ( path . Join ( tmpImageDir , "layer.tar" ) )
if err != nil {
return err
}
if _ , err = io . Copy ( fsTar , fs ) ; err != nil {
return err
}
fsTar . Close ( )
// find parent
if i . Parent != "" {
i , err = srv . ImageInspect ( i . Parent )
if err != nil {
return err
}
} else {
i = nil
}
}
return nil
}
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-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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 {
job . Error ( err )
return engine . StatusErr
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-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
2013-11-18 19:48:55 +00:00
}
if err := os . Mkdir ( repoDir , os . ModeDir ) ; err != nil {
2014-01-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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 ( ) {
if err := srv . recursiveLoad ( d . Name ( ) , tmpImageDir ) ; err != nil {
2014-01-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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 {
repositories := map [ string ] Repository { }
if err := json . Unmarshal ( repositoriesJson , & repositories ) ; err != nil {
2014-01-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
2013-11-21 01:28:19 +00:00
}
for imageName , tagMap := range repositories {
for tag , address := range tagMap {
if err := srv . runtime . repositories . Set ( imageName , tag , address , true ) ; err != nil {
2014-01-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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-21 00:09:17 +00:00
job . Error ( err )
return engine . StatusErr
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
}
func ( srv * Server ) recursiveLoad ( address , tmpImageDir string ) error {
2013-11-18 19:48:55 +00:00
if _ , err := srv . ImageInspect ( address ) ; 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
}
img , err := NewImgJSON ( imageJson )
if err != nil {
utils . Debugf ( "Error unmarshalling json" , err )
return err
}
if img . Parent != "" {
if ! srv . runtime . graph . Exists ( img . Parent ) {
2013-11-18 19:48:55 +00:00
if err := srv . recursiveLoad ( img . Parent , tmpImageDir ) ; err != nil {
return err
}
2013-09-02 16:06:17 +00:00
}
}
2013-11-18 19:48:55 +00:00
if err := srv . runtime . graph . Register ( imageJson , layer , img ) ; err != nil {
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 22:43:12 +00:00
func ( srv * Server ) ImagesSearch ( job * engine . Job ) engine . Status {
if n := len ( job . Args ) ; n != 1 {
job . Errorf ( "Usage: %s TERM" , job . Name )
return engine . StatusErr
}
term := job . Args [ 0 ]
2013-10-22 18:49:13 +00:00
r , err := registry . NewRegistry ( nil , srv . HTTPRequestFactory ( nil ) , auth . IndexServerAddress ( ) )
2013-06-17 18:13:40 +00:00
if err != nil {
2014-01-15 22:43:12 +00:00
job . Error ( err )
return engine . StatusErr
2013-06-17 18:13:40 +00:00
}
results , err := r . SearchRepositories ( term )
2013-05-07 18:37:35 +00:00
if err != nil {
2014-01-15 22:43:12 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-07 18:59:04 +00:00
}
2014-01-15 22:43:12 +00:00
outs := engine . NewTable ( "star_count" , 0 )
for _ , result := range results . Results {
out := & engine . Env { }
out . Import ( result )
outs . Add ( out )
}
outs . ReverseSort ( )
if _ , err := outs . WriteTo ( job . Stdout ) ; err != nil {
job . Error ( err )
return engine . StatusErr
}
return engine . StatusOK
2013-05-07 18:37:35 +00:00
}
2014-01-09 18:06:16 +00:00
func ( srv * Server ) ImageInsert ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 3 {
job . Errorf ( "Usage: %s IMAGE URL PATH\n" , job . Name )
return engine . StatusErr
}
var (
name = job . Args [ 0 ]
url = job . Args [ 1 ]
path = job . Args [ 2 ]
)
sf := utils . NewStreamFormatter ( job . GetenvBool ( "json" ) )
out := utils . NewWriteFlusher ( job . Stdout )
2013-05-07 17:23:50 +00:00
img , err := srv . runtime . repositories . LookupImage ( name )
if err != nil {
2014-01-09 18:06:16 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-07 17:23:50 +00:00
}
2013-12-04 19:54:11 +00:00
file , err := utils . Download ( url )
2013-05-07 17:23:50 +00:00
if err != nil {
2014-01-09 18:06:16 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-07 17:23:50 +00:00
}
defer file . Body . Close ( )
2014-01-15 22:36:13 +00:00
config , _ , _ , err := ParseRun ( [ ] string { img . ID , "echo" , "insert" , url , path } , srv . runtime . sysInfo )
2013-05-07 17:23:50 +00:00
if err != nil {
2014-01-09 18:06:16 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-07 17:23:50 +00:00
}
2013-10-28 23:58:59 +00:00
c , _ , err := srv . runtime . Create ( config , "" )
2013-05-07 17:23:50 +00:00
if err != nil {
2014-01-09 18:06:16 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-07 17:23:50 +00:00
}
2014-01-15 23:41:57 +00:00
if err := c . Inject ( utils . ProgressReader ( file . Body , int ( file . ContentLength ) , out , sf , false , utils . TruncateID ( img . ID ) , "Downloading" ) , path ) ; err != nil {
2014-01-09 18:06:16 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-07 17:23:50 +00:00
}
// FIXME: Handle custom repo, tag comment, author
2013-09-07 00:33:05 +00:00
img , err = srv . runtime . Commit ( c , "" , "" , img . Comment , img . Author , nil )
2013-05-07 17:23:50 +00:00
if err != nil {
2014-01-09 18:06:16 +00:00
out . Write ( sf . FormatError ( err ) )
2014-01-10 22:54:54 +00:00
return engine . StatusErr
2013-05-07 17:23:50 +00:00
}
2014-01-09 18:06:16 +00:00
out . Write ( sf . FormatStatus ( "" , img . ID ) )
return engine . StatusOK
2013-05-07 17:23:50 +00:00
}
2014-01-15 21:52:35 +00:00
func ( srv * Server ) ImagesViz ( job * engine . Job ) engine . Status {
2013-10-08 13:52:36 +00:00
images , _ := srv . runtime . graph . Map ( )
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 (
parentImage * Image
err error
)
for _ , image := range images {
parentImage , err = image . GetParent ( )
if err != nil {
2014-01-15 21:52:35 +00:00
job . Errorf ( "Error while getting parent image: %v" , err )
return engine . StatusErr
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
}
}
reporefs := make ( map [ string ] [ ] string )
for name , repository := range srv . runtime . repositories . Repositories {
for tag , id := range repository {
reporefs [ utils . TruncateID ( id ) ] = append ( reporefs [ utils . TruncateID ( id ) ] , fmt . Sprintf ( "%s:%s" , name , tag ) )
}
}
for id , repos := range reporefs {
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 (
allImages map [ string ] * Image
err error
)
2013-12-12 22:39:35 +00:00
if job . GetenvBool ( "all" ) {
2013-05-06 09:31:22 +00:00
allImages , err = srv . runtime . graph . Map ( )
} else {
allImages , err = srv . runtime . graph . Heads ( )
}
if err != nil {
2014-01-14 19:43:58 +00:00
job . Error ( err )
2013-12-12 22:39:35 +00:00
return engine . StatusErr
2013-05-06 09:31:22 +00:00
}
2013-12-12 22:39:35 +00:00
lookup := make ( map [ string ] * engine . Env )
2013-05-06 09:31:22 +00:00
for name , repository := range srv . runtime . 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 {
image , err := srv . runtime . graph . Get ( id )
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-01-13 22:55:31 +00:00
out . SetList ( "RepoTags" , append ( out . GetList ( "RepoTags" ) , fmt . Sprintf ( "%s:%s" , name , tag ) ) )
2013-10-06 05:44:04 +00:00
} else {
2013-12-12 22:39:35 +00:00
out := & engine . Env { }
2013-10-06 05:44:04 +00:00
delete ( allImages , id )
2014-01-13 22:55:31 +00:00
out . Set ( "ParentId" , image . Parent )
out . SetList ( "RepoTags" , [ ] string { fmt . Sprintf ( "%s:%s" , name , tag ) } )
2013-12-12 22:39:35 +00:00
out . Set ( "ID" , image . ID )
out . SetInt64 ( "Created" , image . Created . Unix ( ) )
out . SetInt64 ( "Size" , image . Size )
out . SetInt64 ( "VirtualSize" , image . getParentsSize ( 0 ) + image . Size )
2013-10-06 05:44:04 +00:00
lookup [ id ] = out
}
2013-05-06 09:31:22 +00:00
}
}
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>" } )
2013-12-12 22:39:35 +00:00
out . Set ( "ID" , image . ID )
out . SetInt64 ( "Created" , image . Created . Unix ( ) )
out . SetInt64 ( "Size" , image . Size )
out . SetInt64 ( "VirtualSize" , image . getParentsSize ( 0 ) + image . Size )
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-13 22:55:31 +00:00
if _ , err := outs . WriteTo ( job . Stdout ) ; err != nil {
2014-01-14 19:43:58 +00:00
job . Error ( err )
2013-12-12 22:39:35 +00:00
return engine . StatusErr
}
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 {
2013-09-01 03:31:21 +00:00
images , _ := srv . runtime . 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 == "" {
// if that fails, we'll just return the path from the runtime
initPath = srv . runtime . sysInitPath
}
2013-12-11 18:35:21 +00:00
v := & engine . Env { }
2013-12-12 21:35:50 +00:00
v . SetInt ( "Containers" , len ( srv . runtime . List ( ) ) )
2013-12-11 18:35:21 +00:00
v . SetInt ( "Images" , imgcount )
v . Set ( "Driver" , srv . runtime . driver . String ( ) )
v . SetJson ( "DriverStatus" , srv . runtime . driver . Status ( ) )
2014-01-15 22:36:13 +00:00
v . SetBool ( "MemoryLimit" , srv . runtime . sysInfo . MemoryLimit )
v . SetBool ( "SwapLimit" , srv . runtime . sysInfo . SwapLimit )
v . SetBool ( "IPv4Forwarding" , ! srv . runtime . sysInfo . 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 ( ) )
v . SetInt ( "NGoroutines" , runtime . NumGoroutine ( ) )
2014-01-16 19:59:46 +00:00
v . Set ( "ExecutionDriver" , srv . runtime . execDriver . Name ( ) )
2013-12-12 21:35:50 +00:00
v . SetInt ( "NEventsListener" , len ( srv . events ) )
2013-12-11 18:35:21 +00:00
v . Set ( "KernelVersion" , kernelVersion )
v . Set ( "IndexServerAddress" , auth . IndexServerAddress ( ) )
2013-12-05 09:14:15 +00:00
v . Set ( "InitSha1" , utils . INITSHA1 )
v . Set ( "InitPath" , initPath )
2013-12-11 18:35:21 +00:00
if _ , err := v . WriteTo ( job . Stdout ) ; err != nil {
job . Error ( err )
return engine . StatusErr
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 {
job . Errorf ( "Usage: %s IMAGE" , job . Name )
return engine . StatusErr
}
name := job . Args [ 0 ]
2013-05-06 09:31:22 +00:00
image , err := srv . runtime . repositories . LookupImage ( name )
if err != nil {
2014-01-14 00:50:10 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-06 09:31:22 +00:00
}
2013-06-18 17:31:07 +00:00
lookupMap := make ( map [ string ] [ ] string )
2013-06-18 01:39:30 +00:00
for name , repository := range srv . runtime . repositories . Repositories {
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 )
2013-05-06 09:31:22 +00:00
err = image . WalkHistory ( func ( img * Image ) error {
2014-01-14 00:50:10 +00:00
out := & engine . Env { }
out . Set ( "ID" , img . ID )
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-14 00:50:10 +00:00
outs . ReverseSort ( )
if _ , err := outs . WriteTo ( job . Stdout ) ; err != nil {
2014-01-14 19:43:58 +00:00
job . Error ( err )
2014-01-14 00:50:10 +00:00
return engine . StatusErr
}
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 {
job . Errorf ( "Not enough arguments. Usage: %s CONTAINER [PS_ARGS]\n" , job . Name )
return engine . StatusErr
}
var (
name = job . Args [ 0 ]
psArgs = "-ef"
)
if len ( job . Args ) == 2 && job . Args [ 1 ] != "" {
psArgs = job . Args [ 1 ]
}
2013-06-28 15:51:58 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
2013-12-23 22:42:04 +00:00
if ! container . State . IsRunning ( ) {
2014-01-16 22:58:20 +00:00
job . Errorf ( "Container %s is not running" , name )
return engine . StatusErr
2013-12-23 22:42:04 +00:00
}
2013-12-21 16:02:06 +00:00
pids , err := cgroups . GetPidsForContainer ( container . ID )
2013-06-28 15:51:58 +00:00
if err != nil {
2014-01-16 22:58:20 +00:00
job . Error ( err )
return engine . StatusErr
2013-12-11 14:56:44 +00:00
}
output , err := exec . Command ( "ps" , psArgs ) . Output ( )
if err != nil {
2014-01-16 22:58:20 +00:00
job . Errorf ( "Error running ps: %s" , err )
return engine . StatusErr
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-16 22:58:20 +00:00
job . Errorf ( "Couldn't find PID field in ps output" )
return engine . StatusErr
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-16 22:58:20 +00:00
job . Errorf ( "Unexpected pid '%s': %s" , fields [ pidIndex ] , err )
return engine . StatusErr
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-16 22:58:20 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
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 {
job . Errorf ( "Usage: %s CONTAINER" , job . Name )
return engine . StatusErr
}
name := job . Args [ 0 ]
2013-05-06 09:31:22 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
2014-01-15 00:51:59 +00:00
outs := engine . NewTable ( "" , 0 )
changes , err := container . Changes ( )
if err != nil {
job . Error ( err )
return engine . StatusErr
}
for _ , change := range changes {
out := & engine . Env { }
if err := out . Import ( change ) ; err != nil {
job . Error ( err )
return engine . StatusErr
}
outs . Add ( out )
}
if _ , err := outs . WriteTo ( job . Stdout ) ; err != nil {
job . Error ( err )
return engine . StatusErr
}
} else {
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
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
}
2013-06-20 14:19:50 +00:00
func ( srv * Server ) Containers ( all , size bool , n int , since , before string ) [ ] APIContainers {
2013-05-08 16:28:11 +00:00
var foundBefore bool
var displayed int
2013-10-05 02:25:15 +00:00
out := [ ] APIContainers { }
2013-05-10 00:50:56 +00:00
2013-11-19 19:02:10 +00:00
names := map [ string ] [ ] string { }
2013-11-20 19:07:42 +00:00
srv . runtime . 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 )
2013-05-08 16:28:11 +00:00
for _ , container := range srv . runtime . List ( ) {
2013-11-21 20:21:03 +00:00
if ! container . State . IsRunning ( ) && ! all && n == - 1 && since == "" && before == "" {
2013-05-06 09:31:22 +00:00
continue
}
2013-11-13 09:29:00 +00:00
if before != "" && ! foundBefore {
2013-10-25 01:59:59 +00:00
if container . ID == before || utils . TruncateID ( container . ID ) == before {
2013-05-08 16:28:11 +00:00
foundBefore = true
}
2013-11-13 09:29:00 +00:00
continue
2013-05-08 16:28:11 +00:00
}
if displayed == n {
break
}
2013-10-25 01:59:59 +00:00
if container . ID == since || utils . TruncateID ( container . ID ) == since {
2013-05-06 09:31:22 +00:00
break
}
2013-05-10 00:50:56 +00:00
displayed ++
2013-11-19 19:02:10 +00:00
c := createAPIContainer ( names [ container . ID ] , container , size , srv . runtime )
2013-10-05 02:25:15 +00:00
out = append ( out , c )
2013-05-06 09:31:22 +00:00
}
2013-10-05 02:25:15 +00:00
return out
2013-05-06 09:31:22 +00:00
}
2013-11-19 19:02:10 +00:00
func createAPIContainer ( names [ ] string , container * Container , size bool , runtime * Runtime ) APIContainers {
2013-10-05 02:25:15 +00:00
c := APIContainers {
ID : container . ID ,
}
c . Names = names
c . Image = runtime . repositories . ImageName ( container . Image )
c . Command = fmt . Sprintf ( "%s %s" , container . Path , strings . Join ( container . Args , " " ) )
c . Created = container . Created . Unix ( )
c . Status = container . State . String ( )
c . Ports = container . NetworkSettings . PortMappingAPI ( )
if size {
c . SizeRw , c . SizeRootFs = container . GetSize ( )
}
return c
}
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 {
job . Errorf ( "Not enough arguments. Usage: %s CONTAINER\n" , job . Name )
2013-12-12 01:03:48 +00:00
return engine . StatusErr
}
2013-12-13 22:29:27 +00:00
name := job . Args [ 0 ]
2013-12-12 01:03:48 +00:00
2013-05-07 17:23:50 +00:00
container := srv . runtime . Get ( name )
if container == nil {
2013-12-12 01:03:48 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
}
2013-12-13 23:01:54 +00:00
var config Config
if err := job . GetenvJson ( "config" , & config ) ; err != nil {
job . Error ( err )
return engine . StatusErr
2013-05-07 17:23:50 +00:00
}
2013-12-12 01:03:48 +00:00
2013-12-13 23:01:54 +00:00
img , err := srv . runtime . Commit ( container , job . Getenv ( "repo" ) , job . Getenv ( "tag" ) , job . Getenv ( "comment" ) , job . Getenv ( "author" ) , & config )
2013-05-06 09:31:22 +00:00
if err != nil {
2013-12-12 01:03:48 +00:00
job . Error ( err )
return engine . StatusErr
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 {
job . Errorf ( "Usage: %s IMAGE REPOSITORY [TAG]\n" , job . Name )
return engine . StatusErr
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 ]
}
if err := srv . runtime . repositories . Set ( job . Args [ 1 ] , tag , job . Args [ 0 ] , job . GetenvBool ( "force" ) ) ; err != nil {
job . Error ( err )
return engine . StatusErr
}
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 {
2013-10-08 07:54:47 +00:00
utils . Errorf ( "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 )
2013-05-15 01:41:39 +00:00
if ! srv . runtime . graph . Exists ( id ) {
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Pulling metadata" , nil ) )
2013-06-07 01:16:16 +00:00
imgJSON , imgSize , err := r . GetRemoteImageJSON ( id , endpoint , token )
2013-05-15 01:41:39 +00:00
if err != nil {
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error pulling dependent layers" , nil ) )
2013-08-12 17:53:06 +00:00
// FIXME: Keep going in case of error?
2013-05-15 01:41:39 +00:00
return err
}
2013-06-04 18:00:22 +00:00
img , err := NewImgJSON ( imgJSON )
2013-05-15 01:41:39 +00:00
if err != nil {
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error pulling dependent layers" , nil ) )
2013-05-15 01:41:39 +00:00
return fmt . Errorf ( "Failed to parse json: %s" , err )
}
// Get the layer
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Pulling fs layer" , nil ) )
2013-06-07 01:16:16 +00:00
layer , err := r . GetRemoteImageLayer ( img . ID , endpoint , token )
2013-05-15 01:41:39 +00:00
if err != nil {
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error pulling dependent layers" , nil ) )
2013-05-15 01:41:39 +00:00
return err
}
2013-06-03 19:14:57 +00:00
defer layer . Close ( )
2013-11-28 20:16:57 +00:00
if err := srv . runtime . graph . Register ( imgJSON , utils . ProgressReader ( layer , imgSize , out , sf , false , utils . TruncateID ( id ) , "Downloading" ) , img ) ; err != nil {
out . Write ( sf . FormatProgress ( utils . TruncateID ( id ) , "Error downloading dependent layers" , nil ) )
2013-05-15 01:41:39 +00:00
return err
}
}
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 {
2013-05-15 01:41:39 +00:00
return err
}
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 {
utils . Errorf ( "Image (id: %s) pull is already running, skipping: %v" , img . ID , err )
}
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 {
2013-08-22 11:23:05 +00:00
// Its not ideal that only the last error is returned, it would be better to concatenate the errors.
// 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 {
2013-11-28 20:16:57 +00:00
out . Write ( sf . FormatProgress ( utils . TruncateID ( img . ID ) , fmt . Sprintf ( "Error pulling image (%s) from %s, %s" , img . Tag , localName , lastErr ) , nil ) )
2013-08-22 11:23:05 +00:00
if parallel {
errors <- fmt . Errorf ( "Could not find repository on any of the indexed registries." )
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
}
2013-07-09 00:26:50 +00:00
if err := srv . runtime . repositories . Set ( localName , tag , id , true ) ; err != nil {
2013-05-15 01:41:39 +00:00
return err
}
}
if err := srv . runtime . repositories . Save ( ) ; err != nil {
2013-05-06 11:34:31 +00:00
return err
}
2013-05-15 01:41:39 +00:00
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
}
2013-08-22 19:15:31 +00:00
func ( srv * Server ) ImagePull ( localName string , tag string , out io . Writer , sf * utils . StreamFormatter , authConfig * auth . AuthConfig , metaHeaders map [ string ] [ ] string , parallel bool ) error {
2013-11-27 12:18:01 +00:00
out = utils . NewWriteFlusher ( out )
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
out . Write ( sf . FormatStatus ( "" , "Repository %s already being pulled by another client. Waiting." , localName ) )
<- c
return nil
}
return 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
2013-07-09 00:26:50 +00:00
endpoint , remoteName , err := registry . ResolveRepositoryName ( localName )
2013-07-05 19:20:58 +00:00
if err != nil {
2013-06-17 23:10:00 +00:00
return err
}
2013-07-05 19:20:58 +00:00
2013-10-22 18:49:13 +00:00
r , err := registry . NewRegistry ( authConfig , srv . HTTPRequestFactory ( metaHeaders ) , endpoint )
if err != nil {
return err
}
2013-07-09 18:30:12 +00:00
if endpoint == auth . IndexServerAddress ( ) {
// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar"
localName = remoteName
}
2013-06-17 23:10:00 +00:00
2013-10-22 18:49:13 +00:00
if err = srv . pullRepository ( r , out , localName , remoteName , tag , sf , parallel ) ; err != nil {
2013-09-03 18:45:49 +00:00
return err
}
2013-06-05 22:13:01 +00:00
2013-05-15 01:41:39 +00:00
return nil
}
2013-05-15 03:27:15 +00:00
// Retrieve the all the images to be uploaded in the correct order
2013-12-09 14:58:04 +00:00
func ( srv * Server ) getImageList ( localRepo map [ string ] string ) ( [ ] string , map [ string ] [ ] string , error ) {
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 {
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 )
for img , err := srv . runtime . 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
}
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
}
2013-10-22 18:49:13 +00:00
func ( srv * Server ) pushRepository ( r * registry . Registry , out io . Writer , localName , remoteName string , localRepo map [ string ] 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 )
imgList , tagsByImage , err := srv . getImageList ( localRepo )
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
2013-05-24 17:37:34 +00:00
var repoData * registry . RepositoryData
2013-12-09 14:58:04 +00:00
var imageIndex [ ] * registry . ImgData
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
}
for _ , ep := range repoData . Endpoints {
2013-07-24 17:10:59 +00:00
out . Write ( sf . FormatStatus ( "" , "Pushing repository %s (%d tags)" , localName , len ( localRepo ) ) )
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 )
2013-07-02 22:27:22 +00:00
jsonRaw , err := ioutil . ReadFile ( path . Join ( srv . runtime . 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
}
2013-10-31 23:57:45 +00:00
layerData , err := srv . runtime . 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
2013-12-10 18:57:16 +00:00
checksum , 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
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.
2013-08-22 19:15:31 +00:00
func ( srv * Server ) ImagePush ( localName string , out io . Writer , sf * utils . StreamFormatter , authConfig * auth . AuthConfig , metaHeaders map [ string ] [ ] string ) error {
2013-11-20 21:51:05 +00:00
if _ , err := srv . poolAdd ( "push" , localName ) ; err != nil {
2013-06-17 23:10:00 +00:00
return err
}
2013-07-09 00:26:50 +00:00
defer srv . poolRemove ( "push" , localName )
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
2013-07-09 00:26:50 +00:00
endpoint , remoteName , err := registry . ResolveRepositoryName ( localName )
2013-07-05 22:26:08 +00:00
if err != nil {
2013-06-17 23:10:00 +00:00
return err
}
2013-05-20 17:58:35 +00:00
out = utils . NewWriteFlusher ( out )
2013-07-09 00:26:50 +00:00
img , err := srv . runtime . graph . Get ( localName )
2013-10-22 18:49:13 +00:00
r , err2 := registry . NewRegistry ( authConfig , srv . HTTPRequestFactory ( metaHeaders ) , endpoint )
2013-06-17 18:13:40 +00:00
if err2 != nil {
return err2
}
2013-06-05 22:12:50 +00:00
2013-05-06 11:34:31 +00:00
if err != nil {
2013-07-09 00:26:50 +00:00
reposLen := len ( srv . runtime . repositories . Repositories [ localName ] )
2013-07-24 17:10:59 +00:00
out . 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
2013-07-09 00:26:50 +00:00
if localRepo , exists := srv . runtime . repositories . Repositories [ localName ] ; exists {
2013-10-22 18:49:13 +00:00
if err := srv . pushRepository ( r , out , localName , remoteName , localRepo , sf ) ; err != nil {
2013-05-06 11:34:31 +00:00
return err
}
return nil
}
return err
}
2013-07-05 19:20:58 +00:00
var token [ ] string
2013-07-24 17:10:59 +00:00
out . Write ( sf . FormatStatus ( "" , "The push refers to an image: [%s]" , localName ) )
2013-07-22 23:44:34 +00:00
if _ , err := srv . pushImage ( r , out , remoteName , img . ID , endpoint , token , sf ) ; err != nil {
2013-05-06 11:34:31 +00:00
return err
}
return nil
}
2013-05-25 15:09:46 +00:00
func ( srv * Server ) ImageImport ( src , repo , tag string , in io . Reader , out io . Writer , sf * utils . StreamFormatter ) error {
2013-05-06 09:31:22 +00:00
var archive io . Reader
var resp * http . Response
if src == "-" {
2013-05-08 01:06:49 +00:00
archive = in
2013-05-06 09:31:22 +00:00
} else {
u , err := url . Parse ( src )
if err != nil {
2013-05-25 15:09:46 +00:00
return err
2013-05-06 09:31:22 +00:00
}
if u . Scheme == "" {
u . Scheme = "http"
u . Host = src
u . Path = ""
}
2013-07-24 17:10:59 +00:00
out . Write ( sf . FormatStatus ( "" , "Downloading from %s" , u ) )
2013-05-07 23:33:12 +00:00
// Download with curl (pretty progress bar)
// If curl is not available, fallback to http.Get()
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 {
return err
}
2013-11-28 20:16:57 +00:00
archive = utils . ProgressReader ( resp . Body , int ( resp . ContentLength ) , out , sf , true , "" , "Importing" )
2013-05-06 09:31:22 +00:00
}
img , err := srv . runtime . graph . Create ( archive , nil , "Imported from " + src , "" , nil )
if err != nil {
return err
}
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 != "" {
2013-06-04 18:00:22 +00:00
if err := srv . runtime . repositories . Set ( repo , tag , img . ID , true ) ; err != nil {
2013-05-06 09:31:22 +00:00
return err
}
}
2013-10-25 01:59:59 +00:00
out . Write ( sf . FormatStatus ( "" , img . ID ) )
2013-05-06 09:31:22 +00:00
return nil
}
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 {
2013-11-20 07:37:03 +00:00
job . Printf ( "Usage: %s" , job . Name )
return engine . StatusErr
2013-10-28 02:20:00 +00:00
}
var config Config
if err := job . ExportEnv ( & config ) ; err != nil {
2013-11-20 07:37:03 +00:00
job . Error ( err )
return engine . StatusErr
2013-10-28 02:20:00 +00:00
}
2013-06-14 16:46:04 +00:00
if config . Memory != 0 && config . Memory < 524288 {
2013-11-20 07:37:03 +00:00
job . Errorf ( "Minimum memory limit allowed is 512k" )
return engine . StatusErr
2013-06-14 16:46:04 +00:00
}
2014-01-15 22:36:13 +00:00
if config . Memory > 0 && ! srv . runtime . sysInfo . MemoryLimit {
2013-05-06 09:31:22 +00:00
config . Memory = 0
}
2014-01-15 22:36:13 +00:00
if config . Memory > 0 && ! srv . runtime . sysInfo . SwapLimit {
2013-05-06 09:31:22 +00:00
config . MemorySwap = - 1
}
2013-10-28 02:20:00 +00:00
container , buildWarnings , err := srv . runtime . Create ( & config , name )
2013-05-06 09:31:22 +00:00
if err != nil {
if srv . runtime . graph . IsNotExist ( err ) {
2013-08-18 03:03:54 +00:00
_ , tag := utils . ParseRepositoryTag ( config . Image )
if tag == "" {
tag = DEFAULTTAG
}
2013-11-20 07:37:03 +00:00
job . Errorf ( "No such image: %s (tag: %s)" , config . Image , tag )
return engine . StatusErr
2013-05-06 09:31:22 +00:00
}
2013-11-20 07:37:03 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-06 09:31:22 +00:00
}
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "create" , container . ID , srv . runtime . repositories . ImageName ( container . Image ) )
2013-11-14 06:08:08 +00:00
// FIXME: this is necessary because runtime.Create might return a nil container
// 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 {
job . Errorf ( "%s\n" , warning )
}
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 {
job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
return engine . StatusErr
}
name := job . Args [ 0 ]
t := job . GetenvInt ( "t" )
if t == - 1 {
t = 10
}
2013-05-06 09:31:22 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
2014-01-07 01:34:51 +00:00
if err := container . Restart ( int ( t ) ) ; err != nil {
job . Errorf ( "Cannot restart container %s: %s\n" , name , err )
return engine . StatusErr
2013-05-06 09:31:22 +00:00
}
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "restart" , container . ID , srv . runtime . repositories . ImageName ( container . Image ) )
2013-05-06 09:31:22 +00:00
} else {
2014-01-07 01:34:51 +00:00
job . Errorf ( "No such container: %s\n" , name )
return engine . StatusErr
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 {
job . Errorf ( "Not enough arguments. Usage: %s CONTAINER\n" , job . Name )
return engine . StatusErr
}
name := job . Args [ 0 ]
removeVolume := job . GetenvBool ( "removeVolume" )
removeLink := job . GetenvBool ( "removeLink" )
2013-10-25 23:49:49 +00:00
container := srv . runtime . Get ( name )
2013-10-05 02:25:15 +00:00
if removeLink {
2013-10-25 23:49:49 +00:00
if container == nil {
2013-12-11 23:23:38 +00:00
job . Errorf ( "No such link: %s" , name )
return engine . StatusErr
2013-10-05 02:25:15 +00:00
}
2014-01-01 23:06:50 +00:00
name , err := getFullName ( name )
2013-11-04 17:28:40 +00:00
if err != nil {
2013-12-11 23:23:38 +00:00
job . Error ( err )
return engine . StatusErr
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 == "/" {
2013-12-11 23:23:38 +00:00
job . Errorf ( "Conflict, cannot remove the default name of the container" )
return engine . StatusErr
2013-10-30 18:45:11 +00:00
}
2013-10-05 02:25:15 +00:00
pe := srv . runtime . containerGraph . Get ( parent )
2013-10-29 02:19:31 +00:00
if pe == nil {
2013-12-11 23:23:38 +00:00
job . Errorf ( "Cannot get parent %s for name %s" , parent , name )
return engine . StatusErr
2013-10-05 02:25:15 +00:00
}
2013-10-29 02:19:31 +00:00
parentContainer := srv . runtime . Get ( pe . ID ( ) )
2013-10-05 02:25:15 +00:00
2013-10-29 02:19:31 +00:00
if parentContainer != nil && parentContainer . activeLinks != nil {
if link , exists := parentContainer . activeLinks [ n ] ; exists {
link . Disable ( )
} else {
utils . Debugf ( "Could not find active link for %s" , name )
}
2013-10-25 23:49:49 +00:00
}
2013-10-05 02:25:15 +00:00
if err := srv . runtime . containerGraph . Delete ( name ) ; err != nil {
2013-12-11 23:23:38 +00:00
job . Error ( err )
return engine . StatusErr
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 ( ) {
2013-12-11 23:23:38 +00:00
job . Errorf ( "Impossible to remove a running container, please stop it first" )
return engine . StatusErr
2013-06-20 15:45:30 +00:00
}
2013-05-06 09:52:15 +00:00
volumes := make ( map [ string ] struct { } )
2013-11-13 06:59:24 +00:00
binds := make ( map [ string ] struct { } )
for _ , bind := range container . hostConfig . Binds {
splitBind := strings . Split ( bind , ":" )
source := splitBind [ 0 ]
binds [ source ] = struct { } { }
}
2013-05-06 09:52:15 +00:00
// Store all the deleted containers volumes
for _ , volumeId := range container . Volumes {
2013-11-13 06:59:24 +00:00
// Skip the volumes mounted from external
if _ , exists := binds [ volumeId ] ; exists {
continue
}
volumeId = strings . TrimSuffix ( volumeId , "/layer" )
2013-10-08 16:35:47 +00:00
volumeId = filepath . Base ( volumeId )
2013-05-06 09:52:15 +00:00
volumes [ volumeId ] = struct { } { }
}
2013-05-06 09:31:22 +00:00
if err := srv . runtime . Destroy ( container ) ; err != nil {
2013-12-11 23:23:38 +00:00
job . Errorf ( "Cannot destroy container %s: %s" , name , err )
return engine . StatusErr
2013-05-06 09:31:22 +00:00
}
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "destroy" , container . ID , srv . runtime . repositories . ImageName ( container . Image ) )
2013-05-06 09:52:15 +00:00
2013-05-10 02:19:55 +00:00
if removeVolume {
2013-05-06 09:52:15 +00:00
// Retrieve all volumes from all remaining containers
usedVolumes := make ( map [ string ] * Container )
for _ , container := range srv . runtime . List ( ) {
for _ , containerVolumeId := range container . Volumes {
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
}
if err := srv . runtime . volumes . Delete ( volumeId ) ; err != nil {
2013-12-11 23:23:38 +00:00
job . Error ( err )
return engine . StatusErr
2013-05-06 09:52:15 +00:00
}
}
}
2013-05-06 09:31:22 +00:00
} else {
2013-12-11 23:23:38 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
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
}
2013-05-30 19:30:21 +00:00
var ErrImageReferenced = errors . New ( "Image referenced by a repository" )
2013-11-27 17:55:15 +00:00
func ( srv * Server ) deleteImageAndChildren ( id string , imgs * [ ] APIRmi , byParents map [ string ] [ ] * Image ) error {
2013-05-30 19:30:21 +00:00
// If the image is referenced by a repo, do not delete
2013-06-10 21:05:54 +00:00
if len ( srv . runtime . repositories . ByID ( ) [ id ] ) != 0 {
2013-05-30 19:30:21 +00:00
return ErrImageReferenced
}
// If the image is not referenced but has children, go recursive
referenced := false
for _ , img := range byParents [ id ] {
2013-11-27 17:55:15 +00:00
if err := srv . deleteImageAndChildren ( img . ID , imgs , byParents ) ; err != nil {
2013-05-30 19:30:21 +00:00
if err != ErrImageReferenced {
return err
}
2013-06-10 21:05:54 +00:00
referenced = true
2013-05-30 19:30:21 +00:00
}
2013-06-04 13:51:12 +00:00
}
2013-05-30 19:30:21 +00:00
if referenced {
return ErrImageReferenced
}
2013-05-30 22:53:45 +00:00
// If the image is not referenced and has no children, remove it
2013-11-27 17:55:15 +00:00
byParents , err := srv . runtime . graph . ByParent ( )
2013-05-30 22:53:45 +00:00
if err != nil {
return err
}
2013-12-21 00:26:02 +00:00
if len ( byParents [ id ] ) == 0 && srv . canDeleteImage ( id ) == nil {
2013-05-30 22:53:45 +00:00
if err := srv . runtime . repositories . DeleteAll ( id ) ; err != nil {
return err
}
2013-05-31 14:37:02 +00:00
err := srv . runtime . graph . Delete ( id )
if err != nil {
return err
}
2013-11-19 02:39:02 +00:00
* imgs = append ( * imgs , APIRmi { Deleted : id } )
srv . LogEvent ( "delete" , id , "" )
2013-05-31 14:37:02 +00:00
return nil
2013-05-06 09:31:22 +00:00
}
return nil
}
2013-06-10 21:05:54 +00:00
func ( srv * Server ) deleteImageParents ( img * Image , imgs * [ ] APIRmi ) error {
2013-05-30 19:30:21 +00:00
if img . Parent != "" {
parent , err := srv . runtime . graph . Get ( img . Parent )
if err != nil {
return err
}
2013-11-27 17:55:15 +00:00
byParents , err := srv . runtime . graph . ByParent ( )
if err != nil {
return err
}
2013-05-30 19:30:21 +00:00
// Remove all children images
2013-11-27 17:55:15 +00:00
if err := srv . deleteImageAndChildren ( img . Parent , imgs , byParents ) ; err != nil {
2013-05-30 19:30:21 +00:00
return err
}
2013-05-31 14:37:02 +00:00
return srv . deleteImageParents ( parent , imgs )
2013-05-06 09:31:22 +00:00
}
return nil
}
2013-06-28 19:41:09 +00:00
func ( srv * Server ) deleteImage ( img * Image , repoName , tag string ) ( [ ] APIRmi , error ) {
2013-12-16 21:29:43 +00:00
var (
imgs = [ ] APIRmi { }
tags = [ ] string { }
)
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 == "" {
2013-07-17 15:48:53 +00:00
for _ , repoAndTag := range srv . runtime . 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
}
2013-07-17 15:48:53 +00:00
} else if repoName != parsedRepo {
// the id belongs to multiple repos, like base:latest and user:test,
// in that case return conflict
2013-12-17 12:31:35 +00:00
return nil , fmt . Errorf ( "Conflict, cannot delete image %s because it is tagged in multiple repositories" , utils . TruncateID ( img . ID ) )
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
2013-07-17 15:48:53 +00:00
//Untag the current image
2013-10-21 23:54:02 +00:00
for _ , tag := range tags {
tagDeleted , err := srv . runtime . repositories . Delete ( repoName , tag )
if err != nil {
return nil , err
}
if tagDeleted {
2013-10-25 01:59:59 +00:00
imgs = append ( imgs , APIRmi { Untagged : img . ID } )
srv . LogEvent ( "untag" , img . ID , "" )
2013-10-21 23:54:02 +00:00
}
2013-05-30 19:30:21 +00:00
}
2013-12-16 21:29:43 +00:00
2013-06-10 21:05:54 +00:00
if len ( srv . runtime . repositories . ByID ( ) [ img . ID ] ) == 0 {
2013-11-27 17:55:15 +00:00
if err := srv . deleteImageAndChildren ( img . ID , & imgs , nil ) ; err != nil {
2013-05-30 22:53:45 +00:00
if err != ErrImageReferenced {
2013-06-28 19:41:09 +00:00
return imgs , err
2013-05-30 22:53:45 +00:00
}
2013-05-31 14:37:02 +00:00
} else if err := srv . deleteImageParents ( img , & imgs ) ; err != nil {
2013-05-30 19:30:21 +00:00
if err != ErrImageReferenced {
2013-06-28 19:41:09 +00:00
return imgs , err
2013-05-30 19:30:21 +00:00
}
}
}
2013-06-28 19:41:09 +00:00
return imgs , nil
2013-05-30 19:30:21 +00:00
}
2013-06-28 19:41:09 +00:00
func ( srv * Server ) ImageDelete ( name string , autoPrune bool ) ( [ ] APIRmi , error ) {
2013-12-16 21:29:43 +00:00
var (
repository , tag string
2013-12-21 00:26:02 +00:00
img , err = srv . runtime . repositories . LookupImage ( name )
2013-12-16 21:29:43 +00:00
)
2013-05-30 22:53:45 +00:00
if err != nil {
2013-05-31 14:37:02 +00:00
return nil , fmt . Errorf ( "No such image: %s" , name )
}
2013-12-16 21:29:43 +00:00
// FIXME: What does autoPrune mean ?
2013-05-31 14:37:02 +00:00
if ! autoPrune {
2013-06-10 21:05:54 +00:00
if err := srv . runtime . graph . Delete ( img . ID ) ; err != nil {
2013-10-30 18:45:11 +00:00
return nil , fmt . Errorf ( "Cannot delete image %s: %s" , name , err )
2013-05-31 14:37:02 +00:00
}
return nil , nil
2013-05-30 22:53:45 +00:00
}
2013-11-08 23:01:01 +00:00
2013-12-16 21:29:43 +00:00
if ! strings . Contains ( img . ID , name ) {
repository , tag = utils . ParseRepositoryTag ( name )
}
2013-11-08 23:01:01 +00:00
2013-12-16 21:29:43 +00:00
// If we have a repo and the image is not referenced anywhere else
// then just perform an untag and do not validate.
//
// i.e. only validate if we are performing an actual delete and not
// an untag op
2013-12-21 00:26:02 +00:00
if repository != "" && len ( srv . runtime . repositories . ByID ( ) [ img . ID ] ) == 1 {
// Prevent deletion if image is used by a container
if err := srv . canDeleteImage ( img . ID ) ; err != nil {
return nil , err
}
2013-11-08 23:01:01 +00:00
}
2013-12-21 00:26:02 +00:00
return srv . deleteImage ( img , repository , tag )
}
2013-11-08 23:01:01 +00:00
2013-12-21 00:26:02 +00:00
func ( srv * Server ) canDeleteImage ( imgID string ) error {
for _ , container := range srv . runtime . List ( ) {
parent , err := srv . runtime . repositories . LookupImage ( container . Image )
if err != nil {
return err
}
2013-12-16 21:29:43 +00:00
2013-12-21 00:26:02 +00:00
if err := parent . WalkHistory ( func ( p * Image ) error {
if imgID == p . ID {
return fmt . Errorf ( "Conflict, cannot delete %s because the container %s is using it" , utils . TruncateID ( imgID ) , utils . TruncateID ( container . ID ) )
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
}
2013-07-02 22:27:22 +00:00
func ( srv * Server ) ImageGetCached ( imgID string , config * Config ) ( * Image , error ) {
2013-05-19 17:46:24 +00:00
// Retrieve all images
2013-09-01 03:31:21 +00:00
images , err := srv . runtime . 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-01-01 16:38:25 +00:00
var match * Image
2014-01-07 08:15:04 +00:00
for elem := range imageMap [ imgID ] {
2013-05-19 17:46:24 +00:00
img , err := srv . runtime . graph . Get ( elem )
if err != nil {
return nil , err
}
if CompareConfig ( & 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-01-03 14:47:22 +00:00
func ( srv * Server ) RegisterLinks ( container * Container , hostConfig * HostConfig ) error {
2013-10-05 02:25:15 +00:00
runtime := srv . runtime
2013-10-29 02:19:31 +00:00
2013-10-05 02:25:15 +00:00
if hostConfig != nil && hostConfig . Links != nil {
for _ , l := range hostConfig . Links {
parts , err := parseLink ( l )
if err != nil {
return err
}
2013-10-28 23:58:59 +00:00
child , err := srv . runtime . GetByName ( parts [ "name" ] )
if err != nil {
return err
}
if child == nil {
return fmt . Errorf ( "Could not get container for %s" , parts [ "name" ] )
2013-10-18 21:15:24 +00:00
}
2013-10-28 23:58:59 +00:00
if err := runtime . RegisterLink ( container , child , parts [ "alias" ] ) ; err != nil {
2013-10-05 02:25:15 +00:00
return err
}
}
2013-10-29 02:19:31 +00:00
// After we load all the links into the runtime
// set them to nil on the hostconfig
hostConfig . Links = nil
2013-10-31 21:58:43 +00:00
if err := container . writeHostConfig ( ) ; err != nil {
2013-10-29 02:19:31 +00:00
return err
}
2013-10-05 02:25:15 +00:00
}
2013-10-28 23:58:59 +00:00
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 {
2013-11-20 07:37:03 +00:00
job . Errorf ( "Usage: %s container_id" , job . Name )
return engine . StatusErr
2013-10-27 02:24:01 +00:00
}
name := job . Args [ 0 ]
2013-10-28 23:58:59 +00:00
runtime := srv . runtime
container := runtime . Get ( name )
2013-10-18 22:56:52 +00:00
2013-10-28 23:58:59 +00:00
if container == nil {
2013-11-20 07:37:03 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
2013-10-28 23:58:59 +00:00
}
2013-10-27 02:24:01 +00:00
// If no environment was set, then no hostconfig was passed.
if len ( job . Environ ( ) ) > 0 {
var hostConfig HostConfig
if err := job . ExportEnv ( & hostConfig ) ; err != nil {
2013-11-20 07:37:03 +00:00
job . Error ( err )
return engine . StatusErr
2013-10-27 02:24:01 +00:00
}
2013-11-13 00:36:20 +00:00
// Validate the HostConfig binds. Make sure that:
2013-11-13 19:25:55 +00:00
// 1) the source of a bind mount isn't /
// The bind mount "/:/foo" isn't allowed.
// 2) Check that the source exists
// The source to be bind mounted must exist.
for _ , bind := range hostConfig . Binds {
splitBind := strings . Split ( bind , ":" )
source := splitBind [ 0 ]
// refuse to bind mount "/" to the container
if source == "/" {
2013-11-20 07:37:03 +00:00
job . Errorf ( "Invalid bind mount '%s' : source can't be '/'" , bind )
return engine . StatusErr
2013-11-13 19:25:55 +00:00
}
// ensure the source exists on the host
_ , err := os . Stat ( source )
if err != nil && os . IsNotExist ( err ) {
2013-11-20 07:37:03 +00:00
job . Errorf ( "Invalid bind mount '%s' : source doesn't exist" , bind )
return engine . StatusErr
2013-11-13 19:25:55 +00:00
}
}
2013-10-27 02:24:01 +00:00
// Register any links from the host config before starting the container
2014-01-03 14:47:22 +00:00
if err := srv . RegisterLinks ( container , & hostConfig ) ; err != nil {
2013-11-20 07:37:03 +00:00
job . Error ( err )
return engine . StatusErr
2013-10-27 02:24:01 +00:00
}
container . hostConfig = & hostConfig
2013-10-31 21:58:43 +00:00
container . ToDisk ( )
}
if err := container . Start ( ) ; err != nil {
2013-11-20 07:37:03 +00:00
job . Errorf ( "Cannot start container %s: %s" , name , err )
return engine . StatusErr
2013-10-05 02:25:15 +00:00
}
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "start" , container . ID , runtime . 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 {
job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
2013-11-17 03:00:16 +00:00
return engine . StatusErr
}
name := job . Args [ 0 ]
2013-12-11 23:36:50 +00:00
t := job . GetenvInt ( "t" )
if t == - 1 {
2013-11-17 03:00:16 +00:00
t = 10
}
2013-05-06 09:31:22 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
2013-11-17 03:00:16 +00:00
if err := container . Stop ( int ( t ) ) ; err != nil {
job . Errorf ( "Cannot stop container %s: %s\n" , name , err )
return engine . StatusErr
2013-05-06 09:31:22 +00:00
}
2013-10-25 01:59:59 +00:00
srv . LogEvent ( "stop" , container . ID , srv . runtime . repositories . ImageName ( container . Image ) )
2013-05-06 09:31:22 +00:00
} else {
2013-11-17 03:00:16 +00:00
job . Errorf ( "No such container: %s\n" , name )
return engine . StatusErr
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 {
job . Errorf ( "Usage: %s" , job . Name )
return engine . StatusErr
}
name := job . Args [ 0 ]
2013-05-06 09:31:22 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
2013-11-25 01:05:59 +00:00
status := container . Wait ( )
job . Printf ( "%d\n" , status )
return engine . StatusOK
2013-05-06 09:31:22 +00:00
}
2013-11-25 01:05:59 +00:00
job . Errorf ( "%s: no such container: %s" , job . Name , name )
return engine . StatusErr
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 {
job . Errorf ( "Not enough arguments. Usage: %s CONTAINER HEIGHT WIDTH\n" , job . Name )
return engine . StatusErr
}
name := job . Args [ 0 ]
height , err := strconv . Atoi ( job . Args [ 1 ] )
if err != nil {
job . Error ( err )
return engine . StatusErr
}
width , err := strconv . Atoi ( job . Args [ 2 ] )
if err != nil {
job . Error ( err )
return engine . StatusErr
}
2013-05-24 02:33:28 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
2013-12-12 02:25:30 +00:00
if err := container . Resize ( height , width ) ; err != nil {
job . Error ( err )
return engine . StatusErr
}
return engine . StatusOK
2013-05-24 02:33:28 +00:00
}
2013-12-12 02:25:30 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
2013-05-24 02:33:28 +00:00
}
2014-01-08 22:05:05 +00:00
func ( srv * Server ) ContainerAttach ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 1 {
job . Errorf ( "Usage: %s CONTAINER\n" , job . Name )
return engine . StatusErr
}
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" )
)
2013-05-08 06:32:17 +00:00
container := srv . runtime . Get ( name )
if container == nil {
2014-01-08 22:05:05 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
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 {
2013-11-21 20:21:03 +00:00
if container . State . IsGhost ( ) {
2014-01-08 22:05:05 +00:00
job . Errorf ( "Impossible to attach to a ghost container" )
return engine . StatusErr
2013-05-08 06:32:17 +00:00
}
2013-05-06 09:31:22 +00:00
2013-05-08 06:32:17 +00:00
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
2013-05-08 06:32:17 +00:00
<- container . Attach ( cStdin , cStdinCloser , cStdout , cStderr )
// If we are in stdinonce mode, wait for the process to end
// otherwise, simply return
if container . Config . StdinOnce && ! container . Config . Tty {
container . Wait ( )
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
}
func ( srv * Server ) ContainerInspect ( name string ) ( * Container , error ) {
if container := srv . runtime . Get ( name ) ; container != nil {
return container , nil
}
return nil , fmt . Errorf ( "No such container: %s" , name )
}
func ( srv * Server ) ImageInspect ( name string ) ( * Image , error ) {
if image , err := srv . runtime . repositories . LookupImage ( name ) ; err == nil && image != nil {
return image , nil
}
return nil , fmt . Errorf ( "No such image: %s" , name )
}
2014-01-10 00:37:56 +00:00
func ( srv * Server ) ContainerCopy ( job * engine . Job ) engine . Status {
if len ( job . Args ) != 2 {
job . Errorf ( "Usage: %s CONTAINER RESOURCE\n" , job . Name )
return engine . StatusErr
}
var (
name = job . Args [ 0 ]
resource = job . Args [ 1 ]
)
2013-07-17 04:07:41 +00:00
if container := srv . runtime . Get ( name ) ; container != nil {
data , err := container . Copy ( resource )
if err != nil {
2014-01-10 00:37:56 +00:00
job . Error ( err )
return engine . StatusErr
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 {
job . Error ( err )
return engine . StatusErr
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-10 00:37:56 +00:00
job . Errorf ( "No such container: %s" , name )
return engine . StatusErr
2013-07-17 04:07:41 +00:00
}
2013-10-27 02:24:01 +00:00
func NewServer ( eng * engine . Engine , config * DaemonConfig ) ( * Server , error ) {
2013-10-05 02:25:15 +00:00
runtime , err := NewRuntime ( config )
2013-05-06 09:31:22 +00:00
if err != nil {
return nil , err
}
srv := & Server {
2013-10-27 02:24:01 +00:00
Eng : eng ,
2013-06-17 23:10:00 +00:00
runtime : runtime ,
2013-11-20 21:51:05 +00:00
pullingPool : make ( map [ string ] chan struct { } ) ,
pushingPool : make ( map [ string ] chan struct { } ) ,
2013-07-12 16:29:23 +00:00
events : make ( [ ] utils . JSONMessage , 0 , 64 ) , //only keeps the 64 last events
listeners : make ( map [ string ] chan utils . JSONMessage ) ,
2013-05-06 09:31:22 +00:00
}
2013-05-16 00:17:33 +00:00
runtime . srv = srv
2013-05-06 09:31:22 +00:00
return srv , nil
}
2013-08-22 19:15:31 +00:00
func ( srv * Server ) HTTPRequestFactory ( metaHeaders map [ string ] [ ] string ) * utils . HTTPRequestFactory {
2013-11-25 18:58:17 +00:00
srv . Lock ( )
defer srv . Unlock ( )
2013-12-08 07:35:24 +00:00
v := dockerVersion ( )
httpVersion := make ( [ ] utils . VersionInfo , 0 , 4 )
httpVersion = append ( httpVersion , & simpleVersionInfo { "docker" , v . Get ( "Version" ) } )
httpVersion = append ( httpVersion , & simpleVersionInfo { "go" , v . Get ( "GoVersion" ) } )
httpVersion = append ( httpVersion , & simpleVersionInfo { "git-commit" , v . Get ( "GitCommit" ) } )
httpVersion = append ( httpVersion , & simpleVersionInfo { "kernel" , v . Get ( "KernelVersion" ) } )
2013-12-20 16:11:35 +00:00
httpVersion = append ( httpVersion , & simpleVersionInfo { "os" , v . Get ( "Os" ) } )
httpVersion = append ( httpVersion , & simpleVersionInfo { "arch" , v . Get ( "Arch" ) } )
2013-12-08 07:35:24 +00:00
ud := utils . NewHTTPUserAgentDecorator ( httpVersion ... )
2013-10-22 18:49:13 +00:00
md := & utils . HTTPMetaHeadersDecorator {
Headers : metaHeaders ,
2013-08-02 07:30:45 +00:00
}
2013-10-22 18:49:13 +00:00
factory := utils . NewHTTPRequestFactory ( ud , md )
return factory
2013-08-02 07:30:45 +00:00
}
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 )
2013-07-12 16:29:23 +00:00
for _ , c := range srv . listeners {
2013-07-18 14:35:14 +00:00
select { // non blocking channel
case c <- jm :
default :
}
2013-07-10 12:55:05 +00:00
}
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 ( )
defer srv . Unlock ( )
srv . events = append ( srv . events , jm )
}
func ( srv * Server ) GetEvents ( ) [ ] utils . JSONMessage {
srv . RLock ( )
defer srv . RUnlock ( )
return srv . events
}
2013-05-06 09:31:22 +00:00
type Server struct {
2013-11-25 18:58:17 +00:00
sync . RWMutex
2013-06-17 23:10:00 +00:00
runtime * Runtime
2013-11-20 21:51:05 +00:00
pullingPool map [ string ] chan struct { }
pushingPool map [ string ] chan struct { }
2013-07-12 16:29:23 +00:00
events [ ] utils . JSONMessage
listeners map [ string ] chan utils . JSONMessage
2013-10-27 02:24:01 +00:00
Eng * engine . Engine
2013-05-06 09:31:22 +00:00
}