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

gwctl: Introduce a --for flag to filter results related to a specified resource. #3068

Merged
merged 8 commits into from
May 16, 2024

Conversation

gauravkghildiyal
Copy link
Member

What type of PR is this?
/kind feature

What this PR does / why we need it:

This PR achieves two broad things:

  1. Add --for flag for filtering resources based on an associated resource.

    For example:

    • gwctl get backends --for gateway/ns2/my-gateway
    • gwctl get gateways --for httproute/ns1/my-httproute
    • gwctl get gateways --for gatewayclass/foo-gatewayclass
    • gwctl get policies --for service/foo-namespace/my-svc
  2. Remove duplication from cli setup of Get and Describe commands. This inturn
    allows both commands to share feataures/flags (when applicable) without the
    need to manually configure them independently for each of them

Which issue(s) this PR fixes:

Fixes #

Does this PR introduce a user-facing change?:

gwctl allows filtering results to only those related to the specified resource (using the `--for` flag)

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. kind/feature Categorizes issue or PR as related to a new feature. area/gwctl cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels May 9, 2024
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: gauravkghildiyal

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

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels May 9, 2024
Copy link
Member

@robscott robscott 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 all the great work on this @gauravkghildiyal!

@@ -26,31 +25,45 @@ import (
"github.com/spf13/cobra"
"k8s.io/klog/v2"

"sigs.k8s.io/gateway-api/gwctl/pkg/common"
"sigs.k8s.io/gateway-api/gwctl/pkg/policymanager"
cmdutils "sigs.k8s.io/gateway-api/gwctl/pkg/utils"
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Package name doesn't really match path - maybe the scope of the package expanded over time?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice catch. Changed to use the default name.

Comment on lines 31 to 46
type Factory interface {
K8sClients() (*common.K8sClients, error)
PolicyManager() (*policymanager.PolicyManager, error)
}
Copy link
Member

Choose a reason for hiding this comment

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

Why does this need to be an interface? Are you expecting other implementations of the interface to be added in the future? (Not saying this is necessarily bad, just trying to understand the use case).

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice question. Added an internal comment for future context as well. Happy to explain it differently if needed.

	// INTERNAL COMMENT:
	// - The reason for an interface here is to be able to inject this dependency
	//   during unit tests.
	// - The reason for the "factory" pattern is to delay the construction of the
	//   objects for when the commands get run (as opposed to when the commands
	//   are registered in Cobra). This is required because during registration of
	//   the commands, we will need to inject this dependency, but we cannot
	//   construct the dependency because it depends on some flag values which are
	//   only known during the runtime of the command.

Namespace: httpRouteNode.HTTPRoute.GetNamespace(),
}
var referenceAccepted bool
for _, referenceGrantNode := range backendNode.ReferenceGrants {
Copy link
Member

Choose a reason for hiding this comment

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

It would probably be useful to have some basic scalability tests - let's say 100 of each kind of resources with different levels of connections between to help keep some basic understanding of the impact each additional change like this adds. Not a blocker for this PR, but could be useful as a follow up issue for someone to tackle.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes of course you have the right thoughts/concerns. I certainly know that computing all such relations (including all the other ones) will definitely take some time. I have a few thoughts about how this user experience will look like. Will share / discuss separately.

Comment on lines +504 to +512
// Backend does not exist in the resourceModel (maybe because of some
// error), so skip processing this backend.
Copy link
Member

Choose a reason for hiding this comment

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

Seems like some debug logging could be useful here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure added one log.

The verbosity is a bit higher since a relevant error (with more precise details) for this specific case should have been logged in the previous looping construct i.e. Step 1

Comment on lines +65 to +66
rootCmd.AddCommand(NewSubCommand(factory, os.Stdout, commandNameGet))
rootCmd.AddCommand(NewSubCommand(factory, os.Stdout, commandNameDescribe))
Copy link
Member

Choose a reason for hiding this comment

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

This is a very neat bit of cleanup and refactoring (along with the new subcommand file), nicely done!

if kubeConfigPath == "" {
kubeConfigPath = os.Getenv("KUBECONFIG")
if kubeConfigPath == "" {
kubeConfigPath = path.Join(os.Getenv("HOME"), ".kube/config")
Copy link
Member

Choose a reason for hiding this comment

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

I'm rusty here, but I thought if you left kubeconfig path empty when initializing clients, they'd already choose a sane default?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh yeah I gave it a try and from what I understand, that part is supposed to work when the process is running within a kubernetes clusters (so it can assume certain environment variables being set and so on).

Though that's a good callout. Added an action item to #2761 to figure out what the best approach here is.

Copy link
Member Author

@gauravkghildiyal gauravkghildiyal 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, @robscott

@@ -26,31 +25,45 @@ import (
"github.com/spf13/cobra"
"k8s.io/klog/v2"

"sigs.k8s.io/gateway-api/gwctl/pkg/common"
"sigs.k8s.io/gateway-api/gwctl/pkg/policymanager"
cmdutils "sigs.k8s.io/gateway-api/gwctl/pkg/utils"
Copy link
Member Author

Choose a reason for hiding this comment

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

Nice catch. Changed to use the default name.

Namespace: httpRouteNode.HTTPRoute.GetNamespace(),
}
var referenceAccepted bool
for _, referenceGrantNode := range backendNode.ReferenceGrants {
Copy link
Member Author

Choose a reason for hiding this comment

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

Yes of course you have the right thoughts/concerns. I certainly know that computing all such relations (including all the other ones) will definitely take some time. I have a few thoughts about how this user experience will look like. Will share / discuss separately.

Comment on lines +504 to +512
// Backend does not exist in the resourceModel (maybe because of some
// error), so skip processing this backend.
Copy link
Member Author

Choose a reason for hiding this comment

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

Sure added one log.

The verbosity is a bit higher since a relevant error (with more precise details) for this specific case should have been logged in the previous looping construct i.e. Step 1

Comment on lines 31 to 46
type Factory interface {
K8sClients() (*common.K8sClients, error)
PolicyManager() (*policymanager.PolicyManager, error)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Nice question. Added an internal comment for future context as well. Happy to explain it differently if needed.

	// INTERNAL COMMENT:
	// - The reason for an interface here is to be able to inject this dependency
	//   during unit tests.
	// - The reason for the "factory" pattern is to delay the construction of the
	//   objects for when the commands get run (as opposed to when the commands
	//   are registered in Cobra). This is required because during registration of
	//   the commands, we will need to inject this dependency, but we cannot
	//   construct the dependency because it depends on some flag values which are
	//   only known during the runtime of the command.

if kubeConfigPath == "" {
kubeConfigPath = os.Getenv("KUBECONFIG")
if kubeConfigPath == "" {
kubeConfigPath = path.Join(os.Getenv("HOME"), ".kube/config")
Copy link
Member Author

Choose a reason for hiding this comment

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

Oh yeah I gave it a try and from what I understand, that part is supposed to work when the process is running within a kubernetes clusters (so it can assume certain environment variables being set and so on).

Though that's a good callout. Added an action item to #2761 to figure out what the best approach here is.

…y (-v)

flag. Get rid of global flag variables like kubeConfigPath.
For example:
* gwctl get backends --for gateway/ns2/my-gateway
* gwctl get gateways --for httproute/ns1/my-httproute
* gwctl get gateways --for gatewayclass/foo-gatewayclass
@gauravkghildiyal
Copy link
Member Author

(Rebased)

@robscott
Copy link
Member

Thanks @gauravkghildiyal!

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 16, 2024
@k8s-ci-robot k8s-ci-robot merged commit a296435 into kubernetes-sigs:main May 16, 2024
8 checks passed
@gauravkghildiyal gauravkghildiyal deleted the cli-cleanup branch May 17, 2024 04:49
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. area/gwctl cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. 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. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants