2018-04-19 03:00:15 +08:00
|
|
|
package parse
|
|
|
|
|
|
|
|
// this package should contain functions that parse and validate
|
|
|
|
// user input and is shared either amongst buildah subcommands or
|
|
|
|
// would be useful to projects vendoring buildah
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
2018-04-30 06:36:35 +08:00
|
|
|
"path/filepath"
|
2018-06-07 12:45:17 +08:00
|
|
|
"strconv"
|
2018-04-19 03:00:15 +08:00
|
|
|
"strings"
|
2018-06-07 12:45:17 +08:00
|
|
|
"unicode"
|
2018-04-19 03:00:15 +08:00
|
|
|
|
2021-09-28 05:26:01 +08:00
|
|
|
"github.com/containerd/containerd/platforms"
|
2021-02-07 06:49:40 +08:00
|
|
|
"github.com/containers/buildah/define"
|
2021-10-18 13:51:51 +08:00
|
|
|
internalParse "github.com/containers/buildah/internal/parse"
|
2021-07-26 14:07:23 +08:00
|
|
|
"github.com/containers/buildah/pkg/sshagent"
|
2021-07-12 22:41:28 +08:00
|
|
|
"github.com/containers/common/pkg/parse"
|
2019-10-26 05:19:30 +08:00
|
|
|
"github.com/containers/image/v5/types"
|
2021-10-18 13:51:51 +08:00
|
|
|
"github.com/containers/storage"
|
2018-06-07 12:45:17 +08:00
|
|
|
"github.com/containers/storage/pkg/idtools"
|
2020-07-07 22:15:40 +08:00
|
|
|
"github.com/containers/storage/pkg/unshare"
|
2019-10-02 04:03:57 +08:00
|
|
|
units "github.com/docker/go-units"
|
|
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
2021-10-07 21:10:22 +08:00
|
|
|
"github.com/openshift/imagebuilder"
|
2018-04-19 03:00:15 +08:00
|
|
|
"github.com/pkg/errors"
|
2018-06-07 12:45:17 +08:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-04-07 00:03:58 +08:00
|
|
|
"github.com/spf13/cobra"
|
2022-01-08 06:09:21 +08:00
|
|
|
"github.com/spf13/pflag"
|
2021-08-25 03:30:13 +08:00
|
|
|
"golang.org/x/term"
|
2018-04-19 03:00:15 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// SeccompDefaultPath defines the default seccomp path
|
|
|
|
SeccompDefaultPath = "/usr/share/containers/seccomp.json"
|
|
|
|
// SeccompOverridePath if this exists it overrides the default seccomp path
|
|
|
|
SeccompOverridePath = "/etc/crio/seccomp.json"
|
2019-06-20 02:17:11 +08:00
|
|
|
// TypeBind is the type for mounting host dir
|
|
|
|
TypeBind = "bind"
|
|
|
|
// TypeTmpfs is the type for mounting tmpfs
|
|
|
|
TypeTmpfs = "tmpfs"
|
2021-10-07 16:27:17 +08:00
|
|
|
// TypeCache is the type for mounting a common persistent cache from host
|
|
|
|
TypeCache = "cache"
|
|
|
|
// mount=type=cache must create a persistent directory on host so its available for all consecutive builds.
|
|
|
|
// Lifecycle of following directory will be inherited from how host machine treats temporary directory
|
|
|
|
BuildahCacheDir = "buildah-cache"
|
2019-06-20 02:17:11 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
errDuplicateDest = errors.Errorf("duplicate mount destination")
|
2018-04-19 03:00:15 +08:00
|
|
|
)
|
|
|
|
|
2018-06-07 12:45:17 +08:00
|
|
|
// CommonBuildOptions parses the build options from the bud cli
|
2021-02-07 06:49:40 +08:00
|
|
|
func CommonBuildOptions(c *cobra.Command) (*define.CommonBuildOptions, error) {
|
2022-01-08 06:09:21 +08:00
|
|
|
return CommonBuildOptionsFromFlagSet(c.Flags(), c.Flag)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CommonBuildOptionsFromFlagSet parses the build options from the bud cli
|
|
|
|
func CommonBuildOptionsFromFlagSet(flags *pflag.FlagSet, findFlagFunc func(name string) *pflag.Flag) (*define.CommonBuildOptions, error) {
|
2018-04-19 03:00:15 +08:00
|
|
|
var (
|
|
|
|
memoryLimit int64
|
|
|
|
memorySwap int64
|
2019-06-07 03:44:23 +08:00
|
|
|
noDNS bool
|
2018-04-19 03:00:15 +08:00
|
|
|
err error
|
|
|
|
)
|
2019-04-26 03:39:49 +08:00
|
|
|
|
2022-01-08 06:09:21 +08:00
|
|
|
memVal, _ := flags.GetString("memory")
|
2019-01-22 23:35:52 +08:00
|
|
|
if memVal != "" {
|
|
|
|
memoryLimit, err = units.RAMInBytes(memVal)
|
2018-04-19 03:00:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "invalid value for memory")
|
|
|
|
}
|
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
|
2022-01-08 06:09:21 +08:00
|
|
|
memSwapValue, _ := flags.GetString("memory-swap")
|
2019-01-22 23:35:52 +08:00
|
|
|
if memSwapValue != "" {
|
2021-12-15 18:28:31 +08:00
|
|
|
if memSwapValue == "-1" {
|
|
|
|
memorySwap = -1
|
|
|
|
} else {
|
|
|
|
memorySwap, err = units.RAMInBytes(memSwapValue)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "invalid value for memory-swap")
|
|
|
|
}
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
|
2022-01-08 06:09:21 +08:00
|
|
|
addHost, _ := flags.GetStringSlice("add-host")
|
2019-01-22 23:35:52 +08:00
|
|
|
if len(addHost) > 0 {
|
|
|
|
for _, host := range addHost {
|
2018-04-19 03:00:15 +08:00
|
|
|
if err := validateExtraHost(host); err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "invalid value for add-host")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-07 00:03:58 +08:00
|
|
|
|
2019-06-07 03:44:23 +08:00
|
|
|
noDNS = false
|
2020-02-08 01:54:18 +08:00
|
|
|
dnsServers := []string{}
|
2022-01-08 06:09:21 +08:00
|
|
|
if flags.Changed("dns") {
|
|
|
|
dnsServers, _ = flags.GetStringSlice("dns")
|
2020-02-08 01:54:18 +08:00
|
|
|
for _, server := range dnsServers {
|
|
|
|
if strings.ToLower(server) == "none" {
|
|
|
|
noDNS = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if noDNS && len(dnsServers) > 1 {
|
|
|
|
return nil, errors.Errorf("invalid --dns, --dns=none may not be used with any other --dns options")
|
2019-06-07 03:44:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 01:54:18 +08:00
|
|
|
dnsSearch := []string{}
|
2022-01-08 06:09:21 +08:00
|
|
|
if flags.Changed("dns-search") {
|
|
|
|
dnsSearch, _ = flags.GetStringSlice("dns-search")
|
2020-02-08 01:54:18 +08:00
|
|
|
if noDNS && len(dnsSearch) > 0 {
|
|
|
|
return nil, errors.Errorf("invalid --dns-search, --dns-search may not be used with --dns=none")
|
|
|
|
}
|
2019-06-07 03:44:23 +08:00
|
|
|
}
|
|
|
|
|
2020-02-08 01:54:18 +08:00
|
|
|
dnsOptions := []string{}
|
2022-01-08 06:09:21 +08:00
|
|
|
if flags.Changed("dns-option") {
|
|
|
|
dnsOptions, _ = flags.GetStringSlice("dns-option")
|
2020-02-08 01:54:18 +08:00
|
|
|
if noDNS && len(dnsOptions) > 0 {
|
|
|
|
return nil, errors.Errorf("invalid --dns-option, --dns-option may not be used with --dns=none")
|
|
|
|
}
|
2019-06-07 03:44:23 +08:00
|
|
|
}
|
2019-04-07 00:03:58 +08:00
|
|
|
|
2022-01-08 06:09:21 +08:00
|
|
|
if _, err := units.FromHumanSize(findFlagFunc("shm-size").Value.String()); err != nil {
|
2018-04-19 03:00:15 +08:00
|
|
|
return nil, errors.Wrapf(err, "invalid --shm-size")
|
|
|
|
}
|
2022-01-08 06:09:21 +08:00
|
|
|
volumes, _ := flags.GetStringArray("volume")
|
2019-07-24 19:55:25 +08:00
|
|
|
if err := Volumes(volumes); err != nil {
|
2018-04-19 03:00:15 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
2022-01-08 06:09:21 +08:00
|
|
|
cpuPeriod, _ := flags.GetUint64("cpu-period")
|
|
|
|
cpuQuota, _ := flags.GetInt64("cpu-quota")
|
|
|
|
cpuShares, _ := flags.GetUint64("cpu-shares")
|
|
|
|
httpProxy, _ := flags.GetBool("http-proxy")
|
2020-02-08 01:54:18 +08:00
|
|
|
|
|
|
|
ulimit := []string{}
|
2022-01-08 06:09:21 +08:00
|
|
|
if flags.Changed("ulimit") {
|
|
|
|
ulimit, _ = flags.GetStringSlice("ulimit")
|
2020-02-08 01:54:18 +08:00
|
|
|
}
|
2020-01-14 20:12:56 +08:00
|
|
|
|
2022-01-08 06:09:21 +08:00
|
|
|
secrets, _ := flags.GetStringArray("secret")
|
|
|
|
sshsources, _ := flags.GetStringArray("ssh")
|
2021-04-17 06:21:31 +08:00
|
|
|
|
2021-02-07 06:49:40 +08:00
|
|
|
commonOpts := &define.CommonBuildOptions{
|
2019-01-22 23:35:52 +08:00
|
|
|
AddHost: addHost,
|
|
|
|
CPUPeriod: cpuPeriod,
|
|
|
|
CPUQuota: cpuQuota,
|
2022-01-08 06:09:21 +08:00
|
|
|
CPUSetCPUs: findFlagFunc("cpuset-cpus").Value.String(),
|
|
|
|
CPUSetMems: findFlagFunc("cpuset-mems").Value.String(),
|
2019-01-22 23:35:52 +08:00
|
|
|
CPUShares: cpuShares,
|
2022-01-08 06:09:21 +08:00
|
|
|
CgroupParent: findFlagFunc("cgroup-parent").Value.String(),
|
2020-06-22 18:44:23 +08:00
|
|
|
DNSOptions: dnsOptions,
|
2019-04-07 00:03:58 +08:00
|
|
|
DNSSearch: dnsSearch,
|
|
|
|
DNSServers: dnsServers,
|
2019-04-13 07:03:39 +08:00
|
|
|
HTTPProxy: httpProxy,
|
2018-04-19 03:00:15 +08:00
|
|
|
Memory: memoryLimit,
|
|
|
|
MemorySwap: memorySwap,
|
2022-01-08 06:09:21 +08:00
|
|
|
ShmSize: findFlagFunc("shm-size").Value.String(),
|
2020-02-08 01:54:18 +08:00
|
|
|
Ulimit: ulimit,
|
2019-01-22 23:35:52 +08:00
|
|
|
Volumes: volumes,
|
2021-04-17 06:21:31 +08:00
|
|
|
Secrets: secrets,
|
2021-07-26 14:07:23 +08:00
|
|
|
SSHSources: sshsources,
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
2022-01-08 06:09:21 +08:00
|
|
|
securityOpts, _ := flags.GetStringArray("security-opt")
|
2019-01-22 23:35:52 +08:00
|
|
|
if err := parseSecurityOpts(securityOpts, commonOpts); err != nil {
|
2018-04-19 03:00:15 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return commonOpts, nil
|
|
|
|
}
|
|
|
|
|
2021-02-07 06:49:40 +08:00
|
|
|
func parseSecurityOpts(securityOpts []string, commonOpts *define.CommonBuildOptions) error {
|
2018-04-19 03:00:15 +08:00
|
|
|
for _, opt := range securityOpts {
|
|
|
|
if opt == "no-new-privileges" {
|
|
|
|
return errors.Errorf("no-new-privileges is not supported")
|
|
|
|
}
|
|
|
|
con := strings.SplitN(opt, "=", 2)
|
|
|
|
if len(con) != 2 {
|
2018-03-07 07:13:24 +08:00
|
|
|
return errors.Errorf("Invalid --security-opt name=value pair: %q", opt)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
switch con[0] {
|
|
|
|
case "label":
|
|
|
|
commonOpts.LabelOpts = append(commonOpts.LabelOpts, con[1])
|
|
|
|
case "apparmor":
|
|
|
|
commonOpts.ApparmorProfile = con[1]
|
|
|
|
case "seccomp":
|
|
|
|
commonOpts.SeccompProfilePath = con[1]
|
|
|
|
default:
|
|
|
|
return errors.Errorf("Invalid --security-opt 2: %q", opt)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if commonOpts.SeccompProfilePath == "" {
|
|
|
|
if _, err := os.Stat(SeccompOverridePath); err == nil {
|
|
|
|
commonOpts.SeccompProfilePath = SeccompOverridePath
|
|
|
|
} else {
|
|
|
|
if !os.IsNotExist(err) {
|
2021-03-27 17:17:12 +08:00
|
|
|
return errors.WithStack(err)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
if _, err := os.Stat(SeccompDefaultPath); err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
2021-03-27 17:17:12 +08:00
|
|
|
return errors.WithStack(err)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
commonOpts.SeccompProfilePath = SeccompDefaultPath
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-11 11:26:35 +08:00
|
|
|
// Split string into slice by colon. Backslash-escaped colon (i.e. "\:") will not be regarded as separator
|
|
|
|
func SplitStringWithColonEscape(str string) []string {
|
|
|
|
result := make([]string, 0, 3)
|
|
|
|
sb := &strings.Builder{}
|
|
|
|
for idx, r := range str {
|
|
|
|
if r == ':' {
|
|
|
|
// the colon is backslash-escaped
|
|
|
|
if idx-1 > 0 && str[idx-1] == '\\' {
|
|
|
|
sb.WriteRune(r)
|
|
|
|
} else {
|
|
|
|
// os.Stat will fail if path contains escaped colon
|
|
|
|
result = append(result, revertEscapedColon(sb.String()))
|
|
|
|
sb.Reset()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sb.WriteRune(r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if sb.Len() > 0 {
|
|
|
|
result = append(result, revertEscapedColon(sb.String()))
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert "\:" to ":"
|
|
|
|
func revertEscapedColon(source string) string {
|
|
|
|
return strings.ReplaceAll(source, "\\:", ":")
|
|
|
|
}
|
|
|
|
|
2019-07-24 19:55:25 +08:00
|
|
|
// Volume parses the input of --volume
|
|
|
|
func Volume(volume string) (specs.Mount, error) {
|
2019-04-28 18:33:09 +08:00
|
|
|
mount := specs.Mount{}
|
2021-10-11 11:26:35 +08:00
|
|
|
arr := SplitStringWithColonEscape(volume)
|
2019-04-28 18:33:09 +08:00
|
|
|
if len(arr) < 2 {
|
|
|
|
return mount, errors.Errorf("incorrect volume format %q, should be host-dir:ctr-dir[:option]", volume)
|
|
|
|
}
|
2019-06-20 02:17:11 +08:00
|
|
|
if err := validateVolumeMountHostDir(arr[0]); err != nil {
|
2019-04-28 18:33:09 +08:00
|
|
|
return mount, err
|
|
|
|
}
|
2021-07-12 22:41:28 +08:00
|
|
|
if err := parse.ValidateVolumeCtrDir(arr[1]); err != nil {
|
2019-04-28 18:33:09 +08:00
|
|
|
return mount, err
|
|
|
|
}
|
|
|
|
mountOptions := ""
|
|
|
|
if len(arr) > 2 {
|
|
|
|
mountOptions = arr[2]
|
2021-07-12 22:41:28 +08:00
|
|
|
if _, err := parse.ValidateVolumeOpts(strings.Split(arr[2], ",")); err != nil {
|
2019-04-28 18:33:09 +08:00
|
|
|
return mount, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mountOpts := strings.Split(mountOptions, ",")
|
|
|
|
mount.Source = arr[0]
|
|
|
|
mount.Destination = arr[1]
|
|
|
|
mount.Type = "rbind"
|
|
|
|
mount.Options = mountOpts
|
|
|
|
return mount, nil
|
|
|
|
}
|
|
|
|
|
2019-07-24 19:55:25 +08:00
|
|
|
// Volumes validates the host and container paths passed in to the --volume flag
|
|
|
|
func Volumes(volumes []string) error {
|
2018-04-19 03:00:15 +08:00
|
|
|
if len(volumes) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for _, volume := range volumes {
|
2019-07-24 19:55:25 +08:00
|
|
|
if _, err := Volume(volume); err != nil {
|
2018-04-19 03:00:15 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-06-20 02:17:11 +08:00
|
|
|
func getVolumeMounts(volumes []string) (map[string]specs.Mount, error) {
|
|
|
|
finalVolumeMounts := make(map[string]specs.Mount)
|
|
|
|
|
|
|
|
for _, volume := range volumes {
|
2019-07-24 19:55:25 +08:00
|
|
|
volumeMount, err := Volume(volume)
|
2019-06-20 02:17:11 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if _, ok := finalVolumeMounts[volumeMount.Destination]; ok {
|
|
|
|
return nil, errors.Wrapf(errDuplicateDest, volumeMount.Destination)
|
|
|
|
}
|
|
|
|
finalVolumeMounts[volumeMount.Destination] = volumeMount
|
|
|
|
}
|
|
|
|
return finalVolumeMounts, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetVolumes gets the volumes from --volume and --mount
|
2021-10-18 13:51:51 +08:00
|
|
|
func GetVolumes(ctx *types.SystemContext, store storage.Store, volumes []string, mounts []string, contextDir string) ([]specs.Mount, []string, error) {
|
|
|
|
unifiedMounts, mountedImages, err := getMounts(ctx, store, mounts, contextDir)
|
2019-06-20 02:17:11 +08:00
|
|
|
if err != nil {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, err
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
volumeMounts, err := getVolumeMounts(volumes)
|
|
|
|
if err != nil {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, err
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
for dest, mount := range volumeMounts {
|
|
|
|
if _, ok := unifiedMounts[dest]; ok {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, errors.Wrapf(errDuplicateDest, dest)
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
unifiedMounts[dest] = mount
|
|
|
|
}
|
|
|
|
|
|
|
|
finalMounts := make([]specs.Mount, 0, len(unifiedMounts))
|
|
|
|
for _, mount := range unifiedMounts {
|
|
|
|
finalMounts = append(finalMounts, mount)
|
|
|
|
}
|
2021-10-18 13:51:51 +08:00
|
|
|
return finalMounts, mountedImages, nil
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// getMounts takes user-provided input from the --mount flag and creates OCI
|
|
|
|
// spec mounts.
|
|
|
|
// buildah run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
|
|
|
|
// buildah run --mount type=tmpfs,target=/dev/shm ...
|
2021-10-18 13:51:51 +08:00
|
|
|
func getMounts(ctx *types.SystemContext, store storage.Store, mounts []string, contextDir string) (map[string]specs.Mount, []string, error) {
|
2019-06-20 02:17:11 +08:00
|
|
|
finalMounts := make(map[string]specs.Mount)
|
2021-10-18 13:51:51 +08:00
|
|
|
mountedImages := make([]string, 0)
|
2019-06-20 02:17:11 +08:00
|
|
|
|
|
|
|
errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs>,[src=<host-dir>,]target=<ctr-dir>[,options]")
|
|
|
|
|
|
|
|
// TODO(vrothberg): the manual parsing can be replaced with a regular expression
|
|
|
|
// to allow a more robust parsing of the mount format and to give
|
2019-11-17 00:31:41 +08:00
|
|
|
// precise errors regarding supported format versus supported options.
|
2019-06-20 02:17:11 +08:00
|
|
|
for _, mount := range mounts {
|
|
|
|
arr := strings.SplitN(mount, ",", 2)
|
|
|
|
if len(arr) < 2 {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, errors.Wrapf(errInvalidSyntax, "%q", mount)
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
kv := strings.Split(arr[0], "=")
|
|
|
|
// TODO: type is not explicitly required in Docker.
|
|
|
|
// If not specified, it defaults to "volume".
|
|
|
|
if len(kv) != 2 || kv[0] != "type" {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, errors.Wrapf(errInvalidSyntax, "%q", mount)
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
tokens := strings.Split(arr[1], ",")
|
|
|
|
switch kv[1] {
|
|
|
|
case TypeBind:
|
2021-10-18 13:51:51 +08:00
|
|
|
mount, image, err := internalParse.GetBindMount(ctx, tokens, contextDir, store, "", nil)
|
2019-06-20 02:17:11 +08:00
|
|
|
if err != nil {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, err
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
if _, ok := finalMounts[mount.Destination]; ok {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, errors.Wrapf(errDuplicateDest, mount.Destination)
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
finalMounts[mount.Destination] = mount
|
2021-10-18 13:51:51 +08:00
|
|
|
mountedImages = append(mountedImages, image)
|
2021-10-07 16:27:17 +08:00
|
|
|
case TypeCache:
|
2021-10-18 13:51:51 +08:00
|
|
|
mount, err := internalParse.GetCacheMount(tokens, store, "", nil)
|
2021-10-07 16:27:17 +08:00
|
|
|
if err != nil {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, err
|
2021-10-07 16:27:17 +08:00
|
|
|
}
|
|
|
|
if _, ok := finalMounts[mount.Destination]; ok {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, errors.Wrapf(errDuplicateDest, mount.Destination)
|
2021-10-07 16:27:17 +08:00
|
|
|
}
|
|
|
|
finalMounts[mount.Destination] = mount
|
2019-06-20 02:17:11 +08:00
|
|
|
case TypeTmpfs:
|
2021-10-18 13:51:51 +08:00
|
|
|
mount, err := internalParse.GetTmpfsMount(tokens)
|
2019-06-20 02:17:11 +08:00
|
|
|
if err != nil {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, err
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
if _, ok := finalMounts[mount.Destination]; ok {
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, errors.Wrapf(errDuplicateDest, mount.Destination)
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
finalMounts[mount.Destination] = mount
|
|
|
|
default:
|
2021-10-18 13:51:51 +08:00
|
|
|
return nil, mountedImages, errors.Errorf("invalid filesystem type %q", kv[1])
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-18 13:51:51 +08:00
|
|
|
return finalMounts, mountedImages, nil
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// ValidateVolumeHostDir validates a volume mount's source directory
|
2019-05-04 04:45:01 +08:00
|
|
|
func ValidateVolumeHostDir(hostDir string) error {
|
2021-07-12 22:41:28 +08:00
|
|
|
return parse.ValidateVolumeHostDir(hostDir)
|
2019-06-20 02:17:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// validates the host path of buildah --volume
|
|
|
|
func validateVolumeMountHostDir(hostDir string) error {
|
2018-04-30 06:36:35 +08:00
|
|
|
if !filepath.IsAbs(hostDir) {
|
|
|
|
return errors.Errorf("invalid host path, must be an absolute path %q", hostDir)
|
|
|
|
}
|
2018-04-19 03:00:15 +08:00
|
|
|
if _, err := os.Stat(hostDir); err != nil {
|
2021-03-27 17:17:12 +08:00
|
|
|
return errors.WithStack(err)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-06-20 02:17:11 +08:00
|
|
|
// ValidateVolumeCtrDir validates a volume mount's destination directory.
|
2019-05-04 04:45:01 +08:00
|
|
|
func ValidateVolumeCtrDir(ctrDir string) error {
|
2021-07-12 22:41:28 +08:00
|
|
|
return parse.ValidateVolumeCtrDir(ctrDir)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
|
2019-06-29 04:07:38 +08:00
|
|
|
// ValidateVolumeOpts validates a volume's options
|
|
|
|
func ValidateVolumeOpts(options []string) ([]string, error) {
|
2021-07-12 22:41:28 +08:00
|
|
|
return parse.ValidateVolumeOpts(options)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// validateExtraHost validates that the specified string is a valid extrahost and returns it.
|
|
|
|
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
|
|
|
|
// for add-host flag
|
|
|
|
func validateExtraHost(val string) error {
|
|
|
|
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
|
|
|
|
arr := strings.SplitN(val, ":", 2)
|
|
|
|
if len(arr) != 2 || len(arr[0]) == 0 {
|
2020-04-04 04:34:43 +08:00
|
|
|
return errors.Errorf("bad format for add-host: %q", val)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
if _, err := validateIPAddress(arr[1]); err != nil {
|
2020-04-04 04:34:43 +08:00
|
|
|
return errors.Errorf("invalid IP address in add-host: %q", arr[1])
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// validateIPAddress validates an Ip address.
|
|
|
|
// for dns, ip, and ip6 flags also
|
|
|
|
func validateIPAddress(val string) (string, error) {
|
|
|
|
var ip = net.ParseIP(strings.TrimSpace(val))
|
|
|
|
if ip != nil {
|
|
|
|
return ip.String(), nil
|
|
|
|
}
|
2020-04-04 04:34:43 +08:00
|
|
|
return "", errors.Errorf("%s is not an ip address", val)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// SystemContextFromOptions returns a SystemContext populated with values
|
|
|
|
// per the input parameters provided by the caller for the use in authentication.
|
2019-01-22 23:35:52 +08:00
|
|
|
func SystemContextFromOptions(c *cobra.Command) (*types.SystemContext, error) {
|
|
|
|
certDir, err := c.Flags().GetString("cert-dir")
|
|
|
|
if err != nil {
|
|
|
|
certDir = ""
|
|
|
|
}
|
2018-04-19 03:00:15 +08:00
|
|
|
ctx := &types.SystemContext{
|
2019-01-22 23:35:52 +08:00
|
|
|
DockerCertPath: certDir,
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
tlsVerify, err := c.Flags().GetBool("tls-verify")
|
|
|
|
if err == nil && c.Flag("tls-verify").Changed {
|
2019-02-20 23:25:24 +08:00
|
|
|
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!tlsVerify)
|
|
|
|
ctx.OCIInsecureSkipTLSVerify = !tlsVerify
|
|
|
|
ctx.DockerDaemonInsecureSkipTLSVerify = !tlsVerify
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
2021-04-11 01:44:51 +08:00
|
|
|
disableCompression, err := c.Flags().GetBool("disable-compression")
|
|
|
|
if err == nil {
|
|
|
|
if disableCompression {
|
|
|
|
ctx.OCIAcceptUncompressedLayers = true
|
|
|
|
} else {
|
|
|
|
ctx.DirForceCompress = true
|
|
|
|
}
|
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
creds, err := c.Flags().GetString("creds")
|
|
|
|
if err == nil && c.Flag("creds").Changed {
|
2018-04-19 03:00:15 +08:00
|
|
|
var err error
|
2021-03-22 20:02:39 +08:00
|
|
|
ctx.DockerAuthConfig, err = AuthConfig(creds)
|
2018-04-19 03:00:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
sigPolicy, err := c.Flags().GetString("signature-policy")
|
|
|
|
if err == nil && c.Flag("signature-policy").Changed {
|
|
|
|
ctx.SignaturePolicyPath = sigPolicy
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
authfile, err := c.Flags().GetString("authfile")
|
2019-04-10 03:57:12 +08:00
|
|
|
if err == nil {
|
|
|
|
ctx.AuthFilePath = getAuthFile(authfile)
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
regConf, err := c.Flags().GetString("registries-conf")
|
|
|
|
if err == nil && c.Flag("registries-conf").Changed {
|
|
|
|
ctx.SystemRegistriesConfPath = regConf
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
2019-01-22 23:35:52 +08:00
|
|
|
regConfDir, err := c.Flags().GetString("registries-conf-dir")
|
|
|
|
if err == nil && c.Flag("registries-conf-dir").Changed {
|
|
|
|
ctx.RegistriesDirPath = regConfDir
|
2018-04-19 03:00:15 +08:00
|
|
|
}
|
2021-05-12 00:21:09 +08:00
|
|
|
shortNameAliasConf, err := c.Flags().GetString("short-name-alias-conf")
|
|
|
|
if err == nil && c.Flag("short-name-alias-conf").Changed {
|
|
|
|
ctx.UserShortNameAliasConfPath = shortNameAliasConf
|
|
|
|
}
|
2021-02-07 06:49:40 +08:00
|
|
|
ctx.DockerRegistryUserAgent = fmt.Sprintf("Buildah/%s", define.Version)
|
2021-01-05 01:01:23 +08:00
|
|
|
if c.Flag("os") != nil && c.Flag("os").Changed {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
var os string
|
|
|
|
if os, err = c.Flags().GetString("os"); err != nil {
|
|
|
|
return nil, err
|
2021-01-05 01:01:23 +08:00
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
ctx.OSChoice = os
|
2019-09-11 06:21:07 +08:00
|
|
|
}
|
2021-01-05 01:01:23 +08:00
|
|
|
if c.Flag("arch") != nil && c.Flag("arch").Changed {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
var arch string
|
|
|
|
if arch, err = c.Flags().GetString("arch"); err != nil {
|
|
|
|
return nil, err
|
2021-01-05 01:01:23 +08:00
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
ctx.ArchitectureChoice = arch
|
2020-12-23 05:12:38 +08:00
|
|
|
}
|
2021-01-05 01:01:23 +08:00
|
|
|
if c.Flag("variant") != nil && c.Flag("variant").Changed {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
var variant string
|
|
|
|
if variant, err = c.Flags().GetString("variant"); err != nil {
|
|
|
|
return nil, err
|
2020-12-23 05:12:38 +08:00
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
ctx.VariantChoice = variant
|
2021-01-05 01:01:23 +08:00
|
|
|
}
|
|
|
|
if c.Flag("platform") != nil && c.Flag("platform").Changed {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
var specs []string
|
|
|
|
if specs, err = c.Flags().GetStringSlice("platform"); err != nil {
|
|
|
|
return nil, err
|
2020-12-23 05:12:38 +08:00
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
if len(specs) == 0 || specs[0] == "" {
|
|
|
|
return nil, errors.Errorf("unable to parse --platform value %v", specs)
|
|
|
|
}
|
|
|
|
platform := specs[0]
|
|
|
|
os, arch, variant, err := Platform(platform)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if ctx.OSChoice != "" || ctx.ArchitectureChoice != "" || ctx.VariantChoice != "" {
|
|
|
|
return nil, errors.Errorf("invalid --platform may not be used with --os, --arch, or --variant")
|
|
|
|
}
|
|
|
|
ctx.OSChoice = os
|
|
|
|
ctx.ArchitectureChoice = arch
|
|
|
|
ctx.VariantChoice = variant
|
2020-12-23 05:12:38 +08:00
|
|
|
}
|
|
|
|
|
2021-01-07 03:47:09 +08:00
|
|
|
ctx.BigFilesTemporaryDir = GetTempDir()
|
2018-04-19 03:00:15 +08:00
|
|
|
return ctx, nil
|
|
|
|
}
|
|
|
|
|
2019-04-10 03:57:12 +08:00
|
|
|
func getAuthFile(authfile string) string {
|
|
|
|
if authfile != "" {
|
|
|
|
return authfile
|
|
|
|
}
|
|
|
|
return os.Getenv("REGISTRY_AUTH_FILE")
|
|
|
|
}
|
|
|
|
|
2020-01-20 19:31:09 +08:00
|
|
|
// PlatformFromOptions parses the operating system (os) and architecture (arch)
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
// from the provided command line options. Deprecated in favor of
|
|
|
|
// PlatformsFromOptions(), but kept here because it's part of our API.
|
2020-01-20 19:31:09 +08:00
|
|
|
func PlatformFromOptions(c *cobra.Command) (os, arch string, err error) {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
platforms, err := PlatformsFromOptions(c)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
if len(platforms) < 1 {
|
|
|
|
return "", "", errors.Errorf("invalid platform syntax for --platform (use OS/ARCH[/VARIANT])")
|
|
|
|
}
|
|
|
|
return platforms[0].OS, platforms[0].Arch, nil
|
|
|
|
}
|
2020-01-20 19:31:09 +08:00
|
|
|
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
// PlatformsFromOptions parses the operating system (os) and architecture
|
|
|
|
// (arch) from the provided command line options. If --platform used, it
|
|
|
|
// also returns the list of platforms that were passed in as its argument.
|
|
|
|
func PlatformsFromOptions(c *cobra.Command) (platforms []struct{ OS, Arch, Variant string }, err error) {
|
|
|
|
var os, arch, variant string
|
2021-01-05 01:01:23 +08:00
|
|
|
if c.Flag("os").Changed {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
if os, err = c.Flags().GetString("os"); err != nil {
|
|
|
|
return nil, err
|
2021-01-05 01:01:23 +08:00
|
|
|
}
|
2020-01-20 19:31:09 +08:00
|
|
|
}
|
2021-01-05 01:01:23 +08:00
|
|
|
if c.Flag("arch").Changed {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
if arch, err = c.Flags().GetString("arch"); err != nil {
|
|
|
|
return nil, err
|
2021-01-05 01:01:23 +08:00
|
|
|
}
|
2020-01-20 19:31:09 +08:00
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
if c.Flag("variant").Changed {
|
|
|
|
if variant, err = c.Flags().GetString("variant"); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
platforms = []struct{ OS, Arch, Variant string }{{os, arch, variant}}
|
2021-01-05 01:01:23 +08:00
|
|
|
if c.Flag("platform").Changed {
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
platforms = nil
|
|
|
|
platformSpecs, err := c.Flags().GetStringSlice("platform")
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "unable to parse platform")
|
|
|
|
}
|
|
|
|
if os != "" || arch != "" || variant != "" {
|
|
|
|
return nil, errors.Errorf("invalid --platform may not be used with --os, --arch, or --variant")
|
|
|
|
}
|
|
|
|
for _, pf := range platformSpecs {
|
|
|
|
if os, arch, variant, err = Platform(pf); err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "unable to parse platform %q", pf)
|
2021-01-05 01:01:23 +08:00
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
platforms = append(platforms, struct{ OS, Arch, Variant string }{os, arch, variant})
|
2020-01-20 19:31:09 +08:00
|
|
|
}
|
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
return platforms, nil
|
2020-01-20 19:31:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const platformSep = "/"
|
|
|
|
|
|
|
|
// DefaultPlatform returns the standard platform for the current system
|
|
|
|
func DefaultPlatform() string {
|
2021-09-28 05:26:01 +08:00
|
|
|
return platforms.DefaultString()
|
2020-01-20 19:31:09 +08:00
|
|
|
}
|
|
|
|
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
// Platform separates the platform string into os, arch and variant,
|
|
|
|
// accepting any of $arch, $os/$arch, or $os/$arch/$variant.
|
2021-03-31 03:26:24 +08:00
|
|
|
func Platform(platform string) (os, arch, variant string, err error) {
|
2020-01-20 19:31:09 +08:00
|
|
|
split := strings.Split(platform, platformSep)
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
switch len(split) {
|
|
|
|
case 3:
|
2020-12-23 05:12:38 +08:00
|
|
|
variant = split[2]
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
fallthrough
|
|
|
|
case 2:
|
|
|
|
arch = split[1]
|
|
|
|
os = split[0]
|
|
|
|
return
|
|
|
|
case 1:
|
|
|
|
if platform == "local" {
|
|
|
|
return Platform(DefaultPlatform())
|
|
|
|
}
|
2020-01-20 19:31:09 +08:00
|
|
|
}
|
bud: teach --platform to take a list
Add a pkg/parse.PlatformsFromOptions() which understands a "variant"
value as an optional third value in an OS/ARCH[/VARIANT] argument value,
which accepts a comma-separated list of them, and which returns a list
of platforms.
Teach "from" and "pull" about the --platform option and add integration
tests for them, warning if --platform was given multiple values.
Add a define.BuildOptions.JobSemaphore which an imagebuildah executor
will use in preference to one that it might allocate for itself.
In main(), allocate a JobSemaphore if the number of jobs is not 0 (which
we treat as "unlimited", and continue to allow executors to do).
In addManifest(), take a lock on the manifest list's image ID so that we
don't overwrite changes that another thread might be making while we're
attempting to make changes to it. In main(), create an empty list if
the list doesn't already exist before we start down this path, so that
we don't get two threads trying to create that manifest list at the same
time later on. Two processes could still try to create the same list
twice, but it's an incremental improvement.
Finally, if we've been given multiple platforms to build for, run their
builds concurrently and gather up their results.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2021-06-22 22:52:49 +08:00
|
|
|
return "", "", "", errors.Errorf("invalid platform syntax for %q (use OS/ARCH[/VARIANT][,...])", platform)
|
2020-01-20 19:31:09 +08:00
|
|
|
}
|
|
|
|
|
2018-04-19 03:00:15 +08:00
|
|
|
func parseCreds(creds string) (string, string) {
|
|
|
|
if creds == "" {
|
|
|
|
return "", ""
|
|
|
|
}
|
|
|
|
up := strings.SplitN(creds, ":", 2)
|
|
|
|
if len(up) == 1 {
|
|
|
|
return up[0], ""
|
|
|
|
}
|
|
|
|
if up[0] == "" {
|
|
|
|
return "", up[1]
|
|
|
|
}
|
|
|
|
return up[0], up[1]
|
|
|
|
}
|
|
|
|
|
2021-03-22 20:02:39 +08:00
|
|
|
// AuthConfig parses the creds in format [username[:password] into an auth
|
|
|
|
// config.
|
|
|
|
func AuthConfig(creds string) (*types.DockerAuthConfig, error) {
|
2018-04-19 03:00:15 +08:00
|
|
|
username, password := parseCreds(creds)
|
|
|
|
if username == "" {
|
|
|
|
fmt.Print("Username: ")
|
|
|
|
fmt.Scanln(&username)
|
|
|
|
}
|
|
|
|
if password == "" {
|
|
|
|
fmt.Print("Password: ")
|
2021-08-25 03:30:13 +08:00
|
|
|
termPassword, err := term.ReadPassword(0)
|
2018-04-19 03:00:15 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "could not read password from terminal")
|
|
|
|
}
|
|
|
|
password = string(termPassword)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &types.DockerAuthConfig{
|
|
|
|
Username: username,
|
|
|
|
Password: password,
|
|
|
|
}, nil
|
|
|
|
}
|
2018-06-07 12:45:17 +08:00
|
|
|
|
2018-06-16 00:28:03 +08:00
|
|
|
// IDMappingOptions parses the build options related to user namespaces and ID mapping.
|
2021-02-07 06:49:40 +08:00
|
|
|
func IDMappingOptions(c *cobra.Command, isolation define.Isolation) (usernsOptions define.NamespaceOptions, idmapOptions *define.IDMappingOptions, err error) {
|
2022-01-13 03:14:46 +08:00
|
|
|
return IDMappingOptionsFromFlagSet(c.Flags(), c.PersistentFlags(), c.Flag)
|
|
|
|
}
|
|
|
|
|
|
|
|
// IDMappingOptionsFromFlagSet parses the build options related to user namespaces and ID mapping.
|
|
|
|
func IDMappingOptionsFromFlagSet(flags *pflag.FlagSet, persistentFlags *pflag.FlagSet, findFlagFunc func(name string) *pflag.Flag) (usernsOptions define.NamespaceOptions, idmapOptions *define.IDMappingOptions, err error) {
|
|
|
|
user := findFlagFunc("userns-uid-map-user").Value.String()
|
|
|
|
group := findFlagFunc("userns-gid-map-group").Value.String()
|
2018-06-07 12:45:17 +08:00
|
|
|
// If only the user or group was specified, use the same value for the
|
|
|
|
// other, since we need both in order to initialize the maps using the
|
|
|
|
// names.
|
|
|
|
if user == "" && group != "" {
|
|
|
|
user = group
|
|
|
|
}
|
|
|
|
if group == "" && user != "" {
|
|
|
|
group = user
|
|
|
|
}
|
|
|
|
// Either start with empty maps or the name-based maps.
|
|
|
|
mappings := idtools.NewIDMappingsFromMaps(nil, nil)
|
|
|
|
if user != "" && group != "" {
|
|
|
|
submappings, err := idtools.NewIDMappings(user, group)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
mappings = submappings
|
|
|
|
}
|
2022-01-13 03:14:46 +08:00
|
|
|
globalOptions := persistentFlags
|
2018-06-07 12:45:17 +08:00
|
|
|
// We'll parse the UID and GID mapping options the same way.
|
|
|
|
buildIDMap := func(basemap []idtools.IDMap, option string) ([]specs.LinuxIDMapping, error) {
|
|
|
|
outmap := make([]specs.LinuxIDMapping, 0, len(basemap))
|
|
|
|
// Start with the name-based map entries.
|
|
|
|
for _, m := range basemap {
|
|
|
|
outmap = append(outmap, specs.LinuxIDMapping{
|
|
|
|
ContainerID: uint32(m.ContainerID),
|
|
|
|
HostID: uint32(m.HostID),
|
|
|
|
Size: uint32(m.Size),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
// Parse the flag's value as one or more triples (if it's even
|
|
|
|
// been set), and append them.
|
2018-06-15 10:03:55 +08:00
|
|
|
var spec []string
|
2019-01-22 23:35:52 +08:00
|
|
|
if globalOptions.Lookup(option) != nil && globalOptions.Lookup(option).Changed {
|
|
|
|
spec, _ = globalOptions.GetStringSlice(option)
|
2018-06-15 10:03:55 +08:00
|
|
|
}
|
2022-01-13 03:14:46 +08:00
|
|
|
if findFlagFunc(option).Changed {
|
|
|
|
spec, _ = flags.GetStringSlice(option)
|
2018-06-15 10:03:55 +08:00
|
|
|
}
|
|
|
|
idmap, err := parseIDMap(spec)
|
2018-06-07 12:45:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, m := range idmap {
|
|
|
|
outmap = append(outmap, specs.LinuxIDMapping{
|
|
|
|
ContainerID: m[0],
|
|
|
|
HostID: m[1],
|
|
|
|
Size: m[2],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return outmap, nil
|
|
|
|
}
|
|
|
|
uidmap, err := buildIDMap(mappings.UIDs(), "userns-uid-map")
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
gidmap, err := buildIDMap(mappings.GIDs(), "userns-gid-map")
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
// If we only have one map or the other populated at this point, then
|
|
|
|
// use the same mapping for both, since we know that no user or group
|
|
|
|
// name was specified, but a specific mapping was for one or the other.
|
|
|
|
if len(uidmap) == 0 && len(gidmap) != 0 {
|
|
|
|
uidmap = gidmap
|
|
|
|
}
|
|
|
|
if len(gidmap) == 0 && len(uidmap) != 0 {
|
|
|
|
gidmap = uidmap
|
|
|
|
}
|
2019-03-08 22:00:20 +08:00
|
|
|
|
2018-06-07 12:45:17 +08:00
|
|
|
// By default, having mappings configured means we use a user
|
|
|
|
// namespace. Otherwise, we don't.
|
2021-02-07 06:49:40 +08:00
|
|
|
usernsOption := define.NamespaceOption{
|
2018-06-07 12:45:17 +08:00
|
|
|
Name: string(specs.UserNamespace),
|
2019-03-20 04:08:02 +08:00
|
|
|
Host: len(uidmap) == 0 && len(gidmap) == 0,
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
|
|
|
// If the user specifically requested that we either use or don't use
|
|
|
|
// user namespaces, override that default.
|
2022-01-13 03:14:46 +08:00
|
|
|
if findFlagFunc("userns").Changed {
|
|
|
|
how := findFlagFunc("userns").Value.String()
|
2018-06-07 12:45:17 +08:00
|
|
|
switch how {
|
2020-08-04 22:13:17 +08:00
|
|
|
case "", "container", "private":
|
2018-06-07 12:45:17 +08:00
|
|
|
usernsOption.Host = false
|
|
|
|
case "host":
|
|
|
|
usernsOption.Host = true
|
|
|
|
default:
|
2020-09-30 22:13:02 +08:00
|
|
|
how = strings.TrimPrefix(how, "ns:")
|
2018-06-07 12:45:17 +08:00
|
|
|
if _, err := os.Stat(how); err != nil {
|
2021-03-27 17:17:12 +08:00
|
|
|
return nil, nil, errors.Wrapf(err, "checking %s namespace", string(specs.UserNamespace))
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
|
|
|
logrus.Debugf("setting %q namespace to %q", string(specs.UserNamespace), how)
|
|
|
|
usernsOption.Path = how
|
|
|
|
}
|
|
|
|
}
|
2021-02-07 06:49:40 +08:00
|
|
|
usernsOptions = define.NamespaceOptions{usernsOption}
|
2019-01-22 23:35:52 +08:00
|
|
|
|
2018-06-07 12:45:17 +08:00
|
|
|
// If the user requested that we use the host namespace, but also that
|
|
|
|
// we use mappings, that's not going to work.
|
|
|
|
if (len(uidmap) != 0 || len(gidmap) != 0) && usernsOption.Host {
|
|
|
|
return nil, nil, errors.Errorf("can not specify ID mappings while using host's user namespace")
|
|
|
|
}
|
2021-02-07 06:49:40 +08:00
|
|
|
return usernsOptions, &define.IDMappingOptions{
|
2018-06-07 12:45:17 +08:00
|
|
|
HostUIDMapping: usernsOption.Host,
|
|
|
|
HostGIDMapping: usernsOption.Host,
|
|
|
|
UIDMap: uidmap,
|
|
|
|
GIDMap: gidmap,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseIDMap(spec []string) (m [][3]uint32, err error) {
|
|
|
|
for _, s := range spec {
|
|
|
|
args := strings.FieldsFunc(s, func(r rune) bool { return !unicode.IsDigit(r) })
|
|
|
|
if len(args)%3 != 0 {
|
2020-04-04 04:34:43 +08:00
|
|
|
return nil, errors.Errorf("mapping %q is not in the form containerid:hostid:size[,...]", s)
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
|
|
|
for len(args) >= 3 {
|
|
|
|
cid, err := strconv.ParseUint(args[0], 10, 32)
|
|
|
|
if err != nil {
|
2020-04-04 04:34:43 +08:00
|
|
|
return nil, errors.Wrapf(err, "error parsing container ID %q from mapping %q as a number", args[0], s)
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
|
|
|
hostid, err := strconv.ParseUint(args[1], 10, 32)
|
|
|
|
if err != nil {
|
2020-04-04 04:34:43 +08:00
|
|
|
return nil, errors.Wrapf(err, "error parsing host ID %q from mapping %q as a number", args[1], s)
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
|
|
|
size, err := strconv.ParseUint(args[2], 10, 32)
|
|
|
|
if err != nil {
|
2020-04-04 04:34:43 +08:00
|
|
|
return nil, errors.Wrapf(err, "error parsing %q from mapping %q as a number", args[2], s)
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
|
|
|
m = append(m, [3]uint32{uint32(cid), uint32(hostid), uint32(size)})
|
|
|
|
args = args[3:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
2018-06-16 00:28:03 +08:00
|
|
|
// NamespaceOptions parses the build options for all namespaces except for user namespace.
|
2021-02-07 06:49:40 +08:00
|
|
|
func NamespaceOptions(c *cobra.Command) (namespaceOptions define.NamespaceOptions, networkPolicy define.NetworkConfigurationPolicy, err error) {
|
2022-01-12 04:29:28 +08:00
|
|
|
return NamespaceOptionsFromFlagSet(c.Flags(), c.Flag)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NamespaceOptionsFromFlagSet parses the build options for all namespaces except for user namespace.
|
|
|
|
func NamespaceOptionsFromFlagSet(flags *pflag.FlagSet, findFlagFunc func(name string) *pflag.Flag) (namespaceOptions define.NamespaceOptions, networkPolicy define.NetworkConfigurationPolicy, err error) {
|
2021-02-07 06:49:40 +08:00
|
|
|
options := make(define.NamespaceOptions, 0, 7)
|
|
|
|
policy := define.NetworkDefault
|
2021-11-19 04:20:50 +08:00
|
|
|
for _, what := range []string{"cgroupns", string(specs.IPCNamespace), "network", string(specs.PIDNamespace), string(specs.UTSNamespace)} {
|
2022-01-12 04:29:28 +08:00
|
|
|
if flags.Lookup(what) != nil && findFlagFunc(what).Changed {
|
|
|
|
how := findFlagFunc(what).Value.String()
|
2018-06-07 12:45:17 +08:00
|
|
|
switch what {
|
2020-08-04 22:13:17 +08:00
|
|
|
case "network":
|
2018-06-07 12:45:17 +08:00
|
|
|
what = string(specs.NetworkNamespace)
|
2021-11-19 04:20:50 +08:00
|
|
|
case "cgroupns":
|
|
|
|
what = string(specs.CgroupNamespace)
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
|
|
|
switch how {
|
2020-08-04 22:13:17 +08:00
|
|
|
case "", "container", "private":
|
2018-06-07 12:45:17 +08:00
|
|
|
logrus.Debugf("setting %q namespace to %q", what, "")
|
2021-10-26 16:53:20 +08:00
|
|
|
policy = define.NetworkEnabled
|
2021-02-07 06:49:40 +08:00
|
|
|
options.AddOrReplace(define.NamespaceOption{
|
2018-06-07 12:45:17 +08:00
|
|
|
Name: what,
|
|
|
|
})
|
|
|
|
case "host":
|
|
|
|
logrus.Debugf("setting %q namespace to host", what)
|
2021-10-26 16:53:20 +08:00
|
|
|
policy = define.NetworkEnabled
|
2021-02-07 06:49:40 +08:00
|
|
|
options.AddOrReplace(define.NamespaceOption{
|
2018-06-07 12:45:17 +08:00
|
|
|
Name: what,
|
|
|
|
Host: true,
|
|
|
|
})
|
|
|
|
default:
|
2019-07-16 22:47:25 +08:00
|
|
|
if what == string(specs.NetworkNamespace) {
|
2018-06-07 12:45:17 +08:00
|
|
|
if how == "none" {
|
2021-02-07 06:49:40 +08:00
|
|
|
options.AddOrReplace(define.NamespaceOption{
|
2018-06-07 12:45:17 +08:00
|
|
|
Name: what,
|
|
|
|
})
|
2021-02-07 06:49:40 +08:00
|
|
|
policy = define.NetworkDisabled
|
2018-06-07 12:45:17 +08:00
|
|
|
logrus.Debugf("setting network to disabled")
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-09-30 22:13:02 +08:00
|
|
|
how = strings.TrimPrefix(how, "ns:")
|
2018-06-07 12:45:17 +08:00
|
|
|
if _, err := os.Stat(how); err != nil {
|
2021-03-27 17:17:12 +08:00
|
|
|
return nil, define.NetworkDefault, errors.Wrapf(err, "checking %s namespace", what)
|
2018-06-07 12:45:17 +08:00
|
|
|
}
|
2021-02-07 06:49:40 +08:00
|
|
|
policy = define.NetworkEnabled
|
2018-06-07 12:45:17 +08:00
|
|
|
logrus.Debugf("setting %q namespace to %q", what, how)
|
2021-02-07 06:49:40 +08:00
|
|
|
options.AddOrReplace(define.NamespaceOption{
|
2018-06-07 12:45:17 +08:00
|
|
|
Name: what,
|
|
|
|
Path: how,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return options, policy, nil
|
|
|
|
}
|
2018-05-12 01:00:14 +08:00
|
|
|
|
2021-02-07 06:49:40 +08:00
|
|
|
func defaultIsolation() (define.Isolation, error) {
|
2018-05-12 01:08:18 +08:00
|
|
|
isolation, isSet := os.LookupEnv("BUILDAH_ISOLATION")
|
|
|
|
if isSet {
|
2018-10-04 14:19:12 +08:00
|
|
|
switch strings.ToLower(isolation) {
|
|
|
|
case "oci":
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationOCI, nil
|
2018-10-04 14:19:12 +08:00
|
|
|
case "rootless":
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationOCIRootless, nil
|
2018-10-04 14:19:12 +08:00
|
|
|
case "chroot":
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationChroot, nil
|
2018-10-04 14:19:12 +08:00
|
|
|
default:
|
|
|
|
return 0, errors.Errorf("unrecognized $BUILDAH_ISOLATION value %q", isolation)
|
2018-05-12 01:08:18 +08:00
|
|
|
}
|
2018-05-12 01:00:14 +08:00
|
|
|
}
|
2020-07-07 22:15:40 +08:00
|
|
|
if unshare.IsRootless() {
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationOCIRootless, nil
|
2020-07-07 22:15:40 +08:00
|
|
|
}
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationDefault, nil
|
2018-05-12 01:00:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsolationOption parses the --isolation flag.
|
2021-02-07 06:49:40 +08:00
|
|
|
func IsolationOption(isolation string) (define.Isolation, error) {
|
2019-01-22 23:35:52 +08:00
|
|
|
if isolation != "" {
|
|
|
|
switch strings.ToLower(isolation) {
|
2021-02-23 20:27:24 +08:00
|
|
|
case "oci", "default":
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationOCI, nil
|
2018-10-04 14:19:12 +08:00
|
|
|
case "rootless":
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationOCIRootless, nil
|
2018-10-04 14:19:12 +08:00
|
|
|
case "chroot":
|
2021-02-07 06:49:40 +08:00
|
|
|
return define.IsolationChroot, nil
|
2018-10-04 14:19:12 +08:00
|
|
|
default:
|
2019-01-22 23:35:52 +08:00
|
|
|
return 0, errors.Errorf("unrecognized isolation type %q", isolation)
|
2018-05-12 01:00:14 +08:00
|
|
|
}
|
|
|
|
}
|
2018-05-12 01:08:18 +08:00
|
|
|
return defaultIsolation()
|
2018-05-12 01:00:14 +08:00
|
|
|
}
|
2019-04-10 03:57:12 +08:00
|
|
|
|
2019-09-07 03:07:18 +08:00
|
|
|
// Device parses device mapping string to a src, dest & permissions string
|
2020-12-22 00:19:56 +08:00
|
|
|
// Valid values for device look like:
|
2019-09-07 03:07:18 +08:00
|
|
|
// '/dev/sdc"
|
|
|
|
// '/dev/sdc:/dev/xvdc"
|
|
|
|
// '/dev/sdc:/dev/xvdc:rwm"
|
|
|
|
// '/dev/sdc:rm"
|
|
|
|
func Device(device string) (string, string, string, error) {
|
|
|
|
src := ""
|
|
|
|
dst := ""
|
|
|
|
permissions := "rwm"
|
|
|
|
arr := strings.Split(device, ":")
|
|
|
|
switch len(arr) {
|
|
|
|
case 3:
|
|
|
|
if !isValidDeviceMode(arr[2]) {
|
2020-04-04 04:34:43 +08:00
|
|
|
return "", "", "", errors.Errorf("invalid device mode: %s", arr[2])
|
2019-09-07 03:07:18 +08:00
|
|
|
}
|
|
|
|
permissions = arr[2]
|
|
|
|
fallthrough
|
|
|
|
case 2:
|
|
|
|
if isValidDeviceMode(arr[1]) {
|
|
|
|
permissions = arr[1]
|
|
|
|
} else {
|
|
|
|
if len(arr[1]) == 0 || arr[1][0] != '/' {
|
2020-04-04 04:34:43 +08:00
|
|
|
return "", "", "", errors.Errorf("invalid device mode: %s", arr[1])
|
2019-09-07 03:07:18 +08:00
|
|
|
}
|
|
|
|
dst = arr[1]
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
case 1:
|
|
|
|
if len(arr[0]) > 0 {
|
|
|
|
src = arr[0]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
default:
|
2020-04-04 04:34:43 +08:00
|
|
|
return "", "", "", errors.Errorf("invalid device specification: %s", device)
|
2019-09-07 03:07:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if dst == "" {
|
|
|
|
dst = src
|
|
|
|
}
|
|
|
|
return src, dst, permissions, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// isValidDeviceMode checks if the mode for device is valid or not.
|
|
|
|
// isValid mode is a composition of r (read), w (write), and m (mknod).
|
|
|
|
func isValidDeviceMode(mode string) bool {
|
|
|
|
var legalDeviceMode = map[rune]bool{
|
|
|
|
'r': true,
|
|
|
|
'w': true,
|
|
|
|
'm': true,
|
|
|
|
}
|
|
|
|
if mode == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for _, c := range mode {
|
|
|
|
if !legalDeviceMode[c] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
legalDeviceMode[c] = false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
2019-12-18 00:01:52 +08:00
|
|
|
|
|
|
|
func GetTempDir() string {
|
|
|
|
if tmpdir, ok := os.LookupEnv("TMPDIR"); ok {
|
|
|
|
return tmpdir
|
|
|
|
}
|
|
|
|
return "/var/tmp"
|
|
|
|
}
|
2021-04-17 06:21:31 +08:00
|
|
|
|
|
|
|
// Secrets parses the --secret flag
|
2021-10-22 04:14:05 +08:00
|
|
|
func Secrets(secrets []string) (map[string]define.Secret, error) {
|
|
|
|
invalidSyntax := errors.Errorf("incorrect secret flag format: should be --secret id=foo,src=bar[,env=ENV,type=file|env]")
|
|
|
|
parsed := make(map[string]define.Secret)
|
2021-04-17 06:21:31 +08:00
|
|
|
for _, secret := range secrets {
|
2021-10-22 04:14:05 +08:00
|
|
|
tokens := strings.Split(secret, ",")
|
|
|
|
var id, src, typ string
|
|
|
|
for _, val := range tokens {
|
|
|
|
kv := strings.SplitN(val, "=", 2)
|
|
|
|
switch kv[0] {
|
|
|
|
case "id":
|
|
|
|
id = kv[1]
|
|
|
|
case "src":
|
|
|
|
src = kv[1]
|
|
|
|
case "env":
|
|
|
|
src = kv[1]
|
|
|
|
typ = "env"
|
|
|
|
case "type":
|
|
|
|
if kv[1] != "file" && kv[1] != "env" {
|
|
|
|
return nil, errors.New("invalid secret type, must be file or env")
|
|
|
|
}
|
|
|
|
typ = kv[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if id == "" {
|
2021-04-17 06:21:31 +08:00
|
|
|
return nil, invalidSyntax
|
|
|
|
}
|
2021-10-22 04:14:05 +08:00
|
|
|
if src == "" {
|
|
|
|
src = id
|
|
|
|
}
|
|
|
|
if typ == "" {
|
|
|
|
if _, ok := os.LookupEnv(id); ok {
|
|
|
|
typ = "env"
|
2021-04-17 06:21:31 +08:00
|
|
|
} else {
|
2021-10-22 04:14:05 +08:00
|
|
|
typ = "file"
|
2021-04-17 06:21:31 +08:00
|
|
|
}
|
|
|
|
}
|
2021-10-22 04:14:05 +08:00
|
|
|
|
|
|
|
if typ == "file" {
|
|
|
|
fullPath, err := filepath.Abs(src)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not parse secrets")
|
|
|
|
}
|
|
|
|
_, err = os.Stat(fullPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not parse secrets")
|
|
|
|
}
|
|
|
|
src = fullPath
|
|
|
|
}
|
|
|
|
newSecret := define.Secret{
|
|
|
|
Source: src,
|
|
|
|
SourceType: typ,
|
|
|
|
}
|
|
|
|
parsed[id] = newSecret
|
|
|
|
|
2021-04-17 06:21:31 +08:00
|
|
|
}
|
|
|
|
return parsed, nil
|
|
|
|
}
|
2021-07-26 14:07:23 +08:00
|
|
|
|
|
|
|
// SSH parses the --ssh flag
|
|
|
|
func SSH(sshSources []string) (map[string]*sshagent.Source, error) {
|
|
|
|
parsed := make(map[string]*sshagent.Source)
|
|
|
|
var paths []string
|
|
|
|
for _, v := range sshSources {
|
|
|
|
parts := strings.SplitN(v, "=", 2)
|
|
|
|
if len(parts) > 1 {
|
|
|
|
paths = strings.Split(parts[1], ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
source, err := sshagent.NewSource(paths)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
parsed[parts[0]] = source
|
|
|
|
}
|
|
|
|
return parsed, nil
|
|
|
|
}
|
2021-10-07 21:10:22 +08:00
|
|
|
|
|
|
|
func ContainerIgnoreFile(contextDir, path string) ([]string, string, error) {
|
|
|
|
if path != "" {
|
|
|
|
excludes, err := imagebuilder.ParseIgnore(path)
|
|
|
|
return excludes, path, err
|
|
|
|
}
|
|
|
|
path = filepath.Join(contextDir, ".containerignore")
|
|
|
|
excludes, err := imagebuilder.ParseIgnore(path)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
path = filepath.Join(contextDir, ".dockerignore")
|
|
|
|
excludes, err = imagebuilder.ParseIgnore(path)
|
|
|
|
}
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return excludes, "", nil
|
|
|
|
}
|
|
|
|
return excludes, path, err
|
|
|
|
}
|