Skip to content

eve-k: add cluster tie-breaker support to evetest, fix pkg/kube kubeclient regression - #6309

Merged
rene merged 2 commits into
lf-edge:masterfrom
andrewd-zededa:eve-k-evetest-tb-support
Aug 18, 2026
Merged

eve-k: add cluster tie-breaker support to evetest, fix pkg/kube kubeclient regression#6309
rene merged 2 commits into
lf-edge:masterfrom
andrewd-zededa:eve-k-evetest-tb-support

Conversation

@andrewd-zededa

@andrewd-zededa andrewd-zededa commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

ClusterNode gets a TieBreaker field. NewEdgeClusterConfig finds the onboarded UUID of that node and writes it to
EdgeNodeCluster.TieBreakerNodeId. It writes the UUID to the configuration of all three nodes, because each node compares that UUID with its own to know if the role applies to itself. A new TestHarness.deviceUUID helper does the name-to-UUID lookup. A maximum of one node can be the tie-breaker.

TestTieBreakerCluster starts a three-node cluster with a tie-breaker. It first makes sure that all three nodes report the same designation. It then waits for the tie-breaker-config-applied label, which pkg/kube writes last. The test then examines the node labels, the cordon, the KubeVirt and Longhorn replica counts, the CDI and longhorn-manager node selectors, and the drained node. It last starts an application, and makes sure that no VMI and no Longhorn replica goes to the tie-breaker, and that the application volume uses the two-replica storage class.

The test warns, and does not fail, about the Longhorn node specification, the KubeVirt CR infra replicas, and the node selectors of virt-handler and longhorn-csi-plugin. pkg/kube applies these one time at cluster creation and does not reconcile them, thus other controllers set them back. The warnings show if the values hold at first, or never apply.

The test runs in TestNodeClusterSuite after TestThreeNodesCluster. The comment about device reuse in that suite was not correct, because clusterDeviceRequirements prevents reuse. The comment is now correct.

Second commit — a real regression found while validating the test above,
not a test-only change.** kube-init's Kubernetes client is built once, at
k3s-ready time, and never rebuilt. A node joining a multi-node cluster can
have its local k3s TLS material change afterward; every subsequent API call
from that node then fails permanently with "certificate signed by unknown
authority", which blocks node-label initialization and, transitively, the
tie-breaker phase (and CDI config, the kubevirt feature-gate migration, and
anything else that tick performs). The fix rebuilds the client whenever the
kubeconfig file's content changes, on the same tick that already mirrors
that file for other consumers. See the commit message for the full
mechanism. Reviewers: please give this one the same scrutiny as a
standalone pkg/kube PR — it changes daemon behavior on real devices, not
just test coverage.

PR dependencies

None

How to test and validate this PR

Run the new test

EVETEST_LOG_LEVEL=debug make evetest NAME=TestTieBreakerCluster

Parameters: TPM (bool), FILESYSTEM (defaults to ext4).

Or the whole suite, which places it after TestThreeNodesCluster:

EVETEST_LOG_LEVEL=debug make evetest NAME=TestNodeClusterSuite

This brings up three kubevirt devices from scratch and deploys an app, so
budget accordingly — it is the slowest suite in the tree, and this adds a
fourth full cluster formation to it.

Validating the kubeclient fix specifically

Watch the kube-init log during cluster formation. Before this fix, a node
whose TLS material changed after joining would log the same
"certificate signed by unknown authority" error on every subsequent tick,
forever. After this fix, look for:

kubeclient: rebuilt from /etc/rancher/k3s/k3s.yaml after kubeconfig change

within one healthCheckInterval (15s) of that error appearing, followed by
the errors clearing on the next tick.

Changelog notes

Fixed a bug where a node joining a multi-node EVE-K cluster could get
permanently stuck partway through initialization — node labels, the
tie-breaker role, and cluster component configuration — if its local
Kubernetes TLS material changed during the join. No other user-facing
changes; the evetest additions are test-framework only.

PR Backports

  • 17.0-stable: To be backported.
  • 16.0-stable: No, as the feature is not available there.
  • 14.5-stable: No, as the feature is not available there.
  • 13.4-stable: No, as the feature is not available there.

Checklist

  • I've provided a proper description
  • I've added the proper documentation
  • I've tested my PR on amd64 device
  • I've tested my PR on arm64 device
  • I've written the test verification instructions
  • I've set the proper labels to this PR

And the last but not least:

  • I've checked the boxes above, or I've provided a good reason why I didn't
    check them.

Please, check the boxes above after submitting the PR in interactive mode.

@github-actions
github-actions Bot requested a review from eriknordmark August 13, 2026 21:43
@andrewd-zededa andrewd-zededa added stable Should be backported to stable release(s) next-17.0.x-rc PR must be present in the next 17.0.x-lts release labels Aug 13, 2026
@andrewd-zededa andrewd-zededa changed the title evetest: add cluster tie-breaker support eve-k: add cluster tie-breaker support to evetest, fix pkg/kube kubeclient regression Aug 13, 2026
@andrewd-zededa

Copy link
Copy Markdown
Contributor Author

Fixed a regression introduced in #5971. Tie-breaker config not applied due to stale kubeclient blocking api access.

@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 24.66%. Comparing base (51dd5c1) to head (6de65f1).
⚠️ Report is 22 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6309      +/-   ##
==========================================
+ Coverage   24.31%   24.66%   +0.34%     
==========================================
  Files         512      522      +10     
  Lines       93853    95569    +1716     
==========================================
+ Hits        22820    23571     +751     
- Misses      69219    70000     +781     
- Partials     1814     1998     +184     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

// out of the cluster, which is a destructive change to the topology the
// preceding test relies on.
// Every cluster subtest re-creates its devices, because
// clusterDeviceRequirements sets CreateFromScratchWithLiveImage, which

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should replace CreateFromScratchWithLiveImage with something like ResetDeviceConfig to maximize device reuse and reduce the runtime of this already fairly long test suite.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think I agree, although I'd like to avoid pulling in that work to this pr.

Comment thread evetest/setup.go Outdated
andrewd-zededa and others added 2 commits August 14, 2026 15:40
ClusterNode gets a TieBreaker field. NewEdgeClusterConfig finds the
onboarded UUID of that node and writes it to
EdgeNodeCluster.TieBreakerNodeId. It writes the UUID to the configuration
of all three nodes, because each node compares that UUID with its own to
know if the role applies to itself. A new TestHarness.deviceUUID helper
does the name-to-UUID lookup. A maximum of one node can be the
tie-breaker.

TestTieBreakerCluster starts a three-node cluster with a tie-breaker. It
first makes sure that all three nodes report the same designation. It
then waits for the tie-breaker-config-applied label, which pkg/kube
writes last. The test then examines the node labels, the cordon, the
KubeVirt and Longhorn replica counts, the CDI and longhorn-manager node
selectors, and the drained node. It last starts an application, and makes
sure that no VMI and no Longhorn replica goes to the tie-breaker, and
that the application volume uses the two-replica storage class.

The test warns, and does not fail, about the Longhorn node specification,
the KubeVirt CR infra replicas, and the node selectors of virt-handler
and longhorn-csi-plugin. pkg/kube applies these one time at cluster
creation and does not reconcile them, thus other controllers set them
back. The warnings show if the values hold at first, or never apply.

The test runs in TestNodeClusterSuite after TestThreeNodesCluster. The
comment about device reuse in that suite was not correct, because
clusterDeviceRequirements prevents reuse. The comment is now correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Andrew Durbin <andrewd@zededa.com>
initKubeclient builds the process-wide client-go client one time, at
EvK3sReady, from /etc/rancher/k3s/k3s.yaml. No code ever rebuilds it
after that.

A node that joins a multi-node cluster can have its local k3s server
change its TLS material after EvK3sReady fires. The cached client
then fails every API call with "certificate signed by unknown
authority", and it never recovers. reapplyNodeLabels uses this same
client, so state.NodeLabelsInitialized never gets set, and the
tie-breaker phase skips forever on its own first check ("node labels
not yet initialized").

The old shell implementation did not have this problem. It ran a new
kubectl process for each call, and each process read the current
kubeconfig file fresh from disk.

kubeconfigSyncLoop already reads the kubeconfig file on every tick,
to mirror it for other consumers. This change adds a second check on
the same tick: rebuild kubeclient.Default() when the file content
differs from what the current client was built from, and stop the
informer goroutines of the client it replaces.

The very first tick always rebuilds the client one time, even when
nothing changed, because there is no known content to compare
against yet. The client that initKubeclient built has no stop
function to call, so its informer goroutines stay until the process
ends. Both costs happen at most one time per node boot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Andrew Durbin <andrewd@zededa.com>

@eriknordmark eriknordmark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@rene
rene merged commit 30cbb56 into lf-edge:master Aug 18, 2026
55 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

next-17.0.x-rc PR must be present in the next 17.0.x-lts release stable Should be backported to stable release(s)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants