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
## 0.4.0 (2013-06-03)
+ Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+ Introducing Remote API: control Docker programmatically using a simple HTTP/json API
* Runtime: various reliability and usability improvements
## 0.3.4 (2013-05-30)
+ Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+ Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.

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.
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.
@ -371,4 +371,7 @@ Standard Container Specification
#### 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"
)
const API_VERSION = 1.1
const APIVERSION = 1.1
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
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.Write(b)
}
@ -84,7 +84,7 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -113,11 +113,11 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque
}
if status != "" {
b, err := json.Marshal(&ApiAuth{Status: status})
b, err := json.Marshal(&APIAuth{Status: status})
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
w.WriteHeader(http.StatusNoContent)
@ -130,7 +130,7 @@ func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Req
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -159,7 +159,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
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 {
return err
}
@ -178,7 +178,7 @@ func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -195,7 +195,7 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -212,7 +212,7 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -229,11 +229,11 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
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 {
return err
}
@ -253,7 +253,7 @@ func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *h
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -296,12 +296,12 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
if err != nil {
return err
}
b, err := json.Marshal(&ApiId{id})
b, err := json.Marshal(&APIID{id})
if err != nil {
return err
}
w.WriteHeader(http.StatusCreated)
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -355,7 +355,7 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -374,18 +374,18 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
w.Header().Set("Content-Type", "application/json")
}
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 sf.Used() {
w.Write(sf.FormatError(err))
return nil
}
}
b, err := json.Marshal(&ApiId{Id: imgId})
b, err := json.Marshal(&APIID{ID: imgID})
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -423,8 +423,8 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
return err
}
out := &ApiRun{
Id: id,
out := &APIRun{
ID: id,
}
if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
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
}
w.WriteHeader(http.StatusCreated)
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -500,7 +500,7 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
} else {
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 {
return err
}
b, err := json.Marshal(&ApiWait{StatusCode: status})
b, err := json.Marshal(&APIWait{StatusCode: status})
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -643,7 +643,7 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -661,17 +661,17 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
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 {
return err
}
image, err := srv.ImageGetCached(apiConfig.Id, apiConfig.Config)
image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config)
if err != nil {
return err
}
@ -679,12 +679,12 @@ func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *
w.WriteHeader(http.StatusNotFound)
return nil
}
apiId := &ApiId{Id: image.Id}
b, err := json.Marshal(apiId)
apiID := &APIID{ID: image.ID}
b, err := json.Marshal(apiID)
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@ -730,13 +730,13 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
"/auth": getAuth,
"/version": getVersion,
"/info": getInfo,
"/images/json": getImagesJson,
"/images/json": getImagesJSON,
"/images/viz": getImagesViz,
"/images/search": getImagesSearch,
"/images/{name:.*}/history": getImagesHistory,
"/images/{name:.*}/json": getImagesByName,
"/containers/ps": getContainersJson,
"/containers/json": getContainersJson,
"/containers/ps": getContainersJSON,
"/containers/json": getContainersJSON,
"/containers/{name:.*}/export": getContainersExport,
"/containers/{name:.*}/changes": getContainersChanges,
"/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)
if err != nil {
version = API_VERSION
version = APIVERSION
}
if version == 0 || version > API_VERSION {
if version == 0 || version > APIVERSION {
w.WriteHeader(http.StatusNotFound)
return
}

View file

@ -1,35 +1,35 @@
package docker
type ApiHistory struct {
Id string
type APIHistory struct {
ID string `json:"Id"`
Created int64
CreatedBy string
CreatedBy string `json:",omitempty"`
}
type ApiImages struct {
type APIImages struct {
Repository string `json:",omitempty"`
Tag string `json:",omitempty"`
Id string
ID string `json:"Id"`
Created int64
}
type ApiInfo struct {
Containers int
Version string
Images int
type APIInfo struct {
Debug bool
GoVersion string
NFd int `json:",omitempty"`
NGoroutines int `json:",omitempty"`
Containers int
Images int
NFd int `json:",omitempty"`
NGoroutines int `json:",omitempty"`
MemoryLimit bool `json:",omitempty"`
SwapLimit bool `json:",omitempty"`
}
type ApiRmi struct {
type APIRmi struct {
Deleted string `json:",omitempty"`
Untagged string `json:",omitempty"`
}
type ApiContainers struct {
Id string
type APIContainers struct {
ID string `json:"Id"`
Image string
Command string
Created int64
@ -37,40 +37,39 @@ type ApiContainers struct {
Ports string
}
type ApiSearch struct {
type APISearch struct {
Name string
Description string
}
type ApiId struct {
Id string
type APIID struct {
ID string `json:"Id"`
}
type ApiRun struct {
Id string
Warnings []string
type APIRun struct {
ID string `json:"Id"`
Warnings []string `json:",omitempty"`
}
type ApiPort struct {
type APIPort struct {
Port string
}
type ApiVersion struct {
Version string
GitCommit string
MemoryLimit bool
SwapLimit bool
type APIVersion struct {
Version string
GitCommit string `json:",omitempty"`
GoVersion string `json:",omitempty"`
}
type ApiWait struct {
type APIWait struct {
StatusCode int
}
type ApiAuth struct {
type APIAuth struct {
Status string
}
type ApiImageConfig struct {
Id string
type APIImageConfig struct {
ID string `json:"Id"`
*Config
}

View file

@ -37,17 +37,17 @@ func TestGetAuth(t *testing.T) {
Email: "utest@yopmail.com",
}
authConfigJson, err := json.Marshal(authConfig)
authConfigJSON, err := json.Marshal(authConfig)
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJson))
req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJSON))
if err != nil {
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)
}
@ -73,11 +73,11 @@ func TestGetVersion(t *testing.T) {
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)
}
v := &ApiVersion{}
v := &APIVersion{}
if err = json.Unmarshal(r.Body.Bytes(), v); err != nil {
t.Fatal(err)
}
@ -97,21 +97,21 @@ func TestGetInfo(t *testing.T) {
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)
}
infos := &ApiInfo{}
infos := &APIInfo{}
err = json.Unmarshal(r.Body.Bytes(), infos)
if err != nil {
t.Fatal(err)
}
if infos.Version != VERSION {
t.Errorf("Excepted version %s, %s found", VERSION, infos.Version)
if infos.Images != 1 {
t.Errorf("Excepted images: %d, %d found", 1, infos.Images)
}
}
func TestGetImagesJson(t *testing.T) {
func TestGetImagesJSON(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
@ -128,11 +128,11 @@ func TestGetImagesJson(t *testing.T) {
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)
}
images := []ApiImages{}
images := []APIImages{}
if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
t.Fatal(err)
}
@ -153,11 +153,11 @@ func TestGetImagesJson(t *testing.T) {
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)
}
images2 := []ApiImages{}
images2 := []APIImages{}
if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
t.Fatal(err)
}
@ -166,8 +166,8 @@ func TestGetImagesJson(t *testing.T) {
t.Errorf("Excepted 1 image, %d found", len(images2))
}
if images2[0].Id != GetTestImage(runtime).Id {
t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).Id, images2[0].Id)
if images2[0].ID != GetTestImage(runtime).ID {
t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).ID, images2[0].ID)
}
r3 := httptest.NewRecorder()
@ -178,11 +178,11 @@ func TestGetImagesJson(t *testing.T) {
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)
}
images3 := []ApiImages{}
images3 := []APIImages{}
if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
t.Fatal(err)
}
@ -199,7 +199,7 @@ func TestGetImagesJson(t *testing.T) {
t.Fatal(err)
}
err = getImagesJson(srv, API_VERSION, r4, req4, nil)
err = getImagesJSON(srv, APIVERSION, r4, req4, nil)
if err == nil {
t.Fatalf("Error expected, received none")
}
@ -220,7 +220,7 @@ func TestGetImagesViz(t *testing.T) {
srv := &Server{runtime: runtime}
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)
}
@ -256,11 +256,11 @@ func TestGetImagesSearch(t *testing.T) {
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)
}
results := []ApiSearch{}
results := []APISearch{}
if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil {
t.Fatal(err)
}
@ -280,11 +280,11 @@ func TestGetImagesHistory(t *testing.T) {
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)
}
history := []ApiHistory{}
history := []APIHistory{}
if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil {
t.Fatal(err)
}
@ -303,7 +303,7 @@ func TestGetImagesByName(t *testing.T) {
srv := &Server{runtime: runtime}
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)
}
@ -311,12 +311,12 @@ func TestGetImagesByName(t *testing.T) {
if err := json.Unmarshal(r.Body.Bytes(), img); err != nil {
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")
}
}
func TestGetContainersJson(t *testing.T) {
func TestGetContainersJSON(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
@ -326,7 +326,7 @@ func TestGetContainersJson(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
})
if err != nil {
@ -340,18 +340,18 @@ func TestGetContainersJson(t *testing.T) {
}
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)
}
containers := []ApiContainers{}
containers := []APIContainers{}
if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
t.Fatal(err)
}
if len(containers) != 1 {
t.Fatalf("Excepted %d container, %d found", 1, len(containers))
}
if containers[0].Id != container.Id {
t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.Id, containers[0].Id)
if containers[0].ID != container.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
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
},
)
@ -383,7 +383,7 @@ func TestGetContainersExport(t *testing.T) {
}
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)
}
@ -424,7 +424,7 @@ func TestGetContainersChanges(t *testing.T) {
// Create a container and remove a file
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"},
},
)
@ -438,7 +438,7 @@ func TestGetContainersChanges(t *testing.T) {
}
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)
}
changes := []Change{}
@ -472,7 +472,7 @@ func TestGetContainersByName(t *testing.T) {
// Create a container and remove a file
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
},
)
@ -482,15 +482,15 @@ func TestGetContainersByName(t *testing.T) {
defer runtime.Destroy(container)
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)
}
outContainer := &Container{}
if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
t.Fatal(err)
}
if outContainer.Id != container.Id {
t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.Id, outContainer.Id)
if outContainer.ID != container.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)
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)
}
@ -542,7 +542,7 @@ func TestPostCommit(t *testing.T) {
// Create a container and remove a file
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
},
)
@ -555,24 +555,24 @@ func TestPostCommit(t *testing.T) {
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 {
t.Fatal(err)
}
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)
}
if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
}
apiId := &ApiId{}
if err := json.Unmarshal(r.Body.Bytes(), apiId); err != nil {
apiID := &APIID{}
if err := json.Unmarshal(r.Body.Bytes(), apiID); err != nil {
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")
}
}
@ -715,7 +715,7 @@ func TestPostImagesInsert(t *testing.T) {
// 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)
// }
}
@ -824,8 +824,8 @@ func TestPostContainersCreate(t *testing.T) {
srv := &Server{runtime: runtime}
configJson, err := json.Marshal(&Config{
Image: GetTestImage(runtime).Id,
configJSON, err := json.Marshal(&Config{
Image: GetTestImage(runtime).ID,
Memory: 33554432,
Cmd: []string{"touch", "/test"},
})
@ -833,25 +833,25 @@ func TestPostContainersCreate(t *testing.T) {
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 {
t.Fatal(err)
}
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)
}
if r.Code != http.StatusCreated {
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 {
t.Fatal(err)
}
container := srv.runtime.Get(apiRun.Id)
container := srv.runtime.Get(apiRun.ID)
if container == nil {
t.Fatalf("Container not created")
}
@ -880,7 +880,7 @@ func TestPostContainersKill(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@ -902,7 +902,7 @@ func TestPostContainersKill(t *testing.T) {
}
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)
}
if r.Code != http.StatusNoContent {
@ -924,7 +924,7 @@ func TestPostContainersRestart(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@ -945,12 +945,12 @@ func TestPostContainersRestart(t *testing.T) {
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 {
t.Fatal(err)
}
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)
}
if r.Code != http.StatusNoContent {
@ -980,7 +980,7 @@ func TestPostContainersStart(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@ -991,7 +991,7 @@ func TestPostContainersStart(t *testing.T) {
defer runtime.Destroy(container)
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)
}
if r.Code != http.StatusNoContent {
@ -1006,7 +1006,7 @@ func TestPostContainersStart(t *testing.T) {
}
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")
}
@ -1026,7 +1026,7 @@ func TestPostContainersStop(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@ -1048,12 +1048,12 @@ func TestPostContainersStop(t *testing.T) {
}
// 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 {
t.Fatal(err)
}
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)
}
if r.Code != http.StatusNoContent {
@ -1075,7 +1075,7 @@ func TestPostContainersWait(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sleep", "1"},
OpenStdin: true,
},
@ -1091,10 +1091,10 @@ func TestPostContainersWait(t *testing.T) {
setTimeout(t, "Wait timed out", 3*time.Second, func() {
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)
}
apiWait := &ApiWait{}
apiWait := &APIWait{}
if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil {
t.Fatal(err)
}
@ -1119,7 +1119,7 @@ func TestPostContainersAttach(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@ -1148,12 +1148,12 @@ func TestPostContainersAttach(t *testing.T) {
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 {
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)
}
}()
@ -1206,7 +1206,7 @@ func TestDeleteContainers(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
})
if err != nil {
@ -1218,19 +1218,19 @@ func TestDeleteContainers(t *testing.T) {
t.Fatal(err)
}
req, err := http.NewRequest("DELETE", "/containers/"+container.Id, nil)
req, err := http.NewRequest("DELETE", "/containers/"+container.ID, nil)
if err != nil {
t.Fatal(err)
}
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)
}
if r.Code != http.StatusNoContent {
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")
}
@ -1267,14 +1267,14 @@ func TestDeleteImages(t *testing.T) {
}
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)
}
if r.Code != http.StatusOK {
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 {
t.Fatal(err)
}

View file

@ -16,12 +16,12 @@ import (
const CONFIGFILE = ".dockercfg"
// 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 (
ErrConfigFileMissing error = errors.New("The Auth config file is missing")
ErrConfigFileMissing = errors.New("The Auth config file is missing")
)
type AuthConfig struct {
@ -44,7 +44,7 @@ func IndexServerAddress() string {
if os.Getenv("DOCKER_INDEX_URL") != "" {
return os.Getenv("DOCKER_INDEX_URL") + "/v1"
}
return INDEX_SERVER
return INDEXSERVER
}
// 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
id := GenerateId()
id := GenerateID()
// Generate default hostname
// FIXME: the lxc template no longer needs to set a default hostname
if config.Hostname == "" {
@ -49,17 +49,17 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
container := &Container{
// FIXME: we should generate the ID here instead of receiving it as an argument
Id: id,
ID: id,
Created: time.Now(),
Path: config.Cmd[0],
Args: config.Cmd[1:], //FIXME: de-duplicate from config
Config: config,
Image: img.Id, // Always use the resolved image id
Image: img.ID, // Always use the resolved image id
NetworkSettings: &NetworkSettings{},
// FIXME: do we need to store this in the container?
SysInitPath: sysInitPath,
}
container.root = builder.runtime.containerRoot(container.Id)
container.root = builder.runtime.containerRoot(container.ID)
// Step 1: create the container directory.
// This doubles as a barrier to avoid race conditions.
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
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
}
}

View file

@ -63,11 +63,11 @@ func (b *builderClient) CmdFrom(name string) error {
return err
}
img := &ApiId{}
img := &APIID{}
if err := json.Unmarshal(obj, img); err != nil {
return err
}
b.image = img.Id
b.image = img.ID
utils.Debugf("Using image %s", b.image)
return nil
}
@ -91,19 +91,19 @@ func (b *builderClient) CmdRun(args string) error {
b.config.Cmd = nil
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 statusCode != 404 {
return err
}
}
if statusCode != 404 {
apiId := &ApiId{}
if err := json.Unmarshal(body, apiId); err != nil {
apiID := &APIID{}
if err := json.Unmarshal(body, apiID); err != nil {
return err
}
utils.Debugf("Use cached version")
b.image = apiId.Id
b.image = apiID.ID
return nil
}
cid, err := b.run()
@ -163,7 +163,7 @@ func (b *builderClient) CmdInsert(args string) error {
// return err
// }
// apiId := &ApiId{}
// apiId := &APIId{}
// if err := json.Unmarshal(body, apiId); err != nil {
// return err
// }
@ -182,7 +182,7 @@ func (b *builderClient) run() (string, error) {
return "", err
}
apiRun := &ApiRun{}
apiRun := &APIRun{}
if err := json.Unmarshal(body, apiRun); err != nil {
return "", err
}
@ -191,18 +191,18 @@ func (b *builderClient) run() (string, error) {
}
//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 {
return "", err
}
b.tmpContainers[apiRun.Id] = struct{}{}
b.tmpContainers[apiRun.ID] = struct{}{}
// 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 {
return "", err
}
apiWait := &ApiWait{}
apiWait := &APIWait{}
if err := json.Unmarshal(body, apiWait); err != nil {
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 apiRun.Id, nil
return apiRun.ID, nil
}
func (b *builderClient) commit(id string) error {
@ -222,11 +222,11 @@ func (b *builderClient) commit(id string) error {
if id == "" {
cmd := b.config.Cmd
b.config.Cmd = []string{"true"}
if cid, err := b.run(); err != nil {
cid, err := b.run()
if err != nil {
return err
} else {
id = cid
}
id = cid
b.config.Cmd = cmd
}
@ -239,12 +239,12 @@ func (b *builderClient) commit(id string) error {
if err != nil {
return err
}
apiId := &ApiId{}
if err := json.Unmarshal(body, apiId); err != nil {
apiID := &APIID{}
if err := json.Unmarshal(body, apiID); err != nil {
return err
}
b.tmpImages[apiId.Id] = struct{}{}
b.image = apiId.Id
b.tmpImages[apiID.ID] = struct{}{}
b.image = apiID.ID
b.needCommit = false
return nil
}

View file

@ -73,7 +73,7 @@ func (b *buildFile) CmdFrom(name string) error {
return err
}
}
b.image = image.Id
b.image = image.ID
b.config = &Config{}
return nil
}
@ -102,7 +102,7 @@ func (b *buildFile) CmdRun(args string) error {
return err
} else if cache != nil {
utils.Debugf("[BUILDER] Use cached version")
b.image = cache.Id
b.image = cache.ID
return nil
} else {
utils.Debugf("[BUILDER] Cache miss")
@ -238,7 +238,7 @@ func (b *buildFile) run() (string, error) {
if err != nil {
return "", err
}
b.tmpContainers[c.Id] = struct{}{}
b.tmpContainers[c.ID] = struct{}{}
//start the container
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 c.Id, nil
return c.ID, nil
}
// 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
} else if cache != nil {
utils.Debugf("[BUILDER] Use cached version")
b.image = cache.Id
b.image = cache.ID
return nil
} else {
utils.Debugf("[BUILDER] Cache miss")
}
if cid, err := b.run(); err != nil {
cid, err := b.run()
if err != nil {
return err
} else {
id = cid
}
id = cid
}
container := b.runtime.Get(id)
@ -292,8 +292,8 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
if err != nil {
return err
}
b.tmpImages[image.Id] = struct{}{}
b.image = image.Id
b.tmpImages[image.ID] = struct{}{}
b.image = image.ID
return nil
}

View file

@ -26,7 +26,7 @@ func TestBuild(t *testing.T) {
buildfile := NewBuildFile(srv, &utils.NopWriter{})
imgId, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
imgID, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
if err != nil {
t.Fatal(err)
}
@ -34,7 +34,7 @@ func TestBuild(t *testing.T) {
builder := NewBuilder(runtime)
container, err := builder.Create(
&Config{
Image: imgId,
Image: imgID,
Cmd: []string{"cat", "/tmp/passwd"},
},
)
@ -53,7 +53,7 @@ func TestBuild(t *testing.T) {
container2, err := builder.Create(
&Config{
Image: imgId,
Image: imgID,
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)
// If there is a whiteout, then the file was removed
if strings.HasPrefix(file, ".wh.") {
originalFile := strings.TrimLeft(file, ".wh.")
originalFile := file[len(".wh."):]
change.Path = filepath.Join(filepath.Dir(path), originalFile)
change.Kind = ChangeDelete
} else {

View file

@ -28,10 +28,10 @@ import (
"unicode"
)
const VERSION = "0.3.4"
const VERSION = "0.4.0"
var (
GIT_COMMIT string
GITCOMMIT string
)
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
@ -159,11 +159,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
file = os.Stdin
} else {
// 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
} else {
file = f
}
file = f
// Send context from arg
// Create a FormFile multipart for the context if needed
// 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 {
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
} 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)
}
// 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
} else {
io.Copy(wField, file)
}
io.Copy(wField, file)
multipartBody = io.MultiReader(multipartBody, boundary)
v := &url.Values{}
@ -276,9 +276,8 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
oldState, err := term.SetRawTerminal()
if err != nil {
return err
} else {
defer term.RestoreTerminal(oldState)
}
defer term.RestoreTerminal(oldState)
cmd := Subcmd("login", "", "Register or Login to the docker registry server")
if err := cmd.Parse(args); err != nil {
@ -331,7 +330,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
return err
}
var out2 ApiAuth
var out2 APIAuth
err = json.Unmarshal(body, &out2)
if err != nil {
return err
@ -358,7 +357,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
if err != nil {
fmt.Printf("%s", err)
} else {
var out ApiWait
var out APIWait
err = json.Unmarshal(body, &out)
if err != nil {
return err
@ -386,21 +385,20 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
return err
}
var out ApiVersion
var out APIVersion
err = json.Unmarshal(body, &out)
if err != nil {
utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
return err
}
fmt.Println("Version:", out.Version)
fmt.Println("Git Commit:", out.GitCommit)
if !out.MemoryLimit {
fmt.Println("WARNING: No memory limit support")
fmt.Println("Client version:", VERSION)
fmt.Println("Server version:", out.Version)
if out.GitCommit != "" {
fmt.Println("Git commit:", out.GitCommit)
}
if !out.SwapLimit {
fmt.Println("WARNING: No swap limit support")
if out.GoVersion != "" {
fmt.Println("Go version:", out.GoVersion)
}
return nil
}
@ -420,15 +418,24 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
return err
}
var out ApiInfo
err = json.Unmarshal(body, &out)
if err != nil {
var out APIInfo
if err := json.Unmarshal(body, &out); err != nil {
return err
}
fmt.Printf("containers: %d\nversion: %s\nimages: %d\nGo version: %s\n", out.Containers, out.Version, out.Images, out.GoVersion)
if out.Debug {
fmt.Println("debug mode enabled")
fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
fmt.Printf("Containers: %d\n", out.Containers)
fmt.Printf("Images: %d\n", out.Images)
if out.Debug || os.Getenv("DEBUG") != "" {
fmt.Printf("Debug mode (server): %v\n", out.Debug)
fmt.Printf("Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
fmt.Printf("Fds: %d\n", out.NFd)
fmt.Printf("Goroutines: %d\n", out.NGoroutines)
}
if !out.MemoryLimit {
fmt.Println("WARNING: No memory limit support")
}
if !out.SwapLimit {
fmt.Println("WARNING: No swap limit support")
}
return nil
}
@ -575,7 +582,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
if err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
} else {
var outs []ApiRmi
var outs []APIRmi
err = json.Unmarshal(body, &outs)
if err != nil {
return err
@ -607,7 +614,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
return err
}
var outs []ApiHistory
var outs []APIHistory
err = json.Unmarshal(body, &outs)
if err != nil {
return err
@ -616,7 +623,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
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()
return nil
@ -742,12 +749,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
remote = remoteParts[0]
}
if strings.Contains(remote, "/") {
if _, err := cli.checkIfLogged(true, "pull"); err != nil {
return err
}
}
v := url.Values{}
v.Set("fromImage", remote)
v.Set("tag", *tag)
@ -795,7 +796,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
return err
}
var outs []ApiImages
var outs []APIImages
err = json.Unmarshal(body, &outs)
if err != nil {
return err
@ -817,16 +818,16 @@ func (cli *DockerCli) CmdImages(args ...string) error {
if !*quiet {
fmt.Fprintf(w, "%s\t%s\t", out.Repository, out.Tag)
if *noTrunc {
fmt.Fprintf(w, "%s\t", out.Id)
fmt.Fprintf(w, "%s\t", out.ID)
} 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))))
} else {
if *noTrunc {
fmt.Fprintln(w, out.Id)
fmt.Fprintln(w, out.ID)
} 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
}
var outs []ApiContainers
var outs []APIContainers
err = json.Unmarshal(body, &outs)
if err != nil {
return err
@ -886,15 +887,15 @@ func (cli *DockerCli) CmdPs(args ...string) error {
for _, out := range outs {
if !*quiet {
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 {
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 {
if *noTrunc {
fmt.Fprintln(w, out.Id)
fmt.Fprintln(w, out.ID)
} 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
}
apiId := &ApiId{}
err = json.Unmarshal(body, apiId)
apiID := &APIID{}
err = json.Unmarshal(body, apiID)
if err != nil {
return err
}
fmt.Println(apiId.Id)
fmt.Println(apiID.ID)
return nil
}
@ -1080,7 +1081,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
return err
}
outs := []ApiSearch{}
outs := []APISearch{}
err = json.Unmarshal(body, &outs)
if err != nil {
return err
@ -1212,7 +1213,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
return err
}
out := &ApiRun{}
out := &APIRun{}
err = json.Unmarshal(body, out)
if err != nil {
return err
@ -1233,18 +1234,21 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
//start the container
_, _, err = cli.call("POST", "/containers/"+out.Id+"/start", nil)
_, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
if err != nil {
return err
}
if !config.AttachStdout && !config.AttachStderr {
fmt.Println(out.ID)
}
if connections > 0 {
chErrors := make(chan error, connections)
cli.monitorTtySize(out.Id)
cli.monitorTtySize(out.ID)
if splitStderr && config.AttachStderr {
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")
}
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 {
err := <-chErrors
@ -1272,9 +1276,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
connections -= 1
}
}
if !config.AttachStdout && !config.AttachStderr {
fmt.Println(out.Id)
}
return nil
}
@ -1322,7 +1323,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
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 {
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 {
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 {
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" {
dec := json.NewDecoder(resp.Body)
for {
var m utils.JsonMessage
var m utils.JSONMessage
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
return err
}
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 != "" {
return fmt.Errorf(m.Error)
} 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 {
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 {
return err
}
@ -1425,12 +1426,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
return err
})
if in != nil && setRawTerminal && term.IsTerminal(int(in.Fd())) && os.Getenv("NORAW") == "" {
if oldState, err := term.SetRawTerminal(); err != nil {
if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
oldState, err := term.SetRawTerminal()
if err != nil {
return err
} else {
defer term.RestoreTerminal(oldState)
}
defer term.RestoreTerminal(oldState)
}
sendStdin := utils.Go(func() error {
_, err := io.Copy(rwc, in)
@ -1444,7 +1445,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
return err
}
if !term.IsTerminal(int(os.Stdin.Fd())) {
if !term.IsTerminal(os.Stdin.Fd()) {
if err := <-sendStdin; err != nil {
return err
}

View file

@ -23,7 +23,7 @@ import (
type Container struct {
root string
Id string
ID string
Created time.Time
@ -167,8 +167,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
}
type NetworkSettings struct {
IpAddress string
IpPrefixLen int
IPAddress string
IPPrefixLen int
Gateway string
Bridge string
PortMapping map[string]string
@ -409,7 +409,7 @@ func (container *Container) Start() error {
defer container.State.unlock()
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 {
return err
@ -431,24 +431,24 @@ func (container *Container) Start() error {
// Create the requested volumes 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
} 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 != "" {
c := container.runtime.Get(container.Config.VolumesFrom)
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 {
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 {
return nil
@ -462,7 +462,7 @@ func (container *Container) Start() error {
}
params := []string{
"-n", container.Id,
"-n", container.ID,
"-f", container.lxcConfigPath(),
"--",
"/sbin/init",
@ -573,17 +573,17 @@ func (container *Container) allocateNetwork() error {
}
container.NetworkSettings.PortMapping = make(map[string]string)
for _, spec := range container.Config.PortSpecs {
if nat, err := iface.AllocatePort(spec); err != nil {
nat, err := iface.AllocatePort(spec)
if err != nil {
iface.Release()
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.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
container.NetworkSettings.IpAddress = iface.IPNet.IP.String()
container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size()
container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
container.NetworkSettings.Gateway = iface.Gateway.String()
return nil
}
@ -597,16 +597,16 @@ func (container *Container) releaseNetwork() {
// FIXME: replace this with a control socket within docker-init
func (container *Container) waitLxc() error {
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
} else {
if !strings.Contains(string(output), "RUNNING") {
return nil
}
}
if !strings.Contains(string(output), "RUNNING") {
return nil
}
time.Sleep(500 * time.Millisecond)
}
return nil
panic("Unreachable")
}
func (container *Container) monitor() {
@ -616,17 +616,17 @@ func (container *Container) monitor() {
// If the command does not exists, try to wait via lxc
if container.cmd == nil {
if err := container.waitLxc(); err != nil {
utils.Debugf("%s: Process: %s", container.Id, err)
utils.Debugf("%s: Process: %s", container.ID, err)
}
} else {
if err := container.cmd.Wait(); err != nil {
// 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")
var exitCode int = -1
exitCode := -1
if container.cmd != nil {
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
}
@ -635,24 +635,24 @@ func (container *Container) monitor() {
container.releaseNetwork()
if container.Config.OpenStdin {
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 {
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 {
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 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 {
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
@ -673,7 +673,7 @@ func (container *Container) monitor() {
// This is because State.setStopped() has already been called, and has caused Wait()
// to return.
// 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
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 {
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
if err := container.WaitTimeout(10 * time.Second); err != 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 {
return err
}
@ -721,7 +721,7 @@ func (container *Container) Stop(seconds int) error {
}
// 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("Failed to send SIGTERM to the process, force killing")
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
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 {
return err
}
@ -795,7 +795,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
case <-done:
return nil
}
panic("unreachable")
panic("Unreachable")
}
func (container *Container) EnsureMounted() error {
@ -838,16 +839,16 @@ func (container *Container) Unmount() error {
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.
// 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.
func (container *Container) ShortId() string {
return utils.TruncateId(container.Id)
func (container *Container) ShortID() string {
return utils.TruncateID(container.ID)
}
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) {
@ -887,7 +888,7 @@ func (container *Container) rwPath() string {
return path.Join(container.root, "rw")
}
func validateId(id string) error {
func validateID(id string) error {
if id == "" {
return fmt.Errorf("Invalid empty id")
}

View file

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

View file

@ -11,13 +11,13 @@ import (
"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
func runDaemon() (*exec.Cmd, error) {
os.Remove("/var/run/docker.pid")
exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
cmd := exec.Command(DOCKER_PATH, "-d")
cmd := exec.Command(DOCKERPATH, "-d")
outPipe, err := cmd.StdoutPipe()
if err != nil {
return nil, err
@ -77,7 +77,7 @@ func crashTest() error {
stop = false
for i := 0; i < 100 && !stop; {
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++
totalTestCount++
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 (
GIT_COMMIT string
GITCOMMIT string
)
func main() {
@ -59,7 +59,7 @@ func main() {
if *flDebug {
os.Setenv("DEBUG", "1")
}
docker.GIT_COMMIT = GIT_COMMIT
docker.GITCOMMIT = GITCOMMIT
if *flDaemon {
if flag.NArg() != 0 {
flag.Usage()

View file

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

View file

@ -564,7 +564,7 @@ Create an image
Content-Type: application/json
{"status":"Pulling..."}
{"progress":"1/? (n/a)"}
{"status":"Pulling", "progress":"1/? (n/a)"}
{"error":"Invalid..."}
...
@ -607,7 +607,7 @@ Insert a file in a image
Content-Type: application/json
{"status":"Inserting..."}
{"progress":"1/? (n/a)"}
{"status":"Inserting", "progress":"1/? (n/a)"}
{"error":"Invalid..."}
...
@ -734,7 +734,7 @@ Push an image on the registry
Content-Type: application/json
{"status":"Pushing..."}
{"progress":"1/? (n/a)"}
{"status":"Pushing", "progress":"1/? (n/a)"}
{"error":"Invalid..."}
...
@ -974,10 +974,12 @@ Display system-wide information
{
"Containers":11,
"Version":"0.2.2",
"Images":16,
"GoVersion":"go1.0.3",
"Debug":false
"Debug":false,
"NFd": 11,
"NGoroutines":21,
"MemoryLimit":true,
"SwapLimit":false
}
:statuscode 200: no error
@ -1003,12 +1005,11 @@ Show the docker version information
HTTP/1.1 200 OK
Content-Type: application/json
{
"Version":"0.2.2",
"GitCommit":"5a2a5cc+CHANGES",
"MemoryLimit":true,
"SwapLimit":false
"GoVersion":"go1.0.3"
}
:statuscode 200: no error

View file

@ -5,5 +5,5 @@
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
============================
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
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 PATH=$GOPATH/bin:$PATH

View file

@ -19,7 +19,8 @@ Most frequently asked questions.
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?**
@ -34,15 +35,16 @@ Most frequently asked questions.
You can find more answers on:
* `IRC: docker on freenode`_
* `Docker club mailinglist`_
* `IRC, docker on freenode`_
* `Github`_
* `Ask questions on Stackoverflow`_
* `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
.. _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
.. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
.. _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 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
------------

View file

@ -2,6 +2,7 @@
:description: Docker's tutorial to run docker on Windows
:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
.. _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
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,
before finally outputting the ID of your new image.

View file

@ -64,14 +64,15 @@
<div style="float: right" class="pull-right">
<ul class="nav">
<li><a href="http://www.docker.io/">Introduction</a></li>
<li><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-introduction"><a href="http://www.docker.io/">Introduction</a></li>
<li id="nav-gettingstarted"><a href="http://www.docker.io/gettingstarted/">Getting started</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>
<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 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 style="margin-left: -12px; float: left;">
@ -86,8 +87,13 @@
<div class="container">
<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>-->
<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>
@ -123,8 +129,14 @@
<div class="row">
<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>
{# {%- if show_source and has_source and sourcename %}#}
{# ·#}
{# <a href="{{ pathto('_sources/' + sourcename, true)|e }}"#}
@ -157,7 +169,7 @@
<!-- script which should be loaded after everything else -->
<script type="text/javascript">
// Function to make the sticky header possible
var shiftWindow = function() {
scrollBy(0, -70);
console.log("window shifted")

View file

@ -285,6 +285,40 @@ section.header {
.social .github {
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
======================= */
@ -325,10 +359,15 @@ section.header {
padding-top: 600px;
}
#fork-us {
display: none;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
#nav-gettingstarted {
display: none;
}
}
/* Misc fixes */
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
======================= */
@ -441,14 +473,17 @@ section.header {
/* TODO: Fix this to be relative to the navigation size */
padding-top: 600px;
}
#fork-us {
display: none;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
#nav-gettingstarted {
display: none;
}
}
/* Misc fixes */

View file

@ -34,15 +34,11 @@
<div style="float: right" class="pull-right">
<ul class="nav">
<li><a href="../">Introduction</a></li>
<li class="active"><a href="">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-introduction"><a href="../">Introduction</a></li>
<li id="nav-gettingstarted" class="active"><a href="">Getting started</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>
<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 style="margin-left: -12px; float: left;">
@ -55,14 +51,22 @@
<div class="container">
<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 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.
</div>
<div class="row">
@ -133,13 +137,13 @@
</section>
<section class="contentblock">
<h2>More resources</h2>
<ul>
<li><a href="irc://chat.freenode.net#docker">IRC: docker on freenode</a></li>
<li><a href="http://www.github.com/dotcloud/docker">Github</a></li>
<li><a href="http://stackoverflow.com/tags/docker/">Ask questions on Stackoverflow</a></li>
<li><a href="http://twitter.com/getdocker/">Join the conversation on Twitter</a></li>
</ul>
<h2>Questions? Want to get in touch?</h2>
<p>There are several ways to get in touch:</p>
<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>
<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>
<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>
<p><strong>Get help on using Docker</strong> by asking on <a href="http://stackoverflow.com/tags/docker/">Stackoverflow</a></p>
<p>And of course, <strong>tweet</strong> your tweets to <a href="http://twitter.com/getdocker/">twitter.com/getdocker</a></p>
</section>
@ -172,7 +176,10 @@
<footer id="footer" class="footer">
<div class="row">
<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>
</div>

View file

@ -44,9 +44,18 @@
.debug {
border: 1px red dotted;
}
.twitterblock {
min-height: 75px;
}
.twitterblock img {
float: left;
margin-right: 10px;
}
</style>
</head>
@ -56,17 +65,18 @@
<div class="navbar-dotcloud">
<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" >
<ul class="nav">
<li class="active"><a href="/">Introduction</a></li>
<li ><a href="gettingstarted">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-introduction" class="active"><a href="/">Introduction</a></li>
<li id="nav-gettingstarted"><a href="gettingstarted">Getting started</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>
<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>
@ -81,7 +91,7 @@
<div class="span5" style="margin-bottom: 15px;">
<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>
</div>
@ -130,7 +140,7 @@
<section class="contentblock">
<div class="container">
<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 class="span4" style="margin-left: 0">
<h4>Do you think it is cool to hack on docker? Join us!</h4>
@ -156,7 +166,7 @@
</div>
</a>
&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')" />
</section>
@ -184,32 +194,19 @@
</div>
<style>
.twitterblock {
min-height: 75px;
}
.twitterblock img {
float: left;
margin-right: 10px;
}
</style>
<div class="container">
<div class="row">
<div class="span6">
<section class="contentblock twitterblock">
<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>
</div>
<div class="span6">
<section class="contentblock twitterblock">
<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>
</div>
</div>
@ -217,13 +214,13 @@
<div class="span6">
<section class="contentblock twitterblock">
<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>
</div>
<div class="span6">
<section class="contentblock twitterblock">
<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>
</div>
</div>
@ -317,7 +314,10 @@
<footer id="footer" class="footer">
<div class="row">
<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>
</div>

View file

@ -86,14 +86,14 @@ func (graph *Graph) Get(name string) (*Image, error) {
if err != nil {
return nil, err
}
if img.Id != id {
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.Id)
if img.ID != id {
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
}
img.graph = graph
graph.lockSumMap.Lock()
defer graph.lockSumMap.Unlock()
if _, exists := graph.checksumLock[img.Id]; !exists {
graph.checksumLock[img.Id] = &sync.Mutex{}
if _, exists := graph.checksumLock[img.ID]; !exists {
graph.checksumLock[img.ID] = &sync.Mutex{}
}
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.
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
img := &Image{
Id: GenerateId(),
ID: GenerateID(),
Comment: comment,
Created: time.Now(),
DockerVersion: VERSION,
@ -111,7 +111,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
}
if container != nil {
img.Parent = container.Image
img.Container = container.Id
img.Container = container.ID
img.ContainerConfig = *container.Config
}
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.
// FIXME: pass img as first argument
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
}
// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
if graph.Exists(img.Id) {
return fmt.Errorf("Image %s already exists", img.Id)
if graph.Exists(img.ID) {
return fmt.Errorf("Image %s already exists", img.ID)
}
tmp, err := graph.Mktemp("")
defer os.RemoveAll(tmp)
@ -140,12 +140,12 @@ func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
return err
}
// 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
}
img.graph = graph
graph.idIndex.Add(img.Id)
graph.checksumLock[img.Id] = &sync.Mutex{}
graph.idIndex.Add(img.ID)
graph.checksumLock[img.ID] = &sync.Mutex{}
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.
func (graph *Graph) Mktemp(id string) (string, error) {
if id == "" {
id = GenerateId()
id = GenerateID()
}
tmp, err := graph.tmp()
if err != nil {
@ -230,7 +230,7 @@ func (graph *Graph) Map() (map[string]*Image, error) {
}
images := make(map[string]*Image, len(all))
for _, image := range all {
images[image.Id] = image
images[image.ID] = image
}
return images, nil
}
@ -273,10 +273,10 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) {
if err != nil {
return
}
if children, exists := byParent[parent.Id]; exists {
byParent[parent.Id] = []*Image{image}
if children, exists := byParent[parent.ID]; exists {
byParent[parent.ID] = []*Image{image}
} else {
byParent[parent.Id] = append(children, image)
byParent[parent.ID] = append(children, image)
}
})
return byParent, err
@ -293,8 +293,8 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
err = graph.WalkAll(func(image *Image) {
// If it's not in the byParent lookup table, then
// it's not a parent -> so it's a head!
if _, exists := byParent[image.Id]; !exists {
heads[image.Id] = image
if _, exists := byParent[image.ID]; !exists {
heads[image.ID] = image
}
})
return heads, err
@ -317,11 +317,11 @@ func (graph *Graph) getStoredChecksums() (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 {
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 nil

View file

@ -34,14 +34,14 @@ func TestInterruptedRegister(t *testing.T) {
defer os.RemoveAll(graph.Root)
badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
image := &Image{
Id: GenerateId(),
ID: GenerateID(),
Comment: "testing",
Created: time.Now(),
}
go graph.Register(badArchive, false, image)
time.Sleep(200 * time.Millisecond)
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")
}
// 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 {
t.Fatal(err)
}
if err := ValidateId(image.Id); err != nil {
if err := ValidateID(image.ID); err != nil {
t.Fatal(err)
}
if image.Comment != "Testing" {
@ -91,7 +91,7 @@ func TestRegister(t *testing.T) {
t.Fatal(err)
}
image := &Image{
Id: GenerateId(),
ID: GenerateID(),
Comment: "testing",
Created: time.Now(),
}
@ -104,11 +104,11 @@ func TestRegister(t *testing.T) {
} else if l := len(images); l != 1 {
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)
} else {
if resultImg.Id != image.Id {
t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.Id, resultImg.Id)
if resultImg.ID != image.ID {
t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
}
if resultImg.Comment != image.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)
defer os.RemoveAll(graph.Root)
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)
}
assertNImages(graph, t, 0)
@ -187,7 +187,7 @@ func TestDelete(t *testing.T) {
t.Fatal(err)
}
assertNImages(graph, t, 1)
if err := graph.Delete(img.Id); err != nil {
if err := graph.Delete(img.ID); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 0)
@ -201,7 +201,7 @@ func TestDelete(t *testing.T) {
t.Fatal(err)
}
assertNImages(graph, t, 2)
if err := graph.Delete(img1.Id); err != nil {
if err := graph.Delete(img1.ID); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 1)
@ -216,7 +216,7 @@ func TestDelete(t *testing.T) {
if err := graph.Register(archive, false, img1); err != nil {
t.Fatal(err)
}
if err := graph.Delete(img1.Id); err != nil {
if err := graph.Delete(img1.ID); err != nil {
t.Fatal(err)
}
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 {
Id string `json:"id"`
ID string `json:"id"`
Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"`
@ -42,18 +42,17 @@ func LoadImage(root string) (*Image, error) {
if err := json.Unmarshal(jsonData, img); err != nil {
return nil, err
}
if err := ValidateId(img.Id); err != nil {
if err := ValidateID(img.ID); err != nil {
return nil, err
}
// Check that the filesystem layer exists
if stat, err := os.Stat(layerPath(root)); err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
} else {
return nil, err
return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
}
return nil, err
} 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
}
@ -61,7 +60,7 @@ func LoadImage(root string) (*Image, error) {
func StoreImage(img *Image, layerData Archive, root string, store bool) error {
// Check that root doesn't already exist
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) {
return err
}
@ -181,11 +180,11 @@ func (image *Image) Changes(rw string) ([]Change, error) {
return Changes(layers, rw)
}
func (image *Image) ShortId() string {
return utils.TruncateId(image.Id)
func (image *Image) ShortID() string {
return utils.TruncateID(image.ID)
}
func ValidateId(id string) error {
func ValidateID(id string) error {
if id == "" {
return fmt.Errorf("Image id can't be empty")
}
@ -195,7 +194,7 @@ func ValidateId(id string) error {
return nil
}
func GenerateId() string {
func GenerateID() string {
id := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, id)
if err != nil {
@ -241,7 +240,7 @@ func (img *Image) layers() ([]string, error) {
return nil, e
}
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
}
@ -276,7 +275,7 @@ func (img *Image) root() (string, error) {
if img.graph == nil {
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
@ -289,8 +288,8 @@ func (img *Image) layer() (string, error) {
}
func (img *Image) Checksum() (string, error) {
img.graph.checksumLock[img.Id].Lock()
defer img.graph.checksumLock[img.Id].Unlock()
img.graph.checksumLock[img.ID].Lock()
defer img.graph.checksumLock[img.ID].Unlock()
root, err := img.root()
if err != nil {
@ -301,7 +300,7 @@ func (img *Image) Checksum() (string, error) {
if err != nil {
return "", err
}
if checksum, ok := checksums[img.Id]; ok {
if checksum, ok := checksums[img.ID]; ok {
return checksum, nil
}
@ -352,7 +351,7 @@ func (img *Image) Checksum() (string, error) {
return "", err
}
checksums[img.Id] = hash
checksums[img.ID] = hash
// Dump the checksums to disc
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
func NewImgJson(src []byte) (*Image, error) {
func NewImgJSON(src []byte) (*Image, error) {
ret := &Image{}
utils.Debugf("Json string: {%s}\n", src)

View file

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

View file

@ -52,7 +52,7 @@ func ipToInt(ip net.IP) int32 {
}
// 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)
binary.BigEndian.PutUint32(b, uint32(n))
return net.IP(b)
@ -132,9 +132,8 @@ func CreateBridgeIface(ifaceName string) error {
}
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)
} 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 {
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")
splice(src, dst)
}
return nil
panic("Unreachable")
}
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 {
ip.err = errors.New("No unallocated IP available")
}
@ -465,11 +464,11 @@ func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
return nil, err
}
// 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
} 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 {
iface.manager.portAllocator.Release(nat.Frontend)
return nil, err

View file

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

View file

@ -15,7 +15,7 @@ import (
"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) {
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)
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) {
@ -103,8 +107,8 @@ func (r *Registry) getImagesInRepository(repository string, authConfig *auth.Aut
// Retrieve an image from the Registry.
// 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) {
// Get the Json
func (r *Registry) GetRemoteImageJSON(imgId, registry string, token []string) ([]byte, error) {
// Get the JSON
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
if err != nil {
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, ", "))
res, err := r.client.Do(req)
defer res.Body.Close()
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
} else if res.StatusCode == 404 {
return nil, fmt.Errorf("Repository not found")
}
result := make(map[string]string)
rawJson, err := ioutil.ReadAll(res.Body)
rawJSON, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
if err := json.Unmarshal(rawJson, &result); err != nil {
if err := json.Unmarshal(rawJSON, &result); err != nil {
return nil, err
}
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")
}
checksumsJson, err := ioutil.ReadAll(res.Body)
checksumsJSON, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
remoteChecksums := []*ImgData{}
if err := json.Unmarshal(checksumsJson, &remoteChecksums); err != nil {
if err := json.Unmarshal(checksumsJSON, &remoteChecksums); err != nil {
return nil, err
}
// Forge a better object from the retrieved data
imgsData := make(map[string]*ImgData)
for _, elem := range remoteChecksums {
imgsData[elem.Id] = elem
imgsData[elem.ID] = elem
}
return &RepositoryData{
@ -235,10 +242,10 @@ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
}
// 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"
// 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 {
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("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)
if err != nil {
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
}
func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
imgListJson, err := json.Marshal(imgList)
func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool) (*RepositoryData, error) {
imgListJSON, err := json.Marshal(imgList)
if err != nil {
return nil, err
}
@ -331,14 +338,14 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
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 {
return nil, err
}
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")
res, err := r.client.Do(req)
@ -350,12 +357,12 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
// Redirect if necessary
for res.StatusCode >= 300 && res.StatusCode < 400 {
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 {
return nil, err
}
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")
res, err = r.client.Do(req)
@ -389,11 +396,11 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat
}
if validate {
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
} 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 {
Id string `json:"id"`
ID string `json:"id"`
Checksum string `json:"checksum,omitempty"`
Tag string `json:",omitempty"`
}
@ -470,9 +477,16 @@ func NewRegistry(root string) *Registry {
// If the auth file does not exist, keep going
authConfig, _ := auth.LoadConfig(root)
httpTransport := &http.Transport{
DisableKeepAlives: true,
Proxy: http.ProxyFromEnvironment,
}
r := &Registry{
authConfig: authConfig,
client: &http.Client{},
client: &http.Client{
Transport: httpTransport,
},
}
r.client.Jar = cookiejar.NewCookieJar()
return r

View file

@ -51,7 +51,7 @@ func (runtime *Runtime) List() []*Container {
func (runtime *Runtime) getContainerElement(id string) *list.Element {
for e := runtime.containers.Front(); e != nil; e = e.Next() {
container := e.Value.(*Container)
if container.Id == id {
if container.ID == id {
return e
}
}
@ -83,8 +83,8 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
if err := container.FromDisk(); err != nil {
return nil, err
}
if container.Id != id {
return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
if container.ID != id {
return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
}
if container.State.Running {
container.State.Ghost = true
@ -95,12 +95,12 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
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 {
if container.runtime != nil || runtime.Exists(container.Id) {
if container.runtime != nil || runtime.Exists(container.ID) {
return fmt.Errorf("Container is already loaded")
}
if err := validateId(container.Id); err != nil {
if err := validateID(container.ID); err != nil {
return err
}
@ -123,7 +123,7 @@ func (runtime *Runtime) Register(container *Container) error {
}
// done
runtime.containers.PushBack(container)
runtime.idIndex.Add(container.Id)
runtime.idIndex.Add(container.ID)
// When we actually restart, Start() do the monitoring.
// 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 the container is supposed to be running, make sure of it
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
} else {
if !strings.Contains(string(output), "RUNNING") {
utils.Debugf("Container %s was supposed to be running be is not.", container.Id)
if runtime.autoRestart {
utils.Debugf("Restarting")
container.State.Ghost = false
container.State.setStopped(0)
if err := container.Start(); err != nil {
return err
}
nomonitor = true
} else {
utils.Debugf("Marking as stopped")
container.State.setStopped(-127)
if err := container.ToDisk(); err != nil {
return err
}
}
if !strings.Contains(string(output), "RUNNING") {
utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
if runtime.autoRestart {
utils.Debugf("Restarting")
container.State.Ghost = false
container.State.setStopped(0)
if err := container.Start(); err != nil {
return err
}
nomonitor = true
} else {
utils.Debugf("Marking as stopped")
container.State.setStopped(-127)
if err := container.ToDisk(); err != nil {
return err
}
}
}
@ -182,9 +182,9 @@ func (runtime *Runtime) Destroy(container *Container) error {
return fmt.Errorf("The given container is <nil>")
}
element := runtime.getContainerElement(container.Id)
element := runtime.getContainerElement(container.ID)
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 {
@ -194,14 +194,14 @@ func (runtime *Runtime) Destroy(container *Container) error {
return err
} else if mounted {
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
runtime.idIndex.Delete(container.Id)
runtime.idIndex.Delete(container.ID)
runtime.containers.Remove(element)
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
}
@ -218,7 +218,7 @@ func (runtime *Runtime) restore() error {
utils.Debugf("Failed to load container %v: %v", id, err)
continue
}
utils.Debugf("Loaded container %v", container.Id)
utils.Debugf("Loaded container %v", container.ID)
}
return nil
}

View file

@ -120,7 +120,7 @@ func TestRuntimeCreate(t *testing.T) {
builder := NewBuilder(runtime)
container, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
)
@ -140,29 +140,29 @@ func TestRuntimeCreate(t *testing.T) {
}
// 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])
}
// 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")
}
// 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")
}
// 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")
}
// Make sure crete with bad parameters returns an error
_, err = builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
},
)
if err == nil {
@ -171,7 +171,7 @@ func TestRuntimeCreate(t *testing.T) {
_, err = builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{},
},
)
@ -187,7 +187,7 @@ func TestDestroy(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
)
@ -210,7 +210,7 @@ func TestDestroy(t *testing.T) {
}
// 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")
}
@ -237,7 +237,7 @@ func TestGet(t *testing.T) {
builder := NewBuilder(runtime)
container1, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
)
@ -247,7 +247,7 @@ func TestGet(t *testing.T) {
defer runtime.Destroy(container1)
container2, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
)
@ -257,7 +257,7 @@ func TestGet(t *testing.T) {
defer runtime.Destroy(container2)
container3, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
)
@ -266,16 +266,16 @@ func TestGet(t *testing.T) {
}
defer runtime.Destroy(container3)
if runtime.Get(container1.Id) != container1 {
t.Errorf("Get(test1) returned %v while expecting %v", 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)
}
if runtime.Get(container2.Id) != container2 {
t.Errorf("Get(test2) returned %v while expecting %v", 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)
}
if runtime.Get(container3.Id) != container3 {
t.Errorf("Get(test3) returned %v while expecting %v", 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)
}
}
@ -283,7 +283,7 @@ func TestGet(t *testing.T) {
func findAvailalblePort(runtime *Runtime, port int) (*Container, error) {
strPort := strconv.Itoa(port)
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},
PortSpecs: []string{strPort},
},
@ -379,7 +379,7 @@ func TestRestore(t *testing.T) {
// Create a container with one instance of docker
container1, err := builder.Create(&Config{
Image: GetTestImage(runtime1).Id,
Image: GetTestImage(runtime1).ID,
Cmd: []string{"ls", "-al"},
},
)
@ -390,7 +390,7 @@ func TestRestore(t *testing.T) {
// Create a second container meant to be killed
container2, err := builder.Create(&Config{
Image: GetTestImage(runtime1).Id,
Image: GetTestImage(runtime1).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@ -406,7 +406,7 @@ func TestRestore(t *testing.T) {
}
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'
@ -426,7 +426,7 @@ func TestRestore(t *testing.T) {
}
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
@ -442,14 +442,14 @@ func TestRestore(t *testing.T) {
runningCount := 0
for _, c := range runtime2.List() {
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++
}
}
if runningCount != 0 {
t.Fatalf("Expected 0 container alive, %d found", runningCount)
}
container3 := runtime2.Get(container1.Id)
container3 := runtime2.Get(container1.ID)
if container3 == nil {
t.Fatal("Unable to Get container")
}

188
server.go
View file

@ -17,8 +17,12 @@ import (
"strings"
)
func (srv *Server) DockerVersion() ApiVersion {
return ApiVersion{VERSION, GIT_COMMIT, srv.runtime.capabilities.MemoryLimit, srv.runtime.capabilities.SwapLimit}
func (srv *Server) DockerVersion() APIVersion {
return APIVersion{
Version: VERSION,
GitCommit: GITCOMMIT,
GoVersion: runtime.Version(),
}
}
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)
}
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)
if err != nil {
return nil, err
}
var outs []ApiSearch
var outs []APISearch
for _, repo := range results.Results {
var out ApiSearch
var out APISearch
out.Description = repo["description"]
if len(out.Description) > 45 {
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()
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 {
return "", err
}
@ -101,8 +105,8 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
if err != nil {
return "", err
}
out.Write(sf.FormatStatus(img.Id))
return img.ShortId(), nil
out.Write(sf.FormatStatus(img.ID))
return img.ShortID(), nil
}
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)
}
if parentImage != nil {
out.Write([]byte(" \"" + parentImage.ShortId() + "\" -> \"" + image.ShortId() + "\"\n"))
out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n"))
} 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 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
}
func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
var (
allImages map[string]*Image
err error
@ -156,13 +160,13 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
if err != nil {
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 {
if filter != "" && name != filter {
continue
}
for tag, id := range repository {
var out ApiImages
var out APIImages
image, err := srv.runtime.graph.Get(id)
if err != nil {
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)
out.Repository = name
out.Tag = tag
out.Id = image.Id
out.ID = image.ID
out.Created = image.Created.Unix()
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
if filter == "" {
for _, image := range allImages {
var out ApiImages
out.Id = image.Id
var out APIImages
out.ID = image.ID
out.Created = image.Created.Unix()
outs = append(outs, out)
}
@ -188,7 +192,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
return outs, nil
}
func (srv *Server) DockerInfo() ApiInfo {
func (srv *Server) DockerInfo() *APIInfo {
images, _ := srv.runtime.graph.All()
var imgcount int
if images == nil {
@ -196,29 +200,27 @@ func (srv *Server) DockerInfo() ApiInfo {
} else {
imgcount = len(images)
}
var out ApiInfo
out.Containers = len(srv.runtime.List())
out.Version = VERSION
out.Images = imgcount
out.GoVersion = runtime.Version()
if os.Getenv("DEBUG") != "" {
out.Debug = true
out.NFd = utils.GetTotalUsedFds()
out.NGoroutines = runtime.NumGoroutine()
return &APIInfo{
Containers: len(srv.runtime.List()),
Images: imgcount,
MemoryLimit: srv.runtime.capabilities.MemoryLimit,
SwapLimit: srv.runtime.capabilities.SwapLimit,
Debug: os.Getenv("DEBUG") != "",
NFd: utils.GetTotalUsedFds(),
NGoroutines: runtime.NumGoroutine(),
}
return out
}
func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
image, err := srv.runtime.repositories.LookupImage(name)
if err != nil {
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 {
var out ApiHistory
out.Id = srv.runtime.repositories.ImageName(img.ShortId())
var out APIHistory
out.ID = srv.runtime.repositories.ImageName(img.ShortID())
out.Created = img.Created.Unix()
out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
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)
}
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 displayed int
retContainers := []ApiContainers{}
retContainers := []APIContainers{}
for _, container := range srv.runtime.List() {
if !container.State.Running && !all && n == -1 && since == "" && before == "" {
continue
}
if before != "" {
if container.ShortId() == before {
if container.ShortID() == before {
foundBefore = true
continue
}
@ -256,13 +258,13 @@ func (srv *Server) Containers(all bool, n int, since, before string) []ApiContai
if displayed == n {
break
}
if container.ShortId() == since {
if container.ShortID() == since {
break
}
displayed++
c := ApiContainers{
Id: container.Id,
c := APIContainers{
ID: container.ID,
}
c.Image = srv.runtime.repositories.ImageName(container.Image)
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 {
return "", err
}
return img.ShortId(), err
return img.ShortID(), err
}
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 {
if !srv.runtime.graph.Exists(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 {
// FIXME: Keep goging in case of error?
return err
}
img, err := NewImgJson(imgJson)
img, err := NewImgJSON(imgJSON)
if err != nil {
return fmt.Errorf("Failed to parse json: %s", err)
}
// Get the layer
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 {
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 {
return err
}
@ -328,8 +331,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin
return nil
}
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error {
out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress()))
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", local, auth.IndexServerAddress()))
repoData, err := r.GetRepositoryData(remote)
if err != nil {
return err
@ -354,22 +357,22 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
}
} else {
// Otherwise, check that the tag exists and use only that one
if id, exists := tagsList[askedTag]; !exists {
return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote)
} else {
repoData.ImgList[id].Tag = askedTag
id, exists := tagsList[askedTag]
if !exists {
return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, local)
}
repoData.ImgList[id].Tag = askedTag
}
for _, img := range repoData.ImgList {
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
}
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
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))
continue
}
@ -384,7 +387,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a
if askedTag != "" && tag != askedTag {
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
}
}
@ -404,8 +407,12 @@ func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *util
}
return nil
}
if err := srv.pullRepository(r, out, name, tag, sf); err != nil {
remote := name
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
}
@ -459,16 +466,16 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat
return nil, err
}
img.WalkHistory(func(img *Image) error {
if _, exists := imageSet[img.Id]; exists {
if _, exists := imageSet[img.ID]; exists {
return nil
}
imageSet[img.Id] = struct{}{}
checksum, err := srv.getChecksum(img.Id)
imageSet[img.ID] = struct{}{}
checksum, err := srv.getChecksum(img.ID)
if err != nil {
return err
}
imgList = append([]*registry.ImgData{{
Id: img.Id,
ID: img.ID,
Checksum: checksum,
Tag: tag,
}}, imgList...)
@ -487,7 +494,13 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
}
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 {
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)))
// For each image within the repo, push them
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))
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?
return err
}
out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.Id, ep+"/users/"+name+"/"+elem.Tag))
if err := r.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil {
out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+srvName+"/"+elem.Tag))
if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
return err
}
}
}
if _, err := r.PushImageJsonIndex(name, imgList, true); err != nil {
if _, err := r.PushImageJSONIndex(srvName, imgList, true); err != nil {
return err
}
return nil
@ -531,14 +544,14 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
return err
}
imgData := &registry.ImgData{
Id: imgId,
ID: imgId,
Checksum: checksum,
}
// 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 {
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 err
@ -571,7 +584,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
}
// Send the layer
if err := r.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, 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 nil
@ -595,7 +608,7 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.Str
return err
}
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 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
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
}
}
out.Write(sf.FormatStatus(img.ShortId()))
out.Write(sf.FormatStatus(img.ShortID()))
return nil
}
@ -657,7 +670,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
}
return "", err
}
return container.ShortId(), nil
return container.ShortID(), nil
}
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 {
// If the requested volu
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
}
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")
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 len(srv.runtime.repositories.ById()[id]) != 0 {
if len(srv.runtime.repositories.ByID()[id]) != 0 {
return ErrImageReferenced
}
@ -723,12 +736,11 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]ApiRmi) error {
return err
}
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 {
return err
} else {
referenced = true
}
referenced = true
}
}
if referenced {
@ -748,13 +760,13 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]ApiRmi) error {
if err != nil {
return err
}
*imgs = append(*imgs, ApiRmi{Deleted: utils.TruncateId(id)})
*imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)})
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 != "" {
parent, err := srv.runtime.graph.Get(img.Parent)
if err != nil {
@ -769,18 +781,18 @@ func (srv *Server) deleteImageParents(img *Image, imgs *[]ApiRmi) error {
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
var imgs []ApiRmi
var imgs []APIRmi
tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
if err != nil {
return nil, err
}
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 err := srv.deleteImageAndChildren(img.Id, &imgs); err != nil {
if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil {
if err != ErrImageReferenced {
return &imgs, err
}
@ -793,13 +805,13 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) (*[]ApiRmi, err
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)
if err != nil {
return nil, fmt.Errorf("No such image: %s", name)
}
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, nil
@ -829,7 +841,7 @@ func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error)
if _, exists := imageMap[img.Parent]; !exists {
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

View file

@ -65,7 +65,7 @@ func TestCreateRm(t *testing.T) {
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 {
t.Fatal(err)
}
@ -98,7 +98,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
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 {
t.Fatal(err)
}

28
tags.go
View file

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

View file

@ -7,104 +7,6 @@ import (
"unsafe"
)
type Termios struct {
Iflag uintptr
Oflag uintptr
Cflag uintptr
Lflag uintptr
Cc [20]byte
Ispeed uintptr
Ospeed uintptr
}
const (
// Input flags
inpck = 0x010
istrip = 0x020
icrnl = 0x100
ixon = 0x200
// Output flags
opost = 0x1
// Control flags
cs8 = 0x300
// Local flags
icanon = 0x100
iexten = 0x400
)
const (
HUPCL = 0x4000
ICANON = 0x100
ICRNL = 0x100
IEXTEN = 0x400
BRKINT = 0x2
CFLUSH = 0xf
CLOCAL = 0x8000
CREAD = 0x800
CS5 = 0x0
CS6 = 0x100
CS7 = 0x200
CS8 = 0x300
CSIZE = 0x300
CSTART = 0x11
CSTATUS = 0x14
CSTOP = 0x13
CSTOPB = 0x400
CSUSP = 0x1a
IGNBRK = 0x1
IGNCR = 0x80
IGNPAR = 0x4
IMAXBEL = 0x2000
INLCR = 0x40
INPCK = 0x10
ISIG = 0x80
ISTRIP = 0x20
IUTF8 = 0x4000
IXANY = 0x800
IXOFF = 0x400
IXON = 0x200
NOFLSH = 0x80000000
OCRNL = 0x10
OFDEL = 0x20000
OFILL = 0x80
ONLCR = 0x2
ONLRET = 0x40
ONOCR = 0x20
ONOEOT = 0x8
OPOST = 0x1
RENB = 0x1000
PARMRK = 0x8
PARODD = 0x2000
TOSTOP = 0x400000
VDISCARD = 0xf
VDSUSP = 0xb
VEOF = 0x0
VEOL = 0x1
VEOL2 = 0x2
VERASE = 0x3
VINTR = 0x8
VKILL = 0x5
VLNEXT = 0xe
VMIN = 0x10
VQUIT = 0x9
VREPRINT = 0x6
VSTART = 0xc
VSTATUS = 0x12
VSTOP = 0xd
VSUSP = 0xa
VT0 = 0x0
VT1 = 0x10000
VTDLY = 0x10000
VTIME = 0x11
ECHO = 0x00000008
PENDIN = 0x20000000
)
type State struct {
termios Termios
}
@ -128,21 +30,21 @@ func SetWinsize(fd uintptr, ws *Winsize) error {
}
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
func IsTerminal(fd uintptr) bool {
var termios Termios
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
return err == 0
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
func Restore(fd uintptr, state *State) error {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
return err
}
func SetRawTerminal() (*State, error) {
oldState, err := MakeRaw(int(os.Stdin.Fd()))
oldState, err := MakeRaw(os.Stdin.Fd())
if err != nil {
return nil, err
}
@ -150,12 +52,12 @@ func SetRawTerminal() (*State, error) {
signal.Notify(c, os.Interrupt)
go func() {
_ = <-c
Restore(int(os.Stdin.Fd()), oldState)
Restore(os.Stdin.Fd(), oldState)
os.Exit(0)
}()
return oldState, err
}
func RestoreTerminal(state *State) {
Restore(int(os.Stdin.Fd()), state)
Restore(os.Stdin.Fd(), state)
}

View file

@ -8,23 +8,45 @@ import (
const (
getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA
ECHO = 0x00000008
ONLCR = 0x2
ISTRIP = 0x20
INLCR = 0x40
ISIG = 0x80
IGNCR = 0x80
ICANON = 0x100
ICRNL = 0x100
IXOFF = 0x400
IXON = 0x200
)
type Termios struct {
Iflag uint64
Oflag uint64
Cflag uint64
Lflag uint64
Cc [20]byte
Ispeed uint64
Ospeed uint64
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
func MakeRaw(fd uintptr) (*State, error) {
var oldState State
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
newState.Iflag &^= (ISTRIP | INLCR | IGNCR | IXON | IXOFF)
newState.Iflag |= ICRNL
newState.Oflag |= ONLCR
newState.Lflag &^= ECHO | ICANON | ISIG
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
newState.Lflag &^= (ECHO | ICANON | ISIG)
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}

View file

@ -5,54 +5,40 @@ import (
"unsafe"
)
// #include <termios.h>
// #include <sys/ioctl.h>
/*
void MakeRaw(int fd) {
struct termios t;
// FIXME: Handle errors?
ioctl(fd, TCGETS, &t);
t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
t.c_oflag &= ~OPOST;
t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
t.c_cflag &= ~(CSIZE | PARENB);
t.c_cflag |= CS8;
ioctl(fd, TCSETS, &t);
}
*/
import "C"
const (
getTermios = syscall.TCGETS
setTermios = syscall.TCSETS
)
type Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]byte
Ispeed uint32
Ospeed uint32
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
func MakeRaw(fd uintptr) (*State, error) {
var oldState State
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TCGETS, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
newState.Oflag &^= syscall.OPOST
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
newState.Cflag |= syscall.CS8
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
C.MakeRaw(C.int(fd))
return &oldState, nil
// FIXME: post on goland issues this: very same as the C function bug non-working
// newState := oldState.termios
// newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
// newState.Oflag &^= OPOST
// newState.Lflag &^= (ECHO | syscall.ECHONL | ICANON | ISIG | IEXTEN)
// newState.Cflag &^= (CSIZE | syscall.PARENB)
// newState.Cflag |= CS8
// if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TCSETS, uintptr(unsafe.Pointer(&newState))); err != 0 {
// return nil, err
// }
// return &oldState, nil
}

View file

@ -34,7 +34,7 @@ func Go(f func() error) chan error {
// Request a given URL and return an io.Reader
func Download(url string, stderr io.Writer) (*http.Response, error) {
var resp *http.Response
var err error = nil
var err error
if resp, err = http.Get(url); err != nil {
return nil, err
}
@ -349,11 +349,11 @@ func (idx *TruncIndex) Get(s string) (string, error) {
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.
// 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.
func TruncateId(id string) string {
func TruncateID(id string) string {
shortLen := 12
if len(id) < shortLen {
shortLen = len(id)
@ -566,7 +566,7 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
return &WriteFlusher{w: w, flusher: flusher}
}
type JsonMessage struct {
type JSONMessage struct {
Status string `json:"status,omitempty"`
Progress string `json:"progress,omitempty"`
Error string `json:"error,omitempty"`
@ -585,7 +585,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte
sf.used = true
str := fmt.Sprintf(format, a...)
if sf.json {
b, err := json.Marshal(&JsonMessage{Status:str});
b, err := json.Marshal(&JSONMessage{Status:str});
if err != nil {
return sf.FormatError(err)
}
@ -597,7 +597,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte
func (sf *StreamFormatter) FormatError(err error) []byte {
sf.used = true
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 []byte("{\"error\":\"format error\"}")
@ -608,7 +608,7 @@ func (sf *StreamFormatter) FormatError(err error) []byte {
func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
sf.used = true
if sf.json {
b, err := json.Marshal(&JsonMessage{Progress:str})
b, err := json.Marshal(&JSONMessage{Status: action, Progress:str})
if err != nil {
return nil
}