fix(380): apply labels and taints to nodes in default node pool#388
fix(380): apply labels and taints to nodes in default node pool#388arkhoss wants to merge 8 commits into
Conversation
| if rawLabels, ok := newPool["labels"].(map[string]interface{}); ok && len(rawLabels) > 0 { | ||
| newLabels = make(map[string]string, len(rawLabels)) | ||
| for k, v := range rawLabels { | ||
| if strVal, ok := v.(string); ok { | ||
| newLabels[k] = strVal |
There was a problem hiding this comment.
The len(rawLabels) > 0 guard means that if a user removes all labels (goes from having labels to an empty map), newLabels stays nil, and the old labels from the API (copied at line 424) are never cleared. You should allow an empty map through so labels can be removed:
if rawLabels, ok := newPool["labels"].(map[string]interface{}); ok {
newLabels = make(map[string]string, len(rawLabels))
for k, v := range rawLabels {
if strVal, ok := v.(string); ok {
newLabels[k] = strVal
}
}
}There was a problem hiding this comment.
removed the condition, and this should allow an empty map for labels to be removed.
// Extract labels from new desired state
var newLabels map[string]string
if rawLabels, ok := newPool["labels"].(map[string]interface{}); ok {
newLabels = make(map[string]string, len(rawLabels))
for k, v := range rawLabels {
if strVal, ok := v.(string); ok {
newLabels[k] = strVal
}
}
}
|
|
||
| // Extract taints from new desired state | ||
| var newTaints []corev1.Taint | ||
| if taintSet, ok := newPool["taint"].(*schema.Set); ok { |
There was a problem hiding this comment.
Same issue — if a user removes all taints, newTaints stays nil, and old taints from line 424 are preserved. Should initialize to an empty slice when the key exists but the set is empty:
if taintSet, ok := newPool["taint"].(*schema.Set); ok {
newTaints = make([]corev1.Taint, 0, taintSet.Len())
for _, taintInterface := range taintSet.List() {
...
}
}There was a problem hiding this comment.
Hi @alessandroargentieri , changes should allow the newTaints to stay nil, and old taints preserved.
| } | ||
| } | ||
|
|
||
| nodePools = updateNodePool(nodePools, targetNodePool, newPool["node_count"].(int), newLabels, newTaints) |
There was a problem hiding this comment.
This updates labels/taints only when d.HasChange("pools") is true. But the outer if d.HasChange("pools") block is a coarse check — if only labels changed, does d.HasChange("pools") fire? It should, since labels and taint are nested inside the pools schema, but worth verifying with a test. If a user only changes labels without changing node_count, the diff must still trigger this block.
| "public_ip_node_pool": cluster.Pools[0].PublicIPNodePool, | ||
| } | ||
|
|
||
| if len(cluster.Pools[0].Labels) > 0 { |
There was a problem hiding this comment.
Can you add a unit test for flattenNodePool and updateNodePool that covers:
- Creating a cluster with labels+taints on the default pool
- Verifying no drift on a second plan
- Updating labels/taints and verifying they change
- Removing labels/taints and verifying they're cleared
There was a problem hiding this comment.
I added the tests as requested, but sadly, I think I hit an error or panic from the API itself, and not sure how to debug it from here, leaving you a paste bin link with the output I got, and here is the command I used to run the tests:
$ CIVO_REGION=NYC1 CIVO_TOKEN=<token> TF_ACC=1 /usr/local/go/bin/go test ./civo/kubernetes -v -run TestAccCivoKubernetesCluster_labels_update
not sure if all I added helps you, but I ran a bunch of tests with it, and I feel I'm close, but I lack the knowledge to see it through.
|
Hi @alessandroargentieri , thank you for such detailed review! I will do my best and try to fix all the comments soon, thanks again! |
|
@arkhoss we have some customers blocked on these issues? Are you planning to complete the PRs soon or can I take over them and solve to help our customers? Many thanks for any effort you're getting into this anyway 🙏🏼 |
…so workaround cuz civo api does ignores labels and taints when cluster is created
…uster labels updates
f18747f to
a98bf34
Compare
alessandroargentieri
left a comment
There was a problem hiding this comment.
Thanks @arkhoss for tackling this, and welcome — first PR or not, this is a solid attempt and the direction is right. A few things I want to sort out before we merge, though.
two that are blocking ones for me:
1. clearing labels probably doesn't actually work. When the config has no labels, newLabels ends up as an empty (but non-nil) map. KubernetesClusterPoolUpdateConfig.Labels is json:"labels,omitempty", so an empty map gets dropped from the PUT body entirely — meaning that the API keeps the old labels despite we would like to clear them up. Then waitForPoolLabelsAndTaints sits in this branch forever:
if len(expectedLabels) == 0 && len(labels) > 0 { return cluster, "PENDING", nil }…so the update just spins until TimeoutUpdate. That's exactly what your "Remove labels completely" acceptance step exercises, so I think it'd fail if the acc suite were actually run (it's TF_ACC-gated, so it almost certainly didn't run in CI). Can you double-check how the API actually clears labels and confirm that step goes green? Taints are fine btw — that field isn't omitempty.
2. updateNodePool is dead code now. After the refactor, nothing in the provider calls it anymore — the update path builds poolUpdate inline and calls UpdateKubernetesClusterPool directly. The only thing reaching it is ExportUpdateNodePool in the test. So we've widened its signature and written a test for a function we no longer use, which gives us false confidence. Either wire it back into the real flow or just delete it along with the export and TestAccCivoUpdateNodePool.
, one I'm less sure about but want to flag:
3. possible state drift. labels/taint are Optional only (not Computed), and flattenNodePool now writes everything the API returns back into state. If Civo/k3s injects its own managed labels (the kubernetes.civo.com/... kind) we'd get a permanent non-empty plan, and it'd also make #1 worse — len(labels) > 0 would always be true so clearing could never finish.
apert from that, smaller stuff, not blocking:
- The two unit tests are named
TestAccCivo...but they're pure unit tests (no API, noTF_ACC). TheTestAccprefix is our convention for the real acceptance tests. Mind renaming toTestFlattenNodePool/TestUpdateNodePool? - There's a leftover debug loop in
TestAccCivoKubernetesCluster_labels_update— thefmt.Println("--- ATTRIBUTES ---")block. Drop that before merge. - Tiny one: the CNI test config string picked up a trailing space (
}->}). Accidental, just revert it. waitForClusterActiveis basically a copy of the inlinecreateStateConfin create — could share one helper, but I won't die on that hill.
Thanks for all your effort, I love the approach, just need #1 and #2 sorted (and ideally an answer on #3) and we're good to go
…d filter for civo managed labels, removed updateNodePool and its export
…NodePool to correctly capture and reconcile user-defined node pool labels/taints without hitting asynchronous API and added cluster config no labels for the test
|
Thanks again for such detailed answer, here is a list of the things I did in the last commits:
Prioritized checking With all these changes, the tests are now passing, build, unit tests and acceptance test too: |
This MR is an attempt to resolve #380 .
This is my first time, so be gentle! 😉