commands.go 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763
  1. package client
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/base64"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. "os/exec"
  15. "path"
  16. "path/filepath"
  17. "runtime"
  18. "sort"
  19. "strconv"
  20. "strings"
  21. "sync"
  22. "text/tabwriter"
  23. "text/template"
  24. "time"
  25. log "github.com/Sirupsen/logrus"
  26. "github.com/docker/docker/api"
  27. "github.com/docker/docker/api/stats"
  28. "github.com/docker/docker/dockerversion"
  29. "github.com/docker/docker/engine"
  30. "github.com/docker/docker/graph"
  31. "github.com/docker/docker/nat"
  32. "github.com/docker/docker/opts"
  33. "github.com/docker/docker/pkg/archive"
  34. "github.com/docker/docker/pkg/fileutils"
  35. flag "github.com/docker/docker/pkg/mflag"
  36. "github.com/docker/docker/pkg/parsers"
  37. "github.com/docker/docker/pkg/parsers/filters"
  38. "github.com/docker/docker/pkg/promise"
  39. "github.com/docker/docker/pkg/signal"
  40. "github.com/docker/docker/pkg/term"
  41. "github.com/docker/docker/pkg/timeutils"
  42. "github.com/docker/docker/pkg/units"
  43. "github.com/docker/docker/pkg/urlutil"
  44. "github.com/docker/docker/registry"
  45. "github.com/docker/docker/runconfig"
  46. "github.com/docker/docker/utils"
  47. )
  48. const (
  49. tarHeaderSize = 512
  50. )
  51. func (cli *DockerCli) CmdHelp(args ...string) error {
  52. if len(args) > 1 {
  53. method, exists := cli.getMethod(args[:2]...)
  54. if exists {
  55. method("--help")
  56. return nil
  57. }
  58. }
  59. if len(args) > 0 {
  60. method, exists := cli.getMethod(args[0])
  61. if !exists {
  62. fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0])
  63. os.Exit(1)
  64. } else {
  65. method("--help")
  66. return nil
  67. }
  68. }
  69. flag.Usage()
  70. return nil
  71. }
  72. func (cli *DockerCli) CmdBuild(args ...string) error {
  73. cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH", true)
  74. tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success")
  75. suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
  76. noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
  77. rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
  78. forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds")
  79. pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
  80. dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile(Default is 'Dockerfile' at context root)")
  81. cmd.Require(flag.Exact, 1)
  82. utils.ParseFlags(cmd, args, true)
  83. var (
  84. context archive.Archive
  85. isRemote bool
  86. err error
  87. )
  88. _, err = exec.LookPath("git")
  89. hasGit := err == nil
  90. if cmd.Arg(0) == "-" {
  91. // As a special case, 'docker build -' will build from either an empty context with the
  92. // contents of stdin as a Dockerfile, or a tar-ed context from stdin.
  93. buf := bufio.NewReader(cli.in)
  94. magic, err := buf.Peek(tarHeaderSize)
  95. if err != nil && err != io.EOF {
  96. return fmt.Errorf("failed to peek context header from STDIN: %v", err)
  97. }
  98. if !archive.IsArchive(magic) {
  99. dockerfile, err := ioutil.ReadAll(buf)
  100. if err != nil {
  101. return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
  102. }
  103. if *dockerfileName == "" {
  104. *dockerfileName = api.DefaultDockerfileName
  105. }
  106. context, err = archive.Generate(*dockerfileName, string(dockerfile))
  107. } else {
  108. context = ioutil.NopCloser(buf)
  109. }
  110. } else if urlutil.IsURL(cmd.Arg(0)) && (!urlutil.IsGitURL(cmd.Arg(0)) || !hasGit) {
  111. isRemote = true
  112. } else {
  113. root := cmd.Arg(0)
  114. if urlutil.IsGitURL(root) {
  115. remoteURL := cmd.Arg(0)
  116. if !urlutil.IsGitTransport(remoteURL) {
  117. remoteURL = "https://" + remoteURL
  118. }
  119. root, err = ioutil.TempDir("", "docker-build-git")
  120. if err != nil {
  121. return err
  122. }
  123. defer os.RemoveAll(root)
  124. if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
  125. return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
  126. }
  127. }
  128. if _, err := os.Stat(root); err != nil {
  129. return err
  130. }
  131. absRoot, err := filepath.Abs(root)
  132. if err != nil {
  133. return err
  134. }
  135. var filename string // path to Dockerfile
  136. var origDockerfile string // used for error msg
  137. if *dockerfileName == "" {
  138. // No -f/--file was specified so use the default
  139. origDockerfile = api.DefaultDockerfileName
  140. *dockerfileName = origDockerfile
  141. filename = path.Join(absRoot, *dockerfileName)
  142. } else {
  143. origDockerfile = *dockerfileName
  144. if filename, err = filepath.Abs(*dockerfileName); err != nil {
  145. return err
  146. }
  147. // Verify that 'filename' is within the build context
  148. if !strings.HasSuffix(absRoot, string(os.PathSeparator)) {
  149. absRoot += string(os.PathSeparator)
  150. }
  151. if !strings.HasPrefix(filename, absRoot) {
  152. return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", *dockerfileName, root)
  153. }
  154. // Now reset the dockerfileName to be relative to the build context
  155. *dockerfileName = filename[len(absRoot):]
  156. }
  157. if _, err = os.Stat(filename); os.IsNotExist(err) {
  158. return fmt.Errorf("Can not locate Dockerfile: %s", origDockerfile)
  159. }
  160. var includes []string = []string{"."}
  161. excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore"))
  162. if err != nil {
  163. return err
  164. }
  165. // If .dockerignore mentions .dockerignore or the Dockerfile
  166. // then make sure we send both files over to the daemon
  167. // because Dockerfile is, obviously, needed no matter what, and
  168. // .dockerignore is needed to know if either one needs to be
  169. // removed. The deamon will remove them for us, if needed, after it
  170. // parses the Dockerfile.
  171. keepThem1, _ := fileutils.Matches(".dockerignore", excludes)
  172. keepThem2, _ := fileutils.Matches(*dockerfileName, excludes)
  173. if keepThem1 || keepThem2 {
  174. includes = append(includes, ".dockerignore", *dockerfileName)
  175. }
  176. if err = utils.ValidateContextDirectory(root, excludes); err != nil {
  177. return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
  178. }
  179. options := &archive.TarOptions{
  180. Compression: archive.Uncompressed,
  181. ExcludePatterns: excludes,
  182. IncludeFiles: includes,
  183. }
  184. context, err = archive.TarWithOptions(root, options)
  185. if err != nil {
  186. return err
  187. }
  188. }
  189. var body io.Reader
  190. // Setup an upload progress bar
  191. // FIXME: ProgressReader shouldn't be this annoying to use
  192. if context != nil {
  193. sf := utils.NewStreamFormatter(false)
  194. body = utils.ProgressReader(context, 0, cli.out, sf, true, "", "Sending build context to Docker daemon")
  195. }
  196. // Send the build context
  197. v := &url.Values{}
  198. //Check if the given image name can be resolved
  199. if *tag != "" {
  200. repository, tag := parsers.ParseRepositoryTag(*tag)
  201. if err := registry.ValidateRepositoryName(repository); err != nil {
  202. return err
  203. }
  204. if len(tag) > 0 {
  205. if err := graph.ValidateTagName(tag); err != nil {
  206. return err
  207. }
  208. }
  209. }
  210. v.Set("t", *tag)
  211. if *suppressOutput {
  212. v.Set("q", "1")
  213. }
  214. if isRemote {
  215. v.Set("remote", cmd.Arg(0))
  216. }
  217. if *noCache {
  218. v.Set("nocache", "1")
  219. }
  220. if *rm {
  221. v.Set("rm", "1")
  222. } else {
  223. v.Set("rm", "0")
  224. }
  225. if *forceRm {
  226. v.Set("forcerm", "1")
  227. }
  228. if *pull {
  229. v.Set("pull", "1")
  230. }
  231. v.Set("dockerfile", *dockerfileName)
  232. cli.LoadConfigFile()
  233. headers := http.Header(make(map[string][]string))
  234. buf, err := json.Marshal(cli.configFile)
  235. if err != nil {
  236. return err
  237. }
  238. headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
  239. if context != nil {
  240. headers.Set("Content-Type", "application/tar")
  241. }
  242. err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
  243. if jerr, ok := err.(*utils.JSONError); ok {
  244. // If no error code is set, default to 1
  245. if jerr.Code == 0 {
  246. jerr.Code = 1
  247. }
  248. return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
  249. }
  250. return err
  251. }
  252. // 'docker login': login / register a user to registry service.
  253. func (cli *DockerCli) CmdLogin(args ...string) error {
  254. cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true)
  255. cmd.Require(flag.Max, 1)
  256. var username, password, email string
  257. cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
  258. cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
  259. cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
  260. utils.ParseFlags(cmd, args, true)
  261. serverAddress := registry.IndexServerAddress()
  262. if len(cmd.Args()) > 0 {
  263. serverAddress = cmd.Arg(0)
  264. }
  265. promptDefault := func(prompt string, configDefault string) {
  266. if configDefault == "" {
  267. fmt.Fprintf(cli.out, "%s: ", prompt)
  268. } else {
  269. fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
  270. }
  271. }
  272. readInput := func(in io.Reader, out io.Writer) string {
  273. reader := bufio.NewReader(in)
  274. line, _, err := reader.ReadLine()
  275. if err != nil {
  276. fmt.Fprintln(out, err.Error())
  277. os.Exit(1)
  278. }
  279. return string(line)
  280. }
  281. cli.LoadConfigFile()
  282. authconfig, ok := cli.configFile.Configs[serverAddress]
  283. if !ok {
  284. authconfig = registry.AuthConfig{}
  285. }
  286. if username == "" {
  287. promptDefault("Username", authconfig.Username)
  288. username = readInput(cli.in, cli.out)
  289. if username == "" {
  290. username = authconfig.Username
  291. }
  292. }
  293. // Assume that a different username means they may not want to use
  294. // the password or email from the config file, so prompt them
  295. if username != authconfig.Username {
  296. if password == "" {
  297. oldState, err := term.SaveState(cli.inFd)
  298. if err != nil {
  299. return err
  300. }
  301. fmt.Fprintf(cli.out, "Password: ")
  302. term.DisableEcho(cli.inFd, oldState)
  303. password = readInput(cli.in, cli.out)
  304. fmt.Fprint(cli.out, "\n")
  305. term.RestoreTerminal(cli.inFd, oldState)
  306. if password == "" {
  307. return fmt.Errorf("Error : Password Required")
  308. }
  309. }
  310. if email == "" {
  311. promptDefault("Email", authconfig.Email)
  312. email = readInput(cli.in, cli.out)
  313. if email == "" {
  314. email = authconfig.Email
  315. }
  316. }
  317. } else {
  318. // However, if they don't override the username use the
  319. // password or email from the cmd line if specified. IOW, allow
  320. // then to change/overide them. And if not specified, just
  321. // use what's in the config file
  322. if password == "" {
  323. password = authconfig.Password
  324. }
  325. if email == "" {
  326. email = authconfig.Email
  327. }
  328. }
  329. authconfig.Username = username
  330. authconfig.Password = password
  331. authconfig.Email = email
  332. authconfig.ServerAddress = serverAddress
  333. cli.configFile.Configs[serverAddress] = authconfig
  334. stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
  335. if statusCode == 401 {
  336. delete(cli.configFile.Configs, serverAddress)
  337. registry.SaveConfig(cli.configFile)
  338. return err
  339. }
  340. if err != nil {
  341. return err
  342. }
  343. var out2 engine.Env
  344. err = out2.Decode(stream)
  345. if err != nil {
  346. cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME"))
  347. return err
  348. }
  349. registry.SaveConfig(cli.configFile)
  350. if out2.Get("Status") != "" {
  351. fmt.Fprintf(cli.out, "%s\n", out2.Get("Status"))
  352. }
  353. return nil
  354. }
  355. // log out from a Docker registry
  356. func (cli *DockerCli) CmdLogout(args ...string) error {
  357. cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true)
  358. cmd.Require(flag.Max, 1)
  359. utils.ParseFlags(cmd, args, false)
  360. serverAddress := registry.IndexServerAddress()
  361. if len(cmd.Args()) > 0 {
  362. serverAddress = cmd.Arg(0)
  363. }
  364. cli.LoadConfigFile()
  365. if _, ok := cli.configFile.Configs[serverAddress]; !ok {
  366. fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
  367. } else {
  368. fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
  369. delete(cli.configFile.Configs, serverAddress)
  370. if err := registry.SaveConfig(cli.configFile); err != nil {
  371. return fmt.Errorf("Failed to save docker config: %v", err)
  372. }
  373. }
  374. return nil
  375. }
  376. // 'docker wait': block until a container stops
  377. func (cli *DockerCli) CmdWait(args ...string) error {
  378. cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.", true)
  379. cmd.Require(flag.Min, 1)
  380. utils.ParseFlags(cmd, args, true)
  381. var encounteredError error
  382. for _, name := range cmd.Args() {
  383. status, err := waitForExit(cli, name)
  384. if err != nil {
  385. fmt.Fprintf(cli.err, "%s\n", err)
  386. encounteredError = fmt.Errorf("Error: failed to wait one or more containers")
  387. } else {
  388. fmt.Fprintf(cli.out, "%d\n", status)
  389. }
  390. }
  391. return encounteredError
  392. }
  393. // 'docker version': show version information
  394. func (cli *DockerCli) CmdVersion(args ...string) error {
  395. cmd := cli.Subcmd("version", "", "Show the Docker version information.", true)
  396. cmd.Require(flag.Exact, 0)
  397. utils.ParseFlags(cmd, args, false)
  398. if dockerversion.VERSION != "" {
  399. fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
  400. }
  401. fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION)
  402. fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
  403. if dockerversion.GITCOMMIT != "" {
  404. fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
  405. }
  406. fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
  407. body, _, err := readBody(cli.call("GET", "/version", nil, false))
  408. if err != nil {
  409. return err
  410. }
  411. out := engine.NewOutput()
  412. remoteVersion, err := out.AddEnv()
  413. if err != nil {
  414. log.Errorf("Error reading remote version: %s", err)
  415. return err
  416. }
  417. if _, err := out.Write(body); err != nil {
  418. log.Errorf("Error reading remote version: %s", err)
  419. return err
  420. }
  421. out.Close()
  422. fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
  423. if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" {
  424. fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion)
  425. }
  426. fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
  427. fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
  428. return nil
  429. }
  430. // 'docker info': display system-wide information.
  431. func (cli *DockerCli) CmdInfo(args ...string) error {
  432. cmd := cli.Subcmd("info", "", "Display system-wide information", true)
  433. cmd.Require(flag.Exact, 0)
  434. utils.ParseFlags(cmd, args, false)
  435. body, _, err := readBody(cli.call("GET", "/info", nil, false))
  436. if err != nil {
  437. return err
  438. }
  439. out := engine.NewOutput()
  440. remoteInfo, err := out.AddEnv()
  441. if err != nil {
  442. return err
  443. }
  444. if _, err := out.Write(body); err != nil {
  445. log.Errorf("Error reading remote info: %s", err)
  446. return err
  447. }
  448. out.Close()
  449. if remoteInfo.Exists("Containers") {
  450. fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
  451. }
  452. if remoteInfo.Exists("Images") {
  453. fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
  454. }
  455. if remoteInfo.Exists("Driver") {
  456. fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver"))
  457. }
  458. if remoteInfo.Exists("DriverStatus") {
  459. var driverStatus [][2]string
  460. if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
  461. return err
  462. }
  463. for _, pair := range driverStatus {
  464. fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
  465. }
  466. }
  467. if remoteInfo.Exists("ExecutionDriver") {
  468. fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
  469. }
  470. if remoteInfo.Exists("KernelVersion") {
  471. fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
  472. }
  473. if remoteInfo.Exists("OperatingSystem") {
  474. fmt.Fprintf(cli.out, "Operating System: %s\n", remoteInfo.Get("OperatingSystem"))
  475. }
  476. if remoteInfo.Exists("NCPU") {
  477. fmt.Fprintf(cli.out, "CPUs: %d\n", remoteInfo.GetInt("NCPU"))
  478. }
  479. if remoteInfo.Exists("MemTotal") {
  480. fmt.Fprintf(cli.out, "Total Memory: %s\n", units.BytesSize(float64(remoteInfo.GetInt64("MemTotal"))))
  481. }
  482. if remoteInfo.Exists("Name") {
  483. fmt.Fprintf(cli.out, "Name: %s\n", remoteInfo.Get("Name"))
  484. }
  485. if remoteInfo.Exists("ID") {
  486. fmt.Fprintf(cli.out, "ID: %s\n", remoteInfo.Get("ID"))
  487. }
  488. if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
  489. if remoteInfo.Exists("Debug") {
  490. fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
  491. }
  492. fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
  493. if remoteInfo.Exists("NFd") {
  494. fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
  495. }
  496. if remoteInfo.Exists("NGoroutines") {
  497. fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
  498. }
  499. if remoteInfo.Exists("NEventsListener") {
  500. fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
  501. }
  502. if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
  503. fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
  504. }
  505. if initPath := remoteInfo.Get("InitPath"); initPath != "" {
  506. fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
  507. }
  508. if root := remoteInfo.Get("DockerRootDir"); root != "" {
  509. fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", root)
  510. }
  511. }
  512. if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
  513. cli.LoadConfigFile()
  514. u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username
  515. if len(u) > 0 {
  516. fmt.Fprintf(cli.out, "Username: %v\n", u)
  517. fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress"))
  518. }
  519. }
  520. if remoteInfo.Exists("MemoryLimit") && !remoteInfo.GetBool("MemoryLimit") {
  521. fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
  522. }
  523. if remoteInfo.Exists("SwapLimit") && !remoteInfo.GetBool("SwapLimit") {
  524. fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
  525. }
  526. if remoteInfo.Exists("IPv4Forwarding") && !remoteInfo.GetBool("IPv4Forwarding") {
  527. fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
  528. }
  529. if remoteInfo.Exists("Labels") {
  530. fmt.Fprintln(cli.out, "Labels:")
  531. for _, attribute := range remoteInfo.GetList("Labels") {
  532. fmt.Fprintf(cli.out, " %s\n", attribute)
  533. }
  534. }
  535. return nil
  536. }
  537. func (cli *DockerCli) CmdStop(args ...string) error {
  538. cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period", true)
  539. nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.")
  540. cmd.Require(flag.Min, 1)
  541. utils.ParseFlags(cmd, args, true)
  542. v := url.Values{}
  543. v.Set("t", strconv.Itoa(*nSeconds))
  544. var encounteredError error
  545. for _, name := range cmd.Args() {
  546. _, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
  547. if err != nil {
  548. fmt.Fprintf(cli.err, "%s\n", err)
  549. encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
  550. } else {
  551. fmt.Fprintf(cli.out, "%s\n", name)
  552. }
  553. }
  554. return encounteredError
  555. }
  556. func (cli *DockerCli) CmdRestart(args ...string) error {
  557. cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true)
  558. 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 is 10 seconds.")
  559. cmd.Require(flag.Min, 1)
  560. utils.ParseFlags(cmd, args, true)
  561. v := url.Values{}
  562. v.Set("t", strconv.Itoa(*nSeconds))
  563. var encounteredError error
  564. for _, name := range cmd.Args() {
  565. _, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
  566. if err != nil {
  567. fmt.Fprintf(cli.err, "%s\n", err)
  568. encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
  569. } else {
  570. fmt.Fprintf(cli.out, "%s\n", name)
  571. }
  572. }
  573. return encounteredError
  574. }
  575. func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
  576. sigc := make(chan os.Signal, 128)
  577. signal.CatchAll(sigc)
  578. go func() {
  579. for s := range sigc {
  580. if s == signal.SIGCHLD {
  581. continue
  582. }
  583. var sig string
  584. for sigStr, sigN := range signal.SignalMap {
  585. if sigN == s {
  586. sig = sigStr
  587. break
  588. }
  589. }
  590. if sig == "" {
  591. log.Errorf("Unsupported signal: %v. Discarding.", s)
  592. }
  593. if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
  594. log.Debugf("Error sending signal: %s", err)
  595. }
  596. }
  597. }()
  598. return sigc
  599. }
  600. func (cli *DockerCli) CmdStart(args ...string) error {
  601. var (
  602. cErr chan error
  603. tty bool
  604. cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container", true)
  605. attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's STDOUT and STDERR and forward all signals to the process")
  606. openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
  607. )
  608. cmd.Require(flag.Min, 1)
  609. utils.ParseFlags(cmd, args, true)
  610. hijacked := make(chan io.Closer)
  611. if *attach || *openStdin {
  612. if cmd.NArg() > 1 {
  613. return fmt.Errorf("You cannot start and attach multiple containers at once.")
  614. }
  615. stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
  616. if err != nil {
  617. return err
  618. }
  619. env := engine.Env{}
  620. if err := env.Decode(stream); err != nil {
  621. return err
  622. }
  623. config := env.GetSubEnv("Config")
  624. tty = config.GetBool("Tty")
  625. if !tty {
  626. sigc := cli.forwardAllSignals(cmd.Arg(0))
  627. defer signal.StopCatch(sigc)
  628. }
  629. var in io.ReadCloser
  630. v := url.Values{}
  631. v.Set("stream", "1")
  632. if *openStdin && config.GetBool("OpenStdin") {
  633. v.Set("stdin", "1")
  634. in = cli.in
  635. }
  636. v.Set("stdout", "1")
  637. v.Set("stderr", "1")
  638. cErr = promise.Go(func() error {
  639. return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil)
  640. })
  641. } else {
  642. close(hijacked)
  643. }
  644. // Acknowledge the hijack before starting
  645. select {
  646. case closer := <-hijacked:
  647. // Make sure that the hijack gets closed when returning (results
  648. // in closing the hijack chan and freeing server's goroutines)
  649. if closer != nil {
  650. defer closer.Close()
  651. }
  652. case err := <-cErr:
  653. if err != nil {
  654. return err
  655. }
  656. }
  657. var encounteredError error
  658. for _, name := range cmd.Args() {
  659. _, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
  660. if err != nil {
  661. if !*attach && !*openStdin {
  662. fmt.Fprintf(cli.err, "%s\n", err)
  663. }
  664. encounteredError = fmt.Errorf("Error: failed to start one or more containers")
  665. } else {
  666. if !*attach && !*openStdin {
  667. fmt.Fprintf(cli.out, "%s\n", name)
  668. }
  669. }
  670. }
  671. if encounteredError != nil {
  672. if *openStdin || *attach {
  673. cli.in.Close()
  674. }
  675. return encounteredError
  676. }
  677. if *openStdin || *attach {
  678. if tty && cli.isTerminalOut {
  679. if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
  680. log.Errorf("Error monitoring TTY size: %s", err)
  681. }
  682. }
  683. if attchErr := <-cErr; attchErr != nil {
  684. return attchErr
  685. }
  686. _, status, err := getExitCode(cli, cmd.Arg(0))
  687. if err != nil {
  688. return err
  689. }
  690. if status != 0 {
  691. return &utils.StatusError{StatusCode: status}
  692. }
  693. }
  694. return nil
  695. }
  696. func (cli *DockerCli) CmdUnpause(args ...string) error {
  697. cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container", true)
  698. cmd.Require(flag.Exact, 1)
  699. utils.ParseFlags(cmd, args, false)
  700. var encounteredError error
  701. for _, name := range cmd.Args() {
  702. if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
  703. fmt.Fprintf(cli.err, "%s\n", err)
  704. encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
  705. } else {
  706. fmt.Fprintf(cli.out, "%s\n", name)
  707. }
  708. }
  709. return encounteredError
  710. }
  711. func (cli *DockerCli) CmdPause(args ...string) error {
  712. cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container", true)
  713. cmd.Require(flag.Exact, 1)
  714. utils.ParseFlags(cmd, args, false)
  715. var encounteredError error
  716. for _, name := range cmd.Args() {
  717. if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
  718. fmt.Fprintf(cli.err, "%s\n", err)
  719. encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
  720. } else {
  721. fmt.Fprintf(cli.out, "%s\n", name)
  722. }
  723. }
  724. return encounteredError
  725. }
  726. func (cli *DockerCli) CmdRename(args ...string) error {
  727. cmd := cli.Subcmd("rename", "OLD_NAME NEW_NAME", "Rename a container", true)
  728. if err := cmd.Parse(args); err != nil {
  729. return nil
  730. }
  731. if cmd.NArg() != 2 {
  732. cmd.Usage()
  733. return nil
  734. }
  735. old_name := cmd.Arg(0)
  736. new_name := cmd.Arg(1)
  737. if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, false)); err != nil {
  738. fmt.Fprintf(cli.err, "%s\n", err)
  739. return fmt.Errorf("Error: failed to rename container named %s", old_name)
  740. }
  741. return nil
  742. }
  743. func (cli *DockerCli) CmdInspect(args ...string) error {
  744. cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true)
  745. tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
  746. cmd.Require(flag.Min, 1)
  747. utils.ParseFlags(cmd, args, true)
  748. var tmpl *template.Template
  749. if *tmplStr != "" {
  750. var err error
  751. if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
  752. fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
  753. return &utils.StatusError{StatusCode: 64,
  754. Status: "Template parsing error: " + err.Error()}
  755. }
  756. }
  757. indented := new(bytes.Buffer)
  758. indented.WriteByte('[')
  759. status := 0
  760. for _, name := range cmd.Args() {
  761. obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
  762. if err != nil {
  763. obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
  764. if err != nil {
  765. if strings.Contains(err.Error(), "No such") {
  766. fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
  767. } else {
  768. fmt.Fprintf(cli.err, "%s", err)
  769. }
  770. status = 1
  771. continue
  772. }
  773. }
  774. if tmpl == nil {
  775. if err = json.Indent(indented, obj, "", " "); err != nil {
  776. fmt.Fprintf(cli.err, "%s\n", err)
  777. status = 1
  778. continue
  779. }
  780. } else {
  781. // Has template, will render
  782. var value interface{}
  783. if err := json.Unmarshal(obj, &value); err != nil {
  784. fmt.Fprintf(cli.err, "%s\n", err)
  785. status = 1
  786. continue
  787. }
  788. if err := tmpl.Execute(cli.out, value); err != nil {
  789. return err
  790. }
  791. cli.out.Write([]byte{'\n'})
  792. }
  793. indented.WriteString(",")
  794. }
  795. if indented.Len() > 1 {
  796. // Remove trailing ','
  797. indented.Truncate(indented.Len() - 1)
  798. }
  799. indented.WriteString("]\n")
  800. if tmpl == nil {
  801. if _, err := io.Copy(cli.out, indented); err != nil {
  802. return err
  803. }
  804. }
  805. if status != 0 {
  806. return &utils.StatusError{StatusCode: status}
  807. }
  808. return nil
  809. }
  810. func (cli *DockerCli) CmdTop(args ...string) error {
  811. cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container", true)
  812. cmd.Require(flag.Min, 1)
  813. utils.ParseFlags(cmd, args, true)
  814. val := url.Values{}
  815. if cmd.NArg() > 1 {
  816. val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
  817. }
  818. stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
  819. if err != nil {
  820. return err
  821. }
  822. var procs engine.Env
  823. if err := procs.Decode(stream); err != nil {
  824. return err
  825. }
  826. w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
  827. fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t"))
  828. processes := [][]string{}
  829. if err := procs.GetJson("Processes", &processes); err != nil {
  830. return err
  831. }
  832. for _, proc := range processes {
  833. fmt.Fprintln(w, strings.Join(proc, "\t"))
  834. }
  835. w.Flush()
  836. return nil
  837. }
  838. func (cli *DockerCli) CmdPort(args ...string) error {
  839. cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT", true)
  840. cmd.Require(flag.Min, 1)
  841. utils.ParseFlags(cmd, args, true)
  842. stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
  843. if err != nil {
  844. return err
  845. }
  846. env := engine.Env{}
  847. if err := env.Decode(stream); err != nil {
  848. return err
  849. }
  850. ports := nat.PortMap{}
  851. if err := env.GetSubEnv("NetworkSettings").GetJson("Ports", &ports); err != nil {
  852. return err
  853. }
  854. if cmd.NArg() == 2 {
  855. var (
  856. port = cmd.Arg(1)
  857. proto = "tcp"
  858. parts = strings.SplitN(port, "/", 2)
  859. )
  860. if len(parts) == 2 && len(parts[1]) != 0 {
  861. port = parts[0]
  862. proto = parts[1]
  863. }
  864. natPort := port + "/" + proto
  865. if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
  866. for _, frontend := range frontends {
  867. fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
  868. }
  869. return nil
  870. }
  871. return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0))
  872. }
  873. for from, frontends := range ports {
  874. for _, frontend := range frontends {
  875. fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort)
  876. }
  877. }
  878. return nil
  879. }
  880. // 'docker rmi IMAGE' removes all images with the name IMAGE
  881. func (cli *DockerCli) CmdRmi(args ...string) error {
  882. var (
  883. cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images", true)
  884. force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
  885. noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
  886. )
  887. cmd.Require(flag.Min, 1)
  888. utils.ParseFlags(cmd, args, true)
  889. v := url.Values{}
  890. if *force {
  891. v.Set("force", "1")
  892. }
  893. if *noprune {
  894. v.Set("noprune", "1")
  895. }
  896. var encounteredError error
  897. for _, name := range cmd.Args() {
  898. body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
  899. if err != nil {
  900. fmt.Fprintf(cli.err, "%s\n", err)
  901. encounteredError = fmt.Errorf("Error: failed to remove one or more images")
  902. } else {
  903. outs := engine.NewTable("Created", 0)
  904. if _, err := outs.ReadListFrom(body); err != nil {
  905. fmt.Fprintf(cli.err, "%s\n", err)
  906. encounteredError = fmt.Errorf("Error: failed to remove one or more images")
  907. continue
  908. }
  909. for _, out := range outs.Data {
  910. if out.Get("Deleted") != "" {
  911. fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
  912. } else {
  913. fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
  914. }
  915. }
  916. }
  917. }
  918. return encounteredError
  919. }
  920. func (cli *DockerCli) CmdHistory(args ...string) error {
  921. cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true)
  922. quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
  923. noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
  924. cmd.Require(flag.Exact, 1)
  925. utils.ParseFlags(cmd, args, true)
  926. body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
  927. if err != nil {
  928. return err
  929. }
  930. outs := engine.NewTable("Created", 0)
  931. if _, err := outs.ReadListFrom(body); err != nil {
  932. return err
  933. }
  934. w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
  935. if !*quiet {
  936. fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE")
  937. }
  938. for _, out := range outs.Data {
  939. outID := out.Get("Id")
  940. if !*quiet {
  941. if *noTrunc {
  942. fmt.Fprintf(w, "%s\t", outID)
  943. } else {
  944. fmt.Fprintf(w, "%s\t", utils.TruncateID(outID))
  945. }
  946. fmt.Fprintf(w, "%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))))
  947. if *noTrunc {
  948. fmt.Fprintf(w, "%s\t", out.Get("CreatedBy"))
  949. } else {
  950. fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45))
  951. }
  952. fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("Size"))))
  953. } else {
  954. if *noTrunc {
  955. fmt.Fprintln(w, outID)
  956. } else {
  957. fmt.Fprintln(w, utils.TruncateID(outID))
  958. }
  959. }
  960. }
  961. w.Flush()
  962. return nil
  963. }
  964. func (cli *DockerCli) CmdRm(args ...string) error {
  965. cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers", true)
  966. v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
  967. link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
  968. force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")
  969. cmd.Require(flag.Min, 1)
  970. utils.ParseFlags(cmd, args, true)
  971. val := url.Values{}
  972. if *v {
  973. val.Set("v", "1")
  974. }
  975. if *link {
  976. val.Set("link", "1")
  977. }
  978. if *force {
  979. val.Set("force", "1")
  980. }
  981. var encounteredError error
  982. for _, name := range cmd.Args() {
  983. _, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
  984. if err != nil {
  985. fmt.Fprintf(cli.err, "%s\n", err)
  986. encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
  987. } else {
  988. fmt.Fprintf(cli.out, "%s\n", name)
  989. }
  990. }
  991. return encounteredError
  992. }
  993. // 'docker kill NAME' kills a running container
  994. func (cli *DockerCli) CmdKill(args ...string) error {
  995. cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal", true)
  996. signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
  997. cmd.Require(flag.Min, 1)
  998. utils.ParseFlags(cmd, args, true)
  999. var encounteredError error
  1000. for _, name := range cmd.Args() {
  1001. if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
  1002. fmt.Fprintf(cli.err, "%s\n", err)
  1003. encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
  1004. } else {
  1005. fmt.Fprintf(cli.out, "%s\n", name)
  1006. }
  1007. }
  1008. return encounteredError
  1009. }
  1010. func (cli *DockerCli) CmdImport(args ...string) error {
  1011. 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.", true)
  1012. cmd.Require(flag.Min, 1)
  1013. utils.ParseFlags(cmd, args, true)
  1014. var (
  1015. v = url.Values{}
  1016. src = cmd.Arg(0)
  1017. repository = cmd.Arg(1)
  1018. )
  1019. v.Set("fromSrc", src)
  1020. v.Set("repo", repository)
  1021. if cmd.NArg() == 3 {
  1022. fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' has been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
  1023. v.Set("tag", cmd.Arg(2))
  1024. }
  1025. if repository != "" {
  1026. //Check if the given image name can be resolved
  1027. repo, _ := parsers.ParseRepositoryTag(repository)
  1028. if err := registry.ValidateRepositoryName(repo); err != nil {
  1029. return err
  1030. }
  1031. }
  1032. var in io.Reader
  1033. if src == "-" {
  1034. in = cli.in
  1035. }
  1036. return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil)
  1037. }
  1038. func (cli *DockerCli) CmdPush(args ...string) error {
  1039. cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry", true)
  1040. cmd.Require(flag.Exact, 1)
  1041. utils.ParseFlags(cmd, args, true)
  1042. name := cmd.Arg(0)
  1043. cli.LoadConfigFile()
  1044. remote, tag := parsers.ParseRepositoryTag(name)
  1045. // Resolve the Repository name from fqn to RepositoryInfo
  1046. repoInfo, err := registry.ParseRepositoryInfo(remote)
  1047. if err != nil {
  1048. return err
  1049. }
  1050. // Resolve the Auth config relevant for this server
  1051. authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
  1052. // If we're not using a custom registry, we know the restrictions
  1053. // applied to repository names and can warn the user in advance.
  1054. // Custom repositories can have different rules, and we must also
  1055. // allow pushing by image ID.
  1056. if repoInfo.Official {
  1057. username := authConfig.Username
  1058. if username == "" {
  1059. username = "<user>"
  1060. }
  1061. return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to <user>/<repo> (ex: %s/%s)", username, repoInfo.LocalName)
  1062. }
  1063. v := url.Values{}
  1064. v.Set("tag", tag)
  1065. push := func(authConfig registry.AuthConfig) error {
  1066. buf, err := json.Marshal(authConfig)
  1067. if err != nil {
  1068. return err
  1069. }
  1070. registryAuthHeader := []string{
  1071. base64.URLEncoding.EncodeToString(buf),
  1072. }
  1073. return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
  1074. "X-Registry-Auth": registryAuthHeader,
  1075. })
  1076. }
  1077. if err := push(authConfig); err != nil {
  1078. if strings.Contains(err.Error(), "Status 401") {
  1079. fmt.Fprintln(cli.out, "\nPlease login prior to push:")
  1080. if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
  1081. return err
  1082. }
  1083. authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
  1084. return push(authConfig)
  1085. }
  1086. return err
  1087. }
  1088. return nil
  1089. }
  1090. func (cli *DockerCli) CmdPull(args ...string) error {
  1091. cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry", true)
  1092. allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
  1093. cmd.Require(flag.Exact, 1)
  1094. utils.ParseFlags(cmd, args, true)
  1095. var (
  1096. v = url.Values{}
  1097. remote = cmd.Arg(0)
  1098. newRemote = remote
  1099. )
  1100. taglessRemote, tag := parsers.ParseRepositoryTag(remote)
  1101. if tag == "" && !*allTags {
  1102. newRemote = taglessRemote + ":" + graph.DEFAULTTAG
  1103. }
  1104. if tag != "" && *allTags {
  1105. return fmt.Errorf("tag can't be used with --all-tags/-a")
  1106. }
  1107. v.Set("fromImage", newRemote)
  1108. // Resolve the Repository name from fqn to RepositoryInfo
  1109. repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
  1110. if err != nil {
  1111. return err
  1112. }
  1113. cli.LoadConfigFile()
  1114. // Resolve the Auth config relevant for this server
  1115. authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
  1116. pull := func(authConfig registry.AuthConfig) error {
  1117. buf, err := json.Marshal(authConfig)
  1118. if err != nil {
  1119. return err
  1120. }
  1121. registryAuthHeader := []string{
  1122. base64.URLEncoding.EncodeToString(buf),
  1123. }
  1124. return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
  1125. "X-Registry-Auth": registryAuthHeader,
  1126. })
  1127. }
  1128. if err := pull(authConfig); err != nil {
  1129. if strings.Contains(err.Error(), "Status 401") {
  1130. fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
  1131. if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
  1132. return err
  1133. }
  1134. authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
  1135. return pull(authConfig)
  1136. }
  1137. return err
  1138. }
  1139. return nil
  1140. }
  1141. func (cli *DockerCli) CmdImages(args ...string) error {
  1142. cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true)
  1143. quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
  1144. all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate image layers)")
  1145. noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
  1146. // FIXME: --viz and --tree are deprecated. Remove them in a future version.
  1147. flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format")
  1148. flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format")
  1149. flFilter := opts.NewListOpts(nil)
  1150. cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e., 'dangling=true')")
  1151. cmd.Require(flag.Max, 1)
  1152. utils.ParseFlags(cmd, args, true)
  1153. // Consolidate all filter flags, and sanity check them early.
  1154. // They'll get process in the daemon/server.
  1155. imageFilterArgs := filters.Args{}
  1156. for _, f := range flFilter.GetAll() {
  1157. var err error
  1158. imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs)
  1159. if err != nil {
  1160. return err
  1161. }
  1162. }
  1163. matchName := cmd.Arg(0)
  1164. // FIXME: --viz and --tree are deprecated. Remove them in a future version.
  1165. if *flViz || *flTree {
  1166. v := url.Values{
  1167. "all": []string{"1"},
  1168. }
  1169. if len(imageFilterArgs) > 0 {
  1170. filterJson, err := filters.ToParam(imageFilterArgs)
  1171. if err != nil {
  1172. return err
  1173. }
  1174. v.Set("filters", filterJson)
  1175. }
  1176. body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
  1177. if err != nil {
  1178. return err
  1179. }
  1180. outs := engine.NewTable("Created", 0)
  1181. if _, err := outs.ReadListFrom(body); err != nil {
  1182. return err
  1183. }
  1184. var (
  1185. printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
  1186. startImage *engine.Env
  1187. roots = engine.NewTable("Created", outs.Len())
  1188. byParent = make(map[string]*engine.Table)
  1189. )
  1190. for _, image := range outs.Data {
  1191. if image.Get("ParentId") == "" {
  1192. roots.Add(image)
  1193. } else {
  1194. if children, exists := byParent[image.Get("ParentId")]; exists {
  1195. children.Add(image)
  1196. } else {
  1197. byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
  1198. byParent[image.Get("ParentId")].Add(image)
  1199. }
  1200. }
  1201. if matchName != "" {
  1202. if matchName == image.Get("Id") || matchName == utils.TruncateID(image.Get("Id")) {
  1203. startImage = image
  1204. }
  1205. for _, repotag := range image.GetList("RepoTags") {
  1206. if repotag == matchName {
  1207. startImage = image
  1208. }
  1209. }
  1210. }
  1211. }
  1212. if *flViz {
  1213. fmt.Fprintf(cli.out, "digraph docker {\n")
  1214. printNode = (*DockerCli).printVizNode
  1215. } else {
  1216. printNode = (*DockerCli).printTreeNode
  1217. }
  1218. if startImage != nil {
  1219. root := engine.NewTable("Created", 1)
  1220. root.Add(startImage)
  1221. cli.WalkTree(*noTrunc, root, byParent, "", printNode)
  1222. } else if matchName == "" {
  1223. cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
  1224. }
  1225. if *flViz {
  1226. fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
  1227. }
  1228. } else {
  1229. v := url.Values{}
  1230. if len(imageFilterArgs) > 0 {
  1231. filterJson, err := filters.ToParam(imageFilterArgs)
  1232. if err != nil {
  1233. return err
  1234. }
  1235. v.Set("filters", filterJson)
  1236. }
  1237. if cmd.NArg() == 1 {
  1238. // FIXME rename this parameter, to not be confused with the filters flag
  1239. v.Set("filter", matchName)
  1240. }
  1241. if *all {
  1242. v.Set("all", "1")
  1243. }
  1244. body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
  1245. if err != nil {
  1246. return err
  1247. }
  1248. outs := engine.NewTable("Created", 0)
  1249. if _, err := outs.ReadListFrom(body); err != nil {
  1250. return err
  1251. }
  1252. w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
  1253. if !*quiet {
  1254. fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
  1255. }
  1256. for _, out := range outs.Data {
  1257. for _, repotag := range out.GetList("RepoTags") {
  1258. repo, tag := parsers.ParseRepositoryTag(repotag)
  1259. outID := out.Get("Id")
  1260. if !*noTrunc {
  1261. outID = utils.TruncateID(outID)
  1262. }
  1263. if !*quiet {
  1264. fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize"))))
  1265. } else {
  1266. fmt.Fprintln(w, outID)
  1267. }
  1268. }
  1269. }
  1270. if !*quiet {
  1271. w.Flush()
  1272. }
  1273. }
  1274. return nil
  1275. }
  1276. // FIXME: --viz and --tree are deprecated. Remove them in a future version.
  1277. 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)) {
  1278. length := images.Len()
  1279. if length > 1 {
  1280. for index, image := range images.Data {
  1281. if index+1 == length {
  1282. printNode(cli, noTrunc, image, prefix+"└─")
  1283. if subimages, exists := byParent[image.Get("Id")]; exists {
  1284. cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode)
  1285. }
  1286. } else {
  1287. printNode(cli, noTrunc, image, prefix+"\u251C─")
  1288. if subimages, exists := byParent[image.Get("Id")]; exists {
  1289. cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
  1290. }
  1291. }
  1292. }
  1293. } else {
  1294. for _, image := range images.Data {
  1295. printNode(cli, noTrunc, image, prefix+"└─")
  1296. if subimages, exists := byParent[image.Get("Id")]; exists {
  1297. cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode)
  1298. }
  1299. }
  1300. }
  1301. }
  1302. // FIXME: --viz and --tree are deprecated. Remove them in a future version.
  1303. func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
  1304. var (
  1305. imageID string
  1306. parentID string
  1307. )
  1308. if noTrunc {
  1309. imageID = image.Get("Id")
  1310. parentID = image.Get("ParentId")
  1311. } else {
  1312. imageID = utils.TruncateID(image.Get("Id"))
  1313. parentID = utils.TruncateID(image.Get("ParentId"))
  1314. }
  1315. if parentID == "" {
  1316. fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
  1317. } else {
  1318. fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
  1319. }
  1320. if image.GetList("RepoTags")[0] != "<none>:<none>" {
  1321. fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
  1322. imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
  1323. }
  1324. }
  1325. // FIXME: --viz and --tree are deprecated. Remove them in a future version.
  1326. func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
  1327. var imageID string
  1328. if noTrunc {
  1329. imageID = image.Get("Id")
  1330. } else {
  1331. imageID = utils.TruncateID(image.Get("Id"))
  1332. }
  1333. fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.GetInt64("VirtualSize"))))
  1334. if image.GetList("RepoTags")[0] != "<none>:<none>" {
  1335. fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
  1336. } else {
  1337. fmt.Fprint(cli.out, "\n")
  1338. }
  1339. }
  1340. func (cli *DockerCli) CmdPs(args ...string) error {
  1341. var (
  1342. err error
  1343. psFilterArgs = filters.Args{}
  1344. v = url.Values{}
  1345. cmd = cli.Subcmd("ps", "", "List containers", true)
  1346. quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
  1347. size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
  1348. all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.")
  1349. noTrunc = cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
  1350. nLatest = cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.")
  1351. since = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.")
  1352. before = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.")
  1353. last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.")
  1354. flFilter = opts.NewListOpts(nil)
  1355. )
  1356. cmd.Require(flag.Exact, 0)
  1357. cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>\nstatus=(restarting|running|paused|exited)")
  1358. utils.ParseFlags(cmd, args, true)
  1359. if *last == -1 && *nLatest {
  1360. *last = 1
  1361. }
  1362. if *all {
  1363. v.Set("all", "1")
  1364. }
  1365. if *last != -1 {
  1366. v.Set("limit", strconv.Itoa(*last))
  1367. }
  1368. if *since != "" {
  1369. v.Set("since", *since)
  1370. }
  1371. if *before != "" {
  1372. v.Set("before", *before)
  1373. }
  1374. if *size {
  1375. v.Set("size", "1")
  1376. }
  1377. // Consolidate all filter flags, and sanity check them.
  1378. // They'll get processed in the daemon/server.
  1379. for _, f := range flFilter.GetAll() {
  1380. if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil {
  1381. return err
  1382. }
  1383. }
  1384. if len(psFilterArgs) > 0 {
  1385. filterJson, err := filters.ToParam(psFilterArgs)
  1386. if err != nil {
  1387. return err
  1388. }
  1389. v.Set("filters", filterJson)
  1390. }
  1391. body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
  1392. if err != nil {
  1393. return err
  1394. }
  1395. outs := engine.NewTable("Created", 0)
  1396. if _, err := outs.ReadListFrom(body); err != nil {
  1397. return err
  1398. }
  1399. w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
  1400. if !*quiet {
  1401. fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
  1402. if *size {
  1403. fmt.Fprintln(w, "\tSIZE")
  1404. } else {
  1405. fmt.Fprint(w, "\n")
  1406. }
  1407. }
  1408. stripNamePrefix := func(ss []string) []string {
  1409. for i, s := range ss {
  1410. ss[i] = s[1:]
  1411. }
  1412. return ss
  1413. }
  1414. for _, out := range outs.Data {
  1415. outID := out.Get("Id")
  1416. if !*noTrunc {
  1417. outID = utils.TruncateID(outID)
  1418. }
  1419. if *quiet {
  1420. fmt.Fprintln(w, outID)
  1421. continue
  1422. }
  1423. var (
  1424. outNames = stripNamePrefix(out.GetList("Names"))
  1425. outCommand = strconv.Quote(out.Get("Command"))
  1426. ports = engine.NewTable("", 0)
  1427. )
  1428. if !*noTrunc {
  1429. outCommand = utils.Trunc(outCommand, 20)
  1430. // only display the default name for the container with notrunc is passed
  1431. for _, name := range outNames {
  1432. if len(strings.Split(name, "/")) == 1 {
  1433. outNames = []string{name}
  1434. break
  1435. }
  1436. }
  1437. }
  1438. ports.ReadListFrom([]byte(out.Get("Ports")))
  1439. image := out.Get("Image")
  1440. if image == "" {
  1441. image = "<no image>"
  1442. }
  1443. fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand,
  1444. units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))),
  1445. out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
  1446. if *size {
  1447. if out.GetInt("SizeRootFs") > 0 {
  1448. fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(out.GetInt64("SizeRw"))), units.HumanSize(float64(out.GetInt64("SizeRootFs"))))
  1449. } else {
  1450. fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("SizeRw"))))
  1451. }
  1452. continue
  1453. }
  1454. fmt.Fprint(w, "\n")
  1455. }
  1456. if !*quiet {
  1457. w.Flush()
  1458. }
  1459. return nil
  1460. }
  1461. func (cli *DockerCli) CmdCommit(args ...string) error {
  1462. cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes", true)
  1463. flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
  1464. flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
  1465. flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
  1466. // FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands.
  1467. flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
  1468. cmd.Require(flag.Max, 2)
  1469. cmd.Require(flag.Min, 1)
  1470. utils.ParseFlags(cmd, args, true)
  1471. var (
  1472. name = cmd.Arg(0)
  1473. repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
  1474. )
  1475. //Check if the given image name can be resolved
  1476. if repository != "" {
  1477. if err := registry.ValidateRepositoryName(repository); err != nil {
  1478. return err
  1479. }
  1480. }
  1481. v := url.Values{}
  1482. v.Set("container", name)
  1483. v.Set("repo", repository)
  1484. v.Set("tag", tag)
  1485. v.Set("comment", *flComment)
  1486. v.Set("author", *flAuthor)
  1487. if *flPause != true {
  1488. v.Set("pause", "0")
  1489. }
  1490. var (
  1491. config *runconfig.Config
  1492. env engine.Env
  1493. )
  1494. if *flConfig != "" {
  1495. config = &runconfig.Config{}
  1496. if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
  1497. return err
  1498. }
  1499. }
  1500. stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
  1501. if err != nil {
  1502. return err
  1503. }
  1504. if err := env.Decode(stream); err != nil {
  1505. return err
  1506. }
  1507. fmt.Fprintf(cli.out, "%s\n", env.Get("Id"))
  1508. return nil
  1509. }
  1510. func (cli *DockerCli) CmdEvents(args ...string) error {
  1511. cmd := cli.Subcmd("events", "", "Get real time events from the server", true)
  1512. since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
  1513. until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
  1514. flFilter := opts.NewListOpts(nil)
  1515. cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e., 'event=stop')")
  1516. cmd.Require(flag.Exact, 0)
  1517. utils.ParseFlags(cmd, args, true)
  1518. var (
  1519. v = url.Values{}
  1520. loc = time.FixedZone(time.Now().Zone())
  1521. eventFilterArgs = filters.Args{}
  1522. )
  1523. // Consolidate all filter flags, and sanity check them early.
  1524. // They'll get process in the daemon/server.
  1525. for _, f := range flFilter.GetAll() {
  1526. var err error
  1527. eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs)
  1528. if err != nil {
  1529. return err
  1530. }
  1531. }
  1532. var setTime = func(key, value string) {
  1533. format := timeutils.RFC3339NanoFixed
  1534. if len(value) < len(format) {
  1535. format = format[:len(value)]
  1536. }
  1537. if t, err := time.ParseInLocation(format, value, loc); err == nil {
  1538. v.Set(key, strconv.FormatInt(t.Unix(), 10))
  1539. } else {
  1540. v.Set(key, value)
  1541. }
  1542. }
  1543. if *since != "" {
  1544. setTime("since", *since)
  1545. }
  1546. if *until != "" {
  1547. setTime("until", *until)
  1548. }
  1549. if len(eventFilterArgs) > 0 {
  1550. filterJson, err := filters.ToParam(eventFilterArgs)
  1551. if err != nil {
  1552. return err
  1553. }
  1554. v.Set("filters", filterJson)
  1555. }
  1556. if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
  1557. return err
  1558. }
  1559. return nil
  1560. }
  1561. func (cli *DockerCli) CmdExport(args ...string) error {
  1562. cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT", true)
  1563. cmd.Require(flag.Exact, 1)
  1564. utils.ParseFlags(cmd, args, true)
  1565. if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil {
  1566. return err
  1567. }
  1568. return nil
  1569. }
  1570. func (cli *DockerCli) CmdDiff(args ...string) error {
  1571. cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem", true)
  1572. cmd.Require(flag.Exact, 1)
  1573. utils.ParseFlags(cmd, args, true)
  1574. body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
  1575. if err != nil {
  1576. return err
  1577. }
  1578. outs := engine.NewTable("", 0)
  1579. if _, err := outs.ReadListFrom(body); err != nil {
  1580. return err
  1581. }
  1582. for _, change := range outs.Data {
  1583. var kind string
  1584. switch change.GetInt("Kind") {
  1585. case archive.ChangeModify:
  1586. kind = "C"
  1587. case archive.ChangeAdd:
  1588. kind = "A"
  1589. case archive.ChangeDelete:
  1590. kind = "D"
  1591. }
  1592. fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path"))
  1593. }
  1594. return nil
  1595. }
  1596. func (cli *DockerCli) CmdLogs(args ...string) error {
  1597. var (
  1598. cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container", true)
  1599. follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
  1600. times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
  1601. tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)")
  1602. )
  1603. cmd.Require(flag.Exact, 1)
  1604. utils.ParseFlags(cmd, args, true)
  1605. name := cmd.Arg(0)
  1606. stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
  1607. if err != nil {
  1608. return err
  1609. }
  1610. env := engine.Env{}
  1611. if err := env.Decode(stream); err != nil {
  1612. return err
  1613. }
  1614. v := url.Values{}
  1615. v.Set("stdout", "1")
  1616. v.Set("stderr", "1")
  1617. if *times {
  1618. v.Set("timestamps", "1")
  1619. }
  1620. if *follow {
  1621. v.Set("follow", "1")
  1622. }
  1623. v.Set("tail", *tail)
  1624. return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil)
  1625. }
  1626. func (cli *DockerCli) CmdAttach(args ...string) error {
  1627. var (
  1628. cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container", true)
  1629. noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
  1630. proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process (non-TTY mode only). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.")
  1631. )
  1632. cmd.Require(flag.Exact, 1)
  1633. utils.ParseFlags(cmd, args, true)
  1634. name := cmd.Arg(0)
  1635. stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
  1636. if err != nil {
  1637. return err
  1638. }
  1639. env := engine.Env{}
  1640. if err := env.Decode(stream); err != nil {
  1641. return err
  1642. }
  1643. if !env.GetSubEnv("State").GetBool("Running") {
  1644. return fmt.Errorf("You cannot attach to a stopped container, start it first")
  1645. }
  1646. var (
  1647. config = env.GetSubEnv("Config")
  1648. tty = config.GetBool("Tty")
  1649. )
  1650. if err := cli.CheckTtyInput(!*noStdin, tty); err != nil {
  1651. return err
  1652. }
  1653. if tty && cli.isTerminalOut {
  1654. if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
  1655. log.Debugf("Error monitoring TTY size: %s", err)
  1656. }
  1657. }
  1658. var in io.ReadCloser
  1659. v := url.Values{}
  1660. v.Set("stream", "1")
  1661. if !*noStdin && config.GetBool("OpenStdin") {
  1662. v.Set("stdin", "1")
  1663. in = cli.in
  1664. }
  1665. v.Set("stdout", "1")
  1666. v.Set("stderr", "1")
  1667. if *proxy && !tty {
  1668. sigc := cli.forwardAllSignals(cmd.Arg(0))
  1669. defer signal.StopCatch(sigc)
  1670. }
  1671. if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil); err != nil {
  1672. return err
  1673. }
  1674. _, status, err := getExitCode(cli, cmd.Arg(0))
  1675. if err != nil {
  1676. return err
  1677. }
  1678. if status != 0 {
  1679. return &utils.StatusError{StatusCode: status}
  1680. }
  1681. return nil
  1682. }
  1683. func (cli *DockerCli) CmdSearch(args ...string) error {
  1684. cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images", true)
  1685. noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
  1686. trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
  1687. automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
  1688. stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars")
  1689. cmd.Require(flag.Exact, 1)
  1690. utils.ParseFlags(cmd, args, true)
  1691. v := url.Values{}
  1692. v.Set("term", cmd.Arg(0))
  1693. body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
  1694. if err != nil {
  1695. return err
  1696. }
  1697. outs := engine.NewTable("star_count", 0)
  1698. if _, err := outs.ReadListFrom(body); err != nil {
  1699. return err
  1700. }
  1701. w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
  1702. fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
  1703. for _, out := range outs.Data {
  1704. if ((*automated || *trusted) && (!out.GetBool("is_trusted") && !out.GetBool("is_automated"))) || (*stars > out.GetInt("star_count")) {
  1705. continue
  1706. }
  1707. desc := strings.Replace(out.Get("description"), "\n", " ", -1)
  1708. desc = strings.Replace(desc, "\r", " ", -1)
  1709. if !*noTrunc && len(desc) > 45 {
  1710. desc = utils.Trunc(desc, 42) + "..."
  1711. }
  1712. fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count"))
  1713. if out.GetBool("is_official") {
  1714. fmt.Fprint(w, "[OK]")
  1715. }
  1716. fmt.Fprint(w, "\t")
  1717. if out.GetBool("is_automated") || out.GetBool("is_trusted") {
  1718. fmt.Fprint(w, "[OK]")
  1719. }
  1720. fmt.Fprint(w, "\n")
  1721. }
  1722. w.Flush()
  1723. return nil
  1724. }
  1725. // Ports type - Used to parse multiple -p flags
  1726. type ports []int
  1727. func (cli *DockerCli) CmdTag(args ...string) error {
  1728. cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository", true)
  1729. force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
  1730. cmd.Require(flag.Exact, 2)
  1731. utils.ParseFlags(cmd, args, true)
  1732. var (
  1733. repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
  1734. v = url.Values{}
  1735. )
  1736. //Check if the given image name can be resolved
  1737. if err := registry.ValidateRepositoryName(repository); err != nil {
  1738. return err
  1739. }
  1740. v.Set("repo", repository)
  1741. v.Set("tag", tag)
  1742. if *force {
  1743. v.Set("force", "1")
  1744. }
  1745. if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
  1746. return err
  1747. }
  1748. return nil
  1749. }
  1750. func (cli *DockerCli) pullImage(image string) error {
  1751. return cli.pullImageCustomOut(image, cli.out)
  1752. }
  1753. func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
  1754. v := url.Values{}
  1755. repos, tag := parsers.ParseRepositoryTag(image)
  1756. // pull only the image tagged 'latest' if no tag was specified
  1757. if tag == "" {
  1758. tag = graph.DEFAULTTAG
  1759. }
  1760. v.Set("fromImage", repos)
  1761. v.Set("tag", tag)
  1762. // Resolve the Repository name from fqn to RepositoryInfo
  1763. repoInfo, err := registry.ParseRepositoryInfo(repos)
  1764. if err != nil {
  1765. return err
  1766. }
  1767. // Load the auth config file, to be able to pull the image
  1768. cli.LoadConfigFile()
  1769. // Resolve the Auth config relevant for this server
  1770. authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
  1771. buf, err := json.Marshal(authConfig)
  1772. if err != nil {
  1773. return err
  1774. }
  1775. registryAuthHeader := []string{
  1776. base64.URLEncoding.EncodeToString(buf),
  1777. }
  1778. if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
  1779. return err
  1780. }
  1781. return nil
  1782. }
  1783. type cidFile struct {
  1784. path string
  1785. file *os.File
  1786. written bool
  1787. }
  1788. func newCIDFile(path string) (*cidFile, error) {
  1789. if _, err := os.Stat(path); err == nil {
  1790. return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
  1791. }
  1792. f, err := os.Create(path)
  1793. if err != nil {
  1794. return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
  1795. }
  1796. return &cidFile{path: path, file: f}, nil
  1797. }
  1798. func (cid *cidFile) Close() error {
  1799. cid.file.Close()
  1800. if !cid.written {
  1801. if err := os.Remove(cid.path); err != nil {
  1802. return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
  1803. }
  1804. }
  1805. return nil
  1806. }
  1807. func (cid *cidFile) Write(id string) error {
  1808. if _, err := cid.file.Write([]byte(id)); err != nil {
  1809. return fmt.Errorf("Failed to write the container ID to the file: %s", err)
  1810. }
  1811. cid.written = true
  1812. return nil
  1813. }
  1814. func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) {
  1815. containerValues := url.Values{}
  1816. if name != "" {
  1817. containerValues.Set("name", name)
  1818. }
  1819. mergedConfig := runconfig.MergeConfigs(config, hostConfig)
  1820. var containerIDFile *cidFile
  1821. if cidfile != "" {
  1822. var err error
  1823. if containerIDFile, err = newCIDFile(cidfile); err != nil {
  1824. return nil, err
  1825. }
  1826. defer containerIDFile.Close()
  1827. }
  1828. //create the container
  1829. stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false)
  1830. //if image not found try to pull it
  1831. if statusCode == 404 {
  1832. repo, tag := parsers.ParseRepositoryTag(config.Image)
  1833. if tag == "" {
  1834. tag = graph.DEFAULTTAG
  1835. }
  1836. fmt.Fprintf(cli.err, "Unable to find image '%s:%s' locally\n", repo, tag)
  1837. // we don't want to write to stdout anything apart from container.ID
  1838. if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil {
  1839. return nil, err
  1840. }
  1841. // Retry
  1842. if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil {
  1843. return nil, err
  1844. }
  1845. } else if err != nil {
  1846. return nil, err
  1847. }
  1848. var result engine.Env
  1849. if err := result.Decode(stream); err != nil {
  1850. return nil, err
  1851. }
  1852. for _, warning := range result.GetList("Warnings") {
  1853. fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
  1854. }
  1855. if containerIDFile != nil {
  1856. if err = containerIDFile.Write(result.Get("Id")); err != nil {
  1857. return nil, err
  1858. }
  1859. }
  1860. return result, nil
  1861. }
  1862. func (cli *DockerCli) CmdCreate(args ...string) error {
  1863. cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true)
  1864. // These are flags not stored in Config/HostConfig
  1865. var (
  1866. flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
  1867. )
  1868. config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
  1869. if err != nil {
  1870. utils.ReportError(cmd, err.Error(), true)
  1871. }
  1872. if config.Image == "" {
  1873. cmd.Usage()
  1874. return nil
  1875. }
  1876. createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
  1877. if err != nil {
  1878. return err
  1879. }
  1880. fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id"))
  1881. return nil
  1882. }
  1883. func (cli *DockerCli) CmdRun(args ...string) error {
  1884. // FIXME: just use runconfig.Parse already
  1885. cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container", true)
  1886. // These are flags not stored in Config/HostConfig
  1887. var (
  1888. flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
  1889. flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run the container in the background and print the new container ID")
  1890. flSigProxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.")
  1891. flName = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
  1892. flAttach *opts.ListOpts
  1893. ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
  1894. ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
  1895. ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
  1896. )
  1897. config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
  1898. // just in case the Parse does not exit
  1899. if err != nil {
  1900. utils.ReportError(cmd, err.Error(), true)
  1901. }
  1902. if config.Image == "" {
  1903. cmd.Usage()
  1904. return nil
  1905. }
  1906. if !*flDetach {
  1907. if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
  1908. return err
  1909. }
  1910. } else {
  1911. if fl := cmd.Lookup("attach"); fl != nil {
  1912. flAttach = fl.Value.(*opts.ListOpts)
  1913. if flAttach.Len() != 0 {
  1914. return ErrConflictAttachDetach
  1915. }
  1916. }
  1917. if *flAutoRemove {
  1918. return ErrConflictDetachAutoRemove
  1919. }
  1920. config.AttachStdin = false
  1921. config.AttachStdout = false
  1922. config.AttachStderr = false
  1923. config.StdinOnce = false
  1924. }
  1925. // Disable flSigProxy when in TTY mode
  1926. sigProxy := *flSigProxy
  1927. if config.Tty {
  1928. sigProxy = false
  1929. }
  1930. runResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
  1931. if err != nil {
  1932. return err
  1933. }
  1934. if sigProxy {
  1935. sigc := cli.forwardAllSignals(runResult.Get("Id"))
  1936. defer signal.StopCatch(sigc)
  1937. }
  1938. var (
  1939. waitDisplayId chan struct{}
  1940. errCh chan error
  1941. )
  1942. if !config.AttachStdout && !config.AttachStderr {
  1943. // Make this asynchronous to allow the client to write to stdin before having to read the ID
  1944. waitDisplayId = make(chan struct{})
  1945. go func() {
  1946. defer close(waitDisplayId)
  1947. fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id"))
  1948. }()
  1949. }
  1950. if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") {
  1951. return ErrConflictRestartPolicyAndAutoRemove
  1952. }
  1953. // We need to instantiate the chan because the select needs it. It can
  1954. // be closed but can't be uninitialized.
  1955. hijacked := make(chan io.Closer)
  1956. // Block the return until the chan gets closed
  1957. defer func() {
  1958. log.Debugf("End of CmdRun(), Waiting for hijack to finish.")
  1959. if _, ok := <-hijacked; ok {
  1960. log.Errorf("Hijack did not finish (chan still open)")
  1961. }
  1962. }()
  1963. if config.AttachStdin || config.AttachStdout || config.AttachStderr {
  1964. var (
  1965. out, stderr io.Writer
  1966. in io.ReadCloser
  1967. v = url.Values{}
  1968. )
  1969. v.Set("stream", "1")
  1970. if config.AttachStdin {
  1971. v.Set("stdin", "1")
  1972. in = cli.in
  1973. }
  1974. if config.AttachStdout {
  1975. v.Set("stdout", "1")
  1976. out = cli.out
  1977. }
  1978. if config.AttachStderr {
  1979. v.Set("stderr", "1")
  1980. if config.Tty {
  1981. stderr = cli.out
  1982. } else {
  1983. stderr = cli.err
  1984. }
  1985. }
  1986. errCh = promise.Go(func() error {
  1987. return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil)
  1988. })
  1989. } else {
  1990. close(hijacked)
  1991. }
  1992. // Acknowledge the hijack before starting
  1993. select {
  1994. case closer := <-hijacked:
  1995. // Make sure that the hijack gets closed when returning (results
  1996. // in closing the hijack chan and freeing server's goroutines)
  1997. if closer != nil {
  1998. defer closer.Close()
  1999. }
  2000. case err := <-errCh:
  2001. if err != nil {
  2002. log.Debugf("Error hijack: %s", err)
  2003. return err
  2004. }
  2005. }
  2006. //start the container
  2007. if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", nil, false)); err != nil {
  2008. return err
  2009. }
  2010. if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
  2011. if err := cli.monitorTtySize(runResult.Get("Id"), false); err != nil {
  2012. log.Errorf("Error monitoring TTY size: %s", err)
  2013. }
  2014. }
  2015. if errCh != nil {
  2016. if err := <-errCh; err != nil {
  2017. log.Debugf("Error hijack: %s", err)
  2018. return err
  2019. }
  2020. }
  2021. // Detached mode: wait for the id to be displayed and return.
  2022. if !config.AttachStdout && !config.AttachStderr {
  2023. // Detached mode
  2024. <-waitDisplayId
  2025. return nil
  2026. }
  2027. var status int
  2028. // Attached mode
  2029. if *flAutoRemove {
  2030. // Autoremove: wait for the container to finish, retrieve
  2031. // the exit code and remove the container
  2032. if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
  2033. return err
  2034. }
  2035. if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
  2036. return err
  2037. }
  2038. if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil {
  2039. return err
  2040. }
  2041. } else {
  2042. // No Autoremove: Simply retrieve the exit code
  2043. if !config.Tty {
  2044. // In non-TTY mode, we can't detach, so we must wait for container exit
  2045. if status, err = waitForExit(cli, runResult.Get("Id")); err != nil {
  2046. return err
  2047. }
  2048. } else {
  2049. // In TTY mode, there is a race: if the process dies too slowly, the state could
  2050. // be updated after the getExitCode call and result in the wrong exit code being reported
  2051. if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
  2052. return err
  2053. }
  2054. }
  2055. }
  2056. if status != 0 {
  2057. return &utils.StatusError{StatusCode: status}
  2058. }
  2059. return nil
  2060. }
  2061. func (cli *DockerCli) CmdCp(args ...string) error {
  2062. cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH", true)
  2063. cmd.Require(flag.Exact, 2)
  2064. utils.ParseFlags(cmd, args, true)
  2065. var copyData engine.Env
  2066. info := strings.Split(cmd.Arg(0), ":")
  2067. if len(info) != 2 {
  2068. return fmt.Errorf("Error: Path not specified")
  2069. }
  2070. copyData.Set("Resource", info[1])
  2071. copyData.Set("HostPath", cmd.Arg(1))
  2072. stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
  2073. if stream != nil {
  2074. defer stream.Close()
  2075. }
  2076. if statusCode == 404 {
  2077. return fmt.Errorf("No such container: %v", info[0])
  2078. }
  2079. if err != nil {
  2080. return err
  2081. }
  2082. if statusCode == 200 {
  2083. if err := archive.Untar(stream, copyData.Get("HostPath"), &archive.TarOptions{NoLchown: true}); err != nil {
  2084. return err
  2085. }
  2086. }
  2087. return nil
  2088. }
  2089. func (cli *DockerCli) CmdSave(args ...string) error {
  2090. cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
  2091. outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
  2092. cmd.Require(flag.Min, 1)
  2093. utils.ParseFlags(cmd, args, true)
  2094. var (
  2095. output io.Writer = cli.out
  2096. err error
  2097. )
  2098. if *outfile != "" {
  2099. output, err = os.Create(*outfile)
  2100. if err != nil {
  2101. return err
  2102. }
  2103. } else if cli.isTerminalOut {
  2104. return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
  2105. }
  2106. if len(cmd.Args()) == 1 {
  2107. image := cmd.Arg(0)
  2108. if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil {
  2109. return err
  2110. }
  2111. } else {
  2112. v := url.Values{}
  2113. for _, arg := range cmd.Args() {
  2114. v.Add("names", arg)
  2115. }
  2116. if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil {
  2117. return err
  2118. }
  2119. }
  2120. return nil
  2121. }
  2122. func (cli *DockerCli) CmdLoad(args ...string) error {
  2123. cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN", true)
  2124. infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
  2125. cmd.Require(flag.Exact, 0)
  2126. utils.ParseFlags(cmd, args, true)
  2127. var (
  2128. input io.Reader = cli.in
  2129. err error
  2130. )
  2131. if *infile != "" {
  2132. input, err = os.Open(*infile)
  2133. if err != nil {
  2134. return err
  2135. }
  2136. }
  2137. if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil {
  2138. return err
  2139. }
  2140. return nil
  2141. }
  2142. func (cli *DockerCli) CmdExec(args ...string) error {
  2143. cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true)
  2144. execConfig, err := runconfig.ParseExec(cmd, args)
  2145. // just in case the ParseExec does not exit
  2146. if execConfig.Container == "" || err != nil {
  2147. return &utils.StatusError{StatusCode: 1}
  2148. }
  2149. stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false)
  2150. if err != nil {
  2151. return err
  2152. }
  2153. var execResult engine.Env
  2154. if err := execResult.Decode(stream); err != nil {
  2155. return err
  2156. }
  2157. execID := execResult.Get("Id")
  2158. if execID == "" {
  2159. fmt.Fprintf(cli.out, "exec ID empty")
  2160. return nil
  2161. }
  2162. if !execConfig.Detach {
  2163. if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil {
  2164. return err
  2165. }
  2166. } else {
  2167. if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil {
  2168. return err
  2169. }
  2170. // For now don't print this - wait for when we support exec wait()
  2171. // fmt.Fprintf(cli.out, "%s\n", execID)
  2172. return nil
  2173. }
  2174. // Interactive exec requested.
  2175. var (
  2176. out, stderr io.Writer
  2177. in io.ReadCloser
  2178. hijacked = make(chan io.Closer)
  2179. errCh chan error
  2180. )
  2181. // Block the return until the chan gets closed
  2182. defer func() {
  2183. log.Debugf("End of CmdExec(), Waiting for hijack to finish.")
  2184. if _, ok := <-hijacked; ok {
  2185. log.Errorf("Hijack did not finish (chan still open)")
  2186. }
  2187. }()
  2188. if execConfig.AttachStdin {
  2189. in = cli.in
  2190. }
  2191. if execConfig.AttachStdout {
  2192. out = cli.out
  2193. }
  2194. if execConfig.AttachStderr {
  2195. if execConfig.Tty {
  2196. stderr = cli.out
  2197. } else {
  2198. stderr = cli.err
  2199. }
  2200. }
  2201. errCh = promise.Go(func() error {
  2202. return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig)
  2203. })
  2204. // Acknowledge the hijack before starting
  2205. select {
  2206. case closer := <-hijacked:
  2207. // Make sure that hijack gets closed when returning. (result
  2208. // in closing hijack chan and freeing server's goroutines.
  2209. if closer != nil {
  2210. defer closer.Close()
  2211. }
  2212. case err := <-errCh:
  2213. if err != nil {
  2214. log.Debugf("Error hijack: %s", err)
  2215. return err
  2216. }
  2217. }
  2218. if execConfig.Tty && cli.isTerminalIn {
  2219. if err := cli.monitorTtySize(execID, true); err != nil {
  2220. log.Errorf("Error monitoring TTY size: %s", err)
  2221. }
  2222. }
  2223. if err := <-errCh; err != nil {
  2224. log.Debugf("Error hijack: %s", err)
  2225. return err
  2226. }
  2227. var status int
  2228. if _, status, err = getExecExitCode(cli, execID); err != nil {
  2229. return err
  2230. }
  2231. if status != 0 {
  2232. return &utils.StatusError{StatusCode: status}
  2233. }
  2234. return nil
  2235. }
  2236. type containerStats struct {
  2237. Name string
  2238. CpuPercentage float64
  2239. Memory float64
  2240. MemoryLimit float64
  2241. MemoryPercentage float64
  2242. NetworkRx float64
  2243. NetworkTx float64
  2244. mu sync.RWMutex
  2245. err error
  2246. }
  2247. func (s *containerStats) Collect(cli *DockerCli) {
  2248. stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
  2249. if err != nil {
  2250. s.err = err
  2251. return
  2252. }
  2253. defer stream.Close()
  2254. var (
  2255. previousCpu uint64
  2256. previousSystem uint64
  2257. start = true
  2258. dec = json.NewDecoder(stream)
  2259. u = make(chan error, 1)
  2260. )
  2261. go func() {
  2262. for {
  2263. var v *stats.Stats
  2264. if err := dec.Decode(&v); err != nil {
  2265. u <- err
  2266. return
  2267. }
  2268. var (
  2269. memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
  2270. cpuPercent = 0.0
  2271. )
  2272. if !start {
  2273. cpuPercent = calcuateCpuPercent(previousCpu, previousSystem, v)
  2274. }
  2275. start = false
  2276. s.mu.Lock()
  2277. s.CpuPercentage = cpuPercent
  2278. s.Memory = float64(v.MemoryStats.Usage)
  2279. s.MemoryLimit = float64(v.MemoryStats.Limit)
  2280. s.MemoryPercentage = memPercent
  2281. s.NetworkRx = float64(v.Network.RxBytes)
  2282. s.NetworkTx = float64(v.Network.TxBytes)
  2283. s.mu.Unlock()
  2284. previousCpu = v.CpuStats.CpuUsage.TotalUsage
  2285. previousSystem = v.CpuStats.SystemUsage
  2286. u <- nil
  2287. }
  2288. }()
  2289. for {
  2290. select {
  2291. case <-time.After(2 * time.Second):
  2292. // zero out the values if we have not received an update within
  2293. // the specified duration.
  2294. s.mu.Lock()
  2295. s.CpuPercentage = 0
  2296. s.Memory = 0
  2297. s.MemoryPercentage = 0
  2298. s.mu.Unlock()
  2299. case err := <-u:
  2300. if err != nil {
  2301. s.mu.Lock()
  2302. s.err = err
  2303. s.mu.Unlock()
  2304. return
  2305. }
  2306. }
  2307. }
  2308. }
  2309. func (s *containerStats) Display(w io.Writer) error {
  2310. s.mu.RLock()
  2311. defer s.mu.RUnlock()
  2312. if s.err != nil {
  2313. return s.err
  2314. }
  2315. fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n",
  2316. s.Name,
  2317. s.CpuPercentage,
  2318. units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit),
  2319. s.MemoryPercentage,
  2320. units.BytesSize(s.NetworkRx), units.BytesSize(s.NetworkTx))
  2321. return nil
  2322. }
  2323. func (cli *DockerCli) CmdStats(args ...string) error {
  2324. cmd := cli.Subcmd("stats", "CONTAINER", "Display a live stream of one or more containers' resource usage statistics", true)
  2325. cmd.Require(flag.Min, 1)
  2326. utils.ParseFlags(cmd, args, true)
  2327. names := cmd.Args()
  2328. sort.Strings(names)
  2329. var (
  2330. cStats []*containerStats
  2331. w = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
  2332. )
  2333. printHeader := func() {
  2334. fmt.Fprint(cli.out, "\033[2J")
  2335. fmt.Fprint(cli.out, "\033[H")
  2336. fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O")
  2337. }
  2338. for _, n := range names {
  2339. s := &containerStats{Name: n}
  2340. cStats = append(cStats, s)
  2341. go s.Collect(cli)
  2342. }
  2343. // do a quick pause so that any failed connections for containers that do not exist are able to be
  2344. // evicted before we display the initial or default values.
  2345. time.Sleep(500 * time.Millisecond)
  2346. var errs []string
  2347. for _, c := range cStats {
  2348. c.mu.Lock()
  2349. if c.err != nil {
  2350. errs = append(errs, fmt.Sprintf("%s: %s", c.Name, c.err.Error()))
  2351. }
  2352. c.mu.Unlock()
  2353. }
  2354. if len(errs) > 0 {
  2355. return fmt.Errorf("%s", strings.Join(errs, ", "))
  2356. }
  2357. for _ = range time.Tick(500 * time.Millisecond) {
  2358. printHeader()
  2359. toRemove := []int{}
  2360. for i, s := range cStats {
  2361. if err := s.Display(w); err != nil {
  2362. toRemove = append(toRemove, i)
  2363. }
  2364. }
  2365. for j := len(toRemove) - 1; j >= 0; j-- {
  2366. i := toRemove[j]
  2367. cStats = append(cStats[:i], cStats[i+1:]...)
  2368. }
  2369. if len(cStats) == 0 {
  2370. return nil
  2371. }
  2372. w.Flush()
  2373. }
  2374. return nil
  2375. }
  2376. func calcuateCpuPercent(previousCpu, previousSystem uint64, v *stats.Stats) float64 {
  2377. var (
  2378. cpuPercent = 0.0
  2379. // calculate the change for the cpu usage of the container in between readings
  2380. cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCpu)
  2381. // calculate the change for the entire system between readings
  2382. systemDelta = float64(v.CpuStats.SystemUsage - previousSystem)
  2383. )
  2384. if systemDelta > 0.0 && cpuDelta > 0.0 {
  2385. cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
  2386. }
  2387. return cpuPercent
  2388. }