From ca315e317b9d9bee7d021896d980176c1a12cc4f Mon Sep 17 00:00:00 2001 From: AdoHe Date: Wed, 17 Aug 2016 11:26:27 +0800 Subject: [PATCH] kubectl run add pull-policy flag to control image pull policy --- pkg/kubectl/cmd/run.go | 17 ++++++++ pkg/kubectl/run.go | 40 ++++++++++++++---- pkg/kubectl/run_test.go | 89 +++++++++++++++++++++++------------------ 3 files changed, 98 insertions(+), 48 deletions(-) diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index d9af5eaac31..2d2f21151e2 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -105,6 +105,7 @@ func addRunFlags(cmd *cobra.Command) { cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'deployment/v1beta1' if --restart=Always, 'job/v1' for OnFailure and 'run-pod/v1' for Never. This will happen only for cluster version at least 1.3, for 1.2 we will fallback to 'deployment/v1beta1' for --restart=Always, 'job/v1' for others, for olders we will fallback to 'run/v1' for --restart=Always, 'run-pod/v1' for others.") cmd.Flags().String("image", "", "The image for the container to run.") cmd.MarkFlagRequired("image") + cmd.Flags().String("image-pull-policy", "", "The image pull policy for the container. If left empty, this value will not be specified by the client and defaulted by the server") cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.") cmd.Flags().Bool("rm", false, "If true, delete resources created in this command for attached containers.") cmd.Flags().String("overrides", "", "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.") @@ -167,6 +168,10 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob return cmdutil.UsageError(cmd, fmt.Sprintf("--restart=%s requires that --replicas=1, found %d", restartPolicy, replicas)) } + if err := verifyImagePullPolicy(cmd); err != nil { + return err + } + generatorName := cmdutil.GetFlagString(cmd, "generator") schedule := cmdutil.GetFlagString(cmd, "schedule") if len(schedule) != 0 && len(generatorName) == 0 { @@ -417,6 +422,18 @@ func getRestartPolicy(cmd *cobra.Command, interactive bool) (api.RestartPolicy, } } +func verifyImagePullPolicy(cmd *cobra.Command) error { + pullPolicy := cmdutil.GetFlagString(cmd, "image-pull-policy") + switch api.PullPolicy(pullPolicy) { + case api.PullAlways, api.PullIfNotPresent, api.PullNever: + return nil + case "": + return nil + default: + return cmdutil.UsageError(cmd, fmt.Sprintf("invalid image pull policy: %s", pullPolicy)) + } +} + func generateService(f *cmdutil.Factory, cmd *cobra.Command, args []string, serviceGenerator string, paramsIn map[string]interface{}, namespace string, out io.Writer) error { generators := f.Generators("expose") generator, found := generators[serviceGenerator] diff --git a/pkg/kubectl/run.go b/pkg/kubectl/run.go index 3c10d40bba0..c6c3850925a 100644 --- a/pkg/kubectl/run.go +++ b/pkg/kubectl/run.go @@ -42,6 +42,7 @@ func (DeploymentV1Beta1) ParamNames() []GeneratorParam { {"name", true}, {"replicas", true}, {"image", true}, + {"image-pull-policy", false}, {"port", false}, {"hostport", false}, {"stdin", false}, @@ -90,7 +91,8 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime return nil, err } - if err = updatePodContainers(params, args, envs, podSpec); err != nil { + imagePullPolicy := api.PullPolicy(params["image-pull-policy"]) + if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil { return nil, err } @@ -217,6 +219,7 @@ func (JobV1Beta1) ParamNames() []GeneratorParam { {"default-name", false}, {"name", true}, {"image", true}, + {"image-pull-policy", false}, {"port", false}, {"hostport", false}, {"stdin", false}, @@ -262,7 +265,8 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object return nil, err } - if err = updatePodContainers(params, args, envs, podSpec); err != nil { + imagePullPolicy := api.PullPolicy(params["image-pull-policy"]) + if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil { return nil, err } @@ -312,6 +316,7 @@ func (JobV1) ParamNames() []GeneratorParam { {"default-name", false}, {"name", true}, {"image", true}, + {"image-pull-policy", false}, {"port", false}, {"hostport", false}, {"stdin", false}, @@ -357,7 +362,8 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err return nil, err } - if err = updateV1PodContainers(params, args, envs, podSpec); err != nil { + imagePullPolicy := v1.PullPolicy(params["image-pull-policy"]) + if err = updateV1PodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil { return nil, err } @@ -403,6 +409,7 @@ func (ScheduledJobV2Alpha1) ParamNames() []GeneratorParam { {"default-name", false}, {"name", true}, {"image", true}, + {"image-pull-policy", false}, {"port", false}, {"hostport", false}, {"stdin", false}, @@ -449,7 +456,8 @@ func (ScheduledJobV2Alpha1) Generate(genericParams map[string]interface{}) (runt return nil, err } - if err = updateV1PodContainers(params, args, envs, podSpec); err != nil { + imagePullPolicy := v1.PullPolicy(params["image-pull-policy"]) + if err = updateV1PodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil { return nil, err } @@ -502,6 +510,7 @@ func (BasicReplicationController) ParamNames() []GeneratorParam { {"name", true}, {"replicas", true}, {"image", true}, + {"image-pull-policy", false}, {"port", false}, {"hostport", false}, {"stdin", false}, @@ -690,7 +699,8 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{}) return nil, err } - if err = updatePodContainers(params, args, envs, podSpec); err != nil { + imagePullPolicy := api.PullPolicy(params["image-pull-policy"]) + if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil { return nil, err } @@ -717,7 +727,7 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{}) return &controller, nil } -func updatePodContainers(params map[string]string, args []string, envs []api.EnvVar, podSpec *api.PodSpec) error { +func updatePodContainers(params map[string]string, args []string, envs []api.EnvVar, imagePullPolicy api.PullPolicy, podSpec *api.PodSpec) error { if len(args) > 0 { command, err := GetBool(params, "command", false) if err != nil { @@ -733,10 +743,15 @@ func updatePodContainers(params map[string]string, args []string, envs []api.Env if len(envs) > 0 { podSpec.Containers[0].Env = envs } + + if len(imagePullPolicy) > 0 { + // imagePullPolicy should be valid here since we have verified it before. + podSpec.Containers[0].ImagePullPolicy = imagePullPolicy + } return nil } -func updateV1PodContainers(params map[string]string, args []string, envs []v1.EnvVar, podSpec *v1.PodSpec) error { +func updateV1PodContainers(params map[string]string, args []string, envs []v1.EnvVar, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error { if len(args) > 0 { command, err := GetBool(params, "command", false) if err != nil { @@ -752,6 +767,11 @@ func updateV1PodContainers(params map[string]string, args []string, envs []v1.En if len(envs) > 0 { podSpec.Containers[0].Env = envs } + + if len(imagePullPolicy) > 0 { + // imagePullPolicy should be valid here since we have verified it before. + podSpec.Containers[0].ImagePullPolicy = imagePullPolicy + } return nil } @@ -831,6 +851,7 @@ func (BasicPod) ParamNames() []GeneratorParam { {"default-name", false}, {"name", true}, {"image", true}, + {"image-pull-policy", false}, {"port", false}, {"hostport", false}, {"stdin", false}, @@ -894,6 +915,8 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object, if len(restartPolicy) == 0 { restartPolicy = api.RestartPolicyAlways } + // TODO: Figure out why we set ImagePullPolicy here, whether we can make it + // consistent with the other places imagePullPolicy is set using flag. pod := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: name, @@ -915,7 +938,8 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object, RestartPolicy: restartPolicy, }, } - if err = updatePodContainers(params, args, envs, &pod.Spec); err != nil { + imagePullPolicy := api.PullPolicy(params["image-pull-policy"]) + if err = updatePodContainers(params, args, envs, imagePullPolicy, &pod.Spec); err != nil { return nil, err } diff --git a/pkg/kubectl/run_test.go b/pkg/kubectl/run_test.go index 79c20edc770..72e1fe1875f 100644 --- a/pkg/kubectl/run_test.go +++ b/pkg/kubectl/run_test.go @@ -35,10 +35,11 @@ func TestGenerate(t *testing.T) { }{ { params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "-1", + "name": "foo", + "image": "someimage", + "image-pull-policy": "Always", + "replicas": "1", + "port": "-1", }, expected: &api.ReplicationController{ ObjectMeta: api.ObjectMeta{ @@ -55,8 +56,9 @@ func TestGenerate(t *testing.T) { Spec: api.PodSpec{ Containers: []api.Container{ { - Name: "foo", - Image: "someimage", + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullAlways, }, }, }, @@ -110,11 +112,12 @@ func TestGenerate(t *testing.T) { { params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "-1", - "args": []string{"bar", "baz", "blah"}, + "name": "foo", + "image": "someimage", + "image-pull-policy": "Never", + "replicas": "1", + "port": "-1", + "args": []string{"bar", "baz", "blah"}, }, expected: &api.ReplicationController{ ObjectMeta: api.ObjectMeta{ @@ -131,9 +134,10 @@ func TestGenerate(t *testing.T) { Spec: api.PodSpec{ Containers: []api.Container{ { - Name: "foo", - Image: "someimage", - Args: []string{"bar", "baz", "blah"}, + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullNever, + Args: []string{"bar", "baz", "blah"}, }, }, }, @@ -213,11 +217,12 @@ func TestGenerate(t *testing.T) { }, { params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "80", - "hostport": "80", + "name": "foo", + "image": "someimage", + "image-pull-policy": "IfNotPresent", + "replicas": "1", + "port": "80", + "hostport": "80", }, expected: &api.ReplicationController{ ObjectMeta: api.ObjectMeta{ @@ -234,8 +239,9 @@ func TestGenerate(t *testing.T) { Spec: api.PodSpec{ Containers: []api.Container{ { - Name: "foo", - Image: "someimage", + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullIfNotPresent, Ports: []api.ContainerPort{ { ContainerPort: 80, @@ -435,9 +441,10 @@ func TestGeneratePod(t *testing.T) { }, { params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "env": []string{"a=b", "c=d"}, + "name": "foo", + "image": "someimage", + "image-pull-policy": "Always", + "env": []string{"a=b", "c=d"}, }, expected: &api.Pod{ ObjectMeta: api.ObjectMeta{ @@ -448,7 +455,7 @@ func TestGeneratePod(t *testing.T) { { Name: "foo", Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, + ImagePullPolicy: api.PullAlways, Env: []api.EnvVar{ { Name: "a", @@ -639,18 +646,19 @@ func TestGenerateDeployment(t *testing.T) { }{ { params: map[string]interface{}{ - "labels": "foo=bar,baz=blah", - "name": "foo", - "replicas": "3", - "image": "someimage", - "port": "80", - "hostport": "80", - "stdin": "true", - "command": "true", - "args": []string{"bar", "baz", "blah"}, - "env": []string{"a=b", "c=d"}, - "requests": "cpu=100m,memory=100Mi", - "limits": "cpu=400m,memory=200Mi", + "labels": "foo=bar,baz=blah", + "name": "foo", + "replicas": "3", + "image": "someimage", + "image-pull-policy": "Always", + "port": "80", + "hostport": "80", + "stdin": "true", + "command": "true", + "args": []string{"bar", "baz", "blah"}, + "env": []string{"a=b", "c=d"}, + "requests": "cpu=100m,memory=100Mi", + "limits": "cpu=400m,memory=200Mi", }, expected: &extensions.Deployment{ ObjectMeta: api.ObjectMeta{ @@ -667,9 +675,10 @@ func TestGenerateDeployment(t *testing.T) { Spec: api.PodSpec{ Containers: []api.Container{ { - Name: "foo", - Image: "someimage", - Stdin: true, + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullAlways, + Stdin: true, Ports: []api.ContainerPort{ { ContainerPort: 80,