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
|
### 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.
|
-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.
|
--force Create service forcefully, replaces existing service if any.
|
||||||
-h, --help help for create
|
-h, --help help for create
|
||||||
--image string Image to run.
|
--image string Image to run.
|
||||||
--limits-cpu string The limits on the requested CPU (e.g., 1000m).
|
--limits-cpu string The limits on the requested CPU (e.g., 1000m).
|
||||||
--limits-memory string The limits on the requested CPU (e.g., 1024Mi).
|
--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.
|
-n, --namespace string List the requested object(s) in given namespace.
|
||||||
--requests-cpu string The requested CPU (e.g., 250m).
|
--requests-cpu string The requested CPU (e.g., 250m).
|
||||||
--requests-memory string The requested CPU (e.g., 64Mi).
|
--requests-memory string The requested CPU (e.g., 64Mi).
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,15 @@ kn service update NAME [flags]
|
||||||
### Options
|
### 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.
|
-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
|
-h, --help help for update
|
||||||
--image string Image to run.
|
--image string Image to run.
|
||||||
--limits-cpu string The limits on the requested CPU (e.g., 1000m).
|
--limits-cpu string The limits on the requested CPU (e.g., 1000m).
|
||||||
--limits-memory string The limits on the requested CPU (e.g., 1024Mi).
|
--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.
|
-n, --namespace string List the requested object(s) in given namespace.
|
||||||
--requests-cpu string The requested CPU (e.g., 250m).
|
--requests-cpu string The requested CPU (e.g., 250m).
|
||||||
--requests-memory string The requested CPU (e.g., 64Mi).
|
--requests-memory string The requested CPU (e.g., 64Mi).
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ type ConfigurationEditFlags struct {
|
||||||
Env []string
|
Env []string
|
||||||
RequestsFlags, LimitsFlags ResourceFlags
|
RequestsFlags, LimitsFlags ResourceFlags
|
||||||
ForceCreate bool
|
ForceCreate bool
|
||||||
|
MinScale int
|
||||||
|
MaxScale int
|
||||||
|
ConcurrencyTarget int
|
||||||
|
ConcurrencyLimit int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceFlags struct {
|
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.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.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().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) {
|
func (p *ConfigurationEditFlags) AddCreateFlags(command *cobra.Command) {
|
||||||
|
|
@ -93,6 +101,9 @@ func (p *ConfigurationEditFlags) Apply(service *servingv1alpha1.Service, cmd *co
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
servinglib.UpdateConcurrencyConfiguration(template, p.MinScale, p.MaxScale, p.ConcurrencyTarget, p.ConcurrencyLimit)
|
||||||
|
|
||||||
return nil
|
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) {
|
func TestServiceCreateRequestsLimitsCPUMemory(t *testing.T) {
|
||||||
action, created, _, err := fakeServiceCreate([]string{
|
action, created, _, err := fakeServiceCreate([]string{
|
||||||
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",
|
"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) {
|
func TestServiceUpdateImage(t *testing.T) {
|
||||||
orig := &v1alpha1.Service{
|
orig := newEmptyService()
|
||||||
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{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
template, err := servinglib.GetRevisionTemplate(orig)
|
template, err := servinglib.GetRevisionTemplate(orig)
|
||||||
if err != nil {
|
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) {
|
func TestServiceUpdateEnv(t *testing.T) {
|
||||||
orig := &v1alpha1.Service{
|
orig := &v1alpha1.Service{
|
||||||
TypeMeta: metav1.TypeMeta{
|
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 {
|
func createMockServiceWithResources(t *testing.T, requestCPU, requestMemory, limitsCPU, limitsMemory string) *v1alpha1.Service {
|
||||||
service := &v1alpha1.Service{
|
service := &v1alpha1.Service{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@ package serving
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1"
|
servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1"
|
||||||
|
servingv1beta1 "github.com/knative/serving/pkg/apis/serving/v1beta1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -33,6 +35,33 @@ func UpdateEnvVars(template *servingv1alpha1.RevisionTemplateSpec, vars map[stri
|
||||||
return nil
|
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
|
// Utility function to translate between the API list form of env vars, and the
|
||||||
// more convenient map form.
|
// more convenient map form.
|
||||||
func EnvToMap(vars []corev1.EnvVar) (map[string]string, error) {
|
func EnvToMap(vars []corev1.EnvVar) (map[string]string, error) {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,24 @@ import (
|
||||||
corev1 "k8s.io/api/core/v1"
|
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) {
|
func TestUpdateEnvVarsNew(t *testing.T) {
|
||||||
template, container := getV1alpha1RevisionTemplateWithOldFields()
|
template, container := getV1alpha1RevisionTemplateWithOldFields()
|
||||||
testUpdateEnvVarsNew(t, template, container)
|
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/client/clientset/versioned/typed/serving/v1alpha1/fake
|
||||||
github.com/knative/serving/pkg/apis/serving
|
github.com/knative/serving/pkg/apis/serving
|
||||||
github.com/knative/serving/pkg/apis/serving/v1alpha1
|
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/client/clientset/versioned/scheme
|
||||||
github.com/knative/serving/pkg/apis/autoscaling
|
github.com/knative/serving/pkg/apis/autoscaling
|
||||||
github.com/knative/serving/pkg/apis/networking
|
github.com/knative/serving/pkg/apis/networking
|
||||||
github.com/knative/serving/pkg/apis/networking/v1alpha1
|
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/config
|
||||||
|
github.com/knative/serving/pkg/apis/autoscaling/v1alpha1
|
||||||
# github.com/knative/test-infra v0.0.0-20190531180034-a3c073a2fea1
|
# github.com/knative/test-infra v0.0.0-20190531180034-a3c073a2fea1
|
||||||
github.com/knative/test-infra/scripts
|
github.com/knative/test-infra/scripts
|
||||||
# github.com/magiconair/properties v1.8.0
|
# github.com/magiconair/properties v1.8.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue