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

Get command uses print-column extn from Openapi schema #46235

Merged
merged 1 commit into from
Jun 7, 2017

Conversation

droot
Copy link
Contributor

@droot droot commented May 22, 2017

What this PR does / why we need it:

Kubectl Get command now uses metadata 'x-kubernetes-print-column' from Openapi schema to display a resource. This is to enable richer experience for non-compiled types (like service catalog API resources) in Kubectl. This functionality is currently guarded by a boolean flag "use-openapi-print-columns".

Which issue this PR fixes (optional, in fixes #<issue number>(, fixes #<issue_number>, ...) format, will close that issue when PR gets merged): fixes #
fixes kubernetes/kubectl#22

Special notes for your reviewer:

Release note:

Get command uses OpenAPI schema to enhance display for a resource if run with flag 'use-openapi-print-columns'. 
An example command:
kubectl get pods --use-openapi-print-columns 

@k8s-ci-robot
Copy link
Contributor

Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please follow instructions at https://github.com/kubernetes/kubernetes/wiki/CLA-FAQ to sign the CLA.

It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.


Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@k8s-ci-robot k8s-ci-robot added the cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. label May 22, 2017
@k8s-ci-robot
Copy link
Contributor

Hi @droot. Thanks for your PR.

I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with @k8s-bot ok to test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label May 22, 2017
@k8s-github-robot k8s-github-robot added kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API size/L Denotes a PR that changes 100-499 lines, ignoring generated files. release-note-label-needed labels May 22, 2017
@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels May 22, 2017
@@ -2568,6 +2568,7 @@ type PodStatusResult struct {

// Pod is a collection of containers that can run on a host. This resource is created
// by clients and scheduled onto hosts.
// +k8s:openapi-gen=x-kubernetes-print-columns:custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good for testing, but we don't want to merge this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, took it out.

@@ -139,6 +139,8 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
// RunGet implements the generic Get command
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd
func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *GetOptions) error {
go f.OpenAPISchema(cmdutil.GetOpenAPICacheDir(cmd))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets flag guard this, default off until Antoine's optimization is merged

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens to this if the command exits before this is finished? Should we defer waiting for this to finish or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flag guarding is a good idea. I realized we will have to flag guard in other places as well.

About command exiting before this is finished, can potentially cause issues. We will have to fool proof OpenAPI cache reading/writing logic (basically making them idempotent in case of errors). For ex. we may fail while updating the cache (can happen in this case if we don't wait or crash in middle). Will discuss with Antoine about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does the result of this function go? Why is this running asynchronously? How do we not care about the fetched resource?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to trigger eager-loading of OpenAPI schema in cache. This is running asynchronously because we don't want to block the processing of Get command because OpenAPI cache may or may for the Get command processing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It at least deserves a comment.

// default specification to print this resource type
outputFormatFromOpenAPI := f.lookupDisplayColumnsFromOpenAPI(cmd, mapping)
if outputFormatFromOpenAPI != "" {
outputOpts = f.getOutputOptsFromOpenAPISpec(outputFormatFromOpenAPI)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this line be wrapped into: lookupDisplayColumnsFromOpenAPI

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this piece of code inside RunGet in get.go because printerForMapping should not deal with the business logic of output format for a mapping. That details belongs in Get command handling.

About your comment, pl. take a look at the new code and see if that looks OK.

@@ -162,6 +142,48 @@ func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Fact
return printer.PrintObj(info.Object, out)
}

func extractOutputOptions(cmd *cobra.Command) *printers.OutputOptions {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much better. add some documentation to the function before merge plz

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -153,6 +153,7 @@ type CustomColumnsPrinter struct {
Columns []Column
Decoder runtime.Decoder
NoHeaders bool
lastType reflect.Type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this and why is it needed? (add comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment. lastType records the type of resource printed last. This is to avoid repeating headers while printing same types of resources. So header gets printed only when type changes.

@pwittrock
Copy link
Member

generally looks good. will want to add some tests.

@pwittrock
Copy link
Member

/assign mengqiy

@k8s-github-robot k8s-github-robot removed the kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API label May 24, 2017
Copy link
Contributor Author

@droot droot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @pwittrock . Addressed the comments. PTAL.

@@ -153,6 +153,7 @@ type CustomColumnsPrinter struct {
Columns []Column
Decoder runtime.Decoder
NoHeaders bool
lastType reflect.Type
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment. lastType records the type of resource printed last. This is to avoid repeating headers while printing same types of resources. So header gets printed only when type changes.

@@ -162,6 +142,48 @@ func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Fact
return printer.PrintObj(info.Object, out)
}

func extractOutputOptions(cmd *cobra.Command) *printers.OutputOptions {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// default specification to print this resource type
outputFormatFromOpenAPI := f.lookupDisplayColumnsFromOpenAPI(cmd, mapping)
if outputFormatFromOpenAPI != "" {
outputOpts = f.getOutputOptsFromOpenAPISpec(outputFormatFromOpenAPI)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this piece of code inside RunGet in get.go because printerForMapping should not deal with the business logic of output format for a mapping. That details belongs in Get command handling.

About your comment, pl. take a look at the new code and see if that looks OK.

@@ -139,6 +139,8 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
// RunGet implements the generic Get command
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd
func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *GetOptions) error {
go f.OpenAPISchema(cmdutil.GetOpenAPICacheDir(cmd))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flag guarding is a good idea. I realized we will have to flag guard in other places as well.

About command exiting before this is finished, can potentially cause issues. We will have to fool proof OpenAPI cache reading/writing logic (basically making them idempotent in case of errors). For ex. we may fail while updating the cache (can happen in this case if we don't wait or crash in middle). Will discuss with Antoine about it.

@@ -2568,6 +2568,7 @@ type PodStatusResult struct {

// Pod is a collection of containers that can run on a host. This resource is created
// by clients and scheduled onto hosts.
// +k8s:openapi-gen=x-kubernetes-print-columns:custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, took it out.

@@ -501,7 +513,9 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
}
continue
}
if err := printer.PrintObj(decodedObj, w); err != nil {
// TODO: understand if passing original is safe here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should resolve this TODO or at least make it more specific before merging.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am working on it.

}
parts := strings.SplitN(columnStr, "=", 2)
if len(parts) < 2 {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be returning (opts *OutputOptions, ok bool) in accordance with Golang style guides.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for the suggestion.


// OutputOptions represents resource output options which is used to generate a resource printer.
type OutputOptions struct {
FmtType string // e.g. json, yaml, template, jsonpath etc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this comment should point where I can find the actual list of possible values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Unfortunately, these are not defined as constant types in the code. For now, I have mentioned the filename where the supported format types are listed inside a function. I would like to make a separate change for defining these valid format types as constants.

allowMissingTemplateKeys := false
if cmd.Flags().Lookup("allow-missing-template-keys") != nil {
allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
}
printer, generic, err := printers.GetStandardPrinter(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can simplify the signature of GetStandardPrinter() by passing in outputOptions instead of outputFormat, templateFile,allowMissingTemplateKeys.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for the suggestion.

if err := printer.PrintObj(decodedObj, w); err != nil {
// TODO: understand if passing original is safe here
// if err := printer.PrintObj(decodedObj, w); err != nil {
if err := printer.PrintObj(original, w); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use decodedObj, original may be an unversioned object.
Is there any issue when using decodedObj?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran in to a few cases where using an output format like custom-column with spec "NAME:.metadata.name,RSRC:.metadata.resourceVersion" fails with errors saying "metadata" is missing in object when passed decoded object. It works fine with original object though.
I have to root cause it but I am getting lost in the decoding function. There is some magic which I do not quite understand at this point.

Copy link
Contributor Author

@droot droot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed comments and also updated the PR. PTAL.

@@ -139,6 +139,8 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
// RunGet implements the generic Get command
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd
func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, options *GetOptions) error {
go f.OpenAPISchema(cmdutil.GetOpenAPICacheDir(cmd))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to trigger eager-loading of OpenAPI schema in cache. This is running asynchronously because we don't want to block the processing of Get command because OpenAPI cache may or may for the Get command processing.

@@ -501,7 +513,9 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
}
continue
}
if err := printer.PrintObj(decodedObj, w); err != nil {
// TODO: understand if passing original is safe here
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am working on it.

if err := printer.PrintObj(decodedObj, w); err != nil {
// TODO: understand if passing original is safe here
// if err := printer.PrintObj(decodedObj, w); err != nil {
if err := printer.PrintObj(original, w); err != nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran in to a few cases where using an output format like custom-column with spec "NAME:.metadata.name,RSRC:.metadata.resourceVersion" fails with errors saying "metadata" is missing in object when passed decoded object. It works fine with original object though.
I have to root cause it but I am getting lost in the decoding function. There is some magic which I do not quite understand at this point.

}
parts := strings.SplitN(columnStr, "=", 2)
if len(parts) < 2 {
return nil
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for the suggestion.

allowMissingTemplateKeys := false
if cmd.Flags().Lookup("allow-missing-template-keys") != nil {
allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
}
printer, generic, err := printers.GetStandardPrinter(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for the suggestion.


// OutputOptions represents resource output options which is used to generate a resource printer.
type OutputOptions struct {
FmtType string // e.g. json, yaml, template, jsonpath etc.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Unfortunately, these are not defined as constant types in the code. For now, I have mentioned the filename where the supported format types are listed inside a function. I would like to make a separate change for defining these valid format types as constants.

@droot droot force-pushed the cmd-printer-refactor branch 4 times, most recently from 7c13dce to 0e83d2a Compare May 31, 2017 00:19
Copy link
Contributor Author

@droot droot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed comments and updated the PR with additional comments. @pwittrock @mengqiy PTAL.

@k8s-github-robot k8s-github-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 6, 2017
@smarterclayton
Copy link
Contributor

Um, given how this overlaps with server side get, I'm not sure I approve this.

@smarterclayton smarterclayton removed approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm "Looks good to me", indicates that a PR is ready to be merged. labels Jun 6, 2017
@smarterclayton
Copy link
Contributor

I do not want to add this to the client in 1.7. I want this to be on the server side. If we add this to the client, we'll just remove it in 1.8. What's the point of that?

@smarterclayton smarterclayton added the do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. label Jun 6, 2017
@smarterclayton
Copy link
Contributor

How did this get into the queue without the discussion resolution from the original proposal, saying that this is moving to the server.

@k8s-github-robot k8s-github-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 6, 2017
@smarterclayton
Copy link
Contributor

My primary concern is that this is moving to the server in 1.8, so this flag would be removed and no one would every specify it. So given that, if the desire to get something in for service catalog is very high, this needs to be an --experimental-* flag, and we need to say that the flag will be removed in 1.8.

@droot
Copy link
Contributor Author

droot commented Jun 6, 2017

@smarterclayton I have updated the PR and renamed the flag to '--experimental-use-openapi-print-columns'. Can you pl. take a look and remove "do-not-merge" label. Lets re-evaluate if we want to deprecate or remove this functionality from kubectl when we move this logic to server side in 1.8.

@pwittrock
Copy link
Member

How did this get into the queue without the discussion resolution from the original proposal, saying that this is moving to the server.

@smarterclayton Please refer to the email thread "get consensus on displaying resources - sig-apimachinery / sig-cli" (sent April 17). There you requested that we get together quickly over VC and resolve the discussion since it had been languishing. The consensus was that while there was some overlap in the 2 solutions, they were not mutually exclusive.

One of the artifacts from that discussion was - should clients be able to print an object they have without making a request to the server. e.g. does a Java client that wants to print an object it already fetch need to make a second request to the server to display the object? Also how can clients display object configuration for resources defined locally or deleted from the server? We agreed that it would be useful for clients to have some notion for how to print resources without a server roundtrip.

@pwittrock
Copy link
Member

Making the flag experimental is good idea and we should do that. I would also make it deprecated so it is hidden.

@smarterclayton
Copy link
Contributor

Ok, I vaguely recall us saying:

  1. this would be best effort (which means the openapi spec doesn't fit under our normal rules of backwards compatibility)
  2. we'd fail gracefully if we can't fetch the columns

I had assumed that when the rest of the server side get plumbing went in, there would be no need for the flag when offline (since we would simply determine that the object was unrecognized, make a best effort pass to get the open api spec, and then print).

The rename to "experimental" satisfies my concerns about the public API shape, objection withdrawn with that change. For 1.8, I'd expect for us to remove the flag and do the best effort fetch alongside the server side fetch attempt.

@smarterclayton smarterclayton removed the do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. label Jun 6, 2017
Get command now uses metadata x-kubernetes-print-columns, if present, in Openapi schema
to format output for a resource. This functionality is guarded by a boolean
flag 'use-openapi-print-columns'.
@droot
Copy link
Contributor Author

droot commented Jun 6, 2017

Thanks @smarterclayton
@pwittrock marked the flag deprecated as well. PTAL. I need LGTM to get this PR merged now.

@droot
Copy link
Contributor Author

droot commented Jun 6, 2017

@k8s-bot pull-kubernetes-bazel test this

@pwittrock
Copy link
Member

@smarterclayton SGTM. Yes, this will be a best effort fetch and very small and fast in 1.8 (when using the zipped binary proto + cache). Thanks.

@pwittrock
Copy link
Member

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jun 6, 2017
@k8s-github-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: droot, fabianofranz, pwittrock, wojtek-t

Associated issue: 22

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these OWNERS Files:

You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@k8s-github-robot
Copy link

@k8s-bot test this [submit-queue is verifying that this PR is safe to merge]

@k8s-github-robot
Copy link

Automatic merge from submit-queue (batch tested with PRs 46235, 44786, 46833, 46756, 46669)

@k8s-github-robot k8s-github-robot merged commit a42867f into kubernetes:master Jun 7, 2017
@k8s-ci-robot
Copy link
Contributor

@droot: The following tests failed, say /retest to rerun them all:

Test name Commit Details Rerun command
pull-kubernetes-federation-e2e-gce f59769463bcd02d43214382d8f5b5d23b13659f0 link @k8s-bot pull-kubernetes-federation-e2e-gce test this
pull-kubernetes-e2e-gce-etcd3 f768a63 link @k8s-bot pull-kubernetes-e2e-gce-etcd3 test this

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@smarterclayton
Copy link
Contributor

Why did we introduce OutputOptions instead of adding those fields to PrintOptions? I don't see anything special about them that justifies a separate object.

If there's no reason to have it separate, I'm going to move it into print options.

@droot
Copy link
Contributor Author

droot commented Nov 15, 2017

@smarterclayton I think the main distinction is that OutputOptions is used to determine a printer type to use while PrintOptions are simply args to a given Printer to format the output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/cli Categorizes an issue or PR as relevant to SIG CLI. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

kubectl Get command should use open-api extension to display a resource