Repository: knative/client
Pull request #1288 (2021-04-13 14:39:19)

Fix domain list output
## Description

Follow-up PR to #1276. Fix `domain list` to output empty list in requested format.

knative-prow-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **dsimansk** after the PR has been reviewed.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fclient).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/knative/client/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["dsimansk"]} -->

Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 82dbd12f..aee97ec6 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -12,6 +12,16 @@
 | https://github.com/knative/client/pull/[#]
 ////
 
+## (Unreleased)
+[cols="1,10,3", options="header", width="100%"]
+|===
+| | Description | PR
+
+|🐛
+| Respect `-o` in `list` commands in case if no data present
+| https://github.com/knative/client/pull/1276[#1276]
+|===
+
 ## v0.22.0 (2021-04-06)
 [cols="1,10,3", options="header", width="100%"]
 |===
diff --git a/pkg/kn/commands/broker/list.go b/pkg/kn/commands/broker/list.go
index c1fcf548..e8867521 100644
--- a/pkg/kn/commands/broker/list.go
+++ b/pkg/kn/commands/broker/list.go
@@ -61,7 +61,7 @@ func NewBrokerListCommand(p *commands.KnParams) *cobra.Command {
 			if err != nil {
 				return err
 			}
-			if len(brokerList.Items) == 0 {
+			if !brokerListFlags.GenericPrintFlags.OutputFlagSpecified() && len(brokerList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No brokers found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/channel/list.go b/pkg/kn/commands/channel/list.go
index 0146c3bf..ab813717 100644
--- a/pkg/kn/commands/channel/list.go
+++ b/pkg/kn/commands/channel/list.go
@@ -17,10 +17,13 @@ package channel
 import (
 	"fmt"
 
-	"github.com/spf13/cobra"
+	"knative.dev/client/pkg/util"
+	messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1"
 
+	"github.com/spf13/cobra"
 	"knative.dev/client/pkg/kn/commands"
 	"knative.dev/client/pkg/kn/commands/flags"
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
 )
 
 // NewChannelListCommand is for listing channel objects
@@ -51,7 +54,14 @@ func NewChannelListCommand(p *commands.KnParams) *cobra.Command {
 				return err
 			}
 
-			if channelList == nil || len(channelList.Items) == 0 {
+			if channelList == nil {
+				channelList = &messagingv1.ChannelList{}
+				err := util.UpdateGroupVersionKindWithScheme(channelList, messagingv1.SchemeGroupVersion, scheme.Scheme)
+				if err != nil {
+					return err
+				}
+			}
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(channelList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No channels found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/channel/list_types.go b/pkg/kn/commands/channel/list_types.go
index 830cfd50..aa75f9ff 100644
--- a/pkg/kn/commands/channel/list_types.go
+++ b/pkg/kn/commands/channel/list_types.go
@@ -62,10 +62,18 @@ func NewChannelListTypesCommand(p *commands.KnParams) *cobra.Command {
 				return knerrors.GetError(err)
 			}
 
-			if channelListTypes == nil || len(channelListTypes.Items) == 0 {
+			if channelListTypes == nil {
+				channelListTypes = &unstructured.UnstructuredList{}
+			}
+			if !listTypesFlags.GenericPrintFlags.OutputFlagSpecified() && len(channelListTypes.Items) == 0 {
 				return fmt.Errorf("no channels found on the backend, please verify the installation")
 			}
 
+			if channelListTypes.GroupVersionKind().Empty() {
+				channelListTypes.SetAPIVersion("apiextensions.k8s.io/v1")
+				channelListTypes.SetKind("CustomResourceDefinitionList")
+			}
+
 			printer, err := listTypesFlags.ToPrinter()
 			if err != nil {
 				return nil
diff --git a/pkg/kn/commands/domain/list.go b/pkg/kn/commands/domain/list.go
index 941e2050..ab7dcaed 100644
--- a/pkg/kn/commands/domain/list.go
+++ b/pkg/kn/commands/domain/list.go
@@ -55,7 +55,7 @@ func NewDomainMappingListCommand(p *commands.KnParams) *cobra.Command {
 				return err
 			}
 
-			if len(domainMappingList.Items) == 0 {
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(domainMappingList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No domain mapping found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/revision/describe.go b/pkg/kn/commands/revision/describe.go
index e243d5de..9530b1d2 100644
--- a/pkg/kn/commands/revision/describe.go
+++ b/pkg/kn/commands/revision/describe.go
@@ -96,6 +96,7 @@ func describe(w io.Writer, revision *servingv1.Revision, service *servingv1.Serv
 	dw := printers.NewPrefixWriter(w)
 	commands.WriteMetadata(dw, &revision.ObjectMeta, printDetails)
 	WriteImage(dw, revision)
+	WriteReplicas(dw, revision)
 	WritePort(dw, revision)
 	WriteEnv(dw, revision, printDetails)
 	WriteEnvFrom(dw, revision, printDetails)
@@ -202,6 +203,14 @@ func WriteEnvFrom(dw printers.PrefixWriter, revision *servingv1.Revision, printD
 	}
 }
 
+func WriteReplicas(dw printers.PrefixWriter, revision *servingv1.Revision) {
+	actualReplicas := revision.Status.ActualReplicas
+	desiredReplicas := revision.Status.DesiredReplicas
+	if actualReplicas != 0 || desiredReplicas != 0 {
+		dw.WriteAttribute("Replicas", fmt.Sprintf("%d/%d", actualReplicas, desiredReplicas))
+	}
+}
+
 func WriteScale(dw printers.PrefixWriter, revision *servingv1.Revision) {
 	// Scale spec if given
 	scale, err := clientserving.ScalingInfo(&revision.ObjectMeta)
diff --git a/pkg/kn/commands/revision/list.go b/pkg/kn/commands/revision/list.go
index 9bdbe417..2f818b73 100644
--- a/pkg/kn/commands/revision/list.go
+++ b/pkg/kn/commands/revision/list.go
@@ -83,7 +83,7 @@ func NewRevisionListCommand(p *commands.KnParams) *cobra.Command {
 			}
 
 			// Stop if nothing found
-			if len(revisionList.Items) == 0 {
+			if !revisionListFlags.GenericPrintFlags.OutputFlagSpecified() && len(revisionList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No revisions found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/route/list.go b/pkg/kn/commands/route/list.go
index 12710420..2e408963 100644
--- a/pkg/kn/commands/route/list.go
+++ b/pkg/kn/commands/route/list.go
@@ -66,7 +66,7 @@ func NewRouteListCommand(p *commands.KnParams) *cobra.Command {
 			if err != nil {
 				return err
 			}
-			if len(routeList.Items) == 0 {
+			if !routeListFlags.GenericPrintFlags.OutputFlagSpecified() && len(routeList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No routes found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/service/describe.go b/pkg/kn/commands/service/describe.go
index d9793bce..58df0e8b 100644
--- a/pkg/kn/commands/service/describe.go
+++ b/pkg/kn/commands/service/describe.go
@@ -221,6 +221,7 @@ func writeRevisions(dw printers.PrefixWriter, revisions []*revisionDesc, printDe
 			section.WriteAttribute("Error", ready.Reason)
 		}
 		revision.WriteImage(section, revisionDesc.revision)
+		revision.WriteReplicas(section, revisionDesc.revision)
 		if printDetails {
 			revision.WritePort(section, revisionDesc.revision)
 			revision.WriteEnv(section, revisionDesc.revision, printDetails)
diff --git a/pkg/kn/commands/service/list.go b/pkg/kn/commands/service/list.go
index 32a28caf..30e3a58b 100644
--- a/pkg/kn/commands/service/list.go
+++ b/pkg/kn/commands/service/list.go
@@ -64,7 +64,9 @@ func NewServiceListCommand(p *commands.KnParams) *cobra.Command {
 			if err != nil {
 				return err
 			}
-			if len(serviceList.Items) == 0 {
+
+			// Stop if nothing found
+			if !serviceListFlags.GenericPrintFlags.OutputFlagSpecified() && len(serviceList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No services found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/source/apiserver/list.go b/pkg/kn/commands/source/apiserver/list.go
index b4ff1775..edf701e7 100644
--- a/pkg/kn/commands/source/apiserver/list.go
+++ b/pkg/kn/commands/source/apiserver/list.go
@@ -50,7 +50,7 @@ func NewAPIServerListCommand(p *commands.KnParams) *cobra.Command {
 				return err
 			}
 
-			if len(sourceList.Items) == 0 {
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(sourceList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No ApiServer source found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/source/binding/list.go b/pkg/kn/commands/source/binding/list.go
index 63c97136..cf8bb023 100644
--- a/pkg/kn/commands/source/binding/list.go
+++ b/pkg/kn/commands/source/binding/list.go
@@ -49,7 +49,7 @@ func NewBindingListCommand(p *commands.KnParams) *cobra.Command {
 				return err
 			}
 
-			if len(sourceList.Items) == 0 {
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(sourceList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No sink binding found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/source/container/list.go b/pkg/kn/commands/source/container/list.go
index f61be2e6..a28a1d52 100644
--- a/pkg/kn/commands/source/container/list.go
+++ b/pkg/kn/commands/source/container/list.go
@@ -50,7 +50,7 @@ func NewContainerListCommand(p *commands.KnParams) *cobra.Command {
 				return err
 			}
 
-			if len(sourceList.Items) == 0 {
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(sourceList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No Container source found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/source/list.go b/pkg/kn/commands/source/list.go
index 53e53ed7..1b128057 100644
--- a/pkg/kn/commands/source/list.go
+++ b/pkg/kn/commands/source/list.go
@@ -18,7 +18,8 @@ import (
 	"fmt"
 
 	"github.com/spf13/cobra"
-
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	"knative.dev/client/pkg/dynamic"
 	knerrors "knative.dev/client/pkg/errors"
 	"knative.dev/client/pkg/kn/commands"
@@ -27,6 +28,12 @@ import (
 	sourcesv1alpha2 "knative.dev/client/pkg/sources/v1alpha2"
 )
 
+const (
+	sourceListGroup   = "client.knative.dev"
+	sourceListVersion = "v1alpha1"
+	sourceListKind    = "SourceList"
+)
+
 var listExample = `
   # List available eventing sources
   kn source list
@@ -73,10 +80,17 @@ func NewListCommand(p *commands.KnParams) *cobra.Command {
 				return knerrors.GetError(err)
 			}
 
-			if sourceList == nil || len(sourceList.Items) == 0 {
+			if sourceList == nil {
+				sourceList = &unstructured.UnstructuredList{}
+			}
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(sourceList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No sources found.\n")
 				return nil
 			}
+
+			if sourceList.GroupVersionKind().Empty() {
+				sourceList.SetGroupVersionKind(schema.GroupVersionKind{Group: sourceListGroup, Version: sourceListVersion, Kind: sourceListKind})
+			}
 			// empty namespace indicates all namespaces flag is specified
 			if namespace == "" {
 				listFlags.EnsureWithNamespace()
diff --git a/pkg/kn/commands/source/list_types.go b/pkg/kn/commands/source/list_types.go
index cca0cf42..34d1089d 100644
--- a/pkg/kn/commands/source/list_types.go
+++ b/pkg/kn/commands/source/list_types.go
@@ -62,10 +62,19 @@ func NewListTypesCommand(p *commands.KnParams) *cobra.Command {
 				return knerrors.GetError(err)
 			}
 
-			if sourceListTypes == nil || len(sourceListTypes.Items) == 0 {
+			if sourceListTypes == nil {
+				sourceListTypes = &unstructured.UnstructuredList{}
+			}
+
+			if !listTypesFlags.GenericPrintFlags.OutputFlagSpecified() && len(sourceListTypes.Items) == 0 {
 				return fmt.Errorf("no sources found on the backend, please verify the installation")
 			}
 
+			if sourceListTypes.GroupVersionKind().Empty() {
+				sourceListTypes.SetAPIVersion("apiextensions.k8s.io/v1")
+				sourceListTypes.SetKind("CustomResourceDefinitionList")
+			}
+
 			printer, err := listTypesFlags.ToPrinter()
 			if err != nil {
 				return nil
diff --git a/pkg/kn/commands/source/ping/list.go b/pkg/kn/commands/source/ping/list.go
index e8edeaa0..9e2118af 100644
--- a/pkg/kn/commands/source/ping/list.go
+++ b/pkg/kn/commands/source/ping/list.go
@@ -50,7 +50,7 @@ func NewPingListCommand(p *commands.KnParams) *cobra.Command {
 				return err
 			}
 
-			if len(sourceList.Items) == 0 {
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(sourceList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No Ping source found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/subscription/list.go b/pkg/kn/commands/subscription/list.go
index 8a8101bc..d77da374 100644
--- a/pkg/kn/commands/subscription/list.go
+++ b/pkg/kn/commands/subscription/list.go
@@ -19,6 +19,10 @@ package subscription
 import (
 	"fmt"
 
+	"knative.dev/client/pkg/util"
+	messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1"
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"github.com/spf13/cobra"
 
 	"knative.dev/client/pkg/kn/commands"
@@ -52,7 +56,14 @@ func NewSubscriptionListCommand(p *commands.KnParams) *cobra.Command {
 				return err
 			}
 
-			if subscriptionList == nil || len(subscriptionList.Items) == 0 {
+			if subscriptionList == nil {
+				subscriptionList = &messagingv1.SubscriptionList{}
+				err := util.UpdateGroupVersionKindWithScheme(subscriptionList, messagingv1.SchemeGroupVersion, scheme.Scheme)
+				if err != nil {
+					return err
+				}
+			}
+			if !listFlags.GenericPrintFlags.OutputFlagSpecified() && len(subscriptionList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No subscriptions found.\n")
 				return nil
 			}
diff --git a/pkg/kn/commands/trigger/list.go b/pkg/kn/commands/trigger/list.go
index ad751a94..7c952a2b 100644
--- a/pkg/kn/commands/trigger/list.go
+++ b/pkg/kn/commands/trigger/list.go
@@ -50,7 +50,7 @@ func NewTriggerListCommand(p *commands.KnParams) *cobra.Command {
 			if err != nil {
 				return err
 			}
-			if len(triggerList.Items) == 0 {
+			if !triggerListFlags.GenericPrintFlags.OutputFlagSpecified() && len(triggerList.Items) == 0 {
 				fmt.Fprintf(cmd.OutOrStdout(), "No triggers found.\n")
 				return nil
 			}


Test patch:
diff --git a/pkg/kn/commands/broker/list_test.go b/pkg/kn/commands/broker/list_test.go
index 73622e91..ad23aabf 100644
--- a/pkg/kn/commands/broker/list_test.go
+++ b/pkg/kn/commands/broker/list_test.go
@@ -61,6 +61,20 @@ func TestBrokerListEmpty(t *testing.T) {
 	eventingRecorder.Validate()
 }
 
+func TestBrokerListEmptyWithJSON(t *testing.T) {
+	eventingClient := clienteventingv1.NewMockKnEventingClient(t)
+	eventingRecorder := eventingClient.Recorder()
+	brokerList := &v1beta1.BrokerList{}
+	brokerList.APIVersion = "eventing.knative.dev/v1beta1"
+	brokerList.Kind = "BrokerList"
+	eventingRecorder.ListBrokers(brokerList, nil)
+	output, err := executeBrokerCommand(eventingClient, "list", "-o", "json")
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(output, "\"apiVersion\": \"eventing.knative.dev/v1beta1\"", "\"items\": [],", "\"kind\": \"BrokerList\""))
+
+	eventingRecorder.Validate()
+}
+
 func TestTriggerListAllNamespace(t *testing.T) {
 	eventingClient := clienteventingv1.NewMockKnEventingClient(t)
 	eventingRecorder := eventingClient.Recorder()
diff --git a/pkg/kn/commands/channel/list_test.go b/pkg/kn/commands/channel/list_test.go
index 5e392876..cf6c0d31 100644
--- a/pkg/kn/commands/channel/list_test.go
+++ b/pkg/kn/commands/channel/list_test.go
@@ -17,6 +17,8 @@ package channel
 import (
 	"testing"
 
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"gotest.tools/v3/assert"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 
@@ -36,6 +38,29 @@ func TestChannelListNoChannelsFound(t *testing.T) {
 	cRecorder.Validate()
 }
 
+func TestChannelListNoChannelsFoundWithOutputSet(t *testing.T) {
+	cClient := clientmessagingv1.NewMockKnChannelsClient(t)
+	cRecorder := cClient.Recorder()
+	cRecorder.ListChannel(nil, nil)
+	out, err := executeChannelCommand(cClient, "list", "-o", "json")
+	assert.NilError(t, err)
+	assert.Check(t, util.ContainsAll(out, "\"apiVersion\": \"messaging.knative.dev/v1\"", "\"kind\": \"ChannelList\"", "\"items\": []"))
+	cRecorder.Validate()
+}
+
+func TestChannelListEmptyWithOutputSet(t *testing.T) {
+	cClient := clientmessagingv1.NewMockKnChannelsClient(t)
+	cRecorder := cClient.Recorder()
+	channelList := &messagingv1.ChannelList{}
+	err := util.UpdateGroupVersionKindWithScheme(channelList, messagingv1.SchemeGroupVersion, scheme.Scheme)
+	assert.NilError(t, err)
+	cRecorder.ListChannel(channelList, nil)
+	out, err := executeChannelCommand(cClient, "list", "-o", "json")
+	assert.NilError(t, err)
+	assert.Check(t, util.ContainsAll(out, "\"apiVersion\": \"messaging.knative.dev/v1\"", "\"kind\": \"ChannelList\"", "\"items\": []"))
+	cRecorder.Validate()
+}
+
 func TestChannelList(t *testing.T) {
 	cClient := clientmessagingv1.NewMockKnChannelsClient(t)
 	cRecorder := cClient.Recorder()
diff --git a/pkg/kn/commands/channel/list_types_test.go b/pkg/kn/commands/channel/list_types_test.go
index cdb80412..8f6e60ab 100644
--- a/pkg/kn/commands/channel/list_types_test.go
+++ b/pkg/kn/commands/channel/list_types_test.go
@@ -71,6 +71,15 @@ func TestChannelListTypesNoChannelInstalled(t *testing.T) {
 	assert.Check(t, util.ContainsAll(err.Error(), "no channels found on the backend, please verify the installation"))
 }
 
+func TestChannelListTypesNoChannelWithJsonOutput(t *testing.T) {
+	dynamicClient := dynamicfakeClient.CreateFakeKnDynamicClient(testNamespace)
+	assert.Equal(t, dynamicClient.Namespace(), testNamespace)
+
+	output, err := channelFakeCmd([]string{"channel", "list-types", "-o", "json"}, dynamicClient)
+	assert.NilError(t, err)
+	assert.Check(t, util.ContainsAll(strings.Join(output[:], "\n"), "\"apiVersion\": \"apiextensions.k8s.io/v1\"", "\"items\": []", "\"kind\": \"CustomResourceDefinitionList\""))
+}
+
 func TestChannelListTypesErrorDynamicClient(t *testing.T) {
 	dynamicClient := dynamicfakeClient.CreateFakeKnDynamicClient("")
 	assert.Check(t, dynamicClient.Namespace() != testNamespace)
diff --git a/pkg/kn/commands/domain/list_test.go b/pkg/kn/commands/domain/list_test.go
index 574b693b..dbff6834 100644
--- a/pkg/kn/commands/domain/list_test.go
+++ b/pkg/kn/commands/domain/list_test.go
@@ -23,6 +23,7 @@ import (
 	"knative.dev/client/pkg/serving/v1alpha1"
 	"knative.dev/client/pkg/util"
 	servingv1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
+	"knative.dev/serving/pkg/client/clientset/versioned/scheme"
 )
 
 func TestDomainMappingList(t *testing.T) {
@@ -56,3 +57,18 @@ func TestDomainMappingListEmpty(t *testing.T) {
 
 	servingRecorder.Validate()
 }
+
+func TestChannelListEmptyWithOutputSet(t *testing.T) {
+	client := v1alpha1.NewMockKnServiceClient(t)
+
+	servingRecorder := client.Recorder()
+	domainMappingList := &servingv1alpha1.DomainMappingList{}
+	err := util.UpdateGroupVersionKindWithScheme(domainMappingList, servingv1alpha1.SchemeGroupVersion, scheme.Scheme)
+	assert.NilError(t, err)
+	servingRecorder.ListDomainMappings(domainMappingList, nil)
+
+	out, err := executeDomainCommand(client, nil, "list", "-o", "json")
+	assert.NilError(t, err)
+	assert.Check(t, util.ContainsAll(out, "\"apiVersion\": \""+servingv1alpha1.SchemeGroupVersion.String()+"\"", "\"kind\": \"DomainMappingList\"", "\"items\": []"))
+	servingRecorder.Validate()
+}
diff --git a/pkg/kn/commands/revision/describe_test.go b/pkg/kn/commands/revision/describe_test.go
index a908ae04..a903caf5 100644
--- a/pkg/kn/commands/revision/describe_test.go
+++ b/pkg/kn/commands/revision/describe_test.go
@@ -120,6 +120,7 @@ func TestDescribeRevisionBasic(t *testing.T) {
 	}
 
 	assert.Assert(t, util.ContainsAll(data, "Image:", "gcr.io/test/image", "++ Ready", "Port:", "8080"))
+	assert.Assert(t, util.ContainsAll(data, "Replicas:", "0/1"))
 	assert.Assert(t, util.ContainsAll(data, "EnvFrom:", "cm:test1, cm:test2"))
 }
 
@@ -161,6 +162,8 @@ func createTestRevision(revision string, gen int64) servingv1.Revision {
 			},
 		},
 		Status: servingv1.RevisionStatus{
+			ActualReplicas:        0,
+			DesiredReplicas:       1,
 			DeprecatedImageDigest: "gcr.io/test/image@" + imageDigest,
 			Status: duckv1.Status{
 				Conditions: goodConditions(),
diff --git a/pkg/kn/commands/revision/list_test.go b/pkg/kn/commands/revision/list_test.go
index 11d290f5..647baec9 100644
--- a/pkg/kn/commands/revision/list_test.go
+++ b/pkg/kn/commands/revision/list_test.go
@@ -60,6 +60,17 @@ func TestRevisionListEmpty(t *testing.T) {
 	}
 }
 
+func TestRevisionListEmptyWithJSON(t *testing.T) {
+	action, output, err := fakeRevisionList([]string{"revision", "list", "-o", "json"}, &servingv1.RevisionList{})
+	assert.NilError(t, err)
+	if action == nil {
+		t.Errorf("No action")
+	} else if !action.Matches("list", "revisions") {
+		t.Errorf("Bad action %v", action)
+	}
+	assert.Assert(t, util.ContainsAll(strings.Join(output[:], "\n"), "\"apiVersion\": \"serving.knative.dev/v1\"", "\"items\": [],", "\"kind\": \"RevisionList\""))
+}
+
 func TestRevisionListEmptyByName(t *testing.T) {
 	action, _, err := fakeRevisionList([]string{"revision", "list", "name"}, &servingv1.RevisionList{})
 	assert.NilError(t, err)
diff --git a/pkg/kn/commands/route/list_test.go b/pkg/kn/commands/route/list_test.go
index a72f0195..db8b10e8 100644
--- a/pkg/kn/commands/route/list_test.go
+++ b/pkg/kn/commands/route/list_test.go
@@ -57,6 +57,19 @@ func TestListEmpty(t *testing.T) {
 	}
 }
 
+func TestListEmptyWithJsonOutput(t *testing.T) {
+	action, output, err := fakeRouteList([]string{"route", "list", "-o", "json"}, &servingv1.RouteList{})
+	assert.NilError(t, err)
+	if action == nil {
+		t.Errorf("No action")
+	} else if !action.Matches("list", "routes") {
+		t.Errorf("Bad action %v", action)
+	}
+
+	outputJson := strings.Join(output[:], "\n")
+	assert.Assert(t, util.ContainsAll(outputJson, "\"apiVersion\": \"serving.knative.dev/v1\"", "\"items\": [],", "\"kind\": \"RouteList\""))
+}
+
 func TestRouteListDefaultOutput(t *testing.T) {
 	route1 := createMockRouteSingleTarget("foo", "foo-01234", 100)
 	route2 := createMockRouteSingleTarget("bar", "bar-98765", 100)
diff --git a/pkg/kn/commands/service/describe_test.go b/pkg/kn/commands/service/describe_test.go
index 376e17d1..5ea4a22d 100644
--- a/pkg/kn/commands/service/describe_test.go
+++ b/pkg/kn/commands/service/describe_test.go
@@ -68,6 +68,7 @@ func TestServiceDescribeBasic(t *testing.T) {
 	assert.Assert(t, util.ContainsAll(output, "Labels:", "label1=lval1, label2=lval2\n"))
 	assert.Assert(t, util.ContainsAll(output, "[1]"))
 	assert.Assert(t, cmp.Regexp("Service Account: \\s+default-sa", output))
+	assert.Assert(t, util.ContainsAll(output, "Replicas:", "0/1"))
 
 	assert.Equal(t, strings.Count(output, "rev1"), 1)
 
@@ -762,6 +763,8 @@ func createTestRevision(revision string, gen int64, conditions duckv1.Conditions
 			},
 		},
 		Status: servingv1.RevisionStatus{
+			ActualReplicas:        0,
+			DesiredReplicas:       1,
 			DeprecatedImageDigest: "gcr.io/test/image@" + imageDigest,
 			Status: duckv1.Status{
 				Conditions: conditions,
diff --git a/pkg/kn/commands/service/list_test.go b/pkg/kn/commands/service/list_test.go
index 18038ef6..ce2ecc0b 100644
--- a/pkg/kn/commands/service/list_test.go
+++ b/pkg/kn/commands/service/list_test.go
@@ -58,6 +58,18 @@ func TestListEmpty(t *testing.T) {
 	}
 }
 
+func TestListEmptyWithJSON(t *testing.T) {
+	action, output, err := fakeServiceList([]string{"service", "list", "-o", "json"}, &servingv1.ServiceList{})
+	assert.NilError(t, err)
+	if action == nil {
+		t.Errorf("No action")
+	} else if !action.Matches("list", "services") {
+		t.Errorf("Bad action %v", action)
+	}
+
+	assert.Assert(t, util.ContainsAll(strings.Join(output[:], "\n"), "\"apiVersion\": \"serving.knative.dev/v1\"", "\"items\": [],", "\"kind\": \"ServiceList\""))
+}
+
 func TestGetEmpty(t *testing.T) {
 	action, _, err := fakeServiceList([]string{"service", "list", "name"}, &servingv1.ServiceList{})
 	assert.NilError(t, err)
diff --git a/pkg/kn/commands/source/apiserver/list_test.go b/pkg/kn/commands/source/apiserver/list_test.go
index 22da4e6b..0a4164c2 100644
--- a/pkg/kn/commands/source/apiserver/list_test.go
+++ b/pkg/kn/commands/source/apiserver/list_test.go
@@ -17,6 +17,8 @@ package apiserver
 import (
 	"testing"
 
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"gotest.tools/v3/assert"
 
 	v1alpha2 "knative.dev/eventing/pkg/apis/sources/v1alpha2"
@@ -58,3 +60,18 @@ func TestListAPIServerSourceEmpty(t *testing.T) {
 
 	apiServerRecorder.Validate()
 }
+
+func TestListAPIServerSourceEmptyWithJsonOutput(t *testing.T) {
+	apiServerClient := v1alpha22.NewMockKnAPIServerSourceClient(t)
+
+	apiServerRecorder := apiServerClient.Recorder()
+	sampleSourceList := v1alpha2.ApiServerSourceList{}
+	_ = util.UpdateGroupVersionKindWithScheme(&sampleSourceList, v1alpha2.SchemeGroupVersion, scheme.Scheme)
+	apiServerRecorder.ListAPIServerSource(&sampleSourceList, nil)
+
+	out, err := executeAPIServerSourceCommand(apiServerClient, nil, "list", "-o", "json")
+	assert.NilError(t, err, "Sources should be listed")
+	assert.Assert(t, util.ContainsAll(out, "\"apiVersion\": \"sources.knative.dev/v1alpha2\"", "\"items\": []", "\"kind\": \"ApiServerSourceList\""))
+
+	apiServerRecorder.Validate()
+}
diff --git a/pkg/kn/commands/source/binding/list_test.go b/pkg/kn/commands/source/binding/list_test.go
index 2b2454b5..00b7663d 100644
--- a/pkg/kn/commands/source/binding/list_test.go
+++ b/pkg/kn/commands/source/binding/list_test.go
@@ -17,6 +17,8 @@ package binding
 import (
 	"testing"
 
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"gotest.tools/v3/assert"
 	v1alpha2 "knative.dev/eventing/pkg/apis/sources/v1alpha2"
 
@@ -58,3 +60,18 @@ func TestListBindingEmpty(t *testing.T) {
 
 	bindingRecorder.Validate()
 }
+
+func TestListBindingEmptyWithJsonOutput(t *testing.T) {
+	bindingClient := clientv1alpha2.NewMockKnSinkBindingClient(t)
+
+	bindingRecorder := bindingClient.Recorder()
+	bindingList := v1alpha2.SinkBindingList{}
+	_ = util.UpdateGroupVersionKindWithScheme(&bindingList, v1alpha2.SchemeGroupVersion, scheme.Scheme)
+	bindingRecorder.ListSinkBindings(&bindingList, nil)
+
+	out, err := executeSinkBindingCommand(bindingClient, nil, "list", "-o", "json")
+	assert.NilError(t, err, "Sources should be listed")
+	assert.Assert(t, util.ContainsAll(out, "\"apiVersion\": \"sources.knative.dev/v1alpha2\"", "\"items\": []", "\"kind\": \"SinkBindingList\""))
+
+	bindingRecorder.Validate()
+}
diff --git a/pkg/kn/commands/source/container/list_test.go b/pkg/kn/commands/source/container/list_test.go
index 651b1807..d947431a 100644
--- a/pkg/kn/commands/source/container/list_test.go
+++ b/pkg/kn/commands/source/container/list_test.go
@@ -19,6 +19,8 @@ package container
 import (
 	"testing"
 
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"gotest.tools/v3/assert"
 	v1alpha22 "knative.dev/client/pkg/sources/v1alpha2"
 	"knative.dev/client/pkg/util"
@@ -58,3 +60,18 @@ func TestListContainerSourceEmpty(t *testing.T) {
 
 	containerRecorder.Validate()
 }
+
+func TestListContainerSourceEmptyWithJsonOutput(t *testing.T) {
+	containerClient := v1alpha22.NewMockKnContainerSourceClient(t)
+
+	containerRecorder := containerClient.Recorder()
+	sampleSourceList := v1alpha2.ContainerSourceList{}
+	_ = util.UpdateGroupVersionKindWithScheme(&sampleSourceList, v1alpha2.SchemeGroupVersion, scheme.Scheme)
+	containerRecorder.ListContainerSources(&sampleSourceList, nil)
+
+	out, err := executeContainerSourceCommand(containerClient, nil, "list", "-o", "json")
+	assert.NilError(t, err, "Sources should be listed")
+	assert.Assert(t, util.ContainsAll(out, "\"apiVersion\": \"sources.knative.dev/v1alpha2\"", "\"items\": []", "\"kind\": \"ContainerSourceList\""))
+
+	containerRecorder.Validate()
+}
diff --git a/pkg/kn/commands/source/list_test.go b/pkg/kn/commands/source/list_test.go
index 1b0291f1..26d86c84 100644
--- a/pkg/kn/commands/source/list_test.go
+++ b/pkg/kn/commands/source/list_test.go
@@ -59,6 +59,18 @@ func TestSourceListTypesNoSourcesInstalled(t *testing.T) {
 	assert.Check(t, util.ContainsAll(err.Error(), "no sources", "found", "backend", "verify", "installation"))
 }
 
+func TestSourceListTypesNoSourcesWithJsonOutput(t *testing.T) {
+	output, err := sourceFakeCmd([]string{"source", "list-types", "-o", "json"},
+		&unstructured.Unstructured{
+			Object: map[string]interface{}{
+				"apiVersion": "apiextensions.k8s.io/v1",
+				"kind":       "CustomResourceDefinitionList",
+			},
+		})
+	assert.NilError(t, err)
+	assert.Check(t, util.ContainsAll(strings.Join(output[:], "\n"), "\"apiVersion\": \"apiextensions.k8s.io/v1\"", "\"items\": []", "\"kind\": \"CustomResourceDefinitionList\""))
+}
+
 func TestSourceListTypes(t *testing.T) {
 	output, err := sourceFakeCmd([]string{"source", "list-types"},
 		newSourceCRDObjWithSpec("pingsources", "sources.knative.dev", "v1alpha1", "PingSource"),
@@ -95,6 +107,15 @@ func TestSourceListNoSourcesInstalled(t *testing.T) {
 	assert.Check(t, util.ContainsAll(err.Error(), "no sources", "found", "backend", "verify", "installation"))
 }
 
+func TestSourceListEmpty(t *testing.T) {
+	output, err := sourceFakeCmd([]string{"source", "list", "-o", "json"},
+		newSourceCRDObjWithSpec("pingsources", "sources.knative.dev", "v1alpha1", "PingSource"),
+	)
+	assert.NilError(t, err)
+	outputJson := strings.Join(output[:], "\n")
+	assert.Assert(t, util.ContainsAll(outputJson, "\"apiVersion\": \"client.knative.dev/v1alpha1\"", "\"items\": [],", "\"kind\": \"SourceList\""))
+}
+
 func TestSourceList(t *testing.T) {
 	output, err := sourceFakeCmd([]string{"source", "list"},
 		newSourceCRDObjWithSpec("pingsources", "sources.knative.dev", "v1alpha1", "PingSource"),
diff --git a/pkg/kn/commands/source/ping/list_test.go b/pkg/kn/commands/source/ping/list_test.go
index 157cb04d..27bf8aeb 100644
--- a/pkg/kn/commands/source/ping/list_test.go
+++ b/pkg/kn/commands/source/ping/list_test.go
@@ -17,6 +17,8 @@ package ping
 import (
 	"testing"
 
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"gotest.tools/v3/assert"
 
 	v1alpha2 "knative.dev/eventing/pkg/apis/sources/v1alpha2"
@@ -58,3 +60,18 @@ func TestListPingJobSourceEmpty(t *testing.T) {
 
 	pingRecorder.Validate()
 }
+
+func TestListPingJobSourceEmptyWithJsonOutput(t *testing.T) {
+	pingClient := clientv1alpha2.NewMockKnPingSourceClient(t)
+
+	pingRecorder := pingClient.Recorder()
+	cJSourceList := v1alpha2.PingSourceList{}
+	_ = util.UpdateGroupVersionKindWithScheme(&cJSourceList, v1alpha2.SchemeGroupVersion, scheme.Scheme)
+	pingRecorder.ListPingSource(&cJSourceList, nil)
+
+	out, err := executePingSourceCommand(pingClient, nil, "list", "-o", "json")
+	assert.NilError(t, err, "Sources should be listed")
+	assert.Assert(t, util.ContainsAll(out, "\"apiVersion\": \"sources.knative.dev/v1alpha2\"", "\"items\": []", "\"kind\": \"PingSourceList\""))
+
+	pingRecorder.Validate()
+}
diff --git a/pkg/kn/commands/subscription/list_test.go b/pkg/kn/commands/subscription/list_test.go
index f8ebd162..fb2034a1 100644
--- a/pkg/kn/commands/subscription/list_test.go
+++ b/pkg/kn/commands/subscription/list_test.go
@@ -20,6 +20,8 @@ import (
 	"strings"
 	"testing"
 
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"gotest.tools/v3/assert"
 	messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1"
 
@@ -37,6 +39,18 @@ func TestSubscriptionListNoSubscriptionsFound(t *testing.T) {
 	cRecorder.Validate()
 }
 
+func TestSubscriptionListNoSubscriptionsWithJsonOutput(t *testing.T) {
+	cClient := v1beta1.NewMockKnSubscriptionsClient(t)
+	cRecorder := cClient.Recorder()
+	clist := &messagingv1.SubscriptionList{}
+	_ = util.UpdateGroupVersionKindWithScheme(clist, messagingv1.SchemeGroupVersion, scheme.Scheme)
+	cRecorder.ListSubscription(clist, nil)
+	out, err := executeSubscriptionCommand(cClient, nil, "list", "-o", "json")
+	assert.NilError(t, err)
+	assert.Check(t, util.ContainsAll(out, "\"apiVersion\": \"messaging.knative.dev/v1\"", "\"items\": []", "\"kind\": \"SubscriptionList\""))
+	cRecorder.Validate()
+}
+
 func TestSubscriptionList(t *testing.T) {
 	cClient := v1beta1.NewMockKnSubscriptionsClient(t)
 	cRecorder := cClient.Recorder()
diff --git a/pkg/kn/commands/trigger/list_test.go b/pkg/kn/commands/trigger/list_test.go
index 0896e188..df33b610 100644
--- a/pkg/kn/commands/trigger/list_test.go
+++ b/pkg/kn/commands/trigger/list_test.go
@@ -18,6 +18,8 @@ import (
 	"strings"
 	"testing"
 
+	"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
+
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1"
@@ -69,6 +71,19 @@ func TestTriggerListEmpty(t *testing.T) {
 	eventingRecorder.Validate()
 }
 
+func TestTriggerListEmptyWithJsonOutput(t *testing.T) {
+	eventingClient := clienteventingv1.NewMockKnEventingClient(t)
+	eventingRecorder := eventingClient.Recorder()
+	triggerList := &eventingv1.TriggerList{}
+	util.UpdateGroupVersionKindWithScheme(triggerList, eventingv1.SchemeGroupVersion, scheme.Scheme)
+	eventingRecorder.ListTriggers(triggerList, nil)
+	output, err := executeTriggerCommand(eventingClient, nil, "list", "-o", "json")
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(output, " \"apiVersion\": \"eventing.knative.dev/v1\"", "\"kind\": \"TriggerList\"", "\"items\": [],"))
+
+	eventingRecorder.Validate()
+}
+
 func TestTriggerListAllNamespace(t *testing.T) {
 	servingClient := clientservingv1.NewMockKnServiceClient(t)
 	servingRecorder := servingClient.Recorder()
diff --git a/test/e2e/source_list_test.go b/test/e2e/source_list_test.go
index 94473351..69bd8ad6 100644
--- a/test/e2e/source_list_test.go
+++ b/test/e2e/source_list_test.go
@@ -88,7 +88,7 @@ func TestSourceList(t *testing.T) {
 	output = sourceList(r, "--type", "testapisource0")
 	assert.Check(t, util.ContainsAll(output, "No", "sources", "found."))
 	output = sourceList(r, "--type", "TestSource", "-oyaml")
-	assert.Check(t, util.ContainsAll(output, "No", "sources", "found."))
+	assert.Check(t, util.ContainsAll(output, "apiVersion", "client.knative.dev/v1alpha1", "items", "[]", "kind", "SourceList"))
 
 	t.Log("List available source in YAML format")
 	output = sourceList(r, "--type", "PingSource,ApiServerSource", "-oyaml")