Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add changes for --scale-init support #990

Merged
merged 9 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
28 changes: 28 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 Down Expand Up @@ -49,6 +50,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 +151,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 +432,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
hemanrnjn marked this conversation as resolved.
Show resolved Hide resolved
}
}
return false
}

if cmd.Flags().Changed("annotation") && containsAnnotation(p.Annotations, "autoscaling.knative.dev/initialScale") {
hemanrnjn marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("only one of the --scale-init or --annotation autoscaling.knative.dev/initialScale can be specified")
hemanrnjn marked this conversation as resolved.
Show resolved Hide resolved
hemanrnjn marked this conversation as resolved.
Show resolved Hide resolved
}
annotationsMap := map[string]string{
"autoscaling.knative.dev/initialScale": strconv.Itoa(p.ScaleInit),
hemanrnjn marked this conversation as resolved.
Show resolved Hide resolved
}

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()
}
15 changes: 15 additions & 0 deletions test/e2e/service_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ func TestServiceOptions(t *testing.T) {
t.Log("create and validate service resource options")
serviceCreateWithOptions(r, "svc8", "--limit", "memory=500Mi,cpu=1000m", "--request", "memory=250Mi,cpu=200m")
test.ValidateServiceResources(r, "svc8", "250Mi", "200m", "500Mi", "1000m")

t.Log("create and validate service with scale init option")
serviceCreateWithOptions(r, "svc9", "--scale-init", "1")
validateServiceInitScale(r, "svc9", "1")
test.ServiceUpdate(r, "svc9", "--scale-init", "2")
validateServiceInitScale(r, "svc9", "2")
t.Log("delete service")
test.ServiceDelete(r, "svc9")
}

func serviceCreateWithOptions(r *test.KnRunResultCollector, serviceName string, options ...string) {
Expand Down Expand Up @@ -189,6 +197,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