This commit is contained in:
Victor Vieux 2013-06-10 21:05:54 +00:00
commit 66d9a73362
54 changed files with 1042 additions and 1053 deletions

View File

@ -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.

View File

@ -251,7 +251,7 @@ Note
---- ----
We also keep the documentation in this repository. The website documentation is generated using sphinx using these sources. We also keep the documentation in this repository. The website documentation is generated using sphinx using these sources.
Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/master/docs/README.md Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/tree/master/docs/README.md
Please feel free to fix / update the documentation and send us pull requests. More tutorials are also welcome. Please feel free to fix / update the documentation and send us pull requests. More tutorials are also welcome.
@ -371,4 +371,7 @@ Standard Container Specification
#### Security #### Security
### Legal
Transfers Docker shall be in accordance with any applicable export control or other legal requirements.

74
api.go
View File

@ -13,7 +13,7 @@ import (
"strings" "strings"
) )
const API_VERSION = 1.1 const APIVERSION = 1.1
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
conn, _, err := w.(http.Hijacker).Hijack() conn, _, err := w.(http.Hijacker).Hijack()
@ -54,7 +54,7 @@ func httpError(w http.ResponseWriter, err error) {
} }
} }
func writeJson(w http.ResponseWriter, b []byte) { func writeJSON(w http.ResponseWriter, b []byte) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Write(b) w.Write(b)
} }
@ -84,7 +84,7 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -113,11 +113,11 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque
} }
if status != "" { if status != "" {
b, err := json.Marshal(&ApiAuth{Status: status}) b, err := json.Marshal(&APIAuth{Status: status})
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
@ -130,7 +130,7 @@ func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Req
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -159,7 +159,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
return nil return nil
} }
func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil { if err := parseForm(r); err != nil {
return err return err
} }
@ -178,7 +178,7 @@ func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -195,7 +195,7 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -212,7 +212,7 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -229,11 +229,11 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil { if err := parseForm(r); err != nil {
return err return err
} }
@ -253,7 +253,7 @@ func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *h
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -296,12 +296,12 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(&ApiId{id}) b, err := json.Marshal(&APIID{id})
if err != nil { if err != nil {
return err return err
} }
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -355,7 +355,7 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -374,18 +374,18 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
} }
sf := utils.NewStreamFormatter(version > 1.0) sf := utils.NewStreamFormatter(version > 1.0)
imgId, err := srv.ImageInsert(name, url, path, w, sf) imgID, err := srv.ImageInsert(name, url, path, w, sf)
if err != nil { if err != nil {
if sf.Used() { if sf.Used() {
w.Write(sf.FormatError(err)) w.Write(sf.FormatError(err))
return nil return nil
} }
} }
b, err := json.Marshal(&ApiId{Id: imgId}) b, err := json.Marshal(&APIID{ID: imgID})
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -423,8 +423,8 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
return err return err
} }
out := &ApiRun{ out := &APIRun{
Id: id, ID: id,
} }
if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.") log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
@ -439,7 +439,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
return err return err
} }
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -500,7 +500,7 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
} else { } else {
return fmt.Errorf("Conflict, %s wasn't deleted", name) return fmt.Errorf("Conflict, %s wasn't deleted", name)
} }
@ -552,11 +552,11 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(&ApiWait{StatusCode: status}) b, err := json.Marshal(&APIWait{StatusCode: status})
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -643,7 +643,7 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -661,17 +661,17 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
apiConfig := &ApiImageConfig{} apiConfig := &APIImageConfig{}
if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil { if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
return err return err
} }
image, err := srv.ImageGetCached(apiConfig.Id, apiConfig.Config) image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config)
if err != nil { if err != nil {
return err return err
} }
@ -679,12 +679,12 @@ func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
return nil return nil
} }
apiId := &ApiId{Id: image.Id} apiID := &APIID{ID: image.ID}
b, err := json.Marshal(apiId) b, err := json.Marshal(apiID)
if err != nil { if err != nil {
return err return err
} }
writeJson(w, b) writeJSON(w, b)
return nil return nil
} }
@ -730,13 +730,13 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
"/auth": getAuth, "/auth": getAuth,
"/version": getVersion, "/version": getVersion,
"/info": getInfo, "/info": getInfo,
"/images/json": getImagesJson, "/images/json": getImagesJSON,
"/images/viz": getImagesViz, "/images/viz": getImagesViz,
"/images/search": getImagesSearch, "/images/search": getImagesSearch,
"/images/{name:.*}/history": getImagesHistory, "/images/{name:.*}/history": getImagesHistory,
"/images/{name:.*}/json": getImagesByName, "/images/{name:.*}/json": getImagesByName,
"/containers/ps": getContainersJson, "/containers/ps": getContainersJSON,
"/containers/json": getContainersJson, "/containers/json": getContainersJSON,
"/containers/{name:.*}/export": getContainersExport, "/containers/{name:.*}/export": getContainersExport,
"/containers/{name:.*}/changes": getContainersChanges, "/containers/{name:.*}/changes": getContainersChanges,
"/containers/{name:.*}/json": getContainersByName, "/containers/{name:.*}/json": getContainersByName,
@ -785,9 +785,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
} }
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64) version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
if err != nil { if err != nil {
version = API_VERSION version = APIVERSION
} }
if version == 0 || version > API_VERSION { if version == 0 || version > APIVERSION {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
return return
} }

View File

@ -1,35 +1,35 @@
package docker package docker
type ApiHistory struct { type APIHistory struct {
Id string ID string `json:"Id"`
Created int64 Created int64
CreatedBy string CreatedBy string `json:",omitempty"`
} }
type ApiImages struct { type APIImages struct {
Repository string `json:",omitempty"` Repository string `json:",omitempty"`
Tag string `json:",omitempty"` Tag string `json:",omitempty"`
Id string ID string `json:"Id"`
Created int64 Created int64
} }
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 ApiRmi struct { type APIRmi struct {
Deleted string `json:",omitempty"` Deleted string `json:",omitempty"`
Untagged string `json:",omitempty"` Untagged string `json:",omitempty"`
} }
type ApiContainers struct { type APIContainers struct {
Id string ID string `json:"Id"`
Image string Image string
Command string Command string
Created int64 Created int64
@ -37,40 +37,39 @@ type ApiContainers struct {
Ports string Ports string
} }
type ApiSearch struct { type APISearch struct {
Name string Name string
Description string Description string
} }
type ApiId struct { type APIID struct {
Id string ID string `json:"Id"`
} }
type ApiRun struct { type APIRun struct {
Id string ID string `json:"Id"`
Warnings []string Warnings []string `json:",omitempty"`
} }
type ApiPort struct { type APIPort struct {
Port string Port string
} }
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 {
StatusCode int StatusCode int
} }
type ApiAuth struct { type APIAuth struct {
Status string Status string
} }
type ApiImageConfig struct { type APIImageConfig struct {
Id string ID string `json:"Id"`
*Config *Config
} }

View File

@ -37,17 +37,17 @@ func TestGetAuth(t *testing.T) {
Email: "utest@yopmail.com", Email: "utest@yopmail.com",
} }
authConfigJson, err := json.Marshal(authConfig) authConfigJSON, err := json.Marshal(authConfig)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJson)) req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := postAuth(srv, API_VERSION, r, req, nil); err != nil { if err := postAuth(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -73,11 +73,11 @@ func TestGetVersion(t *testing.T) {
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil { if err := getVersion(srv, APIVERSION, r, nil, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
v := &ApiVersion{} v := &APIVersion{}
if err = json.Unmarshal(r.Body.Bytes(), v); err != nil { if err = json.Unmarshal(r.Body.Bytes(), v); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -97,21 +97,21 @@ func TestGetInfo(t *testing.T) {
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil { if err := getInfo(srv, APIVERSION, r, nil, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
infos := &ApiInfo{} infos := &APIInfo{}
err = json.Unmarshal(r.Body.Bytes(), infos) err = json.Unmarshal(r.Body.Bytes(), infos)
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)
} }
} }
func TestGetImagesJson(t *testing.T) { func TestGetImagesJSON(t *testing.T) {
runtime, err := newTestRuntime() runtime, err := newTestRuntime()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -128,11 +128,11 @@ func TestGetImagesJson(t *testing.T) {
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil { if err := getImagesJSON(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
images := []ApiImages{} images := []APIImages{}
if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil { if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -153,11 +153,11 @@ func TestGetImagesJson(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil { if err := getImagesJSON(srv, APIVERSION, r2, req2, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
images2 := []ApiImages{} images2 := []APIImages{}
if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil { if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -166,8 +166,8 @@ func TestGetImagesJson(t *testing.T) {
t.Errorf("Excepted 1 image, %d found", len(images2)) t.Errorf("Excepted 1 image, %d found", len(images2))
} }
if images2[0].Id != GetTestImage(runtime).Id { if images2[0].ID != GetTestImage(runtime).ID {
t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).Id, images2[0].Id) t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).ID, images2[0].ID)
} }
r3 := httptest.NewRecorder() r3 := httptest.NewRecorder()
@ -178,11 +178,11 @@ func TestGetImagesJson(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil { if err := getImagesJSON(srv, APIVERSION, r3, req3, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
images3 := []ApiImages{} images3 := []APIImages{}
if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil { if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -199,7 +199,7 @@ func TestGetImagesJson(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = getImagesJson(srv, API_VERSION, r4, req4, nil) err = getImagesJSON(srv, APIVERSION, r4, req4, nil)
if err == nil { if err == nil {
t.Fatalf("Error expected, received none") t.Fatalf("Error expected, received none")
} }
@ -220,7 +220,7 @@ func TestGetImagesViz(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil { if err := getImagesViz(srv, APIVERSION, r, nil, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -256,11 +256,11 @@ func TestGetImagesSearch(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil { if err := getImagesSearch(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
results := []ApiSearch{} results := []APISearch{}
if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil { if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -280,11 +280,11 @@ func TestGetImagesHistory(t *testing.T) {
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil { if err := getImagesHistory(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
history := []ApiHistory{} history := []APIHistory{}
if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil { if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -303,7 +303,7 @@ func TestGetImagesByName(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil { if err := getImagesByName(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -311,12 +311,12 @@ func TestGetImagesByName(t *testing.T) {
if err := json.Unmarshal(r.Body.Bytes(), img); err != nil { if err := json.Unmarshal(r.Body.Bytes(), img); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if img.Id != GetTestImage(runtime).Id || img.Comment != "Imported from http://get.docker.io/images/busybox" { if img.ID != GetTestImage(runtime).ID || img.Comment != "Imported from http://get.docker.io/images/busybox" {
t.Errorf("Error inspecting image") t.Errorf("Error inspecting image")
} }
} }
func TestGetContainersJson(t *testing.T) { func TestGetContainersJSON(t *testing.T) {
runtime, err := newTestRuntime() runtime, err := newTestRuntime()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -326,7 +326,7 @@ func TestGetContainersJson(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"}, Cmd: []string{"echo", "test"},
}) })
if err != nil { if err != nil {
@ -340,18 +340,18 @@ func TestGetContainersJson(t *testing.T) {
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getContainersJson(srv, API_VERSION, r, req, nil); err != nil { if err := getContainersJSON(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
containers := []ApiContainers{} containers := []APIContainers{}
if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil { if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(containers) != 1 { if len(containers) != 1 {
t.Fatalf("Excepted %d container, %d found", 1, len(containers)) t.Fatalf("Excepted %d container, %d found", 1, len(containers))
} }
if containers[0].Id != container.Id { if containers[0].ID != container.ID {
t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.Id, containers[0].Id) t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID)
} }
} }
@ -369,7 +369,7 @@ func TestGetContainersExport(t *testing.T) {
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"}, Cmd: []string{"touch", "/test"},
}, },
) )
@ -383,7 +383,7 @@ func TestGetContainersExport(t *testing.T) {
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil { if err = getContainersExport(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -424,7 +424,7 @@ func TestGetContainersChanges(t *testing.T) {
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"}, Cmd: []string{"/bin/rm", "/etc/passwd"},
}, },
) )
@ -438,7 +438,7 @@ func TestGetContainersChanges(t *testing.T) {
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil { if err := getContainersChanges(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
changes := []Change{} changes := []Change{}
@ -472,7 +472,7 @@ func TestGetContainersByName(t *testing.T) {
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"}, Cmd: []string{"echo", "test"},
}, },
) )
@ -482,15 +482,15 @@ func TestGetContainersByName(t *testing.T) {
defer runtime.Destroy(container) defer runtime.Destroy(container)
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil { if err := getContainersByName(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
outContainer := &Container{} outContainer := &Container{}
if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil { if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if outContainer.Id != container.Id { if outContainer.ID != container.ID {
t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.Id, outContainer.Id) t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.ID, outContainer.ID)
} }
} }
@ -514,7 +514,7 @@ func TestPostAuth(t *testing.T) {
auth.SaveConfig(runtime.root, authStr, config.Email) auth.SaveConfig(runtime.root, authStr, config.Email)
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil { if err := getAuth(srv, APIVERSION, r, nil, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -542,7 +542,7 @@ func TestPostCommit(t *testing.T) {
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"}, Cmd: []string{"touch", "/test"},
}, },
) )
@ -555,24 +555,24 @@ func TestPostCommit(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.Id, bytes.NewReader([]byte{})) req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.ID, bytes.NewReader([]byte{}))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := postCommit(srv, API_VERSION, r, req, nil); err != nil { if err := postCommit(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusCreated { if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code) t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
} }
apiId := &ApiId{} apiID := &APIID{}
if err := json.Unmarshal(r.Body.Bytes(), apiId); err != nil { if err := json.Unmarshal(r.Body.Bytes(), apiID); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err := runtime.graph.Get(apiId.Id); err != nil { if _, err := runtime.graph.Get(apiID.ID); err != nil {
t.Fatalf("The image has not been commited") t.Fatalf("The image has not been commited")
} }
} }
@ -715,7 +715,7 @@ func TestPostImagesInsert(t *testing.T) {
// t.Fatalf("The test file has not been found") // t.Fatalf("The test file has not been found")
// } // }
// if err := srv.runtime.graph.Delete(img.Id); err != nil { // if err := srv.runtime.graph.Delete(img.ID); err != nil {
// t.Fatal(err) // t.Fatal(err)
// } // }
} }
@ -824,8 +824,8 @@ func TestPostContainersCreate(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
configJson, err := json.Marshal(&Config{ configJSON, err := json.Marshal(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Memory: 33554432, Memory: 33554432,
Cmd: []string{"touch", "/test"}, Cmd: []string{"touch", "/test"},
}) })
@ -833,25 +833,25 @@ func TestPostContainersCreate(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJson)) req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil { if err := postContainersCreate(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusCreated { if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code) t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
} }
apiRun := &ApiRun{} apiRun := &APIRun{}
if err := json.Unmarshal(r.Body.Bytes(), apiRun); err != nil { if err := json.Unmarshal(r.Body.Bytes(), apiRun); err != nil {
t.Fatal(err) t.Fatal(err)
} }
container := srv.runtime.Get(apiRun.Id) container := srv.runtime.Get(apiRun.ID)
if container == nil { if container == nil {
t.Fatalf("Container not created") t.Fatalf("Container not created")
} }
@ -880,7 +880,7 @@ func TestPostContainersKill(t *testing.T) {
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
OpenStdin: true, OpenStdin: true,
}, },
@ -902,7 +902,7 @@ func TestPostContainersKill(t *testing.T) {
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := postContainersKill(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil { if err := postContainersKill(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusNoContent { if r.Code != http.StatusNoContent {
@ -924,7 +924,7 @@ func TestPostContainersRestart(t *testing.T) {
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
OpenStdin: true, OpenStdin: true,
}, },
@ -945,12 +945,12 @@ func TestPostContainersRestart(t *testing.T) {
t.Errorf("Container should be running") t.Errorf("Container should be running")
} }
req, err := http.NewRequest("POST", "/containers/"+container.Id+"/restart?t=1", bytes.NewReader([]byte{})) req, err := http.NewRequest("POST", "/containers/"+container.ID+"/restart?t=1", bytes.NewReader([]byte{}))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := postContainersRestart(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil { if err := postContainersRestart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusNoContent { if r.Code != http.StatusNoContent {
@ -980,7 +980,7 @@ func TestPostContainersStart(t *testing.T) {
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
OpenStdin: true, OpenStdin: true,
}, },
@ -991,7 +991,7 @@ func TestPostContainersStart(t *testing.T) {
defer runtime.Destroy(container) defer runtime.Destroy(container)
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil { if err := postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusNoContent { if r.Code != http.StatusNoContent {
@ -1006,7 +1006,7 @@ func TestPostContainersStart(t *testing.T) {
} }
r = httptest.NewRecorder() r = httptest.NewRecorder()
if err = postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err == nil { if err = postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err == nil {
t.Fatalf("A running containter should be able to be started") t.Fatalf("A running containter should be able to be started")
} }
@ -1026,7 +1026,7 @@ func TestPostContainersStop(t *testing.T) {
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
OpenStdin: true, OpenStdin: true,
}, },
@ -1048,12 +1048,12 @@ func TestPostContainersStop(t *testing.T) {
} }
// Note: as it is a POST request, it requires a body. // Note: as it is a POST request, it requires a body.
req, err := http.NewRequest("POST", "/containers/"+container.Id+"/stop?t=1", bytes.NewReader([]byte{})) req, err := http.NewRequest("POST", "/containers/"+container.ID+"/stop?t=1", bytes.NewReader([]byte{}))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := postContainersStop(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil { if err := postContainersStop(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusNoContent { if r.Code != http.StatusNoContent {
@ -1075,7 +1075,7 @@ func TestPostContainersWait(t *testing.T) {
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sleep", "1"}, Cmd: []string{"/bin/sleep", "1"},
OpenStdin: true, OpenStdin: true,
}, },
@ -1091,10 +1091,10 @@ func TestPostContainersWait(t *testing.T) {
setTimeout(t, "Wait timed out", 3*time.Second, func() { setTimeout(t, "Wait timed out", 3*time.Second, func() {
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil { if err := postContainersWait(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
apiWait := &ApiWait{} apiWait := &APIWait{}
if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil { if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1119,7 +1119,7 @@ func TestPostContainersAttach(t *testing.T) {
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
OpenStdin: true, OpenStdin: true,
}, },
@ -1148,12 +1148,12 @@ func TestPostContainersAttach(t *testing.T) {
out: stdoutPipe, out: stdoutPipe,
} }
req, err := http.NewRequest("POST", "/containers/"+container.Id+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{})) req, err := http.NewRequest("POST", "/containers/"+container.ID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil { if err := postContainersAttach(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
}() }()
@ -1206,7 +1206,7 @@ func TestDeleteContainers(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"}, Cmd: []string{"touch", "/test"},
}) })
if err != nil { if err != nil {
@ -1218,19 +1218,19 @@ func TestDeleteContainers(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
req, err := http.NewRequest("DELETE", "/containers/"+container.Id, nil) req, err := http.NewRequest("DELETE", "/containers/"+container.ID, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := deleteContainers(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil { if err := deleteContainers(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusNoContent { if r.Code != http.StatusNoContent {
t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code) t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
} }
if c := runtime.Get(container.Id); c != nil { if c := runtime.Get(container.ID); c != nil {
t.Fatalf("The container as not been deleted") t.Fatalf("The container as not been deleted")
} }
@ -1267,14 +1267,14 @@ func TestDeleteImages(t *testing.T) {
} }
r := httptest.NewRecorder() r := httptest.NewRecorder()
if err := deleteImages(srv, API_VERSION, r, req, map[string]string{"name": "test:test"}); err != nil { if err := deleteImages(srv, APIVERSION, r, req, map[string]string{"name": "test:test"}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r.Code != http.StatusOK { if r.Code != http.StatusOK {
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code) t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
} }
var outs []ApiRmi var outs []APIRmi
if err := json.Unmarshal(r.Body.Bytes(), &outs); err != nil { if err := json.Unmarshal(r.Body.Bytes(), &outs); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -16,12 +16,12 @@ import (
const CONFIGFILE = ".dockercfg" const CONFIGFILE = ".dockercfg"
// the registry server we want to login against // the registry server we want to login against
const INDEX_SERVER = "https://index.docker.io/v1" const INDEXSERVER = "https://index.docker.io/v1"
//const INDEX_SERVER = "http://indexstaging-docker.dotcloud.com/" //const INDEXSERVER = "http://indexstaging-docker.dotcloud.com/"
var ( var (
ErrConfigFileMissing error = errors.New("The Auth config file is missing") ErrConfigFileMissing = errors.New("The Auth config file is missing")
) )
type AuthConfig struct { type AuthConfig struct {
@ -44,7 +44,7 @@ func IndexServerAddress() string {
if os.Getenv("DOCKER_INDEX_URL") != "" { if os.Getenv("DOCKER_INDEX_URL") != "" {
return os.Getenv("DOCKER_INDEX_URL") + "/v1" return os.Getenv("DOCKER_INDEX_URL") + "/v1"
} }
return INDEX_SERVER return INDEXSERVER
} }
// create a base64 encoded auth string to store in config // create a base64 encoded auth string to store in config

View File

@ -40,7 +40,7 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
} }
// Generate id // Generate id
id := GenerateId() id := GenerateID()
// Generate default hostname // Generate default hostname
// FIXME: the lxc template no longer needs to set a default hostname // FIXME: the lxc template no longer needs to set a default hostname
if config.Hostname == "" { if config.Hostname == "" {
@ -49,17 +49,17 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
container := &Container{ container := &Container{
// FIXME: we should generate the ID here instead of receiving it as an argument // FIXME: we should generate the ID here instead of receiving it as an argument
Id: id, ID: id,
Created: time.Now(), Created: time.Now(),
Path: config.Cmd[0], Path: config.Cmd[0],
Args: config.Cmd[1:], //FIXME: de-duplicate from config Args: config.Cmd[1:], //FIXME: de-duplicate from config
Config: config, Config: config,
Image: img.Id, // Always use the resolved image id Image: img.ID, // Always use the resolved image id
NetworkSettings: &NetworkSettings{}, NetworkSettings: &NetworkSettings{},
// FIXME: do we need to store this in the container? // FIXME: do we need to store this in the container?
SysInitPath: sysInitPath, SysInitPath: sysInitPath,
} }
container.root = builder.runtime.containerRoot(container.Id) container.root = builder.runtime.containerRoot(container.ID)
// Step 1: create the container directory. // Step 1: create the container directory.
// This doubles as a barrier to avoid race conditions. // This doubles as a barrier to avoid race conditions.
if err := os.Mkdir(container.root, 0700); err != nil { if err := os.Mkdir(container.root, 0700); err != nil {
@ -110,7 +110,7 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a
} }
// Register the image if needed // Register the image if needed
if repository != "" { if repository != "" {
if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil { if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
return img, err return img, err
} }
} }

View File

@ -63,11 +63,11 @@ func (b *builderClient) CmdFrom(name string) error {
return err return err
} }
img := &ApiId{} img := &APIID{}
if err := json.Unmarshal(obj, img); err != nil { if err := json.Unmarshal(obj, img); err != nil {
return err return err
} }
b.image = img.Id b.image = img.ID
utils.Debugf("Using image %s", b.image) utils.Debugf("Using image %s", b.image)
return nil return nil
} }
@ -91,19 +91,19 @@ func (b *builderClient) CmdRun(args string) error {
b.config.Cmd = nil b.config.Cmd = nil
MergeConfig(b.config, config) MergeConfig(b.config, config)
body, statusCode, err := b.cli.call("POST", "/images/getCache", &ApiImageConfig{Id: b.image, Config: b.config}) body, statusCode, err := b.cli.call("POST", "/images/getCache", &APIImageConfig{ID: b.image, Config: b.config})
if err != nil { if err != nil {
if statusCode != 404 { if statusCode != 404 {
return err return err
} }
} }
if statusCode != 404 { if statusCode != 404 {
apiId := &ApiId{} apiID := &APIID{}
if err := json.Unmarshal(body, apiId); err != nil { if err := json.Unmarshal(body, apiID); err != nil {
return err return err
} }
utils.Debugf("Use cached version") utils.Debugf("Use cached version")
b.image = apiId.Id b.image = apiID.ID
return nil return nil
} }
cid, err := b.run() cid, err := b.run()
@ -163,7 +163,7 @@ func (b *builderClient) CmdInsert(args string) error {
// return err // return err
// } // }
// apiId := &ApiId{} // apiId := &APIId{}
// if err := json.Unmarshal(body, apiId); err != nil { // if err := json.Unmarshal(body, apiId); err != nil {
// return err // return err
// } // }
@ -182,7 +182,7 @@ func (b *builderClient) run() (string, error) {
return "", err return "", err
} }
apiRun := &ApiRun{} apiRun := &APIRun{}
if err := json.Unmarshal(body, apiRun); err != nil { if err := json.Unmarshal(body, apiRun); err != nil {
return "", err return "", err
} }
@ -191,18 +191,18 @@ func (b *builderClient) run() (string, error) {
} }
//start the container //start the container
_, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/start", nil) _, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/start", nil)
if err != nil { if err != nil {
return "", err return "", err
} }
b.tmpContainers[apiRun.Id] = struct{}{} b.tmpContainers[apiRun.ID] = struct{}{}
// Wait for it to finish // Wait for it to finish
body, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/wait", nil) body, _, err = b.cli.call("POST", "/containers/"+apiRun.ID+"/wait", nil)
if err != nil { if err != nil {
return "", err return "", err
} }
apiWait := &ApiWait{} apiWait := &APIWait{}
if err := json.Unmarshal(body, apiWait); err != nil { if err := json.Unmarshal(body, apiWait); err != nil {
return "", err return "", err
} }
@ -210,7 +210,7 @@ func (b *builderClient) run() (string, error) {
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, apiWait.StatusCode) return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, apiWait.StatusCode)
} }
return apiRun.Id, nil return apiRun.ID, nil
} }
func (b *builderClient) commit(id string) error { func (b *builderClient) commit(id string) error {
@ -222,11 +222,11 @@ func (b *builderClient) commit(id string) error {
if id == "" { if id == "" {
cmd := b.config.Cmd cmd := b.config.Cmd
b.config.Cmd = []string{"true"} b.config.Cmd = []string{"true"}
if cid, err := b.run(); err != nil { cid, err := b.run()
if err != nil {
return err return err
} else {
id = cid
} }
id = cid
b.config.Cmd = cmd b.config.Cmd = cmd
} }
@ -239,12 +239,12 @@ func (b *builderClient) commit(id string) error {
if err != nil { if err != nil {
return err return err
} }
apiId := &ApiId{} apiID := &APIID{}
if err := json.Unmarshal(body, apiId); err != nil { if err := json.Unmarshal(body, apiID); err != nil {
return err return err
} }
b.tmpImages[apiId.Id] = struct{}{} b.tmpImages[apiID.ID] = struct{}{}
b.image = apiId.Id b.image = apiID.ID
b.needCommit = false b.needCommit = false
return nil return nil
} }

View File

@ -73,7 +73,7 @@ func (b *buildFile) CmdFrom(name string) error {
return err return err
} }
} }
b.image = image.Id b.image = image.ID
b.config = &Config{} b.config = &Config{}
return nil return nil
} }
@ -102,7 +102,7 @@ func (b *buildFile) CmdRun(args string) error {
return err return err
} else if cache != nil { } else if cache != nil {
utils.Debugf("[BUILDER] Use cached version") utils.Debugf("[BUILDER] Use cached version")
b.image = cache.Id b.image = cache.ID
return nil return nil
} else { } else {
utils.Debugf("[BUILDER] Cache miss") utils.Debugf("[BUILDER] Cache miss")
@ -238,7 +238,7 @@ func (b *buildFile) run() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
b.tmpContainers[c.Id] = struct{}{} b.tmpContainers[c.ID] = struct{}{}
//start the container //start the container
if err := c.Start(); err != nil { if err := c.Start(); err != nil {
@ -250,7 +250,7 @@ func (b *buildFile) run() (string, error) {
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret) return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
} }
return c.Id, nil return c.ID, nil
} }
// Commit the container <id> with the autorun command <autoCmd> // Commit the container <id> with the autorun command <autoCmd>
@ -266,17 +266,17 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
return err return err
} else if cache != nil { } else if cache != nil {
utils.Debugf("[BUILDER] Use cached version") utils.Debugf("[BUILDER] Use cached version")
b.image = cache.Id b.image = cache.ID
return nil return nil
} else { } else {
utils.Debugf("[BUILDER] Cache miss") utils.Debugf("[BUILDER] Cache miss")
} }
if cid, err := b.run(); err != nil { cid, err := b.run()
if err != nil {
return err return err
} else {
id = cid
} }
id = cid
} }
container := b.runtime.Get(id) container := b.runtime.Get(id)
@ -292,8 +292,8 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
if err != nil { if err != nil {
return err return err
} }
b.tmpImages[image.Id] = struct{}{} b.tmpImages[image.ID] = struct{}{}
b.image = image.Id b.image = image.ID
return nil return nil
} }

View File

@ -26,7 +26,7 @@ func TestBuild(t *testing.T) {
buildfile := NewBuildFile(srv, &utils.NopWriter{}) buildfile := NewBuildFile(srv, &utils.NopWriter{})
imgId, err := buildfile.Build(strings.NewReader(Dockerfile), nil) imgID, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -34,7 +34,7 @@ func TestBuild(t *testing.T) {
builder := NewBuilder(runtime) builder := NewBuilder(runtime)
container, err := builder.Create( container, err := builder.Create(
&Config{ &Config{
Image: imgId, Image: imgID,
Cmd: []string{"cat", "/tmp/passwd"}, Cmd: []string{"cat", "/tmp/passwd"},
}, },
) )
@ -53,7 +53,7 @@ func TestBuild(t *testing.T) {
container2, err := builder.Create( container2, err := builder.Create(
&Config{ &Config{
Image: imgId, Image: imgID,
Cmd: []string{"ls", "-d", "/var/run/sshd"}, Cmd: []string{"ls", "-d", "/var/run/sshd"},
}, },
) )

View File

@ -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 {

View File

@ -28,10 +28,10 @@ import (
"unicode" "unicode"
) )
const VERSION = "0.3.4" const VERSION = "0.4.0"
var ( var (
GIT_COMMIT string GITCOMMIT string
) )
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
@ -159,11 +159,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
file = os.Stdin file = os.Stdin
} else { } else {
// Send Dockerfile from arg/Dockerfile (deprecate later) // Send Dockerfile from arg/Dockerfile (deprecate later)
if f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile")); err != nil { f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile"))
if err != nil {
return err return err
} else {
file = f
} }
file = f
// Send context from arg // Send context from arg
// Create a FormFile multipart for the context if needed // Create a FormFile multipart for the context if needed
// FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage? // FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage?
@ -176,21 +176,21 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if err != nil { if err != nil {
return err return err
} }
if wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension()); err != nil { wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension())
if err != nil {
return err return err
} else {
// FIXME: Find a way to have a progressbar for the upload too
sf := utils.NewStreamFormatter(false)
io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf))
} }
// FIXME: Find a way to have a progressbar for the upload too
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)
} }
// Create a FormFile multipart for the Dockerfile // Create a FormFile multipart for the Dockerfile
if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil { wField, err := w.CreateFormFile("Dockerfile", "Dockerfile")
if err != nil {
return err return err
} else {
io.Copy(wField, file)
} }
io.Copy(wField, file)
multipartBody = io.MultiReader(multipartBody, boundary) multipartBody = io.MultiReader(multipartBody, boundary)
v := &url.Values{} v := &url.Values{}
@ -276,9 +276,8 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
oldState, err := term.SetRawTerminal() oldState, err := term.SetRawTerminal()
if err != nil { if err != nil {
return err return err
} else {
defer term.RestoreTerminal(oldState)
} }
defer term.RestoreTerminal(oldState)
cmd := Subcmd("login", "", "Register or Login to the docker registry server") cmd := Subcmd("login", "", "Register or Login to the docker registry server")
if err := cmd.Parse(args); err != nil { if err := cmd.Parse(args); err != nil {
@ -331,7 +330,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
return err return err
} }
var out2 ApiAuth var out2 APIAuth
err = json.Unmarshal(body, &out2) err = json.Unmarshal(body, &out2)
if err != nil { if err != nil {
return err return err
@ -358,7 +357,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
if err != nil { if err != nil {
fmt.Printf("%s", err) fmt.Printf("%s", err)
} else { } else {
var out ApiWait var out APIWait
err = json.Unmarshal(body, &out) err = json.Unmarshal(body, &out)
if err != nil { if err != nil {
return err return err
@ -386,21 +385,20 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
return err return err
} }
var out ApiVersion var out APIVersion
err = json.Unmarshal(body, &out) err = json.Unmarshal(body, &out)
if err != nil { if err != nil {
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,15 +418,24 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
return err return err
} }
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
} }
@ -575,7 +582,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "%s", err) fmt.Fprintf(os.Stderr, "%s", err)
} else { } else {
var outs []ApiRmi var outs []APIRmi
err = json.Unmarshal(body, &outs) err = json.Unmarshal(body, &outs)
if err != nil { if err != nil {
return err return err
@ -607,7 +614,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
return err return err
} }
var outs []ApiHistory var outs []APIHistory
err = json.Unmarshal(body, &outs) err = json.Unmarshal(body, &outs)
if err != nil { if err != nil {
return err return err
@ -616,7 +623,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
fmt.Fprintln(w, "ID\tCREATED\tCREATED BY") fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
for _, out := range outs { for _, out := range outs {
fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
} }
w.Flush() w.Flush()
return nil return nil
@ -742,12 +749,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)
@ -795,7 +796,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
return err return err
} }
var outs []ApiImages var outs []APIImages
err = json.Unmarshal(body, &outs) err = json.Unmarshal(body, &outs)
if err != nil { if err != nil {
return err return err
@ -817,16 +818,16 @@ func (cli *DockerCli) CmdImages(args ...string) error {
if !*quiet { if !*quiet {
fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag) fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag)
if *noTrunc { if *noTrunc {
fmt.Fprintf(w, "%s\t", out.Id) fmt.Fprintf(w, "%s\t", out.ID)
} else { } else {
fmt.Fprintf(w, "%s\t", utils.TruncateId(out.Id)) fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID))
} }
fmt.Fprintf(w, "%s ago\n", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) fmt.Fprintf(w, "%s ago\n", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
} else { } else {
if *noTrunc { if *noTrunc {
fmt.Fprintln(w, out.Id) fmt.Fprintln(w, out.ID)
} else { } else {
fmt.Fprintln(w, utils.TruncateId(out.Id)) fmt.Fprintln(w, utils.TruncateID(out.ID))
} }
} }
} }
@ -873,7 +874,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
return err return err
} }
var outs []ApiContainers var outs []APIContainers
err = json.Unmarshal(body, &outs) err = json.Unmarshal(body, &outs)
if err != nil { if err != nil {
return err return err
@ -886,15 +887,15 @@ func (cli *DockerCli) CmdPs(args ...string) error {
for _, out := range outs { for _, out := range outs {
if !*quiet { if !*quiet {
if *noTrunc { if *noTrunc {
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", out.Id, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
} else { } else {
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", utils.TruncateId(out.Id), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\n", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports)
} }
} else { } else {
if *noTrunc { if *noTrunc {
fmt.Fprintln(w, out.Id) fmt.Fprintln(w, out.ID)
} else { } else {
fmt.Fprintln(w, utils.TruncateId(out.Id)) fmt.Fprintln(w, utils.TruncateID(out.ID))
} }
} }
} }
@ -937,13 +938,13 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
return err return err
} }
apiId := &ApiId{} apiID := &APIID{}
err = json.Unmarshal(body, apiId) err = json.Unmarshal(body, apiID)
if err != nil { if err != nil {
return err return err
} }
fmt.Println(apiId.Id) fmt.Println(apiID.ID)
return nil return nil
} }
@ -1080,7 +1081,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
return err return err
} }
outs := []ApiSearch{} outs := []APISearch{}
err = json.Unmarshal(body, &outs) err = json.Unmarshal(body, &outs)
if err != nil { if err != nil {
return err return err
@ -1212,7 +1213,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
return err return err
} }
out := &ApiRun{} out := &APIRun{}
err = json.Unmarshal(body, out) err = json.Unmarshal(body, out)
if err != nil { if err != nil {
return err return err
@ -1233,18 +1234,21 @@ func (cli *DockerCli) CmdRun(args ...string) error {
} }
//start the container //start the container
_, _, err = cli.call("POST", "/containers/"+out.Id+"/start", nil) _, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
if err != nil { if err != nil {
return err return err
} }
if !config.AttachStdout && !config.AttachStderr {
fmt.Println(out.ID)
}
if connections > 0 { if connections > 0 {
chErrors := make(chan error, connections) chErrors := make(chan error, connections)
cli.monitorTtySize(out.Id) cli.monitorTtySize(out.ID)
if splitStderr && config.AttachStderr { if splitStderr && config.AttachStderr {
go func() { go func() {
chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr) chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr)
}() }()
} }
@ -1262,7 +1266,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
v.Set("stderr", "1") v.Set("stderr", "1")
} }
go func() { go func() {
chErrors <- cli.hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout) chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout)
}() }()
for connections > 0 { for connections > 0 {
err := <-chErrors err := <-chErrors
@ -1272,9 +1276,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
connections -= 1 connections -= 1
} }
} }
if !config.AttachStdout && !config.AttachStderr {
fmt.Println(out.Id)
}
return nil return nil
} }
@ -1322,7 +1323,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
params = bytes.NewBuffer(buf) params = bytes.NewBuffer(buf)
} }
req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), params) req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), params)
if err != nil { if err != nil {
return nil, -1, err return nil, -1, err
} }
@ -1354,7 +1355,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
if (method == "POST" || method == "PUT") && in == nil { if (method == "POST" || method == "PUT") && in == nil {
in = bytes.NewReader([]byte{}) in = bytes.NewReader([]byte{})
} }
req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), in) req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), in)
if err != nil { if err != nil {
return err return err
} }
@ -1381,14 +1382,14 @@ 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" {
dec := json.NewDecoder(resp.Body) dec := json.NewDecoder(resp.Body)
for { for {
var m utils.JsonMessage 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 {
return err return err
} }
if m.Progress != "" { if m.Progress != "" {
fmt.Fprintf(out, "Downloading %s\r", m.Progress) fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress)
} else if m.Error != "" { } else if m.Error != "" {
return fmt.Errorf(m.Error) return fmt.Errorf(m.Error)
} else { } else {
@ -1404,7 +1405,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
} }
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error { func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error {
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", API_VERSION, path), nil) req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
if err != nil { if err != nil {
return err return err
} }
@ -1425,12 +1426,12 @@ 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 { oldState, err := term.SetRawTerminal()
if err != nil {
return err return err
} else {
defer term.RestoreTerminal(oldState)
} }
defer term.RestoreTerminal(oldState)
} }
sendStdin := utils.Go(func() error { sendStdin := utils.Go(func() error {
_, err := io.Copy(rwc, in) _, err := io.Copy(rwc, in)
@ -1444,7 +1445,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
} }

View File

@ -23,7 +23,7 @@ import (
type Container struct { type Container struct {
root string root string
Id string ID string
Created time.Time Created time.Time
@ -167,8 +167,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
} }
type NetworkSettings struct { type NetworkSettings struct {
IpAddress string IPAddress string
IpPrefixLen int IPPrefixLen int
Gateway string Gateway string
Bridge string Bridge string
PortMapping map[string]string PortMapping map[string]string
@ -409,7 +409,7 @@ func (container *Container) Start() error {
defer container.State.unlock() defer container.State.unlock()
if container.State.Running { if container.State.Running {
return fmt.Errorf("The container %s is already running.", container.Id) return fmt.Errorf("The container %s is already running.", container.ID)
} }
if err := container.EnsureMounted(); err != nil { if err := container.EnsureMounted(); err != nil {
return err return err
@ -431,24 +431,24 @@ func (container *Container) Start() error {
// Create the requested volumes volumes // Create the requested volumes volumes
for volPath := range container.Config.Volumes { for volPath := range container.Config.Volumes {
if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil { c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
if err != nil {
return err return err
} else {
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = c.Id
} }
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = c.ID
} }
if container.Config.VolumesFrom != "" { if container.Config.VolumesFrom != "" {
c := container.runtime.Get(container.Config.VolumesFrom) c := container.runtime.Get(container.Config.VolumesFrom)
if c == nil { if c == nil {
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id) return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
} }
for volPath, id := range c.Volumes { for volPath, id := range c.Volumes {
if _, exists := container.Volumes[volPath]; exists { if _, exists := container.Volumes[volPath]; exists {
return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id) return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.ID)
} }
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil return nil
@ -462,7 +462,7 @@ func (container *Container) Start() error {
} }
params := []string{ params := []string{
"-n", container.Id, "-n", container.ID,
"-f", container.lxcConfigPath(), "-f", container.lxcConfigPath(),
"--", "--",
"/sbin/init", "/sbin/init",
@ -573,17 +573,17 @@ func (container *Container) allocateNetwork() error {
} }
container.NetworkSettings.PortMapping = make(map[string]string) container.NetworkSettings.PortMapping = make(map[string]string)
for _, spec := range container.Config.PortSpecs { for _, spec := range container.Config.PortSpecs {
if nat, err := iface.AllocatePort(spec); err != nil { nat, err := iface.AllocatePort(spec)
if err != nil {
iface.Release() iface.Release()
return err return err
} else {
container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
} }
container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
} }
container.network = iface container.network = iface
container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
container.NetworkSettings.IpAddress = iface.IPNet.IP.String() container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size() container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
container.NetworkSettings.Gateway = iface.Gateway.String() container.NetworkSettings.Gateway = iface.Gateway.String()
return nil return nil
} }
@ -597,16 +597,16 @@ func (container *Container) releaseNetwork() {
// FIXME: replace this with a control socket within docker-init // FIXME: replace this with a control socket within docker-init
func (container *Container) waitLxc() error { func (container *Container) waitLxc() error {
for { for {
if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil { output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
if err != nil {
return err return err
} else { }
if !strings.Contains(string(output), "RUNNING") { if !strings.Contains(string(output), "RUNNING") {
return nil return nil
}
} }
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
} }
return nil panic("Unreachable")
} }
func (container *Container) monitor() { func (container *Container) monitor() {
@ -616,17 +616,17 @@ func (container *Container) monitor() {
// If the command does not exists, try to wait via lxc // If the command does not exists, try to wait via lxc
if container.cmd == nil { if container.cmd == nil {
if err := container.waitLxc(); err != nil { if err := container.waitLxc(); err != nil {
utils.Debugf("%s: Process: %s", container.Id, err) utils.Debugf("%s: Process: %s", container.ID, err)
} }
} else { } else {
if err := container.cmd.Wait(); err != nil { if err := container.cmd.Wait(); err != nil {
// Discard the error as any signals or non 0 returns will generate an error // Discard the error as any signals or non 0 returns will generate an error
utils.Debugf("%s: Process: %s", container.Id, err) utils.Debugf("%s: Process: %s", container.ID, err)
} }
} }
utils.Debugf("Process finished") utils.Debugf("Process finished")
var exitCode int = -1 exitCode := -1
if container.cmd != nil { if container.cmd != nil {
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
} }
@ -635,24 +635,24 @@ func (container *Container) monitor() {
container.releaseNetwork() container.releaseNetwork()
if container.Config.OpenStdin { if container.Config.OpenStdin {
if err := container.stdin.Close(); err != nil { if err := container.stdin.Close(); err != nil {
utils.Debugf("%s: Error close stdin: %s", container.Id, err) utils.Debugf("%s: Error close stdin: %s", container.ID, err)
} }
} }
if err := container.stdout.CloseWriters(); err != nil { if err := container.stdout.CloseWriters(); err != nil {
utils.Debugf("%s: Error close stdout: %s", container.Id, err) utils.Debugf("%s: Error close stdout: %s", container.ID, err)
} }
if err := container.stderr.CloseWriters(); err != nil { if err := container.stderr.CloseWriters(); err != nil {
utils.Debugf("%s: Error close stderr: %s", container.Id, err) utils.Debugf("%s: Error close stderr: %s", container.ID, err)
} }
if container.ptyMaster != nil { if container.ptyMaster != nil {
if err := container.ptyMaster.Close(); err != nil { if err := container.ptyMaster.Close(); err != nil {
utils.Debugf("%s: Error closing Pty master: %s", container.Id, err) utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
} }
} }
if err := container.Unmount(); err != nil { if err := container.Unmount(); err != nil {
log.Printf("%v: Failed to umount filesystem: %v", container.Id, err) log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
} }
// Re-create a brand new stdin pipe once the container exited // Re-create a brand new stdin pipe once the container exited
@ -673,7 +673,7 @@ func (container *Container) monitor() {
// This is because State.setStopped() has already been called, and has caused Wait() // This is because State.setStopped() has already been called, and has caused Wait()
// to return. // to return.
// FIXME: why are we serializing running state to disk in the first place? // FIXME: why are we serializing running state to disk in the first place?
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.Id, err) //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
} }
} }
@ -683,17 +683,17 @@ func (container *Container) kill() error {
} }
// Sending SIGKILL to the process via lxc // Sending SIGKILL to the process via lxc
output, err := exec.Command("lxc-kill", "-n", container.Id, "9").CombinedOutput() output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
if err != nil { if err != nil {
log.Printf("error killing container %s (%s, %s)", container.Id, output, err) log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
} }
// 2. Wait for the process to die, in last resort, try to kill the process directly // 2. Wait for the process to die, in last resort, try to kill the process directly
if err := container.WaitTimeout(10 * time.Second); err != nil { if err := container.WaitTimeout(10 * time.Second); err != nil {
if container.cmd == nil { if container.cmd == nil {
return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.Id) return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID)
} }
log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.Id) log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.ID)
if err := container.cmd.Process.Kill(); err != nil { if err := container.cmd.Process.Kill(); err != nil {
return err return err
} }
@ -721,7 +721,7 @@ func (container *Container) Stop(seconds int) error {
} }
// 1. Send a SIGTERM // 1. Send a SIGTERM
if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil { if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
log.Print(string(output)) log.Print(string(output))
log.Print("Failed to send SIGTERM to the process, force killing") log.Print("Failed to send SIGTERM to the process, force killing")
if err := container.kill(); err != nil { if err := container.kill(); err != nil {
@ -731,7 +731,7 @@ func (container *Container) Stop(seconds int) error {
// 2. Wait for the process to exit on its own // 2. Wait for the process to exit on its own
if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil { if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.Id, seconds) log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
if err := container.kill(); err != nil { if err := container.kill(); err != nil {
return err return err
} }
@ -795,7 +795,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
case <-done: case <-done:
return nil return nil
} }
panic("unreachable")
panic("Unreachable")
} }
func (container *Container) EnsureMounted() error { func (container *Container) EnsureMounted() error {
@ -838,16 +839,16 @@ func (container *Container) Unmount() error {
return Unmount(container.RootfsPath()) return Unmount(container.RootfsPath())
} }
// ShortId returns a shorthand version of the container's id for convenience. // ShortID returns a shorthand version of the container's id for convenience.
// A collision with other container shorthands is very unlikely, but possible. // A collision with other container shorthands is very unlikely, but possible.
// In case of a collision a lookup with Runtime.Get() will fail, and the caller // In case of a collision a lookup with Runtime.Get() will fail, and the caller
// will need to use a langer prefix, or the full-length container Id. // will need to use a langer prefix, or the full-length container Id.
func (container *Container) ShortId() string { func (container *Container) ShortID() string {
return utils.TruncateId(container.Id) return utils.TruncateID(container.ID)
} }
func (container *Container) logPath(name string) string { func (container *Container) logPath(name string) string {
return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name)) return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
} }
func (container *Container) ReadLog(name string) (io.Reader, error) { func (container *Container) ReadLog(name string) (io.Reader, error) {
@ -887,7 +888,7 @@ func (container *Container) rwPath() string {
return path.Join(container.root, "rw") return path.Join(container.root, "rw")
} }
func validateId(id string) error { func validateID(id string) error {
if id == "" { if id == "" {
return fmt.Errorf("Invalid empty id") return fmt.Errorf("Invalid empty id")
} }

View File

@ -14,7 +14,7 @@ import (
"time" "time"
) )
func TestIdFormat(t *testing.T) { func TestIDFormat(t *testing.T) {
runtime, err := newTestRuntime() runtime, err := newTestRuntime()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -22,19 +22,19 @@ func TestIdFormat(t *testing.T) {
defer nuke(runtime) defer nuke(runtime)
container1, err := NewBuilder(runtime).Create( container1, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello world"}, Cmd: []string{"/bin/sh", "-c", "echo hello world"},
}, },
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.Id)) match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.ID))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !match { if !match {
t.Fatalf("Invalid container ID: %s", container1.Id) t.Fatalf("Invalid container ID: %s", container1.ID)
} }
} }
@ -46,7 +46,7 @@ func TestMultipleAttachRestart(t *testing.T) {
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", Cmd: []string{"/bin/sh", "-c",
"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"}, "i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"},
}, },
@ -153,7 +153,7 @@ func TestDiff(t *testing.T) {
// Create a container and remove a file // Create a container and remove a file
container1, err := builder.Create( container1, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"}, Cmd: []string{"/bin/rm", "/etc/passwd"},
}, },
) )
@ -194,7 +194,7 @@ func TestDiff(t *testing.T) {
// Create a new container from the commited image // Create a new container from the commited image
container2, err := builder.Create( container2, err := builder.Create(
&Config{ &Config{
Image: img.Id, Image: img.ID,
Cmd: []string{"cat", "/etc/passwd"}, Cmd: []string{"cat", "/etc/passwd"},
}, },
) )
@ -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) {
@ -229,7 +260,7 @@ func TestCommitAutoRun(t *testing.T) {
builder := NewBuilder(runtime) builder := NewBuilder(runtime)
container1, err := builder.Create( container1, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"}, Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
}, },
) )
@ -260,7 +291,7 @@ func TestCommitAutoRun(t *testing.T) {
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world // FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
container2, err := builder.Create( container2, err := builder.Create(
&Config{ &Config{
Image: img.Id, Image: img.ID,
}, },
) )
if err != nil { if err != nil {
@ -309,7 +340,7 @@ func TestCommitRun(t *testing.T) {
container1, err := builder.Create( container1, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"}, Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
}, },
) )
@ -341,7 +372,7 @@ func TestCommitRun(t *testing.T) {
container2, err := builder.Create( container2, err := builder.Create(
&Config{ &Config{
Image: img.Id, Image: img.ID,
Cmd: []string{"cat", "/world"}, Cmd: []string{"cat", "/world"},
}, },
) )
@ -388,7 +419,7 @@ func TestStart(t *testing.T) {
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Memory: 33554432, Memory: 33554432,
CpuShares: 1000, CpuShares: 1000,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
@ -432,7 +463,7 @@ func TestRun(t *testing.T) {
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"}, Cmd: []string{"ls", "-al"},
}, },
) )
@ -460,7 +491,7 @@ func TestOutput(t *testing.T) {
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := NewBuilder(runtime).Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"}, Cmd: []string{"echo", "-n", "foobar"},
}, },
) )
@ -484,7 +515,7 @@ func TestKillDifferentUser(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"tail", "-f", "/etc/resolv.conf"}, Cmd: []string{"tail", "-f", "/etc/resolv.conf"},
User: "daemon", User: "daemon",
}, },
@ -532,7 +563,7 @@ func TestKill(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/dev/zero"}, Cmd: []string{"cat", "/dev/zero"},
}, },
) )
@ -580,7 +611,7 @@ func TestExitCode(t *testing.T) {
builder := NewBuilder(runtime) builder := NewBuilder(runtime)
trueContainer, err := builder.Create(&Config{ trueContainer, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true", ""}, Cmd: []string{"/bin/true", ""},
}) })
if err != nil { if err != nil {
@ -595,7 +626,7 @@ func TestExitCode(t *testing.T) {
} }
falseContainer, err := builder.Create(&Config{ falseContainer, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/false", ""}, Cmd: []string{"/bin/false", ""},
}) })
if err != nil { if err != nil {
@ -617,7 +648,7 @@ func TestRestart(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"}, Cmd: []string{"echo", "-n", "foobar"},
}, },
) )
@ -650,7 +681,7 @@ func TestRestartStdin(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"}, Cmd: []string{"cat"},
OpenStdin: true, OpenStdin: true,
@ -732,7 +763,7 @@ func TestUser(t *testing.T) {
// Default user must be root // Default user must be root
container, err := builder.Create(&Config{ container, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
}, },
) )
@ -750,7 +781,7 @@ func TestUser(t *testing.T) {
// Set a username // Set a username
container, err = builder.Create(&Config{ container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
User: "root", User: "root",
@ -770,7 +801,7 @@ func TestUser(t *testing.T) {
// Set a UID // Set a UID
container, err = builder.Create(&Config{ container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
User: "0", User: "0",
@ -790,7 +821,7 @@ func TestUser(t *testing.T) {
// Set a different user by uid // Set a different user by uid
container, err = builder.Create(&Config{ container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
User: "1", User: "1",
@ -812,7 +843,7 @@ func TestUser(t *testing.T) {
// Set a different user by username // Set a different user by username
container, err = builder.Create(&Config{ container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
User: "daemon", User: "daemon",
@ -841,7 +872,7 @@ func TestMultipleContainers(t *testing.T) {
builder := NewBuilder(runtime) builder := NewBuilder(runtime)
container1, err := builder.Create(&Config{ container1, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/dev/zero"}, Cmd: []string{"cat", "/dev/zero"},
}, },
) )
@ -851,7 +882,7 @@ func TestMultipleContainers(t *testing.T) {
defer runtime.Destroy(container1) defer runtime.Destroy(container1)
container2, err := builder.Create(&Config{ container2, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/dev/zero"}, Cmd: []string{"cat", "/dev/zero"},
}, },
) )
@ -897,7 +928,7 @@ func TestStdin(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"}, Cmd: []string{"cat"},
OpenStdin: true, OpenStdin: true,
@ -944,7 +975,7 @@ func TestTty(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"}, Cmd: []string{"cat"},
OpenStdin: true, OpenStdin: true,
@ -991,7 +1022,7 @@ func TestEnv(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/usr/bin/env"}, Cmd: []string{"/usr/bin/env"},
}, },
) )
@ -1069,7 +1100,7 @@ func TestLXCConfig(t *testing.T) {
cpuMax := 10000 cpuMax := 10000
cpu := cpuMin + rand.Intn(cpuMax-cpuMin) cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true"}, Cmd: []string{"/bin/true"},
Hostname: "foobar", Hostname: "foobar",
@ -1097,7 +1128,7 @@ func BenchmarkRunSequencial(b *testing.B) {
defer nuke(runtime) defer nuke(runtime)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"}, Cmd: []string{"echo", "-n", "foo"},
}, },
) )
@ -1132,7 +1163,7 @@ func BenchmarkRunParallel(b *testing.B) {
tasks = append(tasks, complete) tasks = append(tasks, complete)
go func(i int, complete chan error) { go func(i int, complete chan error) {
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"}, Cmd: []string{"echo", "-n", "foo"},
}, },
) )

View File

@ -11,13 +11,13 @@ import (
"time" "time"
) )
var DOCKER_PATH string = path.Join(os.Getenv("DOCKERPATH"), "docker") var DOCKERPATH = path.Join(os.Getenv("DOCKERPATH"), "docker")
// WARNING: this crashTest will 1) crash your host, 2) remove all containers // WARNING: this crashTest will 1) crash your host, 2) remove all containers
func runDaemon() (*exec.Cmd, error) { func runDaemon() (*exec.Cmd, error) {
os.Remove("/var/run/docker.pid") os.Remove("/var/run/docker.pid")
exec.Command("rm", "-rf", "/var/lib/docker/containers").Run() exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
cmd := exec.Command(DOCKER_PATH, "-d") cmd := exec.Command(DOCKERPATH, "-d")
outPipe, err := cmd.StdoutPipe() outPipe, err := cmd.StdoutPipe()
if err != nil { if err != nil {
return nil, err return nil, err
@ -77,7 +77,7 @@ func crashTest() error {
stop = false stop = false
for i := 0; i < 100 && !stop; { for i := 0; i < 100 && !stop; {
func() error { func() error {
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount)) cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
i++ i++
totalTestCount++ totalTestCount++
outPipe, err := cmd.StdoutPipe() outPipe, err := cmd.StdoutPipe()

View File

@ -1 +0,0 @@
Solomon Hykes <solomon@dotcloud.com>

View File

@ -1,68 +0,0 @@
# docker-build: build your software with docker
## Description
docker-build is a script to build docker images from source. It will be deprecated once the 'build' feature is incorporated into docker itself (See https://github.com/dotcloud/docker/issues/278)
Author: Solomon Hykes <solomon@dotcloud.com>
## Install
docker-builder requires:
1) A reasonably recent Python setup (tested on 2.7.2).
2) A running docker daemon at version 0.1.4 or more recent (http://www.docker.io/gettingstarted)
## Usage
First create a valid Changefile, which defines a sequence of changes to apply to a base image.
$ cat Changefile
# Start build from a know base image
from base:ubuntu-12.10
# Update ubuntu sources
run echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
run apt-get update
# Install system packages
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
# Insert files from the host (./myscript must be present in the current directory)
copy myscript /usr/local/bin/myscript
Run docker-build, and pass the contents of your Changefile as standard input.
$ IMG=$(./docker-build < Changefile)
This will take a while: for each line of the changefile, docker-build will:
1. Create a new container to execute the given command or insert the given file
2. Wait for the container to complete execution
3. Commit the resulting changes as a new image
4. Use the resulting image as the input of the next step
If all the steps succeed, the result will be an image containing the combined results of each build step.
You can trace back those build steps by inspecting the image's history:
$ docker history $IMG
ID CREATED CREATED BY
1e9e2045de86 A few seconds ago /bin/sh -c cat > /usr/local/bin/myscript; chmod +x /usr/local/bin/git
77db140aa62a A few seconds ago /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
77db140aa62a A few seconds ago /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
77db140aa62a A few seconds ago /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
83e85d155451 A few seconds ago /bin/sh -c apt-get update
bfd53b36d9d3 A few seconds ago /bin/sh -c echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
base 2 weeks ago /bin/bash
27cf78414709 2 weeks ago
Note that your build started from 'base', as instructed by your Changefile. But that base image itself seems to have been built in 2 steps - hence the extra step in the history.
You can use this build technique to create any image you want: a database, a web application, or anything else that can be build by a sequence of unix commands - in other words, anything else.

View File

@ -1,142 +0,0 @@
#!/usr/bin/env python
# docker-build is a script to build docker images from source.
# It will be deprecated once the 'build' feature is incorporated into docker itself.
# (See https://github.com/dotcloud/docker/issues/278)
#
# Author: Solomon Hykes <solomon@dotcloud.com>
# First create a valid Changefile, which defines a sequence of changes to apply to a base image.
#
# $ cat Changefile
# # Start build from a know base image
# from base:ubuntu-12.10
# # Update ubuntu sources
# run echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
# run apt-get update
# # Install system packages
# run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
# run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
# run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
# # Insert files from the host (./myscript must be present in the current directory)
# copy myscript /usr/local/bin/myscript
#
#
# Run docker-build, and pass the contents of your Changefile as standard input.
#
# $ IMG=$(./docker-build < Changefile)
#
# This will take a while: for each line of the changefile, docker-build will:
#
# 1. Create a new container to execute the given command or insert the given file
# 2. Wait for the container to complete execution
# 3. Commit the resulting changes as a new image
# 4. Use the resulting image as the input of the next step
import sys
import subprocess
import json
import hashlib
def docker(args, stdin=None):
print "# docker " + " ".join(args)
p = subprocess.Popen(["docker"] + list(args), stdin=stdin, stdout=subprocess.PIPE)
return p.stdout
def image_exists(img):
return docker(["inspect", img]).read().strip() != ""
def image_config(img):
return json.loads(docker(["inspect", img]).read()).get("config", {})
def run_and_commit(img_in, cmd, stdin=None, author=None, run=None):
run_id = docker(["run"] + (["-i", "-a", "stdin"] if stdin else ["-d"]) + [img_in, "/bin/sh", "-c", cmd], stdin=stdin).read().rstrip()
print "---> Waiting for " + run_id
result=int(docker(["wait", run_id]).read().rstrip())
if result != 0:
print "!!! '{}' return non-zero exit code '{}'. Aborting.".format(cmd, result)
sys.exit(1)
return docker(["commit"] + (["-author", author] if author else []) + (["-run", json.dumps(run)] if run is not None else []) + [run_id]).read().rstrip()
def insert(base, src, dst, author=None):
print "COPY {} to {} in {}".format(src, dst, base)
if dst == "":
raise Exception("Missing destination path")
stdin = file(src)
stdin.seek(0)
return run_and_commit(base, "cat > {0}; chmod +x {0}".format(dst), stdin=stdin, author=author)
def add(base, src, dst, author=None):
print "PUSH to {} in {}".format(dst, base)
if src == ".":
tar = subprocess.Popen(["tar", "-c", "."], stdout=subprocess.PIPE).stdout
else:
tar = subprocess.Popen(["curl", src], stdout=subprocess.PIPE).stdout
if dst == "":
raise Exception("Missing argument to push")
return run_and_commit(base, "mkdir -p '{0}' && tar -C '{0}' -x".format(dst), stdin=tar, author=author)
def main():
base=""
maintainer=""
steps = []
try:
for line in sys.stdin.readlines():
line = line.strip()
# Skip comments and empty lines
if line == "" or line[0] == "#":
continue
op, param = line.split(None, 1)
print op.upper() + " " + param
if op == "from":
base = param
steps.append(base)
elif op == "maintainer":
maintainer = param
elif op == "run":
result = run_and_commit(base, param, author=maintainer)
steps.append(result)
base = result
print "===> " + base
elif op == "copy":
src, dst = param.split(" ", 1)
result = insert(base, src, dst, author=maintainer)
steps.append(result)
base = result
print "===> " + base
elif op == "add":
src, dst = param.split(" ", 1)
result = add(base, src, dst, author=maintainer)
steps.append(result)
base=result
print "===> " + base
elif op == "expose":
config = image_config(base)
if config.get("PortSpecs") is None:
config["PortSpecs"] = []
portspec = param.strip()
config["PortSpecs"].append(portspec)
result = run_and_commit(base, "# (nop) expose port {}".format(portspec), author=maintainer, run=config)
steps.append(result)
base=result
print "===> " + base
elif op == "cmd":
config = image_config(base)
cmd = list(json.loads(param))
config["Cmd"] = cmd
result = run_and_commit(base, "# (nop) set default command to '{}'".format(" ".join(cmd)), author=maintainer, run=config)
steps.append(result)
base=result
print "===> " + base
else:
print "Skipping uknown op " + op
except:
docker(["rmi"] + steps[1:])
raise
print base
if __name__ == "__main__":
main()

View File

@ -1,13 +0,0 @@
# Start build from a know base image
maintainer Solomon Hykes <solomon@dotcloud.com>
from base:ubuntu-12.10
# Update ubuntu sources
run echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
run apt-get update
# Install system packages
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
# Insert files from the host (./myscript must be present in the current directory)
copy myscript /usr/local/bin/myscript
push /src

View File

@ -1,3 +0,0 @@
#!/bin/sh
echo hello, world!

View File

@ -15,7 +15,7 @@ import (
) )
var ( var (
GIT_COMMIT string GITCOMMIT string
) )
func main() { func main() {
@ -59,7 +59,7 @@ func main() {
if *flDebug { if *flDebug {
os.Setenv("DEBUG", "1") os.Setenv("DEBUG", "1")
} }
docker.GIT_COMMIT = GIT_COMMIT docker.GITCOMMIT = GITCOMMIT
if *flDaemon { if *flDaemon {
if flag.NArg() != 0 { if flag.NArg() != 0 {
flag.Usage() flag.Usage()

View File

@ -63,7 +63,7 @@ site:
connect: connect:
@echo connecting dotcloud to www.docker.io website, make sure to use user 1 @echo connecting dotcloud to www.docker.io website, make sure to use user 1
@cd _build/website/ ; \ @cd _build/website/ ; \
dotcloud connect dockerwebsite ; dotcloud connect dockerwebsite ; \
dotcloud list dotcloud list
push: push:

View File

@ -564,7 +564,7 @@ Create an image
Content-Type: application/json Content-Type: application/json
{"status":"Pulling..."} {"status":"Pulling..."}
{"progress":"1/? (n/a)"} {"status":"Pulling", "progress":"1/? (n/a)"}
{"error":"Invalid..."} {"error":"Invalid..."}
... ...
@ -607,7 +607,7 @@ Insert a file in a image
Content-Type: application/json Content-Type: application/json
{"status":"Inserting..."} {"status":"Inserting..."}
{"progress":"1/? (n/a)"} {"status":"Inserting", "progress":"1/? (n/a)"}
{"error":"Invalid..."} {"error":"Invalid..."}
... ...
@ -734,7 +734,7 @@ Push an image on the registry
Content-Type: application/json Content-Type: application/json
{"status":"Pushing..."} {"status":"Pushing..."}
{"progress":"1/? (n/a)"} {"status":"Pushing", "progress":"1/? (n/a)"}
{"error":"Invalid..."} {"error":"Invalid..."}
... ...
@ -974,10 +974,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
@ -1003,12 +1005,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

View File

@ -5,5 +5,5 @@
Contributing to Docker Contributing to Docker
====================== ======================
Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`. Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_.

View File

@ -5,11 +5,35 @@
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 Precise 12.04 (LTS) (64-bit),
Dependencies
------------
**Linux kernel 3.8**
Due to a bug in LXC docker works best on the 3.8 kernel. Precise comes with a 3.2 kernel, so we need to upgrade it. The kernel we install comes with AUFS built in.
.. code-block:: bash .. code-block:: bash
sudo apt-get -y install lxc wget bsdtar curl golang git # install the backported kernel
sudo apt-get update && sudo apt-get install linux-image-generic-lts-raring
# reboot
sudo reboot
Installation
------------
.. code-block:: bash
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:gophers/go
sudo apt-get update
sudo apt-get -y install lxc wget bsdtar curl golang-stable git
export GOPATH=~/go/ export GOPATH=~/go/
export PATH=$GOPATH/bin:$PATH export PATH=$GOPATH/bin:$PATH

View File

@ -19,7 +19,8 @@ Most frequently asked questions.
3. **Does Docker run on Mac OS X or Windows?** 3. **Does Docker run on Mac OS X or Windows?**
Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ installation guides. Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a
virtual machine on your box, and get the best of both worlds. Check out the :ref:`install_using_vagrant` and :ref:`windows` installation guides.
4. **How do containers compare to virtual machines?** 4. **How do containers compare to virtual machines?**
@ -34,15 +35,16 @@ Most frequently asked questions.
You can find more answers on: You can find more answers on:
* `IRC: docker on freenode`_ * `Docker club mailinglist`_
* `IRC, docker on freenode`_
* `Github`_ * `Github`_
* `Ask questions on Stackoverflow`_ * `Ask questions on Stackoverflow`_
* `Join the conversation on Twitter`_ * `Join the conversation on Twitter`_
.. _Windows: ../installation/windows/
.. _MacOSX: ../installation/vagrant/ .. _Docker club mailinglist: https://groups.google.com/d/forum/docker-club
.. _the repo: http://www.github.com/dotcloud/docker .. _the repo: http://www.github.com/dotcloud/docker
.. _IRC\: docker on freenode: irc://chat.freenode.net#docker .. _IRC, docker on freenode: irc://chat.freenode.net#docker
.. _Github: http://www.github.com/dotcloud/docker .. _Github: http://www.github.com/dotcloud/docker
.. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker .. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
.. _Join the conversation on Twitter: http://twitter.com/getdocker .. _Join the conversation on Twitter: http://twitter.com/getdocker

View File

@ -92,6 +92,16 @@ have AUFS filesystem support enabled, so we need to install it.
sudo apt-get update sudo apt-get update
sudo apt-get install linux-image-extra-`uname -r` sudo apt-get install linux-image-extra-`uname -r`
**add-apt-repository support**
Some installations of Ubuntu 13.04 require ``software-properties-common`` to be
installed before being able to use add-apt-repository.
.. code-block:: bash
sudo apt-get install software-properties-common
Installation Installation
------------ ------------

View File

@ -2,6 +2,7 @@
:description: Docker's tutorial to run docker on Windows :description: Docker's tutorial to run docker on Windows
:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin :keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
.. _windows:
Using Vagrant (Windows) Using Vagrant (Windows)
======================= =======================

View File

@ -18,7 +18,7 @@ steps and commit them along the way, giving you a final image.
To use Docker Builder, assemble the steps into a text file (commonly referred to To use Docker Builder, assemble the steps into a text file (commonly referred to
as a Dockerfile) and supply this to `docker build` on STDIN, like so: as a Dockerfile) and supply this to `docker build` on STDIN, like so:
``docker build < Dockerfile`` ``docker build - < Dockerfile``
Docker will run your steps one-by-one, committing the result if necessary, Docker will run your steps one-by-one, committing the result if necessary,
before finally outputting the ID of your new image. before finally outputting the ID of your new image.

View File

@ -64,14 +64,15 @@
<div style="float: right" class="pull-right"> <div style="float: right" class="pull-right">
<ul class="nav"> <ul class="nav">
<li><a href="http://www.docker.io/">Introduction</a></li> <li id="nav-introduction"><a href="http://www.docker.io/">Introduction</a></li>
<li><a href="http://www.docker.io/gettingstarted/">Getting started</a></li> <li id="nav-gettingstarted"><a href="http://www.docker.io/gettingstarted/">Getting started</a></li>
<li class="active"><a href="http://docs.docker.io/en/latest/">Documentation</a></li> <li id="nav-documentation" class="active"><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
</ul> </ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px"> <!--<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">-->
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a> <!--<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>-->
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a> <!--<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>-->
</div> <!--</div>-->
</div> </div>
<div style="margin-left: -12px; float: left;"> <div style="margin-left: -12px; float: left;">
@ -86,8 +87,13 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="span12 titlebar"><h1 class="pageheader">DOCUMENTATION</h1> <div class="span12 titlebar">
<!--<span class="pull-right" style="margin-left: 20px; font-size: 20px">{{version}}</span>--> <!--<span class="pull-right" style="margin-left: 20px; font-size: 20px">{{version}}</span>-->
<div class="pull-right" id="fork-us" style="margin-top: 16px; margin-right: 16px;">
<a href="http://github.com/dotcloud/docker/"><img src="{{ pathto('_static/img/fork-us.png', 1) }}"> Fork us on Github</a>
</div>
<h1 class="pageheader">DOCUMENTATION</h1>
</div> </div>
</div> </div>
@ -123,8 +129,14 @@
<div class="row"> <div class="row">
<div class="span12 footer"> <div class="span12 footer">
<div class="tbox textright forceleftmargin social links pull-right">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a> Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
{# {%- if show_source and has_source and sourcename %}#} {# {%- if show_source and has_source and sourcename %}#}
{# ·#} {# ·#}
{# <a href="{{ pathto('_sources/' + sourcename, true)|e }}"#} {# <a href="{{ pathto('_sources/' + sourcename, true)|e }}"#}
@ -157,7 +169,7 @@
<!-- script which should be loaded after everything else --> <!-- script which should be loaded after everything else -->
<script type="text/javascript"> <script type="text/javascript">
// Function to make the sticky header possible
var shiftWindow = function() { var shiftWindow = function() {
scrollBy(0, -70); scrollBy(0, -70);
console.log("window shifted") console.log("window shifted")

View File

@ -285,6 +285,40 @@ section.header {
.social .github { .social .github {
background-position: -59px 2px; background-position: -59px 2px;
} }
#fork-us {
/*font-family: 'Maven Pro';*/
/*font-weight: bold;*/
font-size: 12px;
/*text-transform: uppercase;*/
display: block;
padding: 0px 1em;
height: 28px;
line-height: 28px;
background-color: #43484c;
filter: progid:dximagetransform.microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35');
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #747474), color-stop(100%, #43484c));
background-image: -webkit-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -moz-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -o-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: linear-gradient(top, #747474 0%, #43484c 100%);
border: 1px solid #43484c;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-ms-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
-moz-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
margin: 8px;
}
#fork-us a {
color: #faf2ee;
text-shadow: rgba(0, 0, 0, 0.3) 0px 1px 0px;
}
/* ======================= /* =======================
Media size overrides Media size overrides
======================= */ ======================= */
@ -325,10 +359,15 @@ section.header {
padding-top: 600px; padding-top: 600px;
} }
#fork-us {
display: none;
}
} }
/* Landscape phones and down */ /* Landscape phones and down */
@media (max-width: 480px) { @media (max-width: 480px) {
#nav-gettingstarted {
display: none;
}
} }
/* Misc fixes */ /* Misc fixes */
table th { table th {

View File

@ -391,6 +391,38 @@ section.header {
} }
#fork-us {
/*font-family: 'Maven Pro';*/
/*font-weight: bold;*/
font-size: 12px;
/*text-transform: uppercase;*/
display: block;
padding: 0px 1em;
height: 28px;
line-height: 28px;
background-color: #43484c;
filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35');
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #747474), color-stop(100%, #43484c));
background-image: -webkit-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -moz-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -o-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: linear-gradient(top, #747474 0%, #43484c 100%);
border: 1px solid #43484c;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-ms-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
-moz-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
margin: 8px;
a {
color: #faf2ee;
text-shadow: rgba(0, 0, 0, 0.3) 0px 1px 0px;
}
}
/* ======================= /* =======================
Media size overrides Media size overrides
======================= */ ======================= */
@ -441,14 +473,17 @@ section.header {
/* TODO: Fix this to be relative to the navigation size */ /* TODO: Fix this to be relative to the navigation size */
padding-top: 600px; padding-top: 600px;
} }
#fork-us {
display: none;
}
} }
/* Landscape phones and down */ /* Landscape phones and down */
@media (max-width: 480px) { @media (max-width: 480px) {
#nav-gettingstarted {
display: none;
}
} }
/* Misc fixes */ /* Misc fixes */

View File

@ -34,15 +34,11 @@
<div style="float: right" class="pull-right"> <div style="float: right" class="pull-right">
<ul class="nav"> <ul class="nav">
<li><a href="../">Introduction</a></li> <li id="nav-introduction"><a href="../">Introduction</a></li>
<li class="active"><a href="">Getting started</a></li> <li id="nav-gettingstarted" class="active"><a href="">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li> <li id="nav-documentation" class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
</ul> </ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
</div> </div>
<div style="margin-left: -12px; float: left;"> <div style="margin-left: -12px; float: left;">
@ -55,14 +51,22 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="span12 titlebar"><h1 class="pageheader">GETTING STARTED</h1>
<div class="span12 titlebar">
<div class="pull-right" id="fork-us" style="margin-top: 16px; margin-right: 16px;">
<a href="http://github.com/dotcloud/docker/"><img src="../static/img/fork-us.png"> Fork us on Github</a>
</div>
<h1 class="pageheader"> GETTING STARTED</h1>
</div> </div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<div class="alert alert-info"> <div class="alert alert-info" style="margin-bottom: 0;">
<strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress. <strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress.
</div> </div>
<div class="row"> <div class="row">
@ -133,13 +137,13 @@
</section> </section>
<section class="contentblock"> <section class="contentblock">
<h2>More resources</h2> <h2>Questions? Want to get in touch?</h2>
<ul> <p>There are several ways to get in touch:</p>
<li><a href="irc://chat.freenode.net#docker">IRC: docker on freenode</a></li> <p><strong>Join the discussion on IRC.</strong> We can be found in the <a href="irc://chat.freenode.net#docker">#docker</a> channel on chat.freenode.net</p>
<li><a href="http://www.github.com/dotcloud/docker">Github</a></li> <p><strong>Discussions</strong> happen on our google group: <a href="https://groups.google.com/d/forum/docker-club">docker-club at googlegroups.com</a></p>
<li><a href="http://stackoverflow.com/tags/docker/">Ask questions on Stackoverflow</a></li> <p>All our <strong>development and decisions</strong> are made out in the open on Github <a href="http://www.github.com/dotcloud/docker">github.com/dotcloud/docker</a></p>
<li><a href="http://twitter.com/getdocker/">Join the conversation on Twitter</a></li> <p><strong>Get help on using Docker</strong> by asking on <a href="http://stackoverflow.com/tags/docker/">Stackoverflow</a></p>
</ul> <p>And of course, <strong>tweet</strong> your tweets to <a href="http://twitter.com/getdocker/">twitter.com/getdocker</a></p>
</section> </section>
@ -172,7 +176,10 @@
<footer id="footer" class="footer"> <footer id="footer" class="footer">
<div class="row"> <div class="row">
<div class="span12 social"> <div class="span12 social">
<div class="tbox textright forceleftmargin social links pull-right">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a> Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
</div> </div>

View File

@ -44,9 +44,18 @@
.debug { .debug {
border: 1px red dotted; border: 1px red dotted;
} }
.twitterblock {
min-height: 75px;
}
.twitterblock img {
float: left;
margin-right: 10px;
}
</style> </style>
</head> </head>
@ -56,17 +65,18 @@
<div class="navbar-dotcloud"> <div class="navbar-dotcloud">
<div class="container" style="text-align: center;"> <div class="container" style="text-align: center;">
<div class="pull-left" id="fork-us" style="margin-top: 16px;">
<a href="http://github.com/dotcloud/docker/"><img src="static/img/fork-us.png" alt="fork-icon"> Fork us on Github</a>
</div>
<div class="pull-right" > <div class="pull-right" >
<ul class="nav"> <ul class="nav">
<li class="active"><a href="/">Introduction</a></li> <li id="nav-introduction" class="active"><a href="/">Introduction</a></li>
<li ><a href="gettingstarted">Getting started</a></li> <li id="nav-gettingstarted"><a href="gettingstarted">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li> <li id="nav-documentation" class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
</ul> </ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -81,7 +91,7 @@
<div class="span5" style="margin-bottom: 15px;"> <div class="span5" style="margin-bottom: 15px;">
<div style="text-align: center;" > <div style="text-align: center;" >
<img src="static/img/docker_letters_500px.png"> <img src="static/img/docker_letters_500px.png" alt="docker letters">
<h2>The Linux container engine</h2> <h2>The Linux container engine</h2>
</div> </div>
@ -130,7 +140,7 @@
<section class="contentblock"> <section class="contentblock">
<div class="container"> <div class="container">
<div class="span2" style="margin-left: 0" > <div class="span2" style="margin-left: 0" >
<a href="http://dotcloud.theresumator.com/apply/mWjkD4/Software-Engineer.html" title="Job description"><img src="static/img/hiring_graphic.png" width="140px" style="margin-top: 25px"></a> <a href="http://dotcloud.theresumator.com/apply/mWjkD4/Software-Engineer.html" title="Job description"><img src="static/img/hiring_graphic.png" alt="we're hiring" width="140" style="margin-top: 25px"></a>
</div> </div>
<div class="span4" style="margin-left: 0"> <div class="span4" style="margin-left: 0">
<h4>Do you think it is cool to hack on docker? Join us!</h4> <h4>Do you think it is cool to hack on docker? Join us!</h4>
@ -156,7 +166,7 @@
</div> </div>
</a> </a>
&nbsp; &nbsp;
<input type="button" class="searchbutton" type="submit" value="Search images" <input type="button" class="searchbutton" value="Search images"
onClick="window.open('https://index.docker.io')" /> onClick="window.open('https://index.docker.io')" />
</section> </section>
@ -184,32 +194,19 @@
</div> </div>
<style>
.twitterblock {
min-height: 75px;
}
.twitterblock img {
float: left;
margin-right: 10px;
}
</style>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="span6"> <div class="span6">
<section class="contentblock twitterblock"> <section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/2707460527/252a64411a339184ff375a96fb68dcb0_bigger.png"> <img src="https://si0.twimg.com/profile_images/2707460527/252a64411a339184ff375a96fb68dcb0_bigger.png">
<em>Mitchell Hashimoto@mitchellh:</em> Docker launched today. It is incredible. Theyre also working RIGHT NOW on a Vagrant provider. LXC is COMING!! <em>Mitchell Hashimoto @mitchellh:</em> Docker launched today. It is incredible. Theyre also working RIGHT NOW on a Vagrant provider. LXC is COMING!!
</section> </section>
</div> </div>
<div class="span6"> <div class="span6">
<section class="contentblock twitterblock"> <section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/1108290260/Adam_Jacob-114x150_original_bigger.jpg"> <img src="https://si0.twimg.com/profile_images/1108290260/Adam_Jacob-114x150_original_bigger.jpg">
<em>Adam Jacob@adamhjk:</em> Docker is clearly the right idea. @solomonstre absolutely killed it. Containerized app deployment is the future, I think. <em>Adam Jacob @adamhjk:</em> Docker is clearly the right idea. @solomonstre absolutely killed it. Containerized app deployment is the future, I think.
</section> </section>
</div> </div>
</div> </div>
@ -217,13 +214,13 @@
<div class="span6"> <div class="span6">
<section class="contentblock twitterblock"> <section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/14872832/twitter_pic_bigger.jpg"> <img src="https://si0.twimg.com/profile_images/14872832/twitter_pic_bigger.jpg">
<em>Matt Townsend@mtownsend:</em> I have a serious code crush on docker.io - it's Lego for PaaS. Motherfucking awesome Lego. <em>Matt Townsend @mtownsend:</em> I have a serious code crush on docker.io - it's Lego for PaaS. Motherfucking awesome Lego.
</section> </section>
</div> </div>
<div class="span6"> <div class="span6">
<section class="contentblock twitterblock"> <section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/1312352395/rupert-259x300_bigger.jpg"> <img src="https://si0.twimg.com/profile_images/1312352395/rupert-259x300_bigger.jpg">
<em>Rob Harrop@robertharrop:</em> Impressed by @getdocker - it's all kinds of magic. Serious rethink of AWS architecture happening @skillsmatter. <em>Rob Harrop @robertharrop:</em> Impressed by @getdocker - it's all kinds of magic. Serious rethink of AWS architecture happening @skillsmatter.
</section> </section>
</div> </div>
</div> </div>
@ -317,7 +314,10 @@
<footer id="footer" class="footer"> <footer id="footer" class="footer">
<div class="row"> <div class="row">
<div class="span12"> <div class="span12">
<div class="tbox textright forceleftmargin social links pull-right">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a> Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
</div> </div>

View File

@ -86,14 +86,14 @@ func (graph *Graph) Get(name string) (*Image, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if img.Id != id { if img.ID != id {
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.Id) return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
} }
img.graph = graph img.graph = graph
graph.lockSumMap.Lock() graph.lockSumMap.Lock()
defer graph.lockSumMap.Unlock() defer graph.lockSumMap.Unlock()
if _, exists := graph.checksumLock[img.Id]; !exists { if _, exists := graph.checksumLock[img.ID]; !exists {
graph.checksumLock[img.Id] = &sync.Mutex{} graph.checksumLock[img.ID] = &sync.Mutex{}
} }
return img, nil return img, nil
} }
@ -101,7 +101,7 @@ func (graph *Graph) Get(name string) (*Image, error) {
// Create creates a new image and registers it in the graph. // Create creates a new image and registers it in the graph.
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) { func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
img := &Image{ img := &Image{
Id: GenerateId(), ID: GenerateID(),
Comment: comment, Comment: comment,
Created: time.Now(), Created: time.Now(),
DockerVersion: VERSION, DockerVersion: VERSION,
@ -111,7 +111,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
} }
if container != nil { if container != nil {
img.Parent = container.Image img.Parent = container.Image
img.Container = container.Id img.Container = container.ID
img.ContainerConfig = *container.Config img.ContainerConfig = *container.Config
} }
if err := graph.Register(layerData, layerData != nil, img); err != nil { if err := graph.Register(layerData, layerData != nil, img); err != nil {
@ -124,12 +124,12 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
// Register imports a pre-existing image into the graph. // Register imports a pre-existing image into the graph.
// FIXME: pass img as first argument // FIXME: pass img as first argument
func (graph *Graph) Register(layerData Archive, store bool, img *Image) error { func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
if err := ValidateId(img.Id); err != nil { if err := ValidateID(img.ID); err != nil {
return err return err
} }
// (This is a convenience to save time. Race conditions are taken care of by os.Rename) // (This is a convenience to save time. Race conditions are taken care of by os.Rename)
if graph.Exists(img.Id) { if graph.Exists(img.ID) {
return fmt.Errorf("Image %s already exists", img.Id) return fmt.Errorf("Image %s already exists", img.ID)
} }
tmp, err := graph.Mktemp("") tmp, err := graph.Mktemp("")
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
@ -140,12 +140,12 @@ func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
return err return err
} }
// Commit // Commit
if err := os.Rename(tmp, graph.imageRoot(img.Id)); err != nil { if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil {
return err return err
} }
img.graph = graph img.graph = graph
graph.idIndex.Add(img.Id) graph.idIndex.Add(img.ID)
graph.checksumLock[img.Id] = &sync.Mutex{} graph.checksumLock[img.ID] = &sync.Mutex{}
return nil return nil
} }
@ -173,7 +173,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
// Mktemp creates a temporary sub-directory inside the graph's filesystem. // Mktemp creates a temporary sub-directory inside the graph's filesystem.
func (graph *Graph) Mktemp(id string) (string, error) { func (graph *Graph) Mktemp(id string) (string, error) {
if id == "" { if id == "" {
id = GenerateId() id = GenerateID()
} }
tmp, err := graph.tmp() tmp, err := graph.tmp()
if err != nil { if err != nil {
@ -230,7 +230,7 @@ func (graph *Graph) Map() (map[string]*Image, error) {
} }
images := make(map[string]*Image, len(all)) images := make(map[string]*Image, len(all))
for _, image := range all { for _, image := range all {
images[image.Id] = image images[image.ID] = image
} }
return images, nil return images, nil
} }
@ -273,10 +273,10 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) {
if err != nil { if err != nil {
return return
} }
if children, exists := byParent[parent.Id]; exists { if children, exists := byParent[parent.ID]; exists {
byParent[parent.Id] = []*Image{image} byParent[parent.ID] = []*Image{image}
} else { } else {
byParent[parent.Id] = append(children, image) byParent[parent.ID] = append(children, image)
} }
}) })
return byParent, err return byParent, err
@ -293,8 +293,8 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
err = graph.WalkAll(func(image *Image) { err = graph.WalkAll(func(image *Image) {
// If it's not in the byParent lookup table, then // If it's not in the byParent lookup table, then
// it's not a parent -> so it's a head! // it's not a parent -> so it's a head!
if _, exists := byParent[image.Id]; !exists { if _, exists := byParent[image.ID]; !exists {
heads[image.Id] = image heads[image.ID] = image
} }
}) })
return heads, err return heads, err
@ -317,11 +317,11 @@ func (graph *Graph) getStoredChecksums() (map[string]string, error) {
} }
func (graph *Graph) storeChecksums(checksums map[string]string) error { func (graph *Graph) storeChecksums(checksums map[string]string) error {
checksumJson, err := json.Marshal(checksums) checksumJSON, err := json.Marshal(checksums)
if err != nil { if err != nil {
return err return err
} }
if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJson, 0600); err != nil { if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJSON, 0600); err != nil {
return err return err
} }
return nil return nil

View File

@ -34,14 +34,14 @@ func TestInterruptedRegister(t *testing.T) {
defer os.RemoveAll(graph.Root) defer os.RemoveAll(graph.Root)
badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
image := &Image{ image := &Image{
Id: GenerateId(), ID: GenerateID(),
Comment: "testing", Comment: "testing",
Created: time.Now(), Created: time.Now(),
} }
go graph.Register(badArchive, false, image) go graph.Register(badArchive, false, image)
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling) w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
if _, err := graph.Get(image.Id); err == nil { if _, err := graph.Get(image.ID); err == nil {
t.Fatal("Image should not exist after Register is interrupted") t.Fatal("Image should not exist after Register is interrupted")
} }
// Registering the same image again should succeed if the first register was interrupted // Registering the same image again should succeed if the first register was interrupted
@ -67,7 +67,7 @@ func TestGraphCreate(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := ValidateId(image.Id); err != nil { if err := ValidateID(image.ID); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if image.Comment != "Testing" { if image.Comment != "Testing" {
@ -91,7 +91,7 @@ func TestRegister(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
image := &Image{ image := &Image{
Id: GenerateId(), ID: GenerateID(),
Comment: "testing", Comment: "testing",
Created: time.Now(), Created: time.Now(),
} }
@ -104,11 +104,11 @@ func TestRegister(t *testing.T) {
} else if l := len(images); l != 1 { } else if l := len(images); l != 1 {
t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l) t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
} }
if resultImg, err := graph.Get(image.Id); err != nil { if resultImg, err := graph.Get(image.ID); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
if resultImg.Id != image.Id { if resultImg.ID != image.ID {
t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.Id, resultImg.Id) t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
} }
if resultImg.Comment != image.Comment { if resultImg.Comment != image.Comment {
t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment) t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
@ -156,7 +156,7 @@ func TestDeletePrefix(t *testing.T) {
graph := tempGraph(t) graph := tempGraph(t)
defer os.RemoveAll(graph.Root) defer os.RemoveAll(graph.Root)
img := createTestImage(graph, t) img := createTestImage(graph, t)
if err := graph.Delete(utils.TruncateId(img.Id)); err != nil { if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
assertNImages(graph, t, 0) assertNImages(graph, t, 0)
@ -187,7 +187,7 @@ func TestDelete(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
assertNImages(graph, t, 1) assertNImages(graph, t, 1)
if err := graph.Delete(img.Id); err != nil { if err := graph.Delete(img.ID); err != nil {
t.Fatal(err) t.Fatal(err)
} }
assertNImages(graph, t, 0) assertNImages(graph, t, 0)
@ -201,7 +201,7 @@ func TestDelete(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
assertNImages(graph, t, 2) assertNImages(graph, t, 2)
if err := graph.Delete(img1.Id); err != nil { if err := graph.Delete(img1.ID); err != nil {
t.Fatal(err) t.Fatal(err)
} }
assertNImages(graph, t, 1) assertNImages(graph, t, 1)
@ -216,7 +216,7 @@ func TestDelete(t *testing.T) {
if err := graph.Register(archive, false, img1); err != nil { if err := graph.Register(archive, false, img1); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := graph.Delete(img1.Id); err != nil { if err := graph.Delete(img1.ID); err != nil {
t.Fatal(err) t.Fatal(err)
} }
assertNImages(graph, t, 1) assertNImages(graph, t, 1)

15
hack/PRINCIPLES.md Normal file
View File

@ -0,0 +1,15 @@
# Docker principles
In the design and development of Docker we try to follow these principles:
(Work in progress)
* Don't try to replace every tool. Instead, be an ingredient to improve them.
* When hesitating between 2 options, choose the one that is easier to reverse.
* No is temporary, Yes is forever. If you're not sure about a new feature, say no. You can change your mind later.
* Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable.
* The less moving parts in a container, the better.
* Don't merge it unless you document it.
* Don't document it unless you can keep it up-to-date.
* Don't merge it unless you test it!
* Everyone's problem is slightly different. Focus on the part that is the same for everyone, and solve that.

88
hack/ROADMAP.md Normal file
View File

@ -0,0 +1,88 @@
# Docker: what's next?
This document is a high-level overview of where we want to take Docker next.
It is a curated selection of planned improvements which are either important, difficult, or both.
For a more complete view of planned and requested improvements, see [the Github issues](https://github.com/dotcloud/docker/issues).
Tu suggest changes to the roadmap, including additions, please write the change as if it were already in effect, and make a pull request.
Broader kernel support
----------------------
Our goal is to make Docker run everywhere, but currently Docker requires [Linux version 3.8 or higher with lxc and aufs support](http://docs.docker.io/en/latest/installation/kernel.html). If you're deploying new machines for the purpose of running Docker, this is a fairly easy requirement to meet.
However, if you're adding Docker to an existing deployment, you may not have the flexibility to update and patch the kernel.
Expanding Docker's kernel support is a priority. This includes running on older kernel versions,
but also on kernels with no AUFS support, or with incomplete lxc capabilities.
Cross-architecture support
--------------------------
Our goal is to make Docker run everywhere. However currently Docker only runs on x86_64 systems.
We plan on expanding architecture support, so that Docker containers can be created and used on more architectures.
Even more integrations
----------------------
We want Docker to be the secret ingredient that makes your existing tools more awesome.
Thanks to this philosophy, Docker has already been integrated with
[Puppet](http://forge.puppetlabs.com/garethr/docker), [Chef](http://www.opscode.com/chef),
[Openstack Nova](https://github.com/dotcloud/openstack-docker), [Jenkins](https://github.com/georgebashi/jenkins-docker-plugin),
[DotCloud sandbox](http://github.com/dotcloud/sandbox), [Pallet](https://github.com/pallet/pallet-docker),
[Strider CI](http://blog.frozenridge.co/next-generation-continuous-integration-deployment-with-dotclouds-docker-and-strider/)
and even [Heroku buildpacks](https://github.com/progrium/buildstep).
Expect Docker to integrate with even more of your favorite tools going forward, including:
* Alternative storage backends such as ZFS, LVM or [BTRFS](github.com/dotcloud/docker/issues/443)
* Alternative containerization backends such as [OpenVZ](http://openvz.org), Solaris Zones, BSD Jails and even plain Chroot.
* Process managers like [Supervisord](http://supervisord.org/), [Runit](http://smarden.org/runit/), [Gaffer](https://gaffer.readthedocs.org/en/latest/#gaffer) and [Systemd](http://www.freedesktop.org/wiki/Software/systemd/)
* Build and integration tools like Make, Maven, Scons, Jenkins, Buildbot and Cruise Control.
* Configuration management tools like [Puppet](http://puppetlabs.com), [Chef](http://www.opscode.com/chef/) and [Salt](http://saltstack.org)
* Personal development environments like [Vagrant](http://vagrantup.com), [Boxen](http://boxen.github.com/), [Koding](http://koding.com) and [Cloud9](http://c9.io).
* Orchestration tools like [Zookeeper](http://zookeeper.apache.org/), [Mesos](http://incubator.apache.org/mesos/) and [Galaxy](https://github.com/ning/galaxy)
* Infrastructure deployment tools like [Openstack](http://openstack.org), [Apache Cloudstack](http://apache.cloudstack.org), [Ganeti](https://code.google.com/p/ganeti/)
Plugin API
----------
We want Docker to run everywhere, and to integrate with every devops tool.
Those are ambitious goals, and the only way to reach them is with the Docker community.
For the community to participate fully, we need an API which allows Docker to be deeply and easily customized.
We are working on a plugin API which will make Docker very, very customization-friendly.
We believe it will facilitate the integrations listed above - and many more we didn't even think about.
Let us know if you want to start playing with the API before it's generally available.
Externally mounted volumes
--------------------------
In 0.3 we [introduced data volumes](https://github.com/dotcloud/docker/wiki/Docker-0.3.0-release-note%2C-May-6-2013#data-volumes),
a great mechanism for manipulating persistent data such as database files, log files, etc.
Data volumes can be shared between containers, a powerful capability [which allows many advanced use cases](http://docs.docker.io/en/latest/examples/couchdb_data_volumes.html). In the future it will also be possible to share volumes between a container and the underlying host. This will make certain scenarios much easier, such as using a high-performance storage backend for your production database,
making live development changes available to a container, etc.
Better documentation
--------------------
We believe that great documentation is worth 10 features. We are often told that "Docker's documentation is great for a 2-month old project".
Our goal is to make it great, period.
If you have feedback on how to improve our documentation, please get in touch by replying to this email,
or by [filing an issue](https://github.com/dotcloud/docker/issues). We always appreciate it!
Production-ready
----------------
Docker is still alpha software, and not suited for production.
We are working hard to get there, and we are confident that it will be possible within a few months.

View File

@ -0,0 +1 @@
Daniel Mizyrycki <daniel@dotcloud.com>

View File

@ -18,7 +18,7 @@ import (
) )
type Image struct { type Image struct {
Id string `json:"id"` ID string `json:"id"`
Parent string `json:"parent,omitempty"` Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"` Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
@ -42,18 +42,17 @@ func LoadImage(root string) (*Image, error) {
if err := json.Unmarshal(jsonData, img); err != nil { if err := json.Unmarshal(jsonData, img); err != nil {
return nil, err return nil, err
} }
if err := ValidateId(img.Id); err != nil { if err := ValidateID(img.ID); err != nil {
return nil, err return nil, err
} }
// Check that the filesystem layer exists // Check that the filesystem layer exists
if stat, err := os.Stat(layerPath(root)); err != nil { if stat, err := os.Stat(layerPath(root)); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id) return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
} else {
return nil, err
} }
return nil, err
} else if !stat.IsDir() { } else if !stat.IsDir() {
return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root)) return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
} }
return img, nil return img, nil
} }
@ -61,7 +60,7 @@ func LoadImage(root string) (*Image, error) {
func StoreImage(img *Image, layerData Archive, root string, store bool) error { func StoreImage(img *Image, layerData Archive, root string, store bool) error {
// Check that root doesn't already exist // Check that root doesn't already exist
if _, err := os.Stat(root); err == nil { if _, err := os.Stat(root); err == nil {
return fmt.Errorf("Image %s already exists", img.Id) return fmt.Errorf("Image %s already exists", img.ID)
} else if !os.IsNotExist(err) { } else if !os.IsNotExist(err) {
return err return err
} }
@ -181,11 +180,11 @@ func (image *Image) Changes(rw string) ([]Change, error) {
return Changes(layers, rw) return Changes(layers, rw)
} }
func (image *Image) ShortId() string { func (image *Image) ShortID() string {
return utils.TruncateId(image.Id) return utils.TruncateID(image.ID)
} }
func ValidateId(id string) error { func ValidateID(id string) error {
if id == "" { if id == "" {
return fmt.Errorf("Image id can't be empty") return fmt.Errorf("Image id can't be empty")
} }
@ -195,7 +194,7 @@ func ValidateId(id string) error {
return nil return nil
} }
func GenerateId() string { func GenerateID() string {
id := make([]byte, 32) id := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, id) _, err := io.ReadFull(rand.Reader, id)
if err != nil { if err != nil {
@ -241,7 +240,7 @@ func (img *Image) layers() ([]string, error) {
return nil, e return nil, e
} }
if len(list) == 0 { if len(list) == 0 {
return nil, fmt.Errorf("No layer found for image %s\n", img.Id) return nil, fmt.Errorf("No layer found for image %s\n", img.ID)
} }
return list, nil return list, nil
} }
@ -276,7 +275,7 @@ func (img *Image) root() (string, error) {
if img.graph == nil { if img.graph == nil {
return "", fmt.Errorf("Can't lookup root of unregistered image") return "", fmt.Errorf("Can't lookup root of unregistered image")
} }
return img.graph.imageRoot(img.Id), nil return img.graph.imageRoot(img.ID), nil
} }
// Return the path of an image's layer // Return the path of an image's layer
@ -289,8 +288,8 @@ func (img *Image) layer() (string, error) {
} }
func (img *Image) Checksum() (string, error) { func (img *Image) Checksum() (string, error) {
img.graph.checksumLock[img.Id].Lock() img.graph.checksumLock[img.ID].Lock()
defer img.graph.checksumLock[img.Id].Unlock() defer img.graph.checksumLock[img.ID].Unlock()
root, err := img.root() root, err := img.root()
if err != nil { if err != nil {
@ -301,7 +300,7 @@ func (img *Image) Checksum() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
if checksum, ok := checksums[img.Id]; ok { if checksum, ok := checksums[img.ID]; ok {
return checksum, nil return checksum, nil
} }
@ -352,7 +351,7 @@ func (img *Image) Checksum() (string, error) {
return "", err return "", err
} }
checksums[img.Id] = hash checksums[img.ID] = hash
// Dump the checksums to disc // Dump the checksums to disc
if err := img.graph.storeChecksums(checksums); err != nil { if err := img.graph.storeChecksums(checksums); err != nil {
@ -363,7 +362,7 @@ func (img *Image) Checksum() (string, error) {
} }
// Build an Image object from raw json data // Build an Image object from raw json data
func NewImgJson(src []byte) (*Image, error) { func NewImgJSON(src []byte) (*Image, error) {
ret := &Image{} ret := &Image{}
utils.Debugf("Json string: {%s}\n", src) utils.Debugf("Json string: {%s}\n", src)

View File

@ -19,7 +19,7 @@ lxc.network.flags = up
lxc.network.link = {{.NetworkSettings.Bridge}} lxc.network.link = {{.NetworkSettings.Bridge}}
lxc.network.name = eth0 lxc.network.name = eth0
lxc.network.mtu = 1500 lxc.network.mtu = 1500
lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}} lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}
# root filesystem # root filesystem
{{$ROOTFS := .RootfsPath}} {{$ROOTFS := .RootfsPath}}

View File

@ -52,7 +52,7 @@ func ipToInt(ip net.IP) int32 {
} }
// Converts 32 bit integer into a 4 bytes IP address // Converts 32 bit integer into a 4 bytes IP address
func intToIp(n int32) net.IP { func intToIP(n int32) net.IP {
b := make([]byte, 4) b := make([]byte, 4)
binary.BigEndian.PutUint32(b, uint32(n)) binary.BigEndian.PutUint32(b, uint32(n))
return net.IP(b) return net.IP(b)
@ -132,9 +132,8 @@ func CreateBridgeIface(ifaceName string) error {
} }
if ifaceAddr == "" { if ifaceAddr == "" {
return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName) return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
} else {
utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
} }
utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil { if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output) return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
@ -258,7 +257,7 @@ func proxy(listener net.Listener, proto, address string) error {
utils.Debugf("Connected to backend, splicing") utils.Debugf("Connected to backend, splicing")
splice(src, dst) splice(src, dst)
} }
return nil panic("Unreachable")
} }
func halfSplice(dst, src net.Conn) error { func halfSplice(dst, src net.Conn) error {
@ -398,7 +397,7 @@ func (alloc *IPAllocator) run() {
} }
} }
ip := allocatedIP{ip: intToIp(newNum)} ip := allocatedIP{ip: intToIP(newNum)}
if inUse { if inUse {
ip.err = errors.New("No unallocated IP available") ip.err = errors.New("No unallocated IP available")
} }
@ -465,11 +464,11 @@ func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
return nil, err return nil, err
} }
// Allocate a random port if Frontend==0 // Allocate a random port if Frontend==0
if extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend); err != nil { extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend)
if err != nil {
return nil, err return nil, err
} else {
nat.Frontend = extPort
} }
nat.Frontend = extPort
if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil { if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil {
iface.manager.portAllocator.Release(nat.Frontend) iface.manager.portAllocator.Release(nat.Frontend)
return nil, err return nil, err

View File

@ -137,7 +137,7 @@ func TestConversion(t *testing.T) {
if i == 0 { if i == 0 {
t.Fatal("converted to zero") t.Fatal("converted to zero")
} }
conv := intToIp(i) conv := intToIP(i)
if !ip.Equal(conv) { if !ip.Equal(conv) {
t.Error(conv.String()) t.Error(conv.String())
} }

View File

@ -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.

View File

@ -15,7 +15,7 @@ import (
"strings" "strings"
) )
var ErrAlreadyExists error = errors.New("Image already exists") var ErrAlreadyExists = errors.New("Image already exists")
func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) { func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
for _, cookie := range c.Jar.Cookies(req.URL) { for _, cookie := range c.Jar.Cookies(req.URL) {
@ -64,7 +64,11 @@ func (r *Registry) LookupRemoteImage(imgId, registry string, authConfig *auth.Au
} }
req.SetBasicAuth(authConfig.Username, authConfig.Password) req.SetBasicAuth(authConfig.Username, authConfig.Password)
res, err := rt.RoundTrip(req) res, err := rt.RoundTrip(req)
return err == nil && res.StatusCode == 307 if err != nil {
return false
}
res.Body.Close()
return res.StatusCode == 307
} }
func (r *Registry) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) { func (r *Registry) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) {
@ -103,8 +107,8 @@ func (r *Registry) getImagesInRepository(repository string, authConfig *auth.Aut
// Retrieve an image from the Registry. // Retrieve an image from the Registry.
// Returns the Image object as well as the layer as an Archive (io.Reader) // Returns the Image object as well as the layer as an Archive (io.Reader)
func (r *Registry) GetRemoteImageJson(imgId, registry string, token []string) ([]byte, error) { func (r *Registry) GetRemoteImageJSON(imgId, registry string, token []string) ([]byte, error) {
// Get the Json // Get the JSON
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil) req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to download json: %s", err) return nil, fmt.Errorf("Failed to download json: %s", err)
@ -152,21 +156,24 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [
} }
req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
res, err := r.client.Do(req) res, err := r.client.Do(req)
defer res.Body.Close()
utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint) utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint)
if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) { if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != 200 && res.StatusCode != 404 {
continue continue
} else if res.StatusCode == 404 { } else if res.StatusCode == 404 {
return nil, fmt.Errorf("Repository not found") return nil, fmt.Errorf("Repository not found")
} }
result := make(map[string]string) result := make(map[string]string)
rawJSON, err := ioutil.ReadAll(res.Body)
rawJson, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := json.Unmarshal(rawJson, &result); err != nil { if err := json.Unmarshal(rawJSON, &result); err != nil {
return nil, err return nil, err
} }
return result, nil return result, nil
@ -212,19 +219,19 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
return nil, fmt.Errorf("Index response didn't contain any endpoints") return nil, fmt.Errorf("Index response didn't contain any endpoints")
} }
checksumsJson, err := ioutil.ReadAll(res.Body) checksumsJSON, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
remoteChecksums := []*ImgData{} remoteChecksums := []*ImgData{}
if err := json.Unmarshal(checksumsJson, &remoteChecksums); err != nil { if err := json.Unmarshal(checksumsJSON, &remoteChecksums); err != nil {
return nil, err return nil, err
} }
// Forge a better object from the retrieved data // Forge a better object from the retrieved data
imgsData := make(map[string]*ImgData) imgsData := make(map[string]*ImgData)
for _, elem := range remoteChecksums { for _, elem := range remoteChecksums {
imgsData[elem.Id] = elem imgsData[elem.ID] = elem
} }
return &RepositoryData{ return &RepositoryData{
@ -235,10 +242,10 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
} }
// Push a local image to the registry // Push a local image to the registry
func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error { func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string, token []string) error {
registry = "https://" + registry + "/v1" registry = "https://" + registry + "/v1"
// FIXME: try json with UTF8 // FIXME: try json with UTF8
req, err := http.NewRequest("PUT", registry+"/images/"+imgData.Id+"/json", strings.NewReader(string(jsonRaw))) req, err := http.NewRequest("PUT", registry+"/images/"+imgData.ID+"/json", strings.NewReader(string(jsonRaw)))
if err != nil { if err != nil {
return err return err
} }
@ -246,7 +253,7 @@ func (r *Registry) PushImageJsonRegistry(imgData *ImgData, jsonRaw []byte, regis
req.Header.Set("Authorization", "Token "+strings.Join(token, ",")) req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
req.Header.Set("X-Docker-Checksum", imgData.Checksum) req.Header.Set("X-Docker-Checksum", imgData.Checksum)
utils.Debugf("Setting checksum for %s: %s", imgData.Id, imgData.Checksum) utils.Debugf("Setting checksum for %s: %s", imgData.ID, imgData.Checksum)
res, err := doWithCookies(r.client, req) res, err := doWithCookies(r.client, req)
if err != nil { if err != nil {
return fmt.Errorf("Failed to upload metadata: %s", err) return fmt.Errorf("Failed to upload metadata: %s", err)
@ -321,8 +328,8 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token
return nil return nil
} }
func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) { func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
imgListJson, err := json.Marshal(imgList) imgListJSON, err := json.Marshal(imgList)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -331,14 +338,14 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
suffix = "images" suffix = "images"
} }
utils.Debugf("Image list pushed to index:\n%s\n", imgListJson) utils.Debugf("Image list pushed to index:\n%s\n", imgListJSON)
req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJson)) req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON))
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
req.ContentLength = int64(len(imgListJson)) req.ContentLength = int64(len(imgListJSON))
req.Header.Set("X-Docker-Token", "true") req.Header.Set("X-Docker-Token", "true")
res, err := r.client.Do(req) res, err := r.client.Do(req)
@ -350,12 +357,12 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
// Redirect if necessary // Redirect if necessary
for res.StatusCode >= 300 && res.StatusCode < 400 { for res.StatusCode >= 300 && res.StatusCode < 400 {
utils.Debugf("Redirected to %s\n", res.Header.Get("Location")) utils.Debugf("Redirected to %s\n", res.Header.Get("Location"))
req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson)) req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON))
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password) req.SetBasicAuth(r.authConfig.Username, r.authConfig.Password)
req.ContentLength = int64(len(imgListJson)) req.ContentLength = int64(len(imgListJSON))
req.Header.Set("X-Docker-Token", "true") req.Header.Set("X-Docker-Token", "true")
res, err = r.client.Do(req) res, err = r.client.Do(req)
@ -389,11 +396,11 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
} }
if validate { if validate {
if res.StatusCode != 204 { if res.StatusCode != 204 {
if errBody, err := ioutil.ReadAll(res.Body); err != nil { errBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err return nil, err
} else {
return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
} }
return nil, fmt.Errorf("Error: Status %d trying to push checksums %s: %s", res.StatusCode, remote, errBody)
} }
} }
@ -456,7 +463,7 @@ type RepositoryData struct {
} }
type ImgData struct { type ImgData struct {
Id string `json:"id"` ID string `json:"id"`
Checksum string `json:"checksum,omitempty"` Checksum string `json:"checksum,omitempty"`
Tag string `json:",omitempty"` Tag string `json:",omitempty"`
} }
@ -470,9 +477,16 @@ func NewRegistry(root string) *Registry {
// If the auth file does not exist, keep going // If the auth file does not exist, keep going
authConfig, _ := auth.LoadConfig(root) authConfig, _ := auth.LoadConfig(root)
httpTransport := &http.Transport{
DisableKeepAlives: true,
Proxy: http.ProxyFromEnvironment,
}
r := &Registry{ r := &Registry{
authConfig: authConfig, authConfig: authConfig,
client: &http.Client{}, client: &http.Client{
Transport: httpTransport,
},
} }
r.client.Jar = cookiejar.NewCookieJar() r.client.Jar = cookiejar.NewCookieJar()
return r return r

View File

@ -51,7 +51,7 @@ func (runtime *Runtime) List() []*Container {
func (runtime *Runtime) getContainerElement(id string) *list.Element { func (runtime *Runtime) getContainerElement(id string) *list.Element {
for e := runtime.containers.Front(); e != nil; e = e.Next() { for e := runtime.containers.Front(); e != nil; e = e.Next() {
container := e.Value.(*Container) container := e.Value.(*Container)
if container.Id == id { if container.ID == id {
return e return e
} }
} }
@ -83,8 +83,8 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
if err := container.FromDisk(); err != nil { if err := container.FromDisk(); err != nil {
return nil, err return nil, err
} }
if container.Id != id { if container.ID != id {
return container, fmt.Errorf("Container %s is stored at %s", container.Id, id) return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
} }
if container.State.Running { if container.State.Running {
container.State.Ghost = true container.State.Ghost = true
@ -95,12 +95,12 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
return container, nil return container, nil
} }
// Register makes a container object usable by the runtime as <container.Id> // Register makes a container object usable by the runtime as <container.ID>
func (runtime *Runtime) Register(container *Container) error { func (runtime *Runtime) Register(container *Container) error {
if container.runtime != nil || runtime.Exists(container.Id) { if container.runtime != nil || runtime.Exists(container.ID) {
return fmt.Errorf("Container is already loaded") return fmt.Errorf("Container is already loaded")
} }
if err := validateId(container.Id); err != nil { if err := validateID(container.ID); err != nil {
return err return err
} }
@ -123,7 +123,7 @@ func (runtime *Runtime) Register(container *Container) error {
} }
// done // done
runtime.containers.PushBack(container) runtime.containers.PushBack(container)
runtime.idIndex.Add(container.Id) runtime.idIndex.Add(container.ID)
// When we actually restart, Start() do the monitoring. // When we actually restart, Start() do the monitoring.
// However, when we simply 'reattach', we have to restart a monitor // However, when we simply 'reattach', we have to restart a monitor
@ -133,25 +133,25 @@ func (runtime *Runtime) Register(container *Container) error {
// if so, then we need to restart monitor and init a new lock // if so, then we need to restart monitor and init a new lock
// If the container is supposed to be running, make sure of it // If the container is supposed to be running, make sure of it
if container.State.Running { if container.State.Running {
if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil { output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
if err != nil {
return err return err
} else { }
if !strings.Contains(string(output), "RUNNING") { if !strings.Contains(string(output), "RUNNING") {
utils.Debugf("Container %s was supposed to be running be is not.", container.Id) utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
if runtime.autoRestart { if runtime.autoRestart {
utils.Debugf("Restarting") utils.Debugf("Restarting")
container.State.Ghost = false container.State.Ghost = false
container.State.setStopped(0) container.State.setStopped(0)
if err := container.Start(); err != nil { if err := container.Start(); err != nil {
return err return err
} }
nomonitor = true nomonitor = true
} else { } else {
utils.Debugf("Marking as stopped") utils.Debugf("Marking as stopped")
container.State.setStopped(-127) container.State.setStopped(-127)
if err := container.ToDisk(); err != nil { if err := container.ToDisk(); err != nil {
return err return err
}
} }
} }
} }
@ -182,9 +182,9 @@ func (runtime *Runtime) Destroy(container *Container) error {
return fmt.Errorf("The given container is <nil>") return fmt.Errorf("The given container is <nil>")
} }
element := runtime.getContainerElement(container.Id) element := runtime.getContainerElement(container.ID)
if element == nil { if element == nil {
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.Id) return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
} }
if err := container.Stop(3); err != nil { if err := container.Stop(3); err != nil {
@ -194,14 +194,14 @@ func (runtime *Runtime) Destroy(container *Container) error {
return err return err
} else if mounted { } else if mounted {
if err := container.Unmount(); err != nil { if err := container.Unmount(); err != nil {
return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err) return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
} }
} }
// Deregister the container before removing its directory, to avoid race conditions // Deregister the container before removing its directory, to avoid race conditions
runtime.idIndex.Delete(container.Id) runtime.idIndex.Delete(container.ID)
runtime.containers.Remove(element) runtime.containers.Remove(element)
if err := os.RemoveAll(container.root); err != nil { if err := os.RemoveAll(container.root); err != nil {
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err) return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
} }
return nil return nil
} }
@ -218,7 +218,7 @@ func (runtime *Runtime) restore() error {
utils.Debugf("Failed to load container %v: %v", id, err) utils.Debugf("Failed to load container %v: %v", id, err)
continue continue
} }
utils.Debugf("Loaded container %v", container.Id) utils.Debugf("Loaded container %v", container.ID)
} }
return nil return nil
} }

View File

@ -120,7 +120,7 @@ func TestRuntimeCreate(t *testing.T) {
builder := NewBuilder(runtime) builder := NewBuilder(runtime)
container, err := builder.Create(&Config{ container, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"}, Cmd: []string{"ls", "-al"},
}, },
) )
@ -140,29 +140,29 @@ func TestRuntimeCreate(t *testing.T) {
} }
// Make sure the container List() returns is the right one // Make sure the container List() returns is the right one
if runtime.List()[0].Id != container.Id { if runtime.List()[0].ID != container.ID {
t.Errorf("Unexpected container %v returned by List", runtime.List()[0]) t.Errorf("Unexpected container %v returned by List", runtime.List()[0])
} }
// Make sure we can get the container with Get() // Make sure we can get the container with Get()
if runtime.Get(container.Id) == nil { if runtime.Get(container.ID) == nil {
t.Errorf("Unable to get newly created container") t.Errorf("Unable to get newly created container")
} }
// Make sure it is the right container // Make sure it is the right container
if runtime.Get(container.Id) != container { if runtime.Get(container.ID) != container {
t.Errorf("Get() returned the wrong container") t.Errorf("Get() returned the wrong container")
} }
// Make sure Exists returns it as existing // Make sure Exists returns it as existing
if !runtime.Exists(container.Id) { if !runtime.Exists(container.ID) {
t.Errorf("Exists() returned false for a newly created container") t.Errorf("Exists() returned false for a newly created container")
} }
// Make sure crete with bad parameters returns an error // Make sure crete with bad parameters returns an error
_, err = builder.Create( _, err = builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
}, },
) )
if err == nil { if err == nil {
@ -171,7 +171,7 @@ func TestRuntimeCreate(t *testing.T) {
_, err = builder.Create( _, err = builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{}, Cmd: []string{},
}, },
) )
@ -187,7 +187,7 @@ func TestDestroy(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"}, Cmd: []string{"ls", "-al"},
}, },
) )
@ -210,7 +210,7 @@ func TestDestroy(t *testing.T) {
} }
// Make sure runtime.Get() refuses to return the unexisting container // Make sure runtime.Get() refuses to return the unexisting container
if runtime.Get(container.Id) != nil { if runtime.Get(container.ID) != nil {
t.Errorf("Unable to get newly created container") t.Errorf("Unable to get newly created container")
} }
@ -237,7 +237,7 @@ func TestGet(t *testing.T) {
builder := NewBuilder(runtime) builder := NewBuilder(runtime)
container1, err := builder.Create(&Config{ container1, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"}, Cmd: []string{"ls", "-al"},
}, },
) )
@ -247,7 +247,7 @@ func TestGet(t *testing.T) {
defer runtime.Destroy(container1) defer runtime.Destroy(container1)
container2, err := builder.Create(&Config{ container2, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"}, Cmd: []string{"ls", "-al"},
}, },
) )
@ -257,7 +257,7 @@ func TestGet(t *testing.T) {
defer runtime.Destroy(container2) defer runtime.Destroy(container2)
container3, err := builder.Create(&Config{ container3, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"}, Cmd: []string{"ls", "-al"},
}, },
) )
@ -266,16 +266,16 @@ func TestGet(t *testing.T) {
} }
defer runtime.Destroy(container3) defer runtime.Destroy(container3)
if runtime.Get(container1.Id) != container1 { if runtime.Get(container1.ID) != container1 {
t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.Id), container1) t.Errorf("Get(test1) returned %v while expecting %v", runtime.Get(container1.ID), container1)
} }
if runtime.Get(container2.Id) != container2 { if runtime.Get(container2.ID) != container2 {
t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.Id), container2) t.Errorf("Get(test2) returned %v while expecting %v", runtime.Get(container2.ID), container2)
} }
if runtime.Get(container3.Id) != container3 { if runtime.Get(container3.ID) != container3 {
t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.Id), container3) t.Errorf("Get(test3) returned %v while expecting %v", runtime.Get(container3.ID), container3)
} }
} }
@ -283,7 +283,7 @@ func TestGet(t *testing.T) {
func findAvailalblePort(runtime *Runtime, port int) (*Container, error) { func findAvailalblePort(runtime *Runtime, port int) (*Container, error) {
strPort := strconv.Itoa(port) strPort := strconv.Itoa(port)
container, err := NewBuilder(runtime).Create(&Config{ container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id, Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", "echo well hello there | nc -l -p " + strPort}, Cmd: []string{"sh", "-c", "echo well hello there | nc -l -p " + strPort},
PortSpecs: []string{strPort}, PortSpecs: []string{strPort},
}, },
@ -379,7 +379,7 @@ func TestRestore(t *testing.T) {
// Create a container with one instance of docker // Create a container with one instance of docker
container1, err := builder.Create(&Config{ container1, err := builder.Create(&Config{
Image: GetTestImage(runtime1).Id, Image: GetTestImage(runtime1).ID,
Cmd: []string{"ls", "-al"}, Cmd: []string{"ls", "-al"},
}, },
) )
@ -390,7 +390,7 @@ func TestRestore(t *testing.T) {
// Create a second container meant to be killed // Create a second container meant to be killed
container2, err := builder.Create(&Config{ container2, err := builder.Create(&Config{
Image: GetTestImage(runtime1).Id, Image: GetTestImage(runtime1).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
OpenStdin: true, OpenStdin: true,
}, },
@ -406,7 +406,7 @@ func TestRestore(t *testing.T) {
} }
if !container2.State.Running { if !container2.State.Running {
t.Fatalf("Container %v should appear as running but isn't", container2.Id) t.Fatalf("Container %v should appear as running but isn't", container2.ID)
} }
// Simulate a crash/manual quit of dockerd: process dies, states stays 'Running' // Simulate a crash/manual quit of dockerd: process dies, states stays 'Running'
@ -426,7 +426,7 @@ func TestRestore(t *testing.T) {
} }
if !container2.State.Running { if !container2.State.Running {
t.Fatalf("Container %v should appear as running but isn't", container2.Id) t.Fatalf("Container %v should appear as running but isn't", container2.ID)
} }
// Here are are simulating a docker restart - that is, reloading all containers // Here are are simulating a docker restart - that is, reloading all containers
@ -442,14 +442,14 @@ func TestRestore(t *testing.T) {
runningCount := 0 runningCount := 0
for _, c := range runtime2.List() { for _, c := range runtime2.List() {
if c.State.Running { if c.State.Running {
t.Errorf("Running container found: %v (%v)", c.Id, c.Path) t.Errorf("Running container found: %v (%v)", c.ID, c.Path)
runningCount++ runningCount++
} }
} }
if runningCount != 0 { if runningCount != 0 {
t.Fatalf("Expected 0 container alive, %d found", runningCount) t.Fatalf("Expected 0 container alive, %d found", runningCount)
} }
container3 := runtime2.Get(container1.Id) container3 := runtime2.Get(container1.ID)
if container3 == nil { if container3 == nil {
t.Fatal("Unable to Get container") t.Fatal("Unable to Get container")
} }

188
server.go
View File

@ -17,8 +17,12 @@ import (
"strings" "strings"
) )
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: GITCOMMIT,
GoVersion: runtime.Version(),
}
} }
func (srv *Server) ContainerKill(name string) error { func (srv *Server) ContainerKill(name string) error {
@ -49,16 +53,16 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
return fmt.Errorf("No such container: %s", name) return fmt.Errorf("No such container: %s", name)
} }
func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) { func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term) results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var outs []ApiSearch var outs []APISearch
for _, repo := range results.Results { for _, repo := range results.Results {
var out ApiSearch var out APISearch
out.Description = repo["description"] out.Description = repo["description"]
if len(out.Description) > 45 { if len(out.Description) > 45 {
out.Description = utils.Trunc(out.Description, 42) + "..." out.Description = utils.Trunc(out.Description, 42) + "..."
@ -82,7 +86,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
} }
defer file.Body.Close() defer file.Body.Close()
config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities) config, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -101,8 +105,8 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
if err != nil { if err != nil {
return "", err return "", err
} }
out.Write(sf.FormatStatus(img.Id)) out.Write(sf.FormatStatus(img.ID))
return img.ShortId(), nil return img.ShortID(), nil
} }
func (srv *Server) ImagesViz(out io.Writer) error { func (srv *Server) ImagesViz(out io.Writer) error {
@ -122,9 +126,9 @@ func (srv *Server) ImagesViz(out io.Writer) error {
return fmt.Errorf("Error while getting parent image: %v", err) return fmt.Errorf("Error while getting parent image: %v", err)
} }
if parentImage != nil { if parentImage != nil {
out.Write([]byte(" \"" + parentImage.ShortId() + "\" -> \"" + image.ShortId() + "\"\n")) out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n"))
} else { } else {
out.Write([]byte(" base -> \"" + image.ShortId() + "\" [style=invis]\n")) out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n"))
} }
} }
@ -132,7 +136,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
for name, repository := range srv.runtime.repositories.Repositories { for name, repository := range srv.runtime.repositories.Repositories {
for tag, id := range repository { for tag, id := range repository {
reporefs[utils.TruncateId(id)] = append(reporefs[utils.TruncateId(id)], fmt.Sprintf("%s:%s", name, tag)) reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag))
} }
} }
@ -143,7 +147,7 @@ func (srv *Server) ImagesViz(out io.Writer) error {
return nil return nil
} }
func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) { func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
var ( var (
allImages map[string]*Image allImages map[string]*Image
err error err error
@ -156,13 +160,13 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
outs := []ApiImages{} //produce [] when empty instead of 'null' outs := []APIImages{} //produce [] when empty instead of 'null'
for name, repository := range srv.runtime.repositories.Repositories { for name, repository := range srv.runtime.repositories.Repositories {
if filter != "" && name != filter { if filter != "" && name != filter {
continue continue
} }
for tag, id := range repository { for tag, id := range repository {
var out ApiImages var out APIImages
image, err := srv.runtime.graph.Get(id) image, err := srv.runtime.graph.Get(id)
if err != nil { if err != nil {
log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err) log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
@ -171,7 +175,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
delete(allImages, id) delete(allImages, id)
out.Repository = name out.Repository = name
out.Tag = tag out.Tag = tag
out.Id = image.Id out.ID = image.ID
out.Created = image.Created.Unix() out.Created = image.Created.Unix()
outs = append(outs, out) outs = append(outs, out)
} }
@ -179,8 +183,8 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
// Display images which aren't part of a // Display images which aren't part of a
if filter == "" { if filter == "" {
for _, image := range allImages { for _, image := range allImages {
var out ApiImages var out APIImages
out.Id = image.Id out.ID = image.ID
out.Created = image.Created.Unix() out.Created = image.Created.Unix()
outs = append(outs, out) outs = append(outs, out)
} }
@ -188,7 +192,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 {
@ -196,29 +200,27 @@ 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) {
image, err := srv.runtime.repositories.LookupImage(name) image, err := srv.runtime.repositories.LookupImage(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null' outs := []APIHistory{} //produce [] when empty instead of 'null'
err = image.WalkHistory(func(img *Image) error { err = image.WalkHistory(func(img *Image) error {
var out ApiHistory var out APIHistory
out.Id = srv.runtime.repositories.ImageName(img.ShortId()) out.ID = srv.runtime.repositories.ImageName(img.ShortID())
out.Created = img.Created.Unix() out.Created = img.Created.Unix()
out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ") out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
outs = append(outs, out) outs = append(outs, out)
@ -235,17 +237,17 @@ func (srv *Server) ContainerChanges(name string) ([]Change, error) {
return nil, fmt.Errorf("No such container: %s", name) return nil, fmt.Errorf("No such container: %s", name)
} }
func (srv *Server) Containers(all bool, n int, since, before string) []ApiContainers { func (srv *Server) Containers(all bool, n int, since, before string) []APIContainers {
var foundBefore bool var foundBefore bool
var displayed int var displayed int
retContainers := []ApiContainers{} retContainers := []APIContainers{}
for _, container := range srv.runtime.List() { for _, container := range srv.runtime.List() {
if !container.State.Running && !all && n == -1 && since == "" && before == "" { if !container.State.Running && !all && n == -1 && since == "" && before == "" {
continue continue
} }
if before != "" { if before != "" {
if container.ShortId() == before { if container.ShortID() == before {
foundBefore = true foundBefore = true
continue continue
} }
@ -256,13 +258,13 @@ func (srv *Server) Containers(all bool, n int, since, before string) []ApiContai
if displayed == n { if displayed == n {
break break
} }
if container.ShortId() == since { if container.ShortID() == since {
break break
} }
displayed++ displayed++
c := ApiContainers{ c := APIContainers{
Id: container.Id, ID: container.ID,
} }
c.Image = srv.runtime.repositories.ImageName(container.Image) c.Image = srv.runtime.repositories.ImageName(container.Image)
c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " ")) c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
@ -283,7 +285,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf
if err != nil { if err != nil {
return "", err return "", err
} }
return img.ShortId(), err return img.ShortID(), err
} }
func (srv *Server) ContainerTag(name, repo, tag string, force bool) error { func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
@ -304,22 +306,23 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
for _, id := range history { for _, id := range history {
if !srv.runtime.graph.Exists(id) { if !srv.runtime.graph.Exists(id) {
out.Write(sf.FormatStatus("Pulling %s metadata", 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?
return err return err
} }
img, err := NewImgJson(imgJson) img, err := NewImgJSON(imgJSON)
if err != nil { if err != nil {
return fmt.Errorf("Failed to parse json: %s", err) return fmt.Errorf("Failed to parse json: %s", err)
} }
// Get the layer // Get the layer
out.Write(sf.FormatStatus("Pulling %s fs layer", 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
} }
defer layer.Close()
if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), 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
} }
@ -328,8 +331,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, sf *utils.StreamFormatter) error { func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, local, remote, askedTag string, sf *utils.StreamFormatter) error {
out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress())) out.Write(sf.FormatStatus("Pulling repository %s from %s", local, auth.IndexServerAddress()))
repoData, err := r.GetRepositoryData(remote) repoData, err := r.GetRepositoryData(remote)
if err != nil { if err != nil {
return err return err
@ -354,22 +357,22 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
} }
} else { } else {
// Otherwise, check that the tag exists and use only that one // Otherwise, check that the tag exists and use only that one
if id, exists := tagsList[askedTag]; !exists { id, exists := tagsList[askedTag]
return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote) if !exists {
} else { return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, local)
repoData.ImgList[id].Tag = askedTag
} }
repoData.ImgList[id].Tag = askedTag
} }
for _, img := range repoData.ImgList { for _, img := range repoData.ImgList {
if askedTag != "" && img.Tag != askedTag { if askedTag != "" && img.Tag != askedTag {
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
} }
out.Write(sf.FormatStatus("Pulling image %s (%s) from %s", 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, sf); err != nil { if err := srv.pullImage(r, out, img.ID, "https://"+ep+"/v1", repoData.Tokens, sf); err != nil {
out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err)) out.Write(sf.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint", askedTag, err))
continue continue
} }
@ -384,7 +387,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
if askedTag != "" && tag != askedTag { if askedTag != "" && tag != askedTag {
continue continue
} }
if err := srv.runtime.repositories.Set(remote, tag, id, true); err != nil { if err := srv.runtime.repositories.Set(local, tag, id, true); err != nil {
return err return err
} }
} }
@ -404,8 +407,12 @@ func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *util
} }
return nil return nil
} }
remote := name
if err := srv.pullRepository(r, out, name, tag, sf); err != nil { parts := strings.Split(name, "/")
if len(parts) > 2 {
remote = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
}
if err := srv.pullRepository(r, out, name, remote, tag, sf); err != nil {
return err return err
} }
@ -459,16 +466,16 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
return nil, err return nil, err
} }
img.WalkHistory(func(img *Image) error { img.WalkHistory(func(img *Image) error {
if _, exists := imageSet[img.Id]; exists { if _, exists := imageSet[img.ID]; exists {
return nil return nil
} }
imageSet[img.Id] = struct{}{} imageSet[img.ID] = struct{}{}
checksum, err := srv.getChecksum(img.Id) checksum, err := srv.getChecksum(img.ID)
if err != nil { if err != nil {
return err return err
} }
imgList = append([]*registry.ImgData{{ imgList = append([]*registry.ImgData{{
Id: img.Id, ID: img.ID,
Checksum: checksum, Checksum: checksum,
Tag: tag, Tag: tag,
}}, imgList...) }}, imgList...)
@ -487,7 +494,13 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
} }
out.Write(sf.FormatStatus("Sending image list")) out.Write(sf.FormatStatus("Sending image list"))
repoData, err := r.PushImageJsonIndex(name, imgList, false) srvName := name
parts := strings.Split(name, "/")
if len(parts) > 2 {
srvName = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/")))
}
repoData, err := r.PushImageJSONIndex(srvName, imgList, false)
if err != nil { if err != nil {
return err return err
} }
@ -496,22 +509,22 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
out.Write(sf.FormatStatus("Pushing repository %s to %s (%d tags)", 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 {
out.Write(sf.FormatStatus("Image %s already on registry, skipping", 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, sf); 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
} }
out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.Id, ep+"/users/"+name+"/"+elem.Tag)) out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+srvName+"/"+elem.Tag))
if err := r.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil { if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
return err return err
} }
} }
} }
if _, err := r.PushImageJsonIndex(name, imgList, true); err != nil { if _, err := r.PushImageJSONIndex(srvName, imgList, true); err != nil {
return err return err
} }
return nil return nil
@ -531,14 +544,14 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
return err return err
} }
imgData := &registry.ImgData{ imgData := &registry.ImgData{
Id: imgId, ID: imgId,
Checksum: checksum, Checksum: checksum,
} }
// 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 {
out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.Id)) out.Write(sf.FormatStatus("Image %s already uploaded ; skipping", imgData.ID))
return nil return nil
} }
return err return err
@ -571,7 +584,7 @@ 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, sf.FormatProgress("", "%v/%v (%v)"), sf), ep, token); err != nil { if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%v/%v (%v)"), sf), ep, token); err != nil {
return err return err
} }
return nil return nil
@ -595,7 +608,7 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.Str
return err return err
} }
out.Write(sf.FormatStatus("The push refers to an image: [%s]", name)) out.Write(sf.FormatStatus("The push refers to an image: [%s]", name))
if err := srv.pushImage(r, out, name, img.Id, endpoint, nil, sf); err != nil { if err := srv.pushImage(r, out, name, img.ID, endpoint, nil, sf); err != nil {
return err return err
} }
return nil return nil
@ -632,11 +645,11 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
} }
// Optionally register the image at REPO/TAG // Optionally register the image at REPO/TAG
if repo != "" { if repo != "" {
if err := srv.runtime.repositories.Set(repo, tag, img.Id, true); err != nil { if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil {
return err return err
} }
} }
out.Write(sf.FormatStatus(img.ShortId())) out.Write(sf.FormatStatus(img.ShortID()))
return nil return nil
} }
@ -657,7 +670,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
} }
return "", err return "", err
} }
return container.ShortId(), nil return container.ShortID(), nil
} }
func (srv *Server) ContainerRestart(name string, t int) error { func (srv *Server) ContainerRestart(name string, t int) error {
@ -694,7 +707,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
for volumeId := range volumes { for volumeId := range volumes {
// If the requested volu // If the requested volu
if c, exists := usedVolumes[volumeId]; exists { if c, exists := usedVolumes[volumeId]; exists {
log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id) log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.ID)
continue continue
} }
if err := srv.runtime.volumes.Delete(volumeId); err != nil { if err := srv.runtime.volumes.Delete(volumeId); err != nil {
@ -710,9 +723,9 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
var ErrImageReferenced = errors.New("Image referenced by a repository") var ErrImageReferenced = errors.New("Image referenced by a repository")
func (srv *Server) deleteImageAndChildren(id string, imgs *[]ApiRmi) error { func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error {
// If the image is referenced by a repo, do not delete // If the image is referenced by a repo, do not delete
if len(srv.runtime.repositories.ById()[id]) != 0 { if len(srv.runtime.repositories.ByID()[id]) != 0 {
return ErrImageReferenced return ErrImageReferenced
} }
@ -723,12 +736,11 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]ApiRmi) error {
return err return err
} }
for _, img := range byParents[id] { for _, img := range byParents[id] {
if err := srv.deleteImageAndChildren(img.Id, imgs); err != nil { if err := srv.deleteImageAndChildren(img.ID, imgs); err != nil {
if err != ErrImageReferenced { if err != ErrImageReferenced {
return err return err
} else {
referenced = true
} }
referenced = true
} }
} }
if referenced { if referenced {
@ -748,13 +760,13 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]ApiRmi) error {
if err != nil { if err != nil {
return err return err
} }
*imgs = append(*imgs, ApiRmi{Deleted: utils.TruncateId(id)}) *imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)})
return nil return nil
} }
return nil return nil
} }
func (srv *Server) deleteImageParents(img *Image, imgs *[]ApiRmi) error { func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error {
if img.Parent != "" { if img.Parent != "" {
parent, err := srv.runtime.graph.Get(img.Parent) parent, err := srv.runtime.graph.Get(img.Parent)
if err != nil { if err != nil {
@ -769,18 +781,18 @@ func (srv *Server) deleteImageParents(img *Image, imgs *[]ApiRmi) error {
return nil return nil
} }
func (srv *Server) deleteImage(img *Image, repoName, tag string) (*[]ApiRmi, error) { func (srv *Server) deleteImage(img *Image, repoName, tag string) (*[]APIRmi, error) {
//Untag the current image //Untag the current image
var imgs []ApiRmi var imgs []APIRmi
tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag) tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if tagDeleted { if tagDeleted {
imgs = append(imgs, ApiRmi{Untagged: img.ShortId()}) imgs = append(imgs, APIRmi{Untagged: img.ShortID()})
} }
if len(srv.runtime.repositories.ById()[img.Id]) == 0 { if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
if err := srv.deleteImageAndChildren(img.Id, &imgs); err != nil { if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil {
if err != ErrImageReferenced { if err != ErrImageReferenced {
return &imgs, err return &imgs, err
} }
@ -793,13 +805,13 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) (*[]ApiRmi, err
return &imgs, nil return &imgs, nil
} }
func (srv *Server) ImageDelete(name string, autoPrune bool) (*[]ApiRmi, error) { func (srv *Server) ImageDelete(name string, autoPrune bool) (*[]APIRmi, error) {
img, err := srv.runtime.repositories.LookupImage(name) img, err := srv.runtime.repositories.LookupImage(name)
if err != nil { if err != nil {
return nil, fmt.Errorf("No such image: %s", name) return nil, fmt.Errorf("No such image: %s", name)
} }
if !autoPrune { if !autoPrune {
if err := srv.runtime.graph.Delete(img.Id); err != nil { if err := srv.runtime.graph.Delete(img.ID); err != nil {
return nil, fmt.Errorf("Error deleting image %s: %s", name, err.Error()) return nil, fmt.Errorf("Error deleting image %s: %s", name, err.Error())
} }
return nil, nil return nil, nil
@ -829,7 +841,7 @@ func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error)
if _, exists := imageMap[img.Parent]; !exists { if _, exists := imageMap[img.Parent]; !exists {
imageMap[img.Parent] = make(map[string]struct{}) imageMap[img.Parent] = make(map[string]struct{})
} }
imageMap[img.Parent][img.Id] = struct{}{} imageMap[img.Parent][img.ID] = struct{}{}
} }
// Loop on the children of the given image and check the config // Loop on the children of the given image and check the config

View File

@ -65,7 +65,7 @@ func TestCreateRm(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "echo test"}, nil) config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -98,7 +98,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
config, _, err := ParseRun([]string{GetTestImage(runtime).Id, "/bin/cat"}, nil) config, _, err := ParseRun([]string{GetTestImage(runtime).ID, "/bin/cat"}, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

28
tags.go
View File

@ -11,7 +11,7 @@ import (
"strings" "strings"
) )
const DEFAULT_TAG = "latest" const DEFAULTTAG = "latest"
type TagStore struct { type TagStore struct {
path string path string
@ -72,7 +72,7 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
// (so we can pass all errors here) // (so we can pass all errors here)
repoAndTag := strings.SplitN(name, ":", 2) repoAndTag := strings.SplitN(name, ":", 2)
if len(repoAndTag) == 1 { if len(repoAndTag) == 1 {
repoAndTag = append(repoAndTag, DEFAULT_TAG) repoAndTag = append(repoAndTag, DEFAULTTAG)
} }
if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil { if i, err := store.GetImage(repoAndTag[0], repoAndTag[1]); err != nil {
return nil, err return nil, err
@ -87,31 +87,31 @@ func (store *TagStore) LookupImage(name string) (*Image, error) {
// Return a reverse-lookup table of all the names which refer to each image // Return a reverse-lookup table of all the names which refer to each image
// Eg. {"43b5f19b10584": {"base:latest", "base:v1"}} // Eg. {"43b5f19b10584": {"base:latest", "base:v1"}}
func (store *TagStore) ById() map[string][]string { func (store *TagStore) ByID() map[string][]string {
byId := make(map[string][]string) byID := make(map[string][]string)
for repoName, repository := range store.Repositories { for repoName, repository := range store.Repositories {
for tag, id := range repository { for tag, id := range repository {
name := repoName + ":" + tag name := repoName + ":" + tag
if _, exists := byId[id]; !exists { if _, exists := byID[id]; !exists {
byId[id] = []string{name} byID[id] = []string{name}
} else { } else {
byId[id] = append(byId[id], name) byID[id] = append(byID[id], name)
sort.Strings(byId[id]) sort.Strings(byID[id])
} }
} }
} }
return byId return byID
} }
func (store *TagStore) ImageName(id string) string { func (store *TagStore) ImageName(id string) string {
if names, exists := store.ById()[id]; exists && len(names) > 0 { if names, exists := store.ByID()[id]; exists && len(names) > 0 {
return names[0] return names[0]
} }
return utils.TruncateId(id) return utils.TruncateID(id)
} }
func (store *TagStore) DeleteAll(id string) error { func (store *TagStore) DeleteAll(id string) error {
names, exists := store.ById()[id] names, exists := store.ByID()[id]
if !exists || len(names) == 0 { if !exists || len(names) == 0 {
return nil return nil
} }
@ -162,7 +162,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
return err return err
} }
if tag == "" { if tag == "" {
tag = DEFAULT_TAG tag = DEFAULTTAG
} }
if err := validateRepoName(repoName); err != nil { if err := validateRepoName(repoName); err != nil {
return err return err
@ -183,7 +183,7 @@ func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
} }
store.Repositories[repoName] = repo store.Repositories[repoName] = repo
} }
repo[tag] = img.Id repo[tag] = img.ID
return store.Save() return store.Save()
} }

View File

@ -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)
} }

View File

@ -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
} }

View File

@ -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
} }

View File

@ -34,7 +34,7 @@ func Go(f func() error) chan error {
// Request a given URL and return an io.Reader // Request a given URL and return an io.Reader
func Download(url string, stderr io.Writer) (*http.Response, error) { func Download(url string, stderr io.Writer) (*http.Response, error) {
var resp *http.Response var resp *http.Response
var err error = nil var err error
if resp, err = http.Get(url); err != nil { if resp, err = http.Get(url); err != nil {
return nil, err return nil, err
} }
@ -349,11 +349,11 @@ func (idx *TruncIndex) Get(s string) (string, error) {
return string(idx.bytes[before:after]), err return string(idx.bytes[before:after]), err
} }
// TruncateId returns a shorthand version of a string identifier for convenience. // TruncateID returns a shorthand version of a string identifier for convenience.
// A collision with other shorthands is very unlikely, but possible. // A collision with other shorthands is very unlikely, but possible.
// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
// will need to use a langer prefix, or the full-length Id. // will need to use a langer prefix, or the full-length Id.
func TruncateId(id string) string { func TruncateID(id string) string {
shortLen := 12 shortLen := 12
if len(id) < shortLen { if len(id) < shortLen {
shortLen = len(id) shortLen = len(id)
@ -566,7 +566,7 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
return &WriteFlusher{w: w, flusher: flusher} return &WriteFlusher{w: w, flusher: flusher}
} }
type JsonMessage struct { type JSONMessage struct {
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
Progress string `json:"progress,omitempty"` Progress string `json:"progress,omitempty"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
@ -585,7 +585,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte
sf.used = true sf.used = true
str := fmt.Sprintf(format, a...) str := fmt.Sprintf(format, a...)
if sf.json { if sf.json {
b, err := json.Marshal(&JsonMessage{Status:str}); b, err := json.Marshal(&JSONMessage{Status:str});
if err != nil { if err != nil {
return sf.FormatError(err) return sf.FormatError(err)
} }
@ -597,7 +597,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte
func (sf *StreamFormatter) FormatError(err error) []byte { func (sf *StreamFormatter) FormatError(err error) []byte {
sf.used = true sf.used = true
if sf.json { if sf.json {
if b, err := json.Marshal(&JsonMessage{Error:err.Error()}); err == nil { if b, err := json.Marshal(&JSONMessage{Error:err.Error()}); err == nil {
return b return b
} }
return []byte("{\"error\":\"format error\"}") return []byte("{\"error\":\"format error\"}")
@ -608,7 +608,7 @@ func (sf *StreamFormatter) FormatError(err error) []byte {
func (sf *StreamFormatter) FormatProgress(action, str string) []byte { func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
sf.used = true sf.used = true
if sf.json { if sf.json {
b, err := json.Marshal(&JsonMessage{Progress:str}) b, err := json.Marshal(&JSONMessage{Status: action, Progress:str})
if err != nil { if err != nil {
return nil return nil
} }