Skip to content

Commit

Permalink
Add changes for --scale-init support (#990)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hemanrnjn authored Sep 15, 2020
1 parent fb47409 commit 6c7fc7c
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/cmd/kn_service_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_update.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 29 additions & 0 deletions pkg/kn/commands/service/configuration_edit_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package service

import (
"fmt"
"strconv"
"strings"

"github.com/spf13/cobra"
Expand All @@ -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"
)
Expand All @@ -49,6 +51,7 @@ type ConfigurationEditFlags struct {
RevisionName string
Annotations []string
ClusterLocal bool
ScaleInit int

// Preferences about how to do the action.
LockToDigest bool
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down
33 changes: 33 additions & 0 deletions pkg/kn/commands/service/create_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
47 changes: 47 additions & 0 deletions pkg/kn/commands/service/service_update_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
16 changes: 16 additions & 0 deletions test/e2e/service_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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}"
Expand Down

0 comments on commit 6c7fc7c

Please sign in to comment.