Files
podman/pkg/api/handlers/compat/images_build.go
Valentin Rothberg 8489dc4345 move go module to v2
With the advent of Podman 2.0.0 we crossed the magical barrier of go
modules.  While we were able to continue importing all packages inside
of the project, the project could not be vendored anymore from the
outside.

Move the go module to new major version and change all imports to
`github.com/containers/libpod/v2`.  The renaming of the imports
was done via `gomove` [1].

[1] https://github.com/KSubedi/gomove

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2020-07-06 15:50:12 +02:00

272 lines
7.7 KiB
Go

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/v2/libpod"
"github.com/containers/libpod/v2/pkg/api/handlers"
"github.com/containers/libpod/v2/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]))
return
}
}
anchorDir, err := extractTarFile(r)
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"` //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"`
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: []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",
}
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
)
if len(query.Tag) > 0 {
output = query.Tag[0]
}
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 {
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: 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,
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)
return
}
// 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) (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 {
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 {
return "", err
}
return anchorDir, nil
}