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

✨ Add support negative polarity conditions #10550

Conversation

fabriziopandini
Copy link
Member

What this PR does / why we need it:
This PR is an iteration on the condition proposal & corresponding utils allowing the implementation of conditions with negative polarity, which is a step toward latest K8s guidelines about conditions

Which issue(s) this PR fixes:
Part of #7635

/area api

cc @JoelSpeed @vincepri @theobarberbany

@fabriziopandini fabriziopandini added the area/api Issues or PRs related to the APIs label May 3, 2024
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label May 3, 2024
@k8s-ci-robot k8s-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label May 3, 2024
util/conditions/getter.go Outdated Show resolved Hide resolved
}

// PositiveFalseCondition returns a negative polarity condition with Status=false and the given type (Status=False has a positive meaning).
func PositiveFalseCondition(t clusterv1.ConditionType) *clusterv1.Condition {
Copy link
Member

Choose a reason for hiding this comment

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

Why can't this have a reason and message?

Copy link
Member Author

Choose a reason for hiding this comment

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

Back in time we introduced all those helpers to enforce a sort of consistency, and what we have to express when status is good (TrueCondition) does not allow to express reason and message & severity.
This func mirror the same for a condition with negative polarity(when status is False but with a positive meaning).

In other words, I tried to keep things consistent with what we have.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sometimes it can be awkward to require a reason, but there are cases where I think it can make sense.

I guess something like Available as a condition, True status, reason would be AsExpected maybe? But does it also make sense without?

But yeah, in general, I wonder if we should just have the reason and message allowed in all cases, rather than being prescriptive?

Are there kube conventions that say you should or shouldn't set reason/message in certain cases?

Copy link
Member Author

@fabriziopandini fabriziopandini May 3, 2024

Choose a reason for hiding this comment

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

If a user wants flexibility, he/she/they can already do this today using the Set helper.
But as far as I know, in the last 4 years almost everyone used "prescriptive" helpers.

WRT to API conventions, before digging into "reason", let's keep in mind that the scope of this PR is to make one step toward API compliance, not the entire path 😉

Based on my understanding, reason is another area where the thinking about convention evolved over time. The current field description in corev1 types sort of captures it:

// reason contains a programmatic identifier indicating the reason for the condition's last transition.
// Producers of specific condition types may define expected values and meanings for this field,
// and whether the values are considered a guaranteed API.
// The value should be a CamelCase string.
// This field may not be empty.

My reading of this is that community is moving towards having reason everywhere, but they recognize producers of condition might have adopted different approaches.

CAPI as of today has two main differences from the API conventions WRT to how we handle reasons:

  • From a semantic point of view, we use reason to "explain" the current state, not the last transition (in some case the diff is subtle, but in controller we just observe what is the current state and surface it in conditions, we do not track how do we get to the current state)
  • We set reason only when condition have a negative meaning (as far as I remember setting a reason for positive state was considered redoundant, but it was 4 years ago so I might be wrong...)

This PR intentionally does not mean to change what CAPI today is doing WRT to reasons, because this could imply a way bigger impact that probably we should defer to when we cut a new API version.

Copy link
Contributor

Choose a reason for hiding this comment

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

This PR intentionally does not mean to change what CAPI today is doing WRT to reasons, because this could imply a way bigger impact that probably we should defer to when we cut a new API version.

This feels sensible for keeping the scope small here 😄

For my 0.02c: this approach 'feels' more right to me (less surprising)

From a semantic point of view, we use reason to "explain" the current state, not the last transition (in some case the diff is subtle, but in controller we just observe what is the current state and surface it in conditions, we do not track how do we get to the current state)

Copy link
Member

@sbueringer sbueringer May 27, 2024

Choose a reason for hiding this comment

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

I also prefer explaining the current state not the transition. I'm not sure how we would even do this if a controller should be able to calculate the status from scratch. How could it even know what the previous state was

(explaining transitions in conditions also sounds very much like describing the "edges" of reconciliations)

@@ -96,6 +96,25 @@ func FalseCondition(t clusterv1.ConditionType, reason string, severity clusterv1
}
}

// NegativeTrueCondition returns a negative polarity condition with Status=True and the given type (Status=True has a negative meaning).
func NegativeTrueCondition(t clusterv1.ConditionType, reason string, severity clusterv1.ConditionSeverity, messageFormat string, messageArgs ...interface{}) *clusterv1.Condition {
Copy link
Member

Choose a reason for hiding this comment

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

why do NegativeTrueCondition and PositiveFalseCondition include the polarity in the func name?
Since all they are doing is set values in a clusterv1.Condition type wouldn't they be called just TrueCondition / falseCondition and let higher invoker decide on polarity?

Copy link
Member Author

Choose a reason for hiding this comment

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

See #10550 (comment), but the intent is to keep things consistent with what we have.

Just a note about the approach you are suggesting about let higher invoker decide on polarity: this is not in discussion.

The difference is that:

  • If we change the existing TrueCondition & FalseCondition, this will impact everyone and we end up to defer to users the task to enforce the same consistency that today we have with this helpers
  • With the proposed change instead, the users specific helpers for the NegativePolarity, which are consistent with the existing one for positive polarity

Last, but not least, there is already a Set helper with allow the users to decide which field to set (but in this case, it also imply the task to enforce consistency across the codebase/all the providers)

util/conditions/getter_test.go Outdated Show resolved Hide resolved
}

// PositiveFalseCondition returns a negative polarity condition with Status=false and the given type (Status=False has a positive meaning).
func PositiveFalseCondition(t clusterv1.ConditionType) *clusterv1.Condition {
Copy link
Contributor

Choose a reason for hiding this comment

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

Sometimes it can be awkward to require a reason, but there are cases where I think it can make sense.

I guess something like Available as a condition, True status, reason would be AsExpected maybe? But does it also make sense without?

But yeah, in general, I wonder if we should just have the reason and message allowed in all cases, rather than being prescriptive?

Are there kube conventions that say you should or shouldn't set reason/message in certain cases?

Copy link
Member

@sbueringer sbueringer left a comment

Choose a reason for hiding this comment

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

I have to spend more time on it, but NegativeTrueCondition and PositiveFalseCondition are really confusing me

docs/proposals/20200506-conditions.md Outdated Show resolved Hide resolved
docs/proposals/20200506-conditions.md Outdated Show resolved Hide resolved
util/conditions/getter_test.go Outdated Show resolved Hide resolved
@JoelSpeed
Copy link
Contributor

What are the next steps here? Do we want to discuss the Positive/Negative nomenclature in more depth? I'm not personally a huge fan of it and would rather users didn't have to worry, ie we could have helpers that allow you just to specify the state true or false explicitly.

But I can also see that this would make a major departure from where we are now and understand the desire to make a small step here to allow more flexibility in conditions. The PR does this in a sympathetic way and unblocks what is required.

I think the naming, and perhaps eventually removing the connotation of positive/negative could be done at a later point once we've understood more about the usage pattern of the negative/neutral condition types being introduced

@fabriziopandini
Copy link
Member Author

@JoelSpeed on the positive side there is agreement on the idea to support negative polarity conditions, that's already a good point to be.

The only thing we are discussing are the new helpers: NegativeTrueCondition, PositiveFalseCondition and the corresponding MarkNegativeTrue and MarkPositiveFalse

Probably, the most quick way forward is to just drop them, and let users willing to use negative polarity conditions to create Condition types "freely" use the Set(Condition) helper.
I personally think this could be accettable for now because I assume there will be just a few negative polarity conditions at the beginning, and we can come out with better helpers in a second time if necessary.

From the other side of the coin, having helpers really helped users so far in keeping things consistent across all providers, and I still see value in investing some time to figure out new names for helpers.

Another option, if we all agree that there is value in having helpers, is to start with the current names (or a variation of them we quickly agree upon) and eventually iterate in the future.

@fabriziopandini
Copy link
Member Author

fabriziopandini commented May 30, 2024

Might be using more explicit names for helpers might help:
NegativeTrueCondition --> TrueConditionWithNegativePolarity
PositiveFalseCondition --> FalseConditionWithNegativePolarity
MarkNegativeTrue --> MarkTrueWithNegativePolarity
MarkPositiveFalse --> MarkFalseWithNegativePolarity

(and BTW, the fact that we add helpers don't prevent users to create Condition types "freely" use the Set(Condition) helper)

@sbueringer
Copy link
Member

Definitely less confusing

@theobarberbany
Copy link
Contributor

theobarberbany commented Jun 4, 2024

I think the new names (above) are good and will make more sense to someone who's not read this PR :)

I also think they're good enough for now as a starting point, and can be iterated later on too if we find people are confused by them!

Do we have documentation on the helpers already? (outside of the proposal?)

@fabriziopandini
Copy link
Member Author

ACK, I will try get back to this next week.
@enxebre @JoelSpeed are you ok with the proposed way forward?

@JoelSpeed
Copy link
Contributor

Ack, I'm happy with the proposed approach forward, please update when you have a chance

@fabriziopandini
Copy link
Member Author

@sbueringer @vincepri @JoelSpeed @enxebre @theobarberbany
renamed the helpers as per comment above/consensus.
PTAL

Copy link
Contributor

@JoelSpeed JoelSpeed left a comment

Choose a reason for hiding this comment

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

LGTM, will hold off labelling so others can have input

@theobarberbany
Copy link
Contributor

Also LGTM :)

@sbueringer
Copy link
Member

sbueringer commented Jun 17, 2024

/lgtm
in general

My summary would be

  • We now support conditions with positive and negative polarity
  • We added util funcs for negative polarity
  • For me "Paused" is neither positive nor negative polarity, so I wouldn't use the new util funcs for it, which should be okay
    • Note: "Paused" should never be part of a summary (Condition that do not represent the operational state of the component, MUST not be included in the `Ready` condition (e.g. `Paused`, which represent a state of the controller that manages the component).) and we don't need the utils just to set the condition.

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

LGTM label has been added.

Git tree hash: 0729ef4fe54d9bba3f5336be19bf52cf5472fee1

Copy link
Member

@vincepri vincepri left a comment

Choose a reason for hiding this comment

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

/approve
/lgtm

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: vincepri

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 the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 24, 2024
@k8s-ci-robot k8s-ci-robot merged commit fe1c38e into kubernetes-sigs:main Jun 24, 2024
21 checks passed
@k8s-ci-robot k8s-ci-robot added this to the v1.8 milestone Jun 24, 2024
@fabriziopandini fabriziopandini deleted the support-negative-polarity-conditions branch July 2, 2024 08:38
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/api Issues or PRs related to the APIs 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. 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.

7 participants