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-).
|
||||
--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-).
|
||||
|
|
|
|||
|
|
@ -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-).
|
||||
--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-).
|
||||
|
|
|
|||
|
|
@ -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,9 +138,13 @@ 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
|
||||
func WriteImage(dw printers.PrefixWriter, revision *v1alpha1.Revision) {
|
||||
c, err := clientserving.ContainerOfRevisionSpec(&revision.Spec)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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{})
|
||||
|
|
|
|||
Loading…
Reference in New Issue