From 5001dcdc16a4a2586d9cfd61b3307da16886228c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Wed, 22 Jan 2020 09:18:23 +0100 Subject: [PATCH] feature(serving): Add --autoscale-window (#614) * feature(serving): Add --autoscale-window Fixes #613 * chore: Update help-text for --autoscale-window * chore: Add missing generated files --- docs/cmd/kn_service_create.md | 51 ++++++++--------- docs/cmd/kn_service_update.md | 55 ++++++++++--------- pkg/kn/commands/revision/describe.go | 7 ++- .../service/configuration_edit_flags.go | 10 ++++ pkg/serving/config_changes.go | 10 ++++ pkg/serving/config_changes_test.go | 26 +++++++-- pkg/serving/revision_template.go | 4 ++ test/e2e/service_options_test.go | 15 +++++ 8 files changed, 121 insertions(+), 57 deletions(-) diff --git a/docs/cmd/kn_service_create.md b/docs/cmd/kn_service_create.md index c5b193489..44ab350a2 100644 --- a/docs/cmd/kn_service_create.md +++ b/docs/cmd/kn_service_create.md @@ -42,31 +42,32 @@ 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-). - --async Create service and don't wait for it to become ready. - --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 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-). - --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-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) - -p, --port int32 The port where application listens on. - --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. Empty service account name will result to clear the service account. - --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-). + --async Create service and don't wait for it to become 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) + --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 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-). + --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-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) + -p, --port int32 The port where application listens on. + --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. Empty service account name will result to clear the service account. + --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 092dedfdb..35f40c0d5 100644 --- a/docs/cmd/kn_service_update.md +++ b/docs/cmd/kn_service_update.md @@ -38,33 +38,34 @@ 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-). - --async Update service and don't wait for it to become ready. - --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 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-). - --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-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) - -p, --port int32 The port where application listens on. - --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. Empty service account name will result to clear the service account. - --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. - --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-). + --async Update service and don't wait for it to become 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) + --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 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-). + --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-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) + -p, --port int32 The port where application listens on. + --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. Empty service account name will result to clear the service account. + --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. + --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 4a37f27ca..686036f7e 100644 --- a/pkg/kn/commands/revision/describe.go +++ b/pkg/kn/commands/revision/describe.go @@ -129,7 +129,8 @@ func describe(w io.Writer, revision *v1alpha1.Revision, service *v1alpha1.Servic func WriteConcurrencyOptions(dw printers.PrefixWriter, revision *v1alpha1.Revision) { target := clientserving.ConcurrencyTarget(&revision.ObjectMeta) limit := revision.Spec.ContainerConcurrency - if target != nil || limit != nil && *limit != 0 { + autoscaleWindow := clientserving.AutoscaleWindow(&revision.ObjectMeta) + if target != nil || limit != nil && *limit != 0 || autoscaleWindow != "" { section := dw.WriteAttribute("Concurrency", "") if limit != nil && *limit != 0 { section.WriteAttribute("Limit", strconv.FormatInt(int64(*limit), 10)) @@ -137,7 +138,11 @@ func WriteConcurrencyOptions(dw printers.PrefixWriter, revision *v1alpha1.Revisi if target != nil { section.WriteAttribute("Target", strconv.Itoa(*target)) } + if autoscaleWindow != "" { + section.WriteAttribute("Window", autoscaleWindow) + } } + } // Write the image attribute (with diff --git a/pkg/kn/commands/service/configuration_edit_flags.go b/pkg/kn/commands/service/configuration_edit_flags.go index 6c1505c9c..3aa6ea439 100644 --- a/pkg/kn/commands/service/configuration_edit_flags.go +++ b/pkg/kn/commands/service/configuration_edit_flags.go @@ -41,6 +41,7 @@ type ConfigurationEditFlags struct { MaxScale int ConcurrencyTarget int ConcurrencyLimit int + AutoscaleWindow string Port int32 Labels []string NamePrefix string @@ -113,6 +114,8 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) { p.markFlagMakesRevision("min-scale") command.Flags().IntVar(&p.MaxScale, "max-scale", 0, "Maximal number of replicas.") p.markFlagMakesRevision("max-scale") + command.Flags().StringVar(&p.AutoscaleWindow, "autoscale-window", "", "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)") + p.markFlagMakesRevision("autoscale-window") command.Flags().IntVar(&p.ConcurrencyTarget, "concurrency-target", 0, "Recommendation for when to scale up based on the concurrent number of incoming request. "+ "Defaults to --concurrency-limit when given.") @@ -290,6 +293,13 @@ func (p *ConfigurationEditFlags) Apply( } } + if cmd.Flags().Changed("autoscale-window") { + err = servinglib.UpdateAutoscaleWindow(template, p.AutoscaleWindow) + if err != nil { + return err + } + } + if cmd.Flags().Changed("concurrency-target") { err = servinglib.UpdateConcurrencyTarget(template, p.ConcurrencyTarget) if err != nil { diff --git a/pkg/serving/config_changes.go b/pkg/serving/config_changes.go index 75a50cb59..53f064b94 100644 --- a/pkg/serving/config_changes.go +++ b/pkg/serving/config_changes.go @@ -21,6 +21,7 @@ import ( "sort" "strconv" "strings" + "time" "unicode" corev1 "k8s.io/api/core/v1" @@ -183,6 +184,15 @@ func UpdateMaxScale(template *servingv1alpha1.RevisionTemplateSpec, max int) err return UpdateRevisionTemplateAnnotation(template, autoscaling.MaxScaleAnnotationKey, strconv.Itoa(max)) } +// UpdateAutoscaleWindow updates the autoscale window annotation +func UpdateAutoscaleWindow(template *servingv1alpha1.RevisionTemplateSpec, window string) error { + _, err := time.ParseDuration(window) + if err != nil { + return fmt.Errorf("invalid duration for 'autoscale-window': %v", err) + } + return UpdateRevisionTemplateAnnotation(template, autoscaling.WindowAnnotationKey, window) +} + // UpdateConcurrencyTarget updates container concurrency annotation func UpdateConcurrencyTarget(template *servingv1alpha1.RevisionTemplateSpec, target int) error { return UpdateRevisionTemplateAnnotation(template, autoscaling.TargetAnnotationKey, strconv.Itoa(target)) diff --git a/pkg/serving/config_changes_test.go b/pkg/serving/config_changes_test.go index d7603fc6d..a431bf3b1 100644 --- a/pkg/serving/config_changes_test.go +++ b/pkg/serving/config_changes_test.go @@ -225,7 +225,7 @@ func TestUpdateMinScale(t *testing.T) { err := UpdateMinScale(template, 10) assert.NilError(t, err) // Verify update is successful or not - checkAnnotationValue(t, template, autoscaling.MinScaleAnnotationKey, 10) + checkAnnotationValueInt(t, template, autoscaling.MinScaleAnnotationKey, 10) // Update with invalid value err = UpdateMinScale(template, -1) assert.ErrorContains(t, err, "minScale") @@ -236,18 +236,29 @@ func TestUpdateMaxScale(t *testing.T) { err := UpdateMaxScale(template, 10) assert.NilError(t, err) // Verify update is successful or not - checkAnnotationValue(t, template, autoscaling.MaxScaleAnnotationKey, 10) + checkAnnotationValueInt(t, template, autoscaling.MaxScaleAnnotationKey, 10) // Update with invalid value err = UpdateMaxScale(template, -1) assert.ErrorContains(t, err, "maxScale") } +func TestAutoscaleWindow(t *testing.T) { + template, _ := getV1alpha1RevisionTemplateWithOldFields() + err := UpdateAutoscaleWindow(template, "10s") + assert.NilError(t, err) + // Verify update is successful or not + checkAnnotationValue(t, template, autoscaling.WindowAnnotationKey, "10s") + // Update with invalid value + err = UpdateAutoscaleWindow(template, "blub") + assert.Check(t, util.ContainsAll(err.Error(), "invalid duration", "autoscale-window")) +} + func TestUpdateConcurrencyTarget(t *testing.T) { template, _ := getV1alpha1RevisionTemplateWithOldFields() err := UpdateConcurrencyTarget(template, 10) assert.NilError(t, err) // Verify update is successful or not - checkAnnotationValue(t, template, autoscaling.TargetAnnotationKey, 10) + checkAnnotationValueInt(t, template, autoscaling.TargetAnnotationKey, 10) // Update with invalid value err = UpdateConcurrencyTarget(template, -1) assert.ErrorContains(t, err, "invalid") @@ -708,13 +719,20 @@ func assertNoV1alpha1(t *testing.T, template *servingv1alpha1.RevisionTemplateSp } } -func checkAnnotationValue(t *testing.T, template *servingv1alpha1.RevisionTemplateSpec, key string, value int) { +func checkAnnotationValueInt(t *testing.T, template *servingv1alpha1.RevisionTemplateSpec, key string, value int) { anno := template.GetAnnotations() if v, ok := anno[key]; !ok && v != strconv.Itoa(value) { t.Errorf("Failed to update %s annotation key: got=%s, want=%d", key, v, value) } } +func checkAnnotationValue(t *testing.T, template *servingv1alpha1.RevisionTemplateSpec, key string, value string) { + anno := template.GetAnnotations() + if v, ok := anno[key]; !ok && v != value { + t.Errorf("Failed to update %s annotation key: got=%s, want=%s", key, v, value) + } +} + func checkContainerConcurrency(t *testing.T, template *servingv1alpha1.RevisionTemplateSpec, value *int64) { if got, want := *template.Spec.ContainerConcurrency, *value; got != want { t.Errorf("Failed to update containerConcurrency value: got=%d, want=%d", got, want) diff --git a/pkg/serving/revision_template.go b/pkg/serving/revision_template.go index 4eb9fbd1d..b956d71fa 100644 --- a/pkg/serving/revision_template.go +++ b/pkg/serving/revision_template.go @@ -67,6 +67,10 @@ func ConcurrencyTarget(m *metav1.ObjectMeta) *int { return ret } +func AutoscaleWindow(m *metav1.ObjectMeta) string { + return m.Annotations[autoscaling.WindowAnnotationKey] +} + func Port(revisionSpec *servingv1alpha1.RevisionSpec) *int32 { c, err := ContainerOfRevisionSpec(revisionSpec) if err != nil { diff --git a/test/e2e/service_options_test.go b/test/e2e/service_options_test.go index f793e9b65..4d621603a 100644 --- a/test/e2e/service_options_test.go +++ b/test/e2e/service_options_test.go @@ -79,6 +79,14 @@ func TestServiceOptions(t *testing.T) { test.validateServiceAnnotations(t, "svc3", map[string]string{"alpha": "direwolf", "brave": ""}) test.serviceDelete(t, "svc3") }) + + t.Run("create, update and validate service with autoscale window option", func(t *testing.T) { + test.serviceCreateWithOptions(t, "svc4", []string{"--autoscale-window", "1m"}) + test.validateAutoscaleWindow(t, "svc4", "1m") + test.serviceUpdate(t, "svc4", []string{"--autoscale-window", "15s"}) + test.validateAutoscaleWindow(t, "svc4", "15s") + test.serviceDelete(t, "svc4") + }) } func (test *e2eTest) serviceCreateWithOptions(t *testing.T, serviceName string, options []string) { @@ -121,6 +129,13 @@ func (test *e2eTest) validateServiceConcurrencyTarget(t *testing.T, serviceName, } } +func (test *e2eTest) validateAutoscaleWindow(t *testing.T, serviceName, window string) { + jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/window}" + out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{}) + assert.NilError(t, err) + assert.Equal(t, out, window) +} + func (test *e2eTest) validateServiceMinScale(t *testing.T, serviceName, minScale string) { jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/minScale}" out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})