github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/api/handlers/compat/images_build.go (about)

     1  package compat
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"os"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/containers/buildah"
    17  	buildahDefine "github.com/containers/buildah/define"
    18  	"github.com/containers/buildah/pkg/parse"
    19  	"github.com/containers/image/v5/types"
    20  	"github.com/hanks177/podman/v4/libpod"
    21  	"github.com/hanks177/podman/v4/pkg/api/handlers/utils"
    22  	api "github.com/hanks177/podman/v4/pkg/api/types"
    23  	"github.com/hanks177/podman/v4/pkg/auth"
    24  	"github.com/hanks177/podman/v4/pkg/channel"
    25  	"github.com/hanks177/podman/v4/pkg/rootless"
    26  	"github.com/containers/storage/pkg/archive"
    27  	"github.com/docker/docker/pkg/jsonmessage"
    28  	"github.com/gorilla/schema"
    29  	"github.com/opencontainers/runtime-spec/specs-go"
    30  	"github.com/pkg/errors"
    31  	"github.com/sirupsen/logrus"
    32  )
    33  
    34  func BuildImage(w http.ResponseWriter, r *http.Request) {
    35  	if hdr, found := r.Header["Content-Type"]; found && len(hdr) > 0 {
    36  		contentType := hdr[0]
    37  		switch contentType {
    38  		case "application/tar":
    39  			logrus.Infof("tar file content type is  %s, should use \"application/x-tar\" content type", contentType)
    40  		case "application/x-tar":
    41  			break
    42  		default:
    43  			if utils.IsLibpodRequest(r) {
    44  				utils.BadRequest(w, "Content-Type", hdr[0],
    45  					fmt.Errorf("Content-Type: %s is not supported. Should be \"application/x-tar\"", hdr[0]))
    46  				return
    47  			}
    48  			logrus.Infof("tar file content type is  %s, should use \"application/x-tar\" content type", contentType)
    49  		}
    50  	}
    51  
    52  	contextDirectory, err := extractTarFile(r)
    53  	if err != nil {
    54  		utils.InternalServerError(w, err)
    55  		return
    56  	}
    57  
    58  	defer func() {
    59  		if logrus.IsLevelEnabled(logrus.DebugLevel) {
    60  			if v, found := os.LookupEnv("PODMAN_RETAIN_BUILD_ARTIFACT"); found {
    61  				if keep, _ := strconv.ParseBool(v); keep {
    62  					return
    63  				}
    64  			}
    65  		}
    66  		err := os.RemoveAll(filepath.Dir(contextDirectory))
    67  		if err != nil {
    68  			logrus.Warn(errors.Wrapf(err, "failed to remove build scratch directory %q", filepath.Dir(contextDirectory)))
    69  		}
    70  	}()
    71  
    72  	query := struct {
    73  		AddHosts                string   `schema:"extrahosts"`
    74  		AdditionalCapabilities  string   `schema:"addcaps"`
    75  		AdditionalBuildContexts string   `schema:"additionalbuildcontexts"`
    76  		AllPlatforms            bool     `schema:"allplatforms"`
    77  		Annotations             string   `schema:"annotations"`
    78  		AppArmor                string   `schema:"apparmor"`
    79  		BuildArgs               string   `schema:"buildargs"`
    80  		CacheFrom               string   `schema:"cachefrom"`
    81  		CgroupParent            string   `schema:"cgroupparent"` // nolint
    82  		Compression             uint64   `schema:"compression"`
    83  		ConfigureNetwork        string   `schema:"networkmode"`
    84  		CPPFlags                string   `schema:"cppflags"`
    85  		CpuPeriod               uint64   `schema:"cpuperiod"`  // nolint
    86  		CpuQuota                int64    `schema:"cpuquota"`   // nolint
    87  		CpuSetCpus              string   `schema:"cpusetcpus"` // nolint
    88  		CpuSetMems              string   `schema:"cpusetmems"` // nolint
    89  		CpuShares               uint64   `schema:"cpushares"`  // nolint
    90  		DNSOptions              string   `schema:"dnsoptions"`
    91  		DNSSearch               string   `schema:"dnssearch"`
    92  		DNSServers              string   `schema:"dnsservers"`
    93  		Devices                 string   `schema:"devices"`
    94  		Dockerfile              string   `schema:"dockerfile"`
    95  		DropCapabilities        string   `schema:"dropcaps"`
    96  		Envs                    []string `schema:"setenv"`
    97  		Excludes                string   `schema:"excludes"`
    98  		ForceRm                 bool     `schema:"forcerm"`
    99  		From                    string   `schema:"from"`
   100  		HTTPProxy               bool     `schema:"httpproxy"`
   101  		IdentityLabel           bool     `schema:"identitylabel"`
   102  		Ignore                  bool     `schema:"ignore"`
   103  		Isolation               string   `schema:"isolation"`
   104  		Jobs                    int      `schema:"jobs"` // nolint
   105  		LabelOpts               string   `schema:"labelopts"`
   106  		Labels                  string   `schema:"labels"`
   107  		Layers                  bool     `schema:"layers"`
   108  		LogRusage               bool     `schema:"rusage"`
   109  		Manifest                string   `schema:"manifest"`
   110  		MemSwap                 int64    `schema:"memswap"`
   111  		Memory                  int64    `schema:"memory"`
   112  		NamespaceOptions        string   `schema:"nsoptions"`
   113  		NoCache                 bool     `schema:"nocache"`
   114  		OSFeatures              []string `schema:"osfeature"`
   115  		OSVersion               string   `schema:"osversion"`
   116  		OutputFormat            string   `schema:"outputformat"`
   117  		Platform                []string `schema:"platform"`
   118  		Pull                    bool     `schema:"pull"`
   119  		PullPolicy              string   `schema:"pullpolicy"`
   120  		Quiet                   bool     `schema:"q"`
   121  		Registry                string   `schema:"registry"`
   122  		Rm                      bool     `schema:"rm"`
   123  		RusageLogFile           string   `schema:"rusagelogfile"`
   124  		Remote                  string   `schema:"remote"`
   125  		Seccomp                 string   `schema:"seccomp"`
   126  		Secrets                 string   `schema:"secrets"`
   127  		SecurityOpt             string   `schema:"securityopt"`
   128  		ShmSize                 int      `schema:"shmsize"`
   129  		Squash                  bool     `schema:"squash"`
   130  		TLSVerify               bool     `schema:"tlsVerify"`
   131  		Tags                    []string `schema:"t"`
   132  		Target                  string   `schema:"target"`
   133  		Timestamp               int64    `schema:"timestamp"`
   134  		Ulimits                 string   `schema:"ulimits"`
   135  		UnsetEnvs               []string `schema:"unsetenv"`
   136  	}{
   137  		Dockerfile:    "Dockerfile",
   138  		IdentityLabel: true,
   139  		Registry:      "docker.io",
   140  		Rm:            true,
   141  		ShmSize:       64 * 1024 * 1024,
   142  	}
   143  
   144  	decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
   145  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   146  		utils.Error(w, http.StatusBadRequest, err)
   147  		return
   148  	}
   149  
   150  	// if layers field not set assume its not from a valid podman-client
   151  	// could be a docker client, set `layers=true` since that is the default
   152  	// expected behaviour
   153  	if !utils.IsLibpodRequest(r) {
   154  		if _, found := r.URL.Query()["layers"]; !found {
   155  			query.Layers = true
   156  		}
   157  	}
   158  
   159  	// convert tag formats
   160  	tags := query.Tags
   161  
   162  	// convert addcaps formats
   163  	var addCaps = []string{}
   164  	if _, found := r.URL.Query()["addcaps"]; found {
   165  		var m = []string{}
   166  		if err := json.Unmarshal([]byte(query.AdditionalCapabilities), &m); err != nil {
   167  			utils.BadRequest(w, "addcaps", query.AdditionalCapabilities, err)
   168  			return
   169  		}
   170  		addCaps = m
   171  	}
   172  
   173  	// convert addcaps formats
   174  	containerFiles := []string{}
   175  	// Tells if query parameter `dockerfile` is set or not.
   176  	dockerFileSet := false
   177  	if utils.IsLibpodRequest(r) && query.Remote != "" {
   178  		// The context directory could be a URL.  Try to handle that.
   179  		anchorDir, err := ioutil.TempDir(parse.GetTempDir(), "libpod_builder")
   180  		if err != nil {
   181  			utils.InternalServerError(w, err)
   182  		}
   183  		tempDir, subDir, err := buildahDefine.TempDirForURL(anchorDir, "buildah", query.Remote)
   184  		if err != nil {
   185  			utils.InternalServerError(w, err)
   186  		}
   187  		if tempDir != "" {
   188  			// We had to download it to a temporary directory.
   189  			// Delete it later.
   190  			defer func() {
   191  				if err = os.RemoveAll(tempDir); err != nil {
   192  					// We are deleting this on server so log on server end
   193  					// client does not have to worry about server cleanup.
   194  					logrus.Errorf("Cannot delete downloaded temp dir %q: %s", tempDir, err)
   195  				}
   196  			}()
   197  			contextDirectory = filepath.Join(tempDir, subDir)
   198  		} else {
   199  			// Nope, it was local.  Use it as is.
   200  			absDir, err := filepath.Abs(query.Remote)
   201  			if err != nil {
   202  				utils.BadRequest(w, "remote", query.Remote, err)
   203  			}
   204  			contextDirectory = absDir
   205  		}
   206  	} else {
   207  		if _, found := r.URL.Query()["dockerfile"]; found {
   208  			var m = []string{}
   209  			if err := json.Unmarshal([]byte(query.Dockerfile), &m); err != nil {
   210  				// it's not json, assume just a string
   211  				m = []string{filepath.Join(contextDirectory, query.Dockerfile)}
   212  			}
   213  			containerFiles = m
   214  			dockerFileSet = true
   215  		}
   216  	}
   217  
   218  	if !dockerFileSet {
   219  		containerFiles = []string{filepath.Join(contextDirectory, "Dockerfile")}
   220  		if utils.IsLibpodRequest(r) {
   221  			containerFiles = []string{filepath.Join(contextDirectory, "Containerfile")}
   222  			if _, err = os.Stat(containerFiles[0]); err != nil {
   223  				containerFiles = []string{filepath.Join(contextDirectory, "Dockerfile")}
   224  				if _, err1 := os.Stat(containerFiles[0]); err1 != nil {
   225  					utils.BadRequest(w, "dockerfile", query.Dockerfile, err)
   226  				}
   227  			}
   228  		}
   229  	}
   230  
   231  	addhosts := []string{}
   232  	if _, found := r.URL.Query()["extrahosts"]; found {
   233  		if err := json.Unmarshal([]byte(query.AddHosts), &addhosts); err != nil {
   234  			utils.BadRequest(w, "extrahosts", query.AddHosts, err)
   235  			return
   236  		}
   237  	}
   238  
   239  	compression := archive.Compression(query.Compression)
   240  
   241  	// convert dropcaps formats
   242  	var dropCaps = []string{}
   243  	if _, found := r.URL.Query()["dropcaps"]; found {
   244  		var m = []string{}
   245  		if err := json.Unmarshal([]byte(query.DropCapabilities), &m); err != nil {
   246  			utils.BadRequest(w, "dropcaps", query.DropCapabilities, err)
   247  			return
   248  		}
   249  		dropCaps = m
   250  	}
   251  
   252  	// convert devices formats
   253  	var devices = []string{}
   254  	if _, found := r.URL.Query()["devices"]; found {
   255  		var m = []string{}
   256  		if err := json.Unmarshal([]byte(query.Devices), &m); err != nil {
   257  			utils.BadRequest(w, "devices", query.Devices, err)
   258  			return
   259  		}
   260  		devices = m
   261  	}
   262  
   263  	var dnsservers = []string{}
   264  	if _, found := r.URL.Query()["dnsservers"]; found {
   265  		var m = []string{}
   266  		if err := json.Unmarshal([]byte(query.DNSServers), &m); err != nil {
   267  			utils.BadRequest(w, "dnsservers", query.DNSServers, err)
   268  			return
   269  		}
   270  		dnsservers = m
   271  	}
   272  
   273  	var dnsoptions = []string{}
   274  	if _, found := r.URL.Query()["dnsoptions"]; found {
   275  		var m = []string{}
   276  		if err := json.Unmarshal([]byte(query.DNSOptions), &m); err != nil {
   277  			utils.BadRequest(w, "dnsoptions", query.DNSOptions, err)
   278  			return
   279  		}
   280  		dnsoptions = m
   281  	}
   282  
   283  	var dnssearch = []string{}
   284  	if _, found := r.URL.Query()["dnssearch"]; found {
   285  		var m = []string{}
   286  		if err := json.Unmarshal([]byte(query.DNSSearch), &m); err != nil {
   287  			utils.BadRequest(w, "dnssearches", query.DNSSearch, err)
   288  			return
   289  		}
   290  		dnssearch = m
   291  	}
   292  
   293  	var secrets = []string{}
   294  	if _, found := r.URL.Query()["secrets"]; found {
   295  		var m = []string{}
   296  		if err := json.Unmarshal([]byte(query.Secrets), &m); err != nil {
   297  			utils.BadRequest(w, "secrets", query.Secrets, err)
   298  			return
   299  		}
   300  
   301  		// for podman-remote all secrets must be picked from context director
   302  		// hence modify src so contextdir is added as prefix
   303  
   304  		for _, secret := range m {
   305  			secretOpt := strings.Split(secret, ",")
   306  			if len(secretOpt) > 0 {
   307  				modifiedOpt := []string{}
   308  				for _, token := range secretOpt {
   309  					arr := strings.SplitN(token, "=", 2)
   310  					if len(arr) > 1 {
   311  						if arr[0] == "src" {
   312  							/* move secret away from contextDir */
   313  							/* to make sure we dont accidentally commit temporary secrets to image*/
   314  							builderDirectory, _ := filepath.Split(contextDirectory)
   315  							// following path is outside build context
   316  							newSecretPath := filepath.Join(builderDirectory, arr[1])
   317  							oldSecretPath := filepath.Join(contextDirectory, arr[1])
   318  							err := os.Rename(oldSecretPath, newSecretPath)
   319  							if err != nil {
   320  								utils.BadRequest(w, "secrets", query.Secrets, err)
   321  								return
   322  							}
   323  
   324  							modifiedSrc := fmt.Sprintf("src=%s", newSecretPath)
   325  							modifiedOpt = append(modifiedOpt, modifiedSrc)
   326  						} else {
   327  							modifiedOpt = append(modifiedOpt, token)
   328  						}
   329  					}
   330  				}
   331  				secrets = append(secrets, strings.Join(modifiedOpt, ","))
   332  			}
   333  		}
   334  	}
   335  
   336  	var output string
   337  	if len(tags) > 0 {
   338  		possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, tags[0])
   339  		if err != nil {
   340  			utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image"))
   341  			return
   342  		}
   343  		output = possiblyNormalizedName
   344  	}
   345  	format := buildah.Dockerv2ImageManifest
   346  	registry := query.Registry
   347  	isolation := buildah.IsolationDefault
   348  	if utils.IsLibpodRequest(r) {
   349  		var err error
   350  		isolation, err = parseLibPodIsolation(query.Isolation)
   351  		if err != nil {
   352  			utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "failed to parse isolation"))
   353  			return
   354  		}
   355  
   356  		// make sure to force rootless as rootless otherwise buildah runs code which is intended to be run only as root.
   357  		if isolation == buildah.IsolationOCI && rootless.IsRootless() {
   358  			isolation = buildah.IsolationOCIRootless
   359  		}
   360  		registry = ""
   361  		format = query.OutputFormat
   362  	} else {
   363  		if _, found := r.URL.Query()["isolation"]; found {
   364  			if query.Isolation != "" && query.Isolation != "default" {
   365  				logrus.Debugf("invalid `isolation` parameter: %q", query.Isolation)
   366  			}
   367  		}
   368  	}
   369  	var additionalTags []string // nolint
   370  	for i := 1; i < len(tags); i++ {
   371  		possiblyNormalizedTag, err := utils.NormalizeToDockerHub(r, tags[i])
   372  		if err != nil {
   373  			utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image"))
   374  			return
   375  		}
   376  		additionalTags = append(additionalTags, possiblyNormalizedTag)
   377  	}
   378  
   379  	var additionalBuildContexts = map[string]*buildahDefine.AdditionalBuildContext{}
   380  	if _, found := r.URL.Query()["additionalbuildcontexts"]; found {
   381  		if err := json.Unmarshal([]byte(query.AdditionalBuildContexts), &additionalBuildContexts); err != nil {
   382  			utils.BadRequest(w, "additionalbuildcontexts", query.AdditionalBuildContexts, err)
   383  			return
   384  		}
   385  	}
   386  
   387  	var buildArgs = map[string]string{}
   388  	if _, found := r.URL.Query()["buildargs"]; found {
   389  		if err := json.Unmarshal([]byte(query.BuildArgs), &buildArgs); err != nil {
   390  			utils.BadRequest(w, "buildargs", query.BuildArgs, err)
   391  			return
   392  		}
   393  	}
   394  
   395  	var excludes = []string{}
   396  	if _, found := r.URL.Query()["excludes"]; found {
   397  		if err := json.Unmarshal([]byte(query.Excludes), &excludes); err != nil {
   398  			utils.BadRequest(w, "excludes", query.Excludes, err)
   399  			return
   400  		}
   401  	}
   402  
   403  	// convert annotations formats
   404  	var annotations = []string{}
   405  	if _, found := r.URL.Query()["annotations"]; found {
   406  		if err := json.Unmarshal([]byte(query.Annotations), &annotations); err != nil {
   407  			utils.BadRequest(w, "annotations", query.Annotations, err)
   408  			return
   409  		}
   410  	}
   411  
   412  	// convert cppflags formats
   413  	var cppflags = []string{}
   414  	if _, found := r.URL.Query()["cppflags"]; found {
   415  		if err := json.Unmarshal([]byte(query.CPPFlags), &cppflags); err != nil {
   416  			utils.BadRequest(w, "cppflags", query.CPPFlags, err)
   417  			return
   418  		}
   419  	}
   420  
   421  	// convert nsoptions formats
   422  	nsoptions := buildah.NamespaceOptions{}
   423  	if _, found := r.URL.Query()["nsoptions"]; found {
   424  		if err := json.Unmarshal([]byte(query.NamespaceOptions), &nsoptions); err != nil {
   425  			utils.BadRequest(w, "nsoptions", query.NamespaceOptions, err)
   426  			return
   427  		}
   428  	} else {
   429  		nsoptions = append(nsoptions, buildah.NamespaceOption{
   430  			Name: string(specs.NetworkNamespace),
   431  			Host: true,
   432  		})
   433  	}
   434  	// convert label formats
   435  	var labels = []string{}
   436  	if _, found := r.URL.Query()["labels"]; found {
   437  		makeLabels := make(map[string]string)
   438  		err := json.Unmarshal([]byte(query.Labels), &makeLabels)
   439  		if err == nil {
   440  			for k, v := range makeLabels {
   441  				labels = append(labels, k+"="+v)
   442  			}
   443  		} else {
   444  			if err := json.Unmarshal([]byte(query.Labels), &labels); err != nil {
   445  				utils.BadRequest(w, "labels", query.Labels, err)
   446  				return
   447  			}
   448  		}
   449  	}
   450  
   451  	jobs := 1
   452  	if _, found := r.URL.Query()["jobs"]; found {
   453  		jobs = query.Jobs
   454  	}
   455  
   456  	var (
   457  		labelOpts = []string{}
   458  		seccomp   string
   459  		apparmor  string
   460  	)
   461  
   462  	if utils.IsLibpodRequest(r) {
   463  		seccomp = query.Seccomp
   464  		apparmor = query.AppArmor
   465  		// convert labelopts formats
   466  		if _, found := r.URL.Query()["labelopts"]; found {
   467  			var m = []string{}
   468  			if err := json.Unmarshal([]byte(query.LabelOpts), &m); err != nil {
   469  				utils.BadRequest(w, "labelopts", query.LabelOpts, err)
   470  				return
   471  			}
   472  			labelOpts = m
   473  		}
   474  	} else {
   475  		// handle security-opt
   476  		if _, found := r.URL.Query()["securityopt"]; found {
   477  			var securityOpts = []string{}
   478  			if err := json.Unmarshal([]byte(query.SecurityOpt), &securityOpts); err != nil {
   479  				utils.BadRequest(w, "securityopt", query.SecurityOpt, err)
   480  				return
   481  			}
   482  			for _, opt := range securityOpts {
   483  				if opt == "no-new-privileges" {
   484  					utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.New("no-new-privileges is not supported"))
   485  					return
   486  				}
   487  				con := strings.SplitN(opt, "=", 2)
   488  				if len(con) != 2 {
   489  					utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt name=value pair: %q", opt))
   490  					return
   491  				}
   492  
   493  				switch con[0] {
   494  				case "label":
   495  					labelOpts = append(labelOpts, con[1])
   496  				case "apparmor":
   497  					apparmor = con[1]
   498  				case "seccomp":
   499  					seccomp = con[1]
   500  				default:
   501  					utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt 2: %q", opt))
   502  					return
   503  				}
   504  			}
   505  		}
   506  	}
   507  
   508  	// convert ulimits formats
   509  	var ulimits = []string{}
   510  	if _, found := r.URL.Query()["ulimits"]; found {
   511  		var m = []string{}
   512  		if err := json.Unmarshal([]byte(query.Ulimits), &m); err != nil {
   513  			utils.BadRequest(w, "ulimits", query.Ulimits, err)
   514  			return
   515  		}
   516  		ulimits = m
   517  	}
   518  
   519  	pullPolicy := buildahDefine.PullIfMissing
   520  	if utils.IsLibpodRequest(r) {
   521  		pullPolicy = buildahDefine.PolicyMap[query.PullPolicy]
   522  	} else {
   523  		if _, found := r.URL.Query()["pull"]; found {
   524  			if query.Pull {
   525  				pullPolicy = buildahDefine.PullAlways
   526  			}
   527  		}
   528  	}
   529  
   530  	creds, authfile, err := auth.GetCredentials(r)
   531  	if err != nil {
   532  		// Credential value(s) not returned as their value is not human readable
   533  		utils.Error(w, http.StatusBadRequest, err)
   534  		return
   535  	}
   536  	defer auth.RemoveAuthfile(authfile)
   537  
   538  	fromImage := query.From
   539  	if fromImage != "" {
   540  		possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, fromImage)
   541  		if err != nil {
   542  			utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error normalizing image"))
   543  			return
   544  		}
   545  		fromImage = possiblyNormalizedName
   546  	}
   547  
   548  	systemContext := &types.SystemContext{
   549  		AuthFilePath:     authfile,
   550  		DockerAuthConfig: creds,
   551  	}
   552  	utils.PossiblyEnforceDockerHub(r, systemContext)
   553  
   554  	if _, found := r.URL.Query()["tlsVerify"]; found {
   555  		systemContext.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
   556  		systemContext.OCIInsecureSkipTLSVerify = !query.TLSVerify
   557  		systemContext.DockerDaemonInsecureSkipTLSVerify = !query.TLSVerify
   558  	}
   559  	// Channels all mux'ed in select{} below to follow API build protocol
   560  	stdout := channel.NewWriter(make(chan []byte))
   561  	defer stdout.Close()
   562  
   563  	auxout := channel.NewWriter(make(chan []byte))
   564  	defer auxout.Close()
   565  
   566  	stderr := channel.NewWriter(make(chan []byte))
   567  	defer stderr.Close()
   568  
   569  	reporter := channel.NewWriter(make(chan []byte))
   570  	defer reporter.Close()
   571  
   572  	runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
   573  	buildOptions := buildahDefine.BuildOptions{
   574  		AddCapabilities:         addCaps,
   575  		AdditionalBuildContexts: additionalBuildContexts,
   576  		AdditionalTags:          additionalTags,
   577  		Annotations:             annotations,
   578  		CPPFlags:                cppflags,
   579  		Args:                    buildArgs,
   580  		AllPlatforms:            query.AllPlatforms,
   581  		CommonBuildOpts: &buildah.CommonBuildOptions{
   582  			AddHost:            addhosts,
   583  			ApparmorProfile:    apparmor,
   584  			CPUPeriod:          query.CpuPeriod,
   585  			CPUQuota:           query.CpuQuota,
   586  			CPUSetCPUs:         query.CpuSetCpus,
   587  			CPUSetMems:         query.CpuSetMems,
   588  			CPUShares:          query.CpuShares,
   589  			CgroupParent:       query.CgroupParent,
   590  			DNSOptions:         dnsoptions,
   591  			DNSSearch:          dnssearch,
   592  			DNSServers:         dnsservers,
   593  			HTTPProxy:          query.HTTPProxy,
   594  			IdentityLabel:      types.NewOptionalBool(query.IdentityLabel),
   595  			LabelOpts:          labelOpts,
   596  			Memory:             query.Memory,
   597  			MemorySwap:         query.MemSwap,
   598  			SeccompProfilePath: seccomp,
   599  			ShmSize:            strconv.Itoa(query.ShmSize),
   600  			Ulimit:             ulimits,
   601  			Secrets:            secrets,
   602  		},
   603  		Compression:                    compression,
   604  		ConfigureNetwork:               parseNetworkConfigurationPolicy(query.ConfigureNetwork),
   605  		ContextDirectory:               contextDirectory,
   606  		Devices:                        devices,
   607  		DropCapabilities:               dropCaps,
   608  		Envs:                           query.Envs,
   609  		Err:                            auxout,
   610  		Excludes:                       excludes,
   611  		ForceRmIntermediateCtrs:        query.ForceRm,
   612  		From:                           fromImage,
   613  		IgnoreUnrecognizedInstructions: query.Ignore,
   614  		Isolation:                      isolation,
   615  		Jobs:                           &jobs,
   616  		Labels:                         labels,
   617  		Layers:                         query.Layers,
   618  		LogRusage:                      query.LogRusage,
   619  		Manifest:                       query.Manifest,
   620  		MaxPullPushRetries:             3,
   621  		NamespaceOptions:               nsoptions,
   622  		NoCache:                        query.NoCache,
   623  		OSFeatures:                     query.OSFeatures,
   624  		OSVersion:                      query.OSVersion,
   625  		Out:                            stdout,
   626  		Output:                         output,
   627  		OutputFormat:                   format,
   628  		PullPolicy:                     pullPolicy,
   629  		PullPushRetryDelay:             2 * time.Second,
   630  		Quiet:                          query.Quiet,
   631  		Registry:                       registry,
   632  		RemoveIntermediateCtrs:         query.Rm,
   633  		ReportWriter:                   reporter,
   634  		RusageLogFile:                  query.RusageLogFile,
   635  		Squash:                         query.Squash,
   636  		SystemContext:                  systemContext,
   637  		Target:                         query.Target,
   638  		UnsetEnvs:                      query.UnsetEnvs,
   639  	}
   640  
   641  	for _, platformSpec := range query.Platform {
   642  		os, arch, variant, err := parse.Platform(platformSpec)
   643  		if err != nil {
   644  			utils.BadRequest(w, "platform", platformSpec, err)
   645  			return
   646  		}
   647  		buildOptions.Platforms = append(buildOptions.Platforms, struct{ OS, Arch, Variant string }{
   648  			OS:      os,
   649  			Arch:    arch,
   650  			Variant: variant,
   651  		})
   652  	}
   653  	if _, found := r.URL.Query()["timestamp"]; found {
   654  		ts := time.Unix(query.Timestamp, 0)
   655  		buildOptions.Timestamp = &ts
   656  	}
   657  
   658  	var (
   659  		imageID string
   660  		success bool
   661  	)
   662  
   663  	runCtx, cancel := context.WithCancel(context.Background())
   664  	go func() {
   665  		defer cancel()
   666  		imageID, _, err = runtime.Build(r.Context(), buildOptions, containerFiles...)
   667  		if err == nil {
   668  			success = true
   669  		} else {
   670  			stderr.Write([]byte(err.Error() + "\n"))
   671  		}
   672  	}()
   673  
   674  	flush := func() {
   675  		if flusher, ok := w.(http.Flusher); ok {
   676  			flusher.Flush()
   677  		}
   678  	}
   679  
   680  	// Send headers and prime client for stream to come
   681  	w.Header().Set("Content-Type", "application/json")
   682  	w.WriteHeader(http.StatusOK)
   683  	flush()
   684  
   685  	body := w.(io.Writer)
   686  	if logrus.IsLevelEnabled(logrus.DebugLevel) {
   687  		if v, found := os.LookupEnv("PODMAN_RETAIN_BUILD_ARTIFACT"); found {
   688  			if keep, _ := strconv.ParseBool(v); keep {
   689  				t, _ := ioutil.TempFile("", "build_*_server")
   690  				defer t.Close()
   691  				body = io.MultiWriter(t, w)
   692  			}
   693  		}
   694  	}
   695  
   696  	enc := json.NewEncoder(body)
   697  	enc.SetEscapeHTML(true)
   698  	var stepErrors []string
   699  
   700  	for {
   701  		type BuildResponse struct {
   702  			Stream string                 `json:"stream,omitempty"`
   703  			Error  *jsonmessage.JSONError `json:"errorDetail,omitempty"`
   704  			// NOTE: `error` is being deprecated check https://github.com/moby/moby/blob/master/pkg/jsonmessage/jsonmessage.go#L148
   705  			ErrorMessage string          `json:"error,omitempty"` // deprecate this slowly
   706  			Aux          json.RawMessage `json:"aux,omitempty"`
   707  		}
   708  		m := BuildResponse{}
   709  
   710  		select {
   711  		case e := <-stdout.Chan():
   712  			m.Stream = string(e)
   713  			if err := enc.Encode(m); err != nil {
   714  				stderr.Write([]byte(err.Error()))
   715  			}
   716  			flush()
   717  		case e := <-reporter.Chan():
   718  			m.Stream = string(e)
   719  			if err := enc.Encode(m); err != nil {
   720  				stderr.Write([]byte(err.Error()))
   721  			}
   722  			flush()
   723  		case e := <-auxout.Chan():
   724  			if !query.Quiet {
   725  				m.Stream = string(e)
   726  				if err := enc.Encode(m); err != nil {
   727  					stderr.Write([]byte(err.Error()))
   728  				}
   729  				flush()
   730  			} else {
   731  				stepErrors = append(stepErrors, string(e))
   732  			}
   733  		case e := <-stderr.Chan():
   734  			// Docker-API Compat parity : Build failed so
   735  			// output all step errors irrespective of quiet
   736  			// flag.
   737  			for _, stepError := range stepErrors {
   738  				t := BuildResponse{}
   739  				t.Stream = stepError
   740  				if err := enc.Encode(t); err != nil {
   741  					stderr.Write([]byte(err.Error()))
   742  				}
   743  				flush()
   744  			}
   745  			m.ErrorMessage = string(e)
   746  			m.Error = &jsonmessage.JSONError{
   747  				Message: m.ErrorMessage,
   748  			}
   749  			if err := enc.Encode(m); err != nil {
   750  				logrus.Warnf("Failed to json encode error %v", err)
   751  			}
   752  			flush()
   753  			return
   754  		case <-runCtx.Done():
   755  			if success {
   756  				if !utils.IsLibpodRequest(r) && !query.Quiet {
   757  					m.Aux = []byte(fmt.Sprintf(`{"ID":"sha256:%s"}`, imageID))
   758  					if err := enc.Encode(m); err != nil {
   759  						logrus.Warnf("failed to json encode error %v", err)
   760  					}
   761  					m.Aux = nil
   762  					m.Stream = fmt.Sprintf("Successfully built %12.12s\n", imageID)
   763  					if err := enc.Encode(m); err != nil {
   764  						logrus.Warnf("Failed to json encode error %v", err)
   765  					}
   766  					flush()
   767  					for _, tag := range tags {
   768  						m.Stream = fmt.Sprintf("Successfully tagged %s\n", tag)
   769  						if err := enc.Encode(m); err != nil {
   770  							logrus.Warnf("Failed to json encode error %v", err)
   771  						}
   772  						flush()
   773  					}
   774  				}
   775  			}
   776  			flush()
   777  			return
   778  		case <-r.Context().Done():
   779  			cancel()
   780  			logrus.Infof("Client disconnect reported for build %q / %q.", registry, query.Dockerfile)
   781  			return
   782  		}
   783  	}
   784  }
   785  
   786  func parseNetworkConfigurationPolicy(network string) buildah.NetworkConfigurationPolicy {
   787  	if val, err := strconv.Atoi(network); err == nil {
   788  		return buildah.NetworkConfigurationPolicy(val)
   789  	}
   790  	switch network {
   791  	case "NetworkDefault":
   792  		return buildah.NetworkDefault
   793  	case "NetworkDisabled":
   794  		return buildah.NetworkDisabled
   795  	case "NetworkEnabled":
   796  		return buildah.NetworkEnabled
   797  	default:
   798  		return buildah.NetworkDefault
   799  	}
   800  }
   801  
   802  func parseLibPodIsolation(isolation string) (buildah.Isolation, error) { // nolint
   803  	if val, err := strconv.Atoi(isolation); err == nil {
   804  		return buildah.Isolation(val), nil
   805  	}
   806  	return parse.IsolationOption(isolation)
   807  }
   808  
   809  func extractTarFile(r *http.Request) (string, error) {
   810  	// build a home for the request body
   811  	anchorDir, err := ioutil.TempDir("", "libpod_builder")
   812  	if err != nil {
   813  		return "", err
   814  	}
   815  
   816  	path := filepath.Join(anchorDir, "tarBall")
   817  	tarBall, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
   818  	if err != nil {
   819  		return "", err
   820  	}
   821  	defer tarBall.Close()
   822  
   823  	// Content-Length not used as too many existing API clients didn't honor it
   824  	_, err = io.Copy(tarBall, r.Body)
   825  	r.Body.Close()
   826  	if err != nil {
   827  		return "", fmt.Errorf("failed Request: Unable to copy tar file from request body %s", r.RequestURI)
   828  	}
   829  
   830  	buildDir := filepath.Join(anchorDir, "build")
   831  	err = os.Mkdir(buildDir, 0o700)
   832  	if err != nil {
   833  		return "", err
   834  	}
   835  
   836  	_, _ = tarBall.Seek(0, 0)
   837  	err = archive.Untar(tarBall, buildDir, nil)
   838  	return buildDir, err
   839  }