diff options
Diffstat (limited to 'vendor/github.com/projectatomic/buildah/pkg/parse/parse.go')
-rw-r--r-- | vendor/github.com/projectatomic/buildah/pkg/parse/parse.go | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/vendor/github.com/projectatomic/buildah/pkg/parse/parse.go b/vendor/github.com/projectatomic/buildah/pkg/parse/parse.go new file mode 100644 index 000000000..f2159d930 --- /dev/null +++ b/vendor/github.com/projectatomic/buildah/pkg/parse/parse.go @@ -0,0 +1,323 @@ +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" + "reflect" + "regexp" + "strings" + + "github.com/containers/image/types" + "github.com/docker/go-units" + "github.com/pkg/errors" + "github.com/projectatomic/buildah" + "github.com/urfave/cli" + "golang.org/x/crypto/ssh/terminal" +) + +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" +) + +// ParseCommonBuildOptions parses the build options from the bud cli +func ParseCommonBuildOptions(c *cli.Context) (*buildah.CommonBuildOptions, error) { + var ( + memoryLimit int64 + memorySwap int64 + err error + ) + if c.String("memory") != "" { + memoryLimit, err = units.RAMInBytes(c.String("memory")) + if err != nil { + return nil, errors.Wrapf(err, "invalid value for memory") + } + } + if c.String("memory-swap") != "" { + memorySwap, err = units.RAMInBytes(c.String("memory-swap")) + if err != nil { + return nil, errors.Wrapf(err, "invalid value for memory-swap") + } + } + if len(c.StringSlice("add-host")) > 0 { + for _, host := range c.StringSlice("add-host") { + if err := validateExtraHost(host); err != nil { + return nil, errors.Wrapf(err, "invalid value for add-host") + } + } + } + if _, err := units.FromHumanSize(c.String("shm-size")); err != nil { + return nil, errors.Wrapf(err, "invalid --shm-size") + } + if err := parseVolumes(c.StringSlice("volume")); err != nil { + return nil, err + } + + commonOpts := &buildah.CommonBuildOptions{ + AddHost: c.StringSlice("add-host"), + CgroupParent: c.String("cgroup-parent"), + CPUPeriod: c.Uint64("cpu-period"), + CPUQuota: c.Int64("cpu-quota"), + CPUSetCPUs: c.String("cpuset-cpus"), + CPUSetMems: c.String("cpuset-mems"), + CPUShares: c.Uint64("cpu-shares"), + Memory: memoryLimit, + MemorySwap: memorySwap, + ShmSize: c.String("shm-size"), + Ulimit: c.StringSlice("ulimit"), + Volumes: c.StringSlice("volume"), + } + if err := parseSecurityOpts(c.StringSlice("security-opt"), commonOpts); err != nil { + return nil, err + } + return commonOpts, nil +} + +func parseSecurityOpts(securityOpts []string, commonOpts *buildah.CommonBuildOptions) error { + 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 { + return errors.Errorf("Invalid --security-opt 1: %q", opt) + } + + 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) { + return errors.Wrapf(err, "can't check if %q exists", SeccompOverridePath) + } + if _, err := os.Stat(SeccompDefaultPath); err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "can't check if %q exists", SeccompDefaultPath) + } + } else { + commonOpts.SeccompProfilePath = SeccompDefaultPath + } + } + } + return nil +} + +func parseVolumes(volumes []string) error { + if len(volumes) == 0 { + return nil + } + for _, volume := range volumes { + arr := strings.SplitN(volume, ":", 3) + if len(arr) < 2 { + return errors.Errorf("incorrect volume format %q, should be host-dir:ctr-dir[:option]", volume) + } + if err := validateVolumeHostDir(arr[0]); err != nil { + return err + } + if err := validateVolumeCtrDir(arr[1]); err != nil { + return err + } + if len(arr) > 2 { + if err := validateVolumeOpts(arr[2]); err != nil { + return err + } + } + } + return nil +} + +func validateVolumeHostDir(hostDir string) error { + if _, err := os.Stat(hostDir); err != nil { + return errors.Wrapf(err, "error checking path %q", hostDir) + } + return nil +} + +func validateVolumeCtrDir(ctrDir string) error { + if ctrDir[0] != '/' { + return errors.Errorf("invalid container directory path %q", ctrDir) + } + return nil +} + +func validateVolumeOpts(option string) error { + var foundRootPropagation, foundRWRO, foundLabelChange int + options := strings.Split(option, ",") + for _, opt := range options { + switch opt { + case "rw", "ro": + if foundRWRO > 1 { + return errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", option) + } + foundRWRO++ + case "z", "Z": + if foundLabelChange > 1 { + return errors.Errorf("invalid options %q, can only specify 1 'z' or 'Z' option", option) + } + foundLabelChange++ + case "private", "rprivate", "shared", "rshared", "slave", "rslave": + if foundRootPropagation > 1 { + return errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private' or '[r]slave' option", option) + } + foundRootPropagation++ + default: + return errors.Errorf("invalid option type %q", option) + } + } + return nil +} + +// 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 { + return fmt.Errorf("bad format for add-host: %q", val) + } + if _, err := validateIPAddress(arr[1]); err != nil { + return fmt.Errorf("invalid IP address in add-host: %q", arr[1]) + } + 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 + } + return "", fmt.Errorf("%s is not an ip address", val) +} + +// ValidateFlags searches for StringFlags or StringSlice flags that never had +// a value set. This commonly occurs when the CLI mistakenly takes the next +// option and uses it as a value. +func ValidateFlags(c *cli.Context, flags []cli.Flag) error { + re, err := regexp.Compile("^-.+") + if err != nil { + return errors.Wrap(err, "compiling regex failed") + } + + // The --cmd flag can have a following command i.e. --cmd="--help". + // Let's skip this check just for the --cmd flag. + for _, flag := range flags { + switch reflect.TypeOf(flag).String() { + case "cli.StringSliceFlag": + { + f := flag.(cli.StringSliceFlag) + name := strings.Split(f.Name, ",") + if f.Name == "cmd" { + continue + } + val := c.StringSlice(name[0]) + for _, v := range val { + if ok := re.MatchString(v); ok { + return errors.Errorf("option --%s requires a value", name[0]) + } + } + } + case "cli.StringFlag": + { + f := flag.(cli.StringFlag) + name := strings.Split(f.Name, ",") + if f.Name == "cmd" { + continue + } + val := c.String(name[0]) + if ok := re.MatchString(val); ok { + return errors.Errorf("option --%s requires a value", name[0]) + } + } + } + } + return nil +} + +// SystemContextFromOptions returns a SystemContext populated with values +// per the input parameters provided by the caller for the use in authentication. +func SystemContextFromOptions(c *cli.Context) (*types.SystemContext, error) { + ctx := &types.SystemContext{ + DockerCertPath: c.String("cert-dir"), + } + if c.IsSet("tls-verify") { + ctx.DockerInsecureSkipTLSVerify = !c.BoolT("tls-verify") + } + if c.IsSet("creds") { + var err error + ctx.DockerAuthConfig, err = getDockerAuth(c.String("creds")) + if err != nil { + return nil, err + } + } + if c.IsSet("signature-policy") { + ctx.SignaturePolicyPath = c.String("signature-policy") + } + if c.IsSet("authfile") { + ctx.AuthFilePath = c.String("authfile") + } + if c.GlobalIsSet("registries-conf") { + ctx.SystemRegistriesConfPath = c.GlobalString("registries-conf") + } + if c.GlobalIsSet("registries-conf-dir") { + ctx.RegistriesDirPath = c.GlobalString("registries-conf-dir") + } + return ctx, nil +} + +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] +} + +func getDockerAuth(creds string) (*types.DockerAuthConfig, error) { + username, password := parseCreds(creds) + if username == "" { + fmt.Print("Username: ") + fmt.Scanln(&username) + } + if password == "" { + fmt.Print("Password: ") + termPassword, err := terminal.ReadPassword(0) + 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 +} |