1
16
17 package env
18
19 import (
20 "bufio"
21 "fmt"
22 "io"
23 "regexp"
24 "strings"
25
26 v1 "k8s.io/api/core/v1"
27 "k8s.io/apimachinery/pkg/util/sets"
28 "k8s.io/apimachinery/pkg/util/validation"
29 )
30
31 var argumentEnvironment = regexp.MustCompile("(?ms)^(.+)\\=(.*)$")
32
33
34 func IsEnvironmentArgument(s string) bool {
35 return argumentEnvironment.MatchString(s)
36 }
37
38
39
40 func SplitEnvironmentFromResources(args []string) (resources, envArgs []string, ok bool) {
41 first := true
42 for _, s := range args {
43
44 isEnv := IsEnvironmentArgument(s) || strings.HasSuffix(s, "-")
45 switch {
46 case first && isEnv:
47 first = false
48 fallthrough
49 case !first && isEnv:
50 envArgs = append(envArgs, s)
51 case first && !isEnv:
52 resources = append(resources, s)
53 case !first && !isEnv:
54 return nil, nil, false
55 }
56 }
57 return resources, envArgs, true
58 }
59
60
61
62 func parseIntoEnvVar(spec []string, defaultReader io.Reader, envVarType string) ([]v1.EnvVar, []string, bool, error) {
63 env := []v1.EnvVar{}
64 exists := sets.NewString()
65 var remove []string
66 usedStdin := false
67 for _, envSpec := range spec {
68 switch {
69 case envSpec == "-":
70 if defaultReader == nil {
71 return nil, nil, usedStdin, fmt.Errorf("when '-' is used, STDIN must be open")
72 }
73 fileEnv, err := readEnv(defaultReader, envVarType)
74 if err != nil {
75 return nil, nil, usedStdin, err
76 }
77 env = append(env, fileEnv...)
78 usedStdin = true
79 case strings.Contains(envSpec, "="):
80 parts := strings.SplitN(envSpec, "=", 2)
81 if len(parts) != 2 {
82 return nil, nil, usedStdin, fmt.Errorf("invalid %s: %v", envVarType, envSpec)
83 }
84 if errs := validation.IsEnvVarName(parts[0]); len(errs) != 0 {
85 return nil, nil, usedStdin, fmt.Errorf("%q is not a valid key name: %s", parts[0], strings.Join(errs, ";"))
86 }
87 exists.Insert(parts[0])
88 env = append(env, v1.EnvVar{
89 Name: parts[0],
90 Value: parts[1],
91 })
92 case strings.HasSuffix(envSpec, "-"):
93 remove = append(remove, envSpec[:len(envSpec)-1])
94 default:
95 return nil, nil, usedStdin, fmt.Errorf("unknown %s: %v", envVarType, envSpec)
96 }
97 }
98 for _, removeLabel := range remove {
99 if _, found := exists[removeLabel]; found {
100 return nil, nil, usedStdin, fmt.Errorf("can not both modify and remove the same %s in the same command", envVarType)
101 }
102 }
103 return env, remove, usedStdin, nil
104 }
105
106
107
108 func ParseEnv(spec []string, defaultReader io.Reader) ([]v1.EnvVar, []string, bool, error) {
109 return parseIntoEnvVar(spec, defaultReader, "environment variable")
110 }
111
112 func readEnv(r io.Reader, envVarType string) ([]v1.EnvVar, error) {
113 env := []v1.EnvVar{}
114 scanner := bufio.NewScanner(r)
115 for scanner.Scan() {
116 envSpec := scanner.Text()
117 if pos := strings.Index(envSpec, "#"); pos != -1 {
118 envSpec = envSpec[:pos]
119 }
120
121 if strings.Contains(envSpec, "=") {
122 parts := strings.SplitN(envSpec, "=", 2)
123 if len(parts) != 2 {
124 return nil, fmt.Errorf("invalid %s: %v", envVarType, envSpec)
125 }
126 env = append(env, v1.EnvVar{
127 Name: parts[0],
128 Value: parts[1],
129 })
130 }
131 }
132
133 if err := scanner.Err(); err != nil && err != io.EOF {
134 return nil, err
135 }
136
137 return env, nil
138 }
139
View as plain text