...

Source file src/k8s.io/kubectl/pkg/generate/versioned/run.go

Documentation: k8s.io/kubectl/pkg/generate/versioned

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    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  // getLabels returns map of labels.
    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  // getName returns the name of newly created resource.
    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  // getParams returns map of generic parameters.
    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  // getArgs returns arguments for the container command.
    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  // getAnnotations returns map of annotations.
    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  // getEnvs returns environment variables.
   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  // populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
   132  // and returns ResourceList.
   133  func populateResourceListV1(spec string) (v1.ResourceList, error) {
   134  	// empty input gets a nil response to preserve generator test expected behaviors
   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  // HandleResourceRequirementsV1 parses the limits and requests parameters if specified
   157  // and returns ResourceRequirements.
   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  // updatePodContainers updates PodSpec.Containers with passed parameters.
   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  		// imagePullPolicy should be valid here since we have verified it before.
   193  		podSpec.Containers[0].ImagePullPolicy = imagePullPolicy
   194  	}
   195  	return nil
   196  }
   197  
   198  // updatePodContainers updates PodSpec.Containers.Ports with passed parameters.
   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  	// Don't include the port if it was not specified.
   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  // parseEnvs converts string into EnvVar objects.
   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