commands.go 75 KB

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