Skip to content

Commit f926244

Browse files
authored
Merge branch 'main' into ladithyav-readme-simplification-2
2 parents bf71edd + 8d55c0c commit f926244

160 files changed

Lines changed: 4026 additions & 2756 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
version: "2"
1616
run:
17-
go: "1.23"
17+
go: "1.27"
1818
modules-download-mode: readonly
1919
issues-exit-code: 1
2020
tests: false

.versions.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
# Language Versions
3333
languages:
34-
go: '1.26.3'
34+
go: '1.27.0'
3535
python: '3.13'
3636

3737
# Build & Package Management
@@ -41,7 +41,7 @@ build_tools:
4141

4242
# Go Development Tools
4343
go_tools:
44-
golangci_lint: 'v2.5.0'
44+
golangci_lint: 'v2.13.1'
4545
gotestsum: 'v1.13.0'
4646
gocover_cobertura: 'v1.4.0'
4747
setup_envtest: 'v0.0.0-20260119123727-a2de7e94d2dd'

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ When reporting issues:
5858
## Development Setup
5959

6060
**Prerequisites**:
61-
- Go 1.25+ (see `.versions.yaml` for exact version)
61+
- Go 1.27+ (see `.versions.yaml` for exact version)
6262
- Kubernetes cluster (for testing)
6363
- Docker (for container builds)
6464
- Make (for build targets)

DEVELOPMENT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ make show-versions
4040
### Prerequisites
4141

4242
**Core Tools** (required):
43-
- [Go 1.25+](https://golang.org/dl/) - See `.versions.yaml` for exact version
43+
- [Go 1.27+](https://golang.org/dl/) - See `.versions.yaml` for exact version
4444
- [Docker](https://docs.docker.com/get-docker/)
4545
- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
4646
- [Helm 3.0+](https://helm.sh/docs/intro/install/)

api/gen/go/device/v1alpha1/gpu.pb.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
module github.com/nvidia/nvsentinel/api
22

3-
go 1.26.0
4-
5-
toolchain go1.26.3
3+
go 1.27.0
64

75
require (
86
google.golang.org/grpc v1.83.1

commons/go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
module github.com/nvidia/nvsentinel/commons
22

3-
go 1.26.0
4-
5-
toolchain go1.26.3
3+
go 1.27.0
64

75
require (
86
github.com/BurntSushi/toml v1.6.0

commons/pkg/healthpub/metrics.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import (
1919
"github.com/prometheus/client_golang/prometheus/promauto"
2020
)
2121

22+
// labelMonitor is the agent-name label shared by every publisher counter.
23+
const labelMonitor = "monitor"
24+
2225
// Counters are registered against the default registry via promauto.
2326
// The `monitor` label is the agent name; `code` on sendsError is a gRPC
2427
// status-code string (bounded cardinality, ~17 values).
@@ -28,22 +31,22 @@ var (
2831
Name: "nvsentinel_health_events_publisher_skipped_pc_unavailable_total",
2932
Help: "Total health-event sends skipped because the platform-connector Unix socket was missing.",
3033
},
31-
[]string{"monitor"},
34+
[]string{labelMonitor},
3235
)
3336

3437
sendsSuccess = promauto.NewCounterVec(
3538
prometheus.CounterOpts{
3639
Name: "nvsentinel_health_events_publisher_sends_success_total",
3740
Help: "Total successful health-event sends.",
3841
},
39-
[]string{"monitor"},
42+
[]string{labelMonitor},
4043
)
4144

4245
sendsError = promauto.NewCounterVec(
4346
prometheus.CounterOpts{
4447
Name: "nvsentinel_health_events_publisher_sends_error_total",
4548
Help: "Total failed health-event sends after retries exhausted (excluding socket-missing skips).",
4649
},
47-
[]string{"monitor", "code"},
50+
[]string{labelMonitor, "code"},
4851
)
4952
)

commons/pkg/kubeclient/nodepatch.go

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"context"
2020
"encoding/json"
2121
"fmt"
22+
"reflect"
2223
"sync"
2324
"time"
2425

@@ -50,7 +51,15 @@ func (p *NodePatcher) Patch(
5051
cached *v1.Node,
5152
mutate func(*v1.Node) error,
5253
) (bool, error) {
53-
current, err := p.currentNode(ctx, nodes, nodeName, cached)
54+
var current *v1.Node
55+
56+
err := retry.OnError(nodePatchBackoff(), isRetryableNodePatchError, func() error {
57+
var err error
58+
59+
current, err = p.currentNode(ctx, nodes, nodeName, cached)
60+
61+
return err
62+
})
5463
if err != nil {
5564
return false, err
5665
}
@@ -85,11 +94,13 @@ func (p *NodePatcher) Patch(
8594
if errors.IsConflict(err) {
8695
patchErr := err
8796

88-
current, err = nodes.Get(ctx, nodeName, metav1.GetOptions{})
97+
refreshed, err := nodes.Get(ctx, nodeName, metav1.GetOptions{})
8998
if err != nil {
9099
return fmt.Errorf("refresh node %q after patch conflict: %w", nodeName, err)
91100
}
92101

102+
current = refreshed
103+
93104
return fmt.Errorf("patch node %q: %w", nodeName, patchErr)
94105
}
95106

@@ -134,8 +145,6 @@ func (p *NodePatcher) currentNode(
134145
return nil, fmt.Errorf("refresh node %q while pending write is not in cache: %w", nodeName, err)
135146
}
136147

137-
p.pendingVersions.CompareAndDelete(nodeName, writtenVersionValue)
138-
139148
return current, nil
140149
}
141150

@@ -156,40 +165,36 @@ func isRetryableNodePatchError(err error) bool {
156165
errors.IsServiceUnavailable(err)
157166
}
158167

159-
// NodeMergePatch builds an RFC 7386 JSON merge patch carrying the label and
160-
// annotation differences between original and modified. It returns a nil patch when
161-
// the two already agree, so callers can skip the write instead of spending an API
162-
// call on a no-op.
168+
// NodeMergePatch builds an RFC 7386 JSON merge patch carrying differences in labels,
169+
// annotations, taints, and unschedulable state. It returns a nil patch when the two
170+
// nodes already agree, so callers can skip a no-op write.
163171
//
164-
// CreateTwoWayMergePatch compares metadata-only projections of the two Nodes.
165-
// Excluding every other field from both inputs ensures an informer projection cannot
166-
// patch its gaps back over the live object.
172+
// CreateTwoWayMergePatch compares projections containing only the fields this helper
173+
// supports. Excluding every other field from both inputs ensures an informer
174+
// projection cannot patch its gaps back over the live object.
167175
//
168-
// Spec fields such as taints and unschedulable are deliberately out of scope: a merge
169-
// patch replaces a list wholesale, so patching taints from a projected Node whose Spec
170-
// had been cleared would silently drop every taint on the real object.
176+
// Taints are emitted only when the caller changed them. A projected Node whose Spec
177+
// is empty on both sides therefore cannot erase taints from the real object.
171178
func NodeMergePatch(original, modified *v1.Node) ([]byte, error) {
172-
originalMetadata := &v1.Node{
173-
ObjectMeta: metav1.ObjectMeta{
174-
Labels: original.Labels,
175-
Annotations: original.Annotations,
176-
},
177-
}
178-
modifiedMetadata := &v1.Node{
179-
ObjectMeta: metav1.ObjectMeta{
180-
Labels: modified.Labels,
181-
Annotations: modified.Annotations,
182-
},
179+
originalProjection := projectNodePatchableFields(original)
180+
modifiedProjection := projectNodePatchableFields(modified)
181+
182+
specChanged := !reflect.DeepEqual(original.Spec.Taints, modified.Spec.Taints) ||
183+
original.Spec.Unschedulable != modified.Spec.Unschedulable
184+
if specChanged {
185+
// Lists in spec, such as taints, are replaced wholesale. ResourceVersion
186+
// prevents a stale list from overwriting a concurrent update.
187+
modifiedProjection.ResourceVersion = original.ResourceVersion
183188
}
184189

185-
originalJSON, err := json.Marshal(originalMetadata)
190+
originalJSON, err := json.Marshal(originalProjection)
186191
if err != nil {
187-
return nil, fmt.Errorf("marshal original metadata for node %q: %w", original.Name, err)
192+
return nil, fmt.Errorf("marshal original patch projection for node %q: %w", original.Name, err)
188193
}
189194

190-
modifiedJSON, err := json.Marshal(modifiedMetadata)
195+
modifiedJSON, err := json.Marshal(modifiedProjection)
191196
if err != nil {
192-
return nil, fmt.Errorf("marshal modified metadata for node %q: %w", original.Name, err)
197+
return nil, fmt.Errorf("marshal modified patch projection for node %q: %w", original.Name, err)
193198
}
194199

195200
patch, err := strategicpatch.CreateTwoWayMergePatch(originalJSON, modifiedJSON, v1.Node{})
@@ -203,3 +208,18 @@ func NodeMergePatch(original, modified *v1.Node) ([]byte, error) {
203208

204209
return patch, nil
205210
}
211+
212+
// projectNodePatchableFields restricts patch generation to the Node fields this
213+
// helper intentionally supports, preventing callbacks from patching unrelated fields.
214+
func projectNodePatchableFields(node *v1.Node) *v1.Node {
215+
return &v1.Node{
216+
ObjectMeta: metav1.ObjectMeta{
217+
Labels: node.Labels,
218+
Annotations: node.Annotations,
219+
},
220+
Spec: v1.NodeSpec{
221+
Taints: node.Spec.Taints,
222+
Unschedulable: node.Spec.Unschedulable,
223+
},
224+
}
225+
}

0 commit comments

Comments
 (0)