first version of Pull

This commit is contained in:
Victor Vieux 2013-05-24 14:43:52 +00:00
commit 3c7bca7a21
14 changed files with 251 additions and 214 deletions

View file

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

90
api.go
View file

@ -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 {
@ -56,8 +58,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
}
@ -65,14 +67,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)
@ -94,7 +96,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 {
@ -104,7 +106,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")
}
@ -116,7 +118,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")
}
@ -129,7 +131,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
}
@ -152,14 +154,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 {
@ -169,7 +171,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")
}
@ -186,7 +188,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")
}
@ -203,7 +205,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
}
@ -227,7 +229,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
}
@ -249,7 +251,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
}
@ -276,7 +278,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
}
@ -288,8 +290,10 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars
if image != "" { //pull
registry := r.Form.Get("registry")
w.Header().Set("Content-Type", "application/json")
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
@ -300,7 +304,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
}
@ -318,7 +322,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
}
@ -336,7 +340,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
}
@ -353,7 +357,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
@ -383,7 +387,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
}
@ -402,7 +406,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
}
@ -422,7 +426,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")
}
@ -434,7 +438,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")
}
@ -446,7 +450,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
}
@ -467,7 +471,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")
}
@ -484,7 +488,7 @@ 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 postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}
@ -527,7 +531,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")
}
@ -545,7 +549,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")
}
@ -563,7 +567,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
@ -590,7 +594,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,
@ -634,7 +638,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)
@ -645,10 +649,20 @@ 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)
}
}

View file

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

View file

@ -32,12 +32,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)

View file

@ -24,7 +24,7 @@ import (
"unicode"
)
const VERSION = "0.3.2"
const VERSION = "0.3.3"
var (
GIT_COMMIT string
@ -1167,7 +1167,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%f", cli.host, cli.port, API_VERSION)+path, params)
if err != nil {
return nil, -1, err
}
@ -1199,7 +1199,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%f%s", cli.host, cli.port, API_VERSION, path), in)
if err != nil {
return err
}
@ -1224,7 +1224,6 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
}
if resp.Header.Get("Content-Type") == "application/json" {
type Message struct {
Status string `json:"status,omitempty"`
Progress string `json:"progress,omitempty"`
@ -1237,13 +1236,12 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
} else if err != nil {
return err
}
if m.Status != "" {
if m.Progress != "" {
fmt.Fprintf(out, "Downloading %s\r", m.Progress)
} else {
fmt.Fprintf(out, "%s\n", m.Status)
} else if m.Progress != "" {
fmt.Fprintf(out, "Downloading... %s\r", m.Progress)
}
}
fmt.Fprintf(out, "\n")
} else {
if _, err := io.Copy(out, resp.Body); err != nil {
return err
@ -1253,7 +1251,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
}
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%f%s", API_VERSION, path), nil)
if err != nil {
return err
}

View file

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

View file

@ -7,61 +7,29 @@
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:
@ -81,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
------------------------
@ -115,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?

View file

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

View file

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

View file

@ -1,37 +1,44 @@
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
- Registry: Use the size to have a good progress bar while pushing
- Registry: Use the actual archive if it exists in order to speed up the push
- Registry: Fix error 400 on push
- Runtime: Store the actual archive on commit
- Registry: Improve the checksum process
- Registry: Use the size to have a good progress bar while pushing
- Registry: Use the actual archive if it exists in order to speed up the push
- Registry: Fix error 400 on push
-- dotCloud <ops@dotcloud.com> Fri, 9 May 2013 00:00:00 -0700
lxc-docker (0.3.1-1) precise; urgency=low
- Builder: Implement the autorun capability within docker builder
- Builder: Add caching to docker builder
- Builder: Add support for docker builder with native API as top level command
- Runtime: Add go version to debug infos
- Builder: Implement ENV within docker builder
- Registry: Add docker search top level command in order to search a repository
- Images: output graph of images to dot (graphviz)
- Documentation: new introduction and high-level overview
- Documentation: Add the documentation for docker builder
- Website: new high-level overview
- Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
- Images: fix ByParent function
- Builder: Check the command existance prior create and add Unit tests for the case
- Registry: Fix pull for official images with specific tag
- Registry: Fix issue when login in with a different user and trying to push
- Documentation: CSS fix for docker documentation to make REST API docs look better.
- Documentation: Fixed CouchDB example page header mistake
- Documentation: fixed README formatting
- Registry: Improve checksum - async calculation
- Runtime: kernel version - don't show the dash if flavor is empty
- Documentation: updated www.docker.io website.
- Builder: use any whitespaces instead of tabs
- Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
- Builder: Implement the autorun capability within docker builder
- Builder: Add caching to docker builder
- Builder: Add support for docker builder with native API as top level command
- Runtime: Add go version to debug infos
- Builder: Implement ENV within docker builder
- Registry: Add docker search top level command in order to search a repository
- Images: output graph of images to dot (graphviz)
- Documentation: new introduction and high-level overview
- Documentation: Add the documentation for docker builder
- Website: new high-level overview
- Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
- Images: fix ByParent function
- Builder: Check the command existance prior create and add Unit tests for the case
- Registry: Fix pull for official images with specific tag
- Registry: Fix issue when login in with a different user and trying to push
- Documentation: CSS fix for docker documentation to make REST API docs look better.
- Documentation: Fixed CouchDB example page header mistake
- Documentation: fixed README formatting
- Registry: Improve checksum - async calculation
- Runtime: kernel version - don't show the dash if flavor is empty
- Documentation: updated www.docker.io website.
- Builder: use any whitespaces instead of tabs
- Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
-- dotCloud <ops@dotcloud.com> Fri, 8 May 2013 00:00:00 -0700

View file

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

View file

@ -91,7 +91,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error {
return err
}
if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil {
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
@ -291,16 +291,17 @@ 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 {
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
}
// FIXME: Try to stream the images?
// FIXME: Launch the getRemoteImage() in goroutines
for _, id := range history {
if !srv.runtime.graph.Exists(id) {
fmt.Fprintf(out, "{\"status\" :\"Pulling %s metadata\"}", 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?
@ -312,12 +313,12 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri
}
// Get the layer
fmt.Fprintf(out, "{\"status\" :\"Pulling %s fs layer\"}", 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, ""), 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
}
}
@ -325,8 +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 {
fmt.Fprintf(out, "{\"status\":\"Pulling repository %s from %s\"}", 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
@ -363,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, "{\"status\":\"Pulling image %s (%s) from %s\"}", 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
@ -392,18 +393,19 @@ 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
}
return nil
}
@ -567,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
@ -618,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 {

View file

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

View file

@ -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,22 +85,27 @@ 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.readProgress, r.readTotal)
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.readProgress, "?")
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, FormatStatus("", r.json))
}
return read, err
}
func (r *progressReader) Close() error {
return io.ReadCloser(r.reader).Close()
}
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader {
if template == "" {
template = "{\"progress\":\"%v/%v\"}"
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string, json bool) *progressReader {
if template == "" {
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
@ -550,3 +556,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"
}