mirror of https://github.com/knative/client.git
feat(service create/update): Add support of minScale/maxScale/concurrency-target/concurrency-limit (#157)
Autoscaler concurrency annotations are added to the revision template if --min-scale / --max-scale / --concurrency-target/--concurrency-limit are provided to `kn service create` and `kn service update` Fixes #151
This commit is contained in:
parent
8a1adf0cb0
commit
b885e41974
|
|
@ -36,12 +36,16 @@ kn service create NAME --image IMAGE [flags]
|
|||
### Options
|
||||
|
||||
```
|
||||
--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.
|
||||
--force Create service forcefully, replaces existing service if any.
|
||||
-h, --help help for create
|
||||
--image string Image to run.
|
||||
--limits-cpu string The limits on the requested CPU (e.g., 1000m).
|
||||
--limits-memory string The limits on the requested CPU (e.g., 1024Mi).
|
||||
--max-scale int Maximal number of replicas.
|
||||
--min-scale int Minimal number of replicas.
|
||||
-n, --namespace string List the requested object(s) in given namespace.
|
||||
--requests-cpu string The requested CPU (e.g., 250m).
|
||||
--requests-memory string The requested CPU (e.g., 64Mi).
|
||||
|
|
|
|||
|
|
@ -24,11 +24,15 @@ kn service update NAME [flags]
|
|||
### Options
|
||||
|
||||
```
|
||||
--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.
|
||||
-h, --help help for update
|
||||
--image string Image to run.
|
||||
--limits-cpu string The limits on the requested CPU (e.g., 1000m).
|
||||
--limits-memory string The limits on the requested CPU (e.g., 1024Mi).
|
||||
--max-scale int Maximal number of replicas.
|
||||
--min-scale int Minimal number of replicas.
|
||||
-n, --namespace string List the requested object(s) in given namespace.
|
||||
--requests-cpu string The requested CPU (e.g., 250m).
|
||||
--requests-memory string The requested CPU (e.g., 64Mi).
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ type ConfigurationEditFlags struct {
|
|||
Env []string
|
||||
RequestsFlags, LimitsFlags ResourceFlags
|
||||
ForceCreate bool
|
||||
MinScale int
|
||||
MaxScale int
|
||||
ConcurrencyTarget int
|
||||
ConcurrencyLimit int
|
||||
}
|
||||
|
||||
type ResourceFlags struct {
|
||||
|
|
@ -46,6 +50,10 @@ func (p *ConfigurationEditFlags) AddUpdateFlags(command *cobra.Command) {
|
|||
command.Flags().StringVar(&p.RequestsFlags.Memory, "requests-memory", "", "The requested CPU (e.g., 64Mi).")
|
||||
command.Flags().StringVar(&p.LimitsFlags.CPU, "limits-cpu", "", "The limits on the requested CPU (e.g., 1000m).")
|
||||
command.Flags().StringVar(&p.LimitsFlags.Memory, "limits-memory", "", "The limits on the requested CPU (e.g., 1024Mi).")
|
||||
command.Flags().IntVar(&p.MinScale, "min-scale", 0, "Minimal number of replicas.")
|
||||
command.Flags().IntVar(&p.MaxScale, "max-scale", 0, "Maximal number of replicas.")
|
||||
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.")
|
||||
command.Flags().IntVar(&p.ConcurrencyLimit, "concurrency-limit", 0, "Hard Limit of concurrent requests to be processed by a single replica.")
|
||||
}
|
||||
|
||||
func (p *ConfigurationEditFlags) AddCreateFlags(command *cobra.Command) {
|
||||
|
|
@ -93,6 +101,9 @@ func (p *ConfigurationEditFlags) Apply(service *servingv1alpha1.Service, cmd *co
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
servinglib.UpdateConcurrencyConfiguration(template, p.MinScale, p.MaxScale, p.ConcurrencyTarget, p.ConcurrencyLimit)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,42 @@ func TestServiceCreateRequestsLimitsMemory(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestServiceCreateMaxMinScale(t *testing.T) {
|
||||
action, created, _, err := fakeServiceCreate([]string{
|
||||
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",
|
||||
"--min-scale", "1", "--max-scale", "5", "--concurrency-target", "10", "--concurrency-limit", "100"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !action.Matches("create", "services") {
|
||||
t.Fatalf("Bad action %v", action)
|
||||
}
|
||||
|
||||
template, err := servinglib.GetRevisionTemplate(created)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actualAnnos := template.Annotations
|
||||
expectedAnnos := []string{
|
||||
"autoscaling.knative.dev/minScale", "1",
|
||||
"autoscaling.knative.dev/maxScale", "5",
|
||||
"autoscaling.knative.dev/target", "10",
|
||||
}
|
||||
|
||||
for i := 0; i < len(expectedAnnos); i += 2 {
|
||||
anno := expectedAnnos[i]
|
||||
if actualAnnos[anno] != expectedAnnos[i+1] {
|
||||
t.Fatalf("Unexpected annotation value for %s : %s (actual) != %s (expected)",
|
||||
anno, actualAnnos[anno], expectedAnnos[i+1])
|
||||
}
|
||||
}
|
||||
|
||||
if template.Spec.ContainerConcurrency != 100 {
|
||||
t.Fatalf("container concurrency not set to given value 1000")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceCreateRequestsLimitsCPUMemory(t *testing.T) {
|
||||
action, created, _, err := fakeServiceCreate([]string{
|
||||
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",
|
||||
|
|
|
|||
|
|
@ -64,27 +64,7 @@ func fakeServiceUpdate(original *v1alpha1.Service, args []string) (
|
|||
}
|
||||
|
||||
func TestServiceUpdateImage(t *testing.T) {
|
||||
orig := &v1alpha1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Service",
|
||||
APIVersion: "knative.dev/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1alpha1.ServiceSpec{
|
||||
DeprecatedRunLatest: &v1alpha1.RunLatestType{
|
||||
Configuration: v1alpha1.ConfigurationSpec{
|
||||
DeprecatedRevisionTemplate: &v1alpha1.RevisionTemplateSpec{
|
||||
Spec: v1alpha1.RevisionSpec{
|
||||
DeprecatedContainer: &corev1.Container{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
orig := newEmptyService()
|
||||
|
||||
template, err := servinglib.GetRevisionTemplate(orig)
|
||||
if err != nil {
|
||||
|
|
@ -110,6 +90,45 @@ func TestServiceUpdateImage(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestServiceUpdateMaxMinScale(t *testing.T) {
|
||||
original := newEmptyService()
|
||||
|
||||
action, updated, _, err := fakeServiceUpdate(original, []string{
|
||||
"service", "update", "foo",
|
||||
"--min-scale", "1", "--max-scale", "5", "--concurrency-target", "10", "--concurrency-limit", "100"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !action.Matches("update", "services") {
|
||||
t.Fatalf("Bad action %v", action)
|
||||
}
|
||||
|
||||
template, err := servinglib.GetRevisionTemplate(updated)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actualAnnos := template.Annotations
|
||||
expectedAnnos := []string{
|
||||
"autoscaling.knative.dev/minScale", "1",
|
||||
"autoscaling.knative.dev/maxScale", "5",
|
||||
"autoscaling.knative.dev/target", "10",
|
||||
}
|
||||
|
||||
for i := 0; i < len(expectedAnnos); i += 2 {
|
||||
anno := expectedAnnos[i]
|
||||
if actualAnnos[anno] != expectedAnnos[i+1] {
|
||||
t.Fatalf("Unexpected annotation value for %s : %s (actual) != %s (expected)",
|
||||
anno, actualAnnos[anno], expectedAnnos[i+1])
|
||||
}
|
||||
}
|
||||
|
||||
if template.Spec.ContainerConcurrency != 100 {
|
||||
t.Fatalf("container concurrency not set to given value 1000")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestServiceUpdateEnv(t *testing.T) {
|
||||
orig := &v1alpha1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
|
|
@ -279,6 +298,30 @@ func TestServiceUpdateRequestsLimitsCPU_and_Memory(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func newEmptyService() *v1alpha1.Service {
|
||||
return &v1alpha1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Service",
|
||||
APIVersion: "knative.dev/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1alpha1.ServiceSpec{
|
||||
DeprecatedRunLatest: &v1alpha1.RunLatestType{
|
||||
Configuration: v1alpha1.ConfigurationSpec{
|
||||
DeprecatedRevisionTemplate: &v1alpha1.RevisionTemplateSpec{
|
||||
Spec: v1alpha1.RevisionSpec{
|
||||
DeprecatedContainer: &corev1.Container{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createMockServiceWithResources(t *testing.T, requestCPU, requestMemory, limitsCPU, limitsMemory string) *v1alpha1.Service {
|
||||
service := &v1alpha1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ package serving
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1"
|
||||
servingv1beta1 "github.com/knative/serving/pkg/apis/serving/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
|
|
@ -33,6 +35,33 @@ func UpdateEnvVars(template *servingv1alpha1.RevisionTemplateSpec, vars map[stri
|
|||
return nil
|
||||
}
|
||||
|
||||
// Update min and max scale annotation if larger than 0
|
||||
func UpdateConcurrencyConfiguration(template *servingv1alpha1.RevisionTemplateSpec, minScale int, maxScale int, target int, limit int) {
|
||||
if minScale != 0 {
|
||||
UpdateAnnotation(template, "autoscaling.knative.dev/minScale", strconv.Itoa(minScale))
|
||||
}
|
||||
if maxScale != 0 {
|
||||
UpdateAnnotation(template, "autoscaling.knative.dev/maxScale", strconv.Itoa(maxScale))
|
||||
}
|
||||
if target != 0 {
|
||||
UpdateAnnotation(template, "autoscaling.knative.dev/target", strconv.Itoa(target))
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
template.Spec.ContainerConcurrency = servingv1beta1.RevisionContainerConcurrencyType(limit)
|
||||
}
|
||||
}
|
||||
|
||||
// Updater (or add) an annotation to the given service
|
||||
func UpdateAnnotation(template *servingv1alpha1.RevisionTemplateSpec, annotation string, value string) {
|
||||
annoMap := template.Annotations
|
||||
if annoMap == nil {
|
||||
annoMap = make(map[string]string)
|
||||
template.Annotations = annoMap
|
||||
}
|
||||
annoMap[annotation] = value
|
||||
}
|
||||
|
||||
// Utility function to translate between the API list form of env vars, and the
|
||||
// more convenient map form.
|
||||
func EnvToMap(vars []corev1.EnvVar) (map[string]string, error) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,24 @@ import (
|
|||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestUpdateAutoscalingAnnotations(t *testing.T) {
|
||||
template := &servingv1alpha1.RevisionTemplateSpec{}
|
||||
UpdateConcurrencyConfiguration(template, 10, 100, 1000, 1000)
|
||||
annos := template.Annotations
|
||||
if annos["autoscaling.knative.dev/minScale"] != "10" {
|
||||
t.Error("minScale failed")
|
||||
}
|
||||
if annos["autoscaling.knative.dev/maxScale"] != "100" {
|
||||
t.Error("maxScale failed")
|
||||
}
|
||||
if annos["autoscaling.knative.dev/target"] != "1000" {
|
||||
t.Error("target failed")
|
||||
}
|
||||
if template.Spec.ContainerConcurrency != 1000 {
|
||||
t.Error("limit failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateEnvVarsNew(t *testing.T) {
|
||||
template, container := getV1alpha1RevisionTemplateWithOldFields()
|
||||
testUpdateEnvVarsNew(t, template, container)
|
||||
|
|
|
|||
|
|
@ -77,13 +77,13 @@ github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1
|
|||
github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake
|
||||
github.com/knative/serving/pkg/apis/serving
|
||||
github.com/knative/serving/pkg/apis/serving/v1alpha1
|
||||
github.com/knative/serving/pkg/apis/serving/v1beta1
|
||||
github.com/knative/serving/pkg/client/clientset/versioned/scheme
|
||||
github.com/knative/serving/pkg/apis/autoscaling
|
||||
github.com/knative/serving/pkg/apis/networking
|
||||
github.com/knative/serving/pkg/apis/networking/v1alpha1
|
||||
github.com/knative/serving/pkg/apis/serving/v1beta1
|
||||
github.com/knative/serving/pkg/apis/autoscaling/v1alpha1
|
||||
github.com/knative/serving/pkg/apis/config
|
||||
github.com/knative/serving/pkg/apis/autoscaling/v1alpha1
|
||||
# github.com/knative/test-infra v0.0.0-20190531180034-a3c073a2fea1
|
||||
github.com/knative/test-infra/scripts
|
||||
# github.com/magiconair/properties v1.8.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue