-
Notifications
You must be signed in to change notification settings - Fork 382
Add JSON and YAML output to svcat #1944
Add JSON and YAML output to svcat #1944
Conversation
Example output $ ./bin/svcat/svcat get instance cosmosdb -o json
{
"metadata": {
"name": "cosmosdb",
"namespace": "default",
"selfLink": "/apis/servicecatalog.k8s.io/v1beta1/namespaces/default/serviceinstances/cosmosdb",
"uid": "dffb52b5-41a1-11e8-8f2d-0a580af4010c",
"resourceVersion": "432",
"generation": 1,
"creationTimestamp": "2018-04-16T18:13:33Z",
"finalizers": [
"kubernetes-incubator/service-catalog"
]
},
"spec": {
"clusterServiceClassExternalName": "azure-cosmosdb-mongo-account",
"clusterServicePlanExternalName": "account",
"clusterServiceClassRef": {
"name": "8797a079-5346-4e84-8018-b7d5ea5c0e3a"
},
"clusterServicePlanRef": {
"name": "86fdda05-78d7-4026-a443-1325928e7b02"
},
"parameters": {
"location": "eastus"
},
"externalID": "dffb4fd6-41a1-11e8-8f2d-0a580af4010c",
"updateRequests": 0
},
"status": {
"conditions": [
{
"type": "Ready",
"status": "True",
"lastTransitionTime": "2018-04-16T18:17:50Z",
"reason": "ProvisionedSuccessfully",
"message": "The instance was provisioned successfully"
}
],
"asyncOpInProgress": false,
"orphanMitigationInProgress": false,
"reconciledGeneration": 1,
"observedGeneration": 1,
"externalProperties": {
"clusterServicePlanExternalName": "account",
"clusterServicePlanExternalID": "86fdda05-78d7-4026-a443-1325928e7b02",
"parameters": {
"location": "eastus"
},
"parameterChecksum": "d0ec58f6385d6c93695c4a23b5f3c7c81c91f31e0f8502f3189cf4a44e7d3095"
},
"provisionStatus": "Provisioned",
"deprovisionStatus": "Required"
}
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
THIS MAKES ME SO HAPPY! 💃
Don't be overwhelmed by the # of my comments, looks great and with a few tweaks will be ready to rock.
@@ -45,7 +50,7 @@ func NewGetCmd(cxt *command.Context) *cobra.Command { | |||
} | |||
|
|||
command.AddNamespaceFlags(cmd.Flags(), true) | |||
|
|||
command.AddOutputFlags(cmd.Flags()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the --output flag is used by kubectl as well, you will need to add an explicit mapping for when svcat is used in plugin mode.
See https://github.com/kubernetes-incubator/service-catalog/blob/2e1ca7f8cf2b5d45b0542602f0d12cb18bb05f98/cmd/svcat/plugin/plugin.go#L60-L66 for an example of how we do that with the -v
and --namespace
flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that need to happen for "--output" and "-o"? Or is it smart enough to handle that?
@@ -71,7 +76,7 @@ func (c *getCmd) getAll() error { | |||
return err | |||
} | |||
|
|||
output.WriteBindingList(c.Output, bindings.Items...) | |||
output.WriteBindingList(c.Output, c.outputFormat, bindings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Yeah I get what you were saying before now about needing to switch this to use the root object and not just items.
@@ -81,7 +87,7 @@ func (c *getCmd) getAll() error { | |||
return err | |||
} | |||
|
|||
output.WriteClassList(c.Output, classes...) | |||
output.WriteClassList(c.Output, c.outputFormat, classes...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is my mistake. c.App.RetrieveClasses()
should return the entire type and not just items, like bindings does. There may be a few other types that should standardize as well on returning the entire response and not just items but that is fine (probably best) to be a follow-on PR.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you go it!
format = strings.ToLower(format) | ||
|
||
switch format { | ||
case "", "table": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wanted to agree that handling ""
and "table"
here instead of setting the default value on the flag is the correct thing to do because in plugin mode, the flag's default value isn't under our control. 👍
cmd/svcat/command/command.go
Outdated
case "yaml": | ||
return "yaml", nil | ||
default: | ||
return "", fmt.Errorf("unknown output format: %s", format) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would help to mention the flag and acceptable formats, maybe something like this?
invalid --output format %q, allowed values are table, json and yaml
// Retrieve the class as well because plans don't have the external class name | ||
class, err := c.App.RetrieveClassByID(plan.Spec.ClusterServiceClassRef.Name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
output.WritePlanList(c.Output, []v1beta1.ClusterServicePlan{*plan}, []v1beta1.ClusterServiceClass{*class}) | ||
output.WritePlan(c.Output, c.outputFormat, *plan, *class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⭐️
@@ -0,0 +1,129 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha this isn't yaml 😀
{name: "get class by name", cmd: "get class user-provided-service", golden: "output/get-class.txt"}, | ||
{name: "get class by uuid", cmd: "get class --uuid 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468", golden: "output/get-class.txt"}, | ||
{name: "describe class by name", cmd: "describe class user-provided-service", golden: "output/describe-class.txt"}, | ||
{name: "describe class uuid", cmd: "describe class --uuid 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468", golden: "output/describe-class.txt"}, | ||
|
||
{name: "list all plans", cmd: "get plans", golden: "output/get-plans.txt"}, | ||
{name: "list all plans (json)", cmd: "get plans -o json", golden: "output/get-plans.json"}, | ||
{name: "list all plans (yaml)", cmd: "get plans -o yaml", golden: "output/get-plans.yaml"}, | ||
{name: "get plan by name", cmd: "get plan default", golden: "output/get-plan.txt"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a missing testcase for outputting yaml/json for "get plan by name". No need to do it for uuid too.
{name: "describe broker", cmd: "describe broker ups-broker", golden: "output/describe-broker.txt"}, | ||
|
||
{name: "list all classes", cmd: "get classes", golden: "output/get-classes.txt"}, | ||
{name: "list all classes (json)", cmd: "get classes -o json", golden: "output/get-classes.json"}, | ||
{name: "list all classes (yaml)", cmd: "get classes -o yaml", golden: "output/get-classes.yaml"}, | ||
{name: "get class by name", cmd: "get class user-provided-service", golden: "output/get-class.txt"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a missing testcase for outputting yaml/json for "get class by name". No need to do it for uuid too.
@@ -88,13 +88,26 @@ tree: | |||
- name: all-namespaces | |||
desc: If present, list the requested object(s) across all namespaces. Namespace | |||
in current context is ignored even if specified with --namespace | |||
- name: output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh gosh, I just realized that the doc I added to the dev guide explaining how to easily update the golden files went missing! 😝 I hope that didn't waste too much of your time.
The trick is this (i'll submit a PR in a minute to fix the doc)
- touch cmd/svcat/testdata/output/NEWFILE
- go test ./cmd/svcat/... -update
That will insert the test output into the golden file without you having to do it manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM once the changes to the plugin file are reverted. Sorry for the wrong advice earlier!
cmd/svcat/output/binding.go
Outdated
case "yaml": | ||
writeBindingListYAML(w, bindingList) | ||
case "table": | ||
case formatJSON: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cmd/svcat/output/json.go
Outdated
|
||
func writeJSON(w io.Writer, obj interface{}, n int) { | ||
|
||
if n == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignoring the value they set seems a bit unexpected to me. If it's a *int
then checking null would be less surprising than converting 0 -> 3. Though then it makes it hard to pass in an int... 🤔
Just something to think about, we can tweak later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, I just went ahead and set it to do three spaces. If we run into the need for variable indent on JSON output, let's revisit?
cmd/svcat/plugin/plugin.go
Outdated
@@ -46,6 +46,9 @@ const ( | |||
// EnvPluginVerbose is the -v=LEVEL flag | |||
EnvPluginVerbose = EnvPluginGlobalFlagPrefix + "_V" | |||
|
|||
// EnvPluginOutput is the -o=FORMAT flag |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested this in plugin mode and I apologize for sending you down the wrong path. It won't be sent to us as a global flag after all so the changes to this file can be reverted. Our default plugin wiring will handle setting it without this (because it will come in as an env var like this KUBECTL_PLUGINS_LOCAL_FLAG_OUTPUT=json
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds JSON/YAML output to svcat, but does not currently include the Type Metadata, so we lose the Kind/GroupVersion info.
Note: This is not 100% complete. Right now, we do not obtain the metav1.TypeMeta within svcat. We will need to figure out how to populate this. See: kubernetes/client-go#308 (comment) Fixes: kubernetes-retired#1814
ecfbd53
to
1e726be
Compare
@carolynvs ok! take a look again, all the changes we discussed should be done now! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Can't wait to use this! 💯
Awesome work. |
At the top it says |
Reflecting and populating type metadata (i.e. kind) will be addressed in a follow-on since it's not really used, and would just be nice to have for completeness sake in comparison to the output of kubectl. |
Adding JSON and YAML output capabilities
Note: This is not 100% complete. Right now, we do not obtain the
metav1.TypeMeta within svcat. We will need to figure out how to
populate this.
See: kubernetes/client-go#308 (comment)
Fixes: #1814