summaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/handlers/compat/events.go22
-rw-r--r--pkg/api/handlers/compat/images_build.go258
-rw-r--r--pkg/api/handlers/compat/ping.go4
-rw-r--r--pkg/api/handlers/compat/version.go8
-rw-r--r--pkg/api/handlers/utils/handler.go4
-rw-r--r--pkg/api/server/handler_api.go4
-rw-r--r--pkg/api/server/register_archive.go6
7 files changed, 171 insertions, 135 deletions
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go
index 289bf4a2d..fbb33410f 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"net/http"
- "sync"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/events"
@@ -113,8 +112,13 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
errorChannel <- runtime.Events(r.Context(), readOpts)
}()
- var coder *jsoniter.Encoder
- var writeHeader sync.Once
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
+ }
+ coder := json.NewEncoder(w)
+ coder.SetEscapeHTML(true)
for stream := true; stream; stream = query.Stream {
select {
@@ -124,18 +128,6 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
return
}
case evt := <-eventChannel:
- writeHeader.Do(func() {
- // Use a sync.Once so that we write the header
- // only once.
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
- }
- coder = json.NewEncoder(w)
- coder.SetEscapeHTML(true)
- })
-
if evt == nil {
continue
}
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 9601f5e18..fbaf8d10a 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -1,7 +1,7 @@
package compat
import (
- "bytes"
+ "context"
"encoding/base64"
"encoding/json"
"fmt"
@@ -18,8 +18,10 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/channel"
"github.com/containers/storage/pkg/archive"
"github.com/gorilla/schema"
+ "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -47,12 +49,25 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
}
- anchorDir, err := extractTarFile(r)
+ contextDirectory, err := extractTarFile(r)
if err != nil {
utils.InternalServerError(w, err)
return
}
- defer os.RemoveAll(anchorDir)
+
+ defer func() {
+ if logrus.IsLevelEnabled(logrus.DebugLevel) {
+ if v, found := os.LookupEnv("PODMAN_RETAIN_BUILD_ARTIFACT"); found {
+ if keep, _ := strconv.ParseBool(v); keep {
+ return
+ }
+ }
+ }
+ err := os.RemoveAll(filepath.Dir(contextDirectory))
+ if err != nil {
+ logrus.Warn(errors.Wrapf(err, "failed to remove build scratch directory %q", filepath.Dir(contextDirectory)))
+ }
+ }()
query := struct {
Dockerfile string `schema:"dockerfile"`
@@ -67,10 +82,10 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
ForceRm bool `schema:"forcerm"`
Memory int64 `schema:"memory"`
MemSwap int64 `schema:"memswap"`
- CpuShares uint64 `schema:"cpushares"` //nolint
- CpuSetCpus string `schema:"cpusetcpus"` //nolint
- CpuPeriod uint64 `schema:"cpuperiod"` //nolint
- CpuQuota int64 `schema:"cpuquota"` //nolint
+ CpuShares uint64 `schema:"cpushares"` // nolint
+ CpuSetCpus string `schema:"cpusetcpus"` // nolint
+ CpuPeriod uint64 `schema:"cpuperiod"` // nolint
+ CpuQuota int64 `schema:"cpuquota"` // nolint
BuildArgs string `schema:"buildargs"`
ShmSize int `schema:"shmsize"`
Squash bool `schema:"squash"`
@@ -81,52 +96,32 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Outputs string `schema:"outputs"`
Registry string `schema:"registry"`
}{
- Dockerfile: "Dockerfile",
- Tag: []string{},
- ExtraHosts: "",
- Remote: "",
- Quiet: false,
- NoCache: false,
- CacheFrom: "",
- Pull: false,
- Rm: true,
- ForceRm: false,
- Memory: 0,
- MemSwap: 0,
- CpuShares: 0,
- CpuSetCpus: "",
- CpuPeriod: 0,
- CpuQuota: 0,
- BuildArgs: "",
- ShmSize: 64 * 1024 * 1024,
- Squash: false,
- Labels: "",
- NetworkMode: "",
- Platform: "",
- Target: "",
- Outputs: "",
- Registry: "docker.io",
+ Dockerfile: "Dockerfile",
+ Tag: []string{},
+ Rm: true,
+ ShmSize: 64 * 1024 * 1024,
+ Registry: "docker.io",
}
+
decoder := r.Context().Value("decoder").(*schema.Decoder)
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
return
}
- var (
- output string
- additionalNames []string
- )
+ var output string
if len(query.Tag) > 0 {
output = query.Tag[0]
}
+ if _, found := r.URL.Query()["target"]; found {
+ output = query.Target
+ }
+
+ var additionalNames []string
if len(query.Tag) > 1 {
additionalNames = query.Tag[1:]
}
- if _, found := r.URL.Query()["target"]; found {
- output = query.Target
- }
var buildArgs = map[string]string{}
if _, found := r.URL.Query()["buildargs"]; found {
if err := json.Unmarshal([]byte(query.BuildArgs), &buildArgs); err != nil {
@@ -156,95 +151,136 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
}
- // build events will be recorded here
- var (
- buildEvents = []string{}
- progress = bytes.Buffer{}
- )
+ // Channels all mux'ed in select{} below to follow API build protocol
+ stdout := channel.NewWriter(make(chan []byte, 1))
+ defer stdout.Close()
+
+ auxout := channel.NewWriter(make(chan []byte, 1))
+ defer auxout.Close()
+
+ stderr := channel.NewWriter(make(chan []byte, 1))
+ defer stderr.Close()
+
+ reporter := channel.NewWriter(make(chan []byte, 1))
+ defer reporter.Close()
buildOptions := imagebuildah.BuildOptions{
- ContextDirectory: filepath.Join(anchorDir, "build"),
+ ContextDirectory: contextDirectory,
PullPolicy: pullPolicy,
Registry: query.Registry,
IgnoreUnrecognizedInstructions: true,
Quiet: query.Quiet,
Isolation: buildah.IsolationChroot,
- Runtime: "",
- RuntimeArgs: nil,
- TransientMounts: nil,
Compression: archive.Gzip,
Args: buildArgs,
Output: output,
AdditionalTags: additionalNames,
- Log: func(format string, args ...interface{}) {
- buildEvents = append(buildEvents, fmt.Sprintf(format, args...))
- },
- In: nil,
- Out: &progress,
- Err: &progress,
- SignaturePolicyPath: "",
- ReportWriter: &progress,
- OutputFormat: buildah.Dockerv2ImageManifest,
- SystemContext: nil,
- NamespaceOptions: nil,
- ConfigureNetwork: 0,
- CNIPluginPath: "",
- CNIConfigDir: "",
- IDMappingOptions: nil,
- AddCapabilities: nil,
- DropCapabilities: nil,
+ Out: stdout,
+ Err: auxout,
+ ReportWriter: reporter,
+ OutputFormat: buildah.Dockerv2ImageManifest,
CommonBuildOpts: &buildah.CommonBuildOptions{
- AddHost: nil,
- CgroupParent: "",
- CPUPeriod: query.CpuPeriod,
- CPUQuota: query.CpuQuota,
- CPUShares: query.CpuShares,
- CPUSetCPUs: query.CpuSetCpus,
- CPUSetMems: "",
- HTTPProxy: false,
- Memory: query.Memory,
- DNSSearch: nil,
- DNSServers: nil,
- DNSOptions: nil,
- MemorySwap: query.MemSwap,
- LabelOpts: nil,
- SeccompProfilePath: "",
- ApparmorProfile: "",
- ShmSize: strconv.Itoa(query.ShmSize),
- Ulimit: nil,
- Volumes: nil,
+ CPUPeriod: query.CpuPeriod,
+ CPUQuota: query.CpuQuota,
+ CPUShares: query.CpuShares,
+ CPUSetCPUs: query.CpuSetCpus,
+ Memory: query.Memory,
+ MemorySwap: query.MemSwap,
+ ShmSize: strconv.Itoa(query.ShmSize),
},
- DefaultMountsFilePath: "",
- IIDFile: "",
Squash: query.Squash,
Labels: labels,
- Annotations: nil,
- OnBuild: nil,
- Layers: false,
NoCache: query.NoCache,
RemoveIntermediateCtrs: query.Rm,
ForceRmIntermediateCtrs: query.ForceRm,
- BlobDirectory: "",
Target: query.Target,
- Devices: nil,
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- id, _, err := runtime.Build(r.Context(), buildOptions, query.Dockerfile)
- if err != nil {
- utils.InternalServerError(w, err)
- return
+ runCtx, cancel := context.WithCancel(context.Background())
+ var imageID string
+ go func() {
+ defer cancel()
+ imageID, _, err = runtime.Build(r.Context(), buildOptions, query.Dockerfile)
+ if err != nil {
+ stderr.Write([]byte(err.Error() + "\n"))
+ }
+ }()
+
+ flush := func() {
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
+ }
}
- // Find image ID that was built...
- utils.WriteResponse(w, http.StatusOK,
- struct {
- Stream string `json:"stream"`
- }{
- Stream: progress.String() + "\n" +
- strings.Join(buildEvents, "\n") +
- fmt.Sprintf("\nSuccessfully built %s\n", id),
- })
+ // Send headers and prime client for stream to come
+ w.WriteHeader(http.StatusOK)
+ w.Header().Add("Content-Type", "application/json")
+ flush()
+
+ var failed bool
+
+ body := w.(io.Writer)
+ if logrus.IsLevelEnabled(logrus.DebugLevel) {
+ if v, found := os.LookupEnv("PODMAN_RETAIN_BUILD_ARTIFACT"); found {
+ if keep, _ := strconv.ParseBool(v); keep {
+ t, _ := ioutil.TempFile("", "build_*_server")
+ defer t.Close()
+ body = io.MultiWriter(t, w)
+ }
+ }
+ }
+
+ enc := json.NewEncoder(body)
+ enc.SetEscapeHTML(true)
+loop:
+ for {
+ m := struct {
+ Stream string `json:"stream,omitempty"`
+ Error string `json:"error,omitempty"`
+ }{}
+
+ select {
+ case e := <-stdout.Chan():
+ m.Stream = string(e)
+ if err := enc.Encode(m); err != nil {
+ stderr.Write([]byte(err.Error()))
+ }
+ flush()
+ case e := <-auxout.Chan():
+ m.Stream = string(e)
+ if err := enc.Encode(m); err != nil {
+ stderr.Write([]byte(err.Error()))
+ }
+ flush()
+ case e := <-reporter.Chan():
+ m.Stream = string(e)
+ if err := enc.Encode(m); err != nil {
+ stderr.Write([]byte(err.Error()))
+ }
+ flush()
+ case e := <-stderr.Chan():
+ failed = true
+ m.Error = string(e)
+ if err := enc.Encode(m); err != nil {
+ logrus.Warnf("Failed to json encode error %q", err.Error())
+ }
+ flush()
+ case <-runCtx.Done():
+ if !failed {
+ if utils.IsLibpodRequest(r) {
+ m.Stream = imageID
+ } else {
+ m.Stream = fmt.Sprintf("Successfully built %12.12s\n", imageID)
+ }
+ if err := enc.Encode(m); err != nil {
+ logrus.Warnf("Failed to json encode error %q", err.Error())
+ }
+ flush()
+ }
+ break loop
+ }
+ }
}
func extractTarFile(r *http.Request) (string, error) {
@@ -253,10 +289,9 @@ func extractTarFile(r *http.Request) (string, error) {
if err != nil {
return "", err
}
- buildDir := filepath.Join(anchorDir, "build")
path := filepath.Join(anchorDir, "tarBall")
- tarBall, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ tarBall, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return "", err
}
@@ -265,14 +300,17 @@ func extractTarFile(r *http.Request) (string, error) {
// Content-Length not used as too many existing API clients didn't honor it
_, err = io.Copy(tarBall, r.Body)
r.Body.Close()
-
if err != nil {
return "", fmt.Errorf("failed Request: Unable to copy tar file from request body %s", r.RequestURI)
}
- _, _ = tarBall.Seek(0, 0)
- if err := archive.Untar(tarBall, buildDir, &archive.TarOptions{}); err != nil {
+ buildDir := filepath.Join(anchorDir, "build")
+ err = os.Mkdir(buildDir, 0700)
+ if err != nil {
return "", err
}
- return anchorDir, nil
+
+ _, _ = tarBall.Seek(0, 0)
+ err = archive.Untar(tarBall, buildDir, nil)
+ return buildDir, err
}
diff --git a/pkg/api/handlers/compat/ping.go b/pkg/api/handlers/compat/ping.go
index eb7eed5b6..06150bb63 100644
--- a/pkg/api/handlers/compat/ping.go
+++ b/pkg/api/handlers/compat/ping.go
@@ -5,7 +5,6 @@ import (
"net/http"
"github.com/containers/buildah"
- "github.com/containers/podman/v2/pkg/api/handlers/utils"
)
// Ping returns headers to client about the service
@@ -14,13 +13,12 @@ import (
// Clients will use the Header availability to test which backend engine is in use.
// Note: Additionally handler supports GET and HEAD methods
func Ping(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("API-Version", utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion].String())
+ // Note API-Version and Libpod-API-Version are set in handler_api.go
w.Header().Set("BuildKit-Version", "")
w.Header().Set("Docker-Experimental", "true")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Pragma", "no-cache")
- w.Header().Set("Libpod-API-Version", utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String())
w.Header().Set("Libpod-Buildha-Version", buildah.Version)
w.WriteHeader(http.StatusOK)
diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go
index e12c7cefa..92900b75d 100644
--- a/pkg/api/handlers/compat/version.go
+++ b/pkg/api/handlers/compat/version.go
@@ -30,6 +30,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to obtain system memory info"))
return
}
+
components := []docker.ComponentVersion{{
Name: "Podman Engine",
Version: versionInfo.Version,
@@ -46,6 +47,9 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
},
}}
+ apiVersion := utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion]
+ minVersion := utils.APIVersion[utils.CompatTree][utils.MinimalAPIVersion]
+
utils.WriteResponse(w, http.StatusOK, entities.ComponentVersion{
Version: docker.Version{
Platform: struct {
@@ -53,7 +57,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
}{
Name: fmt.Sprintf("%s/%s/%s-%s", goRuntime.GOOS, goRuntime.GOARCH, infoData.Host.Distribution.Distribution, infoData.Host.Distribution.Version),
},
- APIVersion: components[0].Details["APIVersion"],
+ APIVersion: fmt.Sprintf("%d.%d", apiVersion.Major, apiVersion.Minor),
Arch: components[0].Details["Arch"],
BuildTime: components[0].Details["BuildTime"],
Components: components,
@@ -61,7 +65,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
GitCommit: components[0].Details["GitCommit"],
GoVersion: components[0].Details["GoVersion"],
KernelVersion: components[0].Details["KernelVersion"],
- MinAPIVersion: components[0].Details["MinAPIVersion"],
+ MinAPIVersion: fmt.Sprintf("%d.%d", minVersion.Major, minVersion.Minor),
Os: components[0].Details["Os"],
Version: components[0].Version,
}})
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 62fdc05dd..517dccad0 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -43,8 +43,8 @@ var (
// clients to shop for the Version they wish to support
APIVersion = map[VersionTree]map[VersionLevel]semver.Version{
LibpodTree: {
- CurrentAPIVersion: semver.MustParse("1.0.0"),
- MinimalAPIVersion: semver.MustParse("1.0.0"),
+ CurrentAPIVersion: semver.MustParse("2.0.0"),
+ MinimalAPIVersion: semver.MustParse("2.0.0"),
},
CompatTree: {
CurrentAPIVersion: semver.MustParse("1.40.0"),
diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go
index e47b66bb4..f2ce0301b 100644
--- a/pkg/api/server/handler_api.go
+++ b/pkg/api/server/handler_api.go
@@ -40,6 +40,10 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc {
c = context.WithValue(c, "idletracker", s.idleTracker) //nolint
r = r.WithContext(c)
+ v := utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion]
+ w.Header().Set("API-Version", fmt.Sprintf("%d.%d", v.Major, v.Minor))
+ w.Header().Set("Libpod-API-Version", utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String())
+
h(w, r)
}
fn(w, r)
diff --git a/pkg/api/server/register_archive.go b/pkg/api/server/register_archive.go
index 5931c2fc9..b2d2543c4 100644
--- a/pkg/api/server/register_archive.go
+++ b/pkg/api/server/register_archive.go
@@ -9,7 +9,7 @@ import (
)
func (s *APIServer) registerAchiveHandlers(r *mux.Router) error {
- // swagger:operation POST /containers/{name}/archive compat putArchive
+ // swagger:operation PUT /containers/{name}/archive compat putArchive
// ---
// summary: Put files into a container
// description: Put a tar archive of files into a container
@@ -84,9 +84,9 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchContainer"
// 500:
// $ref: "#/responses/InternalError"
- r.HandleFunc(VersionedPath("/containers/{name}/archive"), s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPost)
+ r.HandleFunc(VersionedPath("/containers/{name}/archive"), s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPut, http.MethodHead)
// Added non version path to URI to support docker non versioned paths
- r.HandleFunc("/containers/{name}/archive", s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPost)
+ r.HandleFunc("/containers/{name}/archive", s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPut, http.MethodHead)
/*
Libpod