summaryrefslogtreecommitdiff
path: root/pkg/api/handlers/compat/images_build.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api/handlers/compat/images_build.go')
-rw-r--r--pkg/api/handlers/compat/images_build.go271
1 files changed, 271 insertions, 0 deletions
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
new file mode 100644
index 000000000..e208e6ddc
--- /dev/null
+++ b/pkg/api/handlers/compat/images_build.go
@@ -0,0 +1,271 @@
+package compat
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/containers/buildah"
+ "github.com/containers/buildah/imagebuildah"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/storage/pkg/archive"
+ "github.com/gorilla/schema"
+)
+
+func BuildImage(w http.ResponseWriter, r *http.Request) {
+ authConfigs := map[string]handlers.AuthConfig{}
+ if hdr, found := r.Header["X-Registry-Config"]; found && len(hdr) > 0 {
+ authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(hdr[0]))
+ if json.NewDecoder(authConfigsJSON).Decode(&authConfigs) != nil {
+ utils.BadRequest(w, "X-Registry-Config", hdr[0], json.NewDecoder(authConfigsJSON).Decode(&authConfigs))
+ return
+ }
+ }
+
+ if hdr, found := r.Header["Content-Type"]; found && len(hdr) > 0 {
+ if hdr[0] != "application/x-tar" {
+ utils.BadRequest(w, "Content-Type", hdr[0],
+ fmt.Errorf("Content-Type: %s is not supported. Should be \"application/x-tar\"", hdr[0]))
+ }
+ }
+
+ anchorDir, err := extractTarFile(r, w)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ defer os.RemoveAll(anchorDir)
+
+ query := struct {
+ Dockerfile string `schema:"dockerfile"`
+ Tag string `schema:"t"`
+ ExtraHosts string `schema:"extrahosts"`
+ Remote string `schema:"remote"`
+ Quiet bool `schema:"q"`
+ NoCache bool `schema:"nocache"`
+ CacheFrom string `schema:"cachefrom"`
+ Pull bool `schema:"pull"`
+ Rm bool `schema:"rm"`
+ ForceRm bool `schema:"forcerm"`
+ Memory int64 `schema:"memory"`
+ MemSwap int64 `schema:"memswap"`
+ CpuShares uint64 `schema:"cpushares"`
+ CpuSetCpus string `schema:"cpusetcpus"`
+ CpuPeriod uint64 `schema:"cpuperiod"`
+ CpuQuota int64 `schema:"cpuquota"`
+ BuildArgs string `schema:"buildargs"`
+ ShmSize int `schema:"shmsize"`
+ Squash bool `schema:"squash"`
+ Labels string `schema:"labels"`
+ NetworkMode string `schema:"networkmode"`
+ Platform string `schema:"platform"`
+ Target string `schema:"target"`
+ Outputs string `schema:"outputs"`
+ Registry string `schema:"registry"`
+ }{
+ Dockerfile: "Dockerfile",
+ Tag: "",
+ 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",
+ }
+ 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 (
+ // Tag is the name with optional tag...
+ name = query.Tag
+ tag = "latest"
+ )
+ if strings.Contains(query.Tag, ":") {
+ tokens := strings.SplitN(query.Tag, ":", 2)
+ name = tokens[0]
+ tag = tokens[1]
+ }
+
+ if _, found := r.URL.Query()["target"]; found {
+ name = query.Target
+ }
+
+ var buildArgs = map[string]string{}
+ if _, found := r.URL.Query()["buildargs"]; found {
+ if err := json.Unmarshal([]byte(query.BuildArgs), &buildArgs); err != nil {
+ utils.BadRequest(w, "buildargs", query.BuildArgs, err)
+ return
+ }
+ }
+
+ // convert label formats
+ var labels = []string{}
+ if _, found := r.URL.Query()["labels"]; found {
+ var m = map[string]string{}
+ if err := json.Unmarshal([]byte(query.Labels), &m); err != nil {
+ utils.BadRequest(w, "labels", query.Labels, err)
+ return
+ }
+
+ for k, v := range m {
+ labels = append(labels, k+"="+v)
+ }
+ }
+
+ pullPolicy := buildah.PullIfMissing
+ if _, found := r.URL.Query()["pull"]; found {
+ if query.Pull {
+ pullPolicy = buildah.PullAlways
+ }
+ }
+
+ // build events will be recorded here
+ var (
+ buildEvents = []string{}
+ progress = bytes.Buffer{}
+ )
+
+ buildOptions := imagebuildah.BuildOptions{
+ ContextDirectory: filepath.Join(anchorDir, "build"),
+ PullPolicy: pullPolicy,
+ Registry: query.Registry,
+ IgnoreUnrecognizedInstructions: true,
+ Quiet: query.Quiet,
+ Isolation: buildah.IsolationChroot,
+ Runtime: "",
+ RuntimeArgs: nil,
+ TransientMounts: nil,
+ Compression: archive.Gzip,
+ Args: buildArgs,
+ Output: name,
+ AdditionalTags: []string{tag},
+ 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,
+ 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,
+ },
+ 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)
+ }
+
+ // 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),
+ })
+}
+
+func extractTarFile(r *http.Request, w http.ResponseWriter) (string, error) {
+ // build a home for the request body
+ anchorDir, err := ioutil.TempDir("", "libpod_builder")
+ 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)
+ if err != nil {
+ return "", err
+ }
+ defer tarBall.Close()
+
+ // 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 {
+ utils.InternalServerError(w,
+ 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 {
+ return "", err
+ }
+ return anchorDir, nil
+}