mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge branch 'master' into add-libcontainer
Conflicts: execdriver/termconsole.go Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
commit
ce08083f9c
27 changed files with 1636 additions and 255 deletions
|
@ -1,8 +1,7 @@
|
|||
Solomon Hykes <solomon@dotcloud.com> (@shykes)
|
||||
Guillaume Charmes <guillaume@dotcloud.com> (@creack)
|
||||
Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Victor Vieux <vieux@docker.com> (@vieux)
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
.travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
api.go: Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
|
4
Makefile
4
Makefile
|
@ -32,10 +32,10 @@ shell: build
|
|||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
build: bundles
|
||||
docker build -rm -t "$(DOCKER_IMAGE)" .
|
||||
docker build -t "$(DOCKER_IMAGE)" .
|
||||
|
||||
docs-build:
|
||||
docker build -rm -t "$(DOCKER_DOCS_IMAGE)" docs
|
||||
docker build -t "$(DOCKER_DOCS_IMAGE)" docs
|
||||
|
||||
bundles:
|
||||
mkdir bundles
|
||||
|
|
1
api/MAINTAINERS
Normal file
1
api/MAINTAINERS
Normal file
|
@ -0,0 +1 @@
|
|||
Victor Vieux <vieux@docker.com> (@vieux)
|
|
@ -138,7 +138,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success")
|
||||
suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
|
||||
noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
|
||||
rm := cmd.Bool([]string{"#rm", "-rm"}, false, "Remove intermediate containers after a successful build")
|
||||
rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -780,7 +780,10 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
|||
|
||||
// 'docker rmi IMAGE' removes all images with the name IMAGE
|
||||
func (cli *DockerCli) CmdRmi(args ...string) error {
|
||||
cmd := cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
|
||||
var (
|
||||
cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
|
||||
force = cmd.Bool([]string{"f", "-force"}, false, "Force")
|
||||
)
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -789,9 +792,14 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
if *force {
|
||||
v.Set("force", "1")
|
||||
}
|
||||
|
||||
var encounteredError error
|
||||
for _, name := range cmd.Args() {
|
||||
body, _, err := readBody(cli.call("DELETE", "/images/"+name, nil, false))
|
||||
body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
|
||||
|
@ -2032,7 +2040,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
|||
re := regexp.MustCompile("/+")
|
||||
path = re.ReplaceAllString(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), params)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
@ -2109,7 +2117,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
|||
re := regexp.MustCompile("/+")
|
||||
path = re.ReplaceAllString(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2173,7 +2181,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
|||
re := regexp.MustCompile("/+")
|
||||
path = re.ReplaceAllString(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
APIVERSION = 1.9
|
||||
APIVERSION = "1.10"
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
)
|
||||
|
|
160
api/server.go
160
api/server.go
|
@ -12,6 +12,8 @@ import (
|
|||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/pkg/listenbuffer"
|
||||
"github.com/dotcloud/docker/pkg/systemd"
|
||||
"github.com/dotcloud/docker/pkg/user"
|
||||
"github.com/dotcloud/docker/pkg/version"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
|
@ -21,7 +23,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/pprof"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
@ -32,7 +33,7 @@ var (
|
|||
activationLock chan struct{}
|
||||
)
|
||||
|
||||
type HttpApiFunc func(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
|
||||
type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error
|
||||
|
||||
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
|
@ -113,7 +114,7 @@ func getBoolParam(value string) (bool, error) {
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var (
|
||||
authConfig, err = ioutil.ReadAll(r.Body)
|
||||
job = eng.Job("auth")
|
||||
|
@ -136,13 +137,13 @@ func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *htt
|
|||
return nil
|
||||
}
|
||||
|
||||
func getVersion(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
eng.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -160,7 +161,7 @@ func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWri
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -186,7 +187,7 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
job.Setenv("filter", r.Form.Get("filter"))
|
||||
job.Setenv("all", r.Form.Get("all"))
|
||||
|
||||
if version >= 1.7 {
|
||||
if version.GreaterThanOrEqualTo("1.7") {
|
||||
streamJSON(job, w, false)
|
||||
} else if outs, err = job.Stdout.AddListTable(); err != nil {
|
||||
return err
|
||||
|
@ -196,7 +197,7 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return err
|
||||
}
|
||||
|
||||
if version < 1.7 && outs != nil { // Convert to legacy format
|
||||
if version.LessThan("1.7") && outs != nil { // Convert to legacy format
|
||||
outsLegacy := engine.NewTable("Created", 0)
|
||||
for _, out := range outs.Data {
|
||||
for _, repoTag := range out.GetList("RepoTags") {
|
||||
|
@ -219,8 +220,8 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version > 1.6 {
|
||||
func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version.GreaterThan("1.6") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return fmt.Errorf("This is now implemented in the client.")
|
||||
}
|
||||
|
@ -228,13 +229,13 @@ func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func getInfo(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
eng.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -245,7 +246,7 @@ func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -259,7 +260,7 @@ func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -269,8 +270,8 @@ func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version < 1.4 {
|
||||
func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version.LessThan("1.4") {
|
||||
return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
|
||||
}
|
||||
if vars == nil {
|
||||
|
@ -285,7 +286,7 @@ func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -301,7 +302,7 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
|
|||
job.Setenv("before", r.Form.Get("before"))
|
||||
job.Setenv("limit", r.Form.Get("limit"))
|
||||
|
||||
if version >= 1.5 {
|
||||
if version.GreaterThanOrEqualTo("1.5") {
|
||||
streamJSON(job, w, false)
|
||||
} else if outs, err = job.Stdout.AddTable(); err != nil {
|
||||
return err
|
||||
|
@ -309,7 +310,7 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
|
|||
if err = job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if version < 1.5 { // Convert to legacy format
|
||||
if version.LessThan("1.5") { // Convert to legacy format
|
||||
for _, out := range outs.Data {
|
||||
ports := engine.NewTable("", 0)
|
||||
ports.ReadListFrom([]byte(out.Get("Ports")))
|
||||
|
@ -323,7 +324,7 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
|
|||
return nil
|
||||
}
|
||||
|
||||
func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -340,7 +341,7 @@ func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -369,7 +370,7 @@ func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *h
|
|||
}
|
||||
|
||||
// Creates an image from Pull or from Import
|
||||
func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -389,9 +390,6 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
authConfig = &auth.AuthConfig{}
|
||||
}
|
||||
}
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
if image != "" { //pull
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
|
@ -400,7 +398,7 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
}
|
||||
}
|
||||
job = eng.Job("pull", r.Form.Get("fromImage"), tag)
|
||||
job.SetenvBool("parallel", version > 1.3)
|
||||
job.SetenvBool("parallel", version.GreaterThan("1.3"))
|
||||
job.SetenvJson("metaHeaders", metaHeaders)
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
} else { //import
|
||||
|
@ -408,7 +406,7 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
job.Stdin.Add(r.Body)
|
||||
}
|
||||
|
||||
if version > 1.0 {
|
||||
if version.GreaterThan("1.0") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, true)
|
||||
} else {
|
||||
|
@ -418,14 +416,14 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -457,19 +455,15 @@ func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter,
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesInsert(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
|
||||
if version > 1.0 {
|
||||
if version.GreaterThan("1.0") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, false)
|
||||
} else {
|
||||
|
@ -479,14 +473,14 @@ func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -517,13 +511,10 @@ func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter,
|
|||
}
|
||||
}
|
||||
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
job := eng.Job("push", vars["name"])
|
||||
job.SetenvJson("metaHeaders", metaHeaders)
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
if version > 1.0 {
|
||||
if version.GreaterThan("1.0") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, true)
|
||||
} else {
|
||||
|
@ -534,17 +525,17 @@ func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter,
|
|||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if version > 1.0 {
|
||||
if version.GreaterThan("1.0") {
|
||||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
}
|
||||
job := eng.Job("image_export", vars["name"])
|
||||
|
@ -552,13 +543,13 @@ func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func postImagesLoad(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
job := eng.Job("load")
|
||||
job.Stdin.Add(r.Body)
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -589,7 +580,7 @@ func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
return writeJSON(w, http.StatusCreated, out)
|
||||
}
|
||||
|
||||
func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -605,7 +596,7 @@ func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseW
|
|||
return nil
|
||||
}
|
||||
|
||||
func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -622,7 +613,7 @@ func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
return nil
|
||||
}
|
||||
|
||||
func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -631,12 +622,12 @@ func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
}
|
||||
var job = eng.Job("image_delete", vars["name"])
|
||||
streamJSON(job, w, false)
|
||||
job.SetenvBool("autoPrune", version > 1.1)
|
||||
job.Setenv("force", r.Form.Get("force"))
|
||||
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -657,7 +648,7 @@ func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWri
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -673,7 +664,7 @@ func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -695,7 +686,7 @@ func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return writeJSON(w, http.StatusOK, env)
|
||||
}
|
||||
|
||||
func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -708,7 +699,7 @@ func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -750,7 +741,7 @@ func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
|
||||
if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version >= 1.6 {
|
||||
if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
|
||||
errStream = utils.NewStdWriter(outStream, utils.Stderr)
|
||||
outStream = utils.NewStdWriter(outStream, utils.Stdout)
|
||||
} else {
|
||||
|
@ -773,7 +764,7 @@ func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
return nil
|
||||
}
|
||||
|
||||
func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -805,7 +796,7 @@ func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -815,7 +806,7 @@ func getContainersByName(eng *engine.Engine, version float64, w http.ResponseWri
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -825,8 +816,8 @@ func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter,
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version < 1.3 {
|
||||
func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version.LessThan("1.3") {
|
||||
return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
||||
}
|
||||
var (
|
||||
|
@ -841,7 +832,7 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
|
|||
// Both headers will be parsed and sent along to the daemon, but if a non-empty
|
||||
// ConfigFile is present, any value provided as an AuthConfig directly will
|
||||
// be overridden. See BuildFile::CmdFrom for details.
|
||||
if version < 1.9 && authEncoded != "" {
|
||||
if version.LessThan("1.9") && authEncoded != "" {
|
||||
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
|
@ -859,7 +850,7 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
|
|||
}
|
||||
}
|
||||
|
||||
if version >= 1.8 {
|
||||
if version.GreaterThanOrEqualTo("1.8") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, true)
|
||||
} else {
|
||||
|
@ -878,13 +869,13 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
|
|||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version >= 1.8)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -907,7 +898,7 @@ func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
}
|
||||
|
||||
job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
|
||||
streamJSON(job, w, false)
|
||||
job.Stdout.Add(w)
|
||||
if err := job.Run(); err != nil {
|
||||
utils.Errorf("%s", err.Error())
|
||||
if strings.Contains(err.Error(), "No such container") {
|
||||
|
@ -917,7 +908,7 @@ func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
func optionsHandler(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
}
|
||||
|
@ -927,7 +918,7 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
|
|||
w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
||||
}
|
||||
|
||||
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion string) http.HandlerFunc {
|
||||
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// log the request
|
||||
utils.Debugf("Calling %s %s", localMethod, localRoute)
|
||||
|
@ -938,20 +929,20 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local
|
|||
|
||||
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
||||
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
||||
if len(userAgent) == 2 && userAgent[1] != dockerVersion {
|
||||
if len(userAgent) == 2 && !dockerVersion.Equal(userAgent[1]) {
|
||||
utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
|
||||
}
|
||||
}
|
||||
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
|
||||
if err != nil {
|
||||
version := version.Version(mux.Vars(r)["version"])
|
||||
if version == "" {
|
||||
version = APIVERSION
|
||||
}
|
||||
if enableCors {
|
||||
writeCorsHeaders(w, r)
|
||||
}
|
||||
|
||||
if version == 0 || version > APIVERSION {
|
||||
http.Error(w, fmt.Errorf("client and server don't have same version (client : %g, server: %g)", version, APIVERSION).Error(), http.StatusNotFound)
|
||||
if version.GreaterThan(APIVERSION) {
|
||||
http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, APIVERSION).Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1049,7 +1040,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
|||
localMethod := method
|
||||
|
||||
// build the handler function
|
||||
f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, dockerVersion)
|
||||
f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, version.Version(dockerVersion))
|
||||
|
||||
// add the new route
|
||||
if localRoute == "" {
|
||||
|
@ -1067,13 +1058,13 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
|||
// ServeRequest processes a single http request to the docker remote api.
|
||||
// FIXME: refactor this to be part of Server and not require re-creating a new
|
||||
// router each time. This requires first moving ListenAndServe into Server.
|
||||
func ServeRequest(eng *engine.Engine, apiversion float64, w http.ResponseWriter, req *http.Request) error {
|
||||
func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) error {
|
||||
router, err := createRouter(eng, false, true, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert APIVERSION into the request as a convenience
|
||||
req.URL.Path = fmt.Sprintf("/v%g%s", apiversion, req.URL.Path)
|
||||
req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
|
||||
router.ServeHTTP(w, req)
|
||||
return nil
|
||||
}
|
||||
|
@ -1142,18 +1133,15 @@ func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors
|
|||
return err
|
||||
}
|
||||
|
||||
groups, err := ioutil.ReadFile("/etc/group")
|
||||
groups, err := user.ParseGroupFilter(func(g *user.Group) bool {
|
||||
return g.Name == "docker"
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
|
||||
if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
|
||||
gid, err := strconv.Atoi(gidMatch[2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utils.Debugf("docker group found. gid: %d", gid)
|
||||
if err := os.Chown(addr, 0, gid); err != nil {
|
||||
if len(groups) > 0 {
|
||||
utils.Debugf("docker group found. gid: %d", groups[0].Gid)
|
||||
if err := os.Chown(addr, 0, groups[0].Gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
32
container.go
32
container.go
|
@ -790,22 +790,8 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
|
|||
utils.Errorf("Error running container: %s", err)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
container.cleanup()
|
||||
|
||||
// Re-create a brand new stdin pipe once the container exited
|
||||
if container.Config.OpenStdin {
|
||||
container.stdin, container.stdinPipe = io.Pipe()
|
||||
}
|
||||
|
||||
container.State.SetStopped(exitCode)
|
||||
|
||||
if container.runtime != nil && container.runtime.srv != nil {
|
||||
container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
|
||||
}
|
||||
|
||||
close(container.waitLock)
|
||||
|
||||
// FIXME: there is a race condition here which causes this to fail during the unit tests.
|
||||
// If another goroutine was waiting for Wait() to return before removing the container's root
|
||||
// from the filesystem... At this point it may already have done so.
|
||||
|
@ -813,7 +799,23 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
|
|||
// to return.
|
||||
// FIXME: why are we serializing running state to disk in the first place?
|
||||
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
||||
container.ToDisk()
|
||||
if err := container.ToDisk(); err != nil {
|
||||
utils.Errorf("Error dumping container state to disk: %s\n", err)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
container.cleanup()
|
||||
|
||||
// Re-create a brand new stdin pipe once the container exited
|
||||
if container.Config.OpenStdin {
|
||||
container.stdin, container.stdinPipe = io.Pipe()
|
||||
}
|
||||
|
||||
if container.runtime != nil && container.runtime.srv != nil {
|
||||
container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
|
||||
}
|
||||
|
||||
close(container.waitLock)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ rm -rf "$target"/var/cache/ldconfig/*
|
|||
|
||||
version=
|
||||
if [ -r "$target"/etc/redhat-release ]; then
|
||||
version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' /etc/redhat-release)"
|
||||
version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' "$target"/etc/redhat-release)"
|
||||
fi
|
||||
|
||||
if [ -z "$version" ]; then
|
||||
|
|
|
@ -122,7 +122,10 @@ If the test are successful then the tail of the output should look something lik
|
|||
PASS
|
||||
ok github.com/dotcloud/docker/utils 0.017s
|
||||
|
||||
If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'.
|
||||
You can use this to select certain tests to run, eg.
|
||||
|
||||
TESTFLAGS='-run ^TestBuild$' make test
|
||||
|
||||
|
||||
Step 6: Use Docker
|
||||
|
|
|
@ -26,15 +26,31 @@ Docker Remote API
|
|||
2. Versions
|
||||
===========
|
||||
|
||||
The current version of the API is 1.9
|
||||
The current version of the API is 1.10
|
||||
|
||||
Calling /images/<name>/insert is the same as calling
|
||||
/v1.9/images/<name>/insert
|
||||
/v1.10/images/<name>/insert
|
||||
|
||||
You can still call an old version of the api using
|
||||
/v1.0/images/<name>/insert
|
||||
|
||||
|
||||
v1.10
|
||||
*****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.10`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
.. http:delete:: /images/(name)
|
||||
|
||||
**New!** You can now use the force parameter to force delete of an image, even if it's
|
||||
tagged in multiple repositories.
|
||||
|
||||
v1.9
|
||||
****
|
||||
|
||||
|
|
1282
docs/sources/reference/api/docker_remote_api_v1.10.rst
Normal file
1282
docs/sources/reference/api/docker_remote_api_v1.10.rst
Normal file
File diff suppressed because it is too large
Load diff
|
@ -185,11 +185,11 @@ Examples:
|
|||
|
||||
Usage: docker build [OPTIONS] PATH | URL | -
|
||||
Build a new container image from the source code at PATH
|
||||
-t, --time="": Repository name (and optionally a tag) to be applied
|
||||
-t, --time="": Repository name (and optionally a tag) to be applied
|
||||
to the resulting image in case of success.
|
||||
-q, --quiet=false: Suppress the verbose output generated by the containers.
|
||||
--no-cache: Do not use the cache when building the image.
|
||||
--rm: Remove intermediate containers after a successful build
|
||||
--rm=true: Remove intermediate containers after a successful build
|
||||
|
||||
The files at ``PATH`` or ``URL`` are called the "context" of the build. The
|
||||
build process may refer to any of the files in the context, for example when
|
||||
|
@ -198,8 +198,6 @@ is given as ``URL``, then no context is set. When a Git repository is set as
|
|||
``URL``, then the repository is used as the context. Git repositories are
|
||||
cloned with their submodules (`git clone --recursive`).
|
||||
|
||||
.. note:: ``docker build --rm`` does not affect the image cache which is used to accelerate builds, it only removes the duplicate writeable container layers.
|
||||
|
||||
.. _cli_build_examples:
|
||||
|
||||
.. seealso:: :ref:`dockerbuilder`.
|
||||
|
@ -209,7 +207,7 @@ Examples:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker build --rm .
|
||||
$ sudo docker build .
|
||||
Uploading context 10240 bytes
|
||||
Step 1 : FROM busybox
|
||||
Pulling repository busybox
|
||||
|
@ -249,9 +247,8 @@ The transfer of context from the local machine to the Docker daemon is
|
|||
what the ``docker`` client means when you see the "Uploading context"
|
||||
message.
|
||||
|
||||
The ``--rm`` option tells Docker to remove the intermediate containers and
|
||||
layers that were used to create each image layer. Doing so has no impact on
|
||||
the image build cache.
|
||||
If you wish to keep the intermediate containers after the build is complete,
|
||||
you must use ``--rm=false``. This does not affect the build cache.
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -1023,6 +1020,8 @@ containers will not be deleted.
|
|||
Usage: docker rmi IMAGE [IMAGE...]
|
||||
|
||||
Remove one or more images
|
||||
|
||||
-f, --force=false: Force
|
||||
|
||||
Removing tagged images
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -29,6 +30,10 @@ func Register(name string, handler Handler) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func unregister(name string) {
|
||||
delete(globalHandlers, name)
|
||||
}
|
||||
|
||||
// The Engine is the core of Docker.
|
||||
// It acts as a store for *containers*, and allows manipulation of these
|
||||
// containers by executing *jobs*.
|
||||
|
@ -106,6 +111,12 @@ func New(root string) (*Engine, error) {
|
|||
Stderr: os.Stderr,
|
||||
Stdin: os.Stdin,
|
||||
}
|
||||
eng.Register("commands", func(job *Job) Status {
|
||||
for _, name := range eng.commands() {
|
||||
job.Printf("%s\n", name)
|
||||
}
|
||||
return StatusOK
|
||||
})
|
||||
// Copy existing global handlers
|
||||
for k, v := range globalHandlers {
|
||||
eng.handlers[k] = v
|
||||
|
@ -117,6 +128,17 @@ func (eng *Engine) String() string {
|
|||
return fmt.Sprintf("%s|%s", eng.Root(), eng.id[:8])
|
||||
}
|
||||
|
||||
// Commands returns a list of all currently registered commands,
|
||||
// sorted alphabetically.
|
||||
func (eng *Engine) commands() []string {
|
||||
names := make([]string, 0, len(eng.handlers))
|
||||
for name := range eng.handlers {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// Job creates a new job which can later be executed.
|
||||
// This function mimics `Command` from the standard os/exec package.
|
||||
func (eng *Engine) Job(name string, args ...string) *Job {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -17,6 +18,8 @@ func TestRegister(t *testing.T) {
|
|||
if err := Register("dummy1", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
// Register is global so let's cleanup to avoid conflicts
|
||||
defer unregister("dummy1")
|
||||
|
||||
eng := newTestEngine(t)
|
||||
|
||||
|
@ -33,6 +36,7 @@ func TestRegister(t *testing.T) {
|
|||
if err := eng.Register("dummy2", nil); err == nil {
|
||||
t.Fatalf("Expecting error, got none")
|
||||
}
|
||||
defer unregister("dummy2")
|
||||
}
|
||||
|
||||
func TestJob(t *testing.T) {
|
||||
|
@ -49,6 +53,7 @@ func TestJob(t *testing.T) {
|
|||
}
|
||||
|
||||
eng.Register("dummy2", h)
|
||||
defer unregister("dummy2")
|
||||
job2 := eng.Job("dummy2", "--level=awesome")
|
||||
|
||||
if job2.handler == nil {
|
||||
|
@ -60,6 +65,24 @@ func TestJob(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEngineCommands(t *testing.T) {
|
||||
eng := newTestEngine(t)
|
||||
defer os.RemoveAll(eng.Root())
|
||||
handler := func(job *Job) Status { return StatusOK }
|
||||
eng.Register("foo", handler)
|
||||
eng.Register("bar", handler)
|
||||
eng.Register("echo", handler)
|
||||
eng.Register("die", handler)
|
||||
var output bytes.Buffer
|
||||
commands := eng.Job("commands")
|
||||
commands.Stdout.Add(&output)
|
||||
commands.Run()
|
||||
expected := "bar\ncommands\ndie\necho\nfoo\n"
|
||||
if result := output.String(); result != expected {
|
||||
t.Fatalf("Unexpected output:\nExpected = %v\nResult = %v\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineRoot(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "docker-test-TestEngineCreateDir")
|
||||
if err != nil {
|
||||
|
|
|
@ -77,7 +77,7 @@ func (d *driver) Name() string {
|
|||
}
|
||||
|
||||
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||
if err := SetTerminal(c, pipes); err != nil {
|
||||
if err := execdriver.SetTerminal(c, pipes); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
configPath, err := d.generateLXCConfig(c)
|
||||
|
|
|
@ -12,6 +12,7 @@ const LxcTemplate = `
|
|||
lxc.network.type = veth
|
||||
lxc.network.link = {{.Network.Bridge}}
|
||||
lxc.network.name = eth0
|
||||
lxc.network.mtu = {{.Network.Mtu}}
|
||||
{{else}}
|
||||
# network is disabled (-n=false)
|
||||
lxc.network.type = empty
|
||||
|
|
|
@ -6,14 +6,13 @@ package native
|
|||
|
||||
import (
|
||||
"github.com/dotcloud/docker/execdriver"
|
||||
"github.com/dotcloud/docker/execdriver/lxc"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type dockerStdTerm struct {
|
||||
lxc.StdConsole
|
||||
execdriver.StdConsole
|
||||
pipes *execdriver.Pipes
|
||||
}
|
||||
|
||||
|
@ -26,7 +25,7 @@ func (d *dockerStdTerm) SetMaster(master *os.File) {
|
|||
}
|
||||
|
||||
type dockerTtyTerm struct {
|
||||
lxc.TtyConsole
|
||||
execdriver.TtyConsole
|
||||
pipes *execdriver.Pipes
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package lxc
|
||||
package execdriver
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/execdriver"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/kr/pty"
|
||||
"io"
|
||||
|
@ -9,9 +8,9 @@ import (
|
|||
"os/exec"
|
||||
)
|
||||
|
||||
func SetTerminal(command *execdriver.Command, pipes *execdriver.Pipes) error {
|
||||
func SetTerminal(command *Command, pipes *Pipes) error {
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
term Terminal
|
||||
err error
|
||||
)
|
||||
if command.Tty {
|
||||
|
@ -31,7 +30,7 @@ type TtyConsole struct {
|
|||
SlavePty *os.File
|
||||
}
|
||||
|
||||
func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) {
|
||||
func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
|
||||
ptyMaster, ptySlave, err := pty.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -56,7 +55,7 @@ func (t *TtyConsole) Resize(h, w int) error {
|
|||
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||
}
|
||||
|
||||
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
||||
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
|
||||
command.Stdout = t.SlavePty
|
||||
command.Stderr = t.SlavePty
|
||||
|
||||
|
@ -89,7 +88,7 @@ func (t *TtyConsole) Close() error {
|
|||
type StdConsole struct {
|
||||
}
|
||||
|
||||
func NewStdConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*StdConsole, error) {
|
||||
func NewStdConsole(command *Command, pipes *Pipes) (*StdConsole, error) {
|
||||
std := &StdConsole{}
|
||||
|
||||
if err := std.AttachPipes(&command.Cmd, pipes); err != nil {
|
||||
|
@ -98,7 +97,7 @@ func NewStdConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*StdCo
|
|||
return std, nil
|
||||
}
|
||||
|
||||
func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
||||
func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
|
||||
command.Stdout = pipes.Stdout
|
||||
command.Stderr = pipes.Stderr
|
||||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/dotcloud/docker/runconfig"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -1175,6 +1176,8 @@ func TestGetEnabledCors(t *testing.T) {
|
|||
|
||||
func TestDeleteImages(t *testing.T) {
|
||||
eng := NewTestEngine(t)
|
||||
//we expect errors, so we disable stderr
|
||||
eng.Stderr = ioutil.Discard
|
||||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||
|
||||
initialImages := getImages(eng, t, true, "")
|
||||
|
|
|
@ -1031,7 +1031,10 @@ func TestContainerOrphaning(t *testing.T) {
|
|||
buildSomething(template2, imageName)
|
||||
|
||||
// remove the second image by name
|
||||
resp, err := srv.DeleteImage(imageName, true)
|
||||
resp := engine.NewTable("", 0)
|
||||
if err := srv.DeleteImage(imageName, resp, true, false); err == nil {
|
||||
t.Fatal("Expected error, got none")
|
||||
}
|
||||
|
||||
// see if we deleted the first image (and orphaned the container)
|
||||
for _, i := range resp.Data {
|
||||
|
@ -1043,11 +1046,12 @@ func TestContainerOrphaning(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCmdKill(t *testing.T) {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
cli2 := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
var (
|
||||
stdin, stdinPipe = io.Pipe()
|
||||
stdout, stdoutPipe = io.Pipe()
|
||||
cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
cli2 = api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
)
|
||||
defer cleanup(globalEngine, t)
|
||||
|
||||
ch := make(chan struct{})
|
||||
|
@ -1086,6 +1090,7 @@ func TestCmdKill(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
stdout.Close()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if !container.State.IsRunning() {
|
||||
t.Fatal("The container should be still running")
|
||||
|
|
|
@ -2,6 +2,7 @@ package docker
|
|||
|
||||
import (
|
||||
"github.com/dotcloud/docker"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -35,7 +36,7 @@ func TestImageTagImageDelete(t *testing.T) {
|
|||
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
||||
}
|
||||
|
||||
if _, err := srv.DeleteImage("utest/docker:tag2", true); err != nil {
|
||||
if err := srv.DeleteImage("utest/docker:tag2", engine.NewTable("", 0), true, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -47,7 +48,7 @@ func TestImageTagImageDelete(t *testing.T) {
|
|||
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
||||
}
|
||||
|
||||
if _, err := srv.DeleteImage("utest:5000/docker:tag3", true); err != nil {
|
||||
if err := srv.DeleteImage("utest:5000/docker:tag3", engine.NewTable("", 0), true, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -56,7 +57,7 @@ func TestImageTagImageDelete(t *testing.T) {
|
|||
nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 1
|
||||
nActual = len(images.Data[0].GetList("RepoTags"))
|
||||
|
||||
if _, err := srv.DeleteImage("utest:tag1", true); err != nil {
|
||||
if err := srv.DeleteImage("utest:tag1", engine.NewTable("", 0), true, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -447,8 +448,7 @@ func TestRmi(t *testing.T) {
|
|||
t.Fatalf("Expected 2 new images, found %d.", images.Len()-initialImages.Len())
|
||||
}
|
||||
|
||||
_, err = srv.DeleteImage(imageID, true)
|
||||
if err != nil {
|
||||
if err = srv.DeleteImage(imageID, engine.NewTable("", 0), true, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -683,8 +683,8 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
|
|||
}
|
||||
|
||||
// Try to remove the tag
|
||||
imgs, err := srv.DeleteImage("utest:tag1", true)
|
||||
if err != nil {
|
||||
imgs := engine.NewTable("", 0)
|
||||
if err := srv.DeleteImage("utest:tag1", imgs, true, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -692,7 +692,7 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
|
|||
t.Fatalf("Should only have deleted one untag %d", len(imgs.Data))
|
||||
}
|
||||
|
||||
if untag := imgs.Data[0].Get("Untagged"); untag != unitTestImageID {
|
||||
if untag := imgs.Data[0].Get("Untagged"); untag != "utest:tag1" {
|
||||
t.Fatalf("Expected %s got %s", unitTestImageID, untag)
|
||||
}
|
||||
}
|
||||
|
|
52
pkg/version/version.go
Normal file
52
pkg/version/version.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Version string
|
||||
|
||||
func (me Version) compareTo(other string) int {
|
||||
var (
|
||||
meTab = strings.Split(string(me), ".")
|
||||
otherTab = strings.Split(other, ".")
|
||||
)
|
||||
for i, s := range meTab {
|
||||
var meInt, otherInt int
|
||||
meInt, _ = strconv.Atoi(s)
|
||||
if len(otherTab) > i {
|
||||
otherInt, _ = strconv.Atoi(otherTab[i])
|
||||
}
|
||||
if meInt > otherInt {
|
||||
return 1
|
||||
}
|
||||
if otherInt > meInt {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
if len(otherTab) > len(meTab) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (me Version) LessThan(other string) bool {
|
||||
return me.compareTo(other) == -1
|
||||
}
|
||||
|
||||
func (me Version) LessThanOrEqualTo(other string) bool {
|
||||
return me.compareTo(other) <= 0
|
||||
}
|
||||
|
||||
func (me Version) GreaterThan(other string) bool {
|
||||
return me.compareTo(other) == 1
|
||||
}
|
||||
|
||||
func (me Version) GreaterThanOrEqualTo(other string) bool {
|
||||
return me.compareTo(other) >= 0
|
||||
}
|
||||
|
||||
func (me Version) Equal(other string) bool {
|
||||
return me.compareTo(other) == 0
|
||||
}
|
25
pkg/version/version_test.go
Normal file
25
pkg/version/version_test.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertVersion(t *testing.T, a, b string, result int) {
|
||||
if r := Version(a).compareTo(b); r != result {
|
||||
t.Fatalf("Unexpected version comparison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareVersion(t *testing.T) {
|
||||
assertVersion(t, "1.12", "1.12", 0)
|
||||
assertVersion(t, "1.05.00.0156", "1.0.221.9289", 1)
|
||||
assertVersion(t, "1", "1.0.1", -1)
|
||||
assertVersion(t, "1.0.1", "1", 1)
|
||||
assertVersion(t, "1.0.1", "1.0.2", -1)
|
||||
assertVersion(t, "1.0.2", "1.0.3", -1)
|
||||
assertVersion(t, "1.0.3", "1.1", -1)
|
||||
assertVersion(t, "1.1", "1.1.1", -1)
|
||||
assertVersion(t, "1.1.1", "1.1.2", -1)
|
||||
assertVersion(t, "1.1.2", "1.2", -1)
|
||||
|
||||
}
|
151
server.go
151
server.go
|
@ -2,7 +2,6 @@ package docker
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
|
@ -1810,102 +1809,33 @@ func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status {
|
|||
return engine.StatusOK
|
||||
}
|
||||
|
||||
var ErrImageReferenced = errors.New("Image referenced by a repository")
|
||||
|
||||
func (srv *Server) deleteImageAndChildren(id string, imgs *engine.Table, byParents map[string][]*Image) error {
|
||||
// If the image is referenced by a repo, do not delete
|
||||
if len(srv.runtime.repositories.ByID()[id]) != 0 {
|
||||
return ErrImageReferenced
|
||||
}
|
||||
// If the image is not referenced but has children, go recursive
|
||||
referenced := false
|
||||
for _, img := range byParents[id] {
|
||||
if err := srv.deleteImageAndChildren(img.ID, imgs, byParents); err != nil {
|
||||
if err != ErrImageReferenced {
|
||||
return err
|
||||
}
|
||||
referenced = true
|
||||
}
|
||||
}
|
||||
if referenced {
|
||||
return ErrImageReferenced
|
||||
}
|
||||
|
||||
// If the image is not referenced and has no children, remove it
|
||||
byParents, err := srv.runtime.graph.ByParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(byParents[id]) == 0 && srv.canDeleteImage(id) == nil {
|
||||
if err := srv.runtime.repositories.DeleteAll(id); err != nil {
|
||||
return err
|
||||
}
|
||||
err := srv.runtime.graph.Delete(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out := &engine.Env{}
|
||||
out.Set("Deleted", id)
|
||||
imgs.Add(out)
|
||||
srv.LogEvent("delete", id, "")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) deleteImageParents(img *Image, imgs *engine.Table) error {
|
||||
if img.Parent != "" {
|
||||
parent, err := srv.runtime.graph.Get(img.Parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
byParents, err := srv.runtime.graph.ByParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove all children images
|
||||
if err := srv.deleteImageAndChildren(img.Parent, imgs, byParents); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.deleteImageParents(parent, imgs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, error) {
|
||||
func (srv *Server) DeleteImage(name string, imgs *engine.Table, first, force bool) error {
|
||||
var (
|
||||
repoName, tag string
|
||||
img, err = srv.runtime.repositories.LookupImage(name)
|
||||
imgs = engine.NewTable("", 0)
|
||||
tags = []string{}
|
||||
)
|
||||
|
||||
repoName, tag = utils.ParseRepositoryTag(name)
|
||||
if tag == "" {
|
||||
tag = DEFAULTTAG
|
||||
}
|
||||
|
||||
img, err := srv.runtime.repositories.LookupImage(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("No such image: %s", name)
|
||||
}
|
||||
|
||||
// FIXME: What does autoPrune mean ?
|
||||
if !autoPrune {
|
||||
if err := srv.runtime.graph.Delete(img.ID); err != nil {
|
||||
return nil, fmt.Errorf("Cannot delete image %s: %s", name, err)
|
||||
if r, _ := srv.runtime.repositories.Get(repoName); r != nil {
|
||||
return fmt.Errorf("No such image: %s:%s", repoName, tag)
|
||||
}
|
||||
return nil, nil
|
||||
return fmt.Errorf("No such image: %s", name)
|
||||
}
|
||||
|
||||
if !strings.Contains(img.ID, name) {
|
||||
repoName, tag = utils.ParseRepositoryTag(name)
|
||||
if strings.Contains(img.ID, name) {
|
||||
repoName = ""
|
||||
tag = ""
|
||||
}
|
||||
|
||||
// If we have a repo and the image is not referenced anywhere else
|
||||
// then just perform an untag and do not validate.
|
||||
//
|
||||
// i.e. only validate if we are performing an actual delete and not
|
||||
// an untag op
|
||||
if repoName != "" && len(srv.runtime.repositories.ByID()[img.ID]) == 1 {
|
||||
// Prevent deletion if image is used by a container
|
||||
if err := srv.canDeleteImage(img.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byParents, err := srv.runtime.graph.ByParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//If delete by id, see if the id belong only to one repository
|
||||
|
@ -1917,51 +1847,68 @@ func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, erro
|
|||
if parsedTag != "" {
|
||||
tags = append(tags, parsedTag)
|
||||
}
|
||||
} else if repoName != parsedRepo {
|
||||
} else if repoName != parsedRepo && !force {
|
||||
// the id belongs to multiple repos, like base:latest and user:test,
|
||||
// in that case return conflict
|
||||
return nil, fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories", utils.TruncateID(img.ID))
|
||||
return fmt.Errorf("Conflict, cannot delete image %s because it is tagged in multiple repositories, use -f to force", name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
if !first && len(tags) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Untag the current image
|
||||
for _, tag := range tags {
|
||||
tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
if tagDeleted {
|
||||
out := &engine.Env{}
|
||||
out.Set("Untagged", img.ID)
|
||||
out.Set("Untagged", repoName+":"+tag)
|
||||
imgs.Add(out)
|
||||
srv.LogEvent("untag", img.ID, "")
|
||||
}
|
||||
}
|
||||
tags = srv.runtime.repositories.ByID()[img.ID]
|
||||
if (len(tags) <= 1 && repoName == "") || len(tags) == 0 {
|
||||
if len(byParents[img.ID]) == 0 {
|
||||
if err := srv.canDeleteImage(img.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.runtime.repositories.DeleteAll(img.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.runtime.graph.Delete(img.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
out := &engine.Env{}
|
||||
out.Set("Deleted", img.ID)
|
||||
imgs.Add(out)
|
||||
srv.LogEvent("delete", img.ID, "")
|
||||
if img.Parent != "" {
|
||||
err := srv.DeleteImage(img.Parent, imgs, false, force)
|
||||
if first {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
|
||||
if err := srv.deleteImageAndChildren(img.ID, imgs, nil); err != nil {
|
||||
if err != ErrImageReferenced {
|
||||
return imgs, err
|
||||
}
|
||||
} else if err := srv.deleteImageParents(img, imgs); err != nil {
|
||||
if err != ErrImageReferenced {
|
||||
return imgs, err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return imgs, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImageDelete(job *engine.Job) engine.Status {
|
||||
if n := len(job.Args); n != 1 {
|
||||
return job.Errorf("Usage: %s IMAGE", job.Name)
|
||||
}
|
||||
|
||||
imgs, err := srv.DeleteImage(job.Args[0], job.GetenvBool("autoPrune"))
|
||||
if err != nil {
|
||||
imgs := engine.NewTable("", 0)
|
||||
if err := srv.DeleteImage(job.Args[0], imgs, true, job.GetenvBool("force")); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
if len(imgs.Data) == 0 {
|
||||
|
|
|
@ -52,7 +52,7 @@ func (p *JSONProgress) String() string {
|
|||
}
|
||||
numbersBox = fmt.Sprintf("%8v/%v", current, total)
|
||||
|
||||
if p.Start > 0 && percentage < 50 {
|
||||
if p.Current > 0 && p.Start > 0 && percentage < 50 {
|
||||
fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0))
|
||||
perEntry := fromStart / time.Duration(p.Current)
|
||||
left := time.Duration(p.Total-p.Current) * perEntry
|
||||
|
|
|
@ -606,16 +606,22 @@ func GetKernelVersion() (*KernelVersionInfo, error) {
|
|||
func ParseRelease(release string) (*KernelVersionInfo, error) {
|
||||
var (
|
||||
kernel, major, minor, parsed int
|
||||
flavor string
|
||||
flavor, partial string
|
||||
)
|
||||
|
||||
// Ignore error from Sscanf to allow an empty flavor. Instead, just
|
||||
// make sure we got all the version numbers.
|
||||
parsed, _ = fmt.Sscanf(release, "%d.%d.%d%s", &kernel, &major, &minor, &flavor)
|
||||
if parsed < 3 {
|
||||
parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial)
|
||||
if parsed < 2 {
|
||||
return nil, errors.New("Can't parse kernel version " + release)
|
||||
}
|
||||
|
||||
// sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64
|
||||
parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor)
|
||||
if parsed < 1 {
|
||||
flavor = partial
|
||||
}
|
||||
|
||||
return &KernelVersionInfo{
|
||||
Kernel: kernel,
|
||||
Major: major,
|
||||
|
|
|
@ -420,6 +420,7 @@ func TestParseRelease(t *testing.T) {
|
|||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
||||
assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0)
|
||||
assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0)
|
||||
assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
|
||||
}
|
||||
|
||||
func TestParsePortMapping(t *testing.T) {
|
||||
|
|
Loading…
Add table
Reference in a new issue