1
16
17 package versioned
18
19 import (
20 "fmt"
21 "strconv"
22 "strings"
23
24 v1 "k8s.io/api/core/v1"
25 "k8s.io/apimachinery/pkg/api/resource"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/util/validation"
29 cmdutil "k8s.io/kubectl/pkg/cmd/util"
30 "k8s.io/kubectl/pkg/generate"
31 )
32
33
34 func getLabels(params map[string]string, name string) (map[string]string, error) {
35 labelString, found := params["labels"]
36 var labels map[string]string
37 var err error
38 if found && len(labelString) > 0 {
39 labels, err = generate.ParseLabels(labelString)
40 if err != nil {
41 return nil, err
42 }
43 } else {
44 labels = map[string]string{
45 "run": name,
46 }
47 }
48 return labels, nil
49 }
50
51
52 func getName(params map[string]string) (string, error) {
53 name, found := params["name"]
54 if !found || len(name) == 0 {
55 name, found = params["default-name"]
56 if !found || len(name) == 0 {
57 return "", fmt.Errorf("'name' is a required parameter")
58 }
59 }
60 return name, nil
61 }
62
63
64 func getParams(genericParams map[string]interface{}) (map[string]string, error) {
65 params := map[string]string{}
66 for key, value := range genericParams {
67 strVal, isString := value.(string)
68 if !isString {
69 return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
70 }
71 params[key] = strVal
72 }
73 return params, nil
74 }
75
76
77 func getArgs(genericParams map[string]interface{}) ([]string, error) {
78 args := []string{}
79 val, found := genericParams["args"]
80 if found {
81 var isArray bool
82 args, isArray = val.([]string)
83 if !isArray {
84 return nil, fmt.Errorf("expected []string, found: %v", val)
85 }
86 delete(genericParams, "args")
87 }
88 return args, nil
89 }
90
91
92 func getAnnotations(genericParams map[string]interface{}) (map[string]string, error) {
93 annotationStrings, ok := genericParams["annotations"]
94 if !ok {
95 return nil, nil
96 }
97
98 annotationStringArray, ok := annotationStrings.([]string)
99 if !ok {
100 return nil, fmt.Errorf("expected []string, found: %v", annotationStrings)
101 }
102
103 annotations, _, err := cmdutil.ParsePairs(annotationStringArray, "annotations", false)
104 if err != nil {
105 return nil, err
106 }
107
108 delete(genericParams, "annotations")
109 return annotations, nil
110 }
111
112
113 func getEnvs(genericParams map[string]interface{}) ([]v1.EnvVar, error) {
114 var envs []v1.EnvVar
115 envStrings, found := genericParams["env"]
116 if found {
117 if envStringArray, isArray := envStrings.([]string); isArray {
118 var err error
119 envs, err = parseEnvs(envStringArray)
120 if err != nil {
121 return nil, err
122 }
123 delete(genericParams, "env")
124 } else {
125 return nil, fmt.Errorf("expected []string, found: %v", envStrings)
126 }
127 }
128 return envs, nil
129 }
130
131
132
133 func populateResourceListV1(spec string) (v1.ResourceList, error) {
134
135 if spec == "" {
136 return nil, nil
137 }
138
139 result := v1.ResourceList{}
140 resourceStatements := strings.Split(spec, ",")
141 for _, resourceStatement := range resourceStatements {
142 parts := strings.Split(resourceStatement, "=")
143 if len(parts) != 2 {
144 return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
145 }
146 resourceName := v1.ResourceName(parts[0])
147 resourceQuantity, err := resource.ParseQuantity(parts[1])
148 if err != nil {
149 return nil, err
150 }
151 result[resourceName] = resourceQuantity
152 }
153 return result, nil
154 }
155
156
157
158 func HandleResourceRequirementsV1(params map[string]string) (v1.ResourceRequirements, error) {
159 result := v1.ResourceRequirements{}
160 limits, err := populateResourceListV1(params["limits"])
161 if err != nil {
162 return result, err
163 }
164 result.Limits = limits
165 requests, err := populateResourceListV1(params["requests"])
166 if err != nil {
167 return result, err
168 }
169 result.Requests = requests
170 return result, nil
171 }
172
173
174 func updatePodContainers(params map[string]string, args []string, envs []v1.EnvVar, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error {
175 if len(args) > 0 {
176 command, err := generate.GetBool(params, "command", false)
177 if err != nil {
178 return err
179 }
180 if command {
181 podSpec.Containers[0].Command = args
182 } else {
183 podSpec.Containers[0].Args = args
184 }
185 }
186
187 if len(envs) > 0 {
188 podSpec.Containers[0].Env = envs
189 }
190
191 if len(imagePullPolicy) > 0 {
192
193 podSpec.Containers[0].ImagePullPolicy = imagePullPolicy
194 }
195 return nil
196 }
197
198
199 func updatePodPorts(params map[string]string, podSpec *v1.PodSpec) (err error) {
200 port := -1
201 hostPort := -1
202 if len(params["port"]) > 0 {
203 port, err = strconv.Atoi(params["port"])
204 if err != nil {
205 return err
206 }
207 }
208
209 if len(params["hostport"]) > 0 {
210 hostPort, err = strconv.Atoi(params["hostport"])
211 if err != nil {
212 return err
213 }
214 if hostPort > 0 && port < 0 {
215 return fmt.Errorf("--hostport requires --port to be specified")
216 }
217 }
218
219
220 if len(params["port"]) > 0 {
221 podSpec.Containers[0].Ports = []v1.ContainerPort{
222 {
223 ContainerPort: int32(port),
224 },
225 }
226 if hostPort > 0 {
227 podSpec.Containers[0].Ports[0].HostPort = int32(hostPort)
228 }
229 }
230 return nil
231 }
232
233 type BasicPod struct{}
234
235 func (BasicPod) ParamNames() []generate.GeneratorParam {
236 return []generate.GeneratorParam{
237 {Name: "labels", Required: false},
238 {Name: "annotations", Required: false},
239 {Name: "default-name", Required: false},
240 {Name: "name", Required: true},
241 {Name: "image", Required: true},
242 {Name: "image-pull-policy", Required: false},
243 {Name: "port", Required: false},
244 {Name: "hostport", Required: false},
245 {Name: "stdin", Required: false},
246 {Name: "leave-stdin-open", Required: false},
247 {Name: "tty", Required: false},
248 {Name: "restart", Required: false},
249 {Name: "command", Required: false},
250 {Name: "args", Required: false},
251 {Name: "env", Required: false},
252 {Name: "requests", Required: false},
253 {Name: "limits", Required: false},
254 {Name: "serviceaccount", Required: false},
255 {Name: "privileged", Required: false},
256 }
257 }
258
259 func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
260 args, err := getArgs(genericParams)
261 if err != nil {
262 return nil, err
263 }
264
265 envs, err := getEnvs(genericParams)
266 if err != nil {
267 return nil, err
268 }
269
270 annotations, err := getAnnotations(genericParams)
271 if err != nil {
272 return nil, err
273 }
274
275 params, err := getParams(genericParams)
276 if err != nil {
277 return nil, err
278 }
279
280 name, err := getName(params)
281 if err != nil {
282 return nil, err
283 }
284
285 labels, err := getLabels(params, name)
286 if err != nil {
287 return nil, err
288 }
289
290 stdin, err := generate.GetBool(params, "stdin", false)
291 if err != nil {
292 return nil, err
293 }
294 leaveStdinOpen, err := generate.GetBool(params, "leave-stdin-open", false)
295 if err != nil {
296 return nil, err
297 }
298
299 tty, err := generate.GetBool(params, "tty", false)
300 if err != nil {
301 return nil, err
302 }
303
304 resourceRequirements, err := HandleResourceRequirementsV1(params)
305 if err != nil {
306 return nil, err
307 }
308
309 restartPolicy := v1.RestartPolicy(params["restart"])
310 if len(restartPolicy) == 0 {
311 restartPolicy = v1.RestartPolicyAlways
312 }
313
314 privileged, err := generate.GetBool(params, "privileged", false)
315 if err != nil {
316 return nil, err
317 }
318 var securityContext *v1.SecurityContext
319 if privileged {
320 securityContext = &v1.SecurityContext{
321 Privileged: &privileged,
322 }
323 }
324
325 pod := v1.Pod{
326 ObjectMeta: metav1.ObjectMeta{
327 Name: name,
328 Labels: labels,
329 Annotations: annotations,
330 },
331 Spec: v1.PodSpec{
332 ServiceAccountName: params["serviceaccount"],
333 Containers: []v1.Container{
334 {
335 Name: name,
336 Image: params["image"],
337 Stdin: stdin,
338 StdinOnce: !leaveStdinOpen && stdin,
339 TTY: tty,
340 Resources: resourceRequirements,
341 SecurityContext: securityContext,
342 },
343 },
344 DNSPolicy: v1.DNSClusterFirst,
345 RestartPolicy: restartPolicy,
346 },
347 }
348 imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
349 if err = updatePodContainers(params, args, envs, imagePullPolicy, &pod.Spec); err != nil {
350 return nil, err
351 }
352
353 if err := updatePodPorts(params, &pod.Spec); err != nil {
354 return nil, err
355 }
356 return &pod, nil
357 }
358
359
360 func parseEnvs(envArray []string) ([]v1.EnvVar, error) {
361 envs := make([]v1.EnvVar, 0, len(envArray))
362 for _, env := range envArray {
363 pos := strings.Index(env, "=")
364 if pos == -1 {
365 return nil, fmt.Errorf("invalid env: %v", env)
366 }
367 name := env[:pos]
368 value := env[pos+1:]
369 if len(name) == 0 {
370 return nil, fmt.Errorf("invalid env: %v", env)
371 }
372 if len(validation.IsEnvVarName(name)) != 0 {
373 return nil, fmt.Errorf("invalid env: %v", env)
374 }
375 envVar := v1.EnvVar{Name: name, Value: value}
376 envs = append(envs, envVar)
377 }
378 return envs, nil
379 }
380
View as plain text