commands.go 76 KB

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