mirror of
https://github.com/containers/podman.git
synced 2025-05-31 07:27:13 +08:00

* Add swagger annotations for all the query and response parameters for buildimages * Improve populating the BuildOptions struct * Improve swagger.json generation, removing tags.xml and move tag definiation into the swagger:meta block * Update Makefile to be more robust, added target for validation * TODO once validation passes add that step to the generation step Signed-off-by: Jhon Honce <jhonce@redhat.com>
269 lines
7.4 KiB
Go
269 lines
7.4 KiB
Go
package handlers
|
|
|
|
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/pkg/api/handlers/utils"
|
|
"github.com/containers/storage/pkg/archive"
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
func BuildImage(w http.ResponseWriter, r *http.Request) {
|
|
authConfigs := map[string]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",
|
|
}
|
|
|
|
if err := decodeQuery(r, &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 t, found := mux.Vars(r)["target"]; found {
|
|
name = t
|
|
}
|
|
|
|
var buildArgs = map[string]string{}
|
|
if a, found := mux.Vars(r)["buildargs"]; found {
|
|
if err := json.Unmarshal([]byte(a), &buildArgs); err != nil {
|
|
utils.BadRequest(w, "buildargs", a, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// convert label formats
|
|
var labels = []string{}
|
|
if l, found := mux.Vars(r)["labels"]; found {
|
|
var m = map[string]string{}
|
|
if err := json.Unmarshal([]byte(l), &m); err != nil {
|
|
utils.BadRequest(w, "labels", l, err)
|
|
return
|
|
}
|
|
|
|
for k, v := range m {
|
|
labels = append(labels, k+"="+v)
|
|
}
|
|
}
|
|
|
|
pullPolicy := buildah.PullIfMissing
|
|
if _, found := mux.Vars(r)["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,
|
|
}
|
|
|
|
id, _, err := getRuntime(r).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
|
|
}
|