2013-03-14 13:26:46 +04:00
package docker
2013-02-14 05:10:00 +04:00
import (
2013-06-15 20:38:18 +04:00
"archive/tar"
2013-08-04 03:43:20 +04:00
"bufio"
2013-02-14 05:28:13 +04:00
"bytes"
2013-09-03 22:45:49 +04:00
"encoding/base64"
2013-02-14 05:28:13 +04:00
"encoding/json"
2013-09-20 22:31:00 +04:00
"errors"
2013-02-14 05:10:00 +04:00
"fmt"
2014-01-29 23:26:54 +04:00
"github.com/dotcloud/docker/api"
2013-11-01 03:57:45 +04:00
"github.com/dotcloud/docker/archive"
2013-03-15 04:43:59 +04:00
"github.com/dotcloud/docker/auth"
2013-12-08 11:41:53 +04:00
"github.com/dotcloud/docker/engine"
2013-12-23 23:43:54 +04:00
flag "github.com/dotcloud/docker/pkg/mflag"
2014-01-16 02:36:13 +04:00
"github.com/dotcloud/docker/pkg/sysinfo"
2013-12-24 03:42:37 +04:00
"github.com/dotcloud/docker/pkg/term"
2013-12-27 05:49:41 +04:00
"github.com/dotcloud/docker/registry"
2013-05-15 02:37:35 +04:00
"github.com/dotcloud/docker/utils"
2013-02-14 05:28:13 +04:00
"io"
2013-04-22 20:17:47 +04:00
"io/ioutil"
2013-04-23 20:20:53 +04:00
"net"
2013-03-13 22:33:48 +04:00
"net/http"
2013-04-23 20:20:53 +04:00
"net/http/httputil"
2013-03-14 05:37:00 +04:00
"net/url"
2013-05-09 02:26:44 +04:00
"os"
2013-05-24 22:07:32 +04:00
"os/signal"
2013-11-21 05:41:25 +04:00
"path"
2013-05-13 13:48:27 +04:00
"reflect"
2013-10-18 03:22:14 +04:00
"regexp"
2013-08-19 16:05:47 +04:00
"runtime"
2013-08-28 02:20:35 +04:00
"sort"
2013-02-28 23:52:22 +04:00
"strconv"
2013-02-14 05:28:13 +04:00
"strings"
2013-05-24 22:07:32 +04:00
"syscall"
2013-02-14 05:28:13 +04:00
"text/tabwriter"
2013-11-28 03:07:15 +04:00
"text/template"
2013-02-14 05:28:13 +04:00
"time"
2013-02-14 05:10:00 +04:00
)
2013-04-11 23:58:23 +04:00
var (
2013-06-04 22:00:22 +04:00
GITCOMMIT string
2013-08-26 19:45:52 +04:00
VERSION string
2013-04-11 23:58:23 +04:00
)
2013-04-01 22:12:56 +04:00
2013-09-20 22:31:00 +04:00
var (
ErrConnectionRefused = errors . New ( "Can't connect to docker daemon. Is 'docker -d' running on this host?" )
)
2013-09-23 04:06:31 +04:00
func ( cli * DockerCli ) getMethod ( name string ) ( func ( ... string ) error , bool ) {
2013-05-23 20:32:39 +04:00
methodName := "Cmd" + strings . ToUpper ( name [ : 1 ] ) + strings . ToLower ( name [ 1 : ] )
2013-09-23 04:06:31 +04:00
method := reflect . ValueOf ( cli ) . MethodByName ( methodName )
if ! method . IsValid ( ) {
return nil , false
}
return method . Interface ( ) . ( func ( ... string ) error ) , true
2013-05-23 20:32:39 +04:00
}
2013-06-18 22:59:56 +04:00
func ParseCommands ( proto , addr string , args ... string ) error {
2013-06-25 01:15:52 +04:00
cli := NewDockerCli ( os . Stdin , os . Stdout , os . Stderr , proto , addr )
2013-04-22 20:17:47 +04:00
if len ( args ) > 0 {
2013-05-23 20:32:39 +04:00
method , exists := cli . getMethod ( args [ 0 ] )
2013-04-22 20:17:47 +04:00
if ! exists {
2013-04-23 20:20:53 +04:00
fmt . Println ( "Error: Command not found:" , args [ 0 ] )
2013-05-23 20:32:39 +04:00
return cli . CmdHelp ( args [ 1 : ] ... )
2013-04-22 20:17:47 +04:00
}
2013-09-23 04:06:31 +04:00
return method ( args [ 1 : ] ... )
2013-04-22 20:17:47 +04:00
}
2013-05-13 13:48:27 +04:00
return cli . CmdHelp ( args ... )
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdHelp ( args ... string ) error {
2013-05-23 20:32:39 +04:00
if len ( args ) > 0 {
method , exists := cli . getMethod ( args [ 0 ] )
if ! exists {
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . err , "Error: Command not found: %s\n" , args [ 0 ] )
2013-05-23 20:32:39 +04:00
} else {
2013-09-23 04:06:31 +04:00
method ( "--help" )
2013-05-23 20:32:39 +04:00
return nil
}
}
2014-01-29 23:26:54 +04:00
help := fmt . Sprintf ( "Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n" , api . DEFAULTUNIXSOCKET )
2013-06-25 01:15:52 +04:00
for _ , command := range [ ] [ ] string {
2013-05-30 18:08:26 +04:00
{ "attach" , "Attach to a running container" } ,
{ "build" , "Build a container from a Dockerfile" } ,
{ "commit" , "Create a new image from a container's changes" } ,
2013-07-17 08:07:41 +04:00
{ "cp" , "Copy files/folders from the containers filesystem to the host path" } ,
2013-05-30 18:08:26 +04:00
{ "diff" , "Inspect changes on a container's filesystem" } ,
2013-07-10 16:55:05 +04:00
{ "events" , "Get real time events from the server" } ,
2013-05-30 18:08:26 +04:00
{ "export" , "Stream the contents of a container as a tar archive" } ,
{ "history" , "Show the history of an image" } ,
{ "images" , "List images" } ,
{ "import" , "Create a new filesystem image from the contents of a tarball" } ,
{ "info" , "Display system-wide information" } ,
{ "insert" , "Insert a file in an image" } ,
{ "inspect" , "Return low-level information on a container" } ,
{ "kill" , "Kill a running container" } ,
2013-09-02 20:06:17 +04:00
{ "load" , "Load an image from a tar archive" } ,
2013-05-30 18:08:26 +04:00
{ "login" , "Register or Login to the docker registry server" } ,
{ "logs" , "Fetch the logs of a container" } ,
{ "port" , "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT" } ,
{ "ps" , "List containers" } ,
{ "pull" , "Pull an image or a repository from the docker registry server" } ,
{ "push" , "Push an image or a repository to the docker registry server" } ,
{ "restart" , "Restart a running container" } ,
2013-07-17 21:46:11 +04:00
{ "rm" , "Remove one or more containers" } ,
{ "rmi" , "Remove one or more images" } ,
2013-05-30 18:08:26 +04:00
{ "run" , "Run a command in a new container" } ,
2013-09-02 20:06:17 +04:00
{ "save" , "Save an image to a tar archive" } ,
2013-05-30 18:08:26 +04:00
{ "search" , "Search for an image in the docker index" } ,
{ "start" , "Start a stopped container" } ,
{ "stop" , "Stop a running container" } ,
{ "tag" , "Tag an image into a repository" } ,
2013-10-20 10:15:36 +04:00
{ "top" , "Lookup the running processes of a container" } ,
2013-05-30 18:08:26 +04:00
{ "version" , "Show the docker version information" } ,
2013-06-03 01:40:56 +04:00
{ "wait" , "Block until a container stops, then print its exit code" } ,
2013-02-14 05:10:00 +04:00
} {
2013-05-30 18:08:26 +04:00
help += fmt . Sprintf ( " %-10.10s%s\n" , command [ 0 ] , command [ 1 ] )
2013-02-14 05:10:00 +04:00
}
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . err , "%s\n" , help )
2013-04-22 20:17:47 +04:00
return nil
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdInsert ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "insert" , "IMAGE URL PATH" , "Insert a file from URL in the IMAGE at PATH" )
2013-04-25 00:37:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 3 {
cmd . Usage ( )
return nil
}
2013-05-07 21:23:50 +04:00
v := url . Values { }
v . Set ( "url" , cmd . Arg ( 1 ) )
v . Set ( "path" , cmd . Arg ( 2 ) )
2013-04-25 02:14:10 +04:00
2013-09-20 12:55:17 +04:00
return cli . stream ( "POST" , "/images/" + cmd . Arg ( 0 ) + "/insert?" + v . Encode ( ) , nil , cli . out , nil )
2013-04-25 00:37:00 +04:00
}
2013-06-15 20:38:18 +04:00
// mkBuildContext returns an archive of an empty context with the contents
// of `dockerfile` at the path ./Dockerfile
2013-11-14 10:08:08 +04:00
func MkBuildContext ( dockerfile string , files [ ] [ 2 ] string ) ( archive . Archive , error ) {
2013-06-15 20:38:18 +04:00
buf := new ( bytes . Buffer )
tw := tar . NewWriter ( buf )
2013-06-15 22:07:49 +04:00
files = append ( files , [ 2 ] string { "Dockerfile" , dockerfile } )
for _ , file := range files {
name , content := file [ 0 ] , file [ 1 ]
hdr := & tar . Header {
Name : name ,
Size : int64 ( len ( content ) ) ,
}
if err := tw . WriteHeader ( hdr ) ; err != nil {
return nil , err
}
if _ , err := tw . Write ( [ ] byte ( content ) ) ; err != nil {
return nil , err
}
2013-06-15 20:38:18 +04:00
}
if err := tw . Close ( ) ; err != nil {
return nil , err
}
return buf , nil
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdBuild ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "build" , "[OPTIONS] PATH | URL | -" , "Build a new container image from the source code at PATH" )
2013-12-23 23:43:54 +04:00
tag := cmd . String ( [ ] string { "t" , "-tag" } , "" , "Repository name (and optionally a tag) to be applied to the resulting image in case of success" )
suppressOutput := cmd . Bool ( [ ] string { "q" , "-quiet" } , false , "Suppress verbose build output" )
noCache := cmd . Bool ( [ ] string { "#no-cache" , "-no-cache" } , false , "Do not use cache when building the image" )
rm := cmd . Bool ( [ ] string { "#rm" , "-rm" } , false , "Remove intermediate containers after a successful build" )
2013-04-24 22:03:01 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-05-30 05:18:57 +04:00
if cmd . NArg ( ) != 1 {
cmd . Usage ( )
return nil
}
2013-05-23 07:07:26 +04:00
2013-05-19 21:46:24 +04:00
var (
2013-11-01 03:57:45 +04:00
context archive . Archive
2013-06-21 09:02:36 +04:00
isRemote bool
err error
2013-05-19 21:46:24 +04:00
)
2013-05-07 21:23:50 +04:00
2013-05-30 05:18:57 +04:00
if cmd . Arg ( 0 ) == "-" {
2013-06-15 20:38:18 +04:00
// As a special case, 'docker build -' will build from an empty context with the
// contents of stdin as a Dockerfile
2013-06-25 01:15:52 +04:00
dockerfile , err := ioutil . ReadAll ( cli . in )
2013-05-29 02:22:34 +04:00
if err != nil {
return err
}
2013-11-14 10:08:08 +04:00
context , err = MkBuildContext ( string ( dockerfile ) , nil )
2013-06-21 09:02:36 +04:00
} else if utils . IsURL ( cmd . Arg ( 0 ) ) || utils . IsGIT ( cmd . Arg ( 0 ) ) {
isRemote = true
2013-06-15 20:38:18 +04:00
} else {
2013-09-03 22:45:49 +04:00
if _ , err := os . Stat ( cmd . Arg ( 0 ) ) ; err != nil {
2013-07-22 18:52:05 +04:00
return err
}
2013-12-02 09:52:45 +04:00
filename := path . Join ( cmd . Arg ( 0 ) , "Dockerfile" )
if _ , err = os . Stat ( filename ) ; os . IsNotExist ( err ) {
return fmt . Errorf ( "no Dockerfile found in %s" , cmd . Arg ( 0 ) )
}
2013-11-01 03:57:45 +04:00
context , err = archive . Tar ( cmd . Arg ( 0 ) , archive . Uncompressed )
2013-05-24 05:32:56 +04:00
}
2013-06-21 09:02:36 +04:00
var body io . Reader
2013-06-18 05:26:41 +04:00
// Setup an upload progress bar
2013-08-12 21:53:06 +04:00
// FIXME: ProgressReader shouldn't be this annoying to use
2013-06-21 09:02:36 +04:00
if context != nil {
sf := utils . NewStreamFormatter ( false )
2013-11-29 00:16:57 +04:00
body = utils . ProgressReader ( ioutil . NopCloser ( context ) , 0 , cli . err , sf , true , "" , "Uploading context" )
2013-05-30 05:18:57 +04:00
}
2013-06-15 20:38:18 +04:00
// Upload the build context
2013-05-30 23:08:21 +04:00
v := & url . Values { }
v . Set ( "t" , * tag )
2013-07-12 04:12:25 +04:00
if * suppressOutput {
v . Set ( "q" , "1" )
}
2013-06-07 03:09:46 +04:00
if isRemote {
v . Set ( "remote" , cmd . Arg ( 0 ) )
}
2013-08-02 20:18:54 +04:00
if * noCache {
v . Set ( "nocache" , "1" )
}
2013-09-10 22:39:47 +04:00
if * rm {
v . Set ( "rm" , "1" )
}
2013-10-12 16:14:52 +04:00
2014-01-04 00:13:32 +04:00
cli . LoadConfigFile ( )
2013-10-12 16:14:52 +04:00
headers := http . Header ( make ( map [ string ] [ ] string ) )
2013-12-07 02:27:10 +04:00
buf , err := json . Marshal ( cli . configFile )
if err != nil {
return err
}
2014-01-04 00:13:32 +04:00
headers . Add ( "X-Registry-Config" , base64 . URLEncoding . EncodeToString ( buf ) )
2013-12-07 02:27:10 +04:00
2013-06-21 09:02:36 +04:00
if context != nil {
2013-10-12 16:14:52 +04:00
headers . Set ( "Content-Type" , "application/tar" )
2013-06-18 22:59:56 +04:00
}
2013-10-12 16:14:52 +04:00
err = cli . stream ( "POST" , fmt . Sprintf ( "/build?%s" , v . Encode ( ) ) , body , cli . out , headers )
if jerr , ok := err . ( * utils . JSONError ) ; ok {
2013-12-27 05:49:41 +04:00
// If no error code is set, default to 1
if jerr . Code == 0 {
jerr . Code = 1
}
2013-10-12 16:14:52 +04:00
return & utils . StatusError { Status : jerr . Message , StatusCode : jerr . Code }
2013-05-23 07:07:26 +04:00
}
2013-10-12 16:14:52 +04:00
return err
2013-04-24 22:03:01 +04:00
}
2013-03-15 04:43:59 +04:00
// 'docker login': login / register a user to registry service.
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdLogin ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "login" , "[OPTIONS] [SERVER]" , "Register or Login to a docker registry server, if no server is specified \"" + auth . IndexServerAddress ( ) + "\" is the default." )
2013-08-04 03:43:20 +04:00
2013-08-18 09:28:05 +04:00
var username , password , email string
2013-12-23 23:43:54 +04:00
cmd . StringVar ( & username , [ ] string { "u" , "-username" } , "" , "username" )
cmd . StringVar ( & password , [ ] string { "p" , "-password" } , "" , "password" )
cmd . StringVar ( & email , [ ] string { "e" , "-email" } , "" , "email" )
2013-06-22 04:37:02 +04:00
err := cmd . Parse ( args )
2013-05-06 15:34:31 +04:00
if err != nil {
2013-03-15 18:49:27 +04:00
return nil
2013-05-06 15:34:31 +04:00
}
2013-09-03 22:45:49 +04:00
serverAddress := auth . IndexServerAddress ( )
if len ( cmd . Args ( ) ) > 0 {
2014-02-06 04:49:43 +04:00
serverAddress = cmd . Arg ( 0 )
2013-09-03 22:45:49 +04:00
}
2013-04-03 23:25:19 +04:00
2013-08-04 03:43:20 +04:00
promptDefault := func ( prompt string , configDefault string ) {
2013-07-27 21:00:36 +04:00
if configDefault == "" {
fmt . Fprintf ( cli . out , "%s: " , prompt )
} else {
fmt . Fprintf ( cli . out , "%s (%s): " , prompt , configDefault )
}
}
2013-08-04 04:27:15 +04:00
readInput := func ( in io . Reader , out io . Writer ) string {
2013-08-04 03:43:20 +04:00
reader := bufio . NewReader ( in )
2013-09-02 03:12:07 +04:00
line , _ , err := reader . ReadLine ( )
2013-08-04 03:43:20 +04:00
if err != nil {
2013-08-04 04:27:15 +04:00
fmt . Fprintln ( out , err . Error ( ) )
os . Exit ( 1 )
2013-08-04 03:43:20 +04:00
}
2013-09-02 03:12:07 +04:00
return string ( line )
2013-08-04 03:43:20 +04:00
}
2013-09-02 03:12:07 +04:00
cli . LoadConfigFile ( )
2013-10-08 08:46:29 +04:00
authconfig , ok := cli . configFile . Configs [ serverAddress ]
2013-07-23 19:04:31 +04:00
if ! ok {
authconfig = auth . AuthConfig { }
}
2013-08-04 03:43:20 +04:00
if username == "" {
2013-07-27 21:17:57 +04:00
promptDefault ( "Username" , authconfig . Username )
2013-08-04 04:27:15 +04:00
username = readInput ( cli . in , cli . out )
2013-06-21 14:00:25 +04:00
if username == "" {
2013-07-23 19:04:31 +04:00
username = authconfig . Username
2013-06-21 14:00:25 +04:00
}
2013-03-15 04:43:59 +04:00
}
2013-07-23 19:04:31 +04:00
if username != authconfig . Username {
2013-08-04 03:43:20 +04:00
if password == "" {
oldState , _ := term . SaveState ( cli . terminalFd )
2013-07-01 16:31:16 +04:00
fmt . Fprintf ( cli . out , "Password: " )
2013-08-18 08:29:37 +04:00
term . DisableEcho ( cli . terminalFd , oldState )
2013-08-04 04:27:15 +04:00
password = readInput ( cli . in , cli . out )
2013-08-04 08:30:07 +04:00
fmt . Fprint ( cli . out , "\n" )
2013-08-04 03:43:20 +04:00
term . RestoreTerminal ( cli . terminalFd , oldState )
2013-06-21 14:00:25 +04:00
if password == "" {
return fmt . Errorf ( "Error : Password Required" )
}
2013-03-15 18:49:27 +04:00
}
2013-08-04 03:43:20 +04:00
if email == "" {
2013-07-27 21:17:57 +04:00
promptDefault ( "Email" , authconfig . Email )
2013-08-04 04:27:15 +04:00
email = readInput ( cli . in , cli . out )
2013-06-21 14:00:25 +04:00
if email == "" {
2013-07-23 19:04:31 +04:00
email = authconfig . Email
2013-06-21 14:00:25 +04:00
}
2013-03-15 04:43:59 +04:00
}
} else {
2013-07-23 19:04:31 +04:00
password = authconfig . Password
email = authconfig . Email
2013-03-15 04:43:59 +04:00
}
2013-07-23 19:04:31 +04:00
authconfig . Username = username
authconfig . Password = password
authconfig . Email = email
2013-09-03 22:45:49 +04:00
authconfig . ServerAddress = serverAddress
cli . configFile . Configs [ serverAddress ] = authconfig
2013-05-06 15:34:31 +04:00
2014-02-01 05:14:59 +04:00
stream , statusCode , err := cli . call ( "POST" , "/auth" , cli . configFile . Configs [ serverAddress ] , false )
2013-06-14 17:38:51 +04:00
if statusCode == 401 {
2013-09-03 22:45:49 +04:00
delete ( cli . configFile . Configs , serverAddress )
2013-07-23 19:04:31 +04:00
auth . SaveConfig ( cli . configFile )
2013-06-14 17:38:51 +04:00
return err
}
2013-03-15 04:43:59 +04:00
if err != nil {
2013-05-06 15:34:31 +04:00
return err
}
2014-01-25 04:39:41 +04:00
var out2 engine . Env
2014-02-01 05:14:59 +04:00
err = out2 . Decode ( stream )
2013-05-06 15:34:31 +04:00
if err != nil {
2013-07-23 19:04:31 +04:00
cli . configFile , _ = auth . LoadConfig ( os . Getenv ( "HOME" ) )
2013-05-06 15:34:31 +04:00
return err
2013-03-15 04:43:59 +04:00
}
2013-07-23 19:04:31 +04:00
auth . SaveConfig ( cli . configFile )
2014-01-25 04:39:41 +04:00
if out2 . Get ( "Status" ) != "" {
fmt . Fprintf ( cli . out , "%s\n" , out2 . Get ( "Status" ) )
2013-03-15 07:21:03 +04:00
}
2013-03-15 04:43:59 +04:00
return nil
}
2013-02-26 23:43:54 +04:00
// 'docker wait': block until a container stops
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdWait ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "wait" , "CONTAINER [CONTAINER...]" , "Block until a container stops, then print its exit code." )
2013-02-26 23:43:54 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) < 1 {
cmd . Usage ( )
return nil
}
2013-11-12 02:44:51 +04:00
var encounteredError error
2013-02-26 23:43:54 +04:00
for _ , name := range cmd . Args ( ) {
2013-09-06 03:54:03 +04:00
status , err := waitForExit ( cli , name )
2013-04-24 16:01:40 +04:00
if err != nil {
2013-11-12 02:44:51 +04:00
fmt . Fprintf ( cli . err , "%s\n" , err )
encounteredError = fmt . Errorf ( "Error: failed to wait one or more containers" )
2013-02-26 23:43:54 +04:00
} else {
2013-09-06 03:54:03 +04:00
fmt . Fprintf ( cli . out , "%d\n" , status )
2013-02-26 23:43:54 +04:00
}
}
2013-11-12 02:44:51 +04:00
return encounteredError
2013-02-26 23:43:54 +04:00
}
2013-03-13 04:34:15 +04:00
// 'docker version': show version information
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdVersion ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "version" , "" , "Show the docker version information." )
2013-04-22 20:17:47 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
2013-04-19 08:08:33 +04:00
}
2013-05-15 18:16:46 +04:00
2013-04-22 20:17:47 +04:00
if cmd . NArg ( ) > 0 {
cmd . Usage ( )
return nil
}
2013-08-30 04:46:43 +04:00
if VERSION != "" {
fmt . Fprintf ( cli . out , "Client version: %s\n" , VERSION )
}
2013-08-19 16:05:47 +04:00
fmt . Fprintf ( cli . out , "Go version (client): %s\n" , runtime . Version ( ) )
if GITCOMMIT != "" {
fmt . Fprintf ( cli . out , "Git commit (client): %s\n" , GITCOMMIT )
}
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/version" , nil , false ) )
2013-04-22 20:17:47 +04:00
if err != nil {
return err
2013-04-11 23:58:23 +04:00
}
2013-04-22 20:17:47 +04:00
2013-12-08 11:41:53 +04:00
out := engine . NewOutput ( )
remoteVersion , err := out . AddEnv ( )
2013-04-22 20:17:47 +04:00
if err != nil {
2013-12-08 11:41:53 +04:00
utils . Errorf ( "Error reading remote version: %s\n" , err )
2013-04-22 20:17:47 +04:00
return err
}
2013-12-08 11:41:53 +04:00
if _ , err := out . Write ( body ) ; err != nil {
utils . Errorf ( "Error reading remote version: %s\n" , err )
return err
2013-04-11 23:58:23 +04:00
}
2013-12-08 11:41:53 +04:00
out . Close ( )
fmt . Fprintf ( cli . out , "Server version: %s\n" , remoteVersion . Get ( "Version" ) )
fmt . Fprintf ( cli . out , "Git commit (server): %s\n" , remoteVersion . Get ( "GitCommit" ) )
fmt . Fprintf ( cli . out , "Go version (server): %s\n" , remoteVersion . Get ( "GoVersion" ) )
2013-07-03 21:11:00 +04:00
release := utils . GetReleaseVersion ( )
if release != "" {
fmt . Fprintf ( cli . out , "Last stable version: %s" , release )
2013-12-08 11:41:53 +04:00
if ( VERSION != "" || remoteVersion . Exists ( "Version" ) ) && ( strings . Trim ( VERSION , "-dev" ) != release || strings . Trim ( remoteVersion . Get ( "Version" ) , "-dev" ) != release ) {
2013-07-03 21:11:00 +04:00
fmt . Fprintf ( cli . out , ", please update docker" )
}
fmt . Fprintf ( cli . out , "\n" )
}
2013-03-13 04:34:15 +04:00
return nil
}
2013-02-14 05:10:00 +04:00
// 'docker info': display system-wide information.
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdInfo ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "info" , "" , "Display system-wide information" )
2013-03-12 03:11:46 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-03-12 19:59:32 +04:00
if cmd . NArg ( ) > 0 {
2013-03-12 03:11:46 +04:00
cmd . Usage ( )
return nil
2013-03-12 15:24:26 +04:00
}
2013-03-30 21:33:10 +04:00
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/info" , nil , false ) )
2013-04-22 20:17:47 +04:00
if err != nil {
return err
2013-03-30 21:33:10 +04:00
}
2013-05-09 03:40:48 +04:00
2013-12-11 22:35:21 +04:00
out := engine . NewOutput ( )
remoteInfo , err := out . AddEnv ( )
if err != nil {
2013-04-22 20:17:47 +04:00
return err
}
2013-06-01 02:53:57 +04:00
2013-12-11 22:35:21 +04:00
if _ , err := out . Write ( body ) ; err != nil {
utils . Errorf ( "Error reading remote info: %s\n" , err )
return err
}
out . Close ( )
fmt . Fprintf ( cli . out , "Containers: %d\n" , remoteInfo . GetInt ( "Containers" ) )
fmt . Fprintf ( cli . out , "Images: %d\n" , remoteInfo . GetInt ( "Images" ) )
fmt . Fprintf ( cli . out , "Driver: %s\n" , remoteInfo . Get ( "Driver" ) )
2013-12-13 01:35:50 +04:00
var driverStatus [ ] [ 2 ] string
if err := remoteInfo . GetJson ( "DriverStatus" , & driverStatus ) ; err != nil {
return err
}
for _ , pair := range driverStatus {
fmt . Fprintf ( cli . out , " %s: %s\n" , pair [ 0 ] , pair [ 1 ] )
2013-11-15 14:04:02 +04:00
}
2013-12-11 22:35:21 +04:00
if remoteInfo . GetBool ( "Debug" ) || os . Getenv ( "DEBUG" ) != "" {
fmt . Fprintf ( cli . out , "Debug mode (server): %v\n" , remoteInfo . GetBool ( "Debug" ) )
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . out , "Debug mode (client): %v\n" , os . Getenv ( "DEBUG" ) != "" )
2013-12-11 22:35:21 +04:00
fmt . Fprintf ( cli . out , "Fds: %d\n" , remoteInfo . GetInt ( "NFd" ) )
fmt . Fprintf ( cli . out , "Goroutines: %d\n" , remoteInfo . GetInt ( "NGoroutines" ) )
2014-01-17 03:09:00 +04:00
fmt . Fprintf ( cli . out , "Execution Driver: %s\n" , remoteInfo . Get ( "ExecutionDriver" ) )
2013-12-11 22:35:21 +04:00
fmt . Fprintf ( cli . out , "EventsListeners: %d\n" , remoteInfo . GetInt ( "NEventsListener" ) )
fmt . Fprintf ( cli . out , "Kernel Version: %s\n" , remoteInfo . Get ( "KernelVersion" ) )
2013-12-05 13:14:15 +04:00
if initSha1 := remoteInfo . Get ( "InitSha1" ) ; initSha1 != "" {
fmt . Fprintf ( cli . out , "Init SHA1: %s\n" , initSha1 )
}
if initPath := remoteInfo . Get ( "InitPath" ) ; initPath != "" {
fmt . Fprintf ( cli . out , "Init Path: %s\n" , initPath )
}
2013-06-01 02:53:57 +04:00
}
2013-07-23 00:09:11 +04:00
2013-12-11 22:35:21 +04:00
if len ( remoteInfo . GetList ( "IndexServerAddress" ) ) != 0 {
2013-08-16 18:29:40 +04:00
cli . LoadConfigFile ( )
2013-12-11 22:35:21 +04:00
u := cli . configFile . Configs [ remoteInfo . Get ( "IndexServerAddress" ) ] . Username
2013-07-22 22:42:31 +04:00
if len ( u ) > 0 {
fmt . Fprintf ( cli . out , "Username: %v\n" , u )
2013-12-11 22:35:21 +04:00
fmt . Fprintf ( cli . out , "Registry: %v\n" , remoteInfo . GetList ( "IndexServerAddress" ) )
2013-07-22 22:42:31 +04:00
}
2013-07-02 03:05:05 +04:00
}
2013-12-11 22:35:21 +04:00
if ! remoteInfo . GetBool ( "MemoryLimit" ) {
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . err , "WARNING: No memory limit support\n" )
2013-06-01 02:53:57 +04:00
}
2013-12-11 22:35:21 +04:00
if ! remoteInfo . GetBool ( "SwapLimit" ) {
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . err , "WARNING: No swap limit support\n" )
2013-03-30 21:33:10 +04:00
}
2013-12-11 22:35:21 +04:00
if ! remoteInfo . GetBool ( "IPv4Forwarding" ) {
2013-08-04 02:06:58 +04:00
fmt . Fprintf ( cli . err , "WARNING: IPv4 forwarding is disabled.\n" )
}
2013-02-14 05:10:00 +04:00
return nil
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdStop ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "stop" , "[OPTIONS] CONTAINER [CONTAINER...]" , "Stop a running container (Send SIGTERM, and then SIGKILL after grace period)" )
2013-12-23 23:43:54 +04:00
nSeconds := cmd . Int ( [ ] string { "t" , "-time" } , 10 , "Number of seconds to wait for the container to stop before killing it." )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) < 1 {
cmd . Usage ( )
return nil
}
2013-04-22 20:17:47 +04:00
2013-05-02 20:36:23 +04:00
v := url . Values { }
v . Set ( "t" , strconv . Itoa ( * nSeconds ) )
2013-11-12 02:44:51 +04:00
var encounteredError error
2013-02-14 05:10:00 +04:00
for _ , name := range cmd . Args ( ) {
2014-01-21 08:06:19 +04:00
_ , _ , err := readBody ( cli . call ( "POST" , "/containers/" + name + "/stop?" + v . Encode ( ) , nil , false ) )
2013-04-22 20:17:47 +04:00
if err != nil {
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . err , "%s\n" , err )
2013-11-12 02:44:51 +04:00
encounteredError = fmt . Errorf ( "Error: failed to stop one or more containers" )
2013-02-14 05:10:00 +04:00
} else {
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . out , "%s\n" , name )
2013-02-15 01:49:05 +04:00
}
}
2013-11-12 02:44:51 +04:00
return encounteredError
2013-02-15 01:49:05 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdRestart ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "restart" , "[OPTIONS] CONTAINER [CONTAINER...]" , "Restart a running container" )
2013-12-23 23:43:54 +04:00
nSeconds := cmd . Int ( [ ] string { "t" , "-time" } , 10 , "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10" )
2013-02-15 01:49:05 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) < 1 {
cmd . Usage ( )
return nil
}
2013-04-22 20:17:47 +04:00
2013-05-02 20:36:23 +04:00
v := url . Values { }
v . Set ( "t" , strconv . Itoa ( * nSeconds ) )
2013-11-12 02:44:51 +04:00
var encounteredError error
2013-02-15 01:49:05 +04:00
for _ , name := range cmd . Args ( ) {
2014-01-21 08:06:19 +04:00
_ , _ , err := readBody ( cli . call ( "POST" , "/containers/" + name + "/restart?" + v . Encode ( ) , nil , false ) )
2013-04-22 20:17:47 +04:00
if err != nil {
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . err , "%s\n" , err )
2013-11-12 02:44:51 +04:00
encounteredError = fmt . Errorf ( "Error: failed to restart one or more containers" )
2013-02-15 01:49:05 +04:00
} else {
2013-06-25 01:15:52 +04:00
fmt . Fprintf ( cli . out , "%s\n" , name )
2013-02-15 01:49:05 +04:00
}
}
2013-11-12 02:44:51 +04:00
return encounteredError
2013-02-15 01:49:05 +04:00
}
2013-10-29 22:59:08 +04:00
func ( cli * DockerCli ) forwardAllSignals ( cid string ) chan os . Signal {
2013-10-08 23:15:29 +04:00
sigc := make ( chan os . Signal , 1 )
utils . CatchAll ( sigc )
go func ( ) {
for s := range sigc {
2013-11-12 19:06:54 +04:00
if s == syscall . SIGCHLD {
continue
}
2014-01-21 08:06:19 +04:00
if _ , _ , err := readBody ( cli . call ( "POST" , fmt . Sprintf ( "/containers/%s/kill?signal=%d" , cid , s ) , nil , false ) ) ; err != nil {
2013-10-08 23:15:29 +04:00
utils . Debugf ( "Error sending signal: %s" , err )
}
}
} ( )
2013-10-29 22:59:08 +04:00
return sigc
2013-10-08 23:15:29 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdStart ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "start" , "CONTAINER [CONTAINER...]" , "Restart a stopped container" )
2013-12-23 23:43:54 +04:00
attach := cmd . Bool ( [ ] string { "a" , "-attach" } , false , "Attach container's stdout/stderr and forward all signals to the process" )
openStdin := cmd . Bool ( [ ] string { "i" , "-interactive" } , false , "Attach container's stdin" )
2013-02-15 01:49:05 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) < 1 {
cmd . Usage ( )
return nil
}
2013-04-22 20:17:47 +04:00
2013-09-12 10:50:26 +04:00
var cErr chan error
2013-11-20 21:27:43 +04:00
var tty bool
2013-09-12 10:50:26 +04:00
if * attach || * openStdin {
if cmd . NArg ( ) > 1 {
return fmt . Errorf ( "Impossible to start and attach multiple containers at once." )
}
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/containers/" + cmd . Arg ( 0 ) + "/json" , nil , false ) )
2013-09-12 10:50:26 +04:00
if err != nil {
return err
}
container := & Container { }
err = json . Unmarshal ( body , container )
if err != nil {
return err
}
2013-11-20 21:27:43 +04:00
tty = container . Config . Tty
2013-09-12 10:50:26 +04:00
if ! container . Config . Tty {
2013-10-29 22:59:08 +04:00
sigc := cli . forwardAllSignals ( cmd . Arg ( 0 ) )
defer utils . StopCatch ( sigc )
2013-09-12 10:50:26 +04:00
}
2013-10-26 04:17:33 +04:00
var in io . ReadCloser
2013-09-12 10:50:26 +04:00
v := url . Values { }
v . Set ( "stream" , "1" )
if * openStdin && container . Config . OpenStdin {
v . Set ( "stdin" , "1" )
2013-10-26 04:17:33 +04:00
in = cli . in
2013-09-12 10:50:26 +04:00
}
v . Set ( "stdout" , "1" )
v . Set ( "stderr" , "1" )
cErr = utils . Go ( func ( ) error {
2013-10-26 04:17:33 +04:00
return cli . hijack ( "POST" , "/containers/" + cmd . Arg ( 0 ) + "/attach?" + v . Encode ( ) , container . Config . Tty , in , cli . out , cli . err , nil )
2013-09-12 10:50:26 +04:00
} )
}
2013-08-23 19:59:11 +04:00
var encounteredError error
2013-09-12 10:50:26 +04:00
for _ , name := range cmd . Args ( ) {
2014-01-21 08:06:19 +04:00
_ , _ , err := readBody ( cli . call ( "POST" , "/containers/" + name + "/start" , nil , false ) )
2013-04-22 20:17:47 +04:00
if err != nil {
2013-09-12 10:50:26 +04:00
if ! * attach || ! * openStdin {
fmt . Fprintf ( cli . err , "%s\n" , err )
encounteredError = fmt . Errorf ( "Error: failed to start one or more containers" )
}
2013-02-15 01:49:05 +04:00
} else {
2013-09-12 10:50:26 +04:00
if ! * attach || ! * openStdin {
fmt . Fprintf ( cli . out , "%s\n" , name )
}
2013-02-14 05:10:00 +04:00
}
}
2013-09-12 10:50:26 +04:00
if encounteredError != nil {
if * openStdin || * attach {
cli . in . Close ( )
<- cErr
}
return encounteredError
}
2013-11-20 21:27:43 +04:00
2013-09-12 10:50:26 +04:00
if * openStdin || * attach {
2013-11-20 21:27:43 +04:00
if tty && cli . isTerminal {
if err := cli . monitorTtySize ( cmd . Arg ( 0 ) ) ; err != nil {
utils . Errorf ( "Error monitoring TTY size: %s\n" , err )
}
}
2013-09-12 10:50:26 +04:00
return <- cErr
}
return nil
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdInspect ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "inspect" , "CONTAINER|IMAGE [CONTAINER|IMAGE...]" , "Return low-level information on a container/image" )
2013-12-23 23:43:54 +04:00
tmplStr := cmd . String ( [ ] string { "f" , "#format" , "-format" } , "" , "Format the output using the given go template." )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-06-06 19:22:54 +04:00
if cmd . NArg ( ) < 1 {
2013-02-14 05:10:00 +04:00
cmd . Usage ( )
return nil
}
2013-10-18 03:40:43 +04:00
2013-11-28 03:07:15 +04:00
var tmpl * template . Template
if * tmplStr != "" {
var err error
if tmpl , err = template . New ( "" ) . Parse ( * tmplStr ) ; err != nil {
fmt . Fprintf ( cli . err , "Template parsing error: %v\n" , err )
return & utils . StatusError { StatusCode : 64 ,
Status : "Template parsing error: " + err . Error ( ) }
}
}
2013-10-18 03:40:43 +04:00
indented := new ( bytes . Buffer )
2013-11-28 03:07:15 +04:00
indented . WriteByte ( '[' )
2013-10-18 03:45:18 +04:00
status := 0
2013-10-18 03:40:43 +04:00
2013-11-28 03:07:15 +04:00
for _ , name := range cmd . Args ( ) {
2014-01-21 08:06:19 +04:00
obj , _ , err := readBody ( cli . call ( "GET" , "/containers/" + name + "/json" , nil , false ) )
2013-04-24 16:01:40 +04:00
if err != nil {
2014-01-21 08:06:19 +04:00
obj , _ , err = readBody ( cli . call ( "GET" , "/images/" + name + "/json" , nil , false ) )
2013-06-06 19:22:54 +04:00
if err != nil {
2013-11-01 05:44:16 +04:00
if strings . Contains ( err . Error ( ) , "No such" ) {
fmt . Fprintf ( cli . err , "Error: No such image or container: %s\n" , name )
} else {
fmt . Fprintf ( cli . err , "%s" , err )
}
2013-10-18 03:45:18 +04:00
status = 1
2013-06-06 19:22:54 +04:00
continue
}
2013-04-24 16:01:40 +04:00
}
2013-05-09 05:46:39 +04:00
2013-11-28 03:07:15 +04:00
if tmpl == nil {
if err = json . Indent ( indented , obj , "" , " " ) ; err != nil {
fmt . Fprintf ( cli . err , "%s\n" , err )
status = 1
continue
}
} else {
// Has template, will render
var value interface { }
if err := json . Unmarshal ( obj , & value ) ; err != nil {
fmt . Fprintf ( cli . err , "%s\n" , err )
status = 1
continue
}
if err := tmpl . Execute ( cli . out , value ) ; err != nil {
return err
}
cli . out . Write ( [ ] byte { '\n' } )
2013-06-06 19:22:54 +04:00
}
2013-10-18 03:40:43 +04:00
indented . WriteString ( "," )
}
2013-11-28 03:07:15 +04:00
if indented . Len ( ) > 1 {
2013-11-01 05:44:16 +04:00
// Remove trailing ','
2013-11-01 05:39:44 +04:00
indented . Truncate ( indented . Len ( ) - 1 )
}
2013-11-28 03:07:15 +04:00
indented . WriteByte ( ']' )
if tmpl == nil {
if _ , err := io . Copy ( cli . out , indented ) ; err != nil {
return err
}
2013-02-14 05:10:00 +04:00
}
2013-11-28 03:07:15 +04:00
2013-10-18 03:45:18 +04:00
if status != 0 {
2013-10-12 16:14:52 +04:00
return & utils . StatusError { StatusCode : status }
2013-10-18 03:45:18 +04:00
}
2013-02-14 05:10:00 +04:00
return nil
}
2013-07-01 19:19:42 +04:00
func ( cli * DockerCli ) CmdTop ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "top" , "CONTAINER [ps OPTIONS]" , "Lookup the running processes of a container" )
2013-06-28 19:51:58 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-07-19 14:06:32 +04:00
if cmd . NArg ( ) == 0 {
2013-06-28 19:51:58 +04:00
cmd . Usage ( )
return nil
}
2013-07-19 14:06:32 +04:00
val := url . Values { }
if cmd . NArg ( ) > 1 {
val . Set ( "ps_args" , strings . Join ( cmd . Args ( ) [ 1 : ] , " " ) )
}
2014-01-25 11:15:40 +04:00
stream , _ , err := cli . call ( "GET" , "/containers/" + cmd . Arg ( 0 ) + "/top?" + val . Encode ( ) , nil , false )
2013-06-28 19:51:58 +04:00
if err != nil {
return err
}
2014-01-25 11:15:40 +04:00
var procs engine . Env
if err := procs . Decode ( stream ) ; err != nil {
2013-06-28 19:51:58 +04:00
return err
}
w := tabwriter . NewWriter ( cli . out , 20 , 1 , 3 , ' ' , 0 )
2014-01-25 11:15:40 +04:00
fmt . Fprintln ( w , strings . Join ( procs . GetList ( "Titles" ) , "\t" ) )
processes := [ ] [ ] string { }
if err := procs . GetJson ( "Processes" , & processes ) ; err != nil {
return err
}
for _ , proc := range processes {
2013-07-19 14:06:32 +04:00
fmt . Fprintln ( w , strings . Join ( proc , "\t" ) )
2013-06-28 19:51:58 +04:00
}
w . Flush ( )
return nil
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdPort ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "port" , "CONTAINER PRIVATE_PORT" , "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT" )
2013-03-06 12:39:03 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 2 {
cmd . Usage ( )
return nil
}
2013-05-08 20:06:43 +04:00
2013-07-11 22:30:08 +04:00
port := cmd . Arg ( 1 )
2013-10-24 03:41:59 +04:00
proto := "tcp"
2013-07-11 22:30:08 +04:00
parts := strings . SplitN ( port , "/" , 2 )
if len ( parts ) == 2 && len ( parts [ 1 ] ) != 0 {
port = parts [ 0 ]
2013-10-24 03:41:59 +04:00
proto = parts [ 1 ]
2013-07-11 22:30:08 +04:00
}
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/containers/" + cmd . Arg ( 0 ) + "/json" , nil , false ) )
2013-04-23 20:20:53 +04:00
if err != nil {
return err
}
2013-05-08 20:06:43 +04:00
var out Container
2013-04-23 20:20:53 +04:00
err = json . Unmarshal ( body , & out )
if err != nil {
return err
2013-03-06 12:39:03 +04:00
}
2013-05-08 20:06:43 +04:00
2013-11-30 04:45:20 +04:00
if frontends , exists := out . NetworkSettings . Ports [ Port ( port + "/" + proto ) ] ; exists && frontends != nil {
for _ , frontend := range frontends {
fmt . Fprintf ( cli . out , "%s:%s\n" , frontend . HostIp , frontend . HostPort )
2013-10-24 03:41:59 +04:00
}
2013-03-06 12:39:03 +04:00
} else {
2013-11-30 04:45:20 +04:00
return fmt . Errorf ( "Error: No public port '%s' published for %s" , cmd . Arg ( 1 ) , cmd . Arg ( 0 ) )
2013-03-06 12:39:03 +04:00
}
return nil
}
2013-04-11 20:46:47 +04:00
// 'docker rmi IMAGE' removes all images with the name IMAGE
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdRmi ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "rmi" , "IMAGE [IMAGE...]" , "Remove one or more images" )
2013-03-26 05:35:31 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) < 1 {
2013-03-13 22:14:37 +04:00
cmd . Usage ( )
return nil
}
2013-04-22 20:17:47 +04:00
2013-11-12 02:44:51 +04:00
var encounteredError error
2013-03-13 22:14:37 +04:00
for _ , name := range cmd . Args ( ) {
2014-01-22 05:56:09 +04:00
body , _ , err := readBody ( cli . call ( "DELETE" , "/images/" + name , nil , false ) )
2013-04-11 04:23:42 +04:00
if err != nil {
2013-11-12 02:44:51 +04:00
fmt . Fprintf ( cli . err , "%s\n" , err )
encounteredError = fmt . Errorf ( "Error: failed to remove one or more images" )
2013-04-22 20:17:47 +04:00
} else {
2014-01-17 06:40:33 +04:00
outs := engine . NewTable ( "Created" , 0 )
2014-01-22 05:56:09 +04:00
if _ , err := outs . ReadListFrom ( body ) ; err != nil {
2013-11-12 02:44:51 +04:00
fmt . Fprintf ( cli . err , "%s\n" , err )
encounteredError = fmt . Errorf ( "Error: failed to remove one or more images" )
continue
2013-05-31 18:37:02 +04:00
}
2014-01-17 06:40:33 +04:00
for _ , out := range outs . Data {
if out . Get ( "Deleted" ) != "" {
fmt . Fprintf ( cli . out , "Deleted: %s\n" , out . Get ( "Deleted" ) )
2013-05-31 18:37:02 +04:00
} else {
2014-01-17 06:40:33 +04:00
fmt . Fprintf ( cli . out , "Untagged: %s\n" , out . Get ( "Untagged" ) )
2013-05-31 18:37:02 +04:00
}
}
2013-03-13 22:14:37 +04:00
}
}
2013-11-12 02:44:51 +04:00
return encounteredError
2013-03-13 22:14:37 +04:00
}
2013-02-14 05:10:00 +04:00
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdHistory ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "history" , "[OPTIONS] IMAGE" , "Show the history of an image" )
2013-12-23 23:43:54 +04:00
quiet := cmd . Bool ( [ ] string { "q" , "-quiet" } , false , "only show numeric IDs" )
noTrunc := cmd . Bool ( [ ] string { "#notrunc" , "-no-trunc" } , false , "Don't truncate output" )
2013-10-19 03:39:40 +04:00
2013-03-26 05:33:56 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 1 {
2013-03-22 08:42:18 +04:00
cmd . Usage ( )
return nil
}
2013-04-22 20:17:47 +04:00
2014-01-22 05:56:09 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/images/" + cmd . Arg ( 0 ) + "/history" , nil , false ) )
2013-03-22 08:42:18 +04:00
if err != nil {
return err
}
2013-04-22 20:17:47 +04:00
2014-01-14 04:50:10 +04:00
outs := engine . NewTable ( "Created" , 0 )
2014-01-22 05:56:09 +04:00
if _ , err := outs . ReadListFrom ( body ) ; err != nil {
2013-03-22 08:42:18 +04:00
return err
}
2013-10-19 03:39:40 +04:00
2013-06-25 01:15:52 +04:00
w := tabwriter . NewWriter ( cli . out , 20 , 1 , 3 , ' ' , 0 )
2013-10-19 03:39:40 +04:00
if ! * quiet {
2013-10-22 16:22:24 +04:00
fmt . Fprintln ( w , "IMAGE\tCREATED\tCREATED BY\tSIZE" )
2013-10-19 03:39:40 +04:00
}
2013-04-22 20:17:47 +04:00
2014-01-14 04:50:10 +04:00
for _ , out := range outs . Data {
2014-01-30 00:31:49 +04:00
outID := out . Get ( "Id" )
2013-10-19 03:39:40 +04:00
if ! * quiet {
if * noTrunc {
2014-01-14 04:50:10 +04:00
fmt . Fprintf ( w , "%s\t" , outID )
2013-10-19 03:39:40 +04:00
} else {
2014-01-14 04:50:10 +04:00
fmt . Fprintf ( w , "%s\t" , utils . TruncateID ( outID ) )
2013-10-19 03:39:40 +04:00
}
2014-01-14 04:50:10 +04:00
fmt . Fprintf ( w , "%s ago\t" , utils . HumanDuration ( time . Now ( ) . UTC ( ) . Sub ( time . Unix ( out . GetInt64 ( "Created" ) , 0 ) ) ) )
2013-10-19 03:39:40 +04:00
if * noTrunc {
2014-01-14 04:50:10 +04:00
fmt . Fprintf ( w , "%s\t" , out . Get ( "CreatedBy" ) )
2013-10-19 03:39:40 +04:00
} else {
2014-01-14 04:50:10 +04:00
fmt . Fprintf ( w , "%s\t" , utils . Trunc ( out . Get ( "CreatedBy" ) , 45 ) )
2013-10-19 03:39:40 +04:00
}
2014-01-14 04:50:10 +04:00
fmt . Fprintf ( w , "%s\n" , utils . HumanSize ( out . GetInt64 ( "Size" ) ) )
2013-10-19 03:39:40 +04:00
} else {
if * noTrunc {
2014-01-14 04:50:10 +04:00
fmt . Fprintln ( w , outID )
2013-10-19 03:39:40 +04:00
} else {
2014-01-14 04:50:10 +04:00
fmt . Fprintln ( w , utils . TruncateID ( outID ) )
2013-10-19 03:39:40 +04:00
}
2013-06-18 21:31:07 +04:00
}
2013-04-22 20:17:47 +04:00
}
w . Flush ( )
return nil
2013-03-22 08:42:18 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdRm ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "rm" , "[OPTIONS] CONTAINER [CONTAINER...]" , "Remove one or more containers" )
2013-12-23 23:43:54 +04:00
v := cmd . Bool ( [ ] string { "v" , "-volumes" } , false , "Remove the volumes associated to the container" )
link := cmd . Bool ( [ ] string { "l" , "#link" , "-link" } , false , "Remove the specified link and not the underlying container" )
2013-10-05 06:25:15 +04:00
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-11 18:27:01 +04:00
if cmd . NArg ( ) < 1 {
cmd . Usage ( )
return nil
}
2013-05-06 13:52:15 +04:00
val := url . Values { }
2013-04-12 20:23:57 +04:00
if * v {
2013-05-06 13:52:15 +04:00
val . Set ( "v" , "1" )
}
2013-10-05 06:25:15 +04:00
if * link {
val . Set ( "link" , "1" )
}
2013-11-12 02:44:51 +04:00
var encounteredError error
2013-05-06 13:52:15 +04:00
for _ , name := range cmd . Args ( ) {
2014-01-21 08:06:19 +04:00
_ , _ , err := readBody ( cli . call ( "DELETE" , "/containers/" + name + "?" + val . Encode ( ) , nil , false ) )
2013-04-22 20:17:47 +04:00
if err != nil {
2013-06-25 21:39:11 +04:00
fmt . Fprintf ( cli . err , "%s\n" , err )
2013-11-12 02:44:51 +04:00
encounteredError = fmt . Errorf ( "Error: failed to remove one or more containers" )
2013-04-22 20:17:47 +04:00
} else {
2013-06-25 21:39:11 +04:00
fmt . Fprintf ( cli . out , "%s\n" , name )
2013-04-12 20:23:57 +04:00
}
}
2013-11-12 02:44:51 +04:00
return encounteredError
2013-02-14 05:10:00 +04:00
}
// 'docker kill NAME' kills a running container
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdKill ( args ... string ) error {
2014-01-22 08:42:36 +04:00
cmd := cli . Subcmd ( "kill" , "[OPTIONS] CONTAINER [CONTAINER...]" , "Kill a running container (send SIGKILL, or specified signal)" )
signal := cmd . String ( [ ] string { "s" , "-signal" } , "KILL" , "Signal to send to the container" )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-12 14:26:31 +04:00
if cmd . NArg ( ) < 1 {
cmd . Usage ( )
return nil
}
2013-04-22 20:17:47 +04:00
2013-11-12 02:44:51 +04:00
var encounteredError error
2014-01-22 08:42:36 +04:00
for _ , name := range cmd . Args ( ) {
if _ , _ , err := readBody ( cli . call ( "POST" , fmt . Sprintf ( "/containers/%s/kill?signal=%s" , name , * signal ) , nil , false ) ) ; err != nil {
2013-06-25 21:39:11 +04:00
fmt . Fprintf ( cli . err , "%s\n" , err )
2013-11-12 02:44:51 +04:00
encounteredError = fmt . Errorf ( "Error: failed to kill one or more containers" )
2013-04-22 20:17:47 +04:00
} else {
2013-06-25 21:39:11 +04:00
fmt . Fprintf ( cli . out , "%s\n" , name )
2013-02-14 05:10:00 +04:00
}
}
2013-11-12 02:44:51 +04:00
return encounteredError
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdImport ( args ... string ) error {
2013-11-16 14:49:42 +04:00
cmd := cli . Subcmd ( "import" , "URL|- [REPOSITORY[:TAG]]" , "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it." )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-12 14:26:31 +04:00
if cmd . NArg ( ) < 1 {
cmd . Usage ( )
return nil
}
2013-11-08 05:30:51 +04:00
var src , repository , tag string
if cmd . NArg ( ) == 3 {
fmt . Fprintf ( cli . err , "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' as been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n" )
src , repository , tag = cmd . Arg ( 0 ) , cmd . Arg ( 1 ) , cmd . Arg ( 2 )
} else {
src = cmd . Arg ( 0 )
repository , tag = utils . ParseRepositoryTag ( cmd . Arg ( 1 ) )
}
2013-04-30 19:04:31 +04:00
v := url . Values { }
v . Set ( "repo" , repository )
v . Set ( "tag" , tag )
2013-05-06 13:31:22 +04:00
v . Set ( "fromSrc" , src )
2013-04-30 19:04:31 +04:00
2013-09-16 05:40:29 +04:00
var in io . Reader
if src == "-" {
in = cli . in
2013-02-14 05:10:00 +04:00
}
2013-09-16 05:40:29 +04:00
return cli . stream ( "POST" , "/images/create?" + v . Encode ( ) , in , cli . out , nil )
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdPush ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "push" , "NAME" , "Push an image or a repository to the registry" )
2013-03-21 14:53:27 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-05-06 15:34:31 +04:00
name := cmd . Arg ( 0 )
2013-03-22 17:38:54 +04:00
2013-05-06 15:34:31 +04:00
if name == "" {
2013-03-21 14:53:27 +04:00
cmd . Usage ( )
return nil
}
2013-08-16 18:29:40 +04:00
cli . LoadConfigFile ( )
2013-09-03 22:45:49 +04:00
// Resolve the Repository name from fqn to endpoint + name
endpoint , _ , err := registry . ResolveRepositoryName ( name )
if err != nil {
return err
}
// Resolve the Auth config relevant for this server
authConfig := cli . configFile . ResolveAuthConfig ( endpoint )
2013-07-05 23:20:58 +04:00
// If we're not using a custom registry, we know the restrictions
// applied to repository names and can warn the user in advance.
// Custom repositories can have different rules, and we must also
// allow pushing by image ID.
2013-05-06 15:34:31 +04:00
if len ( strings . SplitN ( name , "/" , 2 ) ) == 1 {
2013-08-16 18:29:40 +04:00
username := cli . configFile . Configs [ auth . IndexServerAddress ( ) ] . Username
if username == "" {
username = "<user>"
}
return fmt . Errorf ( "Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)" , username , name )
2013-03-22 17:38:54 +04:00
}
2013-07-24 07:01:24 +04:00
v := url . Values { }
2013-09-03 22:45:49 +04:00
push := func ( authConfig auth . AuthConfig ) error {
buf , err := json . Marshal ( authConfig )
2013-07-24 07:01:24 +04:00
if err != nil {
return err
}
2013-08-30 23:46:19 +04:00
registryAuthHeader := [ ] string {
2013-08-31 00:49:37 +04:00
base64 . URLEncoding . EncodeToString ( buf ) ,
2013-08-30 23:46:19 +04:00
}
2013-07-24 07:01:24 +04:00
2013-08-30 23:46:19 +04:00
return cli . stream ( "POST" , "/images/" + name + "/push?" + v . Encode ( ) , nil , cli . out , map [ string ] [ ] string {
"X-Registry-Auth" : registryAuthHeader ,
} )
2013-03-22 17:38:54 +04:00
}
2013-09-03 22:45:49 +04:00
if err := push ( authConfig ) ; err != nil {
2014-02-03 23:38:34 +04:00
if strings . Contains ( err . Error ( ) , "Status 401" ) {
2013-08-13 17:51:49 +04:00
fmt . Fprintln ( cli . out , "\nPlease login prior to push:" )
2013-09-03 22:45:49 +04:00
if err := cli . CmdLogin ( endpoint ) ; err != nil {
2013-08-13 17:51:49 +04:00
return err
2013-07-24 07:01:24 +04:00
}
2013-09-03 22:45:49 +04:00
authConfig := cli . configFile . ResolveAuthConfig ( endpoint )
return push ( authConfig )
2013-07-24 07:01:24 +04:00
}
2013-03-22 14:10:09 +04:00
return err
}
return nil
2013-03-21 14:53:27 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdPull ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "pull" , "NAME" , "Pull an image or a repository from the registry" )
2013-12-23 23:43:54 +04:00
tag := cmd . String ( [ ] string { "t" , "-tag" } , "" , "Download tagged image in repository" )
2013-03-21 14:53:27 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-23 20:20:53 +04:00
if cmd . NArg ( ) != 1 {
2013-03-21 14:53:27 +04:00
cmd . Usage ( )
return nil
}
2013-03-22 12:25:27 +04:00
2013-07-09 19:06:10 +04:00
remote , parsedTag := utils . ParseRepositoryTag ( cmd . Arg ( 0 ) )
2013-07-12 21:42:54 +04:00
if * tag == "" {
* tag = parsedTag
}
2013-04-30 17:55:24 +04:00
2013-09-03 22:45:49 +04:00
// Resolve the Repository name from fqn to endpoint + name
endpoint , _ , err := registry . ResolveRepositoryName ( remote )
if err != nil {
return err
}
cli . LoadConfigFile ( )
// Resolve the Auth config relevant for this server
authConfig := cli . configFile . ResolveAuthConfig ( endpoint )
2013-05-06 13:31:22 +04:00
v := url . Values { }
2013-05-07 21:23:50 +04:00
v . Set ( "fromImage" , remote )
v . Set ( "tag" , * tag )
2013-03-22 12:25:27 +04:00
2013-09-03 22:45:49 +04:00
pull := func ( authConfig auth . AuthConfig ) error {
buf , err := json . Marshal ( authConfig )
if err != nil {
return err
}
2013-08-30 23:46:19 +04:00
registryAuthHeader := [ ] string {
2013-08-31 00:49:37 +04:00
base64 . URLEncoding . EncodeToString ( buf ) ,
2013-08-30 23:46:19 +04:00
}
2013-09-03 22:45:49 +04:00
2013-08-30 23:46:19 +04:00
return cli . stream ( "POST" , "/images/create?" + v . Encode ( ) , nil , cli . out , map [ string ] [ ] string {
"X-Registry-Auth" : registryAuthHeader ,
} )
2013-09-03 22:45:49 +04:00
}
if err := pull ( authConfig ) ; err != nil {
2014-02-03 23:38:34 +04:00
if strings . Contains ( err . Error ( ) , "Status 401" ) {
2014-01-05 13:07:16 +04:00
fmt . Fprintln ( cli . out , "\nPlease login prior to pull:" )
2013-09-03 22:45:49 +04:00
if err := cli . CmdLogin ( endpoint ) ; err != nil {
return err
}
authConfig := cli . configFile . ResolveAuthConfig ( endpoint )
return pull ( authConfig )
}
2013-03-22 14:10:09 +04:00
return err
}
2013-04-23 20:20:53 +04:00
2013-03-22 14:10:09 +04:00
return nil
2013-03-21 14:53:27 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdImages ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "images" , "[OPTIONS] [NAME]" , "List images" )
2013-12-23 23:43:54 +04:00
quiet := cmd . Bool ( [ ] string { "q" , "-quiet" } , false , "only show numeric IDs" )
all := cmd . Bool ( [ ] string { "a" , "-all" } , false , "show all images (by default filter out the intermediate images used to build)" )
noTrunc := cmd . Bool ( [ ] string { "#notrunc" , "-no-trunc" } , false , "Don't truncate output" )
flViz := cmd . Bool ( [ ] string { "v" , "#viz" , "-viz" } , false , "output graph in graphviz format" )
flTree := cmd . Bool ( [ ] string { "t" , "#tree" , "-tree" } , false , "output graph in tree format" )
2013-04-23 20:20:53 +04:00
2013-03-12 02:08:22 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-02-14 05:10:00 +04:00
if cmd . NArg ( ) > 1 {
cmd . Usage ( )
return nil
}
2013-05-01 09:39:48 +04:00
2013-12-09 22:43:58 +04:00
filter := cmd . Arg ( 0 )
2013-10-08 17:41:44 +04:00
2013-12-09 22:43:58 +04:00
if * flViz || * flTree {
2014-01-22 05:56:09 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/images/json?all=1" , nil , false ) )
2013-10-09 18:38:39 +04:00
if err != nil {
return err
}
2013-12-13 22:26:00 +04:00
outs := engine . NewTable ( "Created" , 0 )
2014-01-22 05:56:09 +04:00
if _ , err := outs . ReadListFrom ( body ) ; err != nil {
2013-10-09 18:38:39 +04:00
return err
}
2013-11-13 00:25:35 +04:00
var (
2013-12-13 22:26:00 +04:00
printNode func ( cli * DockerCli , noTrunc bool , image * engine . Env , prefix string )
startImage * engine . Env
2013-11-13 00:25:35 +04:00
2013-12-13 22:26:00 +04:00
roots = engine . NewTable ( "Created" , outs . Len ( ) )
byParent = make ( map [ string ] * engine . Table )
2013-11-13 00:25:35 +04:00
)
2013-10-09 18:38:39 +04:00
2013-12-13 22:26:00 +04:00
for _ , image := range outs . Data {
if image . Get ( "ParentId" ) == "" {
roots . Add ( image )
2013-10-09 18:38:39 +04:00
} else {
2013-12-13 22:26:00 +04:00
if children , exists := byParent [ image . Get ( "ParentId" ) ] ; exists {
children . Add ( image )
2013-10-09 18:38:39 +04:00
} else {
2013-12-13 22:26:00 +04:00
byParent [ image . Get ( "ParentId" ) ] = engine . NewTable ( "Created" , 1 )
byParent [ image . Get ( "ParentId" ) ] . Add ( image )
2013-10-09 18:38:39 +04:00
}
}
2013-12-09 22:43:58 +04:00
if filter != "" {
2014-01-30 00:31:49 +04:00
if filter == image . Get ( "Id" ) || filter == utils . TruncateID ( image . Get ( "Id" ) ) {
2013-10-09 18:38:39 +04:00
startImage = image
}
2013-12-13 22:26:00 +04:00
for _ , repotag := range image . GetList ( "RepoTags" ) {
2013-12-09 22:43:58 +04:00
if repotag == filter {
2013-10-09 18:38:39 +04:00
startImage = image
}
}
}
}
2013-12-09 22:43:58 +04:00
if * flViz {
fmt . Fprintf ( cli . out , "digraph docker {\n" )
printNode = ( * DockerCli ) . printVizNode
2013-10-09 18:38:39 +04:00
} else {
2013-12-09 22:43:58 +04:00
printNode = ( * DockerCli ) . printTreeNode
}
2013-12-13 22:26:00 +04:00
if startImage != nil {
root := engine . NewTable ( "Created" , 1 )
root . Add ( startImage )
cli . WalkTree ( * noTrunc , root , byParent , "" , printNode )
2013-12-09 22:43:58 +04:00
} else if filter == "" {
2013-12-13 22:26:00 +04:00
cli . WalkTree ( * noTrunc , roots , byParent , "" , printNode )
2013-12-09 22:43:58 +04:00
}
if * flViz {
fmt . Fprintf ( cli . out , " base [style=invisible]\n}\n" )
2013-10-09 18:38:39 +04:00
}
2013-05-01 09:39:48 +04:00
} else {
2013-05-07 21:23:50 +04:00
v := url . Values { }
2013-05-01 09:39:48 +04:00
if cmd . NArg ( ) == 1 {
2013-12-09 22:43:58 +04:00
v . Set ( "filter" , filter )
2013-05-01 09:39:48 +04:00
}
2013-05-07 21:23:50 +04:00
if * all {
v . Set ( "all" , "1" )
2013-05-01 09:39:48 +04:00
}
2013-04-22 20:17:47 +04:00
2014-01-22 05:56:09 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/images/json?" + v . Encode ( ) , nil , false ) )
2013-05-01 09:39:48 +04:00
if err != nil {
return err
}
2013-04-22 20:17:47 +04:00
2013-12-13 22:26:00 +04:00
outs := engine . NewTable ( "Created" , 0 )
2014-01-22 05:56:09 +04:00
if _ , err := outs . ReadListFrom ( body ) ; err != nil {
2013-05-07 21:23:50 +04:00
return err
2013-02-14 05:10:00 +04:00
}
2013-05-07 21:23:50 +04:00
2013-06-25 01:15:52 +04:00
w := tabwriter . NewWriter ( cli . out , 20 , 1 , 3 , ' ' , 0 )
2013-04-22 20:17:47 +04:00
if ! * quiet {
2013-12-06 02:03:23 +04:00
fmt . Fprintln ( w , "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE" )
2013-03-22 04:35:49 +04:00
}
2013-04-22 20:17:47 +04:00
2013-12-13 22:26:00 +04:00
for _ , out := range outs . Data {
for _ , repotag := range out . GetList ( "RepoTags" ) {
2013-05-13 14:18:55 +04:00
2013-11-12 01:54:17 +04:00
repo , tag := utils . ParseRepositoryTag ( repotag )
2014-01-30 00:31:49 +04:00
outID := out . Get ( "Id" )
2013-10-06 09:44:04 +04:00
if ! * noTrunc {
2013-12-13 22:26:00 +04:00
outID = utils . TruncateID ( outID )
2013-10-06 09:44:04 +04:00
}
2013-10-17 22:40:38 +04:00
2013-10-06 09:44:04 +04:00
if ! * quiet {
2013-12-13 22:26:00 +04:00
fmt . Fprintf ( w , "%s\t%s\t%s\t%s ago\t%s\n" , repo , tag , outID , utils . HumanDuration ( time . Now ( ) . UTC ( ) . Sub ( time . Unix ( out . GetInt64 ( "Created" ) , 0 ) ) ) , utils . HumanSize ( out . GetInt64 ( "VirtualSize" ) ) )
2013-05-22 17:41:29 +04:00
} else {
2013-12-13 22:26:00 +04:00
fmt . Fprintln ( w , outID )
2013-05-22 17:41:29 +04:00
}
2013-03-22 04:35:49 +04:00
}
}
2013-05-07 21:23:50 +04:00
2013-05-01 09:39:48 +04:00
if ! * quiet {
w . Flush ( )
}
2013-02-14 05:10:00 +04:00
}
return nil
}
2013-12-13 22:26:00 +04:00
func ( cli * DockerCli ) WalkTree ( noTrunc bool , images * engine . Table , byParent map [ string ] * engine . Table , prefix string , printNode func ( cli * DockerCli , noTrunc bool , image * engine . Env , prefix string ) ) {
length := images . Len ( )
2013-12-09 22:43:58 +04:00
if length > 1 {
2013-12-13 22:26:00 +04:00
for index , image := range images . Data {
2013-10-09 18:38:39 +04:00
if index + 1 == length {
2013-12-09 22:43:58 +04:00
printNode ( cli , noTrunc , image , prefix + "└─" )
2014-01-30 00:31:49 +04:00
if subimages , exists := byParent [ image . Get ( "Id" ) ] ; exists {
2013-12-13 22:26:00 +04:00
cli . WalkTree ( noTrunc , subimages , byParent , prefix + " " , printNode )
2013-10-09 18:38:39 +04:00
}
} else {
2013-12-13 22:26:00 +04:00
printNode ( cli , noTrunc , image , prefix + "\u251C─" )
2014-01-30 00:31:49 +04:00
if subimages , exists := byParent [ image . Get ( "Id" ) ] ; exists {
2013-12-13 22:26:00 +04:00
cli . WalkTree ( noTrunc , subimages , byParent , prefix + "\u2502 " , printNode )
2013-10-09 18:38:39 +04:00
}
}
}
} else {
2013-12-13 22:26:00 +04:00
for _ , image := range images . Data {
2013-12-09 22:43:58 +04:00
printNode ( cli , noTrunc , image , prefix + "└─" )
2014-01-30 00:31:49 +04:00
if subimages , exists := byParent [ image . Get ( "Id" ) ] ; exists {
2013-12-13 22:26:00 +04:00
cli . WalkTree ( noTrunc , subimages , byParent , prefix + " " , printNode )
2013-10-09 18:38:39 +04:00
}
}
}
}
2013-12-13 22:26:00 +04:00
func ( cli * DockerCli ) printVizNode ( noTrunc bool , image * engine . Env , prefix string ) {
2013-12-09 22:43:58 +04:00
var (
imageID string
parentID string
)
if noTrunc {
2014-01-30 00:31:49 +04:00
imageID = image . Get ( "Id" )
2013-12-13 22:26:00 +04:00
parentID = image . Get ( "ParentId" )
2013-12-09 22:43:58 +04:00
} else {
2014-01-30 00:31:49 +04:00
imageID = utils . TruncateID ( image . Get ( "Id" ) )
2013-12-13 22:26:00 +04:00
parentID = utils . TruncateID ( image . Get ( "ParentId" ) )
2013-12-09 22:43:58 +04:00
}
2013-12-13 22:26:00 +04:00
if parentID == "" {
2013-12-09 22:43:58 +04:00
fmt . Fprintf ( cli . out , " base -> \"%s\" [style=invis]\n" , imageID )
} else {
fmt . Fprintf ( cli . out , " \"%s\" -> \"%s\"\n" , parentID , imageID )
}
2013-12-13 22:26:00 +04:00
if image . GetList ( "RepoTags" ) [ 0 ] != "<none>:<none>" {
2013-12-09 22:43:58 +04:00
fmt . Fprintf ( cli . out , " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n" ,
2013-12-13 22:26:00 +04:00
imageID , imageID , strings . Join ( image . GetList ( "RepoTags" ) , "\\n" ) )
2013-12-09 22:43:58 +04:00
}
}
2013-12-13 22:26:00 +04:00
func ( cli * DockerCli ) printTreeNode ( noTrunc bool , image * engine . Env , prefix string ) {
2013-10-09 18:38:39 +04:00
var imageID string
2013-12-09 22:43:58 +04:00
if noTrunc {
2014-01-30 00:31:49 +04:00
imageID = image . Get ( "Id" )
2013-10-09 18:38:39 +04:00
} else {
2014-01-30 00:31:49 +04:00
imageID = utils . TruncateID ( image . Get ( "Id" ) )
2013-10-09 18:38:39 +04:00
}
2013-12-13 22:26:00 +04:00
fmt . Fprintf ( cli . out , "%s%s Virtual Size: %s" , prefix , imageID , utils . HumanSize ( image . GetInt64 ( "VirtualSize" ) ) )
if image . GetList ( "RepoTags" ) [ 0 ] != "<none>:<none>" {
fmt . Fprintf ( cli . out , " Tags: %s\n" , strings . Join ( image . GetList ( "RepoTags" ) , ", " ) )
2013-10-09 18:38:39 +04:00
} else {
2013-10-28 03:42:09 +04:00
fmt . Fprint ( cli . out , "\n" )
2013-10-09 18:38:39 +04:00
}
}
2014-01-17 02:00:18 +04:00
func displayablePorts ( ports * engine . Table ) string {
2013-08-28 02:20:35 +04:00
result := [ ] string { }
2014-01-17 02:00:18 +04:00
for _ , port := range ports . Data {
if port . Get ( "IP" ) == "" {
result = append ( result , fmt . Sprintf ( "%d/%s" , port . GetInt ( "PublicPort" ) , port . Get ( "Type" ) ) )
2013-08-28 02:20:35 +04:00
} else {
2014-01-17 02:00:18 +04:00
result = append ( result , fmt . Sprintf ( "%s:%d->%d/%s" , port . Get ( "IP" ) , port . GetInt ( "PublicPort" ) , port . GetInt ( "PrivatePort" ) , port . Get ( "Type" ) ) )
2013-08-28 02:20:35 +04:00
}
}
sort . Strings ( result )
return strings . Join ( result , ", " )
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdPs ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "ps" , "[OPTIONS]" , "List containers" )
2013-12-23 23:43:54 +04:00
quiet := cmd . Bool ( [ ] string { "q" , "-quiet" } , false , "Only display numeric IDs" )
size := cmd . Bool ( [ ] string { "s" , "-size" } , false , "Display sizes" )
all := cmd . Bool ( [ ] string { "a" , "-all" } , false , "Show all containers. Only running containers are shown by default." )
noTrunc := cmd . Bool ( [ ] string { "#notrunc" , "-no-trunc" } , false , "Don't truncate output" )
nLatest := cmd . Bool ( [ ] string { "l" , "-latest" } , false , "Show only the latest created container, include non-running ones." )
since := cmd . String ( [ ] string { "#sinceId" , "-since-id" } , "" , "Show only containers created since Id, include non-running ones." )
before := cmd . String ( [ ] string { "#beforeId" , "-before-id" } , "" , "Show only container created before Id, include non-running ones." )
last := cmd . Int ( [ ] string { "n" } , - 1 , "Show n last created containers, include non-running ones." )
2013-04-22 20:17:47 +04:00
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-22 20:17:47 +04:00
v := url . Values { }
if * last == - 1 && * nLatest {
* last = 1
}
if * all {
2013-04-26 17:08:33 +04:00
v . Set ( "all" , "1" )
2013-04-22 20:17:47 +04:00
}
if * last != - 1 {
2013-05-08 19:35:50 +04:00
v . Set ( "limit" , strconv . Itoa ( * last ) )
2013-04-22 20:17:47 +04:00
}
2013-05-08 20:28:11 +04:00
if * since != "" {
v . Set ( "since" , * since )
}
if * before != "" {
v . Set ( "before" , * before )
}
2013-06-20 18:19:50 +04:00
if * size {
v . Set ( "size" , "1" )
}
2013-04-23 20:20:53 +04:00
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/containers/json?" + v . Encode ( ) , nil , false ) )
2013-04-22 20:17:47 +04:00
if err != nil {
return err
}
2014-01-17 02:00:18 +04:00
outs := engine . NewTable ( "Created" , 0 )
if _ , err := outs . ReadListFrom ( body ) ; err != nil {
2013-04-22 20:17:47 +04:00
return err
2013-04-10 21:30:57 +04:00
}
2013-06-25 01:15:52 +04:00
w := tabwriter . NewWriter ( cli . out , 20 , 1 , 3 , ' ' , 0 )
2013-02-14 05:28:13 +04:00
if ! * quiet {
2013-10-22 16:22:24 +04:00
fmt . Fprint ( w , "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES" )
2013-06-20 18:19:50 +04:00
if * size {
fmt . Fprintln ( w , "\tSIZE" )
} else {
fmt . Fprint ( w , "\n" )
}
2013-02-14 05:10:00 +04:00
}
2013-04-22 20:17:47 +04:00
2014-01-17 02:00:18 +04:00
for _ , out := range outs . Data {
var (
2014-01-30 00:31:49 +04:00
outID = out . Get ( "Id" )
2014-01-17 02:00:18 +04:00
outNames = out . GetList ( "Names" )
)
2013-10-17 22:40:38 +04:00
if ! * noTrunc {
2014-01-17 02:00:18 +04:00
outID = utils . TruncateID ( outID )
2013-10-05 06:25:15 +04:00
}
2013-10-29 03:58:59 +04:00
// Remove the leading / from the names
2014-01-17 02:00:18 +04:00
for i := 0 ; i < len ( outNames ) ; i ++ {
outNames [ i ] = outNames [ i ] [ 1 : ]
2013-10-29 03:58:59 +04:00
}
2013-02-14 05:10:00 +04:00
if ! * quiet {
2014-01-17 02:00:18 +04:00
var (
outCommand = out . Get ( "Command" )
ports = engine . NewTable ( "" , 0 )
)
2013-10-17 22:40:38 +04:00
if ! * noTrunc {
2014-01-17 02:00:18 +04:00
outCommand = utils . Trunc ( outCommand , 20 )
2013-05-22 17:41:29 +04:00
}
2014-01-17 02:00:18 +04:00
ports . ReadListFrom ( [ ] byte ( out . Get ( "Ports" ) ) )
fmt . Fprintf ( w , "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t" , outID , out . Get ( "Image" ) , outCommand , utils . HumanDuration ( time . Now ( ) . UTC ( ) . Sub ( time . Unix ( out . GetInt64 ( "Created" ) , 0 ) ) ) , out . Get ( "Status" ) , displayablePorts ( ports ) , strings . Join ( outNames , "," ) )
2013-06-20 18:19:50 +04:00
if * size {
2014-01-17 02:00:18 +04:00
if out . GetInt ( "SizeRootFs" ) > 0 {
fmt . Fprintf ( w , "%s (virtual %s)\n" , utils . HumanSize ( out . GetInt64 ( "SizeRw" ) ) , utils . HumanSize ( out . GetInt64 ( "SizeRootFs" ) ) )
2013-06-20 18:19:50 +04:00
} else {
2014-01-17 02:00:18 +04:00
fmt . Fprintf ( w , "%s\n" , utils . HumanSize ( out . GetInt64 ( "SizeRw" ) ) )
2013-06-20 18:19:50 +04:00
}
2013-05-13 17:10:26 +04:00
} else {
2013-06-20 18:19:50 +04:00
fmt . Fprint ( w , "\n" )
2013-05-13 17:10:26 +04:00
}
2013-02-14 05:10:00 +04:00
} else {
2014-01-17 02:00:18 +04:00
fmt . Fprintln ( w , outID )
2013-02-14 05:10:00 +04:00
}
}
2013-04-22 20:17:47 +04:00
2013-02-14 05:28:13 +04:00
if ! * quiet {
2013-02-14 05:10:00 +04:00
w . Flush ( )
}
return nil
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdCommit ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "commit" , "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]" , "Create a new image from a container's changes" )
2013-12-23 23:43:54 +04:00
flComment := cmd . String ( [ ] string { "m" , "-message" } , "" , "Commit message" )
flAuthor := cmd . String ( [ ] string { "a" , "#author" , "-author" } , "" , "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"" )
flConfig := cmd . String ( [ ] string { "#run" , "-run" } , "" , "Config automatically applied when the image is run. " + ` (ex: -run=' { "Cmd": ["cat", "/world"], "PortSpecs": ["22"]}') ` )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-11-08 05:30:51 +04:00
var name , repository , tag string
if cmd . NArg ( ) == 3 {
fmt . Fprintf ( cli . err , "[DEPRECATED] The format 'CONTAINER [REPOSITORY [TAG]]' as been deprecated. Please use CONTAINER [REPOSITORY[:TAG]]\n" )
name , repository , tag = cmd . Arg ( 0 ) , cmd . Arg ( 1 ) , cmd . Arg ( 2 )
} else {
name = cmd . Arg ( 0 )
repository , tag = utils . ParseRepositoryTag ( cmd . Arg ( 1 ) )
}
2013-10-30 10:26:13 +04:00
2013-04-24 18:06:03 +04:00
if name == "" {
2013-02-14 05:10:00 +04:00
cmd . Usage ( )
return nil
}
2013-04-26 03:48:31 +04:00
2013-04-24 18:06:03 +04:00
v := url . Values { }
2013-05-07 19:19:41 +04:00
v . Set ( "container" , name )
2013-04-24 18:06:03 +04:00
v . Set ( "repo" , repository )
v . Set ( "tag" , tag )
v . Set ( "comment" , * flComment )
2013-05-02 20:36:23 +04:00
v . Set ( "author" , * flAuthor )
2014-01-25 11:15:40 +04:00
var (
config * Config
env engine . Env
)
2013-04-26 03:48:31 +04:00
if * flConfig != "" {
2013-05-02 02:19:55 +04:00
config = & Config { }
2013-04-26 21:48:33 +04:00
if err := json . Unmarshal ( [ ] byte ( * flConfig ) , config ) ; err != nil {
return err
}
2013-04-26 03:48:31 +04:00
}
2014-01-25 11:15:40 +04:00
stream , _ , err := cli . call ( "POST" , "/commit?" + v . Encode ( ) , config , false )
2013-03-22 07:07:37 +04:00
if err != nil {
return err
2013-04-25 02:24:14 +04:00
}
2014-01-25 11:15:40 +04:00
if err := env . Decode ( stream ) ; err != nil {
2013-03-22 07:07:37 +04:00
return err
2013-02-14 05:10:00 +04:00
}
2013-04-24 18:06:03 +04:00
2014-02-03 17:59:09 +04:00
fmt . Fprintf ( cli . out , "%s\n" , env . Get ( "Id" ) )
2013-03-22 07:07:37 +04:00
return nil
2013-02-14 05:10:00 +04:00
}
2013-07-10 16:55:05 +04:00
func ( cli * DockerCli ) CmdEvents ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "events" , "[OPTIONS]" , "Get real time events from the server" )
2013-12-23 23:43:54 +04:00
since := cmd . String ( [ ] string { "#since" , "-since" } , "" , "Show previously created events and then stream." )
2013-07-10 16:55:05 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 0 {
cmd . Usage ( )
return nil
}
2013-07-12 20:29:23 +04:00
v := url . Values { }
if * since != "" {
2013-10-22 15:48:10 +04:00
loc := time . FixedZone ( time . Now ( ) . Zone ( ) )
format := "2006-01-02 15:04:05 -0700 MST"
if len ( * since ) < len ( format ) {
format = format [ : len ( * since ) ]
}
if t , err := time . ParseInLocation ( format , * since , loc ) ; err == nil {
v . Set ( "since" , strconv . FormatInt ( t . Unix ( ) , 10 ) )
} else {
v . Set ( "since" , * since )
}
2013-07-12 20:29:23 +04:00
}
2013-08-30 23:46:19 +04:00
if err := cli . stream ( "GET" , "/events?" + v . Encode ( ) , nil , cli . out , nil ) ; err != nil {
2013-07-10 16:55:05 +04:00
return err
}
return nil
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdExport ( args ... string ) error {
2013-11-16 14:33:17 +04:00
cmd := cli . Subcmd ( "export" , "CONTAINER" , "Export the contents of a filesystem as a tar archive to STDOUT" )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-24 18:32:51 +04:00
if cmd . NArg ( ) != 1 {
cmd . Usage ( )
2013-02-14 05:10:00 +04:00
return nil
}
2013-04-24 18:32:51 +04:00
2013-08-30 23:46:19 +04:00
if err := cli . stream ( "GET" , "/containers/" + cmd . Arg ( 0 ) + "/export" , nil , cli . out , nil ) ; err != nil {
2013-04-24 18:32:51 +04:00
return err
}
return nil
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdDiff ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "diff" , "CONTAINER" , "Inspect changes on a container's filesystem" )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-24 16:01:40 +04:00
if cmd . NArg ( ) != 1 {
2013-04-12 14:26:31 +04:00
cmd . Usage ( )
return nil
2013-02-14 05:10:00 +04:00
}
2013-04-24 16:01:40 +04:00
2014-01-22 05:56:09 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/containers/" + cmd . Arg ( 0 ) + "/changes" , nil , false ) )
2013-04-24 16:01:40 +04:00
if err != nil {
return err
}
2014-01-15 04:51:59 +04:00
outs := engine . NewTable ( "" , 0 )
2014-01-22 05:56:09 +04:00
if _ , err := outs . ReadListFrom ( body ) ; err != nil {
2013-04-24 16:01:40 +04:00
return err
}
2014-01-15 04:51:59 +04:00
for _ , change := range outs . Data {
var kind string
switch change . GetInt ( "Kind" ) {
case archive . ChangeModify :
kind = "C"
case archive . ChangeAdd :
kind = "A"
case archive . ChangeDelete :
kind = "D"
}
fmt . Fprintf ( cli . out , "%s %s\n" , kind , change . Get ( "Path" ) )
2013-02-14 05:10:00 +04:00
}
return nil
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdLogs ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "logs" , "CONTAINER" , "Fetch the logs of a container" )
2013-12-23 23:43:54 +04:00
follow := cmd . Bool ( [ ] string { "f" , "-follow" } , false , "Follow log output" )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 1 {
cmd . Usage ( )
return nil
}
2013-10-18 03:22:14 +04:00
name := cmd . Arg ( 0 )
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/containers/" + name + "/json" , nil , false ) )
2013-11-01 04:58:15 +04:00
if err != nil {
return err
}
container := & Container { }
err = json . Unmarshal ( body , container )
if err != nil {
return err
}
2013-04-22 20:17:47 +04:00
2013-12-03 10:18:01 +04:00
v := url . Values { }
v . Set ( "logs" , "1" )
v . Set ( "stdout" , "1" )
v . Set ( "stderr" , "1" )
2013-12-04 00:35:22 +04:00
if * follow && container . State . Running {
2013-12-03 10:18:01 +04:00
v . Set ( "stream" , "1" )
}
if err := cli . hijack ( "POST" , "/containers/" + name + "/attach?" + v . Encode ( ) , container . Config . Tty , nil , cli . out , cli . err , nil ) ; err != nil {
2013-04-22 20:17:47 +04:00
return err
2013-02-14 05:10:00 +04:00
}
2013-04-22 20:17:47 +04:00
return nil
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdAttach ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "attach" , "[OPTIONS] CONTAINER" , "Attach to a running container" )
2013-12-23 23:43:54 +04:00
noStdin := cmd . Bool ( [ ] string { "#nostdin" , "-no-stdin" } , false , "Do not attach stdin" )
proxy := cmd . Bool ( [ ] string { "#sig-proxy" , "-sig-proxy" } , true , "Proxify all received signal to the process (even in non-tty mode)" )
2013-02-14 05:10:00 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 1 {
cmd . Usage ( )
return nil
}
2013-10-05 06:25:15 +04:00
name := cmd . Arg ( 0 )
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/containers/" + name + "/json" , nil , false ) )
2013-04-29 19:46:41 +04:00
if err != nil {
return err
2013-02-14 05:10:00 +04:00
}
2013-04-02 23:18:20 +04:00
2013-05-10 08:53:28 +04:00
container := & Container { }
err = json . Unmarshal ( body , container )
2013-04-29 19:46:41 +04:00
if err != nil {
return err
2013-04-20 01:18:03 +04:00
}
2013-11-22 00:21:03 +04:00
if ! container . State . IsRunning ( ) {
2013-06-18 02:40:04 +04:00
return fmt . Errorf ( "Impossible to attach to a stopped container, start it first" )
}
2013-10-25 01:54:04 +04:00
if container . Config . Tty && cli . isTerminal {
2013-06-25 01:59:37 +04:00
if err := cli . monitorTtySize ( cmd . Arg ( 0 ) ) ; err != nil {
2013-10-25 01:54:04 +04:00
utils . Debugf ( "Error monitoring TTY size: %s" , err )
2013-06-25 01:59:37 +04:00
}
2013-06-13 23:57:35 +04:00
}
2013-06-18 01:44:35 +04:00
2013-10-26 04:17:33 +04:00
var in io . ReadCloser
2013-05-02 07:07:06 +04:00
v := url . Values { }
v . Set ( "stream" , "1" )
2013-09-12 10:50:26 +04:00
if ! * noStdin && container . Config . OpenStdin {
v . Set ( "stdin" , "1" )
2013-10-26 04:17:33 +04:00
in = cli . in
2013-09-12 10:50:26 +04:00
}
2013-05-29 15:40:54 +04:00
v . Set ( "stdout" , "1" )
2013-06-18 01:44:35 +04:00
v . Set ( "stderr" , "1" )
2013-09-12 10:50:26 +04:00
if * proxy && ! container . Config . Tty {
2013-10-29 22:59:08 +04:00
sigc := cli . forwardAllSignals ( cmd . Arg ( 0 ) )
defer utils . StopCatch ( sigc )
2013-09-12 10:50:26 +04:00
}
2013-10-26 04:17:33 +04:00
if err := cli . hijack ( "POST" , "/containers/" + cmd . Arg ( 0 ) + "/attach?" + v . Encode ( ) , container . Config . Tty , in , cli . out , cli . err , nil ) ; err != nil {
2013-06-18 01:44:35 +04:00
return err
2013-04-05 08:51:40 +04:00
}
2013-11-28 05:10:20 +04:00
_ , status , err := getExitCode ( cli , cmd . Arg ( 0 ) )
if err != nil {
return err
}
if status != 0 {
2013-10-12 16:14:52 +04:00
return & utils . StatusError { StatusCode : status }
2013-11-28 05:10:20 +04:00
}
2013-04-26 17:08:33 +04:00
return nil
2013-02-14 05:10:00 +04:00
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdSearch ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "search" , "TERM" , "Search the docker index for images" )
2013-12-23 23:43:54 +04:00
noTrunc := cmd . Bool ( [ ] string { "#notrunc" , "-no-trunc" } , false , "Don't truncate output" )
trusted := cmd . Bool ( [ ] string { "t" , "#trusted" , "-trusted" } , false , "Only show trusted builds" )
stars := cmd . Int ( [ ] string { "s" , "#stars" , "-stars" } , 0 , "Only displays with at least xxx stars" )
2013-05-07 14:49:08 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 1 {
cmd . Usage ( )
return nil
}
2013-05-07 22:59:04 +04:00
2013-05-07 22:37:35 +04:00
v := url . Values { }
2013-05-07 22:59:04 +04:00
v . Set ( "term" , cmd . Arg ( 0 ) )
2014-01-22 05:56:09 +04:00
2014-01-31 03:21:42 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/images/search?" + v . Encode ( ) , nil , true ) )
2014-01-22 05:56:09 +04:00
2013-05-07 22:59:04 +04:00
if err != nil {
return err
}
2014-01-16 02:43:12 +04:00
outs := engine . NewTable ( "star_count" , 0 )
2014-01-22 05:56:09 +04:00
if _ , err := outs . ReadListFrom ( body ) ; err != nil {
2013-05-07 14:49:08 +04:00
return err
}
2013-10-24 23:20:34 +04:00
w := tabwriter . NewWriter ( cli . out , 10 , 1 , 3 , ' ' , 0 )
fmt . Fprintf ( w , "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n" )
2014-01-16 02:43:12 +04:00
for _ , out := range outs . Data {
if ( * trusted && ! out . GetBool ( "is_trusted" ) ) || ( * stars > out . GetInt ( "star_count" ) ) {
2013-11-01 06:21:35 +04:00
continue
}
2014-01-16 02:43:12 +04:00
desc := strings . Replace ( out . Get ( "description" ) , "\n" , " " , - 1 )
2013-06-07 17:09:24 +04:00
desc = strings . Replace ( desc , "\r" , " " , - 1 )
2013-11-05 03:34:51 +04:00
if ! * noTrunc && len ( desc ) > 45 {
desc = utils . Trunc ( desc , 42 ) + "..."
2013-06-07 17:09:24 +04:00
}
2014-01-16 02:43:12 +04:00
fmt . Fprintf ( w , "%s\t%s\t%d\t" , out . Get ( "name" ) , desc , out . GetInt ( "star_count" ) )
if out . GetBool ( "is_official" ) {
2013-10-24 23:20:34 +04:00
fmt . Fprint ( w , "[OK]" )
}
fmt . Fprint ( w , "\t" )
2014-01-16 02:43:12 +04:00
if out . GetBool ( "is_trusted" ) {
2013-10-24 23:20:34 +04:00
fmt . Fprint ( w , "[OK]" )
2013-06-07 17:09:24 +04:00
}
2013-10-24 23:20:34 +04:00
fmt . Fprint ( w , "\n" )
2013-05-07 14:49:08 +04:00
}
w . Flush ( )
return nil
}
2013-02-28 23:52:22 +04:00
// Ports type - Used to parse multiple -p flags
type ports [ ] int
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdTag ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "tag" , "[OPTIONS] IMAGE REPOSITORY[:TAG]" , "Tag an image into a repository" )
2013-12-23 23:43:54 +04:00
force := cmd . Bool ( [ ] string { "f" , "#force" , "-force" } , false , "Force" )
2013-03-23 05:27:18 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
2013-04-23 20:20:53 +04:00
if cmd . NArg ( ) != 2 && cmd . NArg ( ) != 3 {
2013-03-23 05:27:18 +04:00
cmd . Usage ( )
return nil
}
2013-04-23 20:20:53 +04:00
2013-11-08 05:30:51 +04:00
var repository , tag string
2013-10-30 10:26:13 +04:00
2013-11-08 05:30:51 +04:00
if cmd . NArg ( ) == 3 {
fmt . Fprintf ( cli . err , "[DEPRECATED] The format 'IMAGE [REPOSITORY [TAG]]' as been deprecated. Please use IMAGE [REPOSITORY[:TAG]]\n" )
repository , tag = cmd . Arg ( 1 ) , cmd . Arg ( 2 )
} else {
repository , tag = utils . ParseRepositoryTag ( cmd . Arg ( 1 ) )
}
v := url . Values { }
2013-10-30 10:26:13 +04:00
v . Set ( "repo" , repository )
v . Set ( "tag" , tag )
2013-04-23 20:20:53 +04:00
if * force {
2013-04-26 17:08:33 +04:00
v . Set ( "force" , "1" )
2013-04-23 20:20:53 +04:00
}
2014-01-21 08:06:19 +04:00
if _ , _ , err := readBody ( cli . call ( "POST" , "/images/" + cmd . Arg ( 0 ) + "/tag?" + v . Encode ( ) , nil , false ) ) ; err != nil {
2013-04-23 20:20:53 +04:00
return err
}
return nil
2013-03-23 05:27:18 +04:00
}
2013-11-21 05:41:25 +04:00
//FIXME Only used in tests
2014-01-16 02:36:13 +04:00
func ParseRun ( args [ ] string , sysInfo * sysinfo . SysInfo ) ( * Config , * HostConfig , * flag . FlagSet , error ) {
2013-11-21 05:41:25 +04:00
cmd := flag . NewFlagSet ( "run" , flag . ContinueOnError )
cmd . SetOutput ( ioutil . Discard )
cmd . Usage = nil
2014-01-16 02:36:13 +04:00
return parseRun ( cmd , args , sysInfo )
2013-11-21 05:41:25 +04:00
}
2014-01-16 02:36:13 +04:00
func parseRun ( cmd * flag . FlagSet , args [ ] string , sysInfo * sysinfo . SysInfo ) ( * Config , * HostConfig , * flag . FlagSet , error ) {
2013-11-22 22:12:27 +04:00
var (
// FIXME: use utils.ListOpts for attach and volumes?
2013-11-26 21:46:06 +04:00
flAttach = NewListOpts ( ValidateAttach )
flVolumes = NewListOpts ( ValidatePath )
flLinks = NewListOpts ( ValidateLink )
flEnv = NewListOpts ( ValidateEnv )
2013-11-22 22:12:27 +04:00
2013-11-26 21:46:06 +04:00
flPublish ListOpts
flExpose ListOpts
flDns ListOpts
flVolumesFrom ListOpts
flLxcOpts ListOpts
2013-11-22 22:12:27 +04:00
2013-12-23 23:43:54 +04:00
flAutoRemove = cmd . Bool ( [ ] string { "#rm" , "-rm" } , false , "Automatically remove the container when it exits (incompatible with -d)" )
flDetach = cmd . Bool ( [ ] string { "d" , "-detach" } , false , "Detached mode: Run container in the background, print new container id" )
flNetwork = cmd . Bool ( [ ] string { "n" , "-networking" } , true , "Enable networking for this container" )
flPrivileged = cmd . Bool ( [ ] string { "#privileged" , "-privileged" } , false , "Give extended privileges to this container" )
flPublishAll = cmd . Bool ( [ ] string { "P" , "-publish-all" } , false , "Publish all exposed ports to the host interfaces" )
flStdin = cmd . Bool ( [ ] string { "i" , "-interactive" } , false , "Keep stdin open even if not attached" )
flTty = cmd . Bool ( [ ] string { "t" , "-tty" } , false , "Allocate a pseudo-tty" )
flContainerIDFile = cmd . String ( [ ] string { "#cidfile" , "-cidfile" } , "" , "Write the container ID to the file" )
flEntrypoint = cmd . String ( [ ] string { "#entrypoint" , "-entrypoint" } , "" , "Overwrite the default entrypoint of the image" )
flHostname = cmd . String ( [ ] string { "h" , "-hostname" } , "" , "Container host name" )
flMemoryString = cmd . String ( [ ] string { "m" , "-memory" } , "" , "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)" )
2014-01-14 00:16:12 +04:00
flUser = cmd . String ( [ ] string { "u" , "-user" } , "" , "Username or UID" )
2013-12-23 23:43:54 +04:00
flWorkingDir = cmd . String ( [ ] string { "w" , "-workdir" } , "" , "Working directory inside the container" )
flCpuShares = cmd . Int64 ( [ ] string { "c" , "-cpu-shares" } , 0 , "CPU shares (relative weight)" )
2013-11-22 22:12:27 +04:00
// For documentation purpose
2013-12-23 23:43:54 +04:00
_ = cmd . Bool ( [ ] string { "#sig-proxy" , "-sig-proxy" } , true , "Proxify all received signal to the process (even in non-tty mode)" )
_ = cmd . String ( [ ] string { "#name" , "-name" } , "" , "Assign a name to the container" )
2013-11-22 22:12:27 +04:00
)
2013-11-21 05:41:25 +04:00
2013-12-23 23:43:54 +04:00
cmd . Var ( & flAttach , [ ] string { "a" , "-attach" } , "Attach to stdin, stdout or stderr." )
cmd . Var ( & flVolumes , [ ] string { "v" , "-volume" } , "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)" )
cmd . Var ( & flLinks , [ ] string { "#link" , "-link" } , "Add link to another container (name:alias)" )
cmd . Var ( & flEnv , [ ] string { "e" , "-env" } , "Set environment variables" )
2013-11-21 05:41:25 +04:00
2013-12-23 23:43:54 +04:00
cmd . Var ( & flPublish , [ ] string { "p" , "-publish" } , fmt . Sprintf ( "Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)" , PortSpecTemplateFormat ) )
cmd . Var ( & flExpose , [ ] string { "#expose" , "-expose" } , "Expose a port from the container without publishing it to your host" )
cmd . Var ( & flDns , [ ] string { "#dns" , "-dns" } , "Set custom dns servers" )
cmd . Var ( & flVolumesFrom , [ ] string { "#volumes-from" , "-volumes-from" } , "Mount volumes from the specified container(s)" )
cmd . Var ( & flLxcOpts , [ ] string { "#lxc-conf" , "-lxc-conf" } , "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"" )
2013-11-21 05:41:25 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil , nil , cmd , err
}
2013-11-22 22:12:27 +04:00
// Check if the kernel supports memory limit cgroup.
2014-01-16 02:36:13 +04:00
if sysInfo != nil && * flMemoryString != "" && ! sysInfo . MemoryLimit {
2013-11-22 22:12:27 +04:00
* flMemoryString = ""
}
// Validate input params
2013-11-26 21:46:06 +04:00
if * flDetach && flAttach . Len ( ) > 0 {
2013-11-21 05:41:25 +04:00
return nil , nil , cmd , ErrConflictAttachDetach
}
if * flWorkingDir != "" && ! path . IsAbs ( * flWorkingDir ) {
return nil , nil , cmd , ErrInvalidWorikingDirectory
}
if * flDetach && * flAutoRemove {
return nil , nil , cmd , ErrConflictDetachAutoRemove
}
// If neither -d or -a are set, attach to everything by default
2013-11-26 21:46:06 +04:00
if flAttach . Len ( ) == 0 && ! * flDetach {
2013-11-21 05:41:25 +04:00
if ! * flDetach {
flAttach . Set ( "stdout" )
flAttach . Set ( "stderr" )
if * flStdin {
flAttach . Set ( "stdin" )
}
}
}
var flMemory int64
if * flMemoryString != "" {
parsedMemory , err := utils . RAMInBytes ( * flMemoryString )
if err != nil {
return nil , nil , cmd , err
}
flMemory = parsedMemory
}
var binds [ ] string
// add any bind targets to the list of container volumes
2013-11-26 21:46:06 +04:00
for bind := range flVolumes . GetMap ( ) {
if arr := strings . Split ( bind , ":" ) ; len ( arr ) > 1 {
2013-11-21 05:41:25 +04:00
if arr [ 0 ] == "/" {
return nil , nil , cmd , fmt . Errorf ( "Invalid bind mount: source can't be '/'" )
}
dstDir := arr [ 1 ]
2013-11-26 21:46:06 +04:00
flVolumes . Set ( dstDir )
2013-11-21 05:41:25 +04:00
binds = append ( binds , bind )
2013-11-26 21:46:06 +04:00
flVolumes . Delete ( bind )
2013-12-18 23:40:00 +04:00
} else if bind == "/" {
return nil , nil , cmd , fmt . Errorf ( "Invalid volume: path can't be '/'" )
2013-11-21 05:41:25 +04:00
}
}
2013-11-22 22:12:27 +04:00
var (
parsedArgs = cmd . Args ( )
runCmd [ ] string
entrypoint [ ] string
image string
)
2013-11-21 05:41:25 +04:00
if len ( parsedArgs ) >= 1 {
image = cmd . Arg ( 0 )
}
if len ( parsedArgs ) > 1 {
runCmd = parsedArgs [ 1 : ]
}
if * flEntrypoint != "" {
entrypoint = [ ] string { * flEntrypoint }
}
lxcConf , err := parseLxcConfOpts ( flLxcOpts )
if err != nil {
return nil , nil , cmd , err
}
2013-11-22 22:12:27 +04:00
var (
domainname string
hostname = * flHostname
parts = strings . SplitN ( hostname , "." , 2 )
)
2013-11-21 05:41:25 +04:00
if len ( parts ) > 1 {
hostname = parts [ 0 ]
domainname = parts [ 1 ]
}
2013-11-26 21:46:06 +04:00
ports , portBindings , err := parsePortSpecs ( flPublish . GetAll ( ) )
2013-11-21 05:41:25 +04:00
if err != nil {
return nil , nil , cmd , err
}
// Merge in exposed ports to the map of published ports
2013-11-26 21:46:06 +04:00
for _ , e := range flExpose . GetAll ( ) {
2013-11-21 05:41:25 +04:00
if strings . Contains ( e , ":" ) {
2013-12-23 23:43:54 +04:00
return nil , nil , cmd , fmt . Errorf ( "Invalid port format for --expose: %s" , e )
2013-11-21 05:41:25 +04:00
}
p := NewPort ( splitProtoPort ( e ) )
if _ , exists := ports [ p ] ; ! exists {
ports [ p ] = struct { } { }
}
}
config := & Config {
Hostname : hostname ,
Domainname : domainname ,
PortSpecs : nil , // Deprecated
ExposedPorts : ports ,
User : * flUser ,
Tty : * flTty ,
NetworkDisabled : ! * flNetwork ,
OpenStdin : * flStdin ,
Memory : flMemory ,
CpuShares : * flCpuShares ,
2013-11-26 21:46:06 +04:00
AttachStdin : flAttach . Get ( "stdin" ) ,
AttachStdout : flAttach . Get ( "stdout" ) ,
AttachStderr : flAttach . Get ( "stderr" ) ,
Env : flEnv . GetAll ( ) ,
2013-11-21 05:41:25 +04:00
Cmd : runCmd ,
2013-11-26 21:46:06 +04:00
Dns : flDns . GetAll ( ) ,
2013-11-21 05:41:25 +04:00
Image : image ,
2013-11-26 21:46:06 +04:00
Volumes : flVolumes . GetMap ( ) ,
VolumesFrom : strings . Join ( flVolumesFrom . GetAll ( ) , "," ) ,
2013-11-21 05:41:25 +04:00
Entrypoint : entrypoint ,
WorkingDir : * flWorkingDir ,
}
hostConfig := & HostConfig {
Binds : binds ,
ContainerIDFile : * flContainerIDFile ,
LxcConf : lxcConf ,
Privileged : * flPrivileged ,
PortBindings : portBindings ,
2013-11-26 21:46:06 +04:00
Links : flLinks . GetAll ( ) ,
2013-11-21 05:41:25 +04:00
PublishAllPorts : * flPublishAll ,
}
2014-01-16 02:36:13 +04:00
if sysInfo != nil && flMemory > 0 && ! sysInfo . SwapLimit {
2013-11-21 05:41:25 +04:00
//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
config . MemorySwap = - 1
}
// When allocating stdin in attached mode, close stdin at client disconnect
if config . OpenStdin && config . AttachStdin {
config . StdinOnce = true
}
return config , hostConfig , cmd , nil
}
2013-05-15 18:16:46 +04:00
func ( cli * DockerCli ) CmdRun ( args ... string ) error {
2013-11-21 05:41:25 +04:00
config , hostConfig , cmd , err := parseRun ( cli . Subcmd ( "run" , "[OPTIONS] IMAGE [COMMAND] [ARG...]" , "Run a command in a new container" ) , args , nil )
2013-03-23 23:16:58 +04:00
if err != nil {
return err
2013-02-14 05:10:00 +04:00
}
2013-03-23 23:39:09 +04:00
if config . Image == "" {
2013-04-23 20:20:53 +04:00
cmd . Usage ( )
return nil
2013-03-23 23:16:58 +04:00
}
2013-04-06 06:48:49 +04:00
2013-11-23 00:14:34 +04:00
// Retrieve relevant client-side config
var (
flName = cmd . Lookup ( "name" )
flRm = cmd . Lookup ( "rm" )
flSigProxy = cmd . Lookup ( "sig-proxy" )
autoRemove , _ = strconv . ParseBool ( flRm . Value . String ( ) )
sigProxy , _ = strconv . ParseBool ( flSigProxy . Value . String ( ) )
)
2013-09-27 01:52:37 +04:00
2013-11-23 00:14:34 +04:00
// Disable sigProxy in case on TTY
2013-10-29 05:23:41 +04:00
if config . Tty {
sigProxy = false
}
2013-10-08 23:15:29 +04:00
2013-11-23 00:14:34 +04:00
var containerIDFile io . WriteCloser
2013-07-12 02:11:44 +04:00
if len ( hostConfig . ContainerIDFile ) > 0 {
2013-11-23 00:14:34 +04:00
if _ , err := os . Stat ( hostConfig . ContainerIDFile ) ; err == nil {
2013-07-12 02:11:44 +04:00
return fmt . Errorf ( "cid file found, make sure the other container isn't running or delete %s" , hostConfig . ContainerIDFile )
}
2013-11-23 00:14:34 +04:00
if containerIDFile , err = os . Create ( hostConfig . ContainerIDFile ) ; err != nil {
2013-07-12 02:11:44 +04:00
return fmt . Errorf ( "failed to create the container ID file: %s" , err )
}
defer containerIDFile . Close ( )
}
2013-11-23 00:14:34 +04:00
2013-10-29 03:58:59 +04:00
containerValues := url . Values { }
2013-11-23 00:14:34 +04:00
if name := flName . Value . String ( ) ; name != "" {
2013-10-29 03:58:59 +04:00
containerValues . Set ( "name" , name )
}
2013-07-12 02:11:44 +04:00
2013-05-02 07:07:06 +04:00
//create the container
2014-01-25 11:15:40 +04:00
stream , statusCode , err := cli . call ( "POST" , "/containers/create?" + containerValues . Encode ( ) , config , false )
2013-05-02 07:07:06 +04:00
//if image not found try to pull it
if statusCode == 404 {
2013-08-18 07:03:54 +04:00
_ , tag := utils . ParseRepositoryTag ( config . Image )
if tag == "" {
tag = DEFAULTTAG
}
2013-08-30 03:25:11 +04:00
fmt . Fprintf ( cli . err , "Unable to find image '%s' (tag: %s) locally\n" , config . Image , tag )
2013-08-18 07:03:54 +04:00
2013-05-06 13:31:22 +04:00
v := url . Values { }
2013-07-10 03:45:32 +04:00
repos , tag := utils . ParseRepositoryTag ( config . Image )
v . Set ( "fromImage" , repos )
v . Set ( "tag" , tag )
2013-08-23 16:02:24 +04:00
// Resolve the Repository name from fqn to endpoint + name
2013-11-23 00:14:34 +04:00
endpoint , _ , err := registry . ResolveRepositoryName ( repos )
2013-08-23 16:02:24 +04:00
if err != nil {
return err
}
// Load the auth config file, to be able to pull the image
cli . LoadConfigFile ( )
// Resolve the Auth config relevant for this server
authConfig := cli . configFile . ResolveAuthConfig ( endpoint )
buf , err := json . Marshal ( authConfig )
if err != nil {
return err
}
2013-08-31 01:15:07 +04:00
registryAuthHeader := [ ] string {
base64 . URLEncoding . EncodeToString ( buf ) ,
}
2013-11-23 00:14:34 +04:00
if err = cli . stream ( "POST" , "/images/create?" + v . Encode ( ) , nil , cli . err , map [ string ] [ ] string { "X-Registry-Auth" : registryAuthHeader } ) ; err != nil {
2013-05-02 07:07:06 +04:00
return err
}
2014-01-25 11:15:40 +04:00
if stream , _ , err = cli . call ( "POST" , "/containers/create?" + containerValues . Encode ( ) , config , false ) ; err != nil {
2013-05-06 13:31:22 +04:00
return err
}
2013-11-26 11:20:36 +04:00
} else if err != nil {
2013-04-02 10:52:20 +04:00
return err
2013-04-05 08:51:40 +04:00
}
2013-04-25 02:14:10 +04:00
2014-01-25 11:15:40 +04:00
var runResult engine . Env
if err := runResult . Decode ( stream ) ; err != nil {
2013-05-02 07:07:06 +04:00
return err
2013-02-14 05:10:00 +04:00
}
2013-05-02 07:07:06 +04:00
2014-01-25 11:15:40 +04:00
for _ , warning := range runResult . GetList ( "Warnings" ) {
2013-06-28 02:25:31 +04:00
fmt . Fprintf ( cli . err , "WARNING: %s\n" , warning )
2013-05-02 20:36:23 +04:00
}
2013-11-23 00:14:34 +04:00
2013-07-08 12:01:16 +04:00
if len ( hostConfig . ContainerIDFile ) > 0 {
2014-01-25 11:15:40 +04:00
if _ , err = containerIDFile . Write ( [ ] byte ( runResult . Get ( "Id" ) ) ) ; err != nil {
2013-07-08 12:01:16 +04:00
return fmt . Errorf ( "failed to write the container ID to the file: %s" , err )
}
}
2013-05-02 20:36:23 +04:00
2013-10-08 23:15:29 +04:00
if sigProxy {
2014-01-25 11:15:40 +04:00
sigc := cli . forwardAllSignals ( runResult . Get ( "Id" ) )
2013-10-29 22:59:08 +04:00
defer utils . StopCatch ( sigc )
2013-10-08 23:15:29 +04:00
}
2013-10-09 05:42:53 +04:00
var (
2013-11-23 00:14:34 +04:00
waitDisplayId chan struct { }
errCh chan error
2013-10-09 05:42:53 +04:00
)
2013-06-27 23:48:25 +04:00
2013-06-05 02:24:25 +04:00
if ! config . AttachStdout && ! config . AttachStderr {
2013-06-25 05:22:02 +04:00
// Make this asynchrone in order to let the client write to stdin before having to read the ID
2013-11-23 00:14:34 +04:00
waitDisplayId = make ( chan struct { } )
2013-06-27 23:48:25 +04:00
go func ( ) {
2013-11-23 00:14:34 +04:00
defer close ( waitDisplayId )
2014-01-25 11:15:40 +04:00
fmt . Fprintf ( cli . out , "%s\n" , runResult . Get ( "Id" ) )
2013-06-27 23:48:25 +04:00
} ( )
2013-06-24 23:12:06 +04:00
}
2013-06-25 05:22:02 +04:00
2013-11-23 02:09:37 +04:00
// We need to instanciate the chan because the select needs it. It can
// be closed but can't be uninitialized.
hijacked := make ( chan io . Closer )
2013-10-10 22:20:50 +04:00
2013-11-23 02:09:37 +04:00
// Block the return until the chan gets closed
defer func ( ) {
utils . Debugf ( "End of CmdRun(), Waiting for hijack to finish." )
if _ , ok := <- hijacked ; ok {
utils . Errorf ( "Hijack did not finish (chan still open)" )
}
} ( )
2013-05-29 15:40:54 +04:00
2013-06-24 23:12:06 +04:00
if config . AttachStdin || config . AttachStdout || config . AttachStderr {
2013-11-23 00:14:34 +04:00
var (
out , stderr io . Writer
in io . ReadCloser
v = url . Values { }
)
2013-05-29 15:40:54 +04:00
v . Set ( "stream" , "1" )
if config . AttachStdin {
v . Set ( "stdin" , "1" )
2013-10-26 04:17:33 +04:00
in = cli . in
2013-05-29 15:40:54 +04:00
}
if config . AttachStdout {
v . Set ( "stdout" , "1" )
2013-08-14 00:30:08 +04:00
out = cli . out
2013-05-29 15:40:54 +04:00
}
2013-06-18 02:45:08 +04:00
if config . AttachStderr {
2013-05-29 15:40:54 +04:00
v . Set ( "stderr" , "1" )
2013-09-10 05:14:23 +04:00
if config . Tty {
stderr = cli . out
} else {
stderr = cli . err
}
2013-05-29 15:40:54 +04:00
}
2013-06-25 05:22:02 +04:00
2013-10-09 05:42:53 +04:00
errCh = utils . Go ( func ( ) error {
2014-01-25 11:15:40 +04:00
return cli . hijack ( "POST" , "/containers/" + runResult . Get ( "Id" ) + "/attach?" + v . Encode ( ) , config . Tty , in , out , stderr , hijacked )
2013-10-09 05:42:53 +04:00
} )
2013-10-10 22:20:50 +04:00
} else {
close ( hijacked )
}
// Acknowledge the hijack before starting
select {
2013-11-23 02:09:37 +04:00
case closer := <- hijacked :
// Make sure that hijack gets closed when returning. (result
// in closing hijack chan and freeing server's goroutines.
if closer != nil {
defer closer . Close ( )
}
2013-10-10 22:20:50 +04:00
case err := <- errCh :
if err != nil {
utils . Debugf ( "Error hijack: %s" , err )
return err
}
2013-10-09 05:42:53 +04:00
}
//start the container
2014-01-25 11:15:40 +04:00
if _ , _ , err = readBody ( cli . call ( "POST" , "/containers/" + runResult . Get ( "Id" ) + "/start" , hostConfig , false ) ) ; err != nil {
2013-10-09 05:42:53 +04:00
return err
}
2013-07-20 14:47:13 +04:00
2013-10-25 01:54:04 +04:00
if ( config . AttachStdin || config . AttachStdout || config . AttachStderr ) && config . Tty && cli . isTerminal {
2014-01-25 11:15:40 +04:00
if err := cli . monitorTtySize ( runResult . Get ( "Id" ) ) ; err != nil {
2013-10-24 22:19:06 +04:00
utils . Errorf ( "Error monitoring TTY size: %s\n" , err )
}
}
2013-10-09 05:42:53 +04:00
if errCh != nil {
if err := <- errCh ; err != nil {
2013-06-18 02:45:08 +04:00
utils . Debugf ( "Error hijack: %s" , err )
return err
2013-05-02 07:07:06 +04:00
}
2013-05-07 23:36:24 +04:00
}
2013-06-27 23:48:25 +04:00
2013-11-23 00:14:34 +04:00
// Detached mode: wait for the id to be displayed and return.
2013-06-27 23:48:25 +04:00
if ! config . AttachStdout && ! config . AttachStderr {
2013-09-06 03:54:03 +04:00
// Detached mode
2013-11-23 00:14:34 +04:00
<- waitDisplayId
return nil
}
2013-11-22 23:23:48 +04:00
2013-11-23 00:14:34 +04:00
var status int
// Attached mode
if autoRemove {
// Autoremove: wait for the container to finish, retrieve
// the exit code and remove the container
2014-01-25 11:15:40 +04:00
if _ , _ , err := readBody ( cli . call ( "POST" , "/containers/" + runResult . Get ( "Id" ) + "/wait" , nil , false ) ) ; err != nil {
2013-09-06 03:54:03 +04:00
return err
}
2014-01-25 11:15:40 +04:00
if _ , status , err = getExitCode ( cli , runResult . Get ( "Id" ) ) ; err != nil {
2013-11-23 00:14:34 +04:00
return err
2013-09-27 01:52:37 +04:00
}
2014-01-25 11:15:40 +04:00
if _ , _ , err := readBody ( cli . call ( "DELETE" , "/containers/" + runResult . Get ( "Id" ) + "?v=1" , nil , false ) ) ; err != nil {
2013-11-23 00:14:34 +04:00
return err
}
} else {
2014-01-22 06:05:03 +04:00
if ! config . Tty {
// In non-tty mode, we can't dettach, so we know we need to wait.
2014-01-25 11:15:40 +04:00
if status , err = waitForExit ( cli , runResult . Get ( "Id" ) ) ; err != nil {
2014-01-22 06:05:03 +04:00
return err
}
} else {
// In TTY mode, there is a race. If the process dies too slowly, the state can be update after the getExitCode call
// and result in a wrong exit code.
// No Autoremove: Simply retrieve the exit code
2014-01-25 11:15:40 +04:00
if _ , status , err = getExitCode ( cli , runResult . Get ( "Id" ) ) ; err != nil {
2014-01-22 06:05:03 +04:00
return err
}
2013-09-10 01:26:35 +04:00
}
2013-06-27 23:48:25 +04:00
}
2013-11-23 00:14:34 +04:00
if status != 0 {
2013-10-12 16:14:52 +04:00
return & utils . StatusError { StatusCode : status }
2013-11-23 00:14:34 +04:00
}
2013-05-02 07:07:06 +04:00
return nil
2013-02-14 05:10:00 +04:00
}
2013-04-23 20:20:53 +04:00
2013-07-17 08:07:41 +04:00
func ( cli * DockerCli ) CmdCp ( args ... string ) error {
2013-11-21 05:41:25 +04:00
cmd := cli . Subcmd ( "cp" , "CONTAINER:PATH HOSTPATH" , "Copy files/folders from the PATH to the HOSTPATH" )
2013-07-17 08:07:41 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return nil
}
if cmd . NArg ( ) != 2 {
cmd . Usage ( )
return nil
}
2014-01-25 11:15:40 +04:00
var copyData engine . Env
2013-07-17 08:07:41 +04:00
info := strings . Split ( cmd . Arg ( 0 ) , ":" )
2013-08-25 03:28:36 +04:00
if len ( info ) != 2 {
2013-11-16 14:15:04 +04:00
return fmt . Errorf ( "Error: Path not specified" )
2013-08-25 03:28:36 +04:00
}
2014-01-25 11:15:40 +04:00
copyData . Set ( "Resource" , info [ 1 ] )
copyData . Set ( "HostPath" , cmd . Arg ( 1 ) )
2013-07-17 08:07:41 +04:00
2014-01-21 08:06:19 +04:00
stream , statusCode , err := cli . call ( "POST" , "/containers/" + info [ 0 ] + "/copy" , copyData , false )
2014-01-16 23:02:51 +04:00
if stream != nil {
defer stream . Close ( )
}
2013-07-17 08:07:41 +04:00
if err != nil {
return err
}
if statusCode == 200 {
2014-01-25 11:15:40 +04:00
if err := archive . Untar ( stream , copyData . Get ( "HostPath" ) , nil ) ; err != nil {
2013-07-17 08:07:41 +04:00
return err
}
}
return nil
}
2013-09-02 20:06:17 +04:00
func ( cli * DockerCli ) CmdSave ( args ... string ) error {
2013-11-26 18:54:46 +04:00
cmd := cli . Subcmd ( "save" , "IMAGE" , "Save an image to a tar archive (streamed to stdout)" )
2013-09-02 20:06:17 +04:00
if err := cmd . Parse ( args ) ; err != nil {
2013-11-21 05:28:19 +04:00
return err
2013-09-02 20:06:17 +04:00
}
if cmd . NArg ( ) != 1 {
cmd . Usage ( )
return nil
}
image := cmd . Arg ( 0 )
if err := cli . stream ( "GET" , "/images/" + image + "/get" , nil , cli . out , nil ) ; err != nil {
return err
}
return nil
}
func ( cli * DockerCli ) CmdLoad ( args ... string ) error {
2013-12-30 00:25:57 +04:00
cmd := cli . Subcmd ( "load" , "" , "Load an image from a tar archive on STDIN" )
2013-11-21 05:28:19 +04:00
if err := cmd . Parse ( args ) ; err != nil {
return err
}
2013-09-02 20:06:17 +04:00
if cmd . NArg ( ) != 0 {
cmd . Usage ( )
return nil
}
2013-11-21 05:28:19 +04:00
if err := cli . stream ( "POST" , "/images/load" , cli . in , cli . out , nil ) ; err != nil {
return err
2013-09-02 20:06:17 +04:00
}
return nil
}
2014-01-21 08:06:19 +04:00
func ( cli * DockerCli ) call ( method , path string , data interface { } , passAuthInfo bool ) ( io . ReadCloser , int , error ) {
2014-01-25 11:15:40 +04:00
params := bytes . NewBuffer ( nil )
2013-04-23 20:20:53 +04:00
if data != nil {
2014-01-25 11:15:40 +04:00
if env , ok := data . ( engine . Env ) ; ok {
if err := env . Encode ( params ) ; err != nil {
return nil , - 1 , err
}
} else {
buf , err := json . Marshal ( data )
if err != nil {
return nil , - 1 , err
}
if _ , err := params . Write ( buf ) ; err != nil {
return nil , - 1 , err
}
2013-04-23 20:20:53 +04:00
}
2013-04-22 20:17:47 +04:00
}
2013-10-18 03:22:14 +04:00
// fixme: refactor client to support redirect
re := regexp . MustCompile ( "/+" )
path = re . ReplaceAllString ( path , "/" )
2014-01-29 23:26:54 +04:00
req , err := http . NewRequest ( method , fmt . Sprintf ( "/v%g%s" , api . APIVERSION , path ) , params )
2013-04-22 20:17:47 +04:00
if err != nil {
2013-05-02 07:07:06 +04:00
return nil , - 1 , err
2013-04-22 20:17:47 +04:00
}
2014-01-21 08:06:19 +04:00
if passAuthInfo {
cli . LoadConfigFile ( )
// Resolve the Auth config relevant for this server
authConfig := cli . configFile . ResolveAuthConfig ( auth . IndexServerAddress ( ) )
getHeaders := func ( authConfig auth . AuthConfig ) ( map [ string ] [ ] string , error ) {
buf , err := json . Marshal ( authConfig )
if err != nil {
return nil , err
}
registryAuthHeader := [ ] string {
base64 . URLEncoding . EncodeToString ( buf ) ,
}
return map [ string ] [ ] string { "X-Registry-Auth" : registryAuthHeader } , nil
}
if headers , err := getHeaders ( authConfig ) ; err == nil && headers != nil {
for k , v := range headers {
req . Header [ k ] = v
}
}
}
2013-05-09 22:24:49 +04:00
req . Header . Set ( "User-Agent" , "Docker-Client/" + VERSION )
2013-08-07 13:35:38 +04:00
req . Host = cli . addr
2013-04-23 20:20:53 +04:00
if data != nil {
req . Header . Set ( "Content-Type" , "application/json" )
2013-05-02 07:07:06 +04:00
} else if method == "POST" {
req . Header . Set ( "Content-Type" , "plain/text" )
2013-04-22 20:17:47 +04:00
}
2013-06-18 22:59:56 +04:00
dial , err := net . Dial ( cli . proto , cli . addr )
if err != nil {
2013-08-15 17:44:11 +04:00
if strings . Contains ( err . Error ( ) , "connection refused" ) {
2013-09-20 22:31:00 +04:00
return nil , - 1 , ErrConnectionRefused
2013-08-15 17:44:11 +04:00
}
2013-06-18 22:59:56 +04:00
return nil , - 1 , err
}
clientconn := httputil . NewClientConn ( dial , nil )
resp , err := clientconn . Do ( req )
2013-05-02 07:07:06 +04:00
if err != nil {
2014-01-16 23:02:51 +04:00
clientconn . Close ( )
2013-05-09 22:24:49 +04:00
if strings . Contains ( err . Error ( ) , "connection refused" ) {
2013-09-20 22:31:00 +04:00
return nil , - 1 , ErrConnectionRefused
2013-05-09 22:24:49 +04:00
}
2013-05-02 07:07:06 +04:00
return nil , - 1 , err
}
2014-01-28 00:51:31 +04:00
if resp . StatusCode < 200 || resp . StatusCode >= 400 {
body , err := ioutil . ReadAll ( resp . Body )
if err != nil {
return nil , - 1 , err
}
if len ( body ) == 0 {
return nil , resp . StatusCode , fmt . Errorf ( "Error :%s" , http . StatusText ( resp . StatusCode ) )
}
return nil , resp . StatusCode , fmt . Errorf ( "Error: %s" , bytes . TrimSpace ( body ) )
}
2014-01-16 23:02:51 +04:00
wrapper := utils . NewReadCloserWrapper ( resp . Body , func ( ) error {
if resp != nil && resp . Body != nil {
resp . Body . Close ( )
2013-06-10 19:06:52 +04:00
}
2014-01-16 23:02:51 +04:00
return clientconn . Close ( )
} )
return wrapper , resp . StatusCode , nil
2013-05-02 07:07:06 +04:00
}
2013-04-22 22:16:32 +04:00
2013-08-30 23:46:19 +04:00
func ( cli * DockerCli ) stream ( method , path string , in io . Reader , out io . Writer , headers map [ string ] [ ] string ) error {
2013-05-16 23:09:06 +04:00
if ( method == "POST" || method == "PUT" ) && in == nil {
in = bytes . NewReader ( [ ] byte { } )
}
2013-10-18 03:22:14 +04:00
// fixme: refactor client to support redirect
re := regexp . MustCompile ( "/+" )
path = re . ReplaceAllString ( path , "/" )
2014-01-29 23:26:54 +04:00
req , err := http . NewRequest ( method , fmt . Sprintf ( "/v%g%s" , api . APIVERSION , path ) , in )
2013-05-10 01:28:03 +04:00
if err != nil {
return err
}
req . Header . Set ( "User-Agent" , "Docker-Client/" + VERSION )
2013-08-07 13:35:38 +04:00
req . Host = cli . addr
2013-05-10 01:28:03 +04:00
if method == "POST" {
req . Header . Set ( "Content-Type" , "plain/text" )
}
2013-08-30 23:46:19 +04:00
if headers != nil {
for k , v := range headers {
req . Header [ k ] = v
}
}
2013-06-18 22:59:56 +04:00
dial , err := net . Dial ( cli . proto , cli . addr )
if err != nil {
2013-08-15 17:44:11 +04:00
if strings . Contains ( err . Error ( ) , "connection refused" ) {
return fmt . Errorf ( "Can't connect to docker daemon. Is 'docker -d' running on this host?" )
}
2013-06-18 22:59:56 +04:00
return err
}
clientconn := httputil . NewClientConn ( dial , nil )
resp , err := clientconn . Do ( req )
defer clientconn . Close ( )
2013-05-10 01:28:03 +04:00
if err != nil {
if strings . Contains ( err . Error ( ) , "connection refused" ) {
return fmt . Errorf ( "Can't connect to docker daemon. Is 'docker -d' running on this host?" )
}
return err
}
defer resp . Body . Close ( )
2013-06-21 05:18:36 +04:00
2013-05-13 13:38:13 +04:00
if resp . StatusCode < 200 || resp . StatusCode >= 400 {
body , err := ioutil . ReadAll ( resp . Body )
if err != nil {
return err
}
2013-06-10 19:06:52 +04:00
if len ( body ) == 0 {
return fmt . Errorf ( "Error :%s" , http . StatusText ( resp . StatusCode ) )
}
2013-11-12 02:44:51 +04:00
return fmt . Errorf ( "Error: %s" , bytes . TrimSpace ( body ) )
2013-05-13 13:38:13 +04:00
}
2014-01-29 23:26:54 +04:00
if api . MatchesContentType ( resp . Header . Get ( "Content-Type" ) , "application/json" ) {
2013-12-02 23:53:04 +04:00
return utils . DisplayJSONMessagesStream ( resp . Body , out , cli . terminalFd , cli . isTerminal )
2013-11-19 03:35:56 +04:00
}
if _ , err := io . Copy ( out , resp . Body ) ; err != nil {
return err
2013-04-22 22:16:32 +04:00
}
2013-04-02 23:21:35 +04:00
return nil
2013-02-14 05:10:00 +04:00
}
2013-11-23 02:09:37 +04:00
func ( cli * DockerCli ) hijack ( method , path string , setRawTerminal bool , in io . ReadCloser , stdout , stderr io . Writer , started chan io . Closer ) error {
defer func ( ) {
if started != nil {
close ( started )
}
} ( )
2013-10-18 03:22:14 +04:00
// fixme: refactor client to support redirect
re := regexp . MustCompile ( "/+" )
path = re . ReplaceAllString ( path , "/" )
2013-06-21 05:18:36 +04:00
2014-01-29 23:26:54 +04:00
req , err := http . NewRequest ( method , fmt . Sprintf ( "/v%g%s" , api . APIVERSION , path ) , nil )
2013-05-02 07:07:06 +04:00
if err != nil {
return err
2013-03-21 13:04:10 +04:00
}
2013-06-21 05:18:36 +04:00
req . Header . Set ( "User-Agent" , "Docker-Client/" + VERSION )
2013-05-07 21:23:50 +04:00
req . Header . Set ( "Content-Type" , "plain/text" )
2013-08-07 13:35:38 +04:00
req . Host = cli . addr
2013-06-21 05:18:36 +04:00
2013-06-18 22:59:56 +04:00
dial , err := net . Dial ( cli . proto , cli . addr )
2013-02-14 05:10:00 +04:00
if err != nil {
2013-08-15 17:44:11 +04:00
if strings . Contains ( err . Error ( ) , "connection refused" ) {
return fmt . Errorf ( "Can't connect to docker daemon. Is 'docker -d' running on this host?" )
}
2013-04-23 20:20:53 +04:00
return err
2013-04-22 20:17:47 +04:00
}
2013-04-23 20:20:53 +04:00
clientconn := httputil . NewClientConn ( dial , nil )
defer clientconn . Close ( )
2013-06-21 05:18:36 +04:00
// Server hijacks the connection, error 'connection closed' expected
clientconn . Do ( req )
2013-05-06 13:31:22 +04:00
rwc , br := clientconn . Hijack ( )
2013-04-23 20:20:53 +04:00
defer rwc . Close ( )
2013-10-10 22:20:50 +04:00
if started != nil {
2013-11-23 02:09:37 +04:00
started <- rwc
2013-10-10 22:20:50 +04:00
}
2013-09-10 05:14:23 +04:00
var receiveStdout chan error
2013-11-29 21:55:15 +04:00
var oldState * term . State
if in != nil && setRawTerminal && cli . isTerminal && os . Getenv ( "NORAW" ) == "" {
oldState , err = term . SetRawTerminal ( cli . terminalFd )
if err != nil {
return err
}
defer term . RestoreTerminal ( cli . terminalFd , oldState )
}
2013-12-05 05:08:14 +04:00
if stdout != nil || stderr != nil {
2013-09-10 05:14:23 +04:00
receiveStdout = utils . Go ( func ( ) ( err error ) {
2013-11-29 21:11:20 +04:00
defer func ( ) {
if in != nil {
2013-11-29 21:55:15 +04:00
if setRawTerminal && cli . isTerminal {
term . RestoreTerminal ( cli . terminalFd , oldState )
}
2013-11-29 21:11:20 +04:00
in . Close ( )
}
} ( )
2013-09-10 05:14:23 +04:00
// When TTY is ON, use regular copy
if setRawTerminal {
_ , err = io . Copy ( stdout , br )
} else {
_ , err = utils . StdCopy ( stdout , stderr , br )
}
2013-08-14 00:30:08 +04:00
utils . Debugf ( "[hijack] End of stdout" )
return err
} )
}
2013-05-06 13:31:22 +04:00
2013-05-15 02:37:35 +04:00
sendStdin := utils . Go ( func ( ) error {
2013-06-25 01:59:37 +04:00
if in != nil {
io . Copy ( rwc , in )
2013-07-24 04:27:49 +04:00
utils . Debugf ( "[hijack] End of stdin" )
2013-06-25 01:59:37 +04:00
}
2013-06-27 16:57:19 +04:00
if tcpc , ok := rwc . ( * net . TCPConn ) ; ok {
if err := tcpc . CloseWrite ( ) ; err != nil {
2013-10-08 11:54:47 +04:00
utils . Errorf ( "Couldn't send EOF: %s\n" , err )
2013-06-27 16:57:19 +04:00
}
} else if unixc , ok := rwc . ( * net . UnixConn ) ; ok {
if err := unixc . CloseWrite ( ) ; err != nil {
2013-10-08 11:54:47 +04:00
utils . Errorf ( "Couldn't send EOF: %s\n" , err )
2013-06-27 16:57:19 +04:00
}
2013-05-08 00:15:21 +04:00
}
2013-06-13 23:57:35 +04:00
// Discard errors due to pipe interruption
return nil
2013-04-23 20:20:53 +04:00
} )
2013-12-05 05:08:14 +04:00
if stdout != nil || stderr != nil {
2013-08-14 00:30:08 +04:00
if err := <- receiveStdout ; err != nil {
2013-10-08 11:54:47 +04:00
utils . Errorf ( "Error receiveStdout: %s" , err )
2013-08-14 00:30:08 +04:00
return err
}
2013-02-14 05:10:00 +04:00
}
2013-05-10 09:28:52 +04:00
2013-06-25 01:59:37 +04:00
if ! cli . isTerminal {
2013-04-23 20:20:53 +04:00
if err := <- sendStdin ; err != nil {
2013-10-08 11:54:47 +04:00
utils . Errorf ( "Error sendStdin: %s" , err )
2013-04-23 20:20:53 +04:00
return err
}
}
return nil
2013-02-14 05:10:00 +04:00
}
2013-07-08 21:20:13 +04:00
func ( cli * DockerCli ) getTtySize ( ) ( int , int ) {
2013-06-25 01:59:37 +04:00
if ! cli . isTerminal {
2013-07-08 21:20:13 +04:00
return 0 , 0
2013-06-25 01:59:37 +04:00
}
ws , err := term . GetWinsize ( cli . terminalFd )
2013-05-25 01:44:16 +04:00
if err != nil {
2013-10-08 11:54:47 +04:00
utils . Errorf ( "Error getting size: %s" , err )
2013-07-08 22:19:12 +04:00
if ws == nil {
return 0 , 0
}
2013-05-25 01:44:16 +04:00
}
2013-07-08 21:20:13 +04:00
return int ( ws . Height ) , int ( ws . Width )
}
func ( cli * DockerCli ) resizeTty ( id string ) {
height , width := cli . getTtySize ( )
if height == 0 && width == 0 {
return
2013-05-25 01:44:16 +04:00
}
v := url . Values { }
2013-07-08 21:20:13 +04:00
v . Set ( "h" , strconv . Itoa ( height ) )
v . Set ( "w" , strconv . Itoa ( width ) )
2014-01-21 08:06:19 +04:00
if _ , _ , err := readBody ( cli . call ( "POST" , "/containers/" + id + "/resize?" + v . Encode ( ) , nil , false ) ) ; err != nil {
2013-10-08 11:54:47 +04:00
utils . Errorf ( "Error resize: %s" , err )
2013-05-25 01:44:16 +04:00
}
}
2013-06-25 01:59:37 +04:00
func ( cli * DockerCli ) monitorTtySize ( id string ) error {
2013-05-25 01:44:16 +04:00
cli . resizeTty ( id )
2013-07-29 21:28:41 +04:00
sigchan := make ( chan os . Signal , 1 )
signal . Notify ( sigchan , syscall . SIGWINCH )
2013-05-25 01:44:16 +04:00
go func ( ) {
2013-08-10 04:42:20 +04:00
for _ = range sigchan {
2013-07-29 22:13:59 +04:00
cli . resizeTty ( id )
2013-05-25 01:44:16 +04:00
}
} ( )
2013-06-25 01:59:37 +04:00
return nil
2013-05-25 01:44:16 +04:00
}
2013-11-21 05:41:25 +04:00
func ( cli * DockerCli ) Subcmd ( name , signature , description string ) * flag . FlagSet {
2013-04-22 20:17:47 +04:00
flags := flag . NewFlagSet ( name , flag . ContinueOnError )
flags . Usage = func ( ) {
2013-11-21 05:41:25 +04:00
fmt . Fprintf ( cli . err , "\nUsage: docker %s %s\n\n%s\n\n" , name , signature , description )
2013-04-22 20:17:47 +04:00
flags . PrintDefaults ( )
2013-11-21 05:41:25 +04:00
os . Exit ( 2 )
2013-04-22 20:17:47 +04:00
}
return flags
2013-02-14 05:10:00 +04:00
}
2013-05-13 13:48:27 +04:00
2013-08-16 18:29:40 +04:00
func ( cli * DockerCli ) LoadConfigFile ( ) ( err error ) {
cli . configFile , err = auth . LoadConfig ( os . Getenv ( "HOME" ) )
if err != nil {
fmt . Fprintf ( cli . err , "WARNING: %s\n" , err )
}
return err
}
2013-09-06 03:54:03 +04:00
func waitForExit ( cli * DockerCli , containerId string ) ( int , error ) {
2014-01-25 11:15:40 +04:00
stream , _ , err := cli . call ( "POST" , "/containers/" + containerId + "/wait" , nil , false )
2013-09-06 03:54:03 +04:00
if err != nil {
2014-01-06 23:11:04 +04:00
return - 1 , err
2013-09-06 03:54:03 +04:00
}
2014-01-25 11:15:40 +04:00
var out engine . Env
if err := out . Decode ( stream ) ; err != nil {
2013-09-06 03:54:03 +04:00
return - 1 , err
}
2014-01-25 11:15:40 +04:00
return out . GetInt ( "StatusCode" ) , nil
2013-09-06 03:54:03 +04:00
}
2013-10-10 01:38:52 +04:00
// getExitCode perform an inspect on the container. It returns
// the running state and the exit code.
func getExitCode ( cli * DockerCli , containerId string ) ( bool , int , error ) {
2014-01-21 08:06:19 +04:00
body , _ , err := readBody ( cli . call ( "GET" , "/containers/" + containerId + "/json" , nil , false ) )
2013-09-20 22:31:00 +04:00
if err != nil {
// If we can't connect, then the daemon probably died.
if err != ErrConnectionRefused {
2013-10-10 01:38:52 +04:00
return false , - 1 , err
2013-09-20 22:31:00 +04:00
}
2013-10-10 01:38:52 +04:00
return false , - 1 , nil
2013-09-20 22:31:00 +04:00
}
c := & Container { }
if err := json . Unmarshal ( body , c ) ; err != nil {
2013-10-10 01:38:52 +04:00
return false , - 1 , err
2013-09-20 22:31:00 +04:00
}
2013-11-22 00:21:03 +04:00
return c . State . IsRunning ( ) , c . State . GetExitCode ( ) , nil
2013-09-20 22:31:00 +04:00
}
2014-01-16 23:02:51 +04:00
func readBody ( stream io . ReadCloser , statusCode int , err error ) ( [ ] byte , int , error ) {
if stream != nil {
defer stream . Close ( )
}
if err != nil {
return nil , statusCode , err
}
body , err := ioutil . ReadAll ( stream )
if err != nil {
return nil , - 1 , err
}
return body , statusCode , nil
}
2013-06-25 01:15:52 +04:00
func NewDockerCli ( in io . ReadCloser , out , err io . Writer , proto , addr string ) * DockerCli {
2013-06-25 01:59:37 +04:00
var (
2013-07-03 02:27:22 +04:00
isTerminal = false
2013-06-25 01:59:37 +04:00
terminalFd uintptr
)
if in != nil {
if file , ok := in . ( * os . File ) ; ok {
terminalFd = file . Fd ( )
isTerminal = term . IsTerminal ( terminalFd )
}
}
if err == nil {
err = out
}
2013-06-25 01:15:52 +04:00
return & DockerCli {
proto : proto ,
addr : addr ,
in : in ,
out : out ,
err : err ,
2013-06-25 01:59:37 +04:00
isTerminal : isTerminal ,
terminalFd : terminalFd ,
2013-06-25 01:15:52 +04:00
}
2013-05-13 13:48:27 +04:00
}
2013-05-15 18:16:46 +04:00
type DockerCli struct {
2013-06-18 22:59:56 +04:00
proto string
addr string
2013-07-23 19:04:31 +04:00
configFile * auth . ConfigFile
2013-06-25 01:15:52 +04:00
in io . ReadCloser
out io . Writer
err io . Writer
2013-06-25 01:59:37 +04:00
isTerminal bool
terminalFd uintptr
2013-05-13 13:48:27 +04:00
}