// getContainersByName inspects containers configuration and serializes it as json. func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } var json interface{} var err error version := httputils.VersionFromContext(ctx) switch { case version.LessThan("1.20"): json, err = s.daemon.ContainerInspectPre120(vars["name"]) case version.Equal("1.20"): json, err = s.daemon.ContainerInspect120(vars["name"]) default: json, err = s.daemon.ContainerInspect(vars["name"]) } if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, json) }
func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { info, err := s.backend.SystemInfo() if err != nil { return err } if s.cluster != nil { info.Swarm = s.cluster.Info() } if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") { // TODO: handle this conversion in engine-api type oldInfo struct { *types.Info ExecutionDriver string } old := &oldInfo{ Info: info, ExecutionDriver: "<not supported>", } nameOnlySecurityOptions := []string{} kvSecOpts, err := types.DecodeSecurityOptions(old.SecurityOptions) if err != nil { return err } for _, s := range kvSecOpts { nameOnlySecurityOptions = append(nameOnlySecurityOptions, s.Name) } old.SecurityOptions = nameOnlySecurityOptions return httputils.WriteJSON(w, http.StatusOK, old) } return httputils.WriteJSON(w, http.StatusOK, info) }
func TestVersionMiddlewareWithErrors(t *testing.T) { handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if httputils.VersionFromContext(ctx) == "" { t.Fatalf("Expected version, got empty string") } return nil } defaultVersion := version.Version("1.10.0") minVersion := version.Version("1.2.0") m := NewVersionMiddleware(defaultVersion.String(), defaultVersion, minVersion) h := m(handler) req, _ := http.NewRequest("GET", "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() vars := map[string]string{"version": "0.1"} err := h(ctx, resp, req, vars) if !strings.Contains(err.Error(), "client version 0.1 is too old. Minimum supported API version is 1.2.0") { t.Fatalf("Expected too old client error, got %v", err) } vars["version"] = "100000" err = h(ctx, resp, req, vars) if !strings.Contains(err.Error(), "client is newer than server") { t.Fatalf("Expected client newer than server error, got %v", err) } }
func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } name := r.Form.Get("name") config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body) if err != nil { return err } version := httputils.VersionFromContext(ctx) adjustCPUShares := versions.LessThan(version, "1.19") ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ Name: name, Config: config, HostConfig: hostConfig, NetworkingConfig: networkingConfig, AdjustCPUShares: adjustCPUShares, }) if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, ccr) }
func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } var sig syscall.Signal name := vars["name"] // If we have a signal, look at it. Otherwise, do nothing if sigStr := r.Form.Get("signal"); sigStr != "" { var err error if sig, err = signal.ParseSignal(sigStr); err != nil { return err } } if err := s.backend.ContainerKill(name, uint64(sig)); err != nil { var isStopped bool if e, ok := err.(errContainerIsRunning); ok { isStopped = !e.ContainerIsRunning() } // Return error that's not caused because the container is stopped. // Return error if the container is not running and the api is >= 1.20 // to keep backwards compatibility. version := httputils.VersionFromContext(ctx) if versions.GreaterThanOrEqualTo(version, "1.20") || !isStopped { return fmt.Errorf("Cannot kill container %s: %v", name, err) } } w.WriteHeader(http.StatusNoContent) return nil }
func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } if err := httputils.ParseForm(r); err != nil { return err } var sig syscall.Signal name := vars["name"] // If we have a signal, look at it. Otherwise, do nothing if sigStr := r.Form.Get("signal"); sigStr != "" { var err error if sig, err = signal.ParseSignal(sigStr); err != nil { return err } } if err := s.daemon.ContainerKill(name, uint64(sig)); err != nil { theErr, isDerr := err.(errcode.ErrorCoder) isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning // Return error that's not caused because the container is stopped. // Return error if the container is not running and the api is >= 1.20 // to keep backwards compatibility. version := httputils.VersionFromContext(ctx) if version.GreaterThanOrEqualTo("1.20") || !isStopped { return fmt.Errorf("Cannot kill container %s: %v", name, err) } } w.WriteHeader(http.StatusNoContent) return nil }
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } version := httputils.VersionFromContext(ctx) if versions.GreaterThan(version, "1.21") { if err := httputils.CheckForJSON(r); err != nil { return err } } var ( execName = vars["name"] stdin, inStream io.ReadCloser stdout, stderr, outStream io.Writer ) execStartCheck := &types.ExecStartCheck{} if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil { return err } if exists, err := s.backend.ExecExists(execName); !exists { return err } if !execStartCheck.Detach { var err error // Setting up the streaming http interface. inStream, outStream, err = httputils.HijackConnection(w) if err != nil { return err } defer httputils.CloseStreams(inStream, outStream) if _, ok := r.Header["Upgrade"]; ok { fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") } else { fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") } stdin = inStream stdout = outStream if !execStartCheck.Tty { stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } } // Now run the user process in container. if err := s.backend.ContainerExecStart(execName, stdin, stdout, stderr); err != nil { if execStartCheck.Detach { return err } stdout.Write([]byte(err.Error() + "\r\n")) logrus.Errorf("Error running exec in container: %v", err) } return nil }
func (c *containerAdapter) create(ctx context.Context) error { var cr types.ContainerCreateResponse var err error version := httputils.VersionFromContext(ctx) validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") if cr, err = c.backend.CreateManagedContainer(types.ContainerCreateConfig{ Name: c.container.name(), Config: c.container.config(), HostConfig: c.container.hostConfig(), // Use the first network in container create NetworkingConfig: c.container.createNetworkingConfig(), }, validateHostname); err != nil { return err } // Docker daemon currently doesn't support multiple networks in container create // Connect to all other networks nc := c.container.connectNetworkingConfig() if nc != nil { for n, ep := range nc.EndpointsConfig { if err := c.backend.ConnectContainerToNetwork(cr.ID, n, ep); err != nil { return err } } } if err := c.backend.UpdateContainerServiceConfig(cr.ID, c.container.serviceConfig()); err != nil { return err } return nil }
func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { info, err := s.backend.SystemInfo() if err != nil { return err } if s.clusterProvider != nil { info.Swarm = s.clusterProvider.Info() } if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") { // TODO: handle this conversion in engine-api type oldInfo struct { *types.InfoBase ExecutionDriver string SecurityOptions []string } old := &oldInfo{ InfoBase: info.InfoBase, ExecutionDriver: "<not supported>", } for _, s := range info.SecurityOptions { if s.Key == "Name" { old.SecurityOptions = append(old.SecurityOptions, s.Value) } } return httputils.WriteJSON(w, http.StatusOK, old) } return httputils.WriteJSON(w, http.StatusOK, info) }
func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { // If contentLength is -1, we can assumed chunked encoding // or more technically that the length is unknown // https://golang.org/src/pkg/net/http/request.go#L139 // net/http otherwise seems to swallow any headers related to chunked encoding // including r.TransferEncoding // allow a nil body for backwards compatibility version := httputils.VersionFromContext(ctx) var hostConfig *container.HostConfig // A non-nil json object is at least 7 characters. if r.ContentLength > 7 || r.ContentLength == -1 { if versions.GreaterThanOrEqualTo(version, "1.24") { return validationError{fmt.Errorf("starting container with non-empty request body was deprecated since v1.10 and removed in v1.12")} } if err := httputils.CheckForJSON(r); err != nil { return err } c, err := s.decoder.DecodeHostConfig(r.Body) if err != nil { return err } hostConfig = c } validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname); err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } version := httputils.VersionFromContext(ctx) var updateConfig container.UpdateConfig decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&updateConfig); err != nil { return err } hostConfig := &container.HostConfig{ Resources: updateConfig.Resources, RestartPolicy: updateConfig.RestartPolicy, } name := vars["name"] validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") resp, err := s.backend.ContainerUpdate(name, hostConfig, validateHostname) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, resp) }
func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } stream := httputils.BoolValueOrDefault(r, "stream", true) var out io.Writer if !stream { w.Header().Set("Content-Type", "application/json") out = w } else { out = ioutils.NewWriteFlusher(w) } var closeNotifier <-chan bool if notifier, ok := w.(http.CloseNotifier); ok { closeNotifier = notifier.CloseNotify() } config := &daemon.ContainerStatsConfig{ Stream: stream, OutStream: out, Stop: closeNotifier, Version: httputils.VersionFromContext(ctx), } return s.daemon.ContainerStats(vars["name"], config) }
func TestVersionMiddlewareWithErrors(t *testing.T) { handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if httputils.VersionFromContext(ctx) == "" { t.Fatalf("Expected version, got empty string") } return nil } h := versionMiddleware(handler) req, _ := http.NewRequest("GET", "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() vars := map[string]string{"version": "0.1"} err := h(ctx, resp, req, vars) if derr, ok := err.(errcode.Error); !ok || derr.ErrorCode() != errors.ErrorCodeOldClientVersion { t.Fatalf("Expected ErrorCodeOldClientVersion, got %v", err) } vars["version"] = "100000" err = h(ctx, resp, req, vars) if derr, ok := err.(errcode.Error); !ok || derr.ErrorCode() != errors.ErrorCodeNewerClientVersion { t.Fatalf("Expected ErrorCodeNewerClientVersion, got %v", err) } }
func TestMiddlewares(t *testing.T) { cfg := &Config{ Version: "0.1omega2", } srv := &Server{ cfg: cfg, } srv.UseMiddleware(middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, api.MinVersion)) req, _ := http.NewRequest("GET", "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() localHandler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if httputils.VersionFromContext(ctx) == "" { t.Fatalf("Expected version, got empty string") } if sv := w.Header().Get("Server"); !strings.Contains(sv, "Docker/0.1omega2") { t.Fatalf("Expected server version in the header `Docker/0.1omega2`, got %s", sv) } return nil } handlerFunc := srv.handlerWithGlobalMiddlewares(localHandler) if err := handlerFunc(ctx, resp, req, map[string]string{}); err != nil { t.Fatal(err) } }
func processConfigRemoveip(cr *containerRouter, ctx context.Context, name string) error { version := httputils.VersionFromContext(ctx) config, ok := cr.inspectContainer(ctx, name, version) if !ok { return fmt.Errorf("can't find the container") } return ipRemove(config) }
// getContainersByName inspects containers configuration and serializes it as json. func (s *containerRouter) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { displaySize := httputils.BoolValue(r, "size") version := httputils.VersionFromContext(ctx) json, err := s.backend.ContainerInspect(vars["name"], displaySize, version) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, json) }
func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } cname := r.Form.Get("container") pause := httputils.BoolValue(r, "pause") version := httputils.VersionFromContext(ctx) if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { pause = true } c, _, _, err := runconfig.DecodeContainerConfig(r.Body) if err != nil && err != io.EOF { //Do not fail if body is empty. return err } if c == nil { c = &container.Config{} } if !s.daemon.Exists(cname) { return derr.ErrorCodeNoSuchContainer.WithArgs(cname) } newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"]) if err != nil { return err } commitCfg := &types.ContainerCommitConfig{ Pause: pause, Repo: r.Form.Get("repo"), Tag: r.Form.Get("tag"), Author: r.Form.Get("author"), Comment: r.Form.Get("comment"), Config: newConfig, MergeConfigs: true, } imgID, err := s.daemon.Commit(cname, commitCfg) if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ ID: string(imgID), }) }
func (c *containerAdapter) create(ctx context.Context) error { var cr containertypes.ContainerCreateCreatedBody var err error version := httputils.VersionFromContext(ctx) validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") if cr, err = c.backend.CreateManagedContainer(types.ContainerCreateConfig{ Name: c.container.name(), Config: c.container.config(), HostConfig: c.container.hostConfig(), // Use the first network in container create NetworkingConfig: c.container.createNetworkingConfig(), }, validateHostname); err != nil { return err } // Docker daemon currently doesn't support multiple networks in container create // Connect to all other networks nc := c.container.connectNetworkingConfig() if nc != nil { for n, ep := range nc.EndpointsConfig { if err := c.backend.ConnectContainerToNetwork(cr.ID, n, ep); err != nil { return err } } } container := c.container.task.Spec.GetContainer() if container == nil { return fmt.Errorf("unable to get container from task spec") } // configure secrets if err := c.backend.SetContainerSecretStore(cr.ID, c.secrets); err != nil { return err } refs := convert.SecretReferencesFromGRPC(container.Secrets) if err := c.backend.SetContainerSecretReferences(cr.ID, refs); err != nil { return err } if err := c.backend.UpdateContainerServiceConfig(cr.ID, c.container.serviceConfig()); err != nil { return err } return nil }
func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } cname := r.Form.Get("container") pause := httputils.BoolValue(r, "pause") version := httputils.VersionFromContext(ctx) if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") { pause = true } c, _, _, err := s.decoder.DecodeConfig(r.Body) if err != nil && err != io.EOF { //Do not fail if body is empty. return err } if c == nil { c = &container.Config{} } commitCfg := &backend.ContainerCommitConfig{ ContainerCommitConfig: types.ContainerCommitConfig{ Pause: pause, Repo: r.Form.Get("repo"), Tag: r.Form.Get("tag"), Author: r.Form.Get("author"), Comment: r.Form.Get("comment"), Config: c, MergeConfigs: true, }, Changes: r.Form["changes"], } imgID, err := s.backend.Commit(cname, commitCfg) if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ ID: string(imgID), }) }
func TestVersionMiddleware(t *testing.T) { handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if httputils.VersionFromContext(ctx) == "" { t.Fatalf("Expected version, got empty string") } return nil } h := versionMiddleware(handler) req, _ := http.NewRequest("GET", "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() if err := h(ctx, resp, req, map[string]string{}); err != nil { t.Fatal(err) } }
func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } stream := httputils.BoolValueOrDefault(r, "stream", true) if !stream { w.Header().Set("Content-Type", "application/json") } config := &backend.ContainerStatsConfig{ Stream: stream, OutStream: w, Version: string(httputils.VersionFromContext(ctx)), } return s.backend.ContainerStats(ctx, vars["name"], config) }
func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { info, err := s.backend.SystemInfo() if err != nil { return err } if s.clusterProvider != nil { info.Swarm = s.clusterProvider.Info() } if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") { // TODO: handle this conversion in engine-api type oldInfo struct { *types.Info ExecutionDriver string } return httputils.WriteJSON(w, http.StatusOK, &oldInfo{Info: info, ExecutionDriver: "<not supported>"}) } return httputils.WriteJSON(w, http.StatusOK, info) }
// postContainersCopy is deprecated in favor of getContainersArchive. func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { // Deprecated since 1.8, Errors out since 1.12 version := httputils.VersionFromContext(ctx) if versions.GreaterThanOrEqualTo(version, "1.24") { w.WriteHeader(http.StatusNotFound) return nil } if err := httputils.CheckForJSON(r); err != nil { return err } cfg := types.CopyConfig{} if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { return err } if cfg.Resource == "" { return fmt.Errorf("Path cannot be empty") } data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource) if err != nil { if strings.Contains(strings.ToLower(err.Error()), "no such container") { w.WriteHeader(http.StatusNotFound) return nil } if os.IsNotExist(err) { return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"]) } return err } defer data.Close() w.Header().Set("Content-Type", "application/x-tar") if _, err := io.Copy(w, data); err != nil { return err } return nil }
func TestMiddlewares(t *testing.T) { cfg := &Config{} srv := &Server{ cfg: cfg, } req, _ := http.NewRequest("GET", "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() localHandler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if httputils.VersionFromContext(ctx) == "" { t.Fatalf("Expected version, got empty string") } return nil } handlerFunc := srv.handleWithGlobalMiddlewares(localHandler) if err := handlerFunc(ctx, resp, req, map[string]string{}); err != nil { t.Fatal(err) } }
func (s *router) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v := &types.Version{ Version: dockerversion.VERSION, APIVersion: api.Version, GitCommit: dockerversion.GITCOMMIT, GoVersion: runtime.Version(), Os: runtime.GOOS, Arch: runtime.GOARCH, BuildTime: dockerversion.BUILDTIME, } version := httputils.VersionFromContext(ctx) if version.GreaterThanOrEqualTo("1.19") { v.Experimental = utils.ExperimentalBuild() } if kernelVersion, err := kernel.GetKernelVersion(); err == nil { v.KernelVersion = kernelVersion.String() } return httputils.WriteJSON(w, http.StatusOK, v) }
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } imageFilters, err := filters.FromParam(r.Form.Get("filters")) if err != nil { return err } version := httputils.VersionFromContext(ctx) filterParam := r.Form.Get("filter") if versions.LessThan(version, "1.28") && filterParam != "" { imageFilters.Add("reference", filterParam) } images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, images) }
// getContainersByName inspects containers configuration and serializes it as json. func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { displaySize := httputils.BoolValue(r, "size") var json interface{} var err error version := httputils.VersionFromContext(ctx) switch { case version.LessThan("1.20"): json, err = s.daemon.ContainerInspectPre120(vars["name"]) case version.Equal("1.20"): json, err = s.daemon.ContainerInspect120(vars["name"]) default: json, err = s.daemon.ContainerInspect(vars["name"], displaySize) } if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, json) }
func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } name := r.Form.Get("name") config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) if err != nil { return err } version := httputils.VersionFromContext(ctx) adjustCPUShares := version.LessThan("1.19") ccr, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares) if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, ccr) }
func (c *containerAdapter) start(ctx context.Context) error { version := httputils.VersionFromContext(ctx) validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") return c.backend.ContainerStart(c.container.name(), nil, validateHostname) }
func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( authConfigs = map[string]types.AuthConfig{} authConfigsEncoded = r.Header.Get("X-Registry-Config") buildConfig = &dockerfile.Config{} notVerboseBuffer = bytes.NewBuffer(nil) ) if authConfigsEncoded != "" { authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting // to be empty. } } w.Header().Set("Content-Type", "application/json") version := httputils.VersionFromContext(ctx) output := ioutils.NewWriteFlusher(w) defer output.Close() sf := streamformatter.NewJSONStreamFormatter() errf := func(err error) error { if !buildConfig.Verbose && notVerboseBuffer.Len() > 0 { output.Write(notVerboseBuffer.Bytes()) } // Do not write the error in the http output if it's still empty. // This prevents from writing a 200(OK) when there is an internal error. if !output.Flushed() { return err } _, err = w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err)))) if err != nil { logrus.Warnf("could not write error response: %v", err) } return nil } if httputils.BoolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else { buildConfig.Remove = httputils.BoolValue(r, "rm") } if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { buildConfig.Pull = true } repoAndTags, err := sanitizeRepoAndTags(r.Form["t"]) if err != nil { return errf(err) } buildConfig.DockerfileName = r.FormValue("dockerfile") buildConfig.Verbose = !httputils.BoolValue(r, "q") buildConfig.UseCache = !httputils.BoolValue(r, "nocache") buildConfig.ForceRemove = httputils.BoolValue(r, "forcerm") buildConfig.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") buildConfig.Memory = httputils.Int64ValueOrZero(r, "memory") buildConfig.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") buildConfig.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") buildConfig.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") buildConfig.CPUSetCpus = r.FormValue("cpusetcpus") buildConfig.CPUSetMems = r.FormValue("cpusetmems") buildConfig.CgroupParent = r.FormValue("cgroupparent") if r.Form.Get("shmsize") != "" { shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) if err != nil { return errf(err) } buildConfig.ShmSize = &shmSize } if i := container.IsolationLevel(r.FormValue("isolation")); i != "" { if !container.IsolationLevel.IsValid(i) { return errf(fmt.Errorf("Unsupported isolation: %q", i)) } buildConfig.Isolation = i } var buildUlimits = []*ulimit.Ulimit{} ulimitsJSON := r.FormValue("ulimits") if ulimitsJSON != "" { if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil { return errf(err) } buildConfig.Ulimits = buildUlimits } var buildArgs = map[string]string{} buildArgsJSON := r.FormValue("buildargs") if buildArgsJSON != "" { if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil { return errf(err) } buildConfig.BuildArgs = buildArgs } remoteURL := r.FormValue("remote") // Currently, only used if context is from a remote url. // Look at code in DetectContextFromRemoteURL for more information. createProgressReader := func(in io.ReadCloser) io.ReadCloser { progressOutput := sf.NewProgressOutput(output, true) if !buildConfig.Verbose { progressOutput = sf.NewProgressOutput(notVerboseBuffer, true) } return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL) } var ( context builder.ModifiableContext dockerfileName string ) context, dockerfileName, err = daemonbuilder.DetectContextFromRemoteURL(r.Body, remoteURL, createProgressReader) if err != nil { return errf(err) } defer func() { if err := context.Close(); err != nil { logrus.Debugf("[BUILDER] failed to remove temporary context: %v", err) } }() uidMaps, gidMaps := br.backend.GetUIDGIDMaps() defaultArchiver := &archive.Archiver{ Untar: chrootarchive.Untar, UIDMaps: uidMaps, GIDMaps: gidMaps, } docker := &daemonbuilder.Docker{ Daemon: br.backend, OutOld: output, AuthConfigs: authConfigs, Archiver: defaultArchiver, } if !buildConfig.Verbose { docker.OutOld = notVerboseBuffer } b, err := dockerfile.NewBuilder(buildConfig, docker, builder.DockerIgnoreContext{ModifiableContext: context}, nil) if err != nil { return errf(err) } b.Stdout = &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf} b.Stderr = &streamformatter.StderrFormatter{Writer: output, StreamFormatter: sf} if !buildConfig.Verbose { b.Stdout = &streamformatter.StdoutFormatter{Writer: notVerboseBuffer, StreamFormatter: sf} b.Stderr = &streamformatter.StderrFormatter{Writer: notVerboseBuffer, StreamFormatter: sf} } if closeNotifier, ok := w.(http.CloseNotifier); ok { finished := make(chan struct{}) defer close(finished) go func() { select { case <-finished: case <-closeNotifier.CloseNotify(): logrus.Infof("Client disconnected, cancelling job: build") b.Cancel() } }() } if len(dockerfileName) > 0 { b.DockerfileName = dockerfileName } imgID, err := b.Build() if err != nil { return errf(err) } for _, rt := range repoAndTags { if err := br.backend.TagImage(rt, imgID); err != nil { return errf(err) } } // Everything worked so if -q was provided the output from the daemon // should be just the image ID and we'll print that to stdout. if !buildConfig.Verbose { stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf} fmt.Fprintf(stdout, "%s\n", string(imgID)) } return nil }