commands.go 76 KB

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