mirror of https://github.com/knative/client.git
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
This commit is contained in:
parent
9fd763e5dd
commit
5001dcdc16
|
|
@ -44,6 +44,7 @@ kn service create NAME --image IMAGE [flags]
|
||||||
```
|
```
|
||||||
--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-).
|
--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.
|
--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-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-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-).
|
-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-).
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ kn service update NAME [flags]
|
||||||
```
|
```
|
||||||
--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-).
|
--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.
|
--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-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-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-).
|
-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-).
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,8 @@ func describe(w io.Writer, revision *v1alpha1.Revision, service *v1alpha1.Servic
|
||||||
func WriteConcurrencyOptions(dw printers.PrefixWriter, revision *v1alpha1.Revision) {
|
func WriteConcurrencyOptions(dw printers.PrefixWriter, revision *v1alpha1.Revision) {
|
||||||
target := clientserving.ConcurrencyTarget(&revision.ObjectMeta)
|
target := clientserving.ConcurrencyTarget(&revision.ObjectMeta)
|
||||||
limit := revision.Spec.ContainerConcurrency
|
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", "")
|
section := dw.WriteAttribute("Concurrency", "")
|
||||||
if limit != nil && *limit != 0 {
|
if limit != nil && *limit != 0 {
|
||||||
section.WriteAttribute("Limit", strconv.FormatInt(int64(*limit), 10))
|
section.WriteAttribute("Limit", strconv.FormatInt(int64(*limit), 10))
|
||||||
|
|
@ -137,7 +138,11 @@ func WriteConcurrencyOptions(dw printers.PrefixWriter, revision *v1alpha1.Revisi
|
||||||
if target != nil {
|
if target != nil {
|
||||||
section.WriteAttribute("Target", strconv.Itoa(*target))
|
section.WriteAttribute("Target", strconv.Itoa(*target))
|
||||||
}
|
}
|
||||||
|
if autoscaleWindow != "" {
|
||||||
|
section.WriteAttribute("Window", autoscaleWindow)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the image attribute (with
|
// Write the image attribute (with
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ type ConfigurationEditFlags struct {
|
||||||
MaxScale int
|
MaxScale int
|
||||||
ConcurrencyTarget int
|
ConcurrencyTarget int
|
||||||
ConcurrencyLimit int
|
ConcurrencyLimit int
|
||||||
|
AutoscaleWindow string
|
||||||
Port int32
|
Port int32
|
||||||
Labels []string
|
Labels []string
|
||||||
NamePrefix string
|
NamePrefix string
|
||||||
|
|
@ -113,6 +114,8 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) {
|
||||||
p.markFlagMakesRevision("min-scale")
|
p.markFlagMakesRevision("min-scale")
|
||||||
command.Flags().IntVar(&p.MaxScale, "max-scale", 0, "Maximal number of replicas.")
|
command.Flags().IntVar(&p.MaxScale, "max-scale", 0, "Maximal number of replicas.")
|
||||||
p.markFlagMakesRevision("max-scale")
|
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,
|
command.Flags().IntVar(&p.ConcurrencyTarget, "concurrency-target", 0,
|
||||||
"Recommendation for when to scale up based on the concurrent number of incoming request. "+
|
"Recommendation for when to scale up based on the concurrent number of incoming request. "+
|
||||||
"Defaults to --concurrency-limit when given.")
|
"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") {
|
if cmd.Flags().Changed("concurrency-target") {
|
||||||
err = servinglib.UpdateConcurrencyTarget(template, p.ConcurrencyTarget)
|
err = servinglib.UpdateConcurrencyTarget(template, p.ConcurrencyTarget)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
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))
|
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
|
// UpdateConcurrencyTarget updates container concurrency annotation
|
||||||
func UpdateConcurrencyTarget(template *servingv1alpha1.RevisionTemplateSpec, target int) error {
|
func UpdateConcurrencyTarget(template *servingv1alpha1.RevisionTemplateSpec, target int) error {
|
||||||
return UpdateRevisionTemplateAnnotation(template, autoscaling.TargetAnnotationKey, strconv.Itoa(target))
|
return UpdateRevisionTemplateAnnotation(template, autoscaling.TargetAnnotationKey, strconv.Itoa(target))
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ func TestUpdateMinScale(t *testing.T) {
|
||||||
err := UpdateMinScale(template, 10)
|
err := UpdateMinScale(template, 10)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
// Verify update is successful or not
|
// Verify update is successful or not
|
||||||
checkAnnotationValue(t, template, autoscaling.MinScaleAnnotationKey, 10)
|
checkAnnotationValueInt(t, template, autoscaling.MinScaleAnnotationKey, 10)
|
||||||
// Update with invalid value
|
// Update with invalid value
|
||||||
err = UpdateMinScale(template, -1)
|
err = UpdateMinScale(template, -1)
|
||||||
assert.ErrorContains(t, err, "minScale")
|
assert.ErrorContains(t, err, "minScale")
|
||||||
|
|
@ -236,18 +236,29 @@ func TestUpdateMaxScale(t *testing.T) {
|
||||||
err := UpdateMaxScale(template, 10)
|
err := UpdateMaxScale(template, 10)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
// Verify update is successful or not
|
// Verify update is successful or not
|
||||||
checkAnnotationValue(t, template, autoscaling.MaxScaleAnnotationKey, 10)
|
checkAnnotationValueInt(t, template, autoscaling.MaxScaleAnnotationKey, 10)
|
||||||
// Update with invalid value
|
// Update with invalid value
|
||||||
err = UpdateMaxScale(template, -1)
|
err = UpdateMaxScale(template, -1)
|
||||||
assert.ErrorContains(t, err, "maxScale")
|
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) {
|
func TestUpdateConcurrencyTarget(t *testing.T) {
|
||||||
template, _ := getV1alpha1RevisionTemplateWithOldFields()
|
template, _ := getV1alpha1RevisionTemplateWithOldFields()
|
||||||
err := UpdateConcurrencyTarget(template, 10)
|
err := UpdateConcurrencyTarget(template, 10)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
// Verify update is successful or not
|
// Verify update is successful or not
|
||||||
checkAnnotationValue(t, template, autoscaling.TargetAnnotationKey, 10)
|
checkAnnotationValueInt(t, template, autoscaling.TargetAnnotationKey, 10)
|
||||||
// Update with invalid value
|
// Update with invalid value
|
||||||
err = UpdateConcurrencyTarget(template, -1)
|
err = UpdateConcurrencyTarget(template, -1)
|
||||||
assert.ErrorContains(t, err, "invalid")
|
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()
|
anno := template.GetAnnotations()
|
||||||
if v, ok := anno[key]; !ok && v != strconv.Itoa(value) {
|
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)
|
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) {
|
func checkContainerConcurrency(t *testing.T, template *servingv1alpha1.RevisionTemplateSpec, value *int64) {
|
||||||
if got, want := *template.Spec.ContainerConcurrency, *value; got != want {
|
if got, want := *template.Spec.ContainerConcurrency, *value; got != want {
|
||||||
t.Errorf("Failed to update containerConcurrency value: got=%d, want=%d", got, want)
|
t.Errorf("Failed to update containerConcurrency value: got=%d, want=%d", got, want)
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ func ConcurrencyTarget(m *metav1.ObjectMeta) *int {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AutoscaleWindow(m *metav1.ObjectMeta) string {
|
||||||
|
return m.Annotations[autoscaling.WindowAnnotationKey]
|
||||||
|
}
|
||||||
|
|
||||||
func Port(revisionSpec *servingv1alpha1.RevisionSpec) *int32 {
|
func Port(revisionSpec *servingv1alpha1.RevisionSpec) *int32 {
|
||||||
c, err := ContainerOfRevisionSpec(revisionSpec)
|
c, err := ContainerOfRevisionSpec(revisionSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,14 @@ func TestServiceOptions(t *testing.T) {
|
||||||
test.validateServiceAnnotations(t, "svc3", map[string]string{"alpha": "direwolf", "brave": ""})
|
test.validateServiceAnnotations(t, "svc3", map[string]string{"alpha": "direwolf", "brave": ""})
|
||||||
test.serviceDelete(t, "svc3")
|
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) {
|
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) {
|
func (test *e2eTest) validateServiceMinScale(t *testing.T, serviceName, minScale string) {
|
||||||
jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/minScale}"
|
jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/minScale}"
|
||||||
out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
|
out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue