2014-03-10 17:10:23 -04:00
package opts
import (
"fmt"
2014-06-13 08:02:12 -04:00
"net"
2014-03-10 17:10:23 -04:00
"os"
2014-10-29 18:46:45 -04:00
"path"
2014-03-10 17:10:23 -04:00
"regexp"
2015-06-11 20:34:20 -04:00
"strconv"
2014-03-10 17:10:23 -04:00
"strings"
2014-07-09 17:47:55 -04:00
2015-06-11 20:34:20 -04:00
"github.com/docker/docker/pkg/blkiodev"
2014-07-28 20:23:38 -04:00
"github.com/docker/docker/pkg/parsers"
2015-07-08 07:06:48 -04:00
"github.com/docker/docker/pkg/units"
2014-03-10 17:10:23 -04:00
)
2014-09-15 23:30:10 -04:00
var (
2015-07-12 04:33:30 -04:00
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*$ ` )
2015-08-10 08:48:08 -04:00
// DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. docker daemon -H tcp://
2015-04-23 16:45:34 -04:00
// TODO Windows. DefaultHTTPPort is only used on Windows if a -H parameter
// is not supplied. A better longer term solution would be to use a named
// pipe as the default on the Windows daemon.
2015-08-21 09:28:49 -04:00
// These are the IANA registered port numbers for use with Docker
// see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker
2015-07-12 04:33:30 -04:00
DefaultHTTPPort = 2375 // Default HTTP Port
2015-08-21 09:28:49 -04:00
// DefaultTLSHTTPPort Default HTTP Port used when TLS enabled
DefaultTLSHTTPPort = 2376 // Default TLS encrypted HTTP Port
2015-07-12 04:33:30 -04:00
// DefaultUnixSocket Path for the unix socket.
// Docker daemon by default always listens on the default unix socket
DefaultUnixSocket = "/var/run/docker.sock"
2015-08-28 23:05:18 -04:00
// DefaultTCPHost constant defines the default host string used by docker on Windows
DefaultTCPHost = fmt . Sprintf ( "tcp://%s:%d" , DefaultHTTPHost , DefaultHTTPPort )
2015-08-21 09:28:49 -04:00
// DefaultTLSHost constant defines the default host string used by docker for TLS sockets
DefaultTLSHost = fmt . Sprintf ( "tcp://%s:%d" , DefaultHTTPHost , DefaultTLSHTTPPort )
2014-09-15 23:30:10 -04:00
)
2015-08-27 03:33:21 -04:00
// ListOpts holds a list of values and a validation function.
2014-03-10 17:10:23 -04:00
type ListOpts struct {
2014-08-09 21:13:44 -04:00
values * [ ] string
2014-03-10 17:10:23 -04:00
validator ValidatorFctType
}
2015-08-27 03:33:21 -04:00
// NewListOpts creates a new ListOpts with the specified validator.
2014-03-10 17:10:23 -04:00
func NewListOpts ( validator ValidatorFctType ) ListOpts {
2014-08-09 21:13:44 -04:00
var values [ ] string
2015-05-05 00:18:28 -04:00
return * NewListOptsRef ( & values , validator )
2014-08-09 21:13:44 -04:00
}
2015-08-27 03:33:21 -04:00
// NewListOptsRef creates a new ListOpts with the specified values and validator.
2015-05-05 00:18:28 -04:00
func NewListOptsRef ( values * [ ] string , validator ValidatorFctType ) * ListOpts {
2014-08-09 21:13:44 -04:00
return & ListOpts {
values : values ,
2014-03-10 17:10:23 -04:00
validator : validator ,
}
}
func ( opts * ListOpts ) String ( ) string {
2014-08-09 21:13:44 -04:00
return fmt . Sprintf ( "%v" , [ ] string ( ( * opts . values ) ) )
2014-03-10 17:10:23 -04:00
}
// Set validates if needed the input value and add it to the
// internal slice.
func ( opts * ListOpts ) Set ( value string ) error {
if opts . validator != nil {
v , err := opts . validator ( value )
if err != nil {
return err
}
value = v
}
2014-08-09 21:13:44 -04:00
( * opts . values ) = append ( ( * opts . values ) , value )
2014-03-10 17:10:23 -04:00
return nil
}
2015-08-27 03:33:21 -04:00
// Delete removes the specified element from the slice.
2014-03-10 17:10:23 -04:00
func ( opts * ListOpts ) Delete ( key string ) {
2014-08-09 21:13:44 -04:00
for i , k := range * opts . values {
2014-03-10 17:10:23 -04:00
if k == key {
2014-08-09 21:13:44 -04:00
( * opts . values ) = append ( ( * opts . values ) [ : i ] , ( * opts . values ) [ i + 1 : ] ... )
2014-03-10 17:10:23 -04:00
return
}
}
}
// GetMap returns the content of values in a map in order to avoid
// duplicates.
func ( opts * ListOpts ) GetMap ( ) map [ string ] struct { } {
ret := make ( map [ string ] struct { } )
2014-08-09 21:13:44 -04:00
for _ , k := range * opts . values {
2014-03-10 17:10:23 -04:00
ret [ k ] = struct { } { }
}
return ret
}
2015-08-27 03:33:21 -04:00
// GetAll returns the values of slice.
2014-03-10 17:10:23 -04:00
func ( opts * ListOpts ) GetAll ( ) [ ] string {
2014-08-09 21:13:44 -04:00
return ( * opts . values )
2014-03-10 17:10:23 -04:00
}
2015-11-06 17:22:48 -05:00
// GetAllOrEmpty returns the values of the slice
// or an empty slice when there are no values.
func ( opts * ListOpts ) GetAllOrEmpty ( ) [ ] string {
v := * opts . values
if v == nil {
return make ( [ ] string , 0 )
}
return v
}
2015-08-27 03:33:21 -04:00
// Get checks the existence of the specified key.
2014-03-10 17:10:23 -04:00
func ( opts * ListOpts ) Get ( key string ) bool {
2014-08-09 21:13:44 -04:00
for _ , k := range * opts . values {
2014-03-10 17:10:23 -04:00
if k == key {
return true
}
}
return false
}
// Len returns the amount of element in the slice.
func ( opts * ListOpts ) Len ( ) int {
2014-08-09 21:13:44 -04:00
return len ( ( * opts . values ) )
2014-03-10 17:10:23 -04:00
}
2015-08-27 03:33:21 -04:00
//MapOpts holds a map of values and a validation function.
2015-05-04 17:39:48 -04:00
type MapOpts struct {
values map [ string ] string
validator ValidatorFctType
}
2015-07-12 04:33:30 -04:00
// Set validates if needed the input value and add it to the
// internal map, by splitting on '='.
2015-05-04 17:39:48 -04:00
func ( opts * MapOpts ) Set ( value string ) error {
if opts . validator != nil {
v , err := opts . validator ( value )
if err != nil {
return err
}
value = v
}
vals := strings . SplitN ( value , "=" , 2 )
if len ( vals ) == 1 {
( opts . values ) [ vals [ 0 ] ] = ""
} else {
( opts . values ) [ vals [ 0 ] ] = vals [ 1 ]
}
return nil
}
2015-08-27 03:33:21 -04:00
// GetAll returns the values of MapOpts as a map.
2015-06-12 09:25:32 -04:00
func ( opts * MapOpts ) GetAll ( ) map [ string ] string {
return opts . values
}
2015-05-04 17:39:48 -04:00
func ( opts * MapOpts ) String ( ) string {
return fmt . Sprintf ( "%v" , map [ string ] string ( ( opts . values ) ) )
}
2015-08-27 03:33:21 -04:00
// NewMapOpts creates a new MapOpts with the specified map of values and a validator.
2015-05-05 00:18:28 -04:00
func NewMapOpts ( values map [ string ] string , validator ValidatorFctType ) * MapOpts {
if values == nil {
values = make ( map [ string ] string )
}
2015-05-04 17:39:48 -04:00
return & MapOpts {
values : values ,
validator : validator ,
}
}
2015-08-27 03:33:21 -04:00
// ValidatorFctType defines a validator function that returns a validated string and/or an error.
2014-03-10 17:10:23 -04:00
type ValidatorFctType func ( val string ) ( string , error )
2015-07-12 04:33:30 -04:00
2015-06-11 20:34:20 -04:00
// ValidatorWeightFctType defines a validator function that returns a validated struct and/or an error.
type ValidatorWeightFctType func ( val string ) ( * blkiodev . WeightDevice , error )
2015-07-08 07:06:48 -04:00
// ValidatorThrottleFctType defines a validator function that returns a validated struct and/or an error.
type ValidatorThrottleFctType func ( val string ) ( * blkiodev . ThrottleDevice , error )
2015-08-27 03:33:21 -04:00
// ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
2014-10-06 21:54:52 -04:00
type ValidatorFctListType func ( val string ) ( [ ] string , error )
2014-03-10 17:10:23 -04:00
2015-08-27 03:33:21 -04:00
// ValidateAttach validates that the specified string is a valid attach option.
2014-03-10 17:10:23 -04:00
func ValidateAttach ( val string ) ( string , error ) {
2014-07-30 10:20:52 -04:00
s := strings . ToLower ( val )
for _ , str := range [ ] string { "stdin" , "stdout" , "stderr" } {
if s == str {
return s , nil
}
2014-03-10 17:10:23 -04:00
}
2015-07-12 04:33:30 -04:00
return val , fmt . Errorf ( "valid streams are STDIN, STDOUT and STDERR" )
2014-03-10 17:10:23 -04:00
}
2015-06-11 20:34:20 -04:00
// ValidateWeightDevice validates that the specified string has a valid device-weight format.
func ValidateWeightDevice ( val string ) ( * blkiodev . 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 & blkiodev . WeightDevice {
Path : split [ 0 ] ,
Weight : uint16 ( weight ) ,
} , nil
}
2015-07-08 07:06:48 -04:00
// ValidateThrottleBpsDevice validates that the specified string has a valid device-rate format.
func ValidateThrottleBpsDevice ( val string ) ( * blkiodev . 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 & blkiodev . ThrottleDevice {
Path : split [ 0 ] ,
Rate : uint64 ( rate ) ,
} , nil
}
2015-08-27 03:33:21 -04:00
// ValidateLink validates that the specified string has a valid link format (containerName:alias).
2014-03-10 17:10:23 -04:00
func ValidateLink ( val string ) ( string , error ) {
2015-05-07 16:02:14 -04:00
if _ , _ , err := parsers . ParseLink ( val ) ; err != nil {
2014-03-10 17:10:23 -04:00
return val , err
}
return val , nil
}
2015-08-24 05:57:12 -04:00
// 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
}
2015-08-27 03:33:21 -04:00
// ValidateDevice validates a path for devices
2015-07-12 04:33:30 -04:00
// It will make sure 'val' is in the form:
// [host-dir:]container-path[:mode]
2015-08-27 03:33:21 -04:00
// It also validates the device mode.
2015-07-12 04:33:30 -04:00
func ValidateDevice ( val string ) ( string , error ) {
2015-08-24 05:57:12 -04:00
return validatePath ( val , ValidDeviceMode )
2015-07-12 04:33:30 -04:00
}
2015-08-24 05:57:12 -04:00
func validatePath ( val string , validator func ( string ) bool ) ( string , error ) {
2014-03-10 17:10:23 -04:00
var containerPath string
2015-07-12 04:33:30 -04:00
var mode string
2014-03-10 17:10:23 -04:00
if strings . Count ( val , ":" ) > 2 {
2015-08-24 05:57:12 -04:00
return val , fmt . Errorf ( "bad format for path: %s" , val )
2014-03-10 17:10:23 -04:00
}
2015-09-14 01:42:33 -04:00
split := strings . SplitN ( val , ":" , 3 )
if split [ 0 ] == "" {
2015-08-24 05:57:12 -04:00
return val , fmt . Errorf ( "bad format for path: %s" , val )
2015-07-12 04:33:30 -04:00
}
2015-09-14 01:42:33 -04:00
switch len ( split ) {
2015-07-12 04:33:30 -04:00
case 1 :
2015-09-14 01:42:33 -04:00
containerPath = split [ 0 ]
2015-07-12 04:33:30 -04:00
val = path . Clean ( containerPath )
case 2 :
2015-09-14 01:42:33 -04:00
if isValid := validator ( split [ 1 ] ) ; isValid {
containerPath = split [ 0 ]
mode = split [ 1 ]
2015-07-12 04:33:30 -04:00
val = fmt . Sprintf ( "%s:%s" , path . Clean ( containerPath ) , mode )
} else {
2015-09-14 01:42:33 -04:00
containerPath = split [ 1 ]
val = fmt . Sprintf ( "%s:%s" , split [ 0 ] , path . Clean ( containerPath ) )
2015-07-12 04:33:30 -04:00
}
case 3 :
2015-09-14 01:42:33 -04:00
containerPath = split [ 1 ]
mode = split [ 2 ]
if isValid := validator ( split [ 2 ] ) ; ! isValid {
2015-08-24 05:57:12 -04:00
return val , fmt . Errorf ( "bad mode specified: %s" , mode )
2015-07-12 04:33:30 -04:00
}
2015-09-14 01:42:33 -04:00
val = fmt . Sprintf ( "%s:%s:%s" , split [ 0 ] , containerPath , mode )
2014-03-10 17:10:23 -04:00
}
2014-10-29 18:46:45 -04:00
if ! path . IsAbs ( containerPath ) {
2014-03-10 17:10:23 -04:00
return val , fmt . Errorf ( "%s is not an absolute path" , containerPath )
}
return val , nil
}
2015-08-27 03:33:21 -04:00
// ValidateEnv validates an environment variable and returns it.
2015-07-12 04:33:30 -04:00
// If no value is specified, it returns the current value using os.Getenv.
2015-09-28 14:26:20 -04:00
//
// As on ParseEnvFile and related to #16585, environment variable names
// are not validate what so ever, it's up to application inside docker
// to validate them or not.
2014-03-10 17:10:23 -04:00
func ValidateEnv ( val string ) ( string , error ) {
arr := strings . Split ( val , "=" )
if len ( arr ) > 1 {
return val , nil
}
2015-04-13 10:17:14 -04:00
if ! doesEnvExist ( val ) {
2015-01-16 15:57:08 -05:00
return val , nil
}
2014-03-10 17:10:23 -04:00
return fmt . Sprintf ( "%s=%s" , val , os . Getenv ( val ) ) , nil
}
2015-08-27 03:33:21 -04:00
// ValidateIPAddress validates an Ip address.
2014-07-09 17:47:55 -04:00
func ValidateIPAddress ( val string ) ( string , error ) {
2014-06-13 08:02:12 -04:00
var ip = net . ParseIP ( strings . TrimSpace ( val ) )
if ip != nil {
return ip . String ( ) , nil
2014-03-10 17:10:23 -04:00
}
2014-06-13 08:02:12 -04:00
return "" , fmt . Errorf ( "%s is not an ip address" , val )
2014-03-10 17:10:23 -04:00
}
2014-02-07 11:48:14 -05:00
2015-08-27 03:33:21 -04:00
// ValidateMACAddress validates a MAC address.
2015-02-27 10:27:12 -05:00
func ValidateMACAddress ( val string ) ( string , error ) {
_ , err := net . ParseMAC ( strings . TrimSpace ( val ) )
if err != nil {
return "" , err
}
2015-03-23 15:21:37 -04:00
return val , nil
2015-02-27 10:27:12 -05:00
}
2015-08-27 03:33:21 -04:00
// ValidateDNSSearch validates domain for resolvconf search configuration.
// A zero length domain is represented by a dot (.).
2015-07-12 04:33:30 -04:00
func ValidateDNSSearch ( val string ) ( string , error ) {
2014-06-26 07:03:23 -04:00
if val = strings . Trim ( val , " " ) ; val == "." {
return val , nil
}
return validateDomain ( val )
}
func validateDomain ( val string ) ( string , error ) {
2014-09-15 23:30:10 -04:00
if alphaRegexp . FindString ( val ) == "" {
2014-02-07 11:48:14 -05:00
return "" , fmt . Errorf ( "%s is not a valid domain" , val )
}
2014-09-15 23:30:10 -04:00
ns := domainRegexp . FindSubmatch ( [ ] byte ( val ) )
2014-11-25 06:45:20 -05:00
if len ( ns ) > 0 && len ( ns [ 1 ] ) < 255 {
2014-02-07 11:48:14 -05:00
return string ( ns [ 1 ] ) , nil
}
return "" , fmt . Errorf ( "%s is not a valid domain" , val )
}
2014-07-18 14:48:19 -04:00
2015-08-27 03:33:21 -04:00
// ValidateExtraHost validates that the specified string is a valid extrahost and returns it.
// ExtraHost are in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
2014-09-13 00:35:59 -04:00
func ValidateExtraHost ( val string ) ( string , error ) {
2015-02-04 10:20:28 -05:00
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
arr := strings . SplitN ( val , ":" , 2 )
2014-09-13 00:35:59 -04:00
if len ( arr ) != 2 || len ( arr [ 0 ] ) == 0 {
2015-02-09 09:59:05 -05:00
return "" , fmt . Errorf ( "bad format for add-host: %q" , val )
2014-09-13 00:35:59 -04:00
}
if _ , err := ValidateIPAddress ( arr [ 1 ] ) ; err != nil {
2015-02-09 09:59:05 -05:00
return "" , fmt . Errorf ( "invalid IP address in add-host: %q" , arr [ 1 ] )
2014-09-13 00:35:59 -04:00
}
return val , nil
}
2015-08-27 03:33:21 -04:00
// ValidateLabel validates that the specified string is a valid label, and returns it.
// Labels are in the form on key=value.
2014-11-20 13:36:05 -05:00
func ValidateLabel ( val string ) ( string , error ) {
2015-07-12 04:33:30 -04:00
if strings . Count ( val , "=" ) < 1 {
2014-11-20 13:36:05 -05:00
return "" , fmt . Errorf ( "bad attribute format: %s" , val )
}
return val , nil
}
2015-04-13 10:17:14 -04:00
2015-08-27 03:33:21 -04:00
// ValidateHost validates that the specified string is a valid host and returns it.
2015-04-13 10:17:14 -04:00
func ValidateHost ( val string ) ( string , error ) {
2015-10-19 09:17:37 -04:00
_ , err := parsers . ParseDockerDaemonHost ( DefaultTCPHost , DefaultTLSHost , DefaultUnixSocket , "" , val )
2015-08-21 09:28:49 -04:00
if err != nil {
return val , err
}
// Note: unlike most flag validators, we don't return the mutated value here
// we need to know what the user entered later (using ParseHost) to adjust for tls
return val , nil
}
// ParseHost and set defaults for a Daemon host string
2015-10-19 09:17:37 -04:00
func ParseHost ( defaultHost , val string ) ( string , error ) {
host , err := parsers . ParseDockerDaemonHost ( DefaultTCPHost , DefaultTLSHost , DefaultUnixSocket , defaultHost , val )
2015-04-13 10:17:14 -04:00
if err != nil {
return val , err
}
return host , nil
}
func doesEnvExist ( name string ) bool {
for _ , entry := range os . Environ ( ) {
parts := strings . SplitN ( entry , "=" , 2 )
if parts [ 0 ] == name {
return true
}
}
return false
}