• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubevirt / hyperconverged-cluster-operator / 16018029710

02 Jul 2025 06:44AM UTC coverage: 75.43% (-0.2%) from 75.599%
16018029710

push

github

web-flow
Monitor the cluster architectures (#3594)

* monitor and cache the cluster node architectures

Signed-off-by: Nahshon Unna-Tsameret <nunnatsa@redhat.com>

* API changes

1. add the new `nodeInfo` field to the HyperConverged's status
2. add the `conditions` field to the DICT status type
3. add the `originalSupportedArchitectures` field to the DICT status
   type.

Signed-off-by: Nahshon Unna-Tsameret <nunnatsa@redhat.com>

* Set the new status fields when the nodeinfo is changed

Move the setting of the HA status field to the HC controller.

The nodes controller only triggers the reconciliation of the HC, but
not changes the HC CR status.

Trigger the HC reconciliation will also promise the right setting of the
DataImportCronTemplates in SSP, in a following commit.

Signed-off-by: Nahshon Unna-Tsameret <nunnatsa@redhat.com>

* validate the nodePlace Affinity field

Signed-off-by: Nahshon Unna-Tsameret <nunnatsa@redhat.com>

* collect node information on boot time

Signed-off-by: Nahshon Unna-Tsameret <nunnatsa@redhat.com>

* update SSP validation

Use the HyperConverged status new fields, to get the node architectures
for the SSP validation.

Signed-off-by: Nahshon Unna-Tsameret <nunnatsa@redhat.com>

* Filter unsupported architectures from DICTs

If there are no supported architectures for a specific DICT, it will not
added to the SSP CR.

The DICTs in the HyperConverged status will have the
`ssp.kubevirt.io/dict.architectures` annotation only with the
architectures that are both suppported by the image, and by the
cluster.

But if there are no supported architectures for a specific DICT, in
addition to not be copied to the SSP CR, its DICT object in the HC
status will have the the new `Deployed` condition in its status,
with status of `False` and reason of `"UnsupportedArchitectures"`.

Also, each DICT object in the HyperConverged status, will have the
new status.originalSupportedArchitectures, with the original values
from the annotation (for debug,... (continued)

246 of 351 new or added lines in 8 files covered. (70.09%)

7 existing lines in 4 files now uncovered.

6585 of 8730 relevant lines covered (75.43%)

1.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.3
/pkg/internal/nodeinfo/nodeinfo.go
1
package nodeinfo
2

3
import (
4
        "context"
5
        "fmt"
6

7
        "github.com/go-logr/logr"
8
        corev1 "k8s.io/api/core/v1"
9
        "k8s.io/apimachinery/pkg/util/sets"
10
        "sigs.k8s.io/controller-runtime/pkg/client"
11

12
        "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
13
)
14

15
func HandleNodeChanges(ctx context.Context, cl client.Client, hc *v1beta1.HyperConverged, logger logr.Logger) (bool, error) {
1✔
16
        logger.Info("reading cluster nodes")
1✔
17
        nodes, err := getNodes(ctx, cl)
1✔
18
        if err != nil {
1✔
NEW
19
                return false, fmt.Errorf("failed to read the cluster nodes; %v", err)
×
20
        }
×
21

22
        return processNodeInfo(nodes, hc), nil
1✔
23
}
24

25
func getNodes(ctx context.Context, cl client.Client) ([]corev1.Node, error) {
1✔
26
        nodesList := &corev1.NodeList{}
1✔
27
        err := cl.List(ctx, nodesList)
1✔
28
        if err != nil {
1✔
29
                return nil, err
×
30
        }
×
31

32
        return nodesList.Items, nil
1✔
33
}
34

35
func processNodeInfo(nodes []corev1.Node, hc *v1beta1.HyperConverged) bool {
1✔
36
        workerNodeCount := 0
1✔
37
        cpNodeCount := 0
1✔
38

1✔
39
        workloadArchs := sets.New[string]()
1✔
40
        cpArchs := sets.New[string]()
1✔
41

1✔
42
        isWorkloadNode := isWorkloadNodeFunc(hc)
1✔
43

1✔
44
        for _, node := range nodes {
2✔
45
                arch := node.Status.NodeInfo.Architecture
1✔
46
                if isWorkerNode(node) {
2✔
47
                        workerNodeCount++
1✔
48
                }
1✔
49

50
                if isWorkloadNode(node) {
2✔
51
                        workloadArchs.Insert(arch)
1✔
52
                }
1✔
53

54
                _, masterLabelExists := node.Labels[LabelNodeRoleMaster]
1✔
55
                _, cpLabelExists := node.Labels[LabelNodeRoleControlPlane]
1✔
56
                if masterLabelExists || cpLabelExists {
2✔
57
                        cpNodeCount++
1✔
58
                        cpArchs.Insert(arch)
1✔
59
                }
1✔
60
        }
61

62
        // remove empty architectures
63
        workloadArchs.Delete("")
1✔
64
        cpArchs.Delete("")
1✔
65

1✔
66
        newValue := cpNodeCount >= 3
1✔
67
        changed := controlPlaneHighlyAvailable.Swap(newValue) != newValue
1✔
68

1✔
69
        newValue = cpNodeCount >= 1
1✔
70
        changed = controlPlaneNodeExist.Swap(newValue) != newValue || changed
1✔
71

1✔
72
        newValue = workerNodeCount >= 2
1✔
73
        changed = infrastructureHighlyAvailable.Swap(newValue) != newValue || changed
1✔
74

1✔
75
        changed = workloadArchitectures.set(workloadArchs) || changed
1✔
76
        changed = controlPlaneArchitectures.set(cpArchs) || changed
1✔
77

1✔
78
        return changed
1✔
79
}
80

81
func isWorkerNode(node corev1.Node) bool {
1✔
82
        _, exists := node.Labels[LabelNodeRoleWorker]
1✔
83
        return exists
1✔
84
}
1✔
85

86
func isWorkloadNodeFunc(hc *v1beta1.HyperConverged) func(corev1.Node) bool {
1✔
87
        if hasWorkloadRequirements(hc) {
2✔
88

1✔
89
                workloadMatcher := getWorkloadMatcher(hc)
1✔
90

1✔
91
                return func(node corev1.Node) bool {
2✔
92
                        matches, err := workloadMatcher.Match(&node)
1✔
93
                        if err != nil { // should not happen, because the validation webhook checks it, but just in case
1✔
NEW
94
                                return false
×
NEW
95
                        }
×
96
                        return matches
1✔
97
                }
98
        }
99

100
        return isWorkerNode
1✔
101
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc