Files
podman/pkg/util/utils.go
Daniel J Walsh a9e9fd4f5b If user specifies UIDMapSlice without GIDMapSlice, set them equal
We need to map slices set for both UID and GID maps to be equivalent if
not specified by user.  Currently if you do not specify both the containers
are not running.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #865
Approved by: baude
2018-05-31 22:30:16 +00:00

206 lines
5.2 KiB
Go

package util
import (
"fmt"
"strconv"
"strings"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/terminal"
)
// Helper function to determine the username/password passed
// in the creds string. It could be either or both.
func parseCreds(creds string) (string, string) {
if creds == "" {
return "", ""
}
up := strings.SplitN(creds, ":", 2)
if len(up) == 1 {
return up[0], ""
}
return up[0], up[1]
}
// ParseRegistryCreds takes a credentials string in the form USERNAME:PASSWORD
// and returns a DockerAuthConfig
func ParseRegistryCreds(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
}
// StringInSlice determines if a string is in a string slice, returns bool
func StringInSlice(s string, sl []string) bool {
for _, i := range sl {
if i == s {
return true
}
}
return false
}
// GetImageConfig converts the --change flag values in the format "CMD=/bin/bash USER=example"
// to a type v1.ImageConfig
func GetImageConfig(changes []string) (v1.ImageConfig, error) {
// USER=value | EXPOSE=value | ENV=value | ENTRYPOINT=value |
// CMD=value | VOLUME=value | WORKDIR=value | LABEL=key=value | STOPSIGNAL=value
var (
user string
env []string
entrypoint []string
cmd []string
workingDir string
stopSignal string
)
exposedPorts := make(map[string]struct{})
volumes := make(map[string]struct{})
labels := make(map[string]string)
for _, ch := range changes {
pair := strings.Split(ch, "=")
if len(pair) == 1 {
return v1.ImageConfig{}, errors.Errorf("no value given for instruction %q", ch)
}
switch pair[0] {
case "USER":
user = pair[1]
case "EXPOSE":
var st struct{}
exposedPorts[pair[1]] = st
case "ENV":
env = append(env, pair[1])
case "ENTRYPOINT":
entrypoint = append(entrypoint, pair[1])
case "CMD":
cmd = append(cmd, pair[1])
case "VOLUME":
var st struct{}
volumes[pair[1]] = st
case "WORKDIR":
workingDir = pair[1]
case "LABEL":
if len(pair) == 3 {
labels[pair[1]] = pair[2]
} else {
labels[pair[1]] = ""
}
case "STOPSIGNAL":
stopSignal = pair[1]
}
}
return v1.ImageConfig{
User: user,
ExposedPorts: exposedPorts,
Env: env,
Entrypoint: entrypoint,
Cmd: cmd,
Volumes: volumes,
WorkingDir: workingDir,
Labels: labels,
StopSignal: stopSignal,
}, nil
}
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
options := storage.IDMappingOptions{
HostUIDMapping: true,
HostGIDMapping: true,
}
if subGIDMap == "" && subUIDMap != "" {
subGIDMap = subUIDMap
}
if subUIDMap == "" && subGIDMap != "" {
subUIDMap = subGIDMap
}
if len(GIDMapSlice) == 0 && len(UIDMapSlice) != 0 {
GIDMapSlice = UIDMapSlice
}
if len(UIDMapSlice) == 0 && len(GIDMapSlice) != 0 {
UIDMapSlice = GIDMapSlice
}
parseTriple := func(spec []string) (container, host, size int, err error) {
cid, err := strconv.ParseUint(spec[0], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[0], err)
}
hid, err := strconv.ParseUint(spec[1], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[1], err)
}
sz, err := strconv.ParseUint(spec[2], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[2], err)
}
return int(cid), int(hid), int(sz), nil
}
parseIDMap := func(spec []string) (idmap []idtools.IDMap, err error) {
for _, uid := range spec {
splitmap := strings.SplitN(uid, ":", 3)
if len(splitmap) < 3 {
return nil, fmt.Errorf("invalid mapping requires 3 fields: %q", uid)
}
cid, hid, size, err := parseTriple(splitmap)
if err != nil {
return nil, err
}
pmap := idtools.IDMap{
ContainerID: cid,
HostID: hid,
Size: size,
}
idmap = append(idmap, pmap)
}
return idmap, nil
}
if subUIDMap != "" && subGIDMap != "" {
mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
if err != nil {
return nil, err
}
options.UIDMap = mappings.UIDs()
options.GIDMap = mappings.GIDs()
}
parsedUIDMap, err := parseIDMap(UIDMapSlice)
if err != nil {
return nil, err
}
parsedGIDMap, err := parseIDMap(GIDMapSlice)
if err != nil {
return nil, err
}
options.UIDMap = append(options.UIDMap, parsedUIDMap...)
options.GIDMap = append(options.GIDMap, parsedGIDMap...)
if len(options.UIDMap) > 0 {
options.HostUIDMapping = false
}
if len(options.GIDMap) > 0 {
options.HostGIDMapping = false
}
return &options, nil
}