-
Notifications
You must be signed in to change notification settings - Fork 86
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
Allow node label to be a flexible expression #75
Conversation
Codecov Report
@@ Coverage Diff @@
## master #75 +/- ##
==========================================
+ Coverage 73.92% 74.18% +0.25%
==========================================
Files 7 7
Lines 464 488 +24
==========================================
+ Hits 343 362 +19
- Misses 113 116 +3
- Partials 8 10 +2
Continue to review full report at Codecov.
|
I have the same problem but your solution would block me in fact because I need a mix of AND and OR. So for example: |
hah, I literally started with that! But then i realized it would be a breaking change. People that are currently specifying I'm fine to tweak my PR to work the way you're proposing but maybe we should do a new flag? |
The
Thats a rough angle I thought to offer up. I think, realistically, that the yaml encoded string encompassed in the command-line arg is kind of ugly. I think in general, if folks are starting to specify more complex selectors a configuration file may start to be appropriate. The structure for these selectors would then mirror the kubernetes resources: leveraging data structures users are already familiar with as well as library functions in client-go. Thus far, the answer has been to just use multiple draino instances for things like this, but that's not very user-centric admittedly. On the other hand, my personal feeling is that the command line arguments are best suited for some happy path scenario. I appreciate the effort for backwards compatibility here as well. I think I like the comma separated syntax more, but don't like the break in compatibility. Why not keep the AND'ing behavior between |
Sounds like I'm repeating myself with the configuration file suggestion: #62 (comment) |
Having the config file is clearly the way to more complex scenarii. I will probably work on it at some point. Waiting for that a simple alternative can be to have 2 flags that are exclusive: repeating --node-label would be an AND WDYT? |
Wanted to draw attention to this. Why not just allow:
Which would evaluate as:
The comma separation in the @dbenque your suggestion was to use commas for
We should be g2g. † Note: Possibly someone in the wild is using a comma literally, but I think that's highly unlikely and might be disallowed anyway by the valid chars in labels values.
|
@jacobstr I agree a configfile is a better end state here. But I dont have the time for a refactor of that magnitude. What you're proposing would be fine with me if the syntax was Who here can actually approve a PR/design? I'm not trying to put more work into this if a design cannot be solidified. I've currently forked and am running this branch in production. @negz you have the collaborator flair, but also @jacobstr you seem like you have merge access? |
@cpoole I work at Planet and hold the keys these days. I think we're iterating on a good design here through the discussion so this debate isn't without merit. Agreed re: |
@jacobstr ok sounds good to me So I'll go ahead and implement it using the following:
logic example: the following nodes would match: Node2: the following nodes would not match: Node2: |
I'm rusty on my boolean algebra buuuut... I the gist of what you'd be trying to do is converting something that it's in disjunctive normal form (or of ands) to conjunctive normal form (and of ors). I believe this is generally possible, though a bit messy and perhaps re-expressing things this way will obfuscate the original meaning of the expression. Some comments here: https://math.stackexchange.com/questions/1710210/converting-formula-from-cnf-to-dnf |
Such transformation (dnf/cnf) would make it completely obscure while with a simple OR there is a natural way of expressing group of labels that define the group of nodes. |
I'm finally circling back to this. I thought about what what @dbenque was saying and I agree... the original proposal in this PR was too restrictive. I've redone the PR using https://github.com/antonmedv/expr as an expression compiler. This allows for incredibly flexible use cases and I think will satisfy 99% of users. However, This is now a fully breaking change... What would y'all like to do? |
I hadn't looked at the push script before and was hoping that we didn't publish the latest tag - might have made me bolder here. I suppose the land shifting and breaking beneath your feet is to be expected for folks relying on latest. Unfortunately, I don't have a mailing list of draino users to even attempt to provide the courtesy of a heads up here. I'll find some time to review things. I suspect we might have some way to put this new syntax behind a new command line flag and have the old-style flag "converted" to your expression compiler to reduce duplication. That'd let us avoid any migration mess. |
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.
Thanks so much for doing this @cpoole. I've left some feedback that I think will provide some CYA in case new requirements (plzcani filter on annotations) come up as well as a path to grandfathering this without breaking compatibility.
@cpoole I'm going to check in around 9:30-3:00 PST tomorrow to drive this to a conclusion. Happy to get this merged vs continuing to linger in the bike shed. |
Sounds good @jacobstr I'll address your comments tonight |
ok @jacobstr updated and tested. |
internal/kubernetes/nodefilters.go
Outdated
|
||
expression, err := expr.Compile(*expressionStr, expr.Env(parameters)) | ||
if err != nil { | ||
log.Fatalf("Could not compile the node label expression: %v", err) |
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.
log.Fatalf
here is a bit unfortunate - it'll cause the program to exit but it's kind of buried in a function that shouldn't have this kind of side effect.
Can we error up front if the expression is invalid? It looks like we don't get the parameters
until we've obtained a particular node but we can probably still compile the expression with an empty environment?
Curious what happens if the metadata.labels. does not exist for a specific expression as well.
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.
- Looks like we could compile the program with no env outside of the callback function body and fail early there.
- Saw your test case with a field that does not exist.
- This may be useful to ensure the Expr provided evaluates to a boolean: https://godoc.org/github.com/antonmedv/expr#AsBool
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.
went ahead and compiled outside of the body... seems like I was planning to do that anyway since I changed the signature to return an err, but i must have gotten distracted 😆
@@ -21,15 +21,17 @@ import ( | |||
"testing" | |||
"time" | |||
|
|||
"gotest.tools/assert" | |||
core "k8s.io/api/core/v1" | |||
meta "k8s.io/apimachinery/pkg/apis/meta/v1" | |||
) | |||
|
|||
func TestNodeLabelFilter(t *testing.T) { |
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.
Would prefer the original test cases intact since we still support the old syntax. Can we add the new ones as separate table-driven tests?
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.
added the old ones back as a separate test
@jacobstr addressed your comments. The build is failing because linter is running out of memory. I dont see a way to retry a travis build. Any suggestions? |
I'll merge this in a moment assuming tests pass and a subsequent rebase on your branch should hopefully resolve it. |
@jacobstr squashed and rebased. Lets put this saga to an end |
@jacobstr bump |
This PR addresses a new feature:
I do not see a way to provide a list of labels that can be OR'd together to select a node. Right now if you provide multiple labels it must match ALL of them.
This PR makes node labels into a user specifiable expression:
(node_labels['region'] == 'us-west-2' && node_labels['app'] == 'nginx') || (node_labels['region'] == 'us-west-2' && node_labels['foo'] == 'bar') || (node_labels['type'] == 'toolbox')
This is achieved through the use of https://github.com/antonmedv/expr
This PR adds the ability to flip draino into OR mode.While doing this I realized when multiple labels are supplied that have the same key but different values only the last provided is used in the app: eg.--node-label=foo=bar --node-label=foo=elephant
results in the*nodeLabels
map only including{foo: elephant}
In order to fix this I parse them as an array of independentmap[string]string
and then let the Filter functions determine how to handle the deduping (required in the AND case to maintain backwards compatible behavior)