mirror of
https://github.com/clearlinux/docker.git
synced 2026-09-06 21:51:33 +00:00
Compare 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 |
@@ -15,6 +15,7 @@ Brian McCallister <brianm@skife.org>
|
|||||||
Bruno Bigras <bigras.bruno@gmail.com>
|
Bruno Bigras <bigras.bruno@gmail.com>
|
||||||
Caleb Spare <cespare@gmail.com>
|
Caleb Spare <cespare@gmail.com>
|
||||||
Charles Hooper <charles.hooper@dotcloud.com>
|
Charles Hooper <charles.hooper@dotcloud.com>
|
||||||
|
Daniel Gasienica <daniel@gasienica.ch>
|
||||||
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
|
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
|
||||||
Daniel Robinson <gottagetmac@gmail.com>
|
Daniel Robinson <gottagetmac@gmail.com>
|
||||||
Daniel Von Fange <daniel@leancoder.com>
|
Daniel Von Fange <daniel@leancoder.com>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# 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)
|
## 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' 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.
|
+ Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ func httpError(w http.ResponseWriter, err error) {
|
|||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
} else if strings.HasPrefix(err.Error(), "Bad parameter") {
|
} else if strings.HasPrefix(err.Error(), "Bad parameter") {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
} else if strings.HasPrefix(err.Error(), "Impossible") {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotAcceptable)
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
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")
|
tag := r.Form.Get("tag")
|
||||||
repo := r.Form.Get("repo")
|
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
|
if image != "" { //pull
|
||||||
registry := r.Form.Get("registry")
|
registry := r.Form.Get("registry")
|
||||||
if version > 1.0 {
|
if err := srv.ImagePull(image, tag, registry, w, sf); err != nil {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
if sf.Used() {
|
||||||
}
|
w.Write(sf.FormatError(err))
|
||||||
if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else { //import
|
} 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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,10 +368,16 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
|
|||||||
return fmt.Errorf("Missing parameter")
|
return fmt.Errorf("Missing parameter")
|
||||||
}
|
}
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
|
if version > 1.0 {
|
||||||
imgId, err := srv.ImageInsert(name, url, path, w)
|
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 {
|
if err != nil {
|
||||||
return err
|
if sf.Used() {
|
||||||
|
w.Write(sf.FormatError(err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&ApiId{Id: imgId})
|
b, err := json.Marshal(&ApiId{Id: imgId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -380,8 +397,15 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
|
|||||||
return fmt.Errorf("Missing parameter")
|
return fmt.Errorf("Missing parameter")
|
||||||
}
|
}
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
|
if version > 1.0 {
|
||||||
if err := srv.ImagePush(name, registry, w); err != nil {
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+11
-12
@@ -3,7 +3,7 @@ package docker
|
|||||||
type ApiHistory struct {
|
type ApiHistory struct {
|
||||||
Id string
|
Id string
|
||||||
Created int64
|
Created int64
|
||||||
CreatedBy string
|
CreatedBy string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiImages struct {
|
type ApiImages struct {
|
||||||
@@ -14,13 +14,13 @@ type ApiImages struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ApiInfo struct {
|
type ApiInfo struct {
|
||||||
Containers int
|
|
||||||
Version string
|
|
||||||
Images int
|
|
||||||
Debug bool
|
Debug bool
|
||||||
GoVersion string
|
Containers int
|
||||||
NFd int `json:",omitempty"`
|
Images int
|
||||||
NGoroutines int `json:",omitempty"`
|
NFd int `json:",omitempty"`
|
||||||
|
NGoroutines int `json:",omitempty"`
|
||||||
|
MemoryLimit bool `json:",omitempty"`
|
||||||
|
SwapLimit bool `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiContainers struct {
|
type ApiContainers struct {
|
||||||
@@ -43,7 +43,7 @@ type ApiId struct {
|
|||||||
|
|
||||||
type ApiRun struct {
|
type ApiRun struct {
|
||||||
Id string
|
Id string
|
||||||
Warnings []string
|
Warnings []string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiPort struct {
|
type ApiPort struct {
|
||||||
@@ -51,10 +51,9 @@ type ApiPort struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ApiVersion struct {
|
type ApiVersion struct {
|
||||||
Version string
|
Version string
|
||||||
GitCommit string
|
GitCommit string `json:",omitempty"`
|
||||||
MemoryLimit bool
|
GoVersion string `json:",omitempty"`
|
||||||
SwapLimit bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiWait struct {
|
type ApiWait struct {
|
||||||
|
|||||||
+2
-2
@@ -106,8 +106,8 @@ func TestGetInfo(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if infos.Version != VERSION {
|
if infos.Images != 1 {
|
||||||
t.Errorf("Excepted version %s, %s found", VERSION, infos.Version)
|
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 {
|
func Untar(archive io.Reader, path string) error {
|
||||||
cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
|
cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
|
||||||
cmd.Stdin = archive
|
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()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %s", err, output)
|
return fmt.Errorf("%s: %s", err, output)
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@ func (b *buildFile) CmdFrom(name string) error {
|
|||||||
remote = name
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
|||||||
file := filepath.Base(path)
|
file := filepath.Base(path)
|
||||||
// If there is a whiteout, then the file was removed
|
// If there is a whiteout, then the file was removed
|
||||||
if strings.HasPrefix(file, ".wh.") {
|
if strings.HasPrefix(file, ".wh.") {
|
||||||
originalFile := strings.TrimLeft(file, ".wh.")
|
originalFile := file[len(".wh."):]
|
||||||
change.Path = filepath.Join(filepath.Dir(path), originalFile)
|
change.Path = filepath.Join(filepath.Dir(path), originalFile)
|
||||||
change.Kind = ChangeDelete
|
change.Kind = ChangeDelete
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+34
-35
@@ -28,7 +28,7 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "0.3.4"
|
const VERSION = "0.4.0"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
GIT_COMMIT string
|
GIT_COMMIT string
|
||||||
@@ -102,7 +102,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
|||||||
{"stop", "Stop a running container"},
|
{"stop", "Stop a running container"},
|
||||||
{"tag", "Tag an image into a repository"},
|
{"tag", "Tag an image into a repository"},
|
||||||
{"version", "Show the docker version information"},
|
{"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])
|
help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1])
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
// FIXME: Find a way to have a progressbar for the upload too
|
// 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)
|
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)
|
utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Version:", out.Version)
|
fmt.Println("Client version:", VERSION)
|
||||||
fmt.Println("Git Commit:", out.GitCommit)
|
fmt.Println("Server version:", out.Version)
|
||||||
if !out.MemoryLimit {
|
if out.GitCommit != "" {
|
||||||
fmt.Println("WARNING: No memory limit support")
|
fmt.Println("Git commit:", out.GitCommit)
|
||||||
}
|
}
|
||||||
if !out.SwapLimit {
|
if out.GoVersion != "" {
|
||||||
fmt.Println("WARNING: No swap limit support")
|
fmt.Println("Go version:", out.GoVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,14 +420,23 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var out ApiInfo
|
var out ApiInfo
|
||||||
err = json.Unmarshal(body, &out)
|
if err := json.Unmarshal(body, &out); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
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.Printf("Containers: %d\n", out.Containers)
|
||||||
fmt.Println("debug mode enabled")
|
fmt.Printf("Images: %d\n", out.Images)
|
||||||
fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@@ -730,12 +739,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
|||||||
remote = remoteParts[0]
|
remote = remoteParts[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(remote, "/") {
|
|
||||||
if _, err := cli.checkIfLogged(true, "pull"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("fromImage", remote)
|
v.Set("fromImage", remote)
|
||||||
v.Set("tag", *tag)
|
v.Set("tag", *tag)
|
||||||
@@ -988,12 +991,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v := url.Values{}
|
if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", nil, os.Stdout); err != nil {
|
||||||
v.Set("logs", "1")
|
return err
|
||||||
v.Set("stdout", "1")
|
}
|
||||||
v.Set("stderr", "1")
|
if err := cli.stream("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", nil, os.Stderr); err != nil {
|
||||||
|
|
||||||
if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), false, nil, os.Stdout); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
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" {
|
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)
|
dec := json.NewDecoder(resp.Body)
|
||||||
for {
|
for {
|
||||||
var m Message
|
var m utils.JsonMessage
|
||||||
if err := dec.Decode(&m); err == io.EOF {
|
if err := dec.Decode(&m); err == io.EOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} 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 != "" {
|
if m.Progress != "" {
|
||||||
fmt.Fprintf(out, "Downloading %s\r", m.Progress)
|
fmt.Fprintf(out, "Downloading %s\r", m.Progress)
|
||||||
|
} else if m.Error != "" {
|
||||||
|
return fmt.Errorf(m.Error)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n", m.Status)
|
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
|
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 {
|
if oldState, err := term.SetRawTerminal(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
@@ -1436,7 +1435,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
if !term.IsTerminal(os.Stdin.Fd()) {
|
||||||
if err := <-sendStdin; err != nil {
|
if err := <-sendStdin; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,37 @@ func TestDiff(t *testing.T) {
|
|||||||
t.Fatalf("/etc/passwd should not be present in the diff after commit.")
|
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) {
|
func TestCommitAutoRun(t *testing.T) {
|
||||||
|
|||||||
@@ -2,18 +2,15 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
# these should match the names found at http://www.debian.org/releases/
|
# these should match the names found at http://www.debian.org/releases/
|
||||||
stableSuite='squeeze'
|
stableSuite='wheezy'
|
||||||
testingSuite='wheezy'
|
testingSuite='jessie'
|
||||||
unstableSuite='sid'
|
unstableSuite='sid'
|
||||||
|
|
||||||
# if suite is equal to this, it gets the "latest" tag
|
|
||||||
latestSuite="$testingSuite"
|
|
||||||
|
|
||||||
variant='minbase'
|
variant='minbase'
|
||||||
include='iproute,iputils-ping'
|
include='iproute,iputils-ping'
|
||||||
|
|
||||||
repo="$1"
|
repo="$1"
|
||||||
suite="${2:-$latestSuite}"
|
suite="${2:-$stableSuite}"
|
||||||
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
|
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
|
||||||
|
|
||||||
if [ ! "$repo" ]; then
|
if [ ! "$repo" ]; then
|
||||||
@@ -41,17 +38,14 @@ img=$(sudo tar -c . | docker import -)
|
|||||||
# tag suite
|
# tag suite
|
||||||
docker tag $img $repo $suite
|
docker tag $img $repo $suite
|
||||||
|
|
||||||
if [ "$suite" = "$latestSuite" ]; then
|
|
||||||
# tag latest
|
|
||||||
docker tag $img $repo latest
|
|
||||||
fi
|
|
||||||
|
|
||||||
# test the image
|
# test the image
|
||||||
docker run -i -t $repo:$suite echo success
|
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" = "$stableSuite" -o "$suite" = 'stable' ]; then
|
||||||
if [ "$suite" != "$unstableSuite" -a "$suite" != 'unstable' ]; then
|
# tag latest
|
||||||
# tag the specific version
|
docker tag $img $repo latest
|
||||||
|
|
||||||
|
# tag the specific debian release version
|
||||||
ver=$(docker run $repo:$suite cat /etc/debian_version)
|
ver=$(docker run $repo:$suite cat /etc/debian_version)
|
||||||
docker tag $img $repo $ver
|
docker tag $img $repo $ver
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ SPHINXOPTS =
|
|||||||
SPHINXBUILD = sphinx-build
|
SPHINXBUILD = sphinx-build
|
||||||
PAPER =
|
PAPER =
|
||||||
BUILDDIR = _build
|
BUILDDIR = _build
|
||||||
|
PYTHON = python
|
||||||
|
|
||||||
# Internal variables.
|
# Internal variables.
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||||
@@ -38,6 +39,7 @@ help:
|
|||||||
# @echo " linkcheck to check all external links for integrity"
|
# @echo " linkcheck to check all external links for integrity"
|
||||||
# @echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
# @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 " 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"
|
@echo " publish to publish the app to dotcloud"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@@ -49,6 +51,8 @@ docs:
|
|||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html."
|
@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html."
|
||||||
|
|
||||||
|
server:
|
||||||
|
@cd $(BUILDDIR)/html; $(PYTHON) -m SimpleHTTPServer 8000
|
||||||
|
|
||||||
site:
|
site:
|
||||||
cp -r website $(BUILDDIR)/
|
cp -r website $(BUILDDIR)/
|
||||||
|
|||||||
+11
-9
@@ -14,20 +14,22 @@ Installation
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* Work in your own fork of the code, we accept pull requests.
|
* Work in your own fork of the code, we accept pull requests.
|
||||||
* Install sphinx: ``pip install sphinx``
|
* Install sphinx: `pip install sphinx`
|
||||||
* Install sphinx httpdomain contrib package ``sphinxcontrib-httpdomain``
|
* 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**
|
* If pip is not available you can probably install it using your favorite package manager as **python-pip**
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
* change the .rst files with your favorite editor to your liking
|
* Change the `.rst` files with your favorite editor to your liking.
|
||||||
* run *make docs* to clean up old files and generate new ones
|
* Run `make docs` to clean up old files and generate new ones.
|
||||||
* your static website can now be found in the _build dir
|
* Your static website can now be found in the `_build` directory.
|
||||||
* to preview what you have generated, cd into _build/html and then run 'python -m SimpleHTTPServer 8000'
|
* 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.
|
you to preview your changes right online. Just be carefull not to create many commits.
|
||||||
|
|
||||||
Images
|
Images
|
||||||
@@ -72,4 +74,4 @@ Guides on using sphinx
|
|||||||
|
|
||||||
* Code examples
|
* 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
|
- 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
|
- 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
|
List containers
|
||||||
@@ -132,6 +139,7 @@ Create a container
|
|||||||
:jsonparam config: the container's configuration
|
:jsonparam config: the container's configuration
|
||||||
:statuscode 201: no error
|
:statuscode 201: no error
|
||||||
:statuscode 404: no such container
|
:statuscode 404: no such container
|
||||||
|
:statuscode 406: impossible to attach (container not running)
|
||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
|
|
||||||
@@ -459,7 +467,7 @@ Remove a container
|
|||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
|
|
||||||
2.2 Images
|
3.2 Images
|
||||||
----------
|
----------
|
||||||
|
|
||||||
List Images
|
List Images
|
||||||
@@ -548,7 +556,19 @@ Create an image
|
|||||||
|
|
||||||
POST /images/create?fromImage=base HTTP/1.1
|
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
|
.. sourcecode:: http
|
||||||
|
|
||||||
@@ -579,7 +599,19 @@ Insert a file in a image
|
|||||||
|
|
||||||
POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
|
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
|
.. sourcecode:: http
|
||||||
|
|
||||||
@@ -694,7 +726,19 @@ Push an image on the registry
|
|||||||
|
|
||||||
POST /images/test/push HTTP/1.1
|
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
|
.. sourcecode:: http
|
||||||
|
|
||||||
@@ -800,7 +844,7 @@ Search images
|
|||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
|
|
||||||
2.3 Misc
|
3.3 Misc
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Build an image from Dockerfile via stdin
|
Build an image from Dockerfile via stdin
|
||||||
@@ -826,6 +870,7 @@ Build an image from Dockerfile via stdin
|
|||||||
|
|
||||||
{{ STREAM }}
|
{{ STREAM }}
|
||||||
|
|
||||||
|
:query t: tag to be applied to the resulting image in case of success
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
:statuscode 500: server error
|
:statuscode 500: server error
|
||||||
|
|
||||||
@@ -912,10 +957,12 @@ Display system-wide information
|
|||||||
|
|
||||||
{
|
{
|
||||||
"Containers":11,
|
"Containers":11,
|
||||||
"Version":"0.2.2",
|
|
||||||
"Images":16,
|
"Images":16,
|
||||||
"GoVersion":"go1.0.3",
|
"Debug":false,
|
||||||
"Debug":false
|
"NFd": 11,
|
||||||
|
"NGoroutines":21,
|
||||||
|
"MemoryLimit":true,
|
||||||
|
"SwapLimit":false
|
||||||
}
|
}
|
||||||
|
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
@@ -941,12 +988,11 @@ Show the docker version information
|
|||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"Version":"0.2.2",
|
"Version":"0.2.2",
|
||||||
"GitCommit":"5a2a5cc+CHANGES",
|
"GitCommit":"5a2a5cc+CHANGES",
|
||||||
"MemoryLimit":true,
|
"GoVersion":"go1.0.3"
|
||||||
"SwapLimit":false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
:description: docker documentation
|
:description: docker documentation
|
||||||
:keywords: docker, ipa, documentation
|
:keywords: docker, ipa, documentation
|
||||||
|
|
||||||
API's
|
APIs
|
||||||
=============
|
====
|
||||||
|
|
||||||
This following :
|
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)
|
- Resolve short names (to avoid passing absolute URLs all the time)
|
||||||
- username/projectname -> \https://registry.docker.io/users/<username>/repositories/<projectname>/
|
- 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)
|
- Authenticate a user as a repos owner (for a central referenced repository)
|
||||||
|
|
||||||
3.1 Without an Index
|
3.1 Without an Index
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
Introduction
|
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.
|
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
|
:description: Guides on how to contribute to docker
|
||||||
:keywords: Docker, documentation, developers, contributing, dev environment
|
: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,
|
Instructions that have been verified to work on Ubuntu 12.10,
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
.. _running_couchdb_service:
|
.. _running_couchdb_service:
|
||||||
|
|
||||||
Create a CouchDB service
|
CouchDB Service
|
||||||
========================
|
===============
|
||||||
|
|
||||||
.. include:: example_header.inc
|
.. include:: example_header.inc
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
:title: Docker Examples
|
:title: Docker Examples
|
||||||
:description: Examples on how to use Docker
|
: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
|
||||||
hello_world_daemon
|
hello_world_daemon
|
||||||
python_web_app
|
python_web_app
|
||||||
|
nodejs_web_app
|
||||||
running_redis_service
|
running_redis_service
|
||||||
running_ssh_service
|
running_ssh_service
|
||||||
couchdb_data_volumes
|
couchdb_data_volumes
|
||||||
|
|||||||
@@ -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:
|
.. _python_web_app:
|
||||||
|
|
||||||
Building a python web app
|
Python Web App
|
||||||
=========================
|
==============
|
||||||
|
|
||||||
.. include:: example_header.inc
|
.. include:: example_header.inc
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
.. _running_examples:
|
.. _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:
|
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:
|
.. _running_redis_service:
|
||||||
|
|
||||||
Create a redis service
|
Redis Service
|
||||||
======================
|
=============
|
||||||
|
|
||||||
.. include:: example_header.inc
|
.. include:: example_header.inc
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ Snapshot the installation
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
docker commit <container_id> <your username>/redis
|
||||||
|
|
||||||
Run the service
|
Run the service
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
.. _running_ssh_service:
|
.. _running_ssh_service:
|
||||||
|
|
||||||
Create an ssh daemon service
|
SSH Daemon Service
|
||||||
============================
|
==================
|
||||||
|
|
||||||
.. include:: example_header.inc
|
.. include:: example_header.inc
|
||||||
|
|
||||||
@@ -20,8 +20,7 @@ minutes and not entirely smooth, but gives you a good idea.
|
|||||||
<div style="margin-top:10px;">
|
<div style="margin-top:10px;">
|
||||||
<iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
|
<iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
You can also get this sshd container by using
|
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'
|
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
|
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.
|
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
|
:keywords: Examples, Usage, basic commands, docker, documentation, examples
|
||||||
|
|
||||||
|
|
||||||
The basics
|
The Basics
|
||||||
=============
|
==========
|
||||||
|
|
||||||
Starting Docker
|
Starting Docker
|
||||||
---------------
|
---------------
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
.. _working_with_the_repository:
|
.. _working_with_the_repository:
|
||||||
|
|
||||||
Working with the repository
|
Working with the Repository
|
||||||
============================
|
===========================
|
||||||
|
|
||||||
|
|
||||||
Top-level repositories and user repositories
|
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
|
Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind
|
||||||
Docker, and user repositories.
|
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.
|
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.
|
* 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>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>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>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>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>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>
|
<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>
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
|
|||||||
DockerVersion: VERSION,
|
DockerVersion: VERSION,
|
||||||
Author: author,
|
Author: author,
|
||||||
Config: config,
|
Config: config,
|
||||||
|
Architecture: "x86_64",
|
||||||
}
|
}
|
||||||
if container != nil {
|
if container != nil {
|
||||||
img.Parent = container.Image
|
img.Parent = container.Image
|
||||||
@@ -165,7 +166,8 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// 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
|
# This will build a container capable of producing an official binary build of docker and
|
||||||
# uploading it to S3
|
# uploading it to S3
|
||||||
|
from ubuntu:12.04
|
||||||
maintainer Solomon Hykes <solomon@dotcloud.com>
|
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
|
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 s3cmd
|
||||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
|
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 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 tar -C /usr/local -xzf /go.tar.gz
|
||||||
run echo "export PATH=$PATH:/usr/local/go/bin" > /.bashrc
|
run echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc
|
||||||
run echo "export PATH=$PATH:/usr/local/go/bin" > /.bash_profile
|
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 git
|
||||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential
|
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential
|
||||||
# Packages required to build an ubuntu package
|
# 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 debhelper
|
||||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q autotools-dev
|
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
|
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/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
|
||||||
run cp /src/s3cfg /.s3cfg
|
run cp /src/s3cfg /.s3cfg
|
||||||
cmd ["dockerbuilder"]
|
cmd ["dockerbuilder"]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
set -x
|
set -x
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
export PATH=$PATH:/usr/local/go/bin
|
export PATH=/usr/local/go/bin:$PATH
|
||||||
|
|
||||||
PACKAGE=github.com/dotcloud/docker
|
PACKAGE=github.com/dotcloud/docker
|
||||||
|
|
||||||
@@ -36,5 +36,6 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$NO_UBUNTU" ]; then
|
if [ -z "$NO_UBUNTU" ]; then
|
||||||
|
export PATH=`echo $PATH | sed 's#/usr/local/go/bin:##g'`
|
||||||
(cd packaging/ubuntu && make ubuntu)
|
(cd packaging/ubuntu && make ubuntu)
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo Whatever you say, man
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Ken Cochrane <ken@dotcloud.com>
|
||||||
|
Jerome Petazzoni <jerome@dotcloud.com>
|
||||||
@@ -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.
|
||||||
@@ -27,6 +27,7 @@ type Image struct {
|
|||||||
DockerVersion string `json:"docker_version,omitempty"`
|
DockerVersion string `json:"docker_version,omitempty"`
|
||||||
Author string `json:"author,omitempty"`
|
Author string `json:"author,omitempty"`
|
||||||
Config *Config `json:"config,omitempty"`
|
Config *Config `json:"config,omitempty"`
|
||||||
|
Architecture string `json:"architecture,omitempty"`
|
||||||
graph *Graph
|
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
|
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' 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.
|
- 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
|
respawn
|
||||||
|
|
||||||
script
|
script
|
||||||
# FIXME: docker should not depend on the system having en_US.UTF-8
|
/usr/bin/docker -d
|
||||||
LC_ALL='en_US.UTF-8' /usr/bin/docker -d
|
|
||||||
end script
|
end script
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ func init() {
|
|||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
}
|
}
|
||||||
// Retrieve the Image
|
// 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)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (srv *Server) DockerVersion() ApiVersion {
|
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 {
|
func (srv *Server) ContainerKill(name string) error {
|
||||||
@@ -68,7 +72,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
|
|||||||
return outs, nil
|
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)
|
out = utils.NewWriteFlusher(out)
|
||||||
img, err := srv.runtime.repositories.LookupImage(name)
|
img, err := srv.runtime.repositories.LookupImage(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,7 +96,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
|
|||||||
return "", err
|
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
|
return "", err
|
||||||
}
|
}
|
||||||
// FIXME: Handle custom repo, tag comment, author
|
// 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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", img.Id)
|
out.Write(sf.FormatStatus(img.Id))
|
||||||
return img.ShortId(), nil
|
return img.ShortId(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +191,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
|
|||||||
return outs, nil
|
return outs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) DockerInfo() ApiInfo {
|
func (srv *Server) DockerInfo() *ApiInfo {
|
||||||
images, _ := srv.runtime.graph.All()
|
images, _ := srv.runtime.graph.All()
|
||||||
var imgcount int
|
var imgcount int
|
||||||
if images == nil {
|
if images == nil {
|
||||||
@@ -195,17 +199,15 @@ func (srv *Server) DockerInfo() ApiInfo {
|
|||||||
} else {
|
} else {
|
||||||
imgcount = len(images)
|
imgcount = len(images)
|
||||||
}
|
}
|
||||||
var out ApiInfo
|
return &ApiInfo{
|
||||||
out.Containers = len(srv.runtime.List())
|
Containers: len(srv.runtime.List()),
|
||||||
out.Version = VERSION
|
Images: imgcount,
|
||||||
out.Images = imgcount
|
MemoryLimit: srv.runtime.capabilities.MemoryLimit,
|
||||||
out.GoVersion = runtime.Version()
|
SwapLimit: srv.runtime.capabilities.SwapLimit,
|
||||||
if os.Getenv("DEBUG") != "" {
|
Debug: os.Getenv("DEBUG") != "",
|
||||||
out.Debug = true
|
NFd: utils.GetTotalUsedFds(),
|
||||||
out.NFd = utils.GetTotalUsedFds()
|
NGoroutines: runtime.NumGoroutine(),
|
||||||
out.NGoroutines = runtime.NumGoroutine()
|
|
||||||
}
|
}
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
|
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
|
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)
|
history, err := r.GetRemoteHistory(imgId, endpoint, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -302,7 +304,7 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
|
|||||||
// FIXME: Launch the getRemoteImage() in goroutines
|
// FIXME: Launch the getRemoteImage() in goroutines
|
||||||
for _, id := range history {
|
for _, id := range history {
|
||||||
if !srv.runtime.graph.Exists(id) {
|
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)
|
imgJson, err := r.GetRemoteImageJson(id, endpoint, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// FIXME: Keep goging in case of error?
|
// 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
|
// 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)
|
layer, contentLength, err := r.GetRemoteImageLayer(img.Id, endpoint, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,8 +329,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, json bool) error {
|
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error {
|
||||||
fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
|
out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress()))
|
||||||
repoData, err := r.GetRepositoryData(remote)
|
repoData, err := r.GetRepositoryData(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
|
||||||
continue
|
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
|
success := false
|
||||||
for _, ep := range repoData.Endpoints {
|
for _, ep := range repoData.Endpoints {
|
||||||
if err := srv.pullImage(r, out, img.Id, "https://"+ep+"/v1", repoData.Tokens, json); err != nil {
|
if err := srv.pullImage(r, out, img.Id, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil {
|
||||||
fmt.Fprintf(out, utils.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint\n", json), askedTag, err)
|
out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
success = true
|
success = true
|
||||||
@@ -394,17 +396,17 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
|
|||||||
return nil
|
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)
|
r := registry.NewRegistry(srv.runtime.root)
|
||||||
out = utils.NewWriteFlusher(out)
|
out = utils.NewWriteFlusher(out)
|
||||||
if endpoint != "" {
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,14 +479,14 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
|
|||||||
return imgList, nil
|
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)
|
out = utils.NewWriteFlusher(out)
|
||||||
fmt.Fprintf(out, "Processing checksums\n")
|
out.Write(sf.FormatStatus("Processing checksums"))
|
||||||
imgList, err := srv.getImageList(localRepo)
|
imgList, err := srv.getImageList(localRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "Sending images list\n")
|
out.Write(sf.FormatStatus("Sending image list"))
|
||||||
|
|
||||||
repoData, err := r.PushImageJsonIndex(name, imgList, false)
|
repoData, err := r.PushImageJsonIndex(name, imgList, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -492,18 +494,18 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, ep := range repoData.Endpoints {
|
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 each image within the repo, push them
|
||||||
for _, elem := range imgList {
|
for _, elem := range imgList {
|
||||||
if _, exists := repoData.ImgList[elem.Id]; exists {
|
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
|
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?
|
// FIXME: Continue on error?
|
||||||
return err
|
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 {
|
if err := r.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -516,13 +518,13 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
|
|||||||
return nil
|
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)
|
out = utils.NewWriteFlusher(out)
|
||||||
jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json"))
|
jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err)
|
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
|
// Make sure we have the image's checksum
|
||||||
checksum, err := srv.getChecksum(imgId)
|
checksum, err := srv.getChecksum(imgId)
|
||||||
@@ -537,7 +539,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
|
|||||||
// Send the json
|
// Send the json
|
||||||
if err := r.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil {
|
if err := r.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil {
|
||||||
if err == registry.ErrAlreadyExists {
|
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 nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -570,22 +572,22 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the layer
|
// 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 err
|
||||||
}
|
}
|
||||||
return nil
|
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)
|
out = utils.NewWriteFlusher(out)
|
||||||
img, err := srv.runtime.graph.Get(name)
|
img, err := srv.runtime.graph.Get(name)
|
||||||
r := registry.NewRegistry(srv.runtime.root)
|
r := registry.NewRegistry(srv.runtime.root)
|
||||||
|
|
||||||
if err != nil {
|
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 it fails, try to get the repository
|
||||||
if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -593,14 +595,14 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer) error {
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "The push refers to an image: [%s]\n", name)
|
out.Write(sf.FormatStatus("The push refers to an image: [%s]", name))
|
||||||
if err := srv.pushImage(r, out, name, img.Id, endpoint, nil); err != nil {
|
if err := srv.pushImage(r, out, name, img.Id, endpoint, nil, sf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
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 archive io.Reader
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
|
||||||
@@ -609,21 +611,21 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
|
|||||||
} else {
|
} else {
|
||||||
u, err := url.Parse(src)
|
u, err := url.Parse(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "Error: %s\n", err)
|
return err
|
||||||
}
|
}
|
||||||
if u.Scheme == "" {
|
if u.Scheme == "" {
|
||||||
u.Scheme = "http"
|
u.Scheme = "http"
|
||||||
u.Host = src
|
u.Host = src
|
||||||
u.Path = ""
|
u.Path = ""
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "Downloading from %s\n", u)
|
out.Write(sf.FormatStatus("Downloading from %s", u))
|
||||||
// Download with curl (pretty progress bar)
|
// Download with curl (pretty progress bar)
|
||||||
// If curl is not available, fallback to http.Get()
|
// If curl is not available, fallback to http.Get()
|
||||||
resp, err = utils.Download(u.String(), out)
|
resp, err = utils.Download(u.String(), out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -635,7 +637,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", img.ShortId())
|
out.Write(sf.FormatStatus(img.ShortId()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +792,6 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
|
|||||||
if container == nil {
|
if container == nil {
|
||||||
return fmt.Errorf("No such container: %s", name)
|
return fmt.Errorf("No such container: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
//logs
|
//logs
|
||||||
if logs {
|
if logs {
|
||||||
if stdout {
|
if stdout {
|
||||||
@@ -816,6 +817,9 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
|
|||||||
if container.State.Ghost {
|
if container.State.Ghost {
|
||||||
return fmt.Errorf("Impossible to attach to a ghost container")
|
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 (
|
var (
|
||||||
cStdin io.ReadCloser
|
cStdin io.ReadCloser
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Guillaume Charmes <guillaume@dotcloud.com>
|
||||||
|
Solomon Hykes <solomon@dotcloud.com>
|
||||||
+7
-105
@@ -7,104 +7,6 @@ import (
|
|||||||
"unsafe"
|
"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 {
|
type State struct {
|
||||||
termios Termios
|
termios Termios
|
||||||
}
|
}
|
||||||
@@ -128,21 +30,21 @@ func SetWinsize(fd uintptr, ws *Winsize) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
func IsTerminal(fd int) bool {
|
func IsTerminal(fd uintptr) bool {
|
||||||
var termios Termios
|
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
|
return err == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore restores the terminal connected to the given file descriptor to a
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
// previous state.
|
// previous state.
|
||||||
func Restore(fd int, state *State) error {
|
func Restore(fd uintptr, state *State) error {
|
||||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetRawTerminal() (*State, error) {
|
func SetRawTerminal() (*State, error) {
|
||||||
oldState, err := MakeRaw(int(os.Stdin.Fd()))
|
oldState, err := MakeRaw(os.Stdin.Fd())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -150,12 +52,12 @@ func SetRawTerminal() (*State, error) {
|
|||||||
signal.Notify(c, os.Interrupt)
|
signal.Notify(c, os.Interrupt)
|
||||||
go func() {
|
go func() {
|
||||||
_ = <-c
|
_ = <-c
|
||||||
Restore(int(os.Stdin.Fd()), oldState)
|
Restore(os.Stdin.Fd(), oldState)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
return oldState, err
|
return oldState, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RestoreTerminal(state *State) {
|
func RestoreTerminal(state *State) {
|
||||||
Restore(int(os.Stdin.Fd()), state)
|
Restore(os.Stdin.Fd(), state)
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-5
@@ -8,23 +8,45 @@ import (
|
|||||||
const (
|
const (
|
||||||
getTermios = syscall.TIOCGETA
|
getTermios = syscall.TIOCGETA
|
||||||
setTermios = syscall.TIOCSETA
|
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
|
// 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
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
// restored.
|
// restored.
|
||||||
func MakeRaw(fd int) (*State, error) {
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
var oldState State
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newState := oldState.termios
|
newState := oldState.termios
|
||||||
newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
|
newState.Iflag &^= (ISTRIP | INLCR | IGNCR | IXON | IXOFF)
|
||||||
newState.Iflag |= ICRNL
|
newState.Iflag |= ICRNL
|
||||||
newState.Oflag |= ONLCR
|
newState.Oflag |= ONLCR
|
||||||
newState.Lflag &^= ECHO | ICANON | ISIG
|
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 {
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-38
@@ -5,54 +5,40 @@ import (
|
|||||||
"unsafe"
|
"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 (
|
const (
|
||||||
getTermios = syscall.TCGETS
|
getTermios = syscall.TCGETS
|
||||||
setTermios = syscall.TCSETS
|
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
|
// 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
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
// restored.
|
// restored.
|
||||||
func MakeRaw(fd int) (*State, error) {
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
var oldState State
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
C.MakeRaw(C.int(fd))
|
|
||||||
return &oldState, nil
|
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-15
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"index/suffixarray"
|
"index/suffixarray"
|
||||||
@@ -69,7 +70,7 @@ type progressReader struct {
|
|||||||
readProgress int // How much has been read so far (bytes)
|
readProgress int // How much has been read so far (bytes)
|
||||||
lastUpdate int // How many bytes read at least update
|
lastUpdate int // How many bytes read at least update
|
||||||
template string // Template to print. Default "%v/%v (%v)"
|
template string // Template to print. Default "%v/%v (%v)"
|
||||||
json bool
|
sf *StreamFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *progressReader) Read(p []byte) (n int, err error) {
|
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
|
// Send newline when complete
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(r.output, FormatStatus("", r.json))
|
r.output.Write(r.sf.FormatStatus(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
return read, err
|
return read, err
|
||||||
@@ -101,11 +102,12 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
|||||||
func (r *progressReader) Close() error {
|
func (r *progressReader) Close() error {
|
||||||
return io.ReadCloser(r.reader).Close()
|
return io.ReadCloser(r.reader).Close()
|
||||||
}
|
}
|
||||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
|
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader {
|
||||||
if template == "" {
|
tpl := string(template)
|
||||||
template = "%v/%v (%v)\r"
|
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
|
// 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}
|
return &WriteFlusher{w: w, flusher: flusher}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatStatus(str string, json bool) string {
|
type JsonMessage struct {
|
||||||
if json {
|
Status string `json:"status,omitempty"`
|
||||||
return "{\"status\" : \"" + str + "\"}"
|
Progress string `json:"progress,omitempty"`
|
||||||
}
|
Error string `json:"error,omitempty"`
|
||||||
return str + "\r\n"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatProgress(str string, json bool) string {
|
type StreamFormatter struct {
|
||||||
if json {
|
json bool
|
||||||
return "{\"progress\" : \"" + str + "\"}"
|
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 "Downloading " + str + "\r"
|
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