Added the option to use --scale for setting MinScale and MaxScale to the same value (#914)

* Added the option to use --scale for setting MinScale and MaxScale to the
same value

* Updated service create/update to resolve test issues

* Removed scale from the annotation section - there isn't a scale
annotation

* Renamed test service name so that it doesn't match a previous test

* Addressed most issues/changes

* Added tests for multiple flags being used at the same time

* Cleaned up the update tests

* Added negative value tests and cleaned up tests in create_test.go
This commit is contained in:
Mike Petersen 2020-07-11 02:33:37 -07:00 committed by GitHub
parent 41e49b98c5
commit e1c48e6f7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 194 additions and 1 deletions

View File

@ -88,6 +88,7 @@ kn service create NAME --image IMAGE
--requests-cpu string DEPRECATED: please use --request instead. The requested CPU (e.g., 250m).
--requests-memory string DEPRECATED: please use --request instead. 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}}")
--scale int Minimum and maximum number of replicas.
--service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace.
--user int The user ID to run the container (e.g., 1001).
--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-.

View File

@ -71,6 +71,7 @@ kn service update NAME
--requests-cpu string DEPRECATED: please use --request instead. The requested CPU (e.g., 250m).
--requests-memory string DEPRECATED: please use --request instead. 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}}")
--scale int Minimum and maximum number of replicas.
--service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace.
--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%.

View File

@ -45,6 +45,7 @@ type ConfigurationEditFlags struct {
RequestsFlags, LimitsFlags ResourceFlags // TODO: Flag marked deprecated in release v0.15.0, remove in release v0.18.0
Resources knflags.ResourceOptions
Scale int
MinScale int
MaxScale int
ConcurrencyTarget int
@ -187,6 +188,9 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) {
command.Flags().IntVar(&p.MaxScale, "max-scale", 0, "Maximal number of replicas.")
p.markFlagMakesRevision("max-scale")
command.Flags().IntVar(&p.Scale, "scale", 0, "Minimum and maximum number of replicas.")
p.markFlagMakesRevision("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")
@ -437,6 +441,23 @@ func (p *ConfigurationEditFlags) Apply(
}
}
if cmd.Flags().Changed("scale") {
if cmd.Flags().Changed("max-scale") {
return fmt.Errorf("only --scale or --max-scale can be specified")
} else if cmd.Flags().Changed("min-scale") {
return fmt.Errorf("only --scale or --min-scale can be specified")
} else {
err = servinglib.UpdateMaxScale(template, p.Scale)
if err != nil {
return err
}
err = servinglib.UpdateMinScale(template, p.Scale)
if err != nil {
return err
}
}
}
if cmd.Flags().Changed("autoscale-window") {
err = servinglib.UpdateAutoscaleWindow(template, p.AutoscaleWindow)
if err != nil {

View File

@ -547,6 +547,76 @@ func TestServiceCreateMaxMinScale(t *testing.T) {
}
}
func TestServiceCreateScale(t *testing.T) {
action, created, _, err := fakeServiceCreate([]string{
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",
"--scale", "5", "--no-wait"}, false)
if err != nil {
t.Fatal(err)
} else if !action.Matches("create", "services") {
t.Fatalf("Bad action %v", action)
}
template := &created.Spec.Template
actualAnnos := template.Annotations
expectedAnnos := []string{
"autoscaling.knative.dev/minScale", "5",
"autoscaling.knative.dev/maxScale", "5",
}
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])
}
}
}
func TestServiceCreateScaleWithNegativeValue(t *testing.T) {
_, _, _, err := fakeServiceCreate([]string{
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",
"--scale", "-1", "--no-wait"}, true)
if err == nil {
t.Fatal(err)
}
expectedErrMsg := "expected 0 <= -1 <= 2147483647: autoscaling.knative.dev/maxScale"
if !strings.Contains(err.Error(), expectedErrMsg) {
t.Errorf("Invalid error output, expected: %s, got : '%s'", expectedErrMsg, err)
}
}
func TestServiceCreateScaleWithMaxScaleSet(t *testing.T) {
_, _, _, err := fakeServiceCreate([]string{
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",
"--scale", "5", "--max-scale", "2", "--no-wait"}, true)
if err == nil {
t.Fatal(err)
}
expectedErrMsg := "only --scale or --max-scale can be specified"
if !strings.Contains(err.Error(), expectedErrMsg) {
t.Errorf("Invalid error output, expected: %s, got : '%s'", expectedErrMsg, err)
}
}
func TestServiceCreateScaleWithMinScaleSet(t *testing.T) {
_, _, _, err := fakeServiceCreate([]string{
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",
"--scale", "5", "--min-scale", "2", "--no-wait"}, true)
if err == nil {
t.Fatal(err)
}
expectedErrMsg := "only --scale or --min-scale can be specified"
if !strings.Contains(err.Error(), expectedErrMsg) {
t.Errorf("Invalid error output, expected: %s, got : '%s'", expectedErrMsg, err)
}
}
func TestServiceCreateRequestsLimitsCPUMemory(t *testing.T) {
action, created, _, err := fakeServiceCreate([]string{
"service", "create", "foo", "--image", "gcr.io/foo/bar:baz",

View File

@ -355,6 +355,96 @@ func TestServiceUpdateMaxMinScale(t *testing.T) {
}
func TestServiceUpdateScale(t *testing.T) {
original := newEmptyService()
action, updated, _, err := fakeServiceUpdate(original, []string{
"service", "update", "foo",
"--scale", "5", "--no-wait"})
if err != nil {
t.Fatal(err)
} else if !action.Matches("update", "services") {
t.Fatalf("Bad action %v", action)
}
template := updated.Spec.Template
if err != nil {
t.Fatal(err)
}
actualAnnos := template.Annotations
expectedAnnos := []string{
"autoscaling.knative.dev/minScale", "5",
"autoscaling.knative.dev/maxScale", "5",
}
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])
}
}
}
func TestServiceUpdateScaleWithNegativeValue(t *testing.T) {
original := newEmptyService()
_, _, _, err := fakeServiceUpdate(original, []string{
"service", "update", "foo",
"--scale", "-1", "--no-wait"})
if err == nil {
t.Fatal("Expected error, got nil")
}
expectedErrMsg := "expected 0 <= -1 <= 2147483647: autoscaling.knative.dev/maxScale"
if !strings.Contains(err.Error(), expectedErrMsg) {
t.Errorf("Invalid error output, expected: %s, got : '%s'", expectedErrMsg, err)
}
}
func TestServiceUpdateScaleWithMaxScaleSet(t *testing.T) {
original := newEmptyService()
_, _, _, err := fakeServiceUpdate(original, []string{
"service", "update", "foo",
"--scale", "5", "--max-scale", "2", "--no-wait"})
if err == nil {
t.Fatal("Expected error, got nil")
}
expectedErrMsg := "only --scale or --max-scale can be specified"
if !strings.Contains(err.Error(), expectedErrMsg) {
t.Errorf("Invalid error output, expected: %s, got : '%s'", expectedErrMsg, err)
}
}
func TestServiceUpdateScaleWithMinScaleSet(t *testing.T) {
original := newEmptyService()
_, _, _, err := fakeServiceUpdate(original, []string{
"service", "update", "foo",
"--scale", "5", "--min-scale", "2", "--no-wait"})
if err == nil {
t.Fatal("Expected error, got nil")
}
expectedErrMsg := "only --scale or --min-scale can be specified"
if !strings.Contains(err.Error(), expectedErrMsg) {
t.Errorf("Invalid error output, expected: %s, got : '%s'", expectedErrMsg, err)
}
}
func TestServiceUpdateEnv(t *testing.T) {
orig := newEmptyService()

View File

@ -67,7 +67,7 @@ func TestServiceOptions(t *testing.T) {
t.Log("delete service")
test.ServiceDelete(r, "svc1")
t.Log("create and validate service with min/max scale options ")
t.Log("create and validate service with min/max scale options")
serviceCreateWithOptions(r, "svc2", "--min-scale", "1", "--max-scale", "3")
validateServiceMinScale(r, "svc2", "1")
validateServiceMaxScale(r, "svc2", "3")
@ -76,6 +76,16 @@ func TestServiceOptions(t *testing.T) {
test.ServiceUpdate(r, "svc2", "--max-scale", "2")
validateServiceMaxScale(r, "svc2", "2")
t.Log("create and validate service with scale options")
serviceCreateWithOptions(r, "svc2a", "--scale", "5")
validateServiceMinScale(r, "svc2a", "5")
validateServiceMaxScale(r, "svc2a", "5")
t.Log("update and validate service with scale option")
test.ServiceUpdate(r, "svc2a", "--scale", "2")
validateServiceMaxScale(r, "svc2a", "2")
validateServiceMinScale(r, "svc2a", "2")
t.Log("delete service")
test.ServiceDelete(r, "svc2")