@@ -22,12 +22,14 @@ import (
22
22
"reflect"
23
23
"strings"
24
24
25
+ "github.com/pkg/errors"
25
26
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26
27
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27
28
28
29
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
29
30
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
30
31
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
32
+ "sigs.k8s.io/cluster-api/internal/util/compare"
31
33
"sigs.k8s.io/cluster-api/util/collections"
32
34
)
33
35
@@ -39,7 +41,7 @@ import (
39
41
// - mutated in-place (ex: NodeDrainTimeout)
40
42
// - are not dictated by KCP (ex: ProviderID)
41
43
// - are not relevant for the rollout decision (ex: failureDomain).
42
- func matchesMachineSpec (infraConfigs map [string ]* unstructured.Unstructured , machineConfigs map [string ]* bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) (string , bool ) {
44
+ func matchesMachineSpec (infraConfigs map [string ]* unstructured.Unstructured , machineConfigs map [string ]* bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) (string , bool , error ) {
43
45
mismatchReasons := []string {}
44
46
45
47
if ! collections .MatchesKubernetesVersion (kcp .Spec .Version )(machine ) {
@@ -50,7 +52,11 @@ func matchesMachineSpec(infraConfigs map[string]*unstructured.Unstructured, mach
50
52
mismatchReasons = append (mismatchReasons , fmt .Sprintf ("Machine version %q is not equal to KCP version %q" , machineVersion , kcp .Spec .Version ))
51
53
}
52
54
53
- if reason , matches := matchesKubeadmBootstrapConfig (machineConfigs , kcp , machine ); ! matches {
55
+ reason , matches , err := matchesKubeadmBootstrapConfig (machineConfigs , kcp , machine )
56
+ if err != nil {
57
+ return "" , false , errors .Wrapf (err , "failed to match Machine spec" )
58
+ }
59
+ if ! matches {
54
60
mismatchReasons = append (mismatchReasons , reason )
55
61
}
56
62
@@ -59,14 +65,14 @@ func matchesMachineSpec(infraConfigs map[string]*unstructured.Unstructured, mach
59
65
}
60
66
61
67
if len (mismatchReasons ) > 0 {
62
- return strings .Join (mismatchReasons , "," ), false
68
+ return strings .Join (mismatchReasons , "," ), false , nil
63
69
}
64
70
65
- return "" , true
71
+ return "" , true , nil
66
72
}
67
73
68
74
// NeedsRollout checks if a Machine needs to be rolled out and returns the reason why.
69
- func NeedsRollout (reconciliationTime , rolloutAfter * metav1.Time , rolloutBefore * controlplanev1.RolloutBefore , infraConfigs map [string ]* unstructured.Unstructured , machineConfigs map [string ]* bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) (string , bool ) {
75
+ func NeedsRollout (reconciliationTime , rolloutAfter * metav1.Time , rolloutBefore * controlplanev1.RolloutBefore , infraConfigs map [string ]* unstructured.Unstructured , machineConfigs map [string ]* bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) (string , bool , error ) {
70
76
rolloutReasons := []string {}
71
77
72
78
// Machines whose certificates are about to expire.
@@ -81,15 +87,19 @@ func NeedsRollout(reconciliationTime, rolloutAfter *metav1.Time, rolloutBefore *
81
87
}
82
88
83
89
// Machines that do not match with KCP config.
84
- if mismatchReason , matches := matchesMachineSpec (infraConfigs , machineConfigs , kcp , machine ); ! matches {
90
+ mismatchReason , matches , err := matchesMachineSpec (infraConfigs , machineConfigs , kcp , machine )
91
+ if err != nil {
92
+ return "" , false , errors .Wrapf (err , "failed to determine if Machine %s needs rollout" , machine .Name )
93
+ }
94
+ if ! matches {
85
95
rolloutReasons = append (rolloutReasons , mismatchReason )
86
96
}
87
97
88
98
if len (rolloutReasons ) > 0 {
89
- return fmt .Sprintf ("Machine %s needs rollout: %s" , machine .Name , strings .Join (rolloutReasons , "," )), true
99
+ return fmt .Sprintf ("Machine %s needs rollout: %s" , machine .Name , strings .Join (rolloutReasons , "," )), true , nil
90
100
}
91
101
92
- return "" , false
102
+ return "" , false , nil
93
103
}
94
104
95
105
// matchesTemplateClonedFrom checks if a Machine has a corresponding infrastructure machine that
@@ -130,50 +140,58 @@ func matchesTemplateClonedFrom(infraConfigs map[string]*unstructured.Unstructure
130
140
// matchesKubeadmBootstrapConfig checks if machine's KubeadmConfigSpec is equivalent with KCP's KubeadmConfigSpec.
131
141
// Note: Differences to the labels and annotations on the KubeadmConfig are not considered for matching
132
142
// criteria, because changes to labels and annotations are propagated in-place to KubeadmConfig.
133
- func matchesKubeadmBootstrapConfig (machineConfigs map [string ]* bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) (string , bool ) {
143
+ func matchesKubeadmBootstrapConfig (machineConfigs map [string ]* bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) (string , bool , error ) {
134
144
if machine == nil {
135
- return "Machine KubeadmConfig cannot be compared: Machine is nil" , false
145
+ return "Machine KubeadmConfig cannot be compared: Machine is nil" , false , nil
136
146
}
137
147
138
148
// Check if KCP and machine ClusterConfiguration matches, if not return
139
- if ! matchClusterConfiguration (kcp , machine ) {
140
- return "Machine ClusterConfiguration is outdated" , false
149
+ match , diff , err := matchClusterConfiguration (kcp , machine )
150
+ if err != nil {
151
+ return "" , false , errors .Wrapf (err , "failed to match KubeadmConfig" )
152
+ }
153
+ if ! match {
154
+ return fmt .Sprintf ("Machine KubeadmConfig ClusterConfiguration is outdated: diff: %s" , diff ), false , nil
141
155
}
142
156
143
157
bootstrapRef := machine .Spec .Bootstrap .ConfigRef
144
158
if bootstrapRef == nil {
145
159
// Missing bootstrap reference should not be considered as unmatching.
146
160
// This is a safety precaution to avoid selecting machines that are broken, which in the future should be remediated separately.
147
- return "" , true
161
+ return "" , true , nil
148
162
}
149
163
150
164
machineConfig , found := machineConfigs [machine .Name ]
151
165
if ! found {
152
166
// Return true here because failing to get KubeadmConfig should not be considered as unmatching.
153
167
// This is a safety precaution to avoid rolling out machines if the client or the api-server is misbehaving.
154
- return "" , true
168
+ return "" , true , nil
155
169
}
156
170
157
171
// Check if KCP and machine InitConfiguration or JoinConfiguration matches
158
172
// NOTE: only one between init configuration and join configuration is set on a machine, depending
159
173
// on the fact that the machine was the initial control plane node or a joining control plane node.
160
- if ! matchInitOrJoinConfiguration (machineConfig , kcp ) {
161
- return "Machine InitConfiguration or JoinConfiguration are outdated" , false
174
+ match , diff , err = matchInitOrJoinConfiguration (machineConfig , kcp )
175
+ if err != nil {
176
+ return "" , false , errors .Wrapf (err , "failed to match KubeadmConfig" )
177
+ }
178
+ if ! match {
179
+ return fmt .Sprintf ("Machine KubeadmConfig InitConfiguration or JoinConfiguration are outdated: diff: %s" , diff ), false , nil
162
180
}
163
181
164
- return "" , true
182
+ return "" , true , nil
165
183
}
166
184
167
185
// matchClusterConfiguration verifies if KCP and machine ClusterConfiguration matches.
168
186
// NOTE: Machines that have KubeadmClusterConfigurationAnnotation will have to match with KCP ClusterConfiguration.
169
187
// If the annotation is not present (machine is either old or adopted), we won't roll out on any possible changes
170
188
// made in KCP's ClusterConfiguration given that we don't have enough information to make a decision.
171
189
// Users should use KCP.Spec.RolloutAfter field to force a rollout in this case.
172
- func matchClusterConfiguration (kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) bool {
190
+ func matchClusterConfiguration (kcp * controlplanev1.KubeadmControlPlane , machine * clusterv1.Machine ) ( bool , string , error ) {
173
191
machineClusterConfigStr , ok := machine .GetAnnotations ()[controlplanev1 .KubeadmClusterConfigurationAnnotation ]
174
192
if ! ok {
175
193
// We don't have enough information to make a decision; don't' trigger a roll out.
176
- return true
194
+ return true , "" , nil
177
195
}
178
196
179
197
machineClusterConfig := & bootstrapv1.ClusterConfiguration {}
@@ -182,7 +200,7 @@ func matchClusterConfiguration(kcp *controlplanev1.KubeadmControlPlane, machine
182
200
// otherwise we won't be able to handle a nil ClusterConfiguration (that is serialized into "null").
183
201
// See https://github.com/kubernetes-sigs/cluster-api/issues/3353.
184
202
if err := json .Unmarshal ([]byte (machineClusterConfigStr ), & machineClusterConfig ); err != nil {
185
- return false
203
+ return false , "" , nil //nolint:nilerr // Intentionally not returning the error here
186
204
}
187
205
188
206
// If any of the compared values are nil, treat them the same as an empty ClusterConfiguration.
@@ -199,16 +217,20 @@ func matchClusterConfiguration(kcp *controlplanev1.KubeadmControlPlane, machine
199
217
machineClusterConfig .DNS = kcpLocalClusterConfiguration .DNS
200
218
201
219
// Compare and return.
202
- return reflect .DeepEqual (machineClusterConfig , kcpLocalClusterConfiguration )
220
+ match , diff , err := compare .Diff (machineClusterConfig , kcpLocalClusterConfiguration )
221
+ if err != nil {
222
+ return false , "" , errors .Wrapf (err , "failed to match ClusterConfiguration" )
223
+ }
224
+ return match , diff , nil
203
225
}
204
226
205
227
// matchInitOrJoinConfiguration verifies if KCP and machine InitConfiguration or JoinConfiguration matches.
206
228
// NOTE: By extension this method takes care of detecting changes in other fields of the KubeadmConfig configuration (e.g. Files, Mounts etc.)
207
- func matchInitOrJoinConfiguration (machineConfig * bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane ) bool {
229
+ func matchInitOrJoinConfiguration (machineConfig * bootstrapv1.KubeadmConfig , kcp * controlplanev1.KubeadmControlPlane ) ( bool , string , error ) {
208
230
if machineConfig == nil {
209
231
// Return true here because failing to get KubeadmConfig should not be considered as unmatching.
210
232
// This is a safety precaution to avoid rolling out machines if the client or the api-server is misbehaving.
211
- return true
233
+ return true , "" , nil
212
234
}
213
235
214
236
// takes the KubeadmConfigSpec from KCP and applies the transformations required
@@ -225,7 +247,11 @@ func matchInitOrJoinConfiguration(machineConfig *bootstrapv1.KubeadmConfig, kcp
225
247
// cleanups all the fields that are not relevant for the comparison.
226
248
cleanupConfigFields (kcpConfig , machineConfig )
227
249
228
- return reflect .DeepEqual (& machineConfig .Spec , kcpConfig )
250
+ match , diff , err := compare .Diff (& machineConfig .Spec , kcpConfig )
251
+ if err != nil {
252
+ return false , "" , errors .Wrapf (err , "failed to match InitConfiguration or JoinConfiguration" )
253
+ }
254
+ return match , diff , nil
229
255
}
230
256
231
257
// getAdjustedKcpConfig takes the KubeadmConfigSpec from KCP and applies the transformations required
0 commit comments