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

List revisions sorted by configuration generation #332

Merged
merged 6 commits into from
Aug 6, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions pkg/kn/commands/revision/human_readable_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func RevisionListHandlers(h hprinters.PrintHandler) {
RevisionColumnDefinitions := []metav1beta1.TableColumnDefinition{
{Name: "Name", Type: "string", Description: "Name of the revision."},
{Name: "Service", Type: "string", Description: "Name of the Knative service."},
{Name: "Order", Type: "string", Description: "Number of generation of the revision"},
{Name: "Age", Type: "string", Description: "Age of the revision."},
{Name: "Conditions", Type: "string", Description: "Conditions describing statuses of the revision."},
{Name: "Ready", Type: "string", Description: "Ready condition status of the revision."},
Expand Down Expand Up @@ -56,6 +57,7 @@ func printRevisionList(revisionList *servingv1alpha1.RevisionList, options hprin
func printRevision(revision *servingv1alpha1.Revision, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
name := revision.Name
service := revision.Labels[serving.ServiceLabelKey]
order := revision.Labels[serving.ConfigurationGenerationLabelKey]
age := commands.TranslateTimestampSince(revision.CreationTimestamp)
conditions := commands.ConditionsValue(revision.Status.Conditions)
ready := commands.ReadyCondition(revision.Status.Conditions)
Expand All @@ -66,6 +68,7 @@ func printRevision(revision *servingv1alpha1.Revision, options hprinters.PrintOp
row.Cells = append(row.Cells,
name,
service,
order,
age,
conditions,
ready,
Expand Down
12 changes: 12 additions & 0 deletions pkg/kn/commands/revision/revision_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ package revision

import (
"fmt"
"sort"

"github.com/knative/serving/pkg/apis/serving"

"github.com/knative/serving/pkg/apis/serving/v1alpha1"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -80,6 +83,15 @@ func NewRevisionListCommand(p *commands.KnParams) *cobra.Command {
return nil
}
}

// sort revisionList by configuration generation key
sort.SliceStable(revisionList.Items, func(i, j int) bool {
if revisionList.Items[i].Labels[serving.ConfigurationGenerationLabelKey] != revisionList.Items[j].Labels[serving.ConfigurationGenerationLabelKey] {
return revisionList.Items[i].Labels[serving.ConfigurationGenerationLabelKey] > revisionList.Items[j].Labels[serving.ConfigurationGenerationLabelKey]
}
return revisionList.Items[i].Name < revisionList.Items[j].Name
})

printer, err := revisionListFlags.ToPrinter()
if err != nil {
return err
Expand Down
49 changes: 29 additions & 20 deletions pkg/kn/commands/revision/revision_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
"github.com/knative/client/pkg/util"
)

var revisionListHeader = []string{"NAME", "SERVICE", "ORDER", "AGE", "CONDITIONS", "READY", "REASON"}

func fakeRevisionList(args []string, response *v1alpha1.RevisionList) (action client_testing.Action, output []string, err error) {
knParams := &commands.KnParams{}
cmd, fakeServing, buf := commands.CreateTestKnCommand(NewRevisionCommand(knParams), knParams)
Expand Down Expand Up @@ -75,9 +77,11 @@ func TestRevisionListEmptyByName(t *testing.T) {
}

func TestRevisionListDefaultOutput(t *testing.T) {
revision1 := createMockRevisionWithParams("foo-abcd", "foo")
revision2 := createMockRevisionWithParams("bar-wxyz", "bar")
RevisionList := &v1alpha1.RevisionList{Items: []v1alpha1.Revision{*revision1, *revision2}}
revision1 := createMockRevisionWithParams("foo-abcd", "foo", "1")
revision2 := createMockRevisionWithParams("bar-abcd", "bar", "1")
revision3 := createMockRevisionWithParams("foo-wxyz", "foo", "2")
revision4 := createMockRevisionWithParams("bar-wxyz", "bar", "2")
RevisionList := &v1alpha1.RevisionList{Items: []v1alpha1.Revision{*revision1, *revision2, *revision3, *revision4}}
action, output, err := fakeRevisionList([]string{"revision", "list"}, RevisionList)
if err != nil {
t.Fatal(err)
Expand All @@ -87,16 +91,18 @@ func TestRevisionListDefaultOutput(t *testing.T) {
} else if !action.Matches("list", "revisions") {
t.Errorf("Bad action %v", action)
}
assert.Check(t, util.ContainsAll(output[0], "NAME", "SERVICE", "AGE", "CONDITIONS", "READY", "REASON"))
assert.Check(t, util.ContainsAll(output[1], "foo-abcd", "foo"))
assert.Check(t, util.ContainsAll(output[2], "bar-wxyz", "bar"))
assert.Check(t, util.ContainsAll(output[0], revisionListHeader...))
assert.Check(t, util.ContainsAll(output[1], "bar-wxyz", "bar", "2"))
assert.Check(t, util.ContainsAll(output[2], "foo-wxyz", "foo", "2"))
assert.Check(t, util.ContainsAll(output[3], "bar-abcd", "bar", "1"))
assert.Check(t, util.ContainsAll(output[4], "foo-abcd", "foo", "1"))
}

func TestRevisionListForService(t *testing.T) {
revision1 := createMockRevisionWithParams("foo-abcd", "svc1")
revision2 := createMockRevisionWithParams("bar-wxyz", "svc1")
revision3 := createMockRevisionWithParams("foo-abcd", "svc2")
revision4 := createMockRevisionWithParams("bar-wxyz", "svc2")
revision1 := createMockRevisionWithParams("foo-abcd", "svc1", "1")
revision2 := createMockRevisionWithParams("bar-wxyz", "svc1", "2")
revision3 := createMockRevisionWithParams("foo-abcd", "svc2", "1")
revision4 := createMockRevisionWithParams("bar-wxyz", "svc2", "2")
RevisionList := &v1alpha1.RevisionList{Items: []v1alpha1.Revision{*revision1, *revision2, *revision3, *revision4}}
action, output, err := fakeRevisionList([]string{"revision", "list", "-s", "svc1"}, RevisionList)
if err != nil {
Expand All @@ -107,9 +113,9 @@ func TestRevisionListForService(t *testing.T) {
} else if !action.Matches("list", "revisions") {
t.Errorf("Bad action %v", action)
}
assert.Check(t, util.ContainsAll(output[0], "NAME", "SERVICE", "AGE", "CONDITIONS", "READY", "REASON"))
assert.Check(t, util.ContainsAll(output[1], "foo-abcd", "svc1"))
assert.Check(t, util.ContainsAll(output[2], "bar-wxyz", "svc1"))
assert.Check(t, util.ContainsAll(output[0], revisionListHeader...))
assert.Check(t, util.ContainsAll(output[1], "bar-wxyz", "svc1"))
assert.Check(t, util.ContainsAll(output[2], "foo-abcd", "svc1"))
action, output, err = fakeRevisionList([]string{"revision", "list", "-s", "svc2"}, RevisionList)
if err != nil {
t.Fatal(err)
Expand All @@ -119,9 +125,9 @@ func TestRevisionListForService(t *testing.T) {
} else if !action.Matches("list", "revisions") {
t.Errorf("Bad action %v", action)
}
assert.Check(t, util.ContainsAll(output[0], "NAME", "SERVICE", "AGE", "CONDITIONS", "READY", "REASON"))
assert.Check(t, util.ContainsAll(output[1], "foo-abcd", "svc2"))
assert.Check(t, util.ContainsAll(output[2], "bar-wxyz", "svc"))
assert.Check(t, util.ContainsAll(output[0], revisionListHeader...))
assert.Check(t, util.ContainsAll(output[1], "bar-wxyz", "svc2"))
assert.Check(t, util.ContainsAll(output[2], "foo-abcd", "svc2"))
//test for non existent service
action, output, err = fakeRevisionList([]string{"revision", "list", "-s", "svc3"}, RevisionList)
if err != nil {
Expand All @@ -137,7 +143,7 @@ func TestRevisionListForService(t *testing.T) {
}

func TestRevisionListOneOutput(t *testing.T) {
revision := createMockRevisionWithParams("foo-abcd", "foo")
revision := createMockRevisionWithParams("foo-abcd", "foo", "1")
RevisionList := &v1alpha1.RevisionList{Items: []v1alpha1.Revision{*revision}}
action, output, err := fakeRevisionList([]string{"revision", "list", "foo-abcd"}, RevisionList)
if err != nil {
Expand All @@ -149,7 +155,7 @@ func TestRevisionListOneOutput(t *testing.T) {
t.Errorf("Bad action %v", action)
}

assert.Assert(t, util.ContainsAll(output[0], "NAME", "SERVICE", "AGE", "CONDITIONS", "READY", "REASON"))
assert.Assert(t, util.ContainsAll(output[0], revisionListHeader...))
assert.Assert(t, util.ContainsAll(output[1], "foo", "foo-abcd"))
}

Expand All @@ -159,7 +165,7 @@ func TestRevisionListOutputWithTwoRevName(t *testing.T) {
assert.ErrorContains(t, err, "'kn revision list' accepts maximum 1 argument")
}

func createMockRevisionWithParams(name, svcName string) *v1alpha1.Revision {
func createMockRevisionWithParams(name, svcName, generation string) *v1alpha1.Revision {
revision := &v1alpha1.Revision{
TypeMeta: metav1.TypeMeta{
Kind: "Revision",
Expand All @@ -168,7 +174,10 @@ func createMockRevisionWithParams(name, svcName string) *v1alpha1.Revision {
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "default",
Labels: map[string]string{serving.ServiceLabelKey: svcName},
Labels: map[string]string{
serving.ServiceLabelKey: svcName,
serving.ConfigurationGenerationLabelKey: generation,
},
},
}
return revision
Expand Down
44 changes: 43 additions & 1 deletion test/e2e/revision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package e2e

import (
"fmt"
"strconv"
"strings"
"testing"

Expand All @@ -39,6 +40,14 @@ func TestRevision(t *testing.T) {
test.revisionDescribeWithPrintFlags(t, "hello")
})

t.Run("update hello service and increase the count of configuration generation", func(t *testing.T) {
test.serviceUpdate(t, "hello", []string{"--env", "TARGET=kn", "--port", "8888"})
})

t.Run("show a list of revisions sorted by the count of configuration generation", func(t *testing.T) {
test.revisionListWithService(t, "hello")
})

t.Run("delete latest revision from hello service and return no error", func(t *testing.T) {
test.revisionDelete(t, "hello")
})
Expand All @@ -48,6 +57,30 @@ func TestRevision(t *testing.T) {
})
}

func (test *e2eTest) revisionListWithService(t *testing.T, serviceNames ...string) {
for _, svcName := range serviceNames {
confGenStr := test.findConfigurationGeneration(t, svcName)
confGen, err := strconv.Atoi(confGenStr)
if err != nil {
t.Fatal(err)
}

out, err := test.kn.RunWithOpts([]string{"revision", "list", "-s", svcName}, runOpts{})
assert.NilError(t, err)

outputLines := strings.Split(out, "\n")
// Ignore the last line because it is an empty string caused by splitting a line break
// at the end of the output string
for _, line := range outputLines[1 : len(outputLines)-1] {
// Skip checking of a revision name line-by-line because currently we don't have a good way to capture revision names
// randomly generated by server side
// TODO(toVersus): check revision name once `kn service update --revision-name revName` is merged
assert.Check(t, util.ContainsAll(line, svcName, confGenStr))
confGenStr = strconv.Itoa(confGen - 1)
}
}
}

func (test *e2eTest) revisionDelete(t *testing.T, serviceName string) {
revName := test.findRevision(t, serviceName)

Expand All @@ -68,10 +101,19 @@ func (test *e2eTest) revisionDescribeWithPrintFlags(t *testing.T, serviceName st
}

func (test *e2eTest) findRevision(t *testing.T, serviceName string) string {
revName, err := test.kn.RunWithOpts([]string{"revision", "list", "-o=jsonpath={.items[0].metadata.name}"}, runOpts{})
revName, err := test.kn.RunWithOpts([]string{"revision", "list", "-s", serviceName, "-o=jsonpath={.items[0].metadata.name}"}, runOpts{})
assert.NilError(t, err)
if strings.Contains(revName, "No resources found.") {
t.Errorf("Could not find revision name.")
}
return revName
}

func (test *e2eTest) findConfigurationGeneration(t *testing.T, serviceName string) string {
revGen, err := test.kn.RunWithOpts([]string{"revision", "list", "-s", serviceName, "-o=jsonpath={.items[0].metadata.labels.serving\\.knative\\.dev/configurationGeneration}"}, runOpts{})
assert.NilError(t, err)
if revGen == "" {
t.Errorf("Could not find configuration generation.")
}
return revGen
}