package util
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strconv"
"strings"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/sets"
)
func EnvInt(key string, defaultValue int32, minValue int32) int32 {
value, err := strconv.ParseInt(Env(key, fmt.Sprintf("%d", defaultValue)), 10, 32)
if err != nil || int32(value) < minValue {
return defaultValue
}
return int32(value)
}
// Env returns an environment variable or a default value if not specified.
func Env(key string, defaultValue string) string {
val := os.Getenv(key)
if len(val) == 0 {
return defaultValue
}
return val
}
// GetEnv returns an environment value if specified
func GetEnv(key string) (string, bool) {
val := os.Getenv(key)
if len(val) == 0 {
return "", false
}
return val, true
}
type Environment map[string]string
var argumentEnvironment = regexp.MustCompile("(?ms)^(.+)\\=(.*)$")
var validArgumentEnvironment = regexp.MustCompile("(?ms)^(\\w+)\\=(.*)$")
func IsEnvironmentArgument(s string) bool {
return argumentEnvironment.MatchString(s)
}
func IsValidEnvironmentArgument(s string) bool {
return validArgumentEnvironment.MatchString(s)
}
func SplitEnvironmentFromResources(args []string) (resources, envArgs []string, ok bool) {
first := true
for _, s := range args {
// this method also has to understand env removal syntax, i.e. KEY-
isEnv := IsEnvironmentArgument(s) || strings.HasSuffix(s, "-")
switch {
case first && isEnv:
first = false
fallthrough
case !first && isEnv:
envArgs = append(envArgs, s)
case first && !isEnv:
resources = append(resources, s)
case !first && !isEnv:
return nil, nil, false
}
}
return resources, envArgs, true
}
func ParseEnvironmentArguments(s []string) (Environment, []string, []error) {
errs := []error{}
duplicates := []string{}
env := make(Environment)
for _, s := range s {
switch matches := validArgumentEnvironment.FindStringSubmatch(s); len(matches) {
case 3:
k, v := matches[1], matches[2]
if exist, ok := env[k]; ok {
duplicates = append(duplicates, fmt.Sprintf("%s=%s", k, exist))
}
env[k] = v
default:
errs = append(errs, fmt.Errorf("environment variables must be of the form key=value: %s", s))
}
}
return env, duplicates, errs
}
// ParseEnv parses the list of environment variables into kubernetes EnvVar
func ParseEnv(spec []string, defaultReader io.Reader) ([]kapi.EnvVar, []string, error) {
env := []kapi.EnvVar{}
exists := sets.NewString()
var remove []string
for _, envSpec := range spec {
switch {
case !IsValidEnvironmentArgument(envSpec) && !strings.HasSuffix(envSpec, "-"):
return nil, nil, fmt.Errorf("environment variables must be of the form key=value and can only contain letters, numbers, and underscores")
case envSpec == "-":
if defaultReader == nil {
return nil, nil, fmt.Errorf("when '-' is used, STDIN must be open")
}
fileEnv, err := readEnv(defaultReader)
if err != nil {
return nil, nil, err
}
env = append(env, fileEnv...)
case strings.Index(envSpec, "=") != -1:
parts := strings.SplitN(envSpec, "=", 2)
if len(parts) != 2 {
return nil, nil, fmt.Errorf("invalid environment variable: %v", envSpec)
}
exists.Insert(parts[0])
env = append(env, kapi.EnvVar{
Name: parts[0],
Value: parts[1],
})
case strings.HasSuffix(envSpec, "-"):
remove = append(remove, envSpec[:len(envSpec)-1])
default:
return nil, nil, fmt.Errorf("unknown environment variable: %v", envSpec)
}
}
for _, removeLabel := range remove {
if _, found := exists[removeLabel]; found {
return nil, nil, fmt.Errorf("can not both modify and remove an environment variable in the same command")
}
}
return env, remove, nil
}
func readEnv(r io.Reader) ([]kapi.EnvVar, error) {
env := []kapi.EnvVar{}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
envSpec := scanner.Text()
if pos := strings.Index(envSpec, "#"); pos != -1 {
envSpec = envSpec[:pos]
}
if strings.Index(envSpec, "=") != -1 {
parts := strings.SplitN(envSpec, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid environment variable: %v", envSpec)
}
env = append(env, kapi.EnvVar{
Name: parts[0],
Value: parts[1],
})
}
}
if err := scanner.Err(); err != nil && err != io.EOF {
return nil, err
}
return env, nil
}