Skip to content

Commit

Permalink
Add new option "envFrom" for kubectl run command.
Browse files Browse the repository at this point in the history
Add new option "envFrom" for `kubectl run` command so that user
can config ConfigMap or Secret env variables from run command.
  • Loading branch information
xingzhou committed Jul 10, 2017
1 parent 5ca03d6 commit d86537c
Show file tree
Hide file tree
Showing 4 changed files with 350 additions and 8 deletions.
10 changes: 9 additions & 1 deletion pkg/kubectl/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ var (
# Start a single instance of hazelcast and set environment variables "DNS_DOMAIN=cluster" and "POD_NAMESPACE=default" in the container.
kubectl run hazelcast --image=hazelcast --env="DNS_DOMAIN=cluster" --env="POD_NAMESPACE=default"
# Start a single instance of hazelcast and set environment variables from ConfigMap named "ConfigMapA" and "ConfigMapB" in the container.
kubectl run hazelcast --image=hazelcast --envFrom="ConfigMapRef=ConfigMapA" --envFrom="ConfigMapB"
# Start a single instance of hazelcast and set environment variables from Secret name "SecretsA" and "SecretsB" in the container.
kubectl run hazelcast --image=hazelcast --envFrom="SecretRef=SecretsA" --envFrom="SecretsB"
# Start a replicated instance of nginx.
kubectl run nginx --image=nginx --replicas=5
Expand Down Expand Up @@ -88,7 +94,7 @@ var (

func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "run NAME --image=image [--env=\"key=value\"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]",
Use: "run NAME --image=image [--env=\"key=value\"] [--envFrom=\"ConfigMapRef|SecretRef=name\"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]",
Short: i18n.T("Run a particular image on the cluster"),
Long: runLong,
Example: runExample,
Expand Down Expand Up @@ -117,6 +123,7 @@ func addRunFlags(cmd *cobra.Command) {
cmd.Flags().Bool("rm", false, "If true, delete resources created in this command for attached containers.")
cmd.Flags().String("overrides", "", i18n.T("An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field."))
cmd.Flags().StringSlice("env", []string{}, "Environment variables to set in the container")
cmd.Flags().StringSlice("envFrom", []string{}, "Environment variables to set from ConfigMap or Secret in the container")
cmd.Flags().String("port", "", i18n.T("The port that this container exposes. If --expose is true, this is also the port used by the service that is created."))
cmd.Flags().Int("hostport", -1, "The host port mapping for the container port. To demonstrate a single-machine container.")
cmd.Flags().StringP("labels", "l", "", "Labels to apply to the pod(s).")
Expand Down Expand Up @@ -252,6 +259,7 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
}

params["env"] = cmdutil.GetFlagStringSlice(cmd, "env")
params["envFrom"] = cmdutil.GetFlagStringSlice(cmd, "envFrom")

obj, _, mapper, mapping, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "overrides"), namespace)
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions pkg/kubectl/cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ func TestGetEnv(t *testing.T) {
}
}

func TestGetEnvFrom(t *testing.T) {
test := struct {
input []string
expected []string
}{
input: []string{"ConfigMapRef=Foo", "SecretRef=Bar"},
expected: []string{"ConfigMapRef=Foo", "SecretRef=Bar"},
}
cmd := &cobra.Command{}
cmd.Flags().StringSlice("envFrom", test.input, "")

envFromStrings := cmdutil.GetFlagStringSlice(cmd, "envFrom")
if len(envFromStrings) != 2 || !reflect.DeepEqual(envFromStrings, test.expected) {
t.Errorf("expected: %s, saw: %s", test.expected, envFromStrings)
}
}

func TestRunArgsFollowDashRules(t *testing.T) {
one := int32(1)
rc := &v1.ReplicationController{
Expand Down
111 changes: 104 additions & 7 deletions pkg/kubectl/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func (DeploymentV1Beta1) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"envFrom", false},
{"requests", false},
{"limits", false},
}
Expand All @@ -66,6 +67,11 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime
return nil, err
}

envFroms, err := getEnvFroms(genericParams)
if err != nil {
return nil, err
}

params, err := getParams(genericParams)
if err != nil {
return nil, err
Expand All @@ -92,7 +98,7 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime
}

imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
if err = updatePodContainers(params, args, envs, envFroms, imagePullPolicy, podSpec); err != nil {
return nil, err
}

Expand Down Expand Up @@ -139,6 +145,7 @@ func (DeploymentAppsV1Beta1) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"envFrom", false},
{"requests", false},
{"limits", false},
}
Expand All @@ -155,6 +162,11 @@ func (DeploymentAppsV1Beta1) Generate(genericParams map[string]interface{}) (run
return nil, err
}

envFroms, err := getEnvFroms(genericParams)
if err != nil {
return nil, err
}

params, err := getParams(genericParams)
if err != nil {
return nil, err
Expand All @@ -181,7 +193,7 @@ func (DeploymentAppsV1Beta1) Generate(genericParams map[string]interface{}) (run
}

imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
if err = updatePodContainers(params, args, envs, envFroms, imagePullPolicy, podSpec); err != nil {
return nil, err
}

Expand Down Expand Up @@ -286,6 +298,25 @@ func getEnvs(genericParams map[string]interface{}) ([]v1.EnvVar, error) {
return envs, nil
}

// getEnvFroms returns EnvFrom variables.
func getEnvFroms(genericParams map[string]interface{}) ([]v1.EnvFromSource, error) {
var envFroms []v1.EnvFromSource
envFromStrings, found := genericParams["envFrom"]
if found {
if envFromStringArray, isArray := envFromStrings.([]string); isArray {
var err error
envFroms, err = parseEnvFroms(envFromStringArray)
if err != nil {
return nil, err
}
delete(genericParams, "envFrom")
} else {
return nil, fmt.Errorf("expected []string, found: %v", envFromStrings)
}
}
return envFroms, nil
}

type JobV1 struct{}

func (JobV1) ParamNames() []GeneratorParam {
Expand All @@ -303,6 +334,7 @@ func (JobV1) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"envFrom", false},
{"requests", false},
{"limits", false},
{"restart", false},
Expand All @@ -320,6 +352,11 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
return nil, err
}

envFroms, err := getEnvFroms(genericParams)
if err != nil {
return nil, err
}

params, err := getParams(genericParams)
if err != nil {
return nil, err
Expand All @@ -341,7 +378,7 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
}

imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
if err = updatePodContainers(params, args, envs, envFroms, imagePullPolicy, podSpec); err != nil {
return nil, err
}

Expand Down Expand Up @@ -396,6 +433,7 @@ func (CronJobV2Alpha1) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"envFrom", false},
{"requests", false},
{"limits", false},
{"restart", false},
Expand All @@ -414,6 +452,11 @@ func (CronJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.O
return nil, err
}

envFroms, err := getEnvFroms(genericParams)
if err != nil {
return nil, err
}

params, err := getParams(genericParams)
if err != nil {
return nil, err
Expand All @@ -435,7 +478,7 @@ func (CronJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.O
}

imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
if err = updatePodContainers(params, args, envs, envFroms, imagePullPolicy, podSpec); err != nil {
return nil, err
}

Expand Down Expand Up @@ -496,6 +539,7 @@ func (BasicReplicationController) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"envFrom", false},
{"requests", false},
{"limits", false},
}
Expand Down Expand Up @@ -627,6 +671,11 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
return nil, err
}

envFroms, err := getEnvFroms(genericParams)
if err != nil {
return nil, err
}

params, err := getParams(genericParams)
if err != nil {
return nil, err
Expand All @@ -653,7 +702,7 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
}

imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
if err = updatePodContainers(params, args, envs, envFroms, imagePullPolicy, podSpec); err != nil {
return nil, err
}

Expand Down Expand Up @@ -682,7 +731,7 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
}

// updatePodContainers updates PodSpec.Containers with passed parameters.
func updatePodContainers(params map[string]string, args []string, envs []v1.EnvVar, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error {
func updatePodContainers(params map[string]string, args []string, envs []v1.EnvVar, envFroms []v1.EnvFromSource, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error {
if len(args) > 0 {
command, err := GetBool(params, "command", false)
if err != nil {
Expand All @@ -699,6 +748,10 @@ func updatePodContainers(params map[string]string, args []string, envs []v1.EnvV
podSpec.Containers[0].Env = envs
}

if len(envFroms) > 0 {
podSpec.Containers[0].EnvFrom = envFroms
}

if len(imagePullPolicy) > 0 {
// imagePullPolicy should be valid here since we have verified it before.
podSpec.Containers[0].ImagePullPolicy = imagePullPolicy
Expand Down Expand Up @@ -759,6 +812,7 @@ func (BasicPod) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"envFrom", false},
{"requests", false},
{"limits", false},
}
Expand All @@ -775,6 +829,11 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
return nil, err
}

envFroms, err := getEnvFroms(genericParams)
if err != nil {
return nil, err
}

params, err := getParams(genericParams)
if err != nil {
return nil, err
Expand Down Expand Up @@ -837,7 +896,7 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
},
}
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, &pod.Spec); err != nil {
if err = updatePodContainers(params, args, envs, envFroms, imagePullPolicy, &pod.Spec); err != nil {
return nil, err
}

Expand Down Expand Up @@ -868,3 +927,41 @@ func parseEnvs(envArray []string) ([]v1.EnvVar, error) {
}
return envs, nil
}

// parseEnvFroms converts string into EnvFromSource objects.
func parseEnvFroms(envFromArray []string) ([]v1.EnvFromSource, error) {
envFroms := make([]v1.EnvFromSource, 0, len(envFromArray))
for _, envFrom := range envFromArray {
pos := strings.Index(envFrom, "=")
if pos == -1 {
return nil, fmt.Errorf("invalid envFrom: %v", envFrom)
}
refType := envFrom[:pos]
name := envFrom[pos+1:]
if len(refType) == 0 || len(name) == 0 {
return nil, fmt.Errorf("invalid envFrom: %v", envFrom)
}
if refType == "ConfigMapRef" {
envFrom := v1.EnvFromSource{
ConfigMapRef: &v1.ConfigMapEnvSource{
LocalObjectReference: v1.LocalObjectReference{
Name: name,
},
},
}
envFroms = append(envFroms, envFrom)
} else if refType == "SecretRef" {
envFrom := v1.EnvFromSource{
SecretRef: &v1.SecretEnvSource{
LocalObjectReference: v1.LocalObjectReference{
Name: name,
},
},
}
envFroms = append(envFroms, envFrom)
} else {
return nil, fmt.Errorf("envFrom can only be 'ConfigMapRef' or 'SecretRef': %v", envFrom)
}
}
return envFroms, nil
}
Loading

0 comments on commit d86537c

Please sign in to comment.