mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			272 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			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/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
 | |
| }
 |