diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 08f7a34e7d..457103072e 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -18,6 +18,10 @@ |=== | | Description | PR +| 🎁 +| Add concurrency-utilization option for `service create` and `service update` (`--concurrency-utilization`) +| https://github.com/knative/client/pull/788[#788] + | 🐣 | Refactor `e2e` common code into `lib/test` | https://github.com/knative/client/pull/765[#765] diff --git a/docs/cmd/kn_service_create.md b/docs/cmd/kn_service_create.md index 834e94f1a9..90bb04f192 100644 --- a/docs/cmd/kn_service_create.md +++ b/docs/cmd/kn_service_create.md @@ -45,41 +45,42 @@ kn service create NAME --image IMAGE [flags] ### Options ``` - --annotation stringArray Service annotation to set. name=value; you may provide this flag any number of times to set multiple annotations. To unset, specify the annotation name followed by a "-" (e.g., name-). - --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. - --async DEPRECATED: please use --no-wait instead. Create service and don't wait for it to be ready. - --autoscale-window string Duration to look back for making auto-scaling decisions. The service is scaled to zero if no request was received in during that time. (eg: 10s) - --cluster-local Specify that the service be private. (--no-cluster-local will make the service publicly available) - --cmd string Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd /app/start --arg myArg to pass aditional arguments. - --concurrency-limit int Hard Limit of concurrent requests to be processed by a single replica. - --concurrency-target int Recommendation for when to scale up based on the concurrent number of incoming request. Defaults to --concurrency-limit when given. - -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). - --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. - --force Create service forcefully, replaces existing service if any. - -h, --help help for create - --image string Image to run. - -l, --label stringArray Labels to set for both Service and Revision. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). - --label-revision stringArray Revision label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. - --label-service stringArray Service label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. - --limits-cpu string The limits on the requested CPU (e.g., 1000m). - --limits-memory string The limits on the requested memory (e.g., 1024Mi). - --lock-to-digest Keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) (default true) - --max-scale int Maximal number of replicas. - --min-scale int Minimal number of replicas. - --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. - -n, --namespace string Specify the namespace to operate in. - --no-cluster-local Do not specify that the service be private. (--no-cluster-local will make the service publicly available) (default true) - --no-lock-to-digest Do not keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) - --no-wait Create service and don't wait for it to be ready. - -p, --port int32 The port where application listens on. - --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. - --requests-cpu string The requested CPU (e.g., 250m). - --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}}") - --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-. - --wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600) + --annotation stringArray Service annotation to set. name=value; you may provide this flag any number of times to set multiple annotations. To unset, specify the annotation name followed by a "-" (e.g., name-). + --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. + --async DEPRECATED: please use --no-wait instead. Create service and don't wait for it to be ready. + --autoscale-window string Duration to look back for making auto-scaling decisions. The service is scaled to zero if no request was received in during that time. (eg: 10s) + --cluster-local Specify that the service be private. (--no-cluster-local will make the service publicly available) + --cmd string Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd /app/start --arg myArg to pass aditional arguments. + --concurrency-limit int Hard Limit of concurrent requests to be processed by a single replica. + --concurrency-target int Recommendation for when to scale up based on the concurrent number of incoming request. Defaults to --concurrency-limit when given. + --concurrency-utilization int Percentage of concurrent requests utilization before scaling up. (default 70) + -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). + --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. + --force Create service forcefully, replaces existing service if any. + -h, --help help for create + --image string Image to run. + -l, --label stringArray Labels to set for both Service and Revision. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). + --label-revision stringArray Revision label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. + --label-service stringArray Service label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. + --limits-cpu string The limits on the requested CPU (e.g., 1000m). + --limits-memory string The limits on the requested memory (e.g., 1024Mi). + --lock-to-digest Keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) (default true) + --max-scale int Maximal number of replicas. + --min-scale int Minimal number of replicas. + --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. + -n, --namespace string Specify the namespace to operate in. + --no-cluster-local Do not specify that the service be private. (--no-cluster-local will make the service publicly available) (default true) + --no-lock-to-digest Do not keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) + --no-wait Create service and don't wait for it to be ready. + -p, --port int32 The port where application listens on. + --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. + --requests-cpu string The requested CPU (e.g., 250m). + --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}}") + --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-. + --wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600) ``` ### Options inherited from parent commands diff --git a/docs/cmd/kn_service_update.md b/docs/cmd/kn_service_update.md index b6afaa9107..c05c30ab05 100644 --- a/docs/cmd/kn_service_update.md +++ b/docs/cmd/kn_service_update.md @@ -38,43 +38,44 @@ kn service update NAME [flags] ### Options ``` - --annotation stringArray Service annotation to set. name=value; you may provide this flag any number of times to set multiple annotations. To unset, specify the annotation name followed by a "-" (e.g., name-). - --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. - --async DEPRECATED: please use --no-wait instead. Update service and don't wait for it to be ready. - --autoscale-window string Duration to look back for making auto-scaling decisions. The service is scaled to zero if no request was received in during that time. (eg: 10s) - --cluster-local Specify that the service be private. (--no-cluster-local will make the service publicly available) - --cmd string Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd /app/start --arg myArg to pass aditional arguments. - --concurrency-limit int Hard Limit of concurrent requests to be processed by a single replica. - --concurrency-target int Recommendation for when to scale up based on the concurrent number of incoming request. Defaults to --concurrency-limit when given. - -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). - --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. - -h, --help help for update - --image string Image to run. - -l, --label stringArray Labels to set for both Service and Revision. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). - --label-revision stringArray Revision label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. - --label-service stringArray Service label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. - --limits-cpu string The limits on the requested CPU (e.g., 1000m). - --limits-memory string The limits on the requested memory (e.g., 1024Mi). - --lock-to-digest Keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) (default true) - --max-scale int Maximal number of replicas. - --min-scale int Minimal number of replicas. - --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. - -n, --namespace string Specify the namespace to operate in. - --no-cluster-local Do not specify that the service be private. (--no-cluster-local will make the service publicly available) (default true) - --no-lock-to-digest Do not keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) - --no-wait Update service and don't wait for it to be ready. - -p, --port int32 The port where application listens on. - --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. - --requests-cpu string The requested CPU (e.g., 250m). - --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}}") - --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. - --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%. - --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-. - --wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600) + --annotation stringArray Service annotation to set. name=value; you may provide this flag any number of times to set multiple annotations. To unset, specify the annotation name followed by a "-" (e.g., name-). + --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. + --async DEPRECATED: please use --no-wait instead. Update service and don't wait for it to be ready. + --autoscale-window string Duration to look back for making auto-scaling decisions. The service is scaled to zero if no request was received in during that time. (eg: 10s) + --cluster-local Specify that the service be private. (--no-cluster-local will make the service publicly available) + --cmd string Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd /app/start --arg myArg to pass aditional arguments. + --concurrency-limit int Hard Limit of concurrent requests to be processed by a single replica. + --concurrency-target int Recommendation for when to scale up based on the concurrent number of incoming request. Defaults to --concurrency-limit when given. + --concurrency-utilization int Percentage of concurrent requests utilization before scaling up. (default 70) + -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). + --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. + -h, --help help for update + --image string Image to run. + -l, --label stringArray Labels to set for both Service and Revision. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). + --label-revision stringArray Revision label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. + --label-service stringArray Service label to set. name=value; you may provide this flag any number of times to set multiple labels. To unset, specify the label name followed by a "-" (e.g., name-). This flag takes precedence over "label" flag. + --limits-cpu string The limits on the requested CPU (e.g., 1000m). + --limits-memory string The limits on the requested memory (e.g., 1024Mi). + --lock-to-digest Keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) (default true) + --max-scale int Maximal number of replicas. + --min-scale int Minimal number of replicas. + --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. + -n, --namespace string Specify the namespace to operate in. + --no-cluster-local Do not specify that the service be private. (--no-cluster-local will make the service publicly available) (default true) + --no-lock-to-digest Do not keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) + --no-wait Update service and don't wait for it to be ready. + -p, --port int32 The port where application listens on. + --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. + --requests-cpu string The requested CPU (e.g., 250m). + --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}}") + --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. + --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%. + --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-. + --wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600) ``` ### Options inherited from parent commands diff --git a/pkg/kn/commands/revision/describe.go b/pkg/kn/commands/revision/describe.go index b6f9119489..afdea65027 100644 --- a/pkg/kn/commands/revision/describe.go +++ b/pkg/kn/commands/revision/describe.go @@ -131,7 +131,8 @@ func WriteConcurrencyOptions(dw printers.PrefixWriter, revision *servingv1.Revis target := clientserving.ConcurrencyTarget(&revision.ObjectMeta) limit := revision.Spec.ContainerConcurrency autoscaleWindow := clientserving.AutoscaleWindow(&revision.ObjectMeta) - if target != nil || limit != nil && *limit != 0 || autoscaleWindow != "" { + concurrencyUtilization := clientserving.ConcurrencyTargetUtilization(&revision.ObjectMeta) + if target != nil || limit != nil && *limit != 0 || autoscaleWindow != "" || concurrencyUtilization != nil { section := dw.WriteAttribute("Concurrency", "") if limit != nil && *limit != 0 { section.WriteAttribute("Limit", strconv.FormatInt(int64(*limit), 10)) @@ -142,6 +143,9 @@ func WriteConcurrencyOptions(dw printers.PrefixWriter, revision *servingv1.Revis if autoscaleWindow != "" { section.WriteAttribute("Window", autoscaleWindow) } + if concurrencyUtilization != nil { + section.WriteAttribute("TargetUtilization", strconv.Itoa(*concurrencyUtilization)) + } } } diff --git a/pkg/kn/commands/service/configuration_edit_flags.go b/pkg/kn/commands/service/configuration_edit_flags.go index 26a1516f2d..505fcac3d2 100644 --- a/pkg/kn/commands/service/configuration_edit_flags.go +++ b/pkg/kn/commands/service/configuration_edit_flags.go @@ -48,6 +48,7 @@ type ConfigurationEditFlags struct { MaxScale int ConcurrencyTarget int ConcurrencyLimit int + ConcurrencyUtilization int AutoscaleWindow string Port int32 Labels []string @@ -180,6 +181,10 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) { "Hard Limit of concurrent requests to be processed by a single replica.") p.markFlagMakesRevision("concurrency-limit") + command.Flags().IntVar(&p.ConcurrencyUtilization, "concurrency-utilization", 70, + "Percentage of concurrent requests utilization before scaling up.") + p.markFlagMakesRevision("concurrency-utilization") + command.Flags().Int32VarP(&p.Port, "port", "p", 0, "The port where application listens on.") p.markFlagMakesRevision("port") @@ -404,6 +409,13 @@ func (p *ConfigurationEditFlags) Apply( } } + if cmd.Flags().Changed("concurrency-utilization") { + err = servinglib.UpdateConcurrencyUtilization(template, p.ConcurrencyUtilization) + if err != nil { + return err + } + } + if cmd.Flags().Changed("cluster-local") || cmd.Flags().Changed("no-cluster-local") { if p.ClusterLocal { labels := servinglib.UpdateLabels(service.ObjectMeta.Labels, map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}, []string{}) diff --git a/pkg/kn/commands/service/create_test.go b/pkg/kn/commands/service/create_test.go index 548018f188..568bbcbb62 100644 --- a/pkg/kn/commands/service/create_test.go +++ b/pkg/kn/commands/service/create_test.go @@ -377,6 +377,7 @@ func TestServiceCreateMaxMinScale(t *testing.T) { "service", "create", "foo", "--image", "gcr.io/foo/bar:baz", "--min-scale", "1", "--max-scale", "5", "--concurrency-target", "10", "--concurrency-limit", "100", + "--concurrency-utilization", "50", "--no-wait"}, false) if err != nil { @@ -392,6 +393,7 @@ func TestServiceCreateMaxMinScale(t *testing.T) { "autoscaling.knative.dev/minScale", "1", "autoscaling.knative.dev/maxScale", "5", "autoscaling.knative.dev/target", "10", + "autoscaling.knative.dev/targetUtilizationPercentage", "50", } for i := 0; i < len(expectedAnnos); i += 2 { diff --git a/pkg/kn/commands/service/describe_test.go b/pkg/kn/commands/service/describe_test.go index 0c2bceb84b..0c7513038a 100644 --- a/pkg/kn/commands/service/describe_test.go +++ b/pkg/kn/commands/service/describe_test.go @@ -253,13 +253,13 @@ func TestServiceDescribeScaling(t *testing.T) { for _, data := range []struct { minScale, maxScale, limit, target string - scaleOut string + scaleOut, utilization string }{ - {"", "", "", "", ""}, - {"", "10", "", "", "0 ... 10"}, - {"10", "", "", "", "10 ... ∞"}, - {"5", "20", "10", "", "5 ... 20"}, - {"", "", "20", "30", ""}, + {"", "", "", "", "", ""}, + {"", "10", "", "", "0 ... 10", ""}, + {"10", "", "", "", "10 ... ∞", ""}, + {"5", "20", "10", "", "5 ... 20", ""}, + {"", "", "20", "30", "", "50"}, } { // New mock client client := knclient.NewMockKnServiceClient(t) @@ -273,7 +273,7 @@ func TestServiceDescribeScaling(t *testing.T) { // Get service & revision r.GetService("foo", &expectedService, nil) rev1 := createTestRevision("rev1", 1) - addScaling(&rev1, data.minScale, data.maxScale, data.target, data.limit) + addScaling(&rev1, data.minScale, data.maxScale, data.target, data.limit, data.utilization) r.GetRevision("rev1", &rev1, nil) revList := servingv1.RevisionList{ @@ -580,7 +580,7 @@ func createTestServiceWithServiceAccount(name string, revisionNames []string, se return service } -func addScaling(revision *servingv1.Revision, minScale, maxScale, concurrencyTarget, concurrencyLimit string) { +func addScaling(revision *servingv1.Revision, minScale, maxScale, concurrencyTarget, concurrencyLimit, utilization string) { annos := make(map[string]string) if minScale != "" { annos[autoscaling.MinScaleAnnotationKey] = minScale @@ -591,6 +591,9 @@ func addScaling(revision *servingv1.Revision, minScale, maxScale, concurrencyTar if concurrencyTarget != "" { annos[autoscaling.TargetAnnotationKey] = concurrencyTarget } + if utilization != "" { + annos[autoscaling.TargetUtilizationPercentageKey] = utilization + } revision.Annotations = annos if concurrencyLimit != "" { l, _ := strconv.ParseInt(concurrencyLimit, 10, 64) diff --git a/pkg/kn/commands/service/update_test.go b/pkg/kn/commands/service/update_test.go index e197684c77..9364ff7914 100644 --- a/pkg/kn/commands/service/update_test.go +++ b/pkg/kn/commands/service/update_test.go @@ -319,7 +319,7 @@ func TestServiceUpdateMaxMinScale(t *testing.T) { action, updated, _, err := fakeServiceUpdate(original, []string{ "service", "update", "foo", - "--min-scale", "1", "--max-scale", "5", "--concurrency-target", "10", "--concurrency-limit", "100", "--no-wait"}) + "--min-scale", "1", "--max-scale", "5", "--concurrency-target", "10", "--concurrency-limit", "100", "--concurrency-utilization", "50", "--no-wait"}) if err != nil { t.Fatal(err) @@ -337,6 +337,7 @@ func TestServiceUpdateMaxMinScale(t *testing.T) { "autoscaling.knative.dev/minScale", "1", "autoscaling.knative.dev/maxScale", "5", "autoscaling.knative.dev/target", "10", + "autoscaling.knative.dev/targetUtilizationPercentage", "50", } for i := 0; i < len(expectedAnnos); i += 2 { diff --git a/pkg/serving/config_changes.go b/pkg/serving/config_changes.go index b91e2612e2..e12354749d 100644 --- a/pkg/serving/config_changes.go +++ b/pkg/serving/config_changes.go @@ -198,6 +198,11 @@ func UpdateConcurrencyTarget(template *servingv1.RevisionTemplateSpec, target in return UpdateRevisionTemplateAnnotation(template, autoscaling.TargetAnnotationKey, strconv.Itoa(target)) } +// UpdateConcurrencyUtilization updates container target utilization percentage annotation +func UpdateConcurrencyUtilization(template *servingv1.RevisionTemplateSpec, target int) error { + return UpdateRevisionTemplateAnnotation(template, autoscaling.TargetUtilizationPercentageKey, strconv.Itoa(target)) +} + // UpdateConcurrencyLimit updates container concurrency limit func UpdateConcurrencyLimit(template *servingv1.RevisionTemplateSpec, limit int64) error { if limit < 0 { diff --git a/pkg/serving/config_changes_test.go b/pkg/serving/config_changes_test.go index 75879a58a5..e4f904eb84 100644 --- a/pkg/serving/config_changes_test.go +++ b/pkg/serving/config_changes_test.go @@ -33,7 +33,7 @@ import ( func TestUpdateAutoscalingAnnotations(t *testing.T) { template := &servingv1.RevisionTemplateSpec{} - updateConcurrencyConfiguration(template, 10, 100, 1000, 1000) + updateConcurrencyConfiguration(template, 10, 100, 1000, 1000, 50) annos := template.Annotations if annos[autoscaling.MinScaleAnnotationKey] != "10" { t.Error("minScale failed") @@ -51,9 +51,9 @@ func TestUpdateAutoscalingAnnotations(t *testing.T) { func TestUpdateInvalidAutoscalingAnnotations(t *testing.T) { template := &servingv1.RevisionTemplateSpec{} - updateConcurrencyConfiguration(template, 10, 100, 1000, 1000) + updateConcurrencyConfiguration(template, 10, 100, 1000, 1000, 50) // Update with invalid concurrency options - updateConcurrencyConfiguration(template, -1, -1, 0, -1) + updateConcurrencyConfiguration(template, -1, -1, 0, -1, 200) annos := template.Annotations if annos[autoscaling.MinScaleAnnotationKey] != "10" { t.Error("minScale failed") @@ -64,6 +64,9 @@ func TestUpdateInvalidAutoscalingAnnotations(t *testing.T) { if annos[autoscaling.TargetAnnotationKey] != "1000" { t.Error("target failed") } + if annos[autoscaling.TargetUtilizationPercentageKey] != "50" { + t.Error("concurrency utilization failed") + } if *template.Spec.ContainerConcurrency != 1000 { t.Error("limit failed") } @@ -723,9 +726,10 @@ func checkContainerConcurrency(t *testing.T, template *servingv1.RevisionTemplat } } -func updateConcurrencyConfiguration(template *servingv1.RevisionTemplateSpec, minScale int, maxScale int, target int, limit int) { +func updateConcurrencyConfiguration(template *servingv1.RevisionTemplateSpec, minScale, maxScale, target, limit, utilization int) { UpdateMinScale(template, minScale) UpdateMaxScale(template, maxScale) UpdateConcurrencyTarget(template, target) UpdateConcurrencyLimit(template, int64(limit)) + UpdateConcurrencyUtilization(template, utilization) } diff --git a/pkg/serving/revision_template.go b/pkg/serving/revision_template.go index a42add6e37..553ebc9e95 100644 --- a/pkg/serving/revision_template.go +++ b/pkg/serving/revision_template.go @@ -63,6 +63,11 @@ func ConcurrencyTarget(m *metav1.ObjectMeta) *int { return ret } +func ConcurrencyTargetUtilization(m *metav1.ObjectMeta) *int { + ret, _ := annotationAsInt(m, autoscaling.TargetUtilizationPercentageKey) + return ret +} + func AutoscaleWindow(m *metav1.ObjectMeta) string { return m.Annotations[autoscaling.WindowAnnotationKey] } diff --git a/test/e2e/service_options_test.go b/test/e2e/service_options_test.go index 985b9ce4a1..6747db1e5b 100644 --- a/test/e2e/service_options_test.go +++ b/test/e2e/service_options_test.go @@ -46,9 +46,10 @@ func TestServiceOptions(t *testing.T) { t.Log("create and validate service with concurrency options") defer r.DumpIfFailed() - serviceCreateWithOptions(r, "svc1", "--concurrency-limit", "250", "--concurrency-target", "300") + serviceCreateWithOptions(r, "svc1", "--concurrency-limit", "250", "--concurrency-target", "300", "--concurrency-utilization", "50") validateServiceConcurrencyTarget(r, "svc1", "300") validateServiceConcurrencyLimit(r, "svc1", "250") + validateServiceConcurrencyUtilization(r, "svc1", "50") t.Log("update and validate service with concurrency limit") serviceUpdate(r, "svc1", "--concurrency-limit", "300") @@ -62,6 +63,7 @@ func TestServiceOptions(t *testing.T) { t.Log("returns steady concurrency options for service") validateServiceConcurrencyLimit(r, "svc1", "300") validateServiceConcurrencyTarget(r, "svc1", "300") + validateServiceConcurrencyUtilization(r, "svc1", "50") t.Log("delete service") serviceDelete(r, "svc1") @@ -135,6 +137,13 @@ func validateServiceConcurrencyTarget(r *test.KnRunResultCollector, serviceName, r.AssertNoError(out) } +func validateServiceConcurrencyUtilization(r *test.KnRunResultCollector, serviceName, concurrencyUtilization string) { + jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/targetUtilizationPercentage}" + out := r.KnTest().Kn().Run("service", "list", serviceName, "-o", jsonpath) + assert.Equal(r.T(), out.Stdout, concurrencyUtilization) + r.AssertNoError(out) +} + func validateAutoscaleWindow(r *test.KnRunResultCollector, serviceName, window string) { jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/window}" out := r.KnTest().Kn().Run("service", "list", serviceName, "-o", jsonpath)