Skip to content

Commit

Permalink
Specify names on service update and generate names client-side. (#282)
Browse files Browse the repository at this point in the history
* Groundwork for naming revisions automatically and deliberately

* Tests for name updates

* Add godoc comments

* Changelog entry for naming flags

* Error when trying to BYO revision name to an old-format service

* fix tests again

* Template the names

* Polsh, remove unused flags, generate helptext, add better helptext

* Decapitalize error msg

* Respond to Roland comments

* Forgot to add test file

* Be a good citizen, add more tests, try to get coverage happy.

* true dat

* Re-add implicit service prefix if one was not added.
  • Loading branch information
sixolet authored and knative-prow-robot committed Aug 14, 2019
1 parent dbb63d4 commit ffe80b9
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
| `kn service list` lists services sorted by alphabetical order
| https://github.com/knative/client/pull/330[#330]

| 🎁
| Add --revision-name flag
| https://github.com/knative/client/pull/282[#282]

|===

## v0.2.0 (2019-07-10)
Expand Down
3 changes: 3 additions & 0 deletions cmd/kn/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ package main

import (
"fmt"
"math/rand"
"os"
"time"

"github.com/knative/client/pkg/kn/core"
"github.com/spf13/viper"
Expand All @@ -30,6 +32,7 @@ var err error

func main() {
defer cleanup()
rand.Seed(time.Now().UnixNano())
err = core.NewDefaultKnCommand().Execute()
if err != nil {
fmt.Fprintln(os.Stderr, err)
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ kn service create NAME --image IMAGE [flags]
-p, --port int32 The port where application listens on.
--requests-cpu string The requested CPU (e.g., 250m).
--requests-memory string 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}}")
--wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 60)
```

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 @@ -42,6 +42,7 @@ kn service update NAME [flags]
-p, --port int32 The port where application listens on.
--requests-cpu string The requested CPU (e.g., 250m).
--requests-memory string 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}}")
--wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 60)
```

Expand Down
88 changes: 80 additions & 8 deletions pkg/kn/commands/service/configuration_edit_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,51 +27,98 @@ import (
)

type ConfigurationEditFlags struct {
// Direct field manipulation
Image string
Env []string
RequestsFlags, LimitsFlags ResourceFlags
ForceCreate bool
MinScale int
MaxScale int
ConcurrencyTarget int
ConcurrencyLimit int
Port int32
Labels []string
NamePrefix string
RevisionName string

// Preferences about how to do the action.
ForceCreate bool

// Bookkeeping
flags []string
}

type ResourceFlags struct {
CPU string
Memory string
}

func (p *ConfigurationEditFlags) AddUpdateFlags(command *cobra.Command) {
// markFlagMakesRevision indicates that a flag will create a new revision if you
// set it.
func (p *ConfigurationEditFlags) markFlagMakesRevision(f string) {
p.flags = append(p.flags, f)
}

// addSharedFlags adds the flags common between create & update.
func (p *ConfigurationEditFlags) addSharedFlags(command *cobra.Command) {
command.Flags().StringVar(&p.Image, "image", "", "Image to run.")
p.markFlagMakesRevision("image")
command.Flags().StringArrayVarP(&p.Env, "env", "e", []string{},
"Environment variable to set. NAME=value; you may provide this flag "+
"any number of times to set multiple environment variables. "+
"To unset, specify the environment variable name followed by a \"-\" (e.g., NAME-).")
p.markFlagMakesRevision("env")
command.Flags().StringVar(&p.RequestsFlags.CPU, "requests-cpu", "", "The requested CPU (e.g., 250m).")
p.markFlagMakesRevision("requests-cpu")
command.Flags().StringVar(&p.RequestsFlags.Memory, "requests-memory", "", "The requested memory (e.g., 64Mi).")
p.markFlagMakesRevision("requests-memory")
command.Flags().StringVar(&p.LimitsFlags.CPU, "limits-cpu", "", "The limits on the requested CPU (e.g., 1000m).")
command.Flags().StringVar(&p.LimitsFlags.Memory, "limits-memory", "", "The limits on the requested memory (e.g., 1024Mi).")
p.markFlagMakesRevision("limits-cpu")
command.Flags().StringVar(&p.LimitsFlags.Memory, "limits-memory", "",
"The limits on the requested memory (e.g., 1024Mi).")
p.markFlagMakesRevision("limits-memory")
command.Flags().IntVar(&p.MinScale, "min-scale", 0, "Minimal number of replicas.")
p.markFlagMakesRevision("min-scale")
command.Flags().IntVar(&p.MaxScale, "max-scale", 0, "Maximal number of replicas.")
command.Flags().IntVar(&p.ConcurrencyTarget, "concurrency-target", 0, "Recommendation for when to scale up based on the concurrent number of incoming request. Defaults to --concurrency-limit when given.")
command.Flags().IntVar(&p.ConcurrencyLimit, "concurrency-limit", 0, "Hard Limit of concurrent requests to be processed by a single replica.")
p.markFlagMakesRevision("max-scale")
command.Flags().IntVar(&p.ConcurrencyTarget, "concurrency-target", 0,
"Recommendation for when to scale up based on the concurrent number of incoming request. "+
"Defaults to --concurrency-limit when given.")
p.markFlagMakesRevision("concurrency-target")
command.Flags().IntVar(&p.ConcurrencyLimit, "concurrency-limit", 0,
"Hard Limit of concurrent requests to be processed by a single replica.")
p.markFlagMakesRevision("concurrency-limit")
command.Flags().Int32VarP(&p.Port, "port", "p", 0, "The port where application listens on.")
p.markFlagMakesRevision("port")
command.Flags().StringArrayVarP(&p.Labels, "label", "l", []string{},
"Service label to set. name=value; you may provide this flag "+
"any number of times to set multiple labels. "+
"To unset, specify the label name followed by a \"-\" (e.g., name-).")
p.markFlagMakesRevision("label")
command.Flags().StringVar(&p.RevisionName, "revision-name", "{{.Service}}-{{.Random 5}}-{{.Generation}}",
"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.")
p.markFlagMakesRevision("revision-name")
}

// AddUpdateFlags adds the flags specific to update.
func (p *ConfigurationEditFlags) AddUpdateFlags(command *cobra.Command) {
p.addSharedFlags(command)
}

// AddCreateFlags adds the flags specific to create
func (p *ConfigurationEditFlags) AddCreateFlags(command *cobra.Command) {
p.AddUpdateFlags(command)
command.Flags().BoolVar(&p.ForceCreate, "force", false, "Create service forcefully, replaces existing service if any.")
p.addSharedFlags(command)
command.Flags().BoolVar(&p.ForceCreate, "force", false,
"Create service forcefully, replaces existing service if any.")
command.MarkFlagRequired("image")
}

func (p *ConfigurationEditFlags) Apply(service *servingv1alpha1.Service, cmd *cobra.Command) error {
// Apply mutates the given service according to the flags in the command.
func (p *ConfigurationEditFlags) Apply(
service *servingv1alpha1.Service,
cmd *cobra.Command) error {

template, err := servinglib.RevisionTemplateOfService(service)
if err != nil {
Expand All @@ -96,6 +143,20 @@ func (p *ConfigurationEditFlags) Apply(service *servingv1alpha1.Service, cmd *co
}
}

name, err := servinglib.GenerateRevisionName(p.RevisionName, service)
if err != nil {
return err
}

if p.AnyMutation(cmd) {
err = servinglib.UpdateName(template, name)
if err == servinglib.ApiTooOldError && !cmd.Flags().Changed("revision-name") {
// Ignore the error if we don't support revision names and nobody
// explicitly asked for one.
} else if err != nil {
return err
}
}
if cmd.Flags().Changed("image") {
err = servinglib.UpdateImage(template, p.Image)
if err != nil {
Expand Down Expand Up @@ -194,3 +255,14 @@ func (p *ConfigurationEditFlags) computeResources(resourceFlags ResourceFlags) (

return resourceList, nil
}

// AnyMutation returns true if there are any revision template mutations in the
// command.
func (p *ConfigurationEditFlags) AnyMutation(cmd *cobra.Command) bool {
for _, flag := range p.flags {
if cmd.Flags().Changed(flag) {
return true
}
}
return false
}
110 changes: 110 additions & 0 deletions pkg/kn/commands/service/service_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

"gotest.tools/assert"
"gotest.tools/assert/cmp"

"github.com/knative/client/pkg/kn/commands"
servinglib "github.com/knative/client/pkg/serving"
Expand Down Expand Up @@ -166,6 +167,98 @@ func TestServiceUpdateImage(t *testing.T) {
}
}

func TestServiceUpdateRevisionNameExplicit(t *testing.T) {
orig := newEmptyServiceBetaAPIStyle()

template, err := servinglib.RevisionTemplateOfService(orig)
if err != nil {
t.Fatal(err)
}

template.Name = "foo-asdf"

// Test user provides prefix
action, updated, _, err := fakeServiceUpdate(orig, []string{
"service", "update", "foo", "--revision-name", "foo-dogs", "--namespace", "bar", "--async"}, false)
assert.NilError(t, err)
if !action.Matches("update", "services") {
t.Fatalf("Bad action %v", action)
}
template, err = servinglib.RevisionTemplateOfService(updated)
assert.NilError(t, err)
assert.Equal(t, "foo-dogs", template.Name)

}

func TestServiceUpdateRevisionNameGenerated(t *testing.T) {
orig := newEmptyServiceBetaAPIStyle()

template, err := servinglib.RevisionTemplateOfService(orig)
if err != nil {
t.Fatal(err)
}

template.Name = "foo-asdf"

// Test prefix added by command
action, updated, _, err := fakeServiceUpdate(orig, []string{
"service", "update", "foo", "--image", "gcr.io/foo/quux:xyzzy", "--namespace", "bar", "--async"}, false)
assert.NilError(t, err)
if !action.Matches("update", "services") {
t.Fatalf("Bad action %v", action)
}

template, err = servinglib.RevisionTemplateOfService(updated)
assert.NilError(t, err)
assert.Assert(t, strings.HasPrefix(template.Name, "foo-"))
assert.Assert(t, !(template.Name == "foo-asdf"))
}

func TestServiceUpdateRevisionNameCleared(t *testing.T) {
orig := newEmptyServiceBetaAPIStyle()

template, err := servinglib.RevisionTemplateOfService(orig)
if err != nil {
t.Fatal(err)
}
template.Name = "foo-asdf"

action, updated, _, err := fakeServiceUpdate(orig, []string{
"service", "update", "foo", "--image", "gcr.io/foo/quux:xyzzy", "--namespace", "bar", "--revision-name=", "--async"}, false)

assert.NilError(t, err)
if !action.Matches("update", "services") {
t.Fatalf("Bad action %v", action)
}

template, err = servinglib.RevisionTemplateOfService(updated)
assert.NilError(t, err)
assert.Assert(t, cmp.Equal(template.Name, ""))
}

func TestServiceUpdateRevisionNameNoMutationNoChange(t *testing.T) {
orig := newEmptyServiceBetaAPIStyle()

template, err := servinglib.RevisionTemplateOfService(orig)
if err != nil {
t.Fatal(err)
}

template.Name = "foo-asdf"

// Test prefix added by command
action, updated, _, err := fakeServiceUpdate(orig, []string{
"service", "update", "foo", "--namespace", "bar", "--async"}, false)
assert.NilError(t, err)
if !action.Matches("update", "services") {
t.Fatalf("Bad action %v", action)
}

template, err = servinglib.RevisionTemplateOfService(updated)
assert.NilError(t, err)
assert.Equal(t, template.Name, "foo-asdf")
}

func TestServiceUpdateMaxMinScale(t *testing.T) {
original := newEmptyService()

Expand Down Expand Up @@ -456,6 +549,23 @@ func newEmptyService() *v1alpha1.Service {
}
}

func newEmptyServiceBetaAPIStyle() *v1alpha1.Service {
ret := &v1alpha1.Service{
TypeMeta: metav1.TypeMeta{
Kind: "Service",
APIVersion: "knative.dev/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
},
Spec: v1alpha1.ServiceSpec{},
}
ret.Spec.Template = &v1alpha1.RevisionTemplateSpec{}
ret.Spec.Template.Spec.Containers = []corev1.Container{{}}
return ret
}

func createMockServiceWithResources(t *testing.T, requestCPU, requestMemory, limitsCPU, limitsMemory string) *v1alpha1.Service {
service := &v1alpha1.Service{
TypeMeta: metav1.TypeMeta{
Expand Down
12 changes: 12 additions & 0 deletions pkg/serving/config_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package serving

import (
"context"
"errors"
"fmt"
"strconv"

Expand Down Expand Up @@ -92,6 +93,17 @@ func UpdateAnnotation(template *servingv1alpha1.RevisionTemplateSpec, annotation
return nil
}

var ApiTooOldError = errors.New("the service is using too old of an API format for the operation")

// UpdateName updates the revision name.
func UpdateName(template *servingv1alpha1.RevisionTemplateSpec, name string) error {
if template.Spec.DeprecatedContainer != nil {
return ApiTooOldError
}
template.Name = name
return nil
}

// EnvToMap is an utility function to translate between the API list form of env vars, and the
// more convenient map form.
func EnvToMap(vars []corev1.EnvVar) (map[string]string, error) {
Expand Down
7 changes: 7 additions & 0 deletions pkg/serving/config_changes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,13 @@ func TestUpdateContainerPort(t *testing.T) {
checkPortUpdate(t, template, 80)
}

func TestUpdateName(t *testing.T) {
template, _ := getV1alpha1Config()
err := UpdateName(template, "foo-asdf")
assert.NilError(t, err)
assert.Equal(t, "foo-asdf", template.Name)
}

func checkPortUpdate(t *testing.T, template *servingv1alpha1.RevisionTemplateSpec, port int32) {
if template.Spec.GetContainer().Ports[0].ContainerPort != port {
t.Error("Failed to update the container port")
Expand Down
Loading

0 comments on commit ffe80b9

Please sign in to comment.