Skip to content
Open
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
32f0c69
fix: enhance handleBatchConfigEvent to include module state changes
May 23, 2025
32c1f58
fix: add mutex locks to ensure thread-safe access to module configura…
May 23, 2025
50ccda6
fix: ensure initialization of checksums map in Add, Remove, and Set m…
May 23, 2025
eed8a44
fix: log YAML marshal errors in mergeValues and return actual error i…
May 23, 2025
3a1e888
fix: ensure timer is stopped and handle errors gracefully in Start me…
May 23, 2025
afb3608
fix: replace log with logger interface for consistent logging in helm…
May 23, 2025
fcbde60
fix: improve label handling and error management in LibClient methods
May 23, 2025
2755a8d
fix: enhance label handling in PostRenderer by using maps.Copy for ex…
May 23, 2025
4310508
fix: remove unnecessary locking in handleConfigEvent for module updates
May 23, 2025
6bcd546
fix: ensure proper locking in handleBatchConfigEvent for safe config …
May 23, 2025
e5b5d4c
fix: update driver name in upgradeRelease and simplify rollbackLatest…
May 23, 2025
b67cb26
fix: remove redundant comment in listResources function for clarity
May 23, 2025
3b2e0df
fix: remove unnecessary comments in ResourcesMonitor for cleaner code
May 24, 2025
969cc3b
fix: ensure proper locking in handleBatchConfigEvent for safe config …
May 24, 2025
f93857d
refactor: extract ensureName method for better code clarity in Checksums
May 24, 2025
32dccc4
fix: skip entries with empty keys in parseSetValues for improved data…
May 24, 2025
2614e35
fix: ensure proper locking in handleConfigEvent for safe access to cu…
May 24, 2025
297fa32
fix: correct typo in debug log message for Helm resource manager
May 24, 2025
69f37cb
fix: trim whitespace from values in parseSetValues for improved data …
May 24, 2025
b323cad
fix: explicitly stop timer in Start method before goroutine exit
May 24, 2025
363fd84
fix: improve locking mechanism in handleConfigEvent for safe event em…
May 24, 2025
1191816
fix: reduce contention by loading module config outside the lock in U…
May 24, 2025
17d235a
fix: ensure proper unlocking in WithDefaultNamespace method
May 24, 2025
609d681
fix: remove redundant comment for logger in helmResourcesManager struct
May 24, 2025
a3b68ad
fix: simplify event emission in handleConfigEvent method
May 25, 2025
5b851d9
fix: improve event handling and locking in KubeConfigManager
May 25, 2025
26b333d
fix: enhance documentation for parseSetValues function
May 25, 2025
a942926
fix: improve locking mechanism and event handling in handleBatchConfi…
May 25, 2025
180a6f4
fix: refactor event handling in handleBatchConfigEvent method
May 25, 2025
9354a5d
fix: remove redundant comment about timer stop in Start method
May 25, 2025
3c1b1a3
fix: improve locking mechanism by replacing Mutex with RWMutex in Kub…
May 25, 2025
a0b61cf
feat: implement event handling methods for KubeConfigManager
May 25, 2025
066f810
fix: update currentConfig handling in KubeConfigManager methods
May 25, 2025
7da6acc
fix: rename mutex variable for clarity in helmResourcesManager
May 25, 2025
849e88f
fix: simplify module maintenance state updates using maps.Copy in han…
May 25, 2025
77f461b
fix: ensure thread-safety in Checksums methods by adding mutex locks
May 25, 2025
9d84041
fix: add mutex lock notes for thread safety in config handling methods
May 25, 2025
ab0e65f
fix: rename mutex variable for clarity in KubeConfigManager
May 25, 2025
80d22ba
test: add unit tests for parseSetValues function
May 25, 2025
e4d36c2
fix: add nil check for extraLabels before copying in PostRenderer
May 25, 2025
3b9fc20
fix: update comment in Copy method to English for clarity
May 25, 2025
e5945c9
fix: update lock note in handleGlobalConfig method for accuracy
May 25, 2025
124bae7
fix: update lock note in isGlobalChanged method for accuracy
May 25, 2025
131b533
fix: update comments in UpdateModuleConfig method for clarity
May 25, 2025
6cf20b4
fix: update lock note in handleModuleDelete and handleModuleUpdate me…
May 25, 2025
a4b7667
fix: add nil check for labels in WithExtraLabels method
May 25, 2025
41773d2
fix: update lock note for processBatchDeletedModules method and impro…
May 25, 2025
e5c1302
fix: update comments in handleConfigEvent and handleBatchConfigEvent …
May 25, 2025
7889132
fix: prevent removal of known checksums on error and use atomic opera…
May 25, 2025
4d20f75
fix: improve task history logging in Test_HandleConvergeModules_globa…
May 25, 2025
12a7cf6
fix: update logging message format in NewKubeConfigManager for better…
May 25, 2025
6bac4d5
fix: improve comments in sendEventIfNeeded and handleConfigEvent meth…
May 25, 2025
4b98136
fix: implement thread-safe TaskHandleHistory in tests to prevent race…
May 25, 2025
5629cf2
fix: remove unused taskInfo struct to clean up code
May 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 39 additions & 15 deletions pkg/addon-operator/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@

res.helmClient.ReleaseNames = []string{moduleToPurge, moduleToDelete}

type taskInfo struct {

Check failure on line 245 in pkg/addon-operator/operator_test.go

View workflow job for this annotation

GitHub Actions / Run Go linters

type taskInfo is unused (unused)
taskType sh_task.TaskType
bindingType BindingType
moduleName string
Expand All @@ -250,7 +250,11 @@
spawnerTaskPhase string
}

taskHandleHistory := make([]taskInfo, 0)
// Use thread-safe TaskHandleHistory
taskHandleHistory := TaskHandleHistory{
taskHandleHistory: make([]TaskInfo, 0),
historyMu: sync.Mutex{},
}
op.engine.TaskQueues.GetMain().WithHandler(func(ctx context.Context, tsk sh_task.Task) queue.TaskResult {
// Put task info to history.
hm := task.HookMetadataAccessor(tsk)
Expand All @@ -261,7 +265,7 @@
case task.ModuleRun:
phase = string(op.ModuleManager.GetModule(hm.ModuleName).GetPhase())
}
taskHandleHistory = append(taskHandleHistory, taskInfo{
taskHandleHistory.Add(&TaskInfo{
taskType: tsk.GetType(),
bindingType: hm.BindingType,
moduleName: hm.ModuleName,
Expand Down Expand Up @@ -338,7 +342,8 @@
{task.ConvergeModules, "", "", string(converge.WaitAfterAll)},
}

for i, historyInfo := range taskHandleHistory {
historyItems := taskHandleHistory.Get()
for i, historyInfo := range historyItems {
if i >= len(historyExpects) {
break
}
Expand Down Expand Up @@ -380,8 +385,10 @@
canHandleTasks := make(chan struct{})
triggerPause := true

// Use thread-safe TaskHandleHistory
taskHandleHistory := TaskHandleHistory{
taskHandleHistory: make([]TaskInfo, 0),
historyMu: sync.Mutex{},
}

op.engine.TaskQueues.GetMain().WithHandler(func(ctx context.Context, tsk sh_task.Task) queue.TaskResult {
Expand All @@ -395,22 +402,37 @@
convergeEvent = tsk.GetProp(converge.ConvergeEventProp).(converge.ConvergeEvent)
case task.ModuleRun:
if triggerPause {
// First add information about the current task to history
taskHandleHistory.Add(&TaskInfo{
id: tsk.GetId(),
taskType: tsk.GetType(),
bindingType: hm.BindingType,
moduleName: hm.ModuleName,
hookName: hm.HookName,
spawnerTaskPhase: string(op.ModuleManager.GetModule(hm.ModuleName).GetPhase()),
convergeEvent: convergeEvent,
})

// Then start the asynchronous operation
close(canChangeConfigMap)
<-canHandleTasks
triggerPause = false
}
phase = string(op.ModuleManager.GetModule(hm.ModuleName).GetPhase())
}

taskHandleHistory.Add(&TaskInfo{
id: tsk.GetId(),
taskType: tsk.GetType(),
bindingType: hm.BindingType,
moduleName: hm.ModuleName,
hookName: hm.HookName,
spawnerTaskPhase: phase,
convergeEvent: convergeEvent,
})
// Add information to history only if we haven't added it yet
if !triggerPause || tsk.GetType() != task.ModuleRun {
taskHandleHistory.Add(&TaskInfo{
id: tsk.GetId(),
taskType: tsk.GetType(),
bindingType: hm.BindingType,
moduleName: hm.ModuleName,
hookName: hm.HookName,
spawnerTaskPhase: phase,
convergeEvent: convergeEvent,
})
}

// Handle it.
return op.TaskService.Handle(ctx, tsk)
Expand Down Expand Up @@ -443,16 +465,17 @@
g.Eventually(convergeDone(op), "30s", "200ms").Should(BeTrue())

hasReloadAllInStandby := false
for i, tsk := range taskHandleHistory.Get() {
historyItems := taskHandleHistory.Get()
for i, tsk := range historyItems {
// if i < ignoreTasksCount {
// continue
//}
if tsk.taskType != task.ApplyKubeConfigValues {
continue
}

g.Expect(taskHandleHistory.Len() > i+1).Should(BeTrue(), "history should not end on ApplyKubeConfigValues")
next := taskHandleHistory.Get()[i+1]
g.Expect(i+1 < len(historyItems)).Should(BeTrue(), "history should not end on ApplyKubeConfigValues")
next := historyItems[i+1]
g.Expect(next.convergeEvent).Should(Equal(converge.ReloadAllModules))
g.Expect(next.spawnerTaskPhase).Should(Equal(string(converge.StandBy)))
hasReloadAllInStandby = true
Expand Down Expand Up @@ -547,6 +570,7 @@

taskHandleHistory := TaskHandleHistory{
taskHandleHistory: make([]TaskInfo, 0),
historyMu: sync.Mutex{},
}

op.engine.TaskQueues.GetMain().WithHandler(func(ctx context.Context, tsk sh_task.Task) queue.TaskResult {
Expand Down
58 changes: 42 additions & 16 deletions pkg/helm/helm3lib/helm3lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log/slog"
"maps"
"os"
"sort"
"strconv"
Expand Down Expand Up @@ -81,8 +82,11 @@ func (h *LibClient) WithLogLabels(logLabels map[string]string) {
}

func (h *LibClient) WithExtraLabels(labels map[string]string) {
for k, v := range labels {
h.labels[k] = v
if h.labels == nil {
h.labels = make(map[string]string)
}
if labels != nil {
maps.Copy(h.labels, labels)
}
}

Expand Down Expand Up @@ -210,13 +214,7 @@ func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesP
}

if len(setValues) > 0 {
m := make(map[string]interface{})
for _, sv := range setValues {
arr := strings.Split(sv, "=")
if len(arr) == 2 {
m[arr[0]] = arr[1]
}
}
m := parseSetValues(setValues)
resultValues = chartutil.CoalesceTables(resultValues, m)
}

Expand Down Expand Up @@ -447,6 +445,40 @@ func (h *LibClient) ListReleasesNames() ([]string, error) {
return releases, nil
}

// parseSetValues parses setValues slice into a map, supporting '=' in values.
// Each entry should be in the format "key=value".
//
// Behavior:
// - Supports '=' characters within values (only the first '=' is used as delimiter)
// - Trims whitespace from both keys and values
// - Skips entries that don't contain '=' (malformed entries)
// - Skips entries with empty keys (after trimming whitespace)
// - For duplicate keys, the last occurrence in the slice takes precedence
// - Returns an empty map if setValues is nil or empty
//
// Examples:
// - "key=value" -> {"key": "value"}
// - "key=value=with=equals" -> {"key": "value=with=equals"}
// - " key = value " -> {"key": "value"}
// - "=value" -> skipped (empty key)
// - "key" -> skipped (no '=' delimiter)
// - ["key=value1", "key=value2"] -> {"key": "value2"} (last wins)
func parseSetValues(setValues []string) map[string]any {
m := make(map[string]any)
for _, sv := range setValues {
arr := strings.SplitN(sv, "=", 2)
if len(arr) == 2 {
key := strings.TrimSpace(arr[0])
if key == "" {
// skip entries with empty key
continue
}
m[key] = strings.TrimSpace(arr[1])
}
}
return m
}

func (h *LibClient) Render(releaseName, chartName string, valuesPaths, setValues []string, namespace string, debug bool) (string, error) {
chart, err := loader.Load(chartName)
if err != nil {
Expand All @@ -465,13 +497,7 @@ func (h *LibClient) Render(releaseName, chartName string, valuesPaths, setValues
}

if len(setValues) > 0 {
m := make(map[string]interface{})
for _, sv := range setValues {
arr := strings.Split(sv, "=")
if len(arr) == 2 {
m[arr[0]] = arr[1]
}
}
m := parseSetValues(setValues)
resultValues = chartutil.CoalesceTables(resultValues, m)
}

Expand Down
73 changes: 73 additions & 0 deletions pkg/helm/helm3lib/helm3lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,79 @@ func TestLastReleaseStatusReturnsErrorForNonExistingRelease(t *testing.T) {
g.Expect(err).Should(HaveOccurred(), "should return an error for a non-existing release")
}

func TestParseSetValues(t *testing.T) {
g := NewWithT(t)

tests := []struct {
name string
input []string
expected map[string]any
}{
{
name: "empty input returns empty map",
input: nil,
expected: map[string]any{},
},
{
name: "empty slice returns empty map",
input: []string{},
expected: map[string]any{},
},
{
name: "single key-value",
input: []string{"foo=bar"},
expected: map[string]any{"foo": "bar"},
},
{
name: "multiple key-values",
input: []string{"foo=bar", "baz=qux"},
expected: map[string]any{"foo": "bar", "baz": "qux"},
},
{
name: "value with equals sign",
input: []string{"foo=bar=baz"},
expected: map[string]any{"foo": "bar=baz"},
},
{
name: "key and value with whitespace",
input: []string{" foo = bar "},
expected: map[string]any{"foo": "bar"},
},
{
name: "entry with no equals is skipped",
input: []string{"foo"},
expected: map[string]any{},
},
{
name: "entry with empty key is skipped",
input: []string{"=bar"},
expected: map[string]any{},
},
{
name: "entry with empty value",
input: []string{"foo="},
expected: map[string]any{"foo": ""},
},
{
name: "duplicate keys, last wins",
input: []string{"foo=bar", "foo=baz"},
expected: map[string]any{"foo": "baz"},
},
{
name: "mixed valid and invalid entries",
input: []string{"foo=bar", "baz", "=qux", "key=value=with=equals", " spaced = spaced value "},
expected: map[string]any{"foo": "bar", "key": "value=with=equals", "spaced": "spaced value"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
result := parseSetValues(tt.input)
g.Expect(result).To(Equal(tt.expected))
})
}
}

func initHelmClient(t *testing.T) *LibClient {
g := NewWithT(t)
err := Init(&Options{
Expand Down
8 changes: 6 additions & 2 deletions pkg/helm/post_renderer/post_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package post_renderer
import (
"bytes"
"fmt"
"maps"

"sigs.k8s.io/kustomize/kyaml/kio"
)
Expand All @@ -29,8 +30,11 @@ func (p *PostRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, erro

for _, node := range nodes {
labels := node.GetLabels()
for k, v := range p.extraLabels {
labels[k] = v
if labels == nil {
labels = make(map[string]string)
}
if p.extraLabels != nil {
maps.Copy(labels, p.extraLabels)
}

_ = node.SetLabels(labels)
Expand Down
Loading
Loading