mirror of https://github.com/docker/cli.git
Move some validators from opts to runconfig/opts.
These validators are only used by runconfig.Parse() or some other part of the client, so move them into the client-side package. Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
cef8b71ff4
commit
e73db35cbf
|
@ -1,67 +0,0 @@
|
|||
package opts
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseEnvFile reads a file with environment variables enumerated by lines
|
||||
//
|
||||
// ``Environment variable names used by the utilities in the Shell and
|
||||
// Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
|
||||
// letters, digits, and the '_' (underscore) from the characters defined in
|
||||
// Portable Character Set and do not begin with a digit. *But*, other
|
||||
// characters may be permitted by an implementation; applications shall
|
||||
// tolerate the presence of such names.''
|
||||
// -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
|
||||
//
|
||||
// As of #16585, it's up to application inside docker to validate or not
|
||||
// environment variables, that's why we just strip leading whitespace and
|
||||
// nothing more.
|
||||
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{}, ErrBadEnvVariable{fmt.Sprintf("variable '%s' has white spaces", 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()
|
||||
}
|
||||
|
||||
var whiteSpaces = " \t"
|
||||
|
||||
// ErrBadEnvVariable typed error for bad environment variable
|
||||
type ErrBadEnvVariable struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e ErrBadEnvVariable) Error() string {
|
||||
return fmt.Sprintf("poorly formatted environment: %s", e.msg)
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package opts
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func tmpFileWithContent(content string, t *testing.T) string {
|
||||
tmpFile, err := ioutil.TempFile("", "envfile-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
|
||||
tmpFile.WriteString(content)
|
||||
return tmpFile.Name()
|
||||
}
|
||||
|
||||
// Test ParseEnvFile for a file with a few well formatted lines
|
||||
func TestParseEnvFileGoodFile(t *testing.T) {
|
||||
content := `foo=bar
|
||||
baz=quux
|
||||
# comment
|
||||
|
||||
_foobar=foobaz
|
||||
with.dots=working
|
||||
and_underscore=working too
|
||||
`
|
||||
// Adding a newline + a line with pure whitespace.
|
||||
// This is being done like this instead of the block above
|
||||
// because it's common for editors to trim trailing whitespace
|
||||
// from lines, which becomes annoying since that's the
|
||||
// exact thing we need to test.
|
||||
content += "\n \t "
|
||||
tmpFile := tmpFileWithContent(content, t)
|
||||
defer os.Remove(tmpFile)
|
||||
|
||||
lines, err := ParseEnvFile(tmpFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedLines := []string{
|
||||
"foo=bar",
|
||||
"baz=quux",
|
||||
"_foobar=foobaz",
|
||||
"with.dots=working",
|
||||
"and_underscore=working too",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(lines, expectedLines) {
|
||||
t.Fatal("lines not equal to expected_lines")
|
||||
}
|
||||
}
|
||||
|
||||
// Test ParseEnvFile for an empty file
|
||||
func TestParseEnvFileEmptyFile(t *testing.T) {
|
||||
tmpFile := tmpFileWithContent("", t)
|
||||
defer os.Remove(tmpFile)
|
||||
|
||||
lines, err := ParseEnvFile(tmpFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(lines) != 0 {
|
||||
t.Fatal("lines not empty; expected empty")
|
||||
}
|
||||
}
|
||||
|
||||
// Test ParseEnvFile for a non existent file
|
||||
func TestParseEnvFileNonExistentFile(t *testing.T) {
|
||||
_, err := ParseEnvFile("foo_bar_baz")
|
||||
if err == nil {
|
||||
t.Fatal("ParseEnvFile succeeded; expected failure")
|
||||
}
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
t.Fatalf("Expected a PathError, got [%v]", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test ParseEnvFile for a badly formatted file
|
||||
func TestParseEnvFileBadlyFormattedFile(t *testing.T) {
|
||||
content := `foo=bar
|
||||
f =quux
|
||||
`
|
||||
|
||||
tmpFile := tmpFileWithContent(content, t)
|
||||
defer os.Remove(tmpFile)
|
||||
|
||||
_, err := ParseEnvFile(tmpFile)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected a ErrBadEnvVariable, got nothing")
|
||||
}
|
||||
if _, ok := err.(ErrBadEnvVariable); !ok {
|
||||
t.Fatalf("Expected a ErrBadEnvVariable, got [%v]", err)
|
||||
}
|
||||
expectedMessage := "poorly formatted environment: variable 'f ' has white spaces"
|
||||
if err.Error() != expectedMessage {
|
||||
t.Fatalf("Expected [%v], got [%v]", expectedMessage, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Test ParseEnvFile for a file with a line exceeding bufio.MaxScanTokenSize
|
||||
func TestParseEnvFileLineTooLongFile(t *testing.T) {
|
||||
content := strings.Repeat("a", bufio.MaxScanTokenSize+42)
|
||||
content = fmt.Sprint("foo=", content)
|
||||
|
||||
tmpFile := tmpFileWithContent(content, t)
|
||||
defer os.Remove(tmpFile)
|
||||
|
||||
_, err := ParseEnvFile(tmpFile)
|
||||
if err == nil {
|
||||
t.Fatal("ParseEnvFile succeeded; expected failure")
|
||||
}
|
||||
}
|
||||
|
||||
// ParseEnvFile with a random file, pass through
|
||||
func TestParseEnvFileRandomFile(t *testing.T) {
|
||||
content := `first line
|
||||
another invalid line`
|
||||
tmpFile := tmpFileWithContent(content, t)
|
||||
defer os.Remove(tmpFile)
|
||||
|
||||
_, err := ParseEnvFile(tmpFile)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Expected a ErrBadEnvVariable, got nothing")
|
||||
}
|
||||
if _, ok := err.(ErrBadEnvVariable); !ok {
|
||||
t.Fatalf("Expected a ErrBadEnvvariable, got [%v]", err)
|
||||
}
|
||||
expectedMessage := "poorly formatted environment: variable 'first line' has white spaces"
|
||||
if err.Error() != expectedMessage {
|
||||
t.Fatalf("Expected [%v], got [%v]", expectedMessage, err.Error())
|
||||
}
|
||||
}
|
62
opts/opts.go
62
opts/opts.go
|
@ -3,7 +3,6 @@ package opts
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
@ -152,34 +151,6 @@ type ValidatorFctType func(val string) (string, error)
|
|||
// ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
|
||||
type ValidatorFctListType func(val string) ([]string, error)
|
||||
|
||||
// ValidateAttach validates that the specified string is a valid attach option.
|
||||
func ValidateAttach(val string) (string, error) {
|
||||
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")
|
||||
}
|
||||
|
||||
// ValidateEnv validates an environment variable and returns it.
|
||||
// If no value is specified, it returns the current value using os.Getenv.
|
||||
//
|
||||
// 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.
|
||||
func ValidateEnv(val string) (string, error) {
|
||||
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
|
||||
}
|
||||
|
||||
// ValidateIPAddress validates an Ip address.
|
||||
func ValidateIPAddress(val string) (string, error) {
|
||||
var ip = net.ParseIP(strings.TrimSpace(val))
|
||||
|
@ -189,15 +160,6 @@ func ValidateIPAddress(val string) (string, error) {
|
|||
return "", fmt.Errorf("%s is not an ip address", val)
|
||||
}
|
||||
|
||||
// ValidateMACAddress validates a MAC address.
|
||||
func ValidateMACAddress(val string) (string, error) {
|
||||
_, err := net.ParseMAC(strings.TrimSpace(val))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// ValidateDNSSearch validates domain for resolvconf search configuration.
|
||||
// A zero length domain is represented by a dot (.).
|
||||
func ValidateDNSSearch(val string) (string, error) {
|
||||
|
@ -218,20 +180,6 @@ func validateDomain(val string) (string, error) {
|
|||
return "", fmt.Errorf("%s is not a valid domain", val)
|
||||
}
|
||||
|
||||
// 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).
|
||||
func ValidateExtraHost(val string) (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 val, nil
|
||||
}
|
||||
|
||||
// ValidateLabel validates that the specified string is a valid label, and returns it.
|
||||
// Labels are in the form on key=value.
|
||||
func ValidateLabel(val string) (string, error) {
|
||||
|
@ -240,13 +188,3 @@ func ValidateLabel(val string) (string, error) {
|
|||
}
|
||||
return 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
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package opts
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -55,20 +54,6 @@ func TestMapOpts(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValidateMACAddress(t *testing.T) {
|
||||
if _, err := ValidateMACAddress(`92:d0:c6:0a:29:33`); err != nil {
|
||||
t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:29:33`) got %s", err)
|
||||
}
|
||||
|
||||
if _, err := ValidateMACAddress(`92:d0:c6:0a:33`); err == nil {
|
||||
t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:33`) succeeded; expected failure on invalid MAC")
|
||||
}
|
||||
|
||||
if _, err := ValidateMACAddress(`random invalid string`); err == nil {
|
||||
t.Fatalf("ValidateMACAddress(`random invalid string`) succeeded; expected failure on invalid MAC")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOptsWithoutValidator(t *testing.T) {
|
||||
o := NewListOpts(nil)
|
||||
o.Set("foo")
|
||||
|
@ -188,92 +173,6 @@ func TestValidateDNSSearch(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValidateExtraHosts(t *testing.T) {
|
||||
valid := []string{
|
||||
`myhost:192.168.0.1`,
|
||||
`thathost:10.0.2.1`,
|
||||
`anipv6host:2003:ab34:e::1`,
|
||||
`ipv6local:::1`,
|
||||
}
|
||||
|
||||
invalid := map[string]string{
|
||||
`myhost:192.notanipaddress.1`: `invalid IP`,
|
||||
`thathost-nosemicolon10.0.0.1`: `bad format`,
|
||||
`anipv6host:::::1`: `invalid IP`,
|
||||
`ipv6local:::0::`: `invalid IP`,
|
||||
}
|
||||
|
||||
for _, extrahost := range valid {
|
||||
if _, err := ValidateExtraHost(extrahost); err != nil {
|
||||
t.Fatalf("ValidateExtraHost(`"+extrahost+"`) should succeed: error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for extraHost, expectedError := range invalid {
|
||||
if _, err := ValidateExtraHost(extraHost); err == nil {
|
||||
t.Fatalf("ValidateExtraHost(`%q`) should have failed validation", extraHost)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), expectedError) {
|
||||
t.Fatalf("ValidateExtraHost(`%q`) error should contain %q", extraHost, expectedError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAttach(t *testing.T) {
|
||||
valid := []string{
|
||||
"stdin",
|
||||
"stdout",
|
||||
"stderr",
|
||||
"STDIN",
|
||||
"STDOUT",
|
||||
"STDERR",
|
||||
}
|
||||
if _, err := ValidateAttach("invalid"); err == nil {
|
||||
t.Fatalf("Expected error with [valid streams are STDIN, STDOUT and STDERR], got nothing")
|
||||
}
|
||||
|
||||
for _, attach := range valid {
|
||||
value, err := ValidateAttach(attach)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if value != strings.ToLower(attach) {
|
||||
t.Fatalf("Expected [%v], got [%v]", attach, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEnv(t *testing.T) {
|
||||
valids := map[string]string{
|
||||
"a": "a",
|
||||
"something": "something",
|
||||
"_=a": "_=a",
|
||||
"env1=value1": "env1=value1",
|
||||
"_env1=value1": "_env1=value1",
|
||||
"env2=value2=value3": "env2=value2=value3",
|
||||
"env3=abc!qwe": "env3=abc!qwe",
|
||||
"env_4=value 4": "env_4=value 4",
|
||||
"PATH": fmt.Sprintf("PATH=%v", os.Getenv("PATH")),
|
||||
"PATH=something": "PATH=something",
|
||||
"asd!qwe": "asd!qwe",
|
||||
"1asd": "1asd",
|
||||
"123": "123",
|
||||
"some space": "some space",
|
||||
" some space before": " some space before",
|
||||
"some space after ": "some space after ",
|
||||
}
|
||||
for value, expected := range valids {
|
||||
actual, err := ValidateEnv(value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected [%v], got [%v]", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateLabel(t *testing.T) {
|
||||
if _, err := ValidateLabel("label"); err == nil || err.Error() != "bad attribute format: label" {
|
||||
t.Fatalf("Expected an error [bad attribute format: label], go %v", err)
|
||||
|
|
Loading…
Reference in New Issue