mirror of https://github.com/knative/client.git
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:
parent
fb47409837
commit
6c7fc7ca70
|
|
@ -87,6 +87,7 @@ kn service create NAME --image IMAGE
|
||||||
--requests-memory string DEPRECATED: please use --request instead. The requested memory (e.g., 64Mi).
|
--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}}")
|
--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 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-max int Maximum number of replicas.
|
||||||
--scale-min int Minimum 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.
|
--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.
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ kn service update NAME
|
||||||
--requests-memory string DEPRECATED: please use --request instead. The requested memory (e.g., 64Mi).
|
--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}}")
|
--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 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-max int Maximum number of replicas.
|
||||||
--scale-min int Minimum 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.
|
--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.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -27,6 +28,7 @@ import (
|
||||||
knflags "knative.dev/client/pkg/kn/flags"
|
knflags "knative.dev/client/pkg/kn/flags"
|
||||||
servinglib "knative.dev/client/pkg/serving"
|
servinglib "knative.dev/client/pkg/serving"
|
||||||
"knative.dev/client/pkg/util"
|
"knative.dev/client/pkg/util"
|
||||||
|
"knative.dev/serving/pkg/apis/autoscaling"
|
||||||
"knative.dev/serving/pkg/apis/serving"
|
"knative.dev/serving/pkg/apis/serving"
|
||||||
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
||||||
)
|
)
|
||||||
|
|
@ -49,6 +51,7 @@ type ConfigurationEditFlags struct {
|
||||||
RevisionName string
|
RevisionName string
|
||||||
Annotations []string
|
Annotations []string
|
||||||
ClusterLocal bool
|
ClusterLocal bool
|
||||||
|
ScaleInit int
|
||||||
|
|
||||||
// Preferences about how to do the action.
|
// Preferences about how to do the action.
|
||||||
LockToDigest bool
|
LockToDigest bool
|
||||||
|
|
@ -149,6 +152,9 @@ func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) {
|
||||||
"any number of times to set multiple annotations. "+
|
"any number of times to set multiple annotations. "+
|
||||||
"To unset, specify the annotation name followed by a \"-\" (e.g., name-).")
|
"To unset, specify the annotation name followed by a \"-\" (e.g., name-).")
|
||||||
p.markFlagMakesRevision("annotation")
|
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.
|
// AddUpdateFlags adds the flags specific to update.
|
||||||
|
|
@ -427,6 +433,29 @@ func (p *ConfigurationEditFlags) Apply(
|
||||||
servinglib.UpdateUser(template, p.PodSpecFlags.User)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -512,6 +512,39 @@ func getService(name string) *servingv1.Service {
|
||||||
return 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 {
|
func getServiceWithUrl(name string, urlName string) *servingv1.Service {
|
||||||
service := servingv1.Service{}
|
service := servingv1.Service{}
|
||||||
url, _ := apis.ParseURL(urlName)
|
url, _ := apis.ParseURL(urlName)
|
||||||
|
|
|
||||||
|
|
@ -1473,3 +1473,50 @@ func TestServiceUpdateUser(t *testing.T) {
|
||||||
|
|
||||||
r.Validate()
|
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()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,15 @@ func TestServiceOptions(t *testing.T) {
|
||||||
serviceCreateWithOptions(r, "svc9", "--image", pkgtest.ImagePath("grpc-ping"), "--port", "h2c:8080")
|
serviceCreateWithOptions(r, "svc9", "--image", pkgtest.ImagePath("grpc-ping"), "--port", "h2c:8080")
|
||||||
validatePort(r, "svc9", 8080, "h2c")
|
validatePort(r, "svc9", 8080, "h2c")
|
||||||
test.ServiceDelete(r, "svc9")
|
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) {
|
func serviceCreateWithOptions(r *test.KnRunResultCollector, serviceName string, options ...string) {
|
||||||
|
|
@ -211,6 +220,13 @@ func validateServiceMaxScale(r *test.KnRunResultCollector, serviceName, maxScale
|
||||||
r.AssertNoError(out)
|
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) {
|
func validateServiceAnnotations(r *test.KnRunResultCollector, serviceName string, annotations map[string]string) {
|
||||||
metadataAnnotationsJsonpathFormat := "jsonpath={.metadata.annotations.%s}"
|
metadataAnnotationsJsonpathFormat := "jsonpath={.metadata.annotations.%s}"
|
||||||
templateAnnotationsJsonpathFormat := "jsonpath={.spec.template.metadata.annotations.%s}"
|
templateAnnotationsJsonpathFormat := "jsonpath={.spec.template.metadata.annotations.%s}"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue