Skip to content

Commit 126fe65

Browse files
authored
fix(apiserver): reset when extra args count stays the same (#1165)
1 parent baf7fd3 commit 126fe65

3 files changed

Lines changed: 116 additions & 13 deletions

File tree

‎internal/builders/controlplane/deployment.go‎

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"fmt"
1010
"path"
1111
"sort"
12-
"strconv"
1312
"strings"
1413

1514
appsv1 "k8s.io/api/apps/v1"
@@ -1217,24 +1216,21 @@ func (d Deployment) resetKubeAPIServerFlags(resource *appsv1.Deployment, tcp kam
12171216
if resource.GetAnnotations() == nil {
12181217
resource.SetAnnotations(map[string]string{})
12191218
}
1220-
// retrieving the current amount of extra flags, used as a sort of hash:
1219+
// retrieving the previous hash for the apiserver args:
12211220
// in case of non-matching values, removing all the args in order to perform a full reconciliation from a clean start.
1222-
var count int
1223-
1224-
if v, ok := resource.GetAnnotations()[apiServerFlagsAnnotation]; ok {
1225-
var err error
1226-
1227-
if count, err = strconv.Atoi(v); err != nil {
1228-
return
1229-
}
1221+
previousHash := resource.GetAnnotations()[apiServerFlagsAnnotation]
1222+
currentHash, err := utilities.CalculateStringSliceChecksum(tcp.Spec.ControlPlane.Deployment.ExtraArgs.APIServer)
1223+
if err != nil {
1224+
return
12301225
}
1231-
// there's a mismatch in the count from the previous hash: let's reset and store the desired extra args count.
1232-
if count != len(tcp.Spec.ControlPlane.Deployment.ExtraArgs.APIServer) {
1226+
1227+
// there's a mismatch between current and previous hash: let's reset and store the new hash.
1228+
if previousHash != currentHash {
12331229
_, index := utilities.HasNamedContainer(resource.Spec.Template.Spec.Containers, apiServerContainerName)
12341230
resource.Spec.Template.Spec.Containers[index].Args = []string{}
12351231
}
12361232

1237-
resource.GetAnnotations()[apiServerFlagsAnnotation] = fmt.Sprintf("%d", len(tcp.Spec.ControlPlane.Deployment.ExtraArgs.APIServer))
1233+
resource.GetAnnotations()[apiServerFlagsAnnotation] = currentHash
12381234
}
12391235

12401236
func (d Deployment) setNodeSelector(spec *corev1.PodSpec, tcp kamajiv1alpha1.TenantControlPlane) {

‎internal/utilities/checksum.go‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,25 @@ func SetObjectChecksum(obj client.Object, data any) {
3636
obj.SetAnnotations(annotations)
3737
}
3838

39+
// CalculateStringSliceChecksum calculates a checksum of slice, calculating the overall md5 of each values.
40+
// It takes order into account, as in :
41+
//
42+
// CalculateStringSliceChecksum([a, b]) != CalculateStringSliceChecksum([b, a]) // (if a != b)
43+
func CalculateStringSliceChecksum(data []string) (string, error) {
44+
h := md5.New()
45+
for _, s := range data {
46+
if _, err := h.Write([]byte(s)); err != nil {
47+
return "", err
48+
}
49+
// Add separator to avoid collisions (e.g., ["ab", "c"] vs ["a", "bc"])
50+
if _, err := h.Write([]byte{0}); err != nil {
51+
return "", err
52+
}
53+
}
54+
55+
return hex.EncodeToString(h.Sum([]byte{})), nil
56+
}
57+
3958
// CalculateMapChecksum orders the map according to its key, and calculating the overall md5 of the values.
4059
// It's expected to work with ConfigMap (map[string]string) and Secrets (map[string][]byte).
4160
func CalculateMapChecksum(data any) string {
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2022 Clastix Labs
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package utilities_test
5+
6+
import (
7+
"crypto/md5"
8+
"encoding/hex"
9+
"testing"
10+
11+
"github.com/clastix/kamaji/internal/utilities"
12+
)
13+
14+
func TestCalculateStringSliceChecksum(t *testing.T) {
15+
tests := []struct {
16+
name string
17+
data []string
18+
expected string
19+
}{
20+
{
21+
name: "empty slice",
22+
data: []string{},
23+
expected: md5Hex([]byte{}),
24+
},
25+
{
26+
name: "single element",
27+
data: []string{"hello"},
28+
expected: md5Hex([]byte("hello\x00")),
29+
},
30+
{
31+
name: "multiple elements",
32+
data: []string{"a", "b", "c"},
33+
expected: md5Hex([]byte("a\x00b\x00c\x00")),
34+
},
35+
}
36+
37+
for _, tt := range tests {
38+
t.Run(tt.name, func(t *testing.T) {
39+
got, err := utilities.CalculateStringSliceChecksum(tt.data)
40+
if err != nil {
41+
t.Fatalf("unexpected error: %v", err)
42+
}
43+
if got != tt.expected {
44+
t.Errorf("CalculateStringSliceChecksum(%v) = %q, want %q", tt.data, got, tt.expected)
45+
}
46+
})
47+
}
48+
}
49+
50+
func TestCalculateStringSliceChecksumOrder(t *testing.T) {
51+
// Test that different orders produce different checksums
52+
a, err := utilities.CalculateStringSliceChecksum([]string{"a", "b"})
53+
if err != nil {
54+
t.Fatalf("unexpected error: %v", err)
55+
}
56+
57+
b, err := utilities.CalculateStringSliceChecksum([]string{"b", "a"})
58+
if err != nil {
59+
t.Fatalf("unexpected error: %v", err)
60+
}
61+
62+
if a == b {
63+
t.Errorf("expected different checksums for [a,b] and [b,a], but got %q and %q", a, b)
64+
}
65+
}
66+
67+
func TestCalculateStringSliceChecksumCollision(t *testing.T) {
68+
// Test that ["ab", "c"] and ["a", "bc"] produce different checksums
69+
a, err := utilities.CalculateStringSliceChecksum([]string{"ab", "c"})
70+
if err != nil {
71+
t.Fatalf("unexpected error: %v", err)
72+
}
73+
74+
b, err := utilities.CalculateStringSliceChecksum([]string{"a", "bc"})
75+
if err != nil {
76+
t.Fatalf("unexpected error: %v", err)
77+
}
78+
79+
if a == b {
80+
t.Errorf("expected different checksums for [ab,c] and [a,bc], but got %q and %q", a, b)
81+
}
82+
}
83+
84+
func md5Hex(data []byte) string {
85+
hash := md5.Sum(data)
86+
87+
return hex.EncodeToString(hash[:])
88+
}

0 commit comments

Comments
 (0)