fix: installer action goroutine count

Signed-off-by: Terry Howe <terrylhowe@gmail.com>
pull/31200/head
Terry Howe 1 week ago
parent d601ce59a9
commit b12cd28503
No known key found for this signature in database

@ -30,6 +30,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"text/template" "text/template"
"time" "time"
@ -126,7 +127,8 @@ type Install struct {
TakeOwnership bool TakeOwnership bool
PostRenderer postrender.PostRenderer PostRenderer postrender.PostRenderer
// Lock to control raceconditions when the process receives a SIGTERM // Lock to control raceconditions when the process receives a SIGTERM
Lock sync.Mutex Lock sync.Mutex
goroutineCount atomic.Int32
} }
// ChartPathOptions captures common options used for controlling chart paths // ChartPathOptions captures common options used for controlling chart paths
@ -446,8 +448,10 @@ func (i *Install) performInstallCtx(ctx context.Context, rel *release.Release, t
resultChan := make(chan Msg, 1) resultChan := make(chan Msg, 1)
go func() { go func() {
i.goroutineCount.Add(1)
rel, err := i.performInstall(rel, toBeAdopted, resources) rel, err := i.performInstall(rel, toBeAdopted, resources)
resultChan <- Msg{rel, err} resultChan <- Msg{rel, err}
i.goroutineCount.Add(-1)
}() }()
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -458,6 +462,11 @@ func (i *Install) performInstallCtx(ctx context.Context, rel *release.Release, t
} }
} }
// getGoroutineCount return the number of running routines
func (i *Install) getGoroutineCount() int32 {
return i.goroutineCount.Load()
}
// isDryRun returns true if Upgrade is set to run as a DryRun // isDryRun returns true if Upgrade is set to run as a DryRun
func (i *Install) isDryRun() bool { func (i *Install) isDryRun() bool {
if i.DryRun || i.DryRunOption == "client" || i.DryRunOption == "server" || i.DryRunOption == "true" { if i.DryRun || i.DryRunOption == "client" || i.DryRunOption == "server" || i.DryRunOption == "true" {

@ -28,7 +28,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -330,8 +329,8 @@ func TestInstallRelease_WithChartAndDependencyParentNotes(t *testing.T) {
} }
rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
is.Equal("with-notes", rel.Name)
is.NoError(err) is.NoError(err)
is.Equal("with-notes", rel.Name)
is.Equal("parent", rel.Info.Notes) is.Equal("parent", rel.Info.Notes)
is.Equal(rel.Info.Description, "Install complete") is.Equal(rel.Info.Description, "Install complete")
} }
@ -349,8 +348,8 @@ func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) {
} }
rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
is.Equal("with-notes", rel.Name)
is.NoError(err) is.NoError(err)
is.Equal("with-notes", rel.Name)
// test run can return as either 'parent\nchild' or 'child\nparent' // test run can return as either 'parent\nchild' or 'child\nparent'
if !strings.Contains(rel.Info.Notes, "parent") && !strings.Contains(rel.Info.Notes, "child") { if !strings.Contains(rel.Info.Notes, "parent") && !strings.Contains(rel.Info.Notes, "child") {
t.Fatalf("Expected 'parent\nchild' or 'child\nparent', got '%s'", rel.Info.Notes) t.Fatalf("Expected 'parent\nchild' or 'child\nparent', got '%s'", rel.Info.Notes)
@ -454,9 +453,7 @@ func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) {
if err == nil { if err == nil {
t.Fatalf("Install should fail containing error: %s", expectedErr) t.Fatalf("Install should fail containing error: %s", expectedErr)
} }
if err != nil { is.Contains(err.Error(), expectedErr)
is.Contains(err.Error(), expectedErr)
}
} }
func TestInstallRelease_NoHooks(t *testing.T) { func TestInstallRelease_NoHooks(t *testing.T) {
@ -541,14 +538,14 @@ func TestInstallRelease_Wait(t *testing.T) {
instAction.WaitStrategy = kube.StatusWatcherStrategy instAction.WaitStrategy = kube.StatusWatcherStrategy
vals := map[string]interface{}{} vals := map[string]interface{}{}
goroutines := runtime.NumGoroutine() goroutines := instAction.getGoroutineCount()
res, err := instAction.Run(buildChart(), vals) res, err := instAction.Run(buildChart(), vals)
is.Error(err) is.Error(err)
is.Contains(res.Info.Description, "I timed out") is.Contains(res.Info.Description, "I timed out")
is.Equal(res.Info.Status, release.StatusFailed) is.Equal(res.Info.Status, release.StatusFailed)
is.Equal(goroutines, runtime.NumGoroutine()) is.Equal(goroutines, instAction.getGoroutineCount())
} }
func TestInstallRelease_Wait_Interrupted(t *testing.T) { func TestInstallRelease_Wait_Interrupted(t *testing.T) {
is := assert.New(t) is := assert.New(t)
@ -563,15 +560,15 @@ func TestInstallRelease_Wait_Interrupted(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context()) ctx, cancel := context.WithCancel(t.Context())
time.AfterFunc(time.Second, cancel) time.AfterFunc(time.Second, cancel)
goroutines := runtime.NumGoroutine() goroutines := instAction.getGoroutineCount()
_, err := instAction.RunWithContext(ctx, buildChart(), vals) _, err := instAction.RunWithContext(ctx, buildChart(), vals)
is.Error(err) is.Error(err)
is.Contains(err.Error(), "context canceled") is.Contains(err.Error(), "context canceled")
is.Equal(goroutines+1, runtime.NumGoroutine()) // installation goroutine still is in background is.Equal(goroutines+1, instAction.getGoroutineCount()) // installation goroutine still is in background
time.Sleep(10 * time.Second) // wait for goroutine to finish time.Sleep(10 * time.Second) // wait for goroutine to finish
is.Equal(goroutines, runtime.NumGoroutine()) is.Equal(goroutines, instAction.getGoroutineCount())
} }
func TestInstallRelease_WaitForJobs(t *testing.T) { func TestInstallRelease_WaitForJobs(t *testing.T) {
is := assert.New(t) is := assert.New(t)
@ -647,7 +644,7 @@ func TestInstallRelease_RollbackOnFailure_Interrupted(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context()) ctx, cancel := context.WithCancel(t.Context())
time.AfterFunc(time.Second, cancel) time.AfterFunc(time.Second, cancel)
goroutines := runtime.NumGoroutine() goroutines := instAction.getGoroutineCount()
res, err := instAction.RunWithContext(ctx, buildChart(), vals) res, err := instAction.RunWithContext(ctx, buildChart(), vals)
is.Error(err) is.Error(err)
@ -659,9 +656,9 @@ func TestInstallRelease_RollbackOnFailure_Interrupted(t *testing.T) {
_, err = instAction.cfg.Releases.Get(res.Name, res.Version) _, err = instAction.cfg.Releases.Get(res.Name, res.Version)
is.Error(err) is.Error(err)
is.Equal(err, driver.ErrReleaseNotFound) is.Equal(err, driver.ErrReleaseNotFound)
is.Equal(goroutines+1, runtime.NumGoroutine()) // installation goroutine still is in background is.Equal(goroutines+1, instAction.getGoroutineCount()) // installation goroutine still is in background
time.Sleep(10 * time.Second) // wait for goroutine to finish time.Sleep(10 * time.Second) // wait for goroutine to finish
is.Equal(goroutines, runtime.NumGoroutine()) is.Equal(goroutines, instAction.getGoroutineCount())
} }
func TestNameTemplate(t *testing.T) { func TestNameTemplate(t *testing.T) {

Loading…
Cancel
Save