/
node_controller.go
151 lines (129 loc) · 3.94 KB
/
node_controller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package controllers
import (
"context"
"github.com/cybozu-go/topolvm"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
)
// NodeReconciler reconciles a Node object
type NodeReconciler struct {
client.Client
Log logr.Logger
}
// +kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch;update;patch
// +kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=get;list;watch;delete
// +kubebuilder:rbac:groups="storage.k8s.io",resources=storageclasses,verbs=get;list;watch
// Reconcile finalize Node
func (r *NodeReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
log := r.Log.WithValues("node", req.NamespacedName)
// your logic here
node := &corev1.Node{}
err := r.Get(ctx, req.NamespacedName, node)
switch {
case err == nil:
case apierrors.IsNotFound(err):
return ctrl.Result{}, nil
default:
return ctrl.Result{}, err
}
if node.DeletionTimestamp == nil {
return ctrl.Result{}, nil
}
needFinalize := false
for _, fin := range node.Finalizers {
if fin == topolvm.NodeFinalizer {
needFinalize = true
break
}
}
if !needFinalize {
return ctrl.Result{}, nil
}
if result, err := r.doFinalize(ctx, log, node); result.Requeue || err != nil {
return result, err
}
node2 := node.DeepCopy()
finalizers := node2.Finalizers[:0]
for _, fin := range node.Finalizers {
if fin == topolvm.NodeFinalizer {
continue
}
finalizers = append(finalizers, fin)
}
node2.Finalizers = finalizers
patch := client.MergeFrom(node)
if err := r.Patch(ctx, node2, patch); err != nil {
log.Error(err, "failed to remove finalizer", "name", node.Name)
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *NodeReconciler) targetStorageClasses(ctx context.Context) (map[string]bool, error) {
var scl storagev1.StorageClassList
if err := r.List(ctx, &scl); err != nil {
return nil, err
}
targets := make(map[string]bool)
for _, sc := range scl.Items {
if sc.Provisioner != topolvm.PluginName {
continue
}
targets[sc.Name] = true
}
return targets, nil
}
func (r *NodeReconciler) doFinalize(ctx context.Context, log logr.Logger, node *corev1.Node) (ctrl.Result, error) {
scs, err := r.targetStorageClasses(ctx)
if err != nil {
log.Error(err, "unable to fetch StorageClass")
return ctrl.Result{}, err
}
var pvcs corev1.PersistentVolumeClaimList
err = r.List(ctx, &pvcs, client.MatchingFields{KeySelectedNode: node.Name})
if err != nil {
log.Error(err, "unable to fetch PersistentVolumeClaimList")
return ctrl.Result{}, err
}
for _, pvc := range pvcs.Items {
if pvc.Spec.StorageClassName == nil {
continue
}
if !scs[*pvc.Spec.StorageClassName] {
continue
}
err = r.Delete(ctx, &pvc)
if err != nil {
log.Error(err, "unable to delete PVC", "name", pvc.Name, "namespace", pvc.Namespace)
return ctrl.Result{}, err
}
log.Info("deleted PVC", "name", pvc.Name, "namespace", pvc.Namespace)
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up Reconciler with Manager.
func (r *NodeReconciler) SetupWithManager(mgr ctrl.Manager) error {
err := mgr.GetFieldIndexer().IndexField(&corev1.PersistentVolumeClaim{}, KeySelectedNode, func(o runtime.Object) []string {
return []string{o.(*corev1.PersistentVolumeClaim).Annotations[AnnSelectedNode]}
})
if err != nil {
return err
}
pred := predicate.Funcs{
CreateFunc: func(event.CreateEvent) bool { return false },
DeleteFunc: func(event.DeleteEvent) bool { return false },
UpdateFunc: func(event.UpdateEvent) bool { return true },
GenericFunc: func(event.GenericEvent) bool { return false },
}
return ctrl.NewControllerManagedBy(mgr).
WithEventFilter(pred).
For(&corev1.Node{}).
Complete(r)
}