commands.go 75 KB

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