Merge master
This commit is contained in:
commit
dce82bc856
73 changed files with 706 additions and 321 deletions
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
## 0.3.3 (2013-05-23)
|
||||
- Registry: Fix push regression
|
||||
- Various bugfixes
|
||||
|
||||
## 0.3.2 (2013-05-09)
|
||||
* Runtime: Store the actual archive on commit
|
||||
* Registry: Improve the checksum process
|
||||
|
|
|
@ -12,7 +12,7 @@ Docker is an open-source implementation of the deployment engine which powers [d
|
|||
It benefits directly from the experience accumulated over several years of large-scale operation and support of hundreds of thousands
|
||||
of applications and databases.
|
||||
|
||||
![Docker L](docs/sources/static_files/lego_docker.jpg "Docker")
|
||||
![Docker L](docs/sources/concepts/images/lego_docker.jpg "Docker")
|
||||
|
||||
## Better than VMs
|
||||
|
||||
|
|
114
api.go
114
api.go
|
@ -13,6 +13,8 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const API_VERSION = 1.1
|
||||
|
||||
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
|
@ -53,6 +55,7 @@ func writeJson(w http.ResponseWriter, b []byte) {
|
|||
w.Write(b)
|
||||
}
|
||||
|
||||
// FIXME: Use stvconv.ParseBool() instead?
|
||||
func getBoolParam(value string) (bool, error) {
|
||||
if value == "1" || strings.ToLower(value) == "true" {
|
||||
return true, nil
|
||||
|
@ -63,8 +66,8 @@ func getBoolParam(value string) (bool, error) {
|
|||
return false, fmt.Errorf("Bad parameter")
|
||||
}
|
||||
|
||||
func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
b, err := json.Marshal(srv.registry.GetAuthConfig())
|
||||
func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
b, err := json.Marshal(srv.registry.GetAuthConfig(false))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -72,14 +75,14 @@ func getAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
|
|||
return nil
|
||||
}
|
||||
|
||||
func postAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
config := &auth.AuthConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.Username == srv.registry.GetAuthConfig().Username {
|
||||
config.Password = srv.registry.GetAuthConfig().Password
|
||||
authConfig := srv.registry.GetAuthConfig(true)
|
||||
if config.Username == authConfig.Username {
|
||||
config.Password = authConfig.Password
|
||||
}
|
||||
|
||||
newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root)
|
||||
|
@ -101,7 +104,7 @@ func postAuth(srv *Server, w http.ResponseWriter, r *http.Request, vars map[stri
|
|||
return nil
|
||||
}
|
||||
|
||||
func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
m := srv.DockerVersion()
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
|
@ -111,7 +114,7 @@ func getVersion(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -123,7 +126,7 @@ func postContainersKill(srv *Server, w http.ResponseWriter, r *http.Request, var
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -136,7 +139,7 @@ func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, va
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesJson(srv *Server, 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
|
||||
}
|
||||
|
@ -159,14 +162,14 @@ func getImagesJson(srv *Server, w http.ResponseWriter, r *http.Request, vars map
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesViz(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := srv.ImagesViz(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
out := srv.DockerInfo()
|
||||
b, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
|
@ -176,7 +179,7 @@ func getInfo(srv *Server, w http.ResponseWriter, r *http.Request, vars map[strin
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -193,7 +196,7 @@ func getImagesHistory(srv *Server, w http.ResponseWriter, r *http.Request, vars
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -210,7 +213,7 @@ func getContainersChanges(srv *Server, w http.ResponseWriter, r *http.Request, v
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersPs(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -234,7 +237,7 @@ func getContainersPs(srv *Server, w http.ResponseWriter, r *http.Request, vars m
|
|||
return nil
|
||||
}
|
||||
|
||||
func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -256,7 +259,7 @@ func postImagesTag(srv *Server, w http.ResponseWriter, r *http.Request, vars map
|
|||
return nil
|
||||
}
|
||||
|
||||
func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -283,7 +286,7 @@ func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
|
|||
}
|
||||
|
||||
// Creates an image from Pull or from Import
|
||||
func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -295,7 +298,10 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
|
|||
|
||||
if image != "" { //pull
|
||||
registry := r.Form.Get("registry")
|
||||
if err := srv.ImagePull(image, tag, registry, w); err != nil {
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
|
||||
return err
|
||||
}
|
||||
} else { //import
|
||||
|
@ -306,7 +312,7 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -324,7 +330,7 @@ func getImagesSearch(srv *Server, w http.ResponseWriter, r *http.Request, vars m
|
|||
return nil
|
||||
}
|
||||
|
||||
func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -348,7 +354,7 @@ func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars
|
|||
return nil
|
||||
}
|
||||
|
||||
func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -365,7 +371,7 @@ func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars ma
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
config := &Config{}
|
||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
|
||||
return err
|
||||
|
@ -395,7 +401,7 @@ func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, v
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -414,7 +420,7 @@ func postContainersRestart(srv *Server, w http.ResponseWriter, r *http.Request,
|
|||
return nil
|
||||
}
|
||||
|
||||
func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -434,7 +440,7 @@ func deleteContainers(srv *Server, w http.ResponseWriter, r *http.Request, vars
|
|||
return nil
|
||||
}
|
||||
|
||||
func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -446,7 +452,7 @@ func deleteImages(srv *Server, w http.ResponseWriter, r *http.Request, vars map[
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -458,7 +464,7 @@ func postContainersStart(srv *Server, w http.ResponseWriter, r *http.Request, va
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -479,7 +485,7 @@ func postContainersStop(srv *Server, w http.ResponseWriter, r *http.Request, var
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -496,7 +502,29 @@ func postContainersWait(srv *Server, w http.ResponseWriter, r *http.Request, var
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
height, err := strconv.Atoi(r.Form.Get("h"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
width, err := strconv.Atoi(r.Form.Get("w"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
if err := srv.ContainerResize(name, height, width); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -539,7 +567,7 @@ func postContainersAttach(srv *Server, w http.ResponseWriter, r *http.Request, v
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -557,7 +585,7 @@ func getContainersByName(srv *Server, w http.ResponseWriter, r *http.Request, va
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -575,7 +603,7 @@ func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars m
|
|||
return nil
|
||||
}
|
||||
|
||||
func postImagesGetCache(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
apiConfig := &ApiImageConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
|
||||
return err
|
||||
|
@ -626,7 +654,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
|
|||
r := mux.NewRouter()
|
||||
log.Printf("Listening for HTTP on %s\n", addr)
|
||||
|
||||
m := map[string]map[string]func(*Server, http.ResponseWriter, *http.Request, map[string]string) error{
|
||||
m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
|
||||
"GET": {
|
||||
"/auth": getAuth,
|
||||
"/version": getVersion,
|
||||
|
@ -656,6 +684,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
|
|||
"/containers/{name:.*}/start": postContainersStart,
|
||||
"/containers/{name:.*}/stop": postContainersStop,
|
||||
"/containers/{name:.*}/wait": postContainersWait,
|
||||
"/containers/{name:.*}/resize": postContainersResize,
|
||||
"/containers/{name:.*}/attach": postContainersAttach,
|
||||
},
|
||||
"DELETE": {
|
||||
|
@ -671,7 +700,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
|
|||
localRoute := route
|
||||
localMethod := method
|
||||
localFct := fct
|
||||
r.Path(localRoute).Methods(localMethod).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
f := func(w http.ResponseWriter, r *http.Request) {
|
||||
utils.Debugf("Calling %s %s", localMethod, localRoute)
|
||||
if logging {
|
||||
log.Println(r.Method, r.RequestURI)
|
||||
|
@ -682,12 +711,21 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
|
|||
utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
|
||||
}
|
||||
}
|
||||
if err := localFct(srv, w, r, mux.Vars(r)); err != nil {
|
||||
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
|
||||
if err != nil {
|
||||
version = API_VERSION
|
||||
}
|
||||
if version == 0 || version > API_VERSION {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil {
|
||||
httpError(w, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
|
||||
r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
|
||||
}
|
||||
}
|
||||
|
||||
return http.ListenAndServe(addr, r)
|
||||
}
|
||||
|
|
54
api_test.go
54
api_test.go
|
@ -48,7 +48,7 @@ func TestGetAuth(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := postAuth(srv, r, req, nil); err != nil {
|
||||
if err := postAuth(srv, API_VERSION, r, req, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ func TestGetAuth(t *testing.T) {
|
|||
t.Fatalf("%d OK or 0 expected, received %d\n", http.StatusOK, r.Code)
|
||||
}
|
||||
|
||||
newAuthConfig := srv.registry.GetAuthConfig()
|
||||
newAuthConfig := srv.registry.GetAuthConfig(false)
|
||||
if newAuthConfig.Username != authConfig.Username ||
|
||||
newAuthConfig.Email != authConfig.Email {
|
||||
t.Fatalf("The auth configuration hasn't been set correctly")
|
||||
|
@ -74,7 +74,7 @@ func TestGetVersion(t *testing.T) {
|
|||
|
||||
r := httptest.NewRecorder()
|
||||
|
||||
if err := getVersion(srv, r, nil, nil); err != nil {
|
||||
if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ func TestGetInfo(t *testing.T) {
|
|||
|
||||
r := httptest.NewRecorder()
|
||||
|
||||
if err := getInfo(srv, r, nil, nil); err != nil {
|
||||
if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ func TestGetImagesJson(t *testing.T) {
|
|||
|
||||
r := httptest.NewRecorder()
|
||||
|
||||
if err := getImagesJson(srv, r, req, nil); err != nil {
|
||||
if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ func TestGetImagesJson(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := getImagesJson(srv, r2, req2, nil); err != nil {
|
||||
if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ func TestGetImagesJson(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := getImagesJson(srv, r3, req3, nil); err != nil {
|
||||
if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -200,7 +200,7 @@ func TestGetImagesJson(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = getImagesJson(srv, r4, req4, nil)
|
||||
err = getImagesJson(srv, API_VERSION, r4, req4, nil)
|
||||
if err == nil {
|
||||
t.Fatalf("Error expected, received none")
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ func TestGetImagesViz(t *testing.T) {
|
|||
srv := &Server{runtime: runtime}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := getImagesViz(srv, r, nil, nil); err != nil {
|
||||
if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,7 @@ func TestGetImagesSearch(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := getImagesSearch(srv, r, req, nil); err != nil {
|
||||
if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -282,7 +282,7 @@ func TestGetImagesHistory(t *testing.T) {
|
|||
|
||||
r := httptest.NewRecorder()
|
||||
|
||||
if err := getImagesHistory(srv, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
||||
if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ func TestGetImagesByName(t *testing.T) {
|
|||
srv := &Server{runtime: runtime}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := getImagesByName(srv, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
||||
if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -342,7 +342,7 @@ func TestGetContainersPs(t *testing.T) {
|
|||
}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := getContainersPs(srv, r, req, nil); err != nil {
|
||||
if err := getContainersPs(srv, API_VERSION, r, req, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containers := []ApiContainers{}
|
||||
|
@ -385,7 +385,7 @@ func TestGetContainersExport(t *testing.T) {
|
|||
}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err = getContainersExport(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -440,7 +440,7 @@ func TestGetContainersChanges(t *testing.T) {
|
|||
}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := getContainersChanges(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
changes := []Change{}
|
||||
|
@ -484,7 +484,7 @@ func TestGetContainersByName(t *testing.T) {
|
|||
defer runtime.Destroy(container)
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := getContainersByName(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outContainer := &Container{}
|
||||
|
@ -515,7 +515,7 @@ func TestPostAuth(t *testing.T) {
|
|||
srv.registry.ResetClient(authConfigOrig)
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := getAuth(srv, r, nil, nil); err != nil {
|
||||
if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -562,7 +562,7 @@ func TestPostCommit(t *testing.T) {
|
|||
}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := postCommit(srv, r, req, nil); err != nil {
|
||||
if err := postCommit(srv, API_VERSION, r, req, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Code != http.StatusCreated {
|
||||
|
@ -840,7 +840,7 @@ func TestPostContainersCreate(t *testing.T) {
|
|||
}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := postContainersCreate(srv, r, req, nil); err != nil {
|
||||
if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Code != http.StatusCreated {
|
||||
|
@ -903,7 +903,7 @@ func TestPostContainersKill(t *testing.T) {
|
|||
}
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := postContainersKill(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := postContainersKill(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Code != http.StatusNoContent {
|
||||
|
@ -951,7 +951,7 @@ func TestPostContainersRestart(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
r := httptest.NewRecorder()
|
||||
if err := postContainersRestart(srv, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := postContainersRestart(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Code != http.StatusNoContent {
|
||||
|
@ -992,7 +992,7 @@ func TestPostContainersStart(t *testing.T) {
|
|||
defer runtime.Destroy(container)
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := postContainersStart(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Code != http.StatusNoContent {
|
||||
|
@ -1007,7 +1007,7 @@ func TestPostContainersStart(t *testing.T) {
|
|||
}
|
||||
|
||||
r = httptest.NewRecorder()
|
||||
if err = postContainersStart(srv, r, nil, map[string]string{"name": container.Id}); err == nil {
|
||||
if err = postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err == nil {
|
||||
t.Fatalf("A running containter should be able to be started")
|
||||
}
|
||||
|
||||
|
@ -1054,7 +1054,7 @@ func TestPostContainersStop(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
r := httptest.NewRecorder()
|
||||
if err := postContainersStop(srv, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := postContainersStop(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Code != http.StatusNoContent {
|
||||
|
@ -1092,7 +1092,7 @@ func TestPostContainersWait(t *testing.T) {
|
|||
|
||||
setTimeout(t, "Wait timed out", 3*time.Second, func() {
|
||||
r := httptest.NewRecorder()
|
||||
if err := postContainersWait(srv, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
apiWait := &ApiWait{}
|
||||
|
@ -1154,7 +1154,7 @@ func TestPostContainersAttach(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := postContainersAttach(srv, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
@ -1224,7 +1224,7 @@ func TestDeleteContainers(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
r := httptest.NewRecorder()
|
||||
if err := deleteContainers(srv, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
if err := deleteContainers(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Code != http.StatusNoContent {
|
||||
|
|
|
@ -26,12 +26,6 @@ type builderClient struct {
|
|||
}
|
||||
|
||||
func (b *builderClient) clearTmp(containers, images map[string]struct{}) {
|
||||
for c := range containers {
|
||||
if _, _, err := b.cli.call("DELETE", "/containers/"+c, nil); err != nil {
|
||||
utils.Debugf("%s", err)
|
||||
}
|
||||
utils.Debugf("Removing container %s", c)
|
||||
}
|
||||
for i := range images {
|
||||
if _, _, err := b.cli.call("DELETE", "/images/"+i, nil); err != nil {
|
||||
utils.Debugf("%s", err)
|
||||
|
|
162
commands.go
162
commands.go
|
@ -16,30 +16,36 @@ import (
|
|||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const VERSION = "0.3.2"
|
||||
const VERSION = "0.3.3"
|
||||
|
||||
var (
|
||||
GIT_COMMIT string
|
||||
)
|
||||
|
||||
func ParseCommands(args ...string) error {
|
||||
cli := NewDockerCli("0.0.0.0", 4243)
|
||||
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
|
||||
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
||||
return reflect.TypeOf(cli).MethodByName(methodName)
|
||||
}
|
||||
|
||||
func ParseCommands(addr string, port int, args ...string) error {
|
||||
cli := NewDockerCli(addr, port)
|
||||
|
||||
if len(args) > 0 {
|
||||
methodName := "Cmd" + strings.ToUpper(args[0][:1]) + strings.ToLower(args[0][1:])
|
||||
method, exists := reflect.TypeOf(cli).MethodByName(methodName)
|
||||
method, exists := cli.getMethod(args[0])
|
||||
if !exists {
|
||||
fmt.Println("Error: Command not found:", args[0])
|
||||
return cli.CmdHelp(args...)
|
||||
return cli.CmdHelp(args[1:]...)
|
||||
}
|
||||
ret := method.Func.CallSlice([]reflect.Value{
|
||||
reflect.ValueOf(cli),
|
||||
|
@ -54,7 +60,19 @@ func ParseCommands(args ...string) error {
|
|||
}
|
||||
|
||||
func (cli *DockerCli) CmdHelp(args ...string) error {
|
||||
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
|
||||
if len(args) > 0 {
|
||||
method, exists := cli.getMethod(args[0])
|
||||
if !exists {
|
||||
fmt.Println("Error: Command not found:", args[0])
|
||||
} else {
|
||||
method.Func.CallSlice([]reflect.Value{
|
||||
reflect.ValueOf(cli),
|
||||
reflect.ValueOf([]string{"--help"}),
|
||||
})[0].Interface()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=\"%s:%d\": Host:port to bind/connect to\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", cli.host, cli.port)
|
||||
for cmd, description := range map[string]string{
|
||||
"attach": "Attach to a running container",
|
||||
"build": "Build a container from a Dockerfile",
|
||||
|
@ -660,39 +678,13 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
body, _, err := cli.call("GET", "/auth", nil)
|
||||
username, err := cli.checkIfLogged(*registry == "", "push")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var out auth.AuthConfig
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the login failed AND we're using the index, abort
|
||||
if *registry == "" && out.Username == "" {
|
||||
if err := cli.CmdLogin(args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, _, err = cli.call("GET", "/auth", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if out.Username == "" {
|
||||
return fmt.Errorf("Please login prior to push. ('docker login')")
|
||||
}
|
||||
}
|
||||
|
||||
if len(strings.SplitN(name, "/", 2)) == 1 {
|
||||
return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", out.Username, name)
|
||||
return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
|
@ -723,6 +715,12 @@ 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)
|
||||
|
@ -1013,6 +1011,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
v.Set("stderr", "1")
|
||||
v.Set("stdin", "1")
|
||||
|
||||
cli.monitorTtySize(cmd.Arg(0))
|
||||
if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1200,6 +1199,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
}
|
||||
|
||||
if config.AttachStdin || config.AttachStdout || config.AttachStderr {
|
||||
cli.monitorTtySize(out.Id)
|
||||
if err := cli.hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1210,6 +1210,40 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) checkIfLogged(condition bool, action string) (string, error) {
|
||||
body, _, err := cli.call("GET", "/auth", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var out auth.AuthConfig
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If condition AND the login failed
|
||||
if condition && out.Username == "" {
|
||||
if err := cli.CmdLogin(""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, _, err = cli.call("GET", "/auth", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if out.Username == "" {
|
||||
return "", fmt.Errorf("Please login prior to %s. ('docker login')", action)
|
||||
}
|
||||
}
|
||||
return out.Username, nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) {
|
||||
var params io.Reader
|
||||
if data != nil {
|
||||
|
@ -1220,7 +1254,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", cli.host, cli.port)+path, params)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), params)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
@ -1252,7 +1286,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%s", cli.host, cli.port, path), in)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, API_VERSION, path), in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1276,14 +1310,35 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
|
|||
return fmt.Errorf("error: %s", body)
|
||||
}
|
||||
|
||||
if resp.Header.Get("Content-Type") == "application/json" {
|
||||
type Message struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Progress string `json:"progress,omitempty"`
|
||||
}
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
for {
|
||||
var m Message
|
||||
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)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n", m.Status)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err := io.Copy(out, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
|
||||
req, err := http.NewRequest(method, path, nil)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", API_VERSION, path), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1333,6 +1388,33 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
|
|||
|
||||
}
|
||||
|
||||
func (cli *DockerCli) resizeTty(id string) {
|
||||
ws, err := term.GetWinsize(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
utils.Debugf("Error getting size: %s", err)
|
||||
}
|
||||
v := url.Values{}
|
||||
v.Set("h", strconv.Itoa(int(ws.Height)))
|
||||
v.Set("w", strconv.Itoa(int(ws.Width)))
|
||||
if _, _, err := cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil); err != nil {
|
||||
utils.Debugf("Error resize: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) monitorTtySize(id string) {
|
||||
cli.resizeTty(id)
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGWINCH)
|
||||
go func() {
|
||||
for sig := range c {
|
||||
if sig == syscall.SIGWINCH {
|
||||
cli.resizeTty(id)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func Subcmd(name, signature, description string) *flag.FlagSet {
|
||||
flags := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||
flags.Usage = func() {
|
||||
|
@ -1342,8 +1424,8 @@ func Subcmd(name, signature, description string) *flag.FlagSet {
|
|||
return flags
|
||||
}
|
||||
|
||||
func NewDockerCli(host string, port int) *DockerCli {
|
||||
return &DockerCli{host, port}
|
||||
func NewDockerCli(addr string, port int) *DockerCli {
|
||||
return &DockerCli{addr, port}
|
||||
}
|
||||
|
||||
type DockerCli struct {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/term"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/kr/pty"
|
||||
"io"
|
||||
|
@ -754,6 +755,14 @@ func (container *Container) Wait() int {
|
|||
return container.State.ExitCode
|
||||
}
|
||||
|
||||
func (container *Container) Resize(h, w int) error {
|
||||
pty, ok := container.ptyMaster.(*os.File)
|
||||
if !ok {
|
||||
return fmt.Errorf("ptyMaster does not have Fd() method")
|
||||
}
|
||||
return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||
}
|
||||
|
||||
func (container *Container) ExportRw() (Archive, error) {
|
||||
return Tar(container.rwPath(), Uncompressed)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
|
@ -23,18 +24,38 @@ func main() {
|
|||
docker.SysInit()
|
||||
return
|
||||
}
|
||||
host := "127.0.0.1"
|
||||
port := 4243
|
||||
// FIXME: Switch d and D ? (to be more sshd like)
|
||||
flDaemon := flag.Bool("d", false, "Daemon mode")
|
||||
flDebug := flag.Bool("D", false, "Debug mode")
|
||||
flAutoRestart := flag.Bool("r", false, "Restart previously running containers")
|
||||
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
|
||||
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
||||
flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to")
|
||||
flag.Parse()
|
||||
if *bridgeName != "" {
|
||||
docker.NetworkBridgeIface = *bridgeName
|
||||
} else {
|
||||
docker.NetworkBridgeIface = docker.DefaultNetworkBridge
|
||||
}
|
||||
|
||||
if strings.Contains(*flHost, ":") {
|
||||
hostParts := strings.Split(*flHost, ":")
|
||||
if len(hostParts) != 2 {
|
||||
log.Fatal("Invalid bind address format.")
|
||||
os.Exit(-1)
|
||||
}
|
||||
if hostParts[0] != "" {
|
||||
host = hostParts[0]
|
||||
}
|
||||
if p, err := strconv.Atoi(hostParts[1]); err == nil {
|
||||
port = p
|
||||
}
|
||||
} else {
|
||||
host = *flHost
|
||||
}
|
||||
|
||||
if *flDebug {
|
||||
os.Setenv("DEBUG", "1")
|
||||
}
|
||||
|
@ -44,12 +65,12 @@ func main() {
|
|||
flag.Usage()
|
||||
return
|
||||
}
|
||||
if err := daemon(*pidfile, *flAutoRestart); err != nil {
|
||||
if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
} else {
|
||||
if err := docker.ParseCommands(flag.Args()...); err != nil {
|
||||
if err := docker.ParseCommands(host, port, flag.Args()...); err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
@ -83,7 +104,10 @@ func removePidFile(pidfile string) {
|
|||
}
|
||||
}
|
||||
|
||||
func daemon(pidfile string, autoRestart bool) error {
|
||||
func daemon(pidfile, addr string, port int, autoRestart bool) error {
|
||||
if addr != "127.0.0.1" {
|
||||
log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
||||
}
|
||||
if err := createPidFile(pidfile); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -103,5 +127,5 @@ func daemon(pidfile string, autoRestart bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return docker.ListenAndServe("0.0.0.0:4243", server, true)
|
||||
return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Remote API
|
||||
:description: API Documentation for Docker
|
||||
:keywords: API, Docker, rcli, REST, documentation
|
||||
|
||||
=================
|
||||
Docker Remote API
|
||||
=================
|
||||
|
@ -118,6 +122,7 @@ Create a container
|
|||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"Id":"e90e34656806"
|
||||
|
@ -373,7 +378,7 @@ Attach to a container
|
|||
|
||||
.. http:post:: /containers/(id)/attach
|
||||
|
||||
Stop the container ``id``
|
||||
Attach to the container ``id``
|
||||
|
||||
**Example request**:
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: docker documentation
|
||||
:title: API Documentation
|
||||
:description: docker documentation
|
||||
:keywords:
|
||||
:keywords: docker, ipa, documentation
|
||||
|
||||
API's
|
||||
=============
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:title: docker Registry documentation
|
||||
:title: Registry Documentation
|
||||
:description: Documentation for docker Registry and Registry API
|
||||
:keywords: docker, registry, api, index
|
||||
|
||||
|
@ -301,7 +301,7 @@ POST /v1/users
|
|||
{"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'}
|
||||
|
||||
**Validation**:
|
||||
- **username** : min 4 character, max 30 characters, all lowercase no special characters.
|
||||
- **username** : min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
|
||||
- **password**: min 5 characters
|
||||
|
||||
**Valid**: return HTTP 200
|
||||
|
@ -345,6 +345,11 @@ GET /v1/users
|
|||
|
||||
The Registry does not know anything about users. Even though repositories are under usernames, it’s just a namespace for the registry. Allowing us to implement organizations or different namespaces per user later, without modifying the Registry’s API.
|
||||
|
||||
The following naming restrictions apply:
|
||||
|
||||
- Namespaces must match the same regular expression as usernames (See 4.2.1.)
|
||||
- Repository names must match the regular expression [a-zA-Z0-9-_.]
|
||||
|
||||
4.3.1 Get all tags
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ To list available commands, either run ``docker`` with no parameters or execute
|
|||
``docker help``::
|
||||
|
||||
$ docker
|
||||
Usage: docker COMMAND [arg...]
|
||||
Usage: docker [OPTIONS] COMMAND [arg...]
|
||||
-H="127.0.0.1:4243": Host:port to bind/connect to
|
||||
|
||||
A self-sufficient runtime for linux containers.
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Attach Command
|
||||
:description: Attach to a running container
|
||||
:keywords: attach, container, docker, documentation
|
||||
|
||||
===========================================
|
||||
``attach`` -- Attach to a running container
|
||||
===========================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Build Command
|
||||
:description: Build a new image from the Dockerfile passed via stdin
|
||||
:keywords: build, docker, container, documentation
|
||||
|
||||
========================================================
|
||||
``build`` -- Build a container from Dockerfile via stdin
|
||||
========================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Commit Command
|
||||
:description: Create a new image from a container's changes
|
||||
:keywords: commit, docker, container, documentation
|
||||
|
||||
===========================================================
|
||||
``commit`` -- Create a new image from a container's changes
|
||||
===========================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Diff Command
|
||||
:description: Inspect changes on a container's filesystem
|
||||
:keywords: diff, docker, container, documentation
|
||||
|
||||
=======================================================
|
||||
``diff`` -- Inspect changes on a container's filesystem
|
||||
=======================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Export Command
|
||||
:description: Export the contents of a filesystem as a tar archive
|
||||
:keywords: export, docker, container, documentation
|
||||
|
||||
=================================================================
|
||||
``export`` -- Stream the contents of a container as a tar archive
|
||||
=================================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: History Command
|
||||
:description: Show the history of an image
|
||||
:keywords: history, docker, container, documentation
|
||||
|
||||
===========================================
|
||||
``history`` -- Show the history of an image
|
||||
===========================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Images Command
|
||||
:description: List images
|
||||
:keywords: images, docker, container, documentation
|
||||
|
||||
=========================
|
||||
``images`` -- List images
|
||||
=========================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Import Command
|
||||
:description: Create a new filesystem image from the contents of a tarball
|
||||
:keywords: import, tarball, docker, url, documentation
|
||||
|
||||
==========================================================================
|
||||
``import`` -- Create a new filesystem image from the contents of a tarball
|
||||
==========================================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Info Command
|
||||
:description: Display system-wide information.
|
||||
:keywords: info, docker, information, documentation
|
||||
|
||||
===========================================
|
||||
``info`` -- Display system-wide information
|
||||
===========================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Inspect Command
|
||||
:description: Return low-level information on a container
|
||||
:keywords: inspect, container, docker, documentation
|
||||
|
||||
==========================================================
|
||||
``inspect`` -- Return low-level information on a container
|
||||
==========================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Kill Command
|
||||
:description: Kill a running container
|
||||
:keywords: kill, container, docker, documentation
|
||||
|
||||
====================================
|
||||
``kill`` -- Kill a running container
|
||||
====================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Login Command
|
||||
:description: Register or Login to the docker registry server
|
||||
:keywords: login, docker, documentation
|
||||
|
||||
============================================================
|
||||
``login`` -- Register or Login to the docker registry server
|
||||
============================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Logs Command
|
||||
:description: Fetch the logs of a container
|
||||
:keywords: logs, container, docker, documentation
|
||||
|
||||
=========================================
|
||||
``logs`` -- Fetch the logs of a container
|
||||
=========================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Port Command
|
||||
:description: Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
|
||||
:keywords: port, docker, container, documentation
|
||||
|
||||
=========================================================================
|
||||
``port`` -- Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
|
||||
=========================================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Ps Command
|
||||
:description: List containers
|
||||
:keywords: ps, docker, documentation, container
|
||||
|
||||
=========================
|
||||
``ps`` -- List containers
|
||||
=========================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Pull Command
|
||||
:description: Pull an image or a repository from the registry
|
||||
:keywords: pull, image, repo, repository, documentation, docker
|
||||
|
||||
=========================================================================
|
||||
``pull`` -- Pull an image or a repository from the docker registry server
|
||||
=========================================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Push Command
|
||||
:description: Push an image or a repository to the registry
|
||||
:keywords: push, docker, image, repository, documentation, repo
|
||||
|
||||
=======================================================================
|
||||
``push`` -- Push an image or a repository to the docker registry server
|
||||
=======================================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Restart Command
|
||||
:description: Restart a running container
|
||||
:keywords: restart, container, docker, documentation
|
||||
|
||||
==========================================
|
||||
``restart`` -- Restart a running container
|
||||
==========================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Rm Command
|
||||
:description: Remove a container
|
||||
:keywords: remove, container, docker, documentation, rm
|
||||
|
||||
============================
|
||||
``rm`` -- Remove a container
|
||||
============================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Rmi Command
|
||||
:description: Remove an image
|
||||
:keywords: rmi, remove, image, docker, documentation
|
||||
|
||||
==========================
|
||||
``rmi`` -- Remove an image
|
||||
==========================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Run Command
|
||||
:description: Run a command in a new container
|
||||
:keywords: run, container, docker, documentation
|
||||
|
||||
===========================================
|
||||
``run`` -- Run a command in a new container
|
||||
===========================================
|
||||
|
@ -19,5 +23,5 @@
|
|||
-t=false: Allocate a pseudo-tty
|
||||
-u="": Username or UID
|
||||
-d=[]: Set custom dns servers for the container
|
||||
-v=[]: Creates a new volumes and mount it at the specified path.
|
||||
-v=[]: Creates a new volume and mounts it at the specified path.
|
||||
-volumes-from="": Mount all volumes from the given container.
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Search Command
|
||||
:description: Searches for the TERM parameter on the Docker index and prints out a list of repositories that match.
|
||||
:keywords: search, docker, image, documentation
|
||||
|
||||
===================================================================
|
||||
``search`` -- Search for an image in the docker index
|
||||
===================================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Start Command
|
||||
:description: Start a stopped container
|
||||
:keywords: start, docker, container, documentation
|
||||
|
||||
======================================
|
||||
``start`` -- Start a stopped container
|
||||
======================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Stop Command
|
||||
:description: Stop a running container
|
||||
:keywords: stop, container, docker, documentation
|
||||
|
||||
====================================
|
||||
``stop`` -- Stop a running container
|
||||
====================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Tag Command
|
||||
:description: Tag an image into a repository
|
||||
:keywords: tag, docker, image, repository, documentation, repo
|
||||
|
||||
=========================================
|
||||
``tag`` -- Tag an image into a repository
|
||||
=========================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Version Command
|
||||
:description:
|
||||
:keywords: version, docker, documentation
|
||||
|
||||
==================================================
|
||||
``version`` -- Show the docker version information
|
||||
==================================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Wait Command
|
||||
:description: Block until a container stops, then print its exit code.
|
||||
:keywords: wait, docker, container, documentation
|
||||
|
||||
===================================================================
|
||||
``wait`` -- Block until a container stops, then print its exit code
|
||||
===================================================================
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: docker documentation
|
||||
:title: Commands
|
||||
:description: -- todo: change me
|
||||
:keywords: todo: change me
|
||||
:keywords: todo, commands, command line, help, docker, documentation
|
||||
|
||||
|
||||
Commands
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:title: Building blocks
|
||||
:title: Building Blocks
|
||||
:description: An introduction to docker and standard containers?
|
||||
:keywords: containers, lxc, concepts, explanation
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: Introduction
|
||||
:description: An introduction to docker and standard containers?
|
||||
:keywords: containers, lxc, concepts, explanation
|
||||
:keywords: containers, lxc, concepts, explanation, docker, documentation
|
||||
|
||||
|
||||
:note: This version of the introduction is temporary, just to make sure we don't break the links from the website when the documentation is updated
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: docker documentation
|
||||
:title: Concepts
|
||||
:description: -- todo: change me
|
||||
:keywords: todo: change me
|
||||
:keywords: concepts, documentation, docker, containers
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Contribution Guidelines
|
||||
:description: Contribution guidelines: create issues, convetions, pull requests
|
||||
:keywords: contributing, docker, documentation, help, guideline
|
||||
|
||||
Contributing to Docker
|
||||
======================
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ We attach to the new container to see what is going on. Ctrl-C to disconnect
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
BUILD_IMG=$(docker commit $BUILD_JOB _/builds/github.com/hykes/helloflask/master)
|
||||
BUILD_IMG=$(docker commit $BUILD_JOB _/builds/github.com/shykes/helloflask/master)
|
||||
|
||||
Save the changed we just made in the container to a new image called "_/builds/github.com/hykes/helloflask/master" and save the image id in the BUILD_IMG variable name.
|
||||
|
||||
|
@ -58,7 +58,7 @@ Use the new image we just created and create a new container with network port 5
|
|||
.. code-block:: bash
|
||||
|
||||
docker logs $WEB_WORKER
|
||||
* Running on \http://0.0.0.0:5000/
|
||||
* Running on http://0.0.0.0:5000/
|
||||
|
||||
view the logs for the new container using the WEB_WORKER variable, and if everything worked as planned you should see the line "Running on http://0.0.0.0:5000/" in the log output.
|
||||
|
||||
|
@ -70,7 +70,8 @@ lookup the public-facing port which is NAT-ed store the private port used by the
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
curl \http://`hostname`:$WEB_PORT
|
||||
# install curl if necessary, then ...
|
||||
curl http://127.0.0.1:$WEB_PORT
|
||||
Hello world!
|
||||
|
||||
access the web app using curl. If everything worked as planned you should see the line "Hello world!" inside of your console.
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: FAQ
|
||||
:description: Most frequently asked questions.
|
||||
:keywords: faq, questions, documentation, docker
|
||||
|
||||
FAQ
|
||||
===
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Index Environment Variable
|
||||
:description: Setting this environment variable on the docker server will change the URL docker index.
|
||||
:keywords: docker, index environment variable, documentation
|
||||
|
||||
=================================
|
||||
Docker Index Environment Variable
|
||||
=================================
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Installation on Amazon EC2
|
||||
:description: Docker installation on Amazon EC2 with a single vagrant command. Vagrant 1.1 or higher is required.
|
||||
:keywords: amazon ec2, virtualization, cloud, docker, documentation, installation
|
||||
|
||||
Amazon EC2
|
||||
==========
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Installation on Arch Linux
|
||||
:description: Docker installation on Arch Linux.
|
||||
:keywords: arch linux, virtualization, docker, documentation, installation
|
||||
|
||||
.. _arch_linux:
|
||||
|
||||
Arch Linux
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Installation from Binaries
|
||||
:description: This instruction set is meant for hackers who want to try out Docker on a variety of environments.
|
||||
:keywords: binaries, installation, docker, documentation, linux
|
||||
|
||||
.. _binaries:
|
||||
|
||||
Binaries
|
||||
|
@ -23,7 +27,7 @@ But we know people have had success running it under
|
|||
Dependencies:
|
||||
-------------
|
||||
|
||||
* 3.8 Kernel
|
||||
* 3.8 Kernel (read more about :ref:`kernel`)
|
||||
* AUFS filesystem support
|
||||
* lxc
|
||||
* bsdtar
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: docker documentation
|
||||
:title: Documentation
|
||||
:description: -- todo: change me
|
||||
:keywords: todo: change me
|
||||
:keywords: todo, docker, documentation, installation, OS support
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,63 +1,35 @@
|
|||
:title: Kernel Requirements
|
||||
:description: Kernel supports
|
||||
:keywords: kernel requirements, kernel support, docker, installation, cgroups, namespaces
|
||||
|
||||
.. _kernel:
|
||||
|
||||
Kernel Requirements
|
||||
===================
|
||||
|
||||
In short, Docker has the following kernel requirements:
|
||||
|
||||
- Linux version 3.8 or above.
|
||||
|
||||
- `AUFS support <http://aufs.sourceforge.net/>`_.
|
||||
|
||||
- Cgroups and namespaces must be enabled.
|
||||
|
||||
|
||||
The officially supported kernel is the one recommended by the
|
||||
:ref:`ubuntu_linux` installation path. It is the one that most developers
|
||||
will use, and the one that receives the most attention from the core
|
||||
contributors. If you decide to go with a different kernel and hit a bug,
|
||||
please try to reproduce it with the official kernels first.
|
||||
|
||||
If for some reason you cannot or do not want to use the "official" kernels,
|
||||
If you cannot or do not want to use the "official" kernels,
|
||||
here is some technical background about the features (both optional and
|
||||
mandatory) that docker needs to run successfully.
|
||||
|
||||
In short, you need kernel version 3.8 (or above), compiled to include
|
||||
`AUFS support <http://aufs.sourceforge.net/>`_. Of course, you need to
|
||||
enable cgroups and namespaces.
|
||||
Linux version 3.8 or above
|
||||
--------------------------
|
||||
|
||||
|
||||
Namespaces and Cgroups
|
||||
----------------------
|
||||
|
||||
You need to enable namespaces and cgroups, to the extend of what is needed
|
||||
to run LXC containers. Technically, while namespaces have been introduced
|
||||
in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
|
||||
to run LXC containers. Note that 2.6.32 has some documented issues regarding
|
||||
network namespace setup and teardown; those issues are not a risk if you
|
||||
run containers in a private environment, but can lead to denial-of-service
|
||||
attacks if you want to run untrusted code in your containers. For more details,
|
||||
see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
|
||||
|
||||
Kernels 2.6.38, and every version since 3.2, have been deployed successfully
|
||||
to run containerized production workloads. Feature-wise, there is no huge
|
||||
improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
|
||||
|
||||
Starting with version 3.7, the kernel has basic support for
|
||||
`Checkpoint/Restore In Userspace <http://criu.org/>`_, which is not used by
|
||||
docker at this point, but allows to suspend the state of a container to
|
||||
disk and resume it later.
|
||||
|
||||
Version 3.8 provides improvements in stability, which are deemed necessary
|
||||
for the operation of docker. Versions 3.2 to 3.5 have been shown to
|
||||
exhibit a reproducible bug (for more details, see issue
|
||||
`#407 <https://github.com/dotcloud/docker/issues/407>`_).
|
||||
|
||||
Version 3.8 also brings better support for the
|
||||
`setns() syscall <http://lwn.net/Articles/531381/>`_ -- but this should not
|
||||
be a concern since docker does not leverage on this feature for now.
|
||||
|
||||
If you want a technical overview about those concepts, you might
|
||||
want to check those articles on dotCloud's blog:
|
||||
`about namespaces <http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part>`_
|
||||
and `about cgroups <http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c>`_.
|
||||
|
||||
|
||||
Important Note About Pre-3.8 Kernels
|
||||
------------------------------------
|
||||
|
||||
As mentioned above, kernels before 3.8 are not stable when used with docker.
|
||||
Kernel versions 3.2 to 3.5 are not stable when used with docker.
|
||||
In some circumstances, you will experience kernel "oopses", or even crashes.
|
||||
The symptoms include:
|
||||
|
||||
|
@ -77,6 +49,36 @@ detects something older than 3.8.
|
|||
See issue `#407 <https://github.com/dotcloud/docker/issues/407>`_ for details.
|
||||
|
||||
|
||||
AUFS support
|
||||
------------
|
||||
|
||||
Docker currently relies on AUFS, an unioning filesystem.
|
||||
While AUFS is included in the kernels built by the Debian and Ubuntu
|
||||
distributions, is not part of the standard kernel. This means that if
|
||||
you decide to roll your own kernel, you will have to patch your
|
||||
kernel tree to add AUFS. The process is documented on
|
||||
`AUFS webpage <http://aufs.sourceforge.net/>`_.
|
||||
|
||||
|
||||
Cgroups and namespaces
|
||||
----------------------
|
||||
|
||||
You need to enable namespaces and cgroups, to the extend of what is needed
|
||||
to run LXC containers. Technically, while namespaces have been introduced
|
||||
in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32
|
||||
to run LXC containers. Note that 2.6.32 has some documented issues regarding
|
||||
network namespace setup and teardown; those issues are not a risk if you
|
||||
run containers in a private environment, but can lead to denial-of-service
|
||||
attacks if you want to run untrusted code in your containers. For more details,
|
||||
see `[LP#720095 <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/720095>`_.
|
||||
|
||||
Kernels 2.6.38, and every version since 3.2, have been deployed successfully
|
||||
to run containerized production workloads. Feature-wise, there is no huge
|
||||
improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!).
|
||||
|
||||
|
||||
|
||||
|
||||
Extra Cgroup Controllers
|
||||
------------------------
|
||||
|
||||
|
@ -111,39 +113,3 @@ And replace it by the following one::
|
|||
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount"
|
||||
|
||||
Then run ``update-grub``, and reboot.
|
||||
|
||||
|
||||
AUFS
|
||||
----
|
||||
|
||||
Docker currently relies on AUFS, an unioning filesystem.
|
||||
While AUFS is included in the kernels built by the Debian and Ubuntu
|
||||
distributions, is not part of the standard kernel. This means that if
|
||||
you decide to roll your own kernel, you will have to patch your
|
||||
kernel tree to add AUFS. The process is documented on
|
||||
`AUFS webpage <http://aufs.sourceforge.net/>`_.
|
||||
|
||||
Note: the AUFS patch is fairly intrusive, but for the record, people have
|
||||
successfully applied GRSEC and AUFS together, to obtain hardened production
|
||||
kernels.
|
||||
|
||||
If you want more information about that topic, there is an
|
||||
`article about AUFS on dotCloud's blog
|
||||
<http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-34-a>`_.
|
||||
|
||||
|
||||
BTRFS, ZFS, OverlayFS...
|
||||
------------------------
|
||||
|
||||
There is ongoing development on docker, to implement support for
|
||||
`BTRFS <http://en.wikipedia.org/wiki/Btrfs>`_
|
||||
(see github issue `#443 <https://github.com/dotcloud/docker/issues/443>`_).
|
||||
|
||||
People have also showed interest for `ZFS <http://en.wikipedia.org/wiki/ZFS>`_
|
||||
(using e.g. `ZFS-on-Linux <http://zfsonlinux.org/>`_) and OverlayFS.
|
||||
The latter is functionally close to AUFS, and it might end up being included
|
||||
in the stock kernel; so it's a strong candidate!
|
||||
|
||||
Would you like to `contribute
|
||||
<https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_
|
||||
support for your favorite filesystem?
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Rackspace Cloud Installation
|
||||
:description: Installing Docker on Ubuntu proviced by Rackspace
|
||||
:keywords: Rackspace Cloud, installation, docker, linux, ubuntu
|
||||
|
||||
===============
|
||||
Rackspace Cloud
|
||||
===============
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Requirements and Installation on Ubuntu Linux
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Docker, Docker documentation, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux
|
||||
|
||||
.. _ubuntu_linux:
|
||||
|
||||
Ubuntu Linux
|
||||
|
@ -12,7 +16,7 @@ Right now, the officially supported distribution are:
|
|||
|
||||
Docker has the following dependencies
|
||||
|
||||
* Linux kernel 3.8
|
||||
* Linux kernel 3.8 (read more about :ref:`kernel`)
|
||||
* AUFS file system support (we are working on BTRFS support as an alternative)
|
||||
|
||||
.. _ubuntu_precise:
|
||||
|
@ -34,7 +38,7 @@ Due to a bug in LXC docker works best on the 3.8 kernel. Precise comes with a 3.
|
|||
.. code-block:: bash
|
||||
|
||||
# install the backported kernel
|
||||
sudo apt-get update && sudo apt-get install linux-image-3.8.0-19-generic
|
||||
sudo apt-get update && sudo apt-get install linux-image-generic-lts-raring
|
||||
|
||||
# reboot
|
||||
sudo reboot
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Upgrading
|
||||
:description: These instructions are for upgrading Docker
|
||||
:keywords: Docker, Docker documentation, upgrading docker, upgrade
|
||||
|
||||
.. _upgrading:
|
||||
|
||||
Upgrading
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
:title: Using Vagrant (Mac, Linux)
|
||||
:description: This guide will setup a new virtualbox virtual machine with docker installed on your computer.
|
||||
:keywords: Docker, Docker documentation, virtualbox, vagrant, git, ssh, putty, cygwin
|
||||
|
||||
.. _install_using_vagrant:
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: docker documentation
|
||||
:description: docker documentation
|
||||
:keywords:
|
||||
:title: Documentation
|
||||
:description: -- todo: change me
|
||||
:keywords: todo, docker, documentation, installation, usage, examples, contributing, faq, command line, concepts
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: Base commands
|
||||
:title: Basic Commands
|
||||
:description: Common usage and commands
|
||||
:keywords: Examples, Usage
|
||||
:keywords: Examples, Usage, basic commands, docker, documentation, examples
|
||||
|
||||
|
||||
The basics
|
||||
|
@ -33,6 +33,19 @@ Running an interactive shell
|
|||
# allocate a tty, attach stdin and stdout
|
||||
docker run -i -t base /bin/bash
|
||||
|
||||
Bind Docker to another host/port
|
||||
--------------------------------
|
||||
|
||||
If you want Docker to listen to another port and bind to another ip
|
||||
use -host and -port on both deamon and client
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Run docker in daemon mode
|
||||
sudo <path to>/docker -H 0.0.0.0:5555 &
|
||||
# Download a base image
|
||||
docker -H :5555 pull base
|
||||
|
||||
|
||||
Starting a long-running worker process
|
||||
--------------------------------------
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Docker Builder
|
||||
:description: Docker Builder specifes a simple DSL which allows you to automate the steps you would normally manually take to create an image.
|
||||
:keywords: builder, docker, Docker Builder, automation, image creation
|
||||
|
||||
==============
|
||||
Docker Builder
|
||||
==============
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: docker documentation
|
||||
:title: Documentation
|
||||
:description: -- todo: change me
|
||||
:keywords: todo: change me
|
||||
:keywords: todo, docker, documentation, basic, builder
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
:title: Puppet Usage
|
||||
:description: Installating and using Puppet
|
||||
:keywords: puppet, installation, usage, docker, documentation
|
||||
|
||||
.. _install_using_puppet:
|
||||
|
||||
|
@ -22,7 +25,7 @@ Installation
|
|||
The module is available on the `Puppet Forge <https://forge.puppetlabs.com/garethr/docker/>`_
|
||||
and can be installed using the built-in module tool.
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
puppet module install garethr/docker
|
||||
|
||||
|
@ -38,7 +41,7 @@ for managing images and containers.
|
|||
Installation
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: ruby
|
||||
.. code-block:: ruby
|
||||
|
||||
include 'docker'
|
||||
|
||||
|
@ -48,13 +51,13 @@ Images
|
|||
The next step is probably to install a docker image, for this we have a
|
||||
defined type which can be used like so:
|
||||
|
||||
.. code-block:: ruby
|
||||
.. code-block:: ruby
|
||||
|
||||
docker::image { 'base': }
|
||||
|
||||
This is equivalent to running:
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
docker pull base
|
||||
|
||||
|
@ -63,7 +66,7 @@ This is downloading a large binary so on first run can take a while.
|
|||
For that reason this define turns off the default 5 minute timeout
|
||||
for exec. Note that you can also remove images you no longer need with:
|
||||
|
||||
.. code-block:: ruby
|
||||
.. code-block:: ruby
|
||||
|
||||
docker::image { 'base':
|
||||
ensure => 'absent',
|
||||
|
@ -75,7 +78,7 @@ Containers
|
|||
Now you have an image you can run commands within a container managed by
|
||||
docker.
|
||||
|
||||
.. code-block:: ruby
|
||||
.. code-block:: ruby
|
||||
|
||||
docker::run { 'helloworld':
|
||||
image => 'base',
|
||||
|
@ -84,13 +87,13 @@ docker.
|
|||
|
||||
This is equivalent to running the following command, but under upstart:
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -d base /bin/sh -c "while true; do echo hello world; sleep 1; done"
|
||||
|
||||
Run also contains a number of optional parameters:
|
||||
|
||||
.. code-block:: ruby
|
||||
.. code-block:: ruby
|
||||
|
||||
docker::run { 'helloworld':
|
||||
image => 'base',
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:title: Working With Repositories
|
||||
:description: Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind Docker, and user repositories.
|
||||
:keywords: repo, repositiores, usage, pull image, push image, image, documentation
|
||||
|
||||
.. _working_with_the_repository:
|
||||
|
||||
Working with the repository
|
||||
|
|
2
docs/theme/docker/layout.html
vendored
2
docs/theme/docker/layout.html
vendored
|
@ -8,7 +8,7 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="google-site-verification" content="UxV66EKuPe87dgnH1sbrldrx6VsoWMrx5NjwkgUFxXI" />
|
||||
|
||||
<title>Docker - {{ meta['title'] if meta and meta['title'] else title }}</title>
|
||||
<title>{{ meta['title'] if meta and meta['title'] else title }} - Docker Documentation</title>
|
||||
|
||||
<meta name="description" content="{{ meta['description'] if meta }}" />
|
||||
<meta name="keywords" content="{{ meta['keywords'] if meta }}" />
|
||||
|
|
|
@ -198,6 +198,35 @@
|
|||
|
||||
|
||||
<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. They’re 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.
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<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.
|
||||
</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.
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span6">
|
||||
<section class="contentblock twitterblock">
|
||||
|
|
2
graph.go
2
graph.go
|
@ -165,7 +165,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root)
|
||||
return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root)
|
||||
}
|
||||
|
||||
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
lxc-docker (0.3.3-1) precise; urgency=low
|
||||
- Registry: Fix push regression
|
||||
- Various bugfixes
|
||||
|
||||
-- dotCloud <ops@dotcloud.com> Thu, 23 May 2013 00:00:00 -0700
|
||||
|
||||
|
||||
lxc-docker (0.3.2-1) precise; urgency=low
|
||||
- Runtime: Store the actual archive on commit
|
||||
- Registry: Improve the checksum process
|
||||
|
|
|
@ -428,9 +428,14 @@ func (r *Registry) ResetClient(authConfig *auth.AuthConfig) {
|
|||
r.client.Jar = cookiejar.NewCookieJar()
|
||||
}
|
||||
|
||||
func (r *Registry) GetAuthConfig() *auth.AuthConfig {
|
||||
func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
|
||||
password := ""
|
||||
if withPasswd {
|
||||
password = r.authConfig.Password
|
||||
}
|
||||
return &auth.AuthConfig{
|
||||
Username: r.authConfig.Username,
|
||||
Password: password,
|
||||
Email: r.authConfig.Email,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,11 +68,13 @@ func init() {
|
|||
registry: registry.NewRegistry(runtime.root),
|
||||
}
|
||||
// Retrieve the Image
|
||||
if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout); err != nil {
|
||||
if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, false); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: test that ImagePull(json=true) send correct json output
|
||||
|
||||
func newTestRuntime() (*Runtime, error) {
|
||||
root, err := ioutil.TempDir("", "docker-test")
|
||||
if err != nil {
|
||||
|
|
42
server.go
42
server.go
|
@ -91,8 +91,8 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) (string, e
|
|||
return "", err
|
||||
}
|
||||
|
||||
if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil {
|
||||
return "", err
|
||||
if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil {
|
||||
return err
|
||||
}
|
||||
// FIXME: Handle custom repo, tag comment, author
|
||||
img, err = b.Commit(c, "", "", img.Comment, img.Author, nil)
|
||||
|
@ -291,8 +291,7 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string) error {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string, json bool) error {
|
||||
history, err := srv.registry.GetRemoteHistory(imgId, registry, token)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -302,7 +301,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
|
|||
// FIXME: Launch the getRemoteImage() in goroutines
|
||||
for _, id := range history {
|
||||
if !srv.runtime.graph.Exists(id) {
|
||||
fmt.Fprintf(out, "Pulling %s metadata\r\n", id)
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling %s metadata", json), id)
|
||||
imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token)
|
||||
if err != nil {
|
||||
// FIXME: Keep goging in case of error?
|
||||
|
@ -314,12 +313,12 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
|
|||
}
|
||||
|
||||
// Get the layer
|
||||
fmt.Fprintf(out, "Pulling %s fs layer\r\n", img.Id)
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling %s fs layer", json), id)
|
||||
layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, "Downloading %v/%v (%v)"), false, img); err != nil {
|
||||
if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, out, utils.FormatProgress("%v/%v (%v)", json), json), false, img); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -327,9 +326,8 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
|
|||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
fmt.Fprintf(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress())
|
||||
func (srv *Server) pullRepository(out io.Writer, remote, askedTag string, json bool) error {
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling repository %s from %s", json), remote, auth.IndexServerAddress())
|
||||
repoData, err := srv.registry.GetRepositoryData(remote)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -366,11 +364,11 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
|
|||
utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.Id)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(out, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote)
|
||||
fmt.Fprintf(out, utils.FormatStatus("Pulling image %s (%s) from %s", json), img.Id, img.Tag, remote)
|
||||
success := false
|
||||
for _, ep := range repoData.Endpoints {
|
||||
if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil {
|
||||
fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err)
|
||||
if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens, json); err != nil {
|
||||
fmt.Fprintf(out, utils.FormatStatus("Error while retrieving image for tag: %s (%s); checking next endpoint\n", json), askedTag, err)
|
||||
continue
|
||||
}
|
||||
success = true
|
||||
|
@ -395,15 +393,16 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImagePull(name, tag, registry string, out io.Writer) error {
|
||||
func (srv *Server) ImagePull(name, tag, registry string, out io.Writer, json bool) error {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
if registry != "" {
|
||||
if err := srv.pullImage(out, name, registry, nil); err != nil {
|
||||
if err := srv.pullImage(out, name, registry, nil, json); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := srv.pullRepository(out, name, tag); err != nil {
|
||||
if err := srv.pullRepository(out, name, tag, json); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -570,7 +569,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st
|
|||
}
|
||||
|
||||
// Send the layer
|
||||
if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, ""), ep, token); err != nil {
|
||||
if err := srv.registry.PushImageLayerRegistry(imgData.Id, utils.ProgressReader(layerData, int(layerData.Size), out, "", false), ep, token); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -621,7 +620,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)")
|
||||
archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)\r", false)
|
||||
}
|
||||
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
|
||||
if err != nil {
|
||||
|
@ -776,6 +775,13 @@ func (srv *Server) ContainerWait(name string) (int, error) {
|
|||
return 0, fmt.Errorf("No such container: %s", name)
|
||||
}
|
||||
|
||||
func (srv *Server) ContainerResize(name string, h, w int) error {
|
||||
if container := srv.runtime.Get(name); container != nil {
|
||||
return container.Resize(h, w)
|
||||
}
|
||||
return fmt.Errorf("No such container: %s", name)
|
||||
}
|
||||
|
||||
func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, stderr bool, in io.ReadCloser, out io.Writer) error {
|
||||
container := srv.runtime.Get(name)
|
||||
if container == nil {
|
||||
|
|
|
@ -85,6 +85,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// FIXME: this failed once with a race condition ("Unable to remove filesystem for xxx: directory not empty")
|
||||
if err = srv.ContainerDestroy(id, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
22
term/term.go
22
term/term.go
|
@ -109,17 +109,35 @@ type State struct {
|
|||
termios Termios
|
||||
}
|
||||
|
||||
type Winsize struct {
|
||||
Width uint16
|
||||
Height uint16
|
||||
x uint16
|
||||
y uint16
|
||||
}
|
||||
|
||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||
ws := &Winsize{}
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||
return ws, err
|
||||
}
|
||||
|
||||
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||
return err
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
var termios Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(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.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
4
utils.go
4
utils.go
|
@ -49,10 +49,10 @@ func CompareConfig(a, b *Config) bool {
|
|||
}
|
||||
|
||||
func MergeConfig(userConf, imageConf *Config) {
|
||||
if userConf.Hostname != "" {
|
||||
if userConf.Hostname == "" {
|
||||
userConf.Hostname = imageConf.Hostname
|
||||
}
|
||||
if userConf.User != "" {
|
||||
if userConf.User == "" {
|
||||
userConf.User = imageConf.User
|
||||
}
|
||||
if userConf.Memory == 0 {
|
||||
|
|
|
@ -69,6 +69,7 @@ type progressReader struct {
|
|||
readProgress int // How much has been read so far (bytes)
|
||||
lastUpdate int // How many bytes read at least update
|
||||
template string // Template to print. Default "%v/%v (%v)"
|
||||
json bool
|
||||
}
|
||||
|
||||
func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
|
@ -84,15 +85,15 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
|||
}
|
||||
if r.readProgress-r.lastUpdate > updateEvery || err != nil {
|
||||
if r.readTotal > 0 {
|
||||
fmt.Fprintf(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
|
||||
fmt.Fprintf(r.output, r.template, r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
|
||||
} else {
|
||||
fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a")
|
||||
fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a")
|
||||
}
|
||||
r.lastUpdate = r.readProgress
|
||||
}
|
||||
// Send newline when complete
|
||||
if err != nil {
|
||||
fmt.Fprintf(r.output, "\n")
|
||||
fmt.Fprintf(r.output, FormatStatus("", r.json))
|
||||
}
|
||||
|
||||
return read, err
|
||||
|
@ -100,11 +101,11 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
|||
func (r *progressReader) Close() error {
|
||||
return io.ReadCloser(r.reader).Close()
|
||||
}
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader {
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
|
||||
if template == "" {
|
||||
template = "%v/%v (%v)"
|
||||
template = "%v/%v (%v)\r"
|
||||
}
|
||||
return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template}
|
||||
return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template, json}
|
||||
}
|
||||
|
||||
// HumanDuration returns a human-readable approximation of a duration
|
||||
|
@ -562,3 +563,19 @@ func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
|||
}
|
||||
return &WriteFlusher{w: w, flusher: flusher}
|
||||
}
|
||||
|
||||
func FormatStatus(str string, json bool) string {
|
||||
if json {
|
||||
return "{\"status\" : \"" + str + "\"}"
|
||||
}
|
||||
return str + "\r\n"
|
||||
}
|
||||
|
||||
func FormatProgress(str string, json bool) string {
|
||||
if json {
|
||||
return "{\"progress\" : \"" + str + "\"}"
|
||||
}
|
||||
return "Downloading " + str + "\r"
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue