[Kubernetes] Coerce Python bool tolerations to lowercase for taint match#9756
Merged
rohansonecha merged 1 commit intoMay 29, 2026
Conversation
`_str_or_empty` already coerces non-string toleration / taint values to
string so YAML-parsed scalars (`value: 0`, `value: 1.5`, `value: true`)
match against the K8s API's always-string taint fields. But `str(True)`
yields the Python-cased `'True'`, while K8s stores taint values as
whatever Go's YAML serializer emits — always lowercase (`'true'` /
`'false'`). A `kubectl taint nodes X foo=true:NoSchedule` ends up with
`value: "true"` on the node.
That silent mismatch means a config
tolerations:
- key: foo
operator: Equal
value: true # unquoted YAML → Python True → 'True'
effect: NoSchedule
would never match a real K8s taint with `value: "true"` — the operator
sees `'True' != 'true'` and the taint reads un-tolerated. The user has
to quote the value (`value: "true"`) to work around it.
Special-case `bool` inside `_str_or_empty` to lowercase. Mirrors what
Go's `strconv.FormatBool` / `fmt.Sprintf("%t", ...)` produce, so the
matcher stays in lockstep with how K8s actually stores taint values.
Tests:
- Updates `yaml-bool` / `yaml-bool-false` parametrize cases to use
`'true'` / `'false'` taint values (the realistic K8s shape).
- Adds two `python-cased-bool-no-match` cases to assert strictness:
a hypothetical taint stored as Python-cased `'True'` / `'False'`
must NOT match a Python-bool toleration — the lowercase coercion
is one-directional, not a case-insensitive compare.
Contributor
There was a problem hiding this comment.
Code Review
This pull request updates the _str_or_empty utility function in sky/provision/kubernetes/utils.py to explicitly convert boolean values to lowercase strings ('true' or 'false'), aligning with how Kubernetes stores taint values. Unit tests in tests/unit_tests/kubernetes/test_kubernetes_utils.py have been updated and expanded to cover these changes and prevent regressions. There are no review comments, and I have no feedback to provide.
kevinmingtarja
approved these changes
May 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #9750.
_str_or_empty(used bytaint_is_toleratedtocompare toleration values against K8s taint values) coerces non-string
scalars to string so unquoted YAML values like
value: 0andvalue: 1.5still match real K8s taints. But for Pythonbool,str(True)produces the Python-cased'True'— while K8s storestaint values as whatever Go's YAML serializer emits, which is always
lowercase (
'true'/'false'). Evenkubectl taint nodes X foo=true:NoSchedulelands on the node asvalue: "true".That silent mismatch means a config
never matches a real K8s taint with
value: "true"— the operatorsees
'True' != 'true'and the taint reads un-tolerated. The user hasto remember to quote the value (
value: "true") to work around it.Special-case
boolinside_str_or_emptyto lowercase. Mirrors whatGo's
strconv.FormatBool/fmt.Sprintf("%t", ...)produce, so thematcher stays in lockstep with how K8s actually stores taint values.
Test plan
yaml-bool/yaml-bool-falseparametrize cases intest_taint_is_tolerated_str_coercionto use'true'/'false'taint values (the realistic K8s shape).
python-cased-bool-no-matchcases asserting the coercionis one-directional / strict: a hypothetical taint stored as
Python-cased
'True'must NOT match a Python-bool toleration. Wefix the toleration side to match K8s reality, not loosen the
comparison to be case-insensitive.
🤖 Generated with Claude Code