Add --user flag for service create and update (#679)

* add run as user flag #678

* add run as user flag #678

* add changelog for pr 679

* review comments  for pr 679

* review comments  for pr 679

* add test for config changes

* add user flag
This commit is contained in:
Murugappan Chetty 2020-02-28 03:55:51 -08:00 committed by GitHub
parent ab00cc2969
commit 66173059fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 113 additions and 0 deletions

View File

@ -59,6 +59,10 @@
| 🧽 | 🧽
| Add `--wait` and `--no-wait` to service delete operation. Change service delete to wait by default. | Add `--wait` and `--no-wait` to service delete operation. Change service delete to wait by default.
| https://github.com/knative/client/pull/682[#682] | https://github.com/knative/client/pull/682[#682]
| 🎁
| Add `--user` flag for specifying the user id to run the container
| https://github.com/knative/client/pull/679[#679]
|=== |===
## v0.12.0 (2020-01-29) ## v0.12.0 (2020-01-29)

View File

@ -70,6 +70,7 @@ kn service create NAME --image IMAGE [flags]
--requests-memory string The requested memory (e.g., 64Mi). --requests-memory string The requested memory (e.g., 64Mi).
--revision-name string The revision name to set. Must start with the service name and a dash as a prefix. Empty revision name will result in the server generating a name for the revision. Accepts golang templates, allowing {{.Service}} for the service name, {{.Generation}} for the generation, and {{.Random [n]}} for n random consonants. (default "{{.Service}}-{{.Random 5}}-{{.Generation}}") --revision-name string The revision name to set. Must start with the service name and a dash as a prefix. Empty revision name will result in the server generating a name for the revision. Accepts golang templates, allowing {{.Service}} for the service name, {{.Generation}} for the generation, and {{.Random [n]}} for n random consonants. (default "{{.Service}}-{{.Random 5}}-{{.Generation}}")
--service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace. --service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace.
--user int The user ID to run the container (e.g., 1001).
--volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret: or sc:). Example: --volume myvolume=cm:myconfigmap or --volume myvolume=secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-. --volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret: or sc:). Example: --volume myvolume=cm:myconfigmap or --volume myvolume=secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-.
--wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600) --wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600)
``` ```

View File

@ -68,6 +68,7 @@ kn service update NAME [flags]
--tag strings Set tag (format: --tag revisionRef=tagName) where revisionRef can be a revision or '@latest' string representing latest ready revision. This flag can be specified multiple times. --tag strings Set tag (format: --tag revisionRef=tagName) where revisionRef can be a revision or '@latest' string representing latest ready revision. This flag can be specified multiple times.
--traffic strings Set traffic distribution (format: --traffic revisionRef=percent) where revisionRef can be a revision or a tag or '@latest' string representing latest ready revision. This flag can be given multiple times with percent summing up to 100%. --traffic strings Set traffic distribution (format: --traffic revisionRef=percent) where revisionRef can be a revision or a tag or '@latest' string representing latest ready revision. This flag can be given multiple times with percent summing up to 100%.
--untag strings Untag revision (format: --untag tagName). This flag can be specified multiple times. --untag strings Untag revision (format: --untag tagName). This flag can be specified multiple times.
--user int The user ID to run the container (e.g., 1001).
--volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret: or sc:). Example: --volume myvolume=cm:myconfigmap or --volume myvolume=secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-. --volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret: or sc:). Example: --volume myvolume=cm:myconfigmap or --volume myvolume=secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-.
--wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600) --wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600)
``` ```

View File

@ -52,6 +52,7 @@ type ConfigurationEditFlags struct {
ServiceAccountName string ServiceAccountName string
ImagePullSecrets string ImagePullSecrets string
Annotations []string Annotations []string
User int64
// Preferences about how to do the action. // Preferences about how to do the action.
LockToDigest bool LockToDigest bool
@ -189,6 +190,8 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) {
"", "",
"Image pull secret to set. An empty argument (\"\") clears the pull secret. The referenced secret must exist in the service's namespace.") "Image pull secret to set. An empty argument (\"\") clears the pull secret. The referenced secret must exist in the service's namespace.")
p.markFlagMakesRevision("pull-secret") p.markFlagMakesRevision("pull-secret")
command.Flags().Int64VarP(&p.User, "user", "", 0, "The user ID to run the container (e.g., 1001).")
p.markFlagMakesRevision("user")
} }
// AddUpdateFlags adds the flags specific to update. // AddUpdateFlags adds the flags specific to update.
@ -396,6 +399,10 @@ func (p *ConfigurationEditFlags) Apply(
servinglib.UpdateImagePullSecrets(template, p.ImagePullSecrets) servinglib.UpdateImagePullSecrets(template, p.ImagePullSecrets)
} }
if cmd.Flags().Changed("user") {
servinglib.UpdateUser(template, p.User)
}
return nil return nil
} }

View File

@ -32,6 +32,7 @@ import (
"knative.dev/client/pkg/wait" "knative.dev/client/pkg/wait"
"knative.dev/client/pkg/util" "knative.dev/client/pkg/util"
"knative.dev/pkg/ptr"
) )
func TestServiceCreateImageMock(t *testing.T) { func TestServiceCreateImageMock(t *testing.T) {
@ -385,6 +386,29 @@ func TestServiceCreateWithMountSecret(t *testing.T) {
r.Validate() r.Validate()
} }
func TestServiceCreateWithUser(t *testing.T) {
client := knclient.NewMockKnServiceClient(t)
r := client.Recorder()
r.GetService("foo", nil, errors.NewNotFound(servingv1.Resource("service"), "foo"))
service := getService("foo")
template := &service.Spec.Template
template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
RunAsUser: ptr.Int64(int64(1001)),
}
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
template.Annotations = map[string]string{servinglib.UserImageAnnotationKey: "gcr.io/foo/bar:baz"}
r.CreateService(service, nil)
output, err := executeServiceCommand(client, "create", "foo", "--image", "gcr.io/foo/bar:baz", "--user", "1001", "--no-wait", "--revision-name=")
assert.NilError(t, err)
assert.Assert(t, util.ContainsAll(output, "created", "foo", "default"))
r.Validate()
}
func getService(name string) *servingv1.Service { func getService(name string) *servingv1.Service {
service := &servingv1.Service{ service := &servingv1.Service{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{

View File

@ -26,6 +26,7 @@ import (
clientserving "knative.dev/client/pkg/serving" clientserving "knative.dev/client/pkg/serving"
clientservingv1 "knative.dev/client/pkg/serving/v1" clientservingv1 "knative.dev/client/pkg/serving/v1"
"knative.dev/client/pkg/util" "knative.dev/client/pkg/util"
"knative.dev/pkg/ptr"
) )
func TestServiceUpdateEnvMock(t *testing.T) { func TestServiceUpdateEnvMock(t *testing.T) {
@ -1427,3 +1428,48 @@ func TestServiceUpdateWithRemovingMount(t *testing.T) {
r.Validate() r.Validate()
} }
func TestServiceUpdateUser(t *testing.T) {
client := clientservingv1.NewMockKnServiceClient(t)
svcName := "svc1"
newService := getService(svcName)
template := &newService.Spec.Template
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
RunAsUser: ptr.Int64(int64(1001)),
}
template.ObjectMeta.Annotations = map[string]string{
clientserving.UserImageAnnotationKey: "gcr.io/foo/bar:baz",
}
updatedService := getService(svcName)
template = &updatedService.Spec.Template
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
RunAsUser: ptr.Int64(int64(1002)),
}
template.ObjectMeta.Annotations = map[string]string{
clientserving.UserImageAnnotationKey: "gcr.io/foo/bar:baz",
}
r := client.Recorder()
recordServiceUpdateWithSuccess(r, svcName, newService, updatedService)
output, err := executeServiceCommand(client,
"create", svcName, "--image", "gcr.io/foo/bar:baz",
"--user", "1001",
"--no-wait", "--revision-name=",
)
assert.NilError(t, err)
assert.Assert(t, util.ContainsAll(output, "created", svcName, "default"))
output, err = executeServiceCommand(client,
"update", svcName,
"--user", "1002",
"--no-wait", "--revision-name=",
)
assert.NilError(t, err)
assert.Assert(t, util.ContainsAll(output, "updated", svcName, "default"))
r.Validate()
}

View File

@ -337,6 +337,18 @@ func UpdateContainerPort(template *servingv1.RevisionTemplateSpec, port int32) e
return nil return nil
} }
// UpdateRunAsUser updates container with a given user id
func UpdateUser(template *servingv1.RevisionTemplateSpec, user int64) error {
container, err := ContainerOfRevisionTemplate(template)
if err != nil {
return err
}
container.SecurityContext = &corev1.SecurityContext{
RunAsUser: &user,
}
return nil
}
// UpdateResources updates resources as requested // UpdateResources updates resources as requested
func UpdateResources(template *servingv1.RevisionTemplateSpec, requestsResourceList corev1.ResourceList, limitsResourceList corev1.ResourceList) error { func UpdateResources(template *servingv1.RevisionTemplateSpec, requestsResourceList corev1.ResourceList, limitsResourceList corev1.ResourceList) error {
container, err := ContainerOfRevisionTemplate(template) container, err := ContainerOfRevisionTemplate(template)

View File

@ -300,6 +300,10 @@ func checkPortUpdate(t *testing.T, template *servingv1.RevisionTemplateSpec, por
} }
} }
func checkUserUpdate(t *testing.T, template *servingv1.RevisionTemplateSpec, user *int64) {
assert.DeepEqual(t, template.Spec.Containers[0].SecurityContext.RunAsUser, user)
}
func TestUpdateEnvVarsBoth(t *testing.T) { func TestUpdateEnvVarsBoth(t *testing.T) {
template, container := getRevisionTemplate() template, container := getRevisionTemplate()
container.Env = []corev1.EnvVar{ container.Env = []corev1.EnvVar{
@ -649,6 +653,20 @@ func TestGenerateVolumeName(t *testing.T) {
} }
} }
func TestUpdateUser(t *testing.T) {
template, _ := getRevisionTemplate()
err := UpdateUser(template, int64(1001))
assert.NilError(t, err)
checkUserUpdate(t, template, ptr.Int64(int64(1001)))
template.Spec.Containers[0].SecurityContext.RunAsUser = ptr.Int64(int64(1002))
err = UpdateUser(template, int64(1002))
assert.NilError(t, err)
checkUserUpdate(t, template, ptr.Int64(int64(1002)))
}
// //
// ========================================================================================================= // =========================================================================================================