mirror of https://github.com/k3s-io/k3s
138 lines
4.6 KiB
Go
138 lines
4.6 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package env
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
)
|
|
|
|
var argumentEnvironment = regexp.MustCompile("(?ms)^(.+)\\=(.*)$")
|
|
var validArgumentEnvironment = regexp.MustCompile("(?ms)^(\\w+)\\=(.*)$")
|
|
|
|
// IsEnvironmentArgument checks whether a string is an environment argument, that is, whether it matches the "anycharacters=anycharacters" pattern.
|
|
func IsEnvironmentArgument(s string) bool {
|
|
return argumentEnvironment.MatchString(s)
|
|
}
|
|
|
|
// IsValidEnvironmentArgument checks whether a string is a valid environment argument, that is, whether it matches the "wordcharacters=anycharacters" pattern. Word characters can be letters, numbers, and underscores.
|
|
func IsValidEnvironmentArgument(s string) bool {
|
|
return validArgumentEnvironment.MatchString(s)
|
|
}
|
|
|
|
// SplitEnvironmentFromResources separates resources from environment arguments.
|
|
// Resources must come first. Arguments may have the "DASH-" syntax.
|
|
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
|
|
}
|
|
|
|
// parseIntoEnvVar parses the list of key-value pairs into kubernetes EnvVar.
|
|
// envVarType is for making errors more specific to user intentions.
|
|
func parseIntoEnvVar(spec []string, defaultReader io.Reader, envVarType string) ([]v1.EnvVar, []string, error) {
|
|
env := []v1.EnvVar{}
|
|
exists := sets.NewString()
|
|
var remove []string
|
|
for _, envSpec := range spec {
|
|
switch {
|
|
case !IsValidEnvironmentArgument(envSpec) && !strings.HasSuffix(envSpec, "-"):
|
|
return nil, nil, fmt.Errorf("%ss must be of the form key=value and can only contain letters, numbers, and underscores", envVarType)
|
|
case envSpec == "-":
|
|
if defaultReader == nil {
|
|
return nil, nil, fmt.Errorf("when '-' is used, STDIN must be open")
|
|
}
|
|
fileEnv, err := readEnv(defaultReader, envVarType)
|
|
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 %s: %v", envVarType, envSpec)
|
|
}
|
|
exists.Insert(parts[0])
|
|
env = append(env, v1.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 %s: %v", envVarType, envSpec)
|
|
}
|
|
}
|
|
for _, removeLabel := range remove {
|
|
if _, found := exists[removeLabel]; found {
|
|
return nil, nil, fmt.Errorf("can not both modify and remove the same %s in the same command", envVarType)
|
|
}
|
|
}
|
|
return env, remove, nil
|
|
}
|
|
|
|
// ParseEnv parses the elements of the first argument looking for environment variables in key=value form and, if one of those values is "-", it also scans the reader.
|
|
// The same environment variable cannot be both modified and removed in the same command.
|
|
func ParseEnv(spec []string, defaultReader io.Reader) ([]v1.EnvVar, []string, error) {
|
|
return parseIntoEnvVar(spec, defaultReader, "environment variable")
|
|
}
|
|
|
|
func readEnv(r io.Reader, envVarType string) ([]v1.EnvVar, error) {
|
|
env := []v1.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 %s: %v", envVarType, envSpec)
|
|
}
|
|
env = append(env, v1.EnvVar{
|
|
Name: parts[0],
|
|
Value: parts[1],
|
|
})
|
|
}
|
|
}
|
|
if err := scanner.Err(); err != nil && err != io.EOF {
|
|
return nil, err
|
|
}
|
|
return env, nil
|
|
}
|