2017-10-30 18:31:31 +00:00
//nolint
2017-10-27 02:03:21 +00:00
// most of these validate and parse functions have been taken from projectatomic/docker
// and modified for cri-o
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"os/user"
"path"
"regexp"
"strconv"
"strings"
units "github.com/docker/go-units"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
)
// Note: for flags that are in the form <number><unit>, use the RAMInBytes function
// from the units package in docker/go-units/size.go
var (
whiteSpaces = " \t"
alphaRegexp = regexp . MustCompile ( ` [a-zA-Z] ` )
domainRegexp = regexp . MustCompile ( ` ^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$ ` )
)
// 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
2017-10-30 18:31:31 +00:00
func validateExtraHost ( val string ) ( string , error ) { //nolint
2017-10-27 02:03:21 +00:00
// 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 val , 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 )
}
// validateAttach validates that the specified string is a valid attach option.
// for attach flag
2017-10-30 18:31:31 +00:00
func validateAttach ( val string ) ( string , error ) { //nolint
2017-10-27 02:03:21 +00:00
s := strings . ToLower ( val )
for _ , str := range [ ] string { "stdin" , "stdout" , "stderr" } {
if s == str {
return s , nil
}
}
return val , fmt . Errorf ( "valid streams are STDIN, STDOUT and STDERR" )
}
// validate the blkioWeight falls in the range of 10 to 1000
// for blkio-weight flag
2017-10-30 18:31:31 +00:00
func validateBlkioWeight ( val int64 ) ( int64 , error ) { //nolint
2017-10-27 02:03:21 +00:00
if val >= 10 && val <= 1000 {
return val , nil
}
return - 1 , errors . Errorf ( "invalid blkio weight %q, should be between 10 and 1000" , val )
}
// weightDevice is a structure that holds device:weight pair
type weightDevice struct {
path string
weight uint16
}
func ( w * weightDevice ) String ( ) string {
return fmt . Sprintf ( "%s:%d" , w . path , w . weight )
}
// validateweightDevice validates that the specified string has a valid device-weight format
// for blkio-weight-device flag
func validateweightDevice ( val string ) ( * weightDevice , error ) {
split := strings . SplitN ( val , ":" , 2 )
if len ( split ) != 2 {
return nil , fmt . Errorf ( "bad format: %s" , val )
}
if ! strings . HasPrefix ( split [ 0 ] , "/dev/" ) {
return nil , fmt . Errorf ( "bad format for device path: %s" , val )
}
weight , err := strconv . ParseUint ( split [ 1 ] , 10 , 0 )
if err != nil {
return nil , fmt . Errorf ( "invalid weight for device: %s" , val )
}
if weight > 0 && ( weight < 10 || weight > 1000 ) {
return nil , fmt . Errorf ( "invalid weight for device: %s" , val )
}
return & weightDevice {
path : split [ 0 ] ,
weight : uint16 ( weight ) ,
} , nil
}
// parseDevice parses a device mapping string to a container.DeviceMapping struct
// for device flag
2017-10-30 18:31:31 +00:00
func parseDevice ( device string ) ( * pb . Device , error ) { //nolint
2017-10-27 02:03:21 +00:00
_ , err := validateDevice ( device )
if err != nil {
return nil , errors . Wrapf ( err , "device string not valid %q" , device )
}
src := ""
dst := ""
permissions := "rwm"
arr := strings . Split ( device , ":" )
switch len ( arr ) {
case 3 :
permissions = arr [ 2 ]
fallthrough
case 2 :
if validDeviceMode ( arr [ 1 ] ) {
permissions = arr [ 1 ]
} else {
dst = arr [ 1 ]
}
fallthrough
case 1 :
src = arr [ 0 ]
default :
return nil , fmt . Errorf ( "invalid device specification: %s" , device )
}
if dst == "" {
dst = src
}
deviceMapping := & pb . Device {
ContainerPath : dst ,
HostPath : src ,
Permissions : permissions ,
}
return deviceMapping , nil
}
// validDeviceMode checks if the mode for device is valid or not.
// Valid mode is a composition of r (read), w (write), and m (mknod).
func validDeviceMode ( 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
}
// validateDevice validates a path for devices
// It will make sure 'val' is in the form:
// [host-dir:]container-path[:mode]
// It also validates the device mode.
func validateDevice ( val string ) ( string , error ) {
return validatePath ( val , validDeviceMode )
}
func validatePath ( val string , validator func ( string ) bool ) ( string , error ) {
var containerPath string
var mode string
if strings . Count ( val , ":" ) > 2 {
return val , fmt . Errorf ( "bad format for path: %s" , val )
}
split := strings . SplitN ( val , ":" , 3 )
if split [ 0 ] == "" {
return val , fmt . Errorf ( "bad format for path: %s" , val )
}
switch len ( split ) {
case 1 :
containerPath = split [ 0 ]
val = path . Clean ( containerPath )
case 2 :
if isValid := validator ( split [ 1 ] ) ; isValid {
containerPath = split [ 0 ]
mode = split [ 1 ]
val = fmt . Sprintf ( "%s:%s" , path . Clean ( containerPath ) , mode )
} else {
containerPath = split [ 1 ]
val = fmt . Sprintf ( "%s:%s" , split [ 0 ] , path . Clean ( containerPath ) )
}
case 3 :
containerPath = split [ 1 ]
mode = split [ 2 ]
if isValid := validator ( split [ 2 ] ) ; ! isValid {
return val , fmt . Errorf ( "bad mode specified: %s" , mode )
}
val = fmt . Sprintf ( "%s:%s:%s" , split [ 0 ] , containerPath , mode )
}
if ! path . IsAbs ( containerPath ) {
return val , fmt . Errorf ( "%s is not an absolute path" , containerPath )
}
return val , nil
}
// throttleDevice is a structure that holds device:rate_per_second pair
type throttleDevice struct {
path string
rate uint64
}
func ( t * throttleDevice ) String ( ) string {
return fmt . Sprintf ( "%s:%d" , t . path , t . rate )
}
// validateBpsDevice validates that the specified string has a valid device-rate format
// for device-read-bps and device-write-bps flags
func validateBpsDevice ( val string ) ( * throttleDevice , error ) {
split := strings . SplitN ( val , ":" , 2 )
if len ( split ) != 2 {
return nil , fmt . Errorf ( "bad format: %s" , val )
}
if ! strings . HasPrefix ( split [ 0 ] , "/dev/" ) {
return nil , fmt . Errorf ( "bad format for device path: %s" , val )
}
rate , err := units . RAMInBytes ( split [ 1 ] )
if err != nil {
return nil , fmt . Errorf ( "invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb" , val )
}
if rate < 0 {
return nil , fmt . Errorf ( "invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb" , val )
}
return & throttleDevice {
path : split [ 0 ] ,
rate : uint64 ( rate ) ,
} , nil
}
// validateIOpsDevice validates that the specified string has a valid device-rate format
// for device-write-iops and device-read-iops flags
2017-10-30 18:31:31 +00:00
func validateIOpsDevice ( val string ) ( * throttleDevice , error ) { //nolint
2017-10-27 02:03:21 +00:00
split := strings . SplitN ( val , ":" , 2 )
if len ( split ) != 2 {
return nil , fmt . Errorf ( "bad format: %s" , val )
}
if ! strings . HasPrefix ( split [ 0 ] , "/dev/" ) {
return nil , fmt . Errorf ( "bad format for device path: %s" , val )
}
rate , err := strconv . ParseUint ( split [ 1 ] , 10 , 64 )
if err != nil {
return nil , fmt . Errorf ( "invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer" , val )
}
if rate < 0 {
return nil , fmt . Errorf ( "invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer" , val )
}
return & throttleDevice {
path : split [ 0 ] ,
rate : uint64 ( rate ) ,
} , nil
}
// validateDNSSearch validates domain for resolvconf search configuration.
// A zero length domain is represented by a dot (.).
// for dns-search flag
2017-10-30 18:31:31 +00:00
func validateDNSSearch ( val string ) ( string , error ) { //nolint
2017-10-27 02:03:21 +00:00
if val = strings . Trim ( val , " " ) ; val == "." {
return val , nil
}
return validateDomain ( val )
}
func validateDomain ( val string ) ( string , error ) {
if alphaRegexp . FindString ( val ) == "" {
return "" , fmt . Errorf ( "%s is not a valid domain" , val )
}
ns := domainRegexp . FindSubmatch ( [ ] byte ( val ) )
if len ( ns ) > 0 && len ( ns [ 1 ] ) < 255 {
return string ( ns [ 1 ] ) , nil
}
return "" , fmt . Errorf ( "%s is not a valid domain" , val )
}
// validateEnv validates an environment variable and returns it.
// If no value is specified, it returns the current value using os.Getenv.
// for env flag
2017-10-30 18:31:31 +00:00
func validateEnv ( val string ) ( string , error ) { //nolint
2017-10-27 02:03:21 +00:00
arr := strings . Split ( val , "=" )
if len ( arr ) > 1 {
return val , nil
}
if ! doesEnvExist ( val ) {
return val , nil
}
return fmt . Sprintf ( "%s=%s" , val , os . Getenv ( val ) ) , nil
}
func doesEnvExist ( name string ) bool {
for _ , entry := range os . Environ ( ) {
parts := strings . SplitN ( entry , "=" , 2 )
if parts [ 0 ] == name {
return true
}
}
return false
}
// reads a file of line terminated key=value pairs, and overrides any keys
// present in the file with additional pairs specified in the override parameter
// for env-file and labels-file flags
func readKVStrings ( files [ ] string , override [ ] string ) ( [ ] string , error ) {
envVariables := [ ] string { }
for _ , ef := range files {
parsedVars , err := parseEnvFile ( ef )
if err != nil {
return nil , err
}
envVariables = append ( envVariables , parsedVars ... )
}
// parse the '-e' and '--env' after, to allow override
envVariables = append ( envVariables , override ... )
return envVariables , nil
}
// parseEnvFile reads a file with environment variables enumerated by lines
func parseEnvFile ( filename string ) ( [ ] string , error ) {
fh , err := os . Open ( filename )
if err != nil {
return [ ] string { } , err
}
defer fh . Close ( )
lines := [ ] string { }
scanner := bufio . NewScanner ( fh )
for scanner . Scan ( ) {
// trim the line from all leading whitespace first
line := strings . TrimLeft ( scanner . Text ( ) , whiteSpaces )
// line is not empty, and not starting with '#'
if len ( line ) > 0 && ! strings . HasPrefix ( line , "#" ) {
data := strings . SplitN ( line , "=" , 2 )
// trim the front of a variable, but nothing else
variable := strings . TrimLeft ( data [ 0 ] , whiteSpaces )
if strings . ContainsAny ( variable , whiteSpaces ) {
return [ ] string { } , errors . Errorf ( "variable %q has white spaces, poorly formatted environment" , variable )
}
if len ( data ) > 1 {
// pass the value through, no trimming
lines = append ( lines , fmt . Sprintf ( "%s=%s" , variable , data [ 1 ] ) )
} else {
// if only a pass-through variable is given, clean it up.
lines = append ( lines , fmt . Sprintf ( "%s=%s" , strings . TrimSpace ( line ) , os . Getenv ( line ) ) )
}
}
}
return lines , scanner . Err ( )
}
// NsIpc represents the container ipc stack.
// for ipc flag
type NsIpc string
// IsPrivate indicates whether the container uses its private ipc stack.
func ( n NsIpc ) IsPrivate ( ) bool {
return ! ( n . IsHost ( ) || n . IsContainer ( ) )
}
// IsHost indicates whether the container uses the host's ipc stack.
func ( n NsIpc ) IsHost ( ) bool {
return n == "host"
}
// IsContainer indicates whether the container uses a container's ipc stack.
func ( n NsIpc ) IsContainer ( ) bool {
parts := strings . SplitN ( string ( n ) , ":" , 2 )
return len ( parts ) > 1 && parts [ 0 ] == "container"
}
// Valid indicates whether the ipc stack is valid.
func ( n NsIpc ) Valid ( ) bool {
parts := strings . Split ( string ( n ) , ":" )
switch mode := parts [ 0 ] ; mode {
case "" , "host" :
case "container" :
if len ( parts ) != 2 || parts [ 1 ] == "" {
return false
}
default :
return false
}
return true
}
// Container returns the name of the container ipc stack is going to be used.
func ( n NsIpc ) Container ( ) string {
parts := strings . SplitN ( string ( n ) , ":" , 2 )
if len ( parts ) > 1 {
return parts [ 1 ]
}
return ""
}
// validateLabel validates that the specified string is a valid label, and returns it.
// Labels are in the form on key=value.
// for label flag
2017-10-30 18:31:31 +00:00
func validateLabel ( val string ) ( string , error ) { //nolint
2017-10-27 02:03:21 +00:00
if strings . Count ( val , "=" ) < 1 {
return "" , fmt . Errorf ( "bad attribute format: %s" , val )
}
return val , nil
}
// validateMACAddress validates a MAC address.
// for mac-address flag
2017-10-30 18:31:31 +00:00
func validateMACAddress ( val string ) ( string , error ) { //nolint
2017-10-27 02:03:21 +00:00
_ , err := net . ParseMAC ( strings . TrimSpace ( val ) )
if err != nil {
return "" , err
}
return val , nil
}
// validateLink validates that the specified string has a valid link format (containerName:alias).
2017-10-30 18:31:31 +00:00
func validateLink ( val string ) ( string , error ) { //nolint
2017-10-27 02:03:21 +00:00
if _ , _ , err := parseLink ( val ) ; err != nil {
return val , err
}
return val , nil
}
// parseLink parses and validates the specified string as a link format (name:alias)
func parseLink ( val string ) ( string , string , error ) {
if val == "" {
return "" , "" , fmt . Errorf ( "empty string specified for links" )
}
arr := strings . Split ( val , ":" )
if len ( arr ) > 2 {
return "" , "" , fmt . Errorf ( "bad format for links: %s" , val )
}
if len ( arr ) == 1 {
return val , val , nil
}
// This is kept because we can actually get a HostConfig with links
// from an already created container and the format is not `foo:bar`
// but `/foo:/c1/bar`
if strings . HasPrefix ( arr [ 0 ] , "/" ) {
_ , alias := path . Split ( arr [ 1 ] )
return arr [ 0 ] [ 1 : ] , alias , nil
}
return arr [ 0 ] , arr [ 1 ] , nil
}
// parseLoggingOpts validates the logDriver and logDriverOpts
// for log-opt and log-driver flags
2017-10-30 18:31:31 +00:00
func parseLoggingOpts ( logDriver string , logDriverOpt [ ] string ) ( map [ string ] string , error ) { //nolint
2017-10-27 02:03:21 +00:00
logOptsMap := convertKVStringsToMap ( logDriverOpt )
if logDriver == "none" && len ( logDriverOpt ) > 0 {
return map [ string ] string { } , errors . Errorf ( "invalid logging opts for driver %s" , logDriver )
}
return logOptsMap , nil
}
// NsPid represents the pid namespace of the container.
//for pid flag
type NsPid string
// IsPrivate indicates whether the container uses its own new pid namespace.
func ( n NsPid ) IsPrivate ( ) bool {
return ! ( n . IsHost ( ) || n . IsContainer ( ) )
}
// IsHost indicates whether the container uses the host's pid namespace.
func ( n NsPid ) IsHost ( ) bool {
return n == "host"
}
// IsContainer indicates whether the container uses a container's pid namespace.
func ( n NsPid ) IsContainer ( ) bool {
parts := strings . SplitN ( string ( n ) , ":" , 2 )
return len ( parts ) > 1 && parts [ 0 ] == "container"
}
// Valid indicates whether the pid namespace is valid.
func ( n NsPid ) Valid ( ) bool {
parts := strings . Split ( string ( n ) , ":" )
switch mode := parts [ 0 ] ; mode {
case "" , "host" :
case "container" :
if len ( parts ) != 2 || parts [ 1 ] == "" {
return false
}
default :
return false
}
return true
}
// Container returns the name of the container whose pid namespace is going to be used.
func ( n NsPid ) Container ( ) string {
parts := strings . SplitN ( string ( n ) , ":" , 2 )
if len ( parts ) > 1 {
return parts [ 1 ]
}
return ""
}
// parsePortSpecs receives port specs in the format of ip:public:private/proto and parses
// these in to the internal types
// for publish, publish-all, and expose flags
2017-10-30 18:31:31 +00:00
func parsePortSpecs ( ports [ ] string ) ( [ ] * pb . PortMapping , error ) { //nolint
2017-10-27 02:03:21 +00:00
var portMappings [ ] * pb . PortMapping
for _ , rawPort := range ports {
portMapping , err := parsePortSpec ( rawPort )
if err != nil {
return nil , err
}
portMappings = append ( portMappings , portMapping ... )
}
return portMappings , nil
}
func validateProto ( proto string ) bool {
for _ , availableProto := range [ ] string { "tcp" , "udp" } {
if availableProto == proto {
return true
}
}
return false
}
// parsePortSpec parses a port specification string into a slice of PortMappings
func parsePortSpec ( rawPort string ) ( [ ] * pb . PortMapping , error ) {
var proto string
rawIP , hostPort , containerPort := splitParts ( rawPort )
proto , containerPort = splitProtoPort ( containerPort )
// Strip [] from IPV6 addresses
ip , _ , err := net . SplitHostPort ( rawIP + ":" )
if err != nil {
return nil , fmt . Errorf ( "Invalid ip address %v: %s" , rawIP , err )
}
if ip != "" && net . ParseIP ( ip ) == nil {
return nil , fmt . Errorf ( "Invalid ip address: %s" , ip )
}
if containerPort == "" {
return nil , fmt . Errorf ( "No port specified: %s<empty>" , rawPort )
}
startPort , endPort , err := parsePortRange ( containerPort )
if err != nil {
return nil , fmt . Errorf ( "Invalid containerPort: %s" , containerPort )
}
var startHostPort , endHostPort uint64 = 0 , 0
if len ( hostPort ) > 0 {
startHostPort , endHostPort , err = parsePortRange ( hostPort )
if err != nil {
return nil , fmt . Errorf ( "Invalid hostPort: %s" , hostPort )
}
}
if hostPort != "" && ( endPort - startPort ) != ( endHostPort - startHostPort ) {
// Allow host port range iff containerPort is not a range.
// In this case, use the host port range as the dynamic
// host port range to allocate into.
if endPort != startPort {
return nil , fmt . Errorf ( "Invalid ranges specified for container and host Ports: %s and %s" , containerPort , hostPort )
}
}
if ! validateProto ( strings . ToLower ( proto ) ) {
return nil , fmt . Errorf ( "invalid proto: %s" , proto )
}
protocol := pb . Protocol_TCP
if strings . ToLower ( proto ) == "udp" {
protocol = pb . Protocol_UDP
}
var ports [ ] * pb . PortMapping
for i := uint64 ( 0 ) ; i <= ( endPort - startPort ) ; i ++ {
containerPort = strconv . FormatUint ( startPort + i , 10 )
if len ( hostPort ) > 0 {
hostPort = strconv . FormatUint ( startHostPort + i , 10 )
}
// Set hostPort to a range only if there is a single container port
// and a dynamic host port.
if startPort == endPort && startHostPort != endHostPort {
hostPort = fmt . Sprintf ( "%s-%s" , hostPort , strconv . FormatUint ( endHostPort , 10 ) )
}
ctrPort , err := strconv . ParseInt ( containerPort , 10 , 32 )
if err != nil {
return nil , err
}
hPort , err := strconv . ParseInt ( hostPort , 10 , 32 )
if err != nil {
return nil , err
}
port := & pb . PortMapping {
Protocol : protocol ,
ContainerPort : int32 ( ctrPort ) ,
HostPort : int32 ( hPort ) ,
HostIp : ip ,
}
ports = append ( ports , port )
}
return ports , nil
}
// parsePortRange parses and validates the specified string as a port-range (8000-9000)
func parsePortRange ( ports string ) ( uint64 , uint64 , error ) {
if ports == "" {
return 0 , 0 , fmt . Errorf ( "empty string specified for ports" )
}
if ! strings . Contains ( ports , "-" ) {
start , err := strconv . ParseUint ( ports , 10 , 16 )
end := start
return start , end , err
}
parts := strings . Split ( ports , "-" )
start , err := strconv . ParseUint ( parts [ 0 ] , 10 , 16 )
if err != nil {
return 0 , 0 , err
}
end , err := strconv . ParseUint ( parts [ 1 ] , 10 , 16 )
if err != nil {
return 0 , 0 , err
}
if end < start {
return 0 , 0 , fmt . Errorf ( "Invalid range specified for the Port: %s" , ports )
}
return start , end , nil
}
// splitParts separates the different parts of rawPort
func splitParts ( rawport string ) ( string , string , string ) {
parts := strings . Split ( rawport , ":" )
n := len ( parts )
containerport := parts [ n - 1 ]
switch n {
case 1 :
return "" , "" , containerport
case 2 :
return "" , parts [ 0 ] , containerport
case 3 :
return parts [ 0 ] , parts [ 1 ] , containerport
default :
return strings . Join ( parts [ : n - 2 ] , ":" ) , parts [ n - 2 ] , containerport
}
}
// splitProtoPort splits a port in the format of port/proto
func splitProtoPort ( rawPort string ) ( string , string ) {
parts := strings . Split ( rawPort , "/" )
l := len ( parts )
if len ( rawPort ) == 0 || l == 0 || len ( parts [ 0 ] ) == 0 {
return "" , ""
}
if l == 1 {
return "tcp" , rawPort
}
if len ( parts [ 1 ] ) == 0 {
return "tcp" , parts [ 0 ]
}
return parts [ 1 ] , parts [ 0 ]
}
// takes a local seccomp file and reads its file contents
// for security-opt flag
2017-10-30 18:31:31 +00:00
func parseSecurityOpts ( securityOpts [ ] string ) ( [ ] string , error ) { //nolint
2017-10-27 02:03:21 +00:00
for key , opt := range securityOpts {
con := strings . SplitN ( opt , "=" , 2 )
if len ( con ) == 1 && con [ 0 ] != "no-new-privileges" {
if strings . Index ( opt , ":" ) != - 1 {
con = strings . SplitN ( opt , ":" , 2 )
} else {
return securityOpts , fmt . Errorf ( "Invalid --security-opt: %q" , opt )
}
}
if con [ 0 ] == "seccomp" && con [ 1 ] != "unconfined" {
f , err := ioutil . ReadFile ( con [ 1 ] )
if err != nil {
return securityOpts , fmt . Errorf ( "opening seccomp profile (%s) failed: %v" , con [ 1 ] , err )
}
b := bytes . NewBuffer ( nil )
if err := json . Compact ( b , f ) ; err != nil {
return securityOpts , fmt . Errorf ( "compacting json for seccomp profile (%s) failed: %v" , con [ 1 ] , err )
}
securityOpts [ key ] = fmt . Sprintf ( "seccomp=%s" , b . Bytes ( ) )
}
}
return securityOpts , nil
}
// parses storage options per container into a map
// for storage-opt flag
2017-10-30 18:31:31 +00:00
func parseStorageOpts ( storageOpts [ ] string ) ( map [ string ] string , error ) { //nolint
2017-10-27 02:03:21 +00:00
m := make ( map [ string ] string )
for _ , option := range storageOpts {
if strings . Contains ( option , "=" ) {
opt := strings . SplitN ( option , "=" , 2 )
m [ opt [ 0 ] ] = opt [ 1 ]
} else {
return nil , errors . Errorf ( "invalid storage option %q" , option )
}
}
return m , nil
}
// parseUser parses the the uid and gid in the format <name|uid>[:<group|gid>]
// for user flag
// FIXME: Issue from https://github.com/projectatomic/buildah/issues/66
2017-10-30 18:31:31 +00:00
func parseUser ( rootdir , userspec string ) ( specs . User , error ) { //nolint
2017-10-27 02:03:21 +00:00
var gid64 uint64
var gerr error = user . UnknownGroupError ( "error looking up group" )
spec := strings . SplitN ( userspec , ":" , 2 )
userspec = spec [ 0 ]
groupspec := ""
if userspec == "" {
return specs . User { } , nil
}
if len ( spec ) > 1 {
groupspec = spec [ 1 ]
}
uid64 , uerr := strconv . ParseUint ( userspec , 10 , 32 )
if uerr == nil && groupspec == "" {
// We parsed the user name as a number, and there's no group
// component, so we need to look up the user's primary GID.
var name string
name , gid64 , gerr = lookupGroupForUIDInContainer ( rootdir , uid64 )
if gerr == nil {
userspec = name
} else {
if userrec , err := user . LookupId ( userspec ) ; err == nil {
gid64 , gerr = strconv . ParseUint ( userrec . Gid , 10 , 32 )
userspec = userrec . Name
}
}
}
if uerr != nil {
uid64 , gid64 , uerr = lookupUserInContainer ( rootdir , userspec )
gerr = uerr
}
if uerr != nil {
if userrec , err := user . Lookup ( userspec ) ; err == nil {
uid64 , uerr = strconv . ParseUint ( userrec . Uid , 10 , 32 )
gid64 , gerr = strconv . ParseUint ( userrec . Gid , 10 , 32 )
}
}
if groupspec != "" {
gid64 , gerr = strconv . ParseUint ( groupspec , 10 , 32 )
if gerr != nil {
gid64 , gerr = lookupGroupInContainer ( rootdir , groupspec )
}
if gerr != nil {
if group , err := user . LookupGroup ( groupspec ) ; err == nil {
gid64 , gerr = strconv . ParseUint ( group . Gid , 10 , 32 )
}
}
}
if uerr == nil && gerr == nil {
u := specs . User {
UID : uint32 ( uid64 ) ,
GID : uint32 ( gid64 ) ,
Username : userspec ,
}
return u , nil
}
err := errors . Wrapf ( uerr , "error determining run uid" )
if uerr == nil {
err = errors . Wrapf ( gerr , "error determining run gid" )
}
return specs . User { } , err
}
// convertKVStringsToMap converts ["key=value"] to {"key":"value"}
func convertKVStringsToMap ( values [ ] string ) map [ string ] string {
result := make ( map [ string ] string , len ( values ) )
for _ , value := range values {
kv := strings . SplitN ( value , "=" , 2 )
if len ( kv ) == 1 {
result [ kv [ 0 ] ] = ""
} else {
result [ kv [ 0 ] ] = kv [ 1 ]
}
}
return result
}
// NsUser represents userns mode in the container.
// for userns flag
type NsUser string
// IsHost indicates whether the container uses the host's userns.
func ( n NsUser ) IsHost ( ) bool {
return n == "host"
}
// IsPrivate indicates whether the container uses the a private userns.
func ( n NsUser ) IsPrivate ( ) bool {
return ! ( n . IsHost ( ) )
}
// Valid indicates whether the userns is valid.
func ( n NsUser ) Valid ( ) bool {
parts := strings . Split ( string ( n ) , ":" )
switch mode := parts [ 0 ] ; mode {
case "" , "host" :
default :
return false
}
return true
}
// NsUts represents the UTS namespace of the container.
// for uts flag
type NsUts string
// IsPrivate indicates whether the container uses its private UTS namespace.
func ( n NsUts ) IsPrivate ( ) bool {
return ! ( n . IsHost ( ) )
}
// IsHost indicates whether the container uses the host's UTS namespace.
func ( n NsUts ) IsHost ( ) bool {
return n == "host"
}
// Valid indicates whether the UTS namespace is valid.
func ( n NsUts ) Valid ( ) bool {
parts := strings . Split ( string ( n ) , ":" )
switch mode := parts [ 0 ] ; mode {
case "" , "host" :
default :
return false
}
return true
}
2017-10-30 18:31:31 +00:00
// Takes a stringslice and converts to a uint32slice
func stringSlicetoUint32Slice ( inputSlice [ ] string ) ( [ ] uint32 , error ) {
var outputSlice [ ] uint32
for _ , v := range inputSlice {
u , err := strconv . ParseUint ( v , 10 , 32 )
if err != nil {
return outputSlice , err
}
outputSlice = append ( outputSlice , uint32 ( u ) )
}
return outputSlice , nil
}