mirror of
https://github.com/helmfile/helmfile.git
synced 2026-09-30 08:15:58 +02:00
Toolchain and CI:
- Bump go directive from 1.26.8 to 1.27.1 (go.mod)
- Use golang:1.27-alpine builder images in all Dockerfiles
- Bump golangci-lint to v2.13.2 (first release with go1.27 support)
- Add CI gate: `go fix -diff` fails when outdated Go patterns are
detected (locally: `make check-modernize`)
Note: darwin binaries now require macOS 13 or later.
Lint fixes required by golangci-lint v2.13.2:
- goconst: ignore tests (all 436 findings were test-only; goconst
got stricter since v2.12 and this option was added for it)
- openai.go: keep deprecated MaxTokens deliberately with a nolint
rationale (max_tokens is the only form universally supported by
OpenAI-compatible backends like One-API, LiteLLM, Ollama shim)
- state.go: drop always-nil flags param from appendChartVersionFlags
(renamed to chartVersionFlags, unparam)
Modernization (go fix ./..., 62 files):
- interface{} -> any, maps.Copy, strings.SplitSeq, range-over-int,
builtin min/max, slices.Contains/ContainsFunc/Sort, WaitGroup.Go,
reflect.Type.Fields(), new(expr)
- exit_error.go: strings.Builder + fmt.Fprintf instead of string
concatenation and WriteString(fmt.Sprintf(...)) (QF1012)
- chart_dependency.go: strings.CutLast for OCI dependency helpers
Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: Claude <noreply@anthropic.com>
155 lines
4.8 KiB
Go
155 lines
4.8 KiB
Go
package state
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/helmfile/helmfile/pkg/filesystem"
|
|
)
|
|
|
|
// TestChartifyTempDirCleanup verifies that chartify output directories tracked
|
|
// via addChartifyTempDir are removed by CleanupChartifyTempDirs, including the
|
|
// parent temp directory when it becomes empty. Regression test for issue #1799.
|
|
func TestChartifyTempDirCleanup(t *testing.T) {
|
|
st := &HelmState{
|
|
logger: zap.NewNop().Sugar(),
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
// Simulate a chartify output directory: /tmp/chartify<rand>/<id>
|
|
parent := t.TempDir()
|
|
dir := filepath.Join(parent, "release-name-12345")
|
|
require.NoError(t, os.MkdirAll(dir, 0755))
|
|
// Add some content so RemoveAll has something to remove
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "Chart.yaml"), []byte("apiVersion: v2\n"), 0644))
|
|
|
|
st.addChartifyTempDir(dir)
|
|
|
|
// Before cleanup, the directory exists
|
|
_, err := os.Stat(dir)
|
|
assert.NoError(t, err)
|
|
|
|
st.CleanupChartifyTempDirs()
|
|
|
|
// After cleanup, the chartify output dir is gone
|
|
_, err = os.Stat(dir)
|
|
assert.True(t, os.IsNotExist(err), "chartify output dir should be removed")
|
|
|
|
// The parent temp dir should also be removed since it is now empty
|
|
_, err = os.Stat(parent)
|
|
assert.True(t, os.IsNotExist(err), "empty parent temp dir should be removed")
|
|
}
|
|
|
|
// TestChartifyTempDirCleanupMultipleReleases verifies that cleanup handles
|
|
// multiple chartify output directories correctly, including shared parents.
|
|
func TestChartifyTempDirCleanupMultipleReleases(t *testing.T) {
|
|
st := &HelmState{
|
|
logger: zap.NewNop().Sugar(),
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
// Two chartify output dirs under the SAME parent (simulating concurrent
|
|
// releases that share /tmp/chartify<rand>/)
|
|
parent := t.TempDir()
|
|
dir1 := filepath.Join(parent, "release-a-111")
|
|
dir2 := filepath.Join(parent, "release-b-222")
|
|
require.NoError(t, os.MkdirAll(dir1, 0755))
|
|
require.NoError(t, os.MkdirAll(dir2, 0755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir1, "Chart.yaml"), []byte("apiVersion: v2\n"), 0644))
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir2, "Chart.yaml"), []byte("apiVersion: v2\n"), 0644))
|
|
|
|
st.addChartifyTempDir(dir1)
|
|
st.addChartifyTempDir(dir2)
|
|
|
|
st.CleanupChartifyTempDirs()
|
|
|
|
for _, d := range []string{dir1, dir2} {
|
|
_, err := os.Stat(d)
|
|
assert.True(t, os.IsNotExist(err), "chartify output dir %s should be removed", d)
|
|
}
|
|
// Parent should also be cleaned since both children are gone
|
|
_, err := os.Stat(parent)
|
|
assert.True(t, os.IsNotExist(err), "parent should be removed when all children removed")
|
|
}
|
|
|
|
// TestChartifyTempDirCleanupNoop verifies CleanupChartifyTempDirs is safe to call
|
|
// when no directories were tracked (e.g. no chartify runs occurred).
|
|
func TestChartifyTempDirCleanupNoop(t *testing.T) {
|
|
st := &HelmState{
|
|
logger: zap.NewNop().Sugar(),
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
// Should not panic
|
|
assert.NotPanics(t, func() {
|
|
st.CleanupChartifyTempDirs()
|
|
})
|
|
}
|
|
|
|
// TestChartifyTempDirCleanupIdempotent verifies that calling cleanup twice is safe.
|
|
func TestChartifyTempDirCleanupIdempotent(t *testing.T) {
|
|
st := &HelmState{
|
|
logger: zap.NewNop().Sugar(),
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "Chart.yaml"), []byte("apiVersion: v2\n"), 0644))
|
|
|
|
st.addChartifyTempDir(dir)
|
|
st.CleanupChartifyTempDirs()
|
|
// Second call should be a no-op, not an error
|
|
assert.NotPanics(t, func() {
|
|
st.CleanupChartifyTempDirs()
|
|
})
|
|
}
|
|
|
|
// TestChartifyTempDirConcurrentTracking verifies that concurrent calls to
|
|
// addChartifyTempDir (as happens with parallel chart-preparation workers) do
|
|
// not lose any tracked directories. The tracker must be pre-initialized before
|
|
// concurrent access — see PrepareCharts. Regression test for issue #1799.
|
|
func TestChartifyTempDirConcurrentTracking(t *testing.T) {
|
|
st := &HelmState{
|
|
logger: zap.NewNop().Sugar(),
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
// Pre-initialize the tracker as PrepareCharts does before launching workers.
|
|
st.chartifyTempDirs = &chartifyTempDirTracker{}
|
|
|
|
const n = 50
|
|
dirs := make([]string, n)
|
|
for i := range dirs {
|
|
dirs[i] = t.TempDir()
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
for i := range n {
|
|
wg.Add(1)
|
|
go func(dir string) {
|
|
defer wg.Done()
|
|
st.addChartifyTempDir(dir)
|
|
}(dirs[i])
|
|
}
|
|
wg.Wait()
|
|
|
|
// All n directories must be tracked — none lost to a race.
|
|
st.chartifyTempDirs.mu.Lock()
|
|
got := len(st.chartifyTempDirs.dirs)
|
|
st.chartifyTempDirs.mu.Unlock()
|
|
assert.Equal(t, n, got, "all concurrently tracked dirs must be present")
|
|
|
|
// Cleanup should remove all of them.
|
|
st.CleanupChartifyTempDirs()
|
|
for i, d := range dirs {
|
|
_, err := os.Stat(d)
|
|
assert.True(t, os.IsNotExist(err), "dir %d (%s) should have been removed", i, d)
|
|
}
|
|
}
|