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

cli: enable/disable specific debug targets #11081

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft

Conversation

davemay99
Copy link
Contributor

No description provided.

Copy link
Contributor

@DerekStrickland DerekStrickland left a comment

Choose a reason for hiding this comment

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

All in all, great work. You write much better go than I do. I left a lot of comments, but that is not indicative of any misgivings as much as feedback. Great job!

}

// IsValid checks whether the key exists in the map
func (m targetMap) IsValid(value string) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be exported? And should targetMap be *targetMap? Also, might this be better named Exists?

Copy link
Contributor Author

@davemay99 davemay99 Aug 30, 2021

Choose a reason for hiding this comment

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

Good catch -- definitely no need to export.

Since maps are already implicitly byref in nature, I think a value receiver makes the most sense here.

As for the function naming, you're right, this is a bit confusing. I initially had a separation between "supported targets" (the list of all explicit targets that can be specified) versus "valid targets" (which also includes the meta sets "all" and "none"). But I stripped it out at the last minute before posting the draft PR.

Will reintroduce the "supported target" concept, which should clear up the distinction and improve readability.

continue
}

enabled = true
Copy link
Contributor

Choose a reason for hiding this comment

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

This is funny. Here we have 2 general programming principles at odds with each other namely default variables to negative case vs. direct boolean function calls. Nothing to change, but just a funny observation.

t = strings.TrimPrefix(t, "-")
enabled = false
}
if !ret.IsValid(t) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If IsValid is only used to check if something does not exist, then you might consider renaming it to DoesNotExist, and getting rid of this !MethodCall. The not symbol before boolean functions hurts readability, and I've always been taught to avoid it if at all possible. However, sometimes it's not possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point on readability -- will address in upcoming commit.

command/operator_debug.go Show resolved Hide resolved
}

// parseTargets parses a comma delimited target string into a targetMap
func (c *OperatorDebugCommand) parseIntervalTargets(intervalTargets string) targetMap {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same pointer observation on return val. Also, this appears to be virtually identical to the method above. I wonder if you couldn't extract a template method, and reduce duplication of logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch - was going to refactor these in a later PR but I'll push a commit shortly to take care of it now.


// Fetch data from vault
if c.targetEnabled("vault") {
vaultAddr := getVaultAddrFromSelf(self)
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need similar logic as the consul stuff in the preceding lines, or is the interface different?

command/operator_debug.go Show resolved Hide resolved
command/operator_debug_test.go Show resolved Hide resolved
website/content/docs/commands/operator/debug.mdx Outdated Show resolved Hide resolved
website/content/docs/commands/operator/debug.mdx Outdated Show resolved Hide resolved
Copy link
Contributor

@notnoop notnoop left a comment

Choose a reason for hiding this comment

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

@DerekStrickland made many good points and I'm adding more :P. I'm giving some conflicting advice here - In places I suggested getting rid of functions, but made nit-picky comments about their content mostly for guiding style.

A question about whether we should track targets vs interval-targets separately and make users track the distinction.

serverIDs []string
targets targetMap
intervalTargets targetMap
targetLock sync.Mutex
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't believe you need this lock. targets and intervalTargets is set early on in Run() and isn't modified afterwards, so it doesn't need a mutex.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are correct -- I was exploring options for enabling or disabling targets based on what is discovered during the capture (OSS vs Enterprise, what features are detected in the configuration, etc). I'll remove the lock and revisit if that functionality is added in the future.

Comment on lines +494 to +495
// String returns a CSV of valid enabled targets
func (t targetMap) String() string {
Copy link
Contributor

Choose a reason for hiding this comment

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

This struct and its methods live among OperatorDebugCommand methods, breaking the flow of methods and making it a bit harder to follow. I'd suggest moving targets at the end of the file or so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed -- good point

var targetString string

for target, enabled := range t {
if enabled && target != "all" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not emit all? If all isn't supposed to be a key here, then this skip seems like papering over a bug and makes it harder to understand what's going on.

Comment on lines +505 to +507
targetString = strings.Join(targets, ",")

return targetString
Copy link
Contributor

Choose a reason for hiding this comment

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

Generally, I'd suggest avoiding declaring variables so far away from where they are used. Also, you probably can get away with avoiding declaring the variable completely:

Suggested change
targetString = strings.Join(targets, ",")
return targetString
return strings.Join(targets, ",")

}

// Enable or disable targets in map based on user input CSV
var enabled bool
Copy link
Contributor

Choose a reason for hiding this comment

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

enabled seems to be only used inside the loop, you don't need to declare it outside.

Comment on lines +491 to +492
// targetMap generic map for targets
type targetMap map[string]bool
Copy link
Contributor

Choose a reason for hiding this comment

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

The comment doesn't add more info than the type name IMO. Also, I wonder if it makes sense to drop Map here; is calling it targets ok here?

Suggested change
// targetMap generic map for targets
type targetMap map[string]bool
// targets is a wrapped set to capture enabled debugging targets
type targets map[string]bool

targets = append(targets, target)
}
}
sort.Strings(targets)
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 on sorting for repeatability and consistency.

// Build starting target map
switch {
case targets == "":
return ret
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we return targetDefaultsNone() here? If we return an empty uninitialized targetMap, I fear that follow up calls with IsValid will generate noisy invalid keys messages because the map is empty.

case targets == "":
return ret
case strings.Contains(targets, "none"):
ret = targetDefaultsNone()
Copy link
Contributor

Choose a reason for hiding this comment

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

What should happen when a user passed none, allocations? Should we generate an error? Should we just return targetDefaultsNone() here as well without further processing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As a draft PR I hadn't fully explained the concept yet, but the idea here is that the target flags are additive / subtractive. If you pass no target flag at all, then the default behavior is the same as it was before and "all" targets are enabled. To clarify I will add a description to the PR and expand the documentation in a future commit.

To your question, none,allocations should result in a map containing only allocations. To verify and help self-document I will add a test to ensure that this is always the case.

As another example, I have an upcoming PR which adds an eventstream target that will be disabled by default. To add this target to a capture you would use -target=eventstream. This results in a map containing all target defaults plus eventstream. Similar to your question, if you wanted only eventstream then you would use -target=none,eventstream.

Comment on lines 143 to 149
-targets=<metrics,-metrics,...,all,none>
Comma separated list of targets to capture. Defaults to "all".
"-<target>" disables capture for that target.

-interval-targets=<metrics,-metrics,...,all,none>
Comma separated list of targets to capture during each interval. Defaults
to "all". "-<target>" disables capture for that target.
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we document which targets are intervals and which ones aren't? Would it make sense to just have a single -targets argument without having users worry about the distinction?

@hashicorp-cla
Copy link

hashicorp-cla commented Mar 12, 2022

CLA assistant check
All committers have signed the CLA.

@tgross
Copy link
Member

tgross commented Sep 30, 2022

@davemay99 this PR is over a year old at this point. Do we want to close this or would you like someone on the team to try to carry it for 1.4.x?

@tgross tgross added the stage/needs-rebase This PR needs to be rebased on main before it can be backported to pick up new BPA workflows label May 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stage/needs-rebase This PR needs to be rebased on main before it can be backported to pick up new BPA workflows
Projects
Status: Needs Roadmapping
Development

Successfully merging this pull request may close these issues.

5 participants