Skip to content

Commit 19c78ff

Browse files
author
Ben Sykes
committed
Merge branch 'feat-node-taint-when-not-ready' into 'main'
feat(controller): added node taint for matching nodes without a PodCIDR See merge request cloudnative/go/cidr-allocator!19
2 parents 810a5cb + 9219fcc commit 19c78ff

File tree

17 files changed

+575
-41
lines changed

17 files changed

+575
-41
lines changed

.gitlab-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ helm:
2626
trigger:
2727
include:
2828
- project: cloudnative/gitlab/gitlabci/helm-chart
29-
ref: v3.x
29+
ref: v4.x
3030
file: .helm.gitlab-ci.yml
3131
strategy: depend

.golangci.yml

Lines changed: 343 additions & 0 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [v1.1.0] - 2024-02-22
9+
### Added
10+
- feat(taint): added custom node taint for matching Nodes that don't have a PodCIDR assigned
11+
812
## [v1.0.1] - 2024-02-16
913
### Fixed
1014
- fix(controller): bad handling of Event Recorder

cmd/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ var (
6363
probeAddr string
6464
// enableLeaderElection specifies whether or not leader election should be used for the controller manager
6565
enableLeaderElection bool
66-
// developmentLogging specifies whether to enable Development (Debug) logging for ZAP. Otherwise, Zap Production logging will be used
67-
developmentLogging bool
66+
// debugLogging specifies whether to enable Development (Debug) logging for ZAP. Otherwise, Zap Production logging will be used
67+
debugLogging bool
6868
// secureMetrics specifies whether the metrics endpoint is served over https
6969
secureMetrics bool
7070
// enableHTTP2 specifies that HTTP/2 will be enabled for the metrics and webhook servers (if exists)
@@ -98,8 +98,8 @@ func init() {
9898
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.",
9999
)
100100
flag.BoolVar(
101-
&developmentLogging,
102-
"dev-logging",
101+
&debugLogging,
102+
"debug",
103103
false,
104104
"Enable development logging",
105105
)
@@ -117,7 +117,7 @@ func init() {
117117
)
118118

119119
opts := zap.Options{
120-
Development: developmentLogging,
120+
Development: debugLogging,
121121
}
122122
opts.BindFlags(flag.CommandLine)
123123
flag.Parse()

config/default/manager_config_patch.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ spec:
1313
args:
1414
- --leader-elect
1515
- --disable-webhooks
16+
- --debug
1617
imagePullPolicy: Never

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/c-robinson/iplib v1.0.8
77
github.com/prometheus/client_golang v1.18.0
88
github.com/prometheus/client_model v0.5.0
9+
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
910
k8s.io/api v0.29.0
1011
k8s.io/apimachinery v0.29.0
1112
k8s.io/client-go v0.29.0
@@ -45,7 +46,6 @@ require (
4546
github.com/spf13/pflag v1.0.5 // indirect
4647
go.uber.org/multierr v1.11.0 // indirect
4748
go.uber.org/zap v1.26.0 // indirect
48-
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
4949
golang.org/x/net v0.20.0 // indirect
5050
golang.org/x/oauth2 v0.12.0 // indirect
5151
golang.org/x/sys v0.16.0 // indirect

install/kubernetes/cidr-allocator/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ keywords:
1010
- Networking
1111
- Node
1212

13-
version: 2.0.2
13+
version: 2.0.3
1414
kubeVersion: ">= 1.16.0-0"
15-
appVersion: "v1.0.1"
15+
appVersion: "v1.1.0"
1616

1717
maintainers:
1818
- name: Ben Sykes

install/kubernetes/cidr-allocator/README.md

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

33
A Helm chart to deploy the STATCAN CIDR-Allocator Controller and CRDs to a Kubernetes Cluster
44

5-
![Version: 2.0.2](https://img.shields.io/badge/Version-2.0.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.0.1](https://img.shields.io/badge/AppVersion-v1.0.1-informational?style=flat-square)
5+
![Version: 2.0.3](https://img.shields.io/badge/Version-2.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.1.0](https://img.shields.io/badge/AppVersion-v1.1.0-informational?style=flat-square)
66

77
A Helm chart to deploy the STATCAN CIDR-Allocator Controller and CRDs to a Kubernetes Cluster
88

internal/controller/nodecidrallocation_controller.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controller
1818

1919
import (
2020
"context"
21+
"errors"
2122

2223
corev1 "k8s.io/api/core/v1"
2324
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -28,6 +29,7 @@ import (
2829
ctrl "sigs.k8s.io/controller-runtime"
2930
"sigs.k8s.io/controller-runtime/pkg/client"
3031
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
32+
"sigs.k8s.io/controller-runtime/pkg/event"
3133
"sigs.k8s.io/controller-runtime/pkg/log"
3234
"sigs.k8s.io/controller-runtime/pkg/metrics"
3335
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -198,10 +200,9 @@ func (r *NodeCIDRAllocationReconciler) Reconcile(ctx context.Context, req ctrl.R
198200
return ctrl.Result{}, err
199201
}
200202

201-
rl.Info(
202-
"reconciling matching Nodes with NodeCIDRAllocation ...",
203-
"nodeCIDRAllocation", nodeCIDRAllocation.GetName(),
204-
)
203+
//
204+
// Begin allocation process
205+
//
205206

206207
freeSubnets := make(map[uint8][]string)
207208
for _, node := range matchingNodes.Items {
@@ -323,6 +324,9 @@ func (r *NodeCIDRAllocationReconciler) finalizeReconcile(ctx context.Context, no
323324
r.updateNodeCIDRAllocationStatus(ctx, nodeCIDRAllocation, nodes, err)
324325
r.updatePrometheusMetrics(ctx)
325326

327+
// remove or add Node taints according to allocation status of each matching node
328+
err = errors.Join(err, r.updateNodeTaints(ctx, nodes))
329+
326330
// passthrough for err (if non-nil) to the Reconcile Result
327331
return err
328332
}
@@ -384,6 +388,24 @@ func (r *NodeCIDRAllocationReconciler) updateNodeCIDRAllocationStatus(ctx contex
384388
}
385389
}
386390

391+
func (r *NodeCIDRAllocationReconciler) updateNodeTaints(ctx context.Context, nodes *corev1.NodeList) error {
392+
ntc := &NodeTaintClient{}
393+
for _, n := range nodes.Items {
394+
ntc.Handle(&n)
395+
if err := r.Client.Update(ctx, &n); err != nil {
396+
if apierrors.IsNotFound(err) {
397+
// node was removed after reconcile request was created - skip the Node
398+
continue
399+
}
400+
401+
// error trying to add the Node taint to the Node object
402+
return err
403+
}
404+
}
405+
406+
return nil
407+
}
408+
387409
// triggerNodeCIDRAllocationReconcileFromNodeChange is a mapping function which takes a Node object
388410
// and returns a list of reconciliation requests for all NodeCIDRAllocation resources that have a matching NodeSelector
389411
func (r *NodeCIDRAllocationReconciler) triggerNodeCIDRAllocationReconcileFromNodeChange(ctx context.Context, o client.Object) []reconcile.Request {
@@ -429,9 +451,12 @@ func (r *NodeCIDRAllocationReconciler) SetupWithManager(mgr ctrl.Manager) error
429451
Watches(
430452
&corev1.Node{},
431453
handler.EnqueueRequestsFromMapFunc(r.triggerNodeCIDRAllocationReconcileFromNodeChange),
432-
builder.WithPredicates(predicate.Or(
433-
predicate.LabelChangedPredicate{},
434-
)),
454+
builder.WithPredicates(predicate.Funcs{
455+
CreateFunc: func(_ event.CreateEvent) bool { return true },
456+
UpdateFunc: func(_ event.UpdateEvent) bool { return false },
457+
DeleteFunc: func(_ event.DeleteEvent) bool { return false },
458+
GenericFunc: func(_ event.GenericEvent) bool { return false },
459+
}),
435460
).
436461
Complete(r)
437462
}

internal/controller/taint.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
MIT License
3+
4+
Copyright (c) His Majesty the King in Right of Canada, as represented by the
5+
Minister responsible for Statistics Canada, 2024
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
*/
25+
26+
package controller
27+
28+
import (
29+
corev1 "k8s.io/api/core/v1"
30+
)
31+
32+
const (
33+
nodeTaintKey = "node.networking.statcan.gc.ca/network-unavailable"
34+
)
35+
36+
type NodeTainter interface {
37+
Handle(*corev1.Node)
38+
}
39+
40+
type NodeTaintClient struct{}
41+
42+
var _ NodeTainter = &NodeTaintClient{}
43+
44+
func (n *NodeTaintClient) Handle(node *corev1.Node) {
45+
if node.Spec.PodCIDR == "" {
46+
n.addNodeTaint(node)
47+
} else {
48+
n.removeNodeTaintIfExists(node)
49+
}
50+
}
51+
52+
func (n *NodeTaintClient) addNodeTaint(node *corev1.Node) {
53+
node.Spec.Taints = append(node.Spec.Taints, corev1.Taint{
54+
Key: nodeTaintKey,
55+
Value: "true",
56+
Effect: corev1.TaintEffectNoSchedule,
57+
})
58+
}
59+
60+
func (n *NodeTaintClient) removeNodeTaintIfExists(node *corev1.Node) {
61+
taints := []corev1.Taint{}
62+
for _, taint := range node.Spec.Taints {
63+
if taint.Key != nodeTaintKey {
64+
taints = append(taints, taint)
65+
}
66+
}
67+
68+
node.Spec.Taints = taints
69+
}

0 commit comments

Comments
 (0)