mirror of
https://github.com/moby/moby.git
synced 2026-01-15 18:02:03 +00:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8ae398bf5 | ||
|
|
7a4408f608 | ||
|
|
854039b6ba | ||
|
|
070923b14f | ||
|
|
71b1657e8d | ||
|
|
1bafe9da26 | ||
|
|
1ce4ba6c9f | ||
|
|
2b1b3c1270 | ||
|
|
8243f2510e | ||
|
|
0443cc351d | ||
|
|
e16c93486d | ||
|
|
e42eb7fa8c | ||
|
|
cebfde9ea5 | ||
|
|
eff7a15bea | ||
|
|
82dadc2005 | ||
|
|
2d52d4d614 | ||
|
|
ca5ae266b7 | ||
|
|
464765b940 | ||
|
|
e9ffc1e499 | ||
|
|
4fb9a6eafb | ||
|
|
157547845a | ||
|
|
2935ca7ee2 | ||
|
|
23452f1573 | ||
|
|
f6f345b1fe | ||
|
|
e3fd61ad74 | ||
|
|
01ce63aacd | ||
|
|
3ca9c11110 | ||
|
|
b4df0b17af | ||
|
|
7f65bf508e | ||
|
|
a70dd65964 | ||
|
|
3cc0963ad1 | ||
|
|
31eb01ae8a | ||
|
|
64f346779f | ||
|
|
078a19d725 | ||
|
|
561ceac55d | ||
|
|
a373c770b6 | ||
|
|
90b8c5ce67 | ||
|
|
9bc71c101c | ||
|
|
f41d2ec4d9 | ||
|
|
1dae7a25b9 | ||
|
|
926c1d45aa | ||
|
|
80b8756da3 | ||
|
|
7d167590bc | ||
|
|
76bb920449 | ||
|
|
1ac36a3adf | ||
|
|
46bdbbabba | ||
|
|
766a2db0d9 | ||
|
|
fd0c501e6d | ||
|
|
468e4c4b56 | ||
|
|
9eda9154a7 | ||
|
|
2baea24879 | ||
|
|
1040225e36 | ||
|
|
1c091657d4 | ||
|
|
bd38b47552 | ||
|
|
43f369ea0c | ||
|
|
531b30119a | ||
|
|
cd002a4d16 | ||
|
|
c7af917d13 | ||
|
|
f339fc2eb9 | ||
|
|
8699805756 | ||
|
|
fbcd8503b3 | ||
|
|
5a36efb61f | ||
|
|
14212930e4 | ||
|
|
c8c7094b2e | ||
|
|
cb0bc4adc2 | ||
|
|
5f69a53dba | ||
|
|
2cf92abf0e | ||
|
|
2f89315bf8 |
1
AUTHORS
1
AUTHORS
@@ -15,6 +15,7 @@ Brian McCallister <brianm@skife.org>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
Caleb Spare <cespare@gmail.com>
|
||||
Charles Hooper <charles.hooper@dotcloud.com>
|
||||
Daniel Gasienica <daniel@gasienica.ch>
|
||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
|
||||
Daniel Robinson <gottagetmac@gmail.com>
|
||||
Daniel Von Fange <daniel@leancoder.com>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.0 (2013-06-03)
|
||||
+ Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
|
||||
+ Introducing Remote API: control Docker programmatically using a simple HTTP/json API
|
||||
* Runtime: various reliability and usability improvements
|
||||
|
||||
## 0.3.4 (2013-05-30)
|
||||
+ Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
|
||||
+ Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.
|
||||
|
||||
44
api.go
44
api.go
@@ -45,6 +45,8 @@ func httpError(w http.ResponseWriter, err error) {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
} else if strings.HasPrefix(err.Error(), "Bad parameter") {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
} else if strings.HasPrefix(err.Error(), "Impossible") {
|
||||
http.Error(w, err.Error(), http.StatusNotAcceptable)
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -312,16 +314,25 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
|
||||
tag := r.Form.Get("tag")
|
||||
repo := r.Form.Get("repo")
|
||||
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
if image != "" { //pull
|
||||
registry := r.Form.Get("registry")
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
|
||||
if err := srv.ImagePull(image, tag, registry, w, sf); err != nil {
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
} else { //import
|
||||
if err := srv.ImageImport(src, repo, tag, r.Body, w); err != nil {
|
||||
if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil {
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -357,10 +368,16 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
imgId, err := srv.ImageInsert(name, url, path, w)
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
imgId, err := srv.ImageInsert(name, url, path, w, sf)
|
||||
if err != nil {
|
||||
return err
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
b, err := json.Marshal(&ApiId{Id: imgId})
|
||||
if err != nil {
|
||||
@@ -380,8 +397,15 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
|
||||
if err := srv.ImagePush(name, registry, w); err != nil {
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
if err := srv.ImagePush(name, registry, w, sf); err != nil {
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -3,7 +3,7 @@ package docker
|
||||
type ApiHistory struct {
|
||||
Id string
|
||||
Created int64
|
||||
CreatedBy string
|
||||
CreatedBy string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ApiImages struct {
|
||||
@@ -14,13 +14,13 @@ type ApiImages struct {
|
||||
}
|
||||
|
||||
type ApiInfo struct {
|
||||
Containers int
|
||||
Version string
|
||||
Images int
|
||||
Debug bool
|
||||
GoVersion string
|
||||
NFd int `json:",omitempty"`
|
||||
NGoroutines int `json:",omitempty"`
|
||||
Containers int
|
||||
Images int
|
||||
NFd int `json:",omitempty"`
|
||||
NGoroutines int `json:",omitempty"`
|
||||
MemoryLimit bool `json:",omitempty"`
|
||||
SwapLimit bool `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ApiContainers struct {
|
||||
@@ -43,7 +43,7 @@ type ApiId struct {
|
||||
|
||||
type ApiRun struct {
|
||||
Id string
|
||||
Warnings []string
|
||||
Warnings []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ApiPort struct {
|
||||
@@ -51,10 +51,9 @@ type ApiPort struct {
|
||||
}
|
||||
|
||||
type ApiVersion struct {
|
||||
Version string
|
||||
GitCommit string
|
||||
MemoryLimit bool
|
||||
SwapLimit bool
|
||||
Version string
|
||||
GitCommit string `json:",omitempty"`
|
||||
GoVersion string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ApiWait struct {
|
||||
|
||||
@@ -106,8 +106,8 @@ func TestGetInfo(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if infos.Version != VERSION {
|
||||
t.Errorf("Excepted version %s, %s found", VERSION, infos.Version)
|
||||
if infos.Images != 1 {
|
||||
t.Errorf("Excepted images: %d, %d found", 1, infos.Images)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ func Tar(path string, compression Compression) (io.Reader, error) {
|
||||
func Untar(archive io.Reader, path string) error {
|
||||
cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
|
||||
cmd.Stdin = archive
|
||||
// Hardcode locale environment for predictable outcome regardless of host configuration.
|
||||
// (see https://github.com/dotcloud/docker/issues/355)
|
||||
cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", err, output)
|
||||
|
||||
@@ -61,7 +61,7 @@ func (b *buildFile) CmdFrom(name string) error {
|
||||
remote = name
|
||||
}
|
||||
|
||||
if err := b.srv.ImagePull(remote, tag, "", b.out, false); err != nil {
|
||||
if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
||||
file := filepath.Base(path)
|
||||
// If there is a whiteout, then the file was removed
|
||||
if strings.HasPrefix(file, ".wh.") {
|
||||
originalFile := strings.TrimLeft(file, ".wh.")
|
||||
originalFile := file[len(".wh."):]
|
||||
change.Path = filepath.Join(filepath.Dir(path), originalFile)
|
||||
change.Kind = ChangeDelete
|
||||
} else {
|
||||
|
||||
69
commands.go
69
commands.go
@@ -28,7 +28,7 @@ import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const VERSION = "0.3.4"
|
||||
const VERSION = "0.4.0"
|
||||
|
||||
var (
|
||||
GIT_COMMIT string
|
||||
@@ -102,7 +102,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
||||
{"stop", "Stop a running container"},
|
||||
{"tag", "Tag an image into a repository"},
|
||||
{"version", "Show the docker version information"},
|
||||
{"wait", "Block until a container stops}, then print its exit code"},
|
||||
{"wait", "Block until a container stops, then print its exit code"},
|
||||
} {
|
||||
help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1])
|
||||
}
|
||||
@@ -180,7 +180,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
return err
|
||||
} else {
|
||||
// FIXME: Find a way to have a progressbar for the upload too
|
||||
io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, "Caching Context %v/%v (%v)\r", false))
|
||||
sf := utils.NewStreamFormatter(false)
|
||||
io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf))
|
||||
}
|
||||
multipartBody = io.MultiReader(multipartBody, boundary)
|
||||
}
|
||||
@@ -391,15 +392,14 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
||||
utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("Version:", out.Version)
|
||||
fmt.Println("Git Commit:", out.GitCommit)
|
||||
if !out.MemoryLimit {
|
||||
fmt.Println("WARNING: No memory limit support")
|
||||
fmt.Println("Client version:", VERSION)
|
||||
fmt.Println("Server version:", out.Version)
|
||||
if out.GitCommit != "" {
|
||||
fmt.Println("Git commit:", out.GitCommit)
|
||||
}
|
||||
if !out.SwapLimit {
|
||||
fmt.Println("WARNING: No swap limit support")
|
||||
if out.GoVersion != "" {
|
||||
fmt.Println("Go version:", out.GoVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -420,14 +420,23 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
}
|
||||
|
||||
var out ApiInfo
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
if err := json.Unmarshal(body, &out); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("containers: %d\nversion: %s\nimages: %d\nGo version: %s\n", out.Containers, out.Version, out.Images, out.GoVersion)
|
||||
if out.Debug {
|
||||
fmt.Println("debug mode enabled")
|
||||
fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
|
||||
|
||||
fmt.Printf("Containers: %d\n", out.Containers)
|
||||
fmt.Printf("Images: %d\n", out.Images)
|
||||
if out.Debug || os.Getenv("DEBUG") != "" {
|
||||
fmt.Printf("Debug mode (server): %v\n", out.Debug)
|
||||
fmt.Printf("Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
|
||||
fmt.Printf("Fds: %d\n", out.NFd)
|
||||
fmt.Printf("Goroutines: %d\n", out.NGoroutines)
|
||||
}
|
||||
if !out.MemoryLimit {
|
||||
fmt.Println("WARNING: No memory limit support")
|
||||
}
|
||||
if !out.SwapLimit {
|
||||
fmt.Println("WARNING: No swap limit support")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -730,12 +739,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
||||
remote = remoteParts[0]
|
||||
}
|
||||
|
||||
if strings.Contains(remote, "/") {
|
||||
if _, err := cli.checkIfLogged(true, "pull"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("fromImage", remote)
|
||||
v.Set("tag", *tag)
|
||||
@@ -988,12 +991,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("logs", "1")
|
||||
v.Set("stdout", "1")
|
||||
v.Set("stderr", "1")
|
||||
|
||||
if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), false, nil, os.Stdout); err != nil {
|
||||
if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", nil, os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", nil, os.Stderr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -1369,13 +1370,9 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
|
||||
}
|
||||
|
||||
if resp.Header.Get("Content-Type") == "application/json" {
|
||||
type Message struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Progress string `json:"progress,omitempty"`
|
||||
}
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
for {
|
||||
var m Message
|
||||
var m utils.JsonMessage
|
||||
if err := dec.Decode(&m); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
@@ -1383,6 +1380,8 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
|
||||
}
|
||||
if m.Progress != "" {
|
||||
fmt.Fprintf(out, "Downloading %s\r", m.Progress)
|
||||
} else if m.Error != "" {
|
||||
return fmt.Errorf(m.Error)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n", m.Status)
|
||||
}
|
||||
@@ -1417,7 +1416,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
|
||||
return err
|
||||
})
|
||||
|
||||
if in != nil && setRawTerminal && term.IsTerminal(int(in.Fd())) && os.Getenv("NORAW") == "" {
|
||||
if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
|
||||
if oldState, err := term.SetRawTerminal(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
@@ -1436,7 +1435,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
|
||||
return err
|
||||
}
|
||||
|
||||
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
if !term.IsTerminal(os.Stdin.Fd()) {
|
||||
if err := <-sendStdin; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -217,6 +217,37 @@ func TestDiff(t *testing.T) {
|
||||
t.Fatalf("/etc/passwd should not be present in the diff after commit.")
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new containere
|
||||
container3, err := builder.Create(
|
||||
&Config{
|
||||
Image: GetTestImage(runtime).Id,
|
||||
Cmd: []string{"rm", "/bin/httpd"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer runtime.Destroy(container3)
|
||||
|
||||
if err := container3.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check the changelog
|
||||
c, err = container3.Changes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
success = false
|
||||
for _, elem := range c {
|
||||
if elem.Path == "/bin/httpd" && elem.Kind == 2 {
|
||||
success = true
|
||||
}
|
||||
}
|
||||
if !success {
|
||||
t.Fatalf("/bin/httpd should be present in the diff after commit.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitAutoRun(t *testing.T) {
|
||||
|
||||
@@ -2,18 +2,15 @@
|
||||
set -e
|
||||
|
||||
# these should match the names found at http://www.debian.org/releases/
|
||||
stableSuite='squeeze'
|
||||
testingSuite='wheezy'
|
||||
stableSuite='wheezy'
|
||||
testingSuite='jessie'
|
||||
unstableSuite='sid'
|
||||
|
||||
# if suite is equal to this, it gets the "latest" tag
|
||||
latestSuite="$testingSuite"
|
||||
|
||||
variant='minbase'
|
||||
include='iproute,iputils-ping'
|
||||
|
||||
repo="$1"
|
||||
suite="${2:-$latestSuite}"
|
||||
suite="${2:-$stableSuite}"
|
||||
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
|
||||
|
||||
if [ ! "$repo" ]; then
|
||||
@@ -41,17 +38,14 @@ img=$(sudo tar -c . | docker import -)
|
||||
# tag suite
|
||||
docker tag $img $repo $suite
|
||||
|
||||
if [ "$suite" = "$latestSuite" ]; then
|
||||
# tag latest
|
||||
docker tag $img $repo latest
|
||||
fi
|
||||
|
||||
# test the image
|
||||
docker run -i -t $repo:$suite echo success
|
||||
|
||||
# unstable's version numbers match testing (since it's mostly just a sandbox for testing), so it doesn't get a version number tag
|
||||
if [ "$suite" != "$unstableSuite" -a "$suite" != 'unstable' ]; then
|
||||
# tag the specific version
|
||||
if [ "$suite" = "$stableSuite" -o "$suite" = 'stable' ]; then
|
||||
# tag latest
|
||||
docker tag $img $repo latest
|
||||
|
||||
# tag the specific debian release version
|
||||
ver=$(docker run $repo:$suite cat /etc/debian_version)
|
||||
docker tag $img $repo $ver
|
||||
fi
|
||||
|
||||
@@ -6,6 +6,7 @@ SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
PYTHON = python
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
@@ -38,6 +39,7 @@ help:
|
||||
# @echo " linkcheck to check all external links for integrity"
|
||||
# @echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " docs to build the docs and copy the static files to the outputdir"
|
||||
@echo " server to serve the docs in your browser under \`http://localhost:8000\`"
|
||||
@echo " publish to publish the app to dotcloud"
|
||||
|
||||
clean:
|
||||
@@ -49,6 +51,8 @@ docs:
|
||||
@echo
|
||||
@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html."
|
||||
|
||||
server:
|
||||
@cd $(BUILDDIR)/html; $(PYTHON) -m SimpleHTTPServer 8000
|
||||
|
||||
site:
|
||||
cp -r website $(BUILDDIR)/
|
||||
|
||||
@@ -14,20 +14,22 @@ Installation
|
||||
------------
|
||||
|
||||
* Work in your own fork of the code, we accept pull requests.
|
||||
* Install sphinx: ``pip install sphinx``
|
||||
* Install sphinx httpdomain contrib package ``sphinxcontrib-httpdomain``
|
||||
* Install sphinx: `pip install sphinx`
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinx`)
|
||||
* Install sphinx httpdomain contrib package: `pip install sphinxcontrib-httpdomain`
|
||||
* Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain`
|
||||
* If pip is not available you can probably install it using your favorite package manager as **python-pip**
|
||||
|
||||
Usage
|
||||
-----
|
||||
* change the .rst files with your favorite editor to your liking
|
||||
* run *make docs* to clean up old files and generate new ones
|
||||
* your static website can now be found in the _build dir
|
||||
* to preview what you have generated, cd into _build/html and then run 'python -m SimpleHTTPServer 8000'
|
||||
* Change the `.rst` files with your favorite editor to your liking.
|
||||
* Run `make docs` to clean up old files and generate new ones.
|
||||
* Your static website can now be found in the `_build` directory.
|
||||
* To preview what you have generated run `make server` and open <http://localhost:8000/> in your favorite browser.
|
||||
|
||||
Working using github's file editor
|
||||
Working using GitHub's file editor
|
||||
----------------------------------
|
||||
Alternatively, for small changes and typo's you might want to use github's built in file editor. It allows
|
||||
Alternatively, for small changes and typo's you might want to use GitHub's built in file editor. It allows
|
||||
you to preview your changes right online. Just be carefull not to create many commits.
|
||||
|
||||
Images
|
||||
@@ -72,4 +74,4 @@ Guides on using sphinx
|
||||
|
||||
* Code examples
|
||||
|
||||
Start without $, so it's easy to copy and paste.
|
||||
Start without $, so it's easy to copy and paste.
|
||||
|
||||
@@ -15,10 +15,17 @@ Docker Remote API
|
||||
- Default port in the docker deamon is 4243
|
||||
- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr
|
||||
|
||||
2. Endpoints
|
||||
2. Version
|
||||
==========
|
||||
|
||||
The current verson of the API is 1.1
|
||||
Calling /images/<name>/insert is the same as calling /v1.1/images/<name>/insert
|
||||
You can still call an old version of the api using /v1.0/images/<name>/insert
|
||||
|
||||
3. Endpoints
|
||||
============
|
||||
|
||||
2.1 Containers
|
||||
3.1 Containers
|
||||
--------------
|
||||
|
||||
List containers
|
||||
@@ -132,6 +139,7 @@ Create a container
|
||||
:jsonparam config: the container's configuration
|
||||
:statuscode 201: no error
|
||||
:statuscode 404: no such container
|
||||
:statuscode 406: impossible to attach (container not running)
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
@@ -459,7 +467,7 @@ Remove a container
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
2.2 Images
|
||||
3.2 Images
|
||||
----------
|
||||
|
||||
List Images
|
||||
@@ -548,7 +556,19 @@ Create an image
|
||||
|
||||
POST /images/create?fromImage=base HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
**Example response v1.1**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status":"Pulling..."}
|
||||
{"progress":"1/? (n/a)"}
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
**Example response v1.0**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
@@ -579,7 +599,19 @@ Insert a file in a image
|
||||
|
||||
POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
**Example response v1.1**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status":"Inserting..."}
|
||||
{"progress":"1/? (n/a)"}
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
**Example response v1.0**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
@@ -694,7 +726,19 @@ Push an image on the registry
|
||||
|
||||
POST /images/test/push HTTP/1.1
|
||||
|
||||
**Example response**:
|
||||
**Example response v1.1**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"status":"Pushing..."}
|
||||
{"progress":"1/? (n/a)"}
|
||||
{"error":"Invalid..."}
|
||||
...
|
||||
|
||||
**Example response v1.0**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
@@ -800,7 +844,7 @@ Search images
|
||||
:statuscode 500: server error
|
||||
|
||||
|
||||
2.3 Misc
|
||||
3.3 Misc
|
||||
--------
|
||||
|
||||
Build an image from Dockerfile via stdin
|
||||
@@ -826,6 +870,7 @@ Build an image from Dockerfile via stdin
|
||||
|
||||
{{ STREAM }}
|
||||
|
||||
:query t: tag to be applied to the resulting image in case of success
|
||||
:statuscode 200: no error
|
||||
:statuscode 500: server error
|
||||
|
||||
@@ -912,10 +957,12 @@ Display system-wide information
|
||||
|
||||
{
|
||||
"Containers":11,
|
||||
"Version":"0.2.2",
|
||||
"Images":16,
|
||||
"GoVersion":"go1.0.3",
|
||||
"Debug":false
|
||||
"Debug":false,
|
||||
"NFd": 11,
|
||||
"NGoroutines":21,
|
||||
"MemoryLimit":true,
|
||||
"SwapLimit":false
|
||||
}
|
||||
|
||||
:statuscode 200: no error
|
||||
@@ -941,12 +988,11 @@ Show the docker version information
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
{
|
||||
"Version":"0.2.2",
|
||||
"GitCommit":"5a2a5cc+CHANGES",
|
||||
"MemoryLimit":true,
|
||||
"SwapLimit":false
|
||||
"GoVersion":"go1.0.3"
|
||||
}
|
||||
|
||||
:statuscode 200: no error
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
:description: docker documentation
|
||||
:keywords: docker, ipa, documentation
|
||||
|
||||
API's
|
||||
=============
|
||||
APIs
|
||||
====
|
||||
|
||||
This following :
|
||||
|
||||
|
||||
@@ -246,7 +246,6 @@ The Index has two main purposes (along with its fancy social features):
|
||||
|
||||
- Resolve short names (to avoid passing absolute URLs all the time)
|
||||
- username/projectname -> \https://registry.docker.io/users/<username>/repositories/<projectname>/
|
||||
- team/projectname -> \https://registry.docker.io/team/<team>/repositories/<projectname>/
|
||||
- Authenticate a user as a repos owner (for a central referenced repository)
|
||||
|
||||
3.1 Without an Index
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
Docker - The Linux container runtime
|
||||
------------------------------------
|
||||
Docker -- The Linux container runtime
|
||||
-------------------------------------
|
||||
|
||||
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
:title: Setting up a dev environment
|
||||
:title: Setting Up a Dev Environment
|
||||
:description: Guides on how to contribute to docker
|
||||
:keywords: Docker, documentation, developers, contributing, dev environment
|
||||
|
||||
Setting up a dev environment
|
||||
Setting Up a Dev Environment
|
||||
============================
|
||||
|
||||
Instructions that have been verified to work on Ubuntu 12.10,
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
.. _running_couchdb_service:
|
||||
|
||||
Create a CouchDB service
|
||||
========================
|
||||
CouchDB Service
|
||||
===============
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:title: Docker Examples
|
||||
:description: Examples on how to use Docker
|
||||
:keywords: docker, hello world, examples
|
||||
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ Contents:
|
||||
hello_world
|
||||
hello_world_daemon
|
||||
python_web_app
|
||||
nodejs_web_app
|
||||
running_redis_service
|
||||
running_ssh_service
|
||||
couchdb_data_volumes
|
||||
|
||||
236
docs/sources/examples/nodejs_web_app.rst
Normal file
236
docs/sources/examples/nodejs_web_app.rst
Normal file
@@ -0,0 +1,236 @@
|
||||
:title: Running a Node.js app on CentOS
|
||||
:description: Installing and running a Node.js app on CentOS
|
||||
:keywords: docker, example, package installation, node, centos
|
||||
|
||||
.. _nodejs_web_app:
|
||||
|
||||
Node.js Web App
|
||||
===============
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
The goal of this example is to show you how you can build your own docker images
|
||||
from a parent image using a ``Dockerfile`` . We will do that by making a simple
|
||||
Node.js hello world web application running on CentOS. You can get the full
|
||||
source code at https://github.com/gasi/docker-node-hello.
|
||||
|
||||
Create Node.js app
|
||||
++++++++++++++++++
|
||||
|
||||
First, create a ``package.json`` file that describes your app and its
|
||||
dependencies:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"name": "docker-centos-hello",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "Node.js Hello World app on CentOS using docker",
|
||||
"author": "Daniel Gasienica <daniel@gasienica.ch>",
|
||||
"dependencies": {
|
||||
"express": "3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
Then, create an ``index.js`` file that defines a web app using the
|
||||
`Express.js <http://expressjs.com/>`_ framework:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
var express = require('express');
|
||||
|
||||
// Constants
|
||||
var PORT = 8080;
|
||||
|
||||
// App
|
||||
var app = express();
|
||||
app.get('/', function (req, res) {
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.listen(PORT)
|
||||
console.log('Running on http://localhost:' + PORT);
|
||||
|
||||
|
||||
In the next steps, we’ll look at how you can run this app inside a CentOS
|
||||
container using docker. First, you’ll need to build a docker image of your app.
|
||||
|
||||
Creating a ``Dockerfile``
|
||||
+++++++++++++++++++++++++
|
||||
|
||||
Create an empty file called ``Dockerfile``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
touch Dockerfile
|
||||
|
||||
Open the ``Dockerfile`` in your favorite text editor and add the following line
|
||||
that defines the version of docker the image requires to build
|
||||
(this example uses docker 0.3.4):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# DOCKER-VERSION 0.3.4
|
||||
|
||||
Next, define the parent image you want to use to build your own image on top of.
|
||||
Here, we’ll use `CentOS <https://index.docker.io/_/centos/>`_ (tag: ``6.4``)
|
||||
available on the `docker index`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
FROM centos:6.4
|
||||
|
||||
Since we’re building a Node.js app, you’ll have to install Node.js as well as
|
||||
npm on your CentOS image. Node.js is required to run your app and npm to install
|
||||
your app’s dependencies defined in ``package.json``.
|
||||
To install the right package for CentOS, we’ll use the instructions from the
|
||||
`Node.js wiki`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Enable EPEL for Node.js
|
||||
RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
|
||||
# Install Node.js and npm
|
||||
RUN yum install -y npm-1.2.17-5.el6
|
||||
|
||||
To bundle your app’s source code inside the docker image, use the ``ADD``
|
||||
command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Bundle app source
|
||||
ADD . /src
|
||||
|
||||
Install your app dependencies using npm:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Install app dependencies
|
||||
RUN cd /src; npm install
|
||||
|
||||
Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` command to have it
|
||||
mapped by the docker daemon:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
Last but not least, define the command to run your app using ``CMD`` which
|
||||
defines your runtime, i.e. ``node``, and the path to our app, i.e.
|
||||
``src/index.js`` (see the step where we added the source to the container):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
CMD ["node", "/src/index.js"]
|
||||
|
||||
Your ``Dockerfile`` should now look like this:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
||||
# DOCKER-VERSION 0.3.4
|
||||
FROM centos:6.4
|
||||
|
||||
# Enable EPEL for Node.js
|
||||
RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
|
||||
# Install Node.js and npm
|
||||
RUN yum install -y npm-1.2.17-5.el6
|
||||
|
||||
# Bundle app source
|
||||
ADD . /src
|
||||
# Install app dependencies
|
||||
RUN cd /src; npm install
|
||||
|
||||
EXPOSE 8080
|
||||
CMD ["node", "/src/index.js"]
|
||||
|
||||
|
||||
Building your image
|
||||
+++++++++++++++++++
|
||||
|
||||
Go to the directory that has your ``Dockerfile`` and run the following command
|
||||
to build a docker image. The ``-t`` flag let’s you tag your image so it’s easier
|
||||
to find later using the ``docker images`` command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker build -t <your username>/centos-node-hello .
|
||||
|
||||
Your image will now be listed by docker:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker images
|
||||
|
||||
> # Example
|
||||
> REPOSITORY TAG ID CREATED
|
||||
> centos 6.4 539c0211cd76 8 weeks ago
|
||||
> gasi/centos-node-hello latest d64d3505b0d2 2 hours ago
|
||||
|
||||
|
||||
Run the image
|
||||
+++++++++++++
|
||||
|
||||
Running your image with ``-d`` runs the container in detached mode, leaving the
|
||||
container running in the background. Run the image you previously built:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -d <your username>/centos-node-hello
|
||||
|
||||
Print the output of your app:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Get container ID
|
||||
docker ps
|
||||
|
||||
# Print app output
|
||||
docker logs <container id>
|
||||
|
||||
> # Example
|
||||
> Running on http://localhost:8080
|
||||
|
||||
|
||||
Test
|
||||
++++
|
||||
|
||||
To test your app, get the the port of your app that docker mapped:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker ps
|
||||
|
||||
> # Example
|
||||
> ID IMAGE COMMAND ... PORTS
|
||||
> ecce33b30ebf gasi/centos-node-hello:latest node /src/index.js 49160->8080
|
||||
|
||||
In the example above, docker mapped the ``8080`` port of the container to
|
||||
``49160``.
|
||||
|
||||
Now you can call your app using ``curl`` (install if needed via:
|
||||
``sudo apt-get install curl``):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -i localhost:49160
|
||||
|
||||
> HTTP/1.1 200 OK
|
||||
> X-Powered-By: Express
|
||||
> Content-Type: text/html; charset=utf-8
|
||||
> Content-Length: 12
|
||||
> Date: Sun, 02 Jun 2013 03:53:22 GMT
|
||||
> Connection: keep-alive
|
||||
>
|
||||
> Hello World
|
||||
|
||||
We hope this tutorial helped you get up and running with Node.js and CentOS on
|
||||
docker. You can get the full source code at
|
||||
https://github.com/gasi/docker-node-hello.
|
||||
|
||||
Continue to :ref:`running_redis_service`.
|
||||
|
||||
|
||||
.. _Node.js wiki: https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#rhelcentosscientific-linux-6
|
||||
.. _docker index: https://index.docker.io/
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
.. _python_web_app:
|
||||
|
||||
Building a python web app
|
||||
=========================
|
||||
Python Web App
|
||||
==============
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
.. _running_examples:
|
||||
|
||||
Running The Examples
|
||||
Running the Examples
|
||||
--------------------
|
||||
|
||||
All the examples assume your machine is running the docker daemon. To run the docker daemon in the background, simply type:
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
.. _running_redis_service:
|
||||
|
||||
Create a redis service
|
||||
======================
|
||||
Redis Service
|
||||
=============
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
@@ -34,7 +34,7 @@ Snapshot the installation
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker ps -a # grab the container id (this will be the last one in the list)
|
||||
docker ps -a # grab the container id (this will be the first one in the list)
|
||||
docker commit <container_id> <your username>/redis
|
||||
|
||||
Run the service
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
.. _running_ssh_service:
|
||||
|
||||
Create an ssh daemon service
|
||||
============================
|
||||
SSH Daemon Service
|
||||
==================
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
@@ -20,8 +20,7 @@ minutes and not entirely smooth, but gives you a good idea.
|
||||
<div style="margin-top:10px;">
|
||||
<iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
You can also get this sshd container by using
|
||||
::
|
||||
|
||||
@@ -30,3 +29,49 @@ You can also get this sshd container by using
|
||||
|
||||
The password is 'screencast'
|
||||
|
||||
**Video's Transcription:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Hello! We are going to try and install openssh on a container and run it as a servic
|
||||
# let's pull base to get a base ubuntu image.
|
||||
$ docker pull base
|
||||
# I had it so it was quick
|
||||
# now let's connect using -i for interactive and with -t for terminal
|
||||
# we execute /bin/bash to get a prompt.
|
||||
$ docker run -i -t base /bin/bash
|
||||
# now let's commit it
|
||||
# which container was it?
|
||||
$ docker ps -a |more
|
||||
$ docker commit a30a3a2f2b130749995f5902f079dc6ad31ea0621fac595128ec59c6da07feea dhrp/sshd
|
||||
# I gave the name dhrp/sshd for the container
|
||||
# now we can run it again
|
||||
$ docker run -d dhrp/sshd /usr/sbin/sshd -D # D for daemon mode
|
||||
# is it running?
|
||||
$ docker ps
|
||||
# yes!
|
||||
# let's stop it
|
||||
$ docker stop 0ebf7cec294755399d063f4b1627980d4cbff7d999f0bc82b59c300f8536a562
|
||||
$ docker ps
|
||||
# and reconnect, but now open a port to it
|
||||
$ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D
|
||||
$ docker port b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 22
|
||||
# it has now given us a port to connect to
|
||||
# we have to connect using a public ip of our host
|
||||
$ hostname
|
||||
$ ifconfig
|
||||
$ ssh root@192.168.33.10 -p 49153
|
||||
# Ah! forgot to set root passwd
|
||||
$ docker commit b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 dhrp/sshd
|
||||
$ docker ps -a
|
||||
$ docker run -i -t dhrp/sshd /bin/bash
|
||||
$ passwd
|
||||
$ exit
|
||||
$ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd
|
||||
$ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D
|
||||
$ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22
|
||||
$ ifconfig
|
||||
$ ssh root@192.168.33.10 -p 49154
|
||||
# Thanks for watching, Thatcher thatcher@dotcloud.com
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
Docker - The Linux container runtime
|
||||
------------------------------------
|
||||
Docker -- The Linux container runtime
|
||||
-------------------------------------
|
||||
|
||||
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
:keywords: Examples, Usage, basic commands, docker, documentation, examples
|
||||
|
||||
|
||||
The basics
|
||||
=============
|
||||
The Basics
|
||||
==========
|
||||
|
||||
Starting Docker
|
||||
---------------
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
.. _working_with_the_repository:
|
||||
|
||||
Working with the repository
|
||||
============================
|
||||
Working with the Repository
|
||||
===========================
|
||||
|
||||
|
||||
Top-level repositories and user repositories
|
||||
@@ -14,9 +14,9 @@ Top-level repositories and user repositories
|
||||
Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind
|
||||
Docker, and user repositories.
|
||||
|
||||
* Top-level repositories can easily be recognized by not having a / (slash) in their name. These repositories can
|
||||
* Top-level repositories can easily be recognized by not having a ``/`` (slash) in their name. These repositories can
|
||||
generally be trusted.
|
||||
* User repositories always come in the form of <username>/<repo_name>. This is what your published images will look like.
|
||||
* User repositories always come in the form of ``<username>/<repo_name>``. This is what your published images will look like.
|
||||
* User images are not checked, it is therefore up to you whether or not you trust the creator of this image.
|
||||
|
||||
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
<li>Filesystem isolation: each process container runs in a completely separate root filesystem.</li>
|
||||
<li>Resource isolation: system resources like cpu and memory can be allocated differently to each process container, using cgroups.</li>
|
||||
<li>Network isolation: each process container runs in its own network namespace, with a virtual interface and IP address of its own.</li>
|
||||
<li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremeley fast, memory-cheap and disk-cheap.</li>
|
||||
<li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremely fast, memory-cheap and disk-cheap.</li>
|
||||
<li>Logging: the standard streams (stdout/stderr/stdin) of each process container is collected and logged for real-time or batch retrieval.</li>
|
||||
<li>Change management: changes to a container's filesystem can be committed into a new image and re-used to create more containers. No templating or manual configuration required.</li>
|
||||
<li>Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throwaway interactive shell.</li>
|
||||
|
||||
4
graph.go
4
graph.go
@@ -107,6 +107,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
|
||||
DockerVersion: VERSION,
|
||||
Author: author,
|
||||
Config: config,
|
||||
Architecture: "x86_64",
|
||||
}
|
||||
if container != nil {
|
||||
img.Parent = container.Image
|
||||
@@ -165,7 +166,8 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root)
|
||||
sf := utils.NewStreamFormatter(false)
|
||||
return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, sf.FormatProgress("Buffering to disk", "%v/%v (%v)"), sf), tmp.Root)
|
||||
}
|
||||
|
||||
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
# This will build a container capable of producing an official binary build of docker and
|
||||
# uploading it to S3
|
||||
from ubuntu:12.04
|
||||
maintainer Solomon Hykes <solomon@dotcloud.com>
|
||||
from ubuntu:12.10
|
||||
# Workaround the upstart issue
|
||||
run dpkg-divert --local --rename --add /sbin/initctl
|
||||
run ln -s /bin/true /sbin/initctl
|
||||
# Enable universe and gophers PPA
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q python-software-properties
|
||||
run add-apt-repository "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe"
|
||||
run add-apt-repository -y ppa:gophers/go/ubuntu
|
||||
run apt-get update
|
||||
# Packages required to checkout, build and upload docker
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
|
||||
# Packages required to checkout and build docker
|
||||
run curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.linux-amd64.tar.gz
|
||||
run tar -C /usr/local -xzf /go.tar.gz
|
||||
run echo "export PATH=$PATH:/usr/local/go/bin" > /.bashrc
|
||||
run echo "export PATH=$PATH:/usr/local/go/bin" > /.bash_profile
|
||||
run echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc
|
||||
run echo "export PATH=/usr/local/go/bin:$PATH" > /.bash_profile
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential
|
||||
# Packages required to build an ubuntu package
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang-stable
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q debhelper
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q autotools-dev
|
||||
copy fake_initctl /usr/local/bin/initctl
|
||||
run apt-get install -y -q devscripts
|
||||
add . /src
|
||||
# Copy dockerbuilder files into the container
|
||||
add . /src
|
||||
run cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
|
||||
run cp /src/s3cfg /.s3cfg
|
||||
cmd ["dockerbuilder"]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -x
|
||||
set -e
|
||||
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
export PATH=/usr/local/go/bin:$PATH
|
||||
|
||||
PACKAGE=github.com/dotcloud/docker
|
||||
|
||||
@@ -36,5 +36,6 @@ else
|
||||
fi
|
||||
|
||||
if [ -z "$NO_UBUNTU" ]; then
|
||||
export PATH=`echo $PATH | sed 's#/usr/local/go/bin:##g'`
|
||||
(cd packaging/ubuntu && make ubuntu)
|
||||
fi
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo Whatever you say, man
|
||||
2
hack/infrastructure/MAINTAINERS
Normal file
2
hack/infrastructure/MAINTAINERS
Normal file
@@ -0,0 +1,2 @@
|
||||
Ken Cochrane <ken@dotcloud.com>
|
||||
Jerome Petazzoni <jerome@dotcloud.com>
|
||||
5
hack/infrastructure/README.md
Normal file
5
hack/infrastructure/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Docker project infrastructure
|
||||
|
||||
This directory holds all information about the technical infrastructure of the docker project; servers, dns, email, and all the corresponding tools and configuration.
|
||||
|
||||
Obviously credentials should not be stored in this repo, but how to obtain and use them should be documented here.
|
||||
1
image.go
1
image.go
@@ -27,6 +27,7 @@ type Image struct {
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Config *Config `json:"config,omitempty"`
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
graph *Graph
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
lxc-docker (0.4.0-1) precise; urgency=low
|
||||
- Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
|
||||
- Introducing Remote API: control Docker programmatically using a simple HTTP/json API
|
||||
- Runtime: various reliability and usability improvements
|
||||
|
||||
-- dotCloud <ops@dotcloud.com> Mon, 03 Jun 2013 00:00:00 -0700
|
||||
|
||||
lxc-docker (0.3.4-1) precise; urgency=low
|
||||
- Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
|
||||
- Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.
|
||||
|
||||
@@ -5,6 +5,5 @@ stop on starting rc RUNLEVEL=[016]
|
||||
respawn
|
||||
|
||||
script
|
||||
# FIXME: docker should not depend on the system having en_US.UTF-8
|
||||
LC_ALL='en_US.UTF-8' /usr/bin/docker -d
|
||||
/usr/bin/docker -d
|
||||
end script
|
||||
|
||||
@@ -68,7 +68,7 @@ func init() {
|
||||
runtime: runtime,
|
||||
}
|
||||
// Retrieve the Image
|
||||
if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, false); err != nil {
|
||||
if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
102
server.go
102
server.go
@@ -17,7 +17,11 @@ import (
|
||||
)
|
||||
|
||||
func (srv *Server) DockerVersion() ApiVersion {
|
||||
return ApiVersion{VERSION, GIT_COMMIT, srv.runtime.capabilities.MemoryLimit, srv.runtime.capabilities.SwapLimit}
|
||||
return ApiVersion{
|
||||
Version: VERSION,
|
||||
GitCommit: GIT_COMMIT,
|
||||
GoVersion: runtime.Version(),
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) ContainerKill(name string) error {
|
||||
@@ -68,7 +72,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
|
||||
return outs, nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, error) {
|
||||
func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
img, err := srv.runtime.repositories.LookupImage(name)
|
||||
if err != nil {
|
||||
@@ -92,7 +96,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil {
|
||||
if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// FIXME: Handle custom repo, tag comment, author
|
||||
@@ -100,7 +104,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", img.Id)
|
||||
out.Write(sf.FormatStatus(img.Id))
|
||||
return img.ShortId(), nil
|
||||
}
|
||||
|
||||
@@ -187,7 +191,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
|
||||
return outs, nil
|
||||
}
|
||||
|
||||
func (srv *Server) DockerInfo() ApiInfo {
|
||||
func (srv *Server) DockerInfo() *ApiInfo {
|
||||
images, _ := srv.runtime.graph.All()
|
||||
var imgcount int
|
||||
if images == nil {
|
||||
@@ -195,17 +199,15 @@ func (srv *Server) DockerInfo() ApiInfo {
|
||||
} else {
|
||||
imgcount = len(images)
|
||||
}
|
||||
var out ApiInfo
|
||||
out.Containers = len(srv.runtime.List())
|
||||
out.Version = VERSION
|
||||
out.Images = imgcount
|
||||
out.GoVersion = runtime.Version()
|
||||
if os.Getenv("DEBUG") != "" {
|
||||
out.Debug = true
|
||||
out.NFd = utils.GetTotalUsedFds()
|
||||
out.NGoroutines = runtime.NumGoroutine()
|
||||
return &ApiInfo{
|
||||
Containers: len(srv.runtime.List()),
|
||||
Images: imgcount,
|
||||
MemoryLimit: srv.runtime.capabilities.MemoryLimit,
|
||||
SwapLimit: srv.runtime.capabilities.SwapLimit,
|
||||
Debug: os.Getenv("DEBUG") != "",
|
||||
NFd: utils.GetTotalUsedFds(),
|
||||
NGoroutines: runtime.NumGoroutine(),
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
|
||||
@@ -292,7 +294,7 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoint string, token []string, json bool) error {
|
||||
func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoint string, token []string, sf *utils.StreamFormatter) error {
|
||||
history, err := r.GetRemoteHistory(imgId, endpoint, token)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -302,7 +304,7 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
|
||||
// FIXME: Launch the getRemoteImage() in goroutines
|
||||
for _, id := range history {
|
||||
if !srv.runtime.graph.Exists(id) {
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id)
|
||||
out.Write(sf.FormatStatus("Pulling %s metadata", id))
|
||||
imgJson, err := r.GetRemoteImageJson(id, endpoint, token)
|
||||
if err != nil {
|
||||
// FIXME: Keep goging in case of error?
|
||||
@@ -314,12 +316,12 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
|
||||
}
|
||||
|
||||
// Get the layer
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id)
|
||||
out.Write(sf.FormatStatus("Pulling %s fs layer", id))
|
||||
layer, contentLength, err := r.GetRemoteImageLayer(img.Id, endpoint, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil {
|
||||
if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), false, img); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -327,8 +329,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, json bool) error {
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
|
||||
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error {
|
||||
out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress()))
|
||||
repoData, err := r.GetRepositoryData(remote)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -365,11 +367,11 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
|
||||
utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling image %s (%s) from %s", json), img.Id, img.Tag, remote)
|
||||
out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", img.Id, img.Tag, remote))
|
||||
success := false
|
||||
for _, ep := range repoData.Endpoints {
|
||||
if err := srv.pullImage(r, out, img.Id, "https://"+ep+"/v1", repoData.Tokens, json); err != nil {
|
||||
fmt.Fprintf(out, utils.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint\n", json), askedTag, err)
|
||||
if err := srv.pullImage(r, out, img.Id, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil {
|
||||
out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err))
|
||||
continue
|
||||
}
|
||||
success = true
|
||||
@@ -394,17 +396,17 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, json bool) error {
|
||||
func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
|
||||
r := registry.NewRegistry(srv.runtime.root)
|
||||
out = utils.NewWriteFlusher(out)
|
||||
if endpoint != "" {
|
||||
if err := srv.pullImage(r, out, name, endpoint, nil, json); err != nil {
|
||||
if err := srv.pullImage(r, out, name, endpoint, nil, sf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := srv.pullRepository(r, out, name, tag, json); err != nil {
|
||||
if err := srv.pullRepository(r, out, name, tag, sf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -477,14 +479,14 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
|
||||
return imgList, nil
|
||||
}
|
||||
|
||||
func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name string, localRepo map[string]string) error {
|
||||
func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name string, localRepo map[string]string, sf *utils.StreamFormatter) error {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
fmt.Fprintf(out, "Processing checksums\n")
|
||||
out.Write(sf.FormatStatus("Processing checksums"))
|
||||
imgList, err := srv.getImageList(localRepo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "Sending images list\n")
|
||||
out.Write(sf.FormatStatus("Sending image list"))
|
||||
|
||||
repoData, err := r.PushImageJsonIndex(name, imgList, false)
|
||||
if err != nil {
|
||||
@@ -492,18 +494,18 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
|
||||
}
|
||||
|
||||
for _, ep := range repoData.Endpoints {
|
||||
fmt.Fprintf(out, "Pushing repository %s to %s (%d tags)\r\n", name, ep, len(localRepo))
|
||||
out.Write(sf.FormatStatus("Pushing repository %s to %s (%d tags)", name, ep, len(localRepo)))
|
||||
// For each image within the repo, push them
|
||||
for _, elem := range imgList {
|
||||
if _, exists := repoData.ImgList[elem.Id]; exists {
|
||||
fmt.Fprintf(out, "Image %s already on registry, skipping\n", name)
|
||||
out.Write(sf.FormatStatus("Image %s already on registry, skipping", name))
|
||||
continue
|
||||
}
|
||||
if err := srv.pushImage(r, out, name, elem.Id, ep, repoData.Tokens); err != nil {
|
||||
if err := srv.pushImage(r, out, name, elem.Id, ep, repoData.Tokens, sf); err != nil {
|
||||
// FIXME: Continue on error?
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "Pushing tags for rev [%s] on {%s}\n", elem.Id, ep+"/users/"+name+"/"+elem.Tag)
|
||||
out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.Id, ep+"/users/"+name+"/"+elem.Tag))
|
||||
if err := r.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -516,13 +518,13 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, ep string, token []string) error {
|
||||
func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, ep string, token []string, sf *utils.StreamFormatter) error {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err)
|
||||
}
|
||||
fmt.Fprintf(out, "Pushing %s\r\n", imgId)
|
||||
out.Write(sf.FormatStatus("Pushing %s", imgId))
|
||||
|
||||
// Make sure we have the image's checksum
|
||||
checksum, err := srv.getChecksum(imgId)
|
||||
@@ -537,7 +539,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
|
||||
// Send the json
|
||||
if err := r.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil {
|
||||
if err == registry.ErrAlreadyExists {
|
||||
fmt.Fprintf(out, "Image %s already uploaded ; skipping\n", imgData.Id)
|
||||
out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.Id))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@@ -570,22 +572,22 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
|
||||
}
|
||||
|
||||
// Send the layer
|
||||
if err := r.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, "", false), ep, token); err != nil {
|
||||
if err := r.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("", "%v/%v (%v)"), sf), ep, token); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImagePush(name, endpoint string, out io.Writer) error {
|
||||
func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
img, err := srv.runtime.graph.Get(name)
|
||||
r := registry.NewRegistry(srv.runtime.root)
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name]))
|
||||
out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", name, len(srv.runtime.repositories.Repositories[name])))
|
||||
// If it fails, try to get the repository
|
||||
if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
|
||||
if err := srv.pushRepository(r, out, name, localRepo); err != nil {
|
||||
if err := srv.pushRepository(r, out, name, localRepo, sf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -593,14 +595,14 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer) error {
|
||||
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "The push refers to an image: [%s]\n", name)
|
||||
if err := srv.pushImage(r, out, name, img.Id, endpoint, nil); err != nil {
|
||||
out.Write(sf.FormatStatus("The push refers to an image: [%s]", name))
|
||||
if err := srv.pushImage(r, out, name, img.Id, endpoint, nil, sf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer) error {
|
||||
func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer, sf *utils.StreamFormatter) error {
|
||||
var archive io.Reader
|
||||
var resp *http.Response
|
||||
|
||||
@@ -609,21 +611,21 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
|
||||
} else {
|
||||
u, err := url.Parse(src)
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "Error: %s\n", err)
|
||||
return err
|
||||
}
|
||||
if u.Scheme == "" {
|
||||
u.Scheme = "http"
|
||||
u.Host = src
|
||||
u.Path = ""
|
||||
}
|
||||
fmt.Fprintf(out, "Downloading from %s\n", u)
|
||||
out.Write(sf.FormatStatus("Downloading from %s", u))
|
||||
// Download with curl (pretty progress bar)
|
||||
// If curl is not available, fallback to http.Get()
|
||||
resp, err = utils.Download(u.String(), out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)\r", false)
|
||||
archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%v/%v (%v)"), sf)
|
||||
}
|
||||
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
|
||||
if err != nil {
|
||||
@@ -635,7 +637,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", img.ShortId())
|
||||
out.Write(sf.FormatStatus(img.ShortId()))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -790,7 +792,6 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
|
||||
if container == nil {
|
||||
return fmt.Errorf("No such container: %s", name)
|
||||
}
|
||||
|
||||
//logs
|
||||
if logs {
|
||||
if stdout {
|
||||
@@ -816,6 +817,9 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
|
||||
if container.State.Ghost {
|
||||
return fmt.Errorf("Impossible to attach to a ghost container")
|
||||
}
|
||||
if !container.State.Running {
|
||||
return fmt.Errorf("Impossible to attach to a stopped container, start it first")
|
||||
}
|
||||
|
||||
var (
|
||||
cStdin io.ReadCloser
|
||||
|
||||
2
term/MAINTAINERS
Normal file
2
term/MAINTAINERS
Normal file
@@ -0,0 +1,2 @@
|
||||
Guillaume Charmes <guillaume@dotcloud.com>
|
||||
Solomon Hykes <solomon@dotcloud.com>
|
||||
112
term/term.go
112
term/term.go
@@ -7,104 +7,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Termios struct {
|
||||
Iflag uintptr
|
||||
Oflag uintptr
|
||||
Cflag uintptr
|
||||
Lflag uintptr
|
||||
Cc [20]byte
|
||||
Ispeed uintptr
|
||||
Ospeed uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
// Input flags
|
||||
inpck = 0x010
|
||||
istrip = 0x020
|
||||
icrnl = 0x100
|
||||
ixon = 0x200
|
||||
|
||||
// Output flags
|
||||
opost = 0x1
|
||||
|
||||
// Control flags
|
||||
cs8 = 0x300
|
||||
|
||||
// Local flags
|
||||
icanon = 0x100
|
||||
iexten = 0x400
|
||||
)
|
||||
|
||||
const (
|
||||
HUPCL = 0x4000
|
||||
ICANON = 0x100
|
||||
ICRNL = 0x100
|
||||
IEXTEN = 0x400
|
||||
BRKINT = 0x2
|
||||
CFLUSH = 0xf
|
||||
CLOCAL = 0x8000
|
||||
CREAD = 0x800
|
||||
CS5 = 0x0
|
||||
CS6 = 0x100
|
||||
CS7 = 0x200
|
||||
CS8 = 0x300
|
||||
CSIZE = 0x300
|
||||
CSTART = 0x11
|
||||
CSTATUS = 0x14
|
||||
CSTOP = 0x13
|
||||
CSTOPB = 0x400
|
||||
CSUSP = 0x1a
|
||||
IGNBRK = 0x1
|
||||
IGNCR = 0x80
|
||||
IGNPAR = 0x4
|
||||
IMAXBEL = 0x2000
|
||||
INLCR = 0x40
|
||||
INPCK = 0x10
|
||||
ISIG = 0x80
|
||||
ISTRIP = 0x20
|
||||
IUTF8 = 0x4000
|
||||
IXANY = 0x800
|
||||
IXOFF = 0x400
|
||||
IXON = 0x200
|
||||
NOFLSH = 0x80000000
|
||||
OCRNL = 0x10
|
||||
OFDEL = 0x20000
|
||||
OFILL = 0x80
|
||||
ONLCR = 0x2
|
||||
ONLRET = 0x40
|
||||
ONOCR = 0x20
|
||||
ONOEOT = 0x8
|
||||
OPOST = 0x1
|
||||
RENB = 0x1000
|
||||
PARMRK = 0x8
|
||||
PARODD = 0x2000
|
||||
|
||||
TOSTOP = 0x400000
|
||||
VDISCARD = 0xf
|
||||
VDSUSP = 0xb
|
||||
VEOF = 0x0
|
||||
VEOL = 0x1
|
||||
VEOL2 = 0x2
|
||||
VERASE = 0x3
|
||||
VINTR = 0x8
|
||||
VKILL = 0x5
|
||||
VLNEXT = 0xe
|
||||
VMIN = 0x10
|
||||
VQUIT = 0x9
|
||||
VREPRINT = 0x6
|
||||
VSTART = 0xc
|
||||
VSTATUS = 0x12
|
||||
VSTOP = 0xd
|
||||
VSUSP = 0xa
|
||||
VT0 = 0x0
|
||||
VT1 = 0x10000
|
||||
VTDLY = 0x10000
|
||||
VTIME = 0x11
|
||||
ECHO = 0x00000008
|
||||
|
||||
PENDIN = 0x20000000
|
||||
)
|
||||
|
||||
type State struct {
|
||||
termios Termios
|
||||
}
|
||||
@@ -128,21 +30,21 @@ func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termios Termios
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
|
||||
return err == 0
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) error {
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
|
||||
func Restore(fd uintptr, state *State) error {
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
|
||||
return err
|
||||
}
|
||||
|
||||
func SetRawTerminal() (*State, error) {
|
||||
oldState, err := MakeRaw(int(os.Stdin.Fd()))
|
||||
oldState, err := MakeRaw(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -150,12 +52,12 @@ func SetRawTerminal() (*State, error) {
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
_ = <-c
|
||||
Restore(int(os.Stdin.Fd()), oldState)
|
||||
Restore(os.Stdin.Fd(), oldState)
|
||||
os.Exit(0)
|
||||
}()
|
||||
return oldState, err
|
||||
}
|
||||
|
||||
func RestoreTerminal(state *State) {
|
||||
Restore(int(os.Stdin.Fd()), state)
|
||||
Restore(os.Stdin.Fd(), state)
|
||||
}
|
||||
|
||||
@@ -8,23 +8,45 @@ import (
|
||||
const (
|
||||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
|
||||
ECHO = 0x00000008
|
||||
ONLCR = 0x2
|
||||
ISTRIP = 0x20
|
||||
INLCR = 0x40
|
||||
ISIG = 0x80
|
||||
IGNCR = 0x80
|
||||
ICANON = 0x100
|
||||
ICRNL = 0x100
|
||||
IXOFF = 0x400
|
||||
IXON = 0x200
|
||||
)
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint64
|
||||
Oflag uint64
|
||||
Cflag uint64
|
||||
Lflag uint64
|
||||
Cc [20]byte
|
||||
Ispeed uint64
|
||||
Ospeed uint64
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
func MakeRaw(fd uintptr) (*State, error) {
|
||||
var oldState State
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := oldState.termios
|
||||
newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
|
||||
newState.Iflag &^= (ISTRIP | INLCR | IGNCR | IXON | IXOFF)
|
||||
newState.Iflag |= ICRNL
|
||||
newState.Oflag |= ONLCR
|
||||
newState.Lflag &^= ECHO | ICANON | ISIG
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||
newState.Lflag &^= (ECHO | ICANON | ISIG)
|
||||
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,54 +5,40 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// #include <termios.h>
|
||||
// #include <sys/ioctl.h>
|
||||
/*
|
||||
void MakeRaw(int fd) {
|
||||
struct termios t;
|
||||
|
||||
// FIXME: Handle errors?
|
||||
ioctl(fd, TCGETS, &t);
|
||||
|
||||
t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
t.c_oflag &= ~OPOST;
|
||||
t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
||||
t.c_cflag &= ~(CSIZE | PARENB);
|
||||
t.c_cflag |= CS8;
|
||||
|
||||
ioctl(fd, TCSETS, &t);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
getTermios = syscall.TCGETS
|
||||
setTermios = syscall.TCSETS
|
||||
)
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
func MakeRaw(fd uintptr) (*State, error) {
|
||||
var oldState State
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TCGETS, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := oldState.termios
|
||||
|
||||
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
|
||||
newState.Oflag &^= syscall.OPOST
|
||||
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
|
||||
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
|
||||
newState.Cflag |= syscall.CS8
|
||||
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
C.MakeRaw(C.int(fd))
|
||||
return &oldState, nil
|
||||
|
||||
// FIXME: post on goland issues this: very same as the C function bug non-working
|
||||
|
||||
// newState := oldState.termios
|
||||
|
||||
// newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||
// newState.Oflag &^= OPOST
|
||||
// newState.Lflag &^= (ECHO | syscall.ECHONL | ICANON | ISIG | IEXTEN)
|
||||
// newState.Cflag &^= (CSIZE | syscall.PARENB)
|
||||
// newState.Cflag |= CS8
|
||||
|
||||
// if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TCSETS, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
// return nil, err
|
||||
// }
|
||||
// return &oldState, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"index/suffixarray"
|
||||
@@ -69,7 +70,7 @@ type progressReader struct {
|
||||
readProgress int // How much has been read so far (bytes)
|
||||
lastUpdate int // How many bytes read at least update
|
||||
template string // Template to print. Default "%v/%v (%v)"
|
||||
json bool
|
||||
sf *StreamFormatter
|
||||
}
|
||||
|
||||
func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
@@ -93,7 +94,7 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
}
|
||||
// Send newline when complete
|
||||
if err != nil {
|
||||
fmt.Fprintf(r.output, FormatStatus("", r.json))
|
||||
r.output.Write(r.sf.FormatStatus(""))
|
||||
}
|
||||
|
||||
return read, err
|
||||
@@ -101,11 +102,12 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
func (r *progressReader) Close() error {
|
||||
return io.ReadCloser(r.reader).Close()
|
||||
}
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
|
||||
if template == "" {
|
||||
template = "%v/%v (%v)\r"
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader {
|
||||
tpl := string(template)
|
||||
if tpl == "" {
|
||||
tpl = string(sf.FormatProgress("", "%v/%v (%v)"))
|
||||
}
|
||||
return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template, json}
|
||||
return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf}
|
||||
}
|
||||
|
||||
// HumanDuration returns a human-readable approximation of a duration
|
||||
@@ -564,16 +566,57 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
||||
return &WriteFlusher{w: w, flusher: flusher}
|
||||
}
|
||||
|
||||
func FormatStatus(str string, json bool) string {
|
||||
if json {
|
||||
return "{\"status\" : \"" + str + "\"}"
|
||||
}
|
||||
return str + "\r\n"
|
||||
type JsonMessage struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Progress string `json:"progress,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func FormatProgress(str string, json bool) string {
|
||||
if json {
|
||||
return "{\"progress\" : \"" + str + "\"}"
|
||||
}
|
||||
return "Downloading " + str + "\r"
|
||||
type StreamFormatter struct {
|
||||
json bool
|
||||
used bool
|
||||
}
|
||||
|
||||
func NewStreamFormatter(json bool) *StreamFormatter {
|
||||
return &StreamFormatter{json, false}
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte {
|
||||
sf.used = true
|
||||
str := fmt.Sprintf(format, a...)
|
||||
if sf.json {
|
||||
b, err := json.Marshal(&JsonMessage{Status:str});
|
||||
if err != nil {
|
||||
return sf.FormatError(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
return []byte(str + "\r\n")
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) FormatError(err error) []byte {
|
||||
sf.used = true
|
||||
if sf.json {
|
||||
if b, err := json.Marshal(&JsonMessage{Error:err.Error()}); err == nil {
|
||||
return b
|
||||
}
|
||||
return []byte("{\"error\":\"format error\"}")
|
||||
}
|
||||
return []byte("Error: " + err.Error() + "\r\n")
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
|
||||
sf.used = true
|
||||
if sf.json {
|
||||
b, err := json.Marshal(&JsonMessage{Progress:str})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
return []byte(action + " " + str + "\r")
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) Used() bool {
|
||||
return sf.used
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user