Repository: knative/client (go)

Issue:
`--env-from` is not shown in `service describe` and `revision describe`
When environment variables are specified with `--env-from` to add from a configmap or a secret, they are correcly inserted into the Knative Service but not shown when using `kn service describe` or `kn revision describe`. They should appear on the same level as regular environment variables.

Walkthrough:

```
$ kubectl create cm testconfig --from-literal=FOO1=BAR1 --from-literal=FOO2=BAR2
configmap/testconfig created

$ kn service create random --image rhuss/random:1.0 --env-from cm:testconfig
Creating service 'random' in namespace 'default':
  0.123s The Route is still working to reflect the latest desired specification.
  0.166s Configuration "random" is waiting for a Revision to become ready.
 12.151s ...
 12.337s Ingress has not yet been reconciled.
 12.626s Ready to serve.
Service 'random' created to latest revision 'random-rdlzd-1' is available at URL:
http://random.default.example.com

$ kubectl exec -it random-rdlzd-1-deployment-75799bf844-4hknx bash
Defaulting container name to user-container.
Use 'kubectl describe pod/random-rdlzd-1-deployment-75799bf844-4hknx -n default' to see all of the containers in this pod.
bash-5.0# env | grep FOO
FOO1=BAR1
FOO2=BAR2

$ kn service describe random --verbose
Name:         random
Namespace:    default
Annotations:  serving.knative.dev/creator=minikube-user
              serving.knative.dev/lastModifier=minikube-user
Age:          5m
URL:          http://random.default.example.com
Cluster:      http://random.default.svc.cluster.local
Revisions:
  100%  @latest (random-rdlzd-1) [1] (5m)
        Image:  rhuss/random:1.0 (pinned to 946b7c)
Conditions:
  OK TYPE                   AGE REASON
  ++ Ready                   5m
  ++ ConfigurationsReady     5m
  ++ RoutesReady             5m

$ kn revision describe random-rdlzd-1 --verbose
Name:                        random-rdlzd-1
Namespace:                   default
Labels:                      serving.knative.dev/configuration=random
                             serving.knative.dev/configurationGeneration=1
                             serving.knative.dev/route=random
                             serving.knative.dev/service=random
Annotations:                 client.knative.dev/user-image=rhuss/random:1.0
                             serving.knative.dev/creator=minikube-user
                             serving.knative.dev/lastPinned=1580285962
Age:                         5m
Image:                       rhuss/random:1.0 (pinned to 946b7c)
Service:                     random
  Configuration Generation:  1
  Latest Created:            true
  Latest Ready:              true
  Traffic:                   100%
Conditions:
  OK TYPE                  AGE REASON
  ++ Ready                  5m
  ++ ContainerHealthy       5m
  ++ ResourcesAvailable     5m
   I Active                46s NoTraffic (The target is not receiving traffic.)
```

Pull request description:
Show envFrom when running describe service or revision
Fixes #626

## Proposed Changes

* Show envFrom when running describe revison. The result like:
`EnvFrom:    ConfigMap:(testConfig), Secret:(testSecret)`

Release Note:
- 🐛 Fix bug #626 by showing envFrom when running describe revision.

Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index cde488d9..d35bc71b 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -14,6 +14,16 @@
 ////
 
 
+## v0.13.0 (unreleased)
+
+[cols="1,10,3", options="header", width="100%"]
+|===
+| | Description | PR
+
+| 🐛
+| Show envFrom when running describe service or revision
+| https://github.com/knative/client/pull/630
+
 ## v0.12.0 (2020-01-29)
 
 [cols="1,10,3", options="header", width="100%"]
diff --git a/pkg/kn/commands/revision/describe.go b/pkg/kn/commands/revision/describe.go
index 686036f7..8e1c92a1 100644
--- a/pkg/kn/commands/revision/describe.go
+++ b/pkg/kn/commands/revision/describe.go
@@ -98,6 +98,7 @@ func describe(w io.Writer, revision *v1alpha1.Revision, service *v1alpha1.Servic
 	WriteImage(dw, revision)
 	WritePort(dw, revision)
 	WriteEnv(dw, revision, printDetails)
+	WriteEnvFrom(dw, revision, printDetails)
 	WriteScale(dw, revision)
 	WriteConcurrencyOptions(dw, revision)
 	WriteResources(dw, revision)
@@ -190,6 +191,13 @@ func WriteEnv(dw printers.PrefixWriter, revision *v1alpha1.Revision, printDetail
 	}
 }
 
+func WriteEnvFrom(dw printers.PrefixWriter, revision *v1alpha1.Revision, printDetails bool) {
+	envFrom := stringifyEnvFrom(revision)
+	if envFrom != nil {
+		commands.WriteSliceDesc(dw, envFrom, "EnvFrom", printDetails)
+	}
+}
+
 func WriteScale(dw printers.PrefixWriter, revision *v1alpha1.Revision) {
 	// Scale spec if given
 	scale, err := clientserving.ScalingInfo(&revision.ObjectMeta)
@@ -274,3 +282,21 @@ func stringifyEnv(revision *v1alpha1.Revision) []string {
 	}
 	return envVars
 }
+
+func stringifyEnvFrom(revision *v1alpha1.Revision) []string {
+	container, err := clientserving.ContainerOfRevisionSpec(&revision.Spec)
+	if err != nil {
+		return nil
+	}
+
+	var result []string
+	for _, envFromSource := range container.EnvFrom {
+		if envFromSource.ConfigMapRef != nil {
+			result = append(result, "cm:"+envFromSource.ConfigMapRef.Name)
+		}
+		if envFromSource.SecretRef != nil {
+			result = append(result, "secret:"+envFromSource.SecretRef.Name)
+		}
+	}
+	return result
+}
diff --git a/pkg/kn/commands/service/describe.go b/pkg/kn/commands/service/describe.go
index 7db5bdd7..3456da4f 100644
--- a/pkg/kn/commands/service/describe.go
+++ b/pkg/kn/commands/service/describe.go
@@ -201,6 +201,7 @@ func writeRevisions(dw printers.PrefixWriter, revisions []*revisionDesc, printDe
 		if printDetails {
 			revision.WritePort(section, revisionDesc.revision)
 			revision.WriteEnv(section, revisionDesc.revision, printDetails)
+			revision.WriteEnvFrom(section, revisionDesc.revision, printDetails)
 			revision.WriteScale(section, revisionDesc.revision)
 			revision.WriteConcurrencyOptions(section, revisionDesc.revision)
 			revision.WriteResources(section, revisionDesc.revision)


Test patch:
diff --git a/pkg/kn/commands/revision/describe_test.go b/pkg/kn/commands/revision/describe_test.go
index 883ba0ac..70a1dbdb 100644
--- a/pkg/kn/commands/revision/describe_test.go
+++ b/pkg/kn/commands/revision/describe_test.go
@@ -124,6 +124,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, "EnvFrom:", "cm:test1, cm:test2"))
 }
 
 func createTestRevision(revision string, gen int64) v1alpha1.Revision {
@@ -153,6 +154,10 @@ func createTestRevision(revision string, gen int64) v1alpha1.Revision {
 								{Name: "env1", Value: "eval1"},
 								{Name: "env2", Value: "eval2"},
 							},
+							EnvFrom: []v1.EnvFromSource{
+								{ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test1"}}},
+								{ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test2"}}},
+							},
 							Ports: []v1.ContainerPort{
 								{ContainerPort: 8080},
 							},
diff --git a/pkg/kn/commands/service/describe_test.go b/pkg/kn/commands/service/describe_test.go
index b54e62e5..c582996f 100644
--- a/pkg/kn/commands/service/describe_test.go
+++ b/pkg/kn/commands/service/describe_test.go
@@ -455,7 +455,8 @@ func TestServiceDescribeVerbose(t *testing.T) {
 
 	assert.Assert(t, cmp.Regexp("Cluster:\\s+http://foo.default.svc.cluster.local", output))
 	assert.Assert(t, util.ContainsAll(output, "Image", "Name", "gcr.io/test/image (at 123456)", "50%", "(0s)"))
-	assert.Assert(t, util.ContainsAll(output, "Env:", "label1=lval1\n", "label2=lval2\n"))
+	assert.Assert(t, util.ContainsAll(output, "Env:", "env1=eval1\n", "env2=eval2\n"))
+	assert.Assert(t, util.ContainsAll(output, "EnvFrom:", "cm:test1\n", "cm:test2\n"))
 	assert.Assert(t, util.ContainsAll(output, "Annotations:", "anno1=aval1\n", "anno2=aval2\n"))
 	assert.Assert(t, util.ContainsAll(output, "Labels:", "label1=lval1\n", "label2=lval2\n"))
 	assert.Assert(t, util.ContainsAll(output, "[1]", "[2]"))
@@ -636,6 +637,10 @@ func createTestRevision(revision string, gen int64) v1alpha1.Revision {
 								{Name: "env1", Value: "eval1"},
 								{Name: "env2", Value: "eval2"},
 							},
+							EnvFrom: []v1.EnvFromSource{
+								{ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test1"}}},
+								{ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test2"}}},
+							},
 							Ports: []v1.ContainerPort{
 								{ContainerPort: 8080},
 							},