Add changes for --scale-init support (#990)

* Add changes for --scale-init support

* Added test cases

* Add test case in e2e tests

* minor fix

* Test case failure fix

* Incorporated review comments

* Add service update test cases

* Incorporate review comments
This commit is contained in:
Himanshu Ranjan 2020-09-15 15:21:41 +05:30 committed by GitHub
parent fb47409837
commit 6c7fc7ca70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 127 additions and 0 deletions

View File

@ -87,6 +87,7 @@ kn service create NAME --image IMAGE
--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.
--scale-init int Initial number of replicas with which a service starts. Can be 0 or a positive integer.
--scale-max int Maximum number of replicas.
--scale-min int Minimum 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.

View File

@ -70,6 +70,7 @@ kn service update NAME
--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.
--scale-init int Initial number of replicas with which a service starts. Can be 0 or a positive integer.
--scale-max int Maximum number of replicas.
--scale-min int Minimum 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.

View File

@ -16,6 +16,7 @@ package service
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
@ -27,6 +28,7 @@ import (
knflags "knative.dev/client/pkg/kn/flags"
servinglib "knative.dev/client/pkg/serving"
"knative.dev/client/pkg/util"
"knative.dev/serving/pkg/apis/autoscaling"
"knative.dev/serving/pkg/apis/serving"
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
)
@ -49,6 +51,7 @@ type ConfigurationEditFlags struct {
RevisionName string
Annotations []string
ClusterLocal bool
ScaleInit int
// Preferences about how to do the action.
LockToDigest bool
@ -149,6 +152,9 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) {
"any number of times to set multiple annotations. "+
"To unset, specify the annotation name followed by a \"-\" (e.g., name-).")
p.markFlagMakesRevision("annotation")
command.Flags().IntVar(&p.ScaleInit, "scale-init", 0, "Initial number of replicas with which a service starts. Can be 0 or a positive integer.")
p.markFlagMakesRevision("scale-init")
}
// AddUpdateFlags adds the flags specific to update.
@ -427,6 +433,29 @@ func (p *ConfigurationEditFlags) Apply(
servinglib.UpdateUser(template, p.PodSpecFlags.User)
}
if cmd.Flags().Changed("scale-init") {
containsAnnotation := func(annotationList []string, annotation string) bool {
for _, element := range annotationList {
if strings.Contains(element, annotation) {
return true
}
}
return false
}
if cmd.Flags().Changed("annotation") && containsAnnotation(p.Annotations, autoscaling.InitialScaleAnnotationKey) {
return fmt.Errorf("only one of the --scale-init or --annotation %s can be specified", autoscaling.InitialScaleAnnotationKey)
}
annotationsMap := map[string]string{
autoscaling.InitialScaleAnnotationKey: strconv.Itoa(p.ScaleInit),
}
err = servinglib.UpdateAnnotations(service, template, annotationsMap, []string{})
if err != nil {
return err
}
}
return nil
}

View File

@ -512,6 +512,39 @@ func getService(name string) *servingv1.Service {
return service
}
func TestServiceCreateWithInitScaleAsOption(t *testing.T) {
client := knclient.NewMockKnServiceClient(t)
r := client.Recorder()
// Check for existing service --> no
r.GetService("foo", nil, errors.NewNotFound(servingv1.Resource("service"), "foo"))
// Create service (don't validate given service --> "Any()" arg is allowed)
r.CreateService(mock.Any(), nil)
// Wait for service to become ready
r.WaitForService("foo", mock.Any(), wait.NoopMessageCallback(), nil, time.Second)
// Get for showing the URL
r.GetService("foo", getServiceWithUrl("foo", "http://foo.example.com"), nil)
output, err := executeServiceCommand(client, "create", "foo", "--image", "gcr.io/foo/bar:baz", "--scale-init", "0")
assert.NilError(t, err)
assert.Assert(t, util.ContainsAll(output, "created", "foo", "default"))
r.Validate()
}
func TestServiceCreateWithBothAnnotationAndInitScaleAsOption(t *testing.T) {
client := knclient.NewMockKnServiceClient(t)
r := client.Recorder()
output, err := executeServiceCommand(client, "create", "foo", "--image", "gcr.io/foo/bar:baz", "--annotation", "autoscaling.knative.dev/initialScale=0", "--scale-init", "0")
assert.Assert(t, err != nil)
assert.Assert(t, util.ContainsAll(output, "only one of the", "--scale-init", "--annotation", "autoscaling.knative.dev/initialScale", "can be specified"))
r.Validate()
}
func getServiceWithUrl(name string, urlName string) *servingv1.Service {
service := servingv1.Service{}
url, _ := apis.ParseURL(urlName)

View File

@ -1473,3 +1473,50 @@ func TestServiceUpdateUser(t *testing.T) {
r.Validate()
}
func TestServiceUpdateInitialScaleMock(t *testing.T) {
client := clientservingv1.NewMockKnServiceClient(t)
svcName := "svc1"
newService := getService(svcName)
template := &newService.Spec.Template
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
newService.ObjectMeta.Annotations = map[string]string{
"autoscaling.knative.dev/initialScale": "1",
}
template.ObjectMeta.Annotations = map[string]string{
"autoscaling.knative.dev/initialScale": "1",
clientserving.UserImageAnnotationKey: "gcr.io/foo/bar:baz",
}
updatedService := getService(svcName)
template = &updatedService.Spec.Template
template.Spec.Containers[0].Image = "gcr.io/foo/bar:baz"
updatedService.ObjectMeta.Annotations = map[string]string{
"autoscaling.knative.dev/initialScale": "2",
}
template.ObjectMeta.Annotations = map[string]string{
"autoscaling.knative.dev/initialScale": "2",
clientserving.UserImageAnnotationKey: "gcr.io/foo/bar:baz",
}
r := client.Recorder()
recordServiceUpdateWithSuccess(r, svcName, newService, updatedService)
output, err := executeServiceCommand(client,
"create", svcName, "--image", "gcr.io/foo/bar:baz",
"--scale-init", "1",
"--no-wait", "--revision-name=",
)
assert.NilError(t, err)
assert.Assert(t, util.ContainsAll(output, "created", svcName, "default"))
output, err = executeServiceCommand(client,
"update", svcName,
"--scale-init", "2",
"--no-wait", "--revision-name=",
)
assert.NilError(t, err)
assert.Assert(t, util.ContainsAll(output, "updated", svcName, "default"))
r.Validate()
}

View File

@ -147,6 +147,15 @@ func TestServiceOptions(t *testing.T) {
serviceCreateWithOptions(r, "svc9", "--image", pkgtest.ImagePath("grpc-ping"), "--port", "h2c:8080")
validatePort(r, "svc9", 8080, "h2c")
test.ServiceDelete(r, "svc9")
t.Log("create and validate service with scale init option")
serviceCreateWithOptions(r, "svc10", "--scale-init", "1")
validateServiceInitScale(r, "svc10", "1")
test.ServiceUpdate(r, "svc10", "--scale-init", "2")
validateServiceInitScale(r, "svc10", "2")
t.Log("delete service")
test.ServiceDelete(r, "svc10")
}
func serviceCreateWithOptions(r *test.KnRunResultCollector, serviceName string, options ...string) {
@ -211,6 +220,13 @@ func validateServiceMaxScale(r *test.KnRunResultCollector, serviceName, maxScale
r.AssertNoError(out)
}
func validateServiceInitScale(r *test.KnRunResultCollector, serviceName, initScale string) {
jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/initialScale}"
out := r.KnTest().Kn().Run("service", "list", serviceName, "-o", jsonpath)
assert.Equal(r.T(), out.Stdout, initScale)
r.AssertNoError(out)
}
func validateServiceAnnotations(r *test.KnRunResultCollector, serviceName string, annotations map[string]string) {
metadataAnnotationsJsonpathFormat := "jsonpath={.metadata.annotations.%s}"
templateAnnotationsJsonpathFormat := "jsonpath={.spec.template.metadata.annotations.%s}"