diff --git a/go.mod b/go.mod index 8f229bf4a4..8fe7dfdc2b 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( k8s.io/apimachinery v0.34.1 k8s.io/client-go v0.34.1 k8s.io/component-base v0.34.1 - sigs.k8s.io/controller-runtime v0.21.0 + sigs.k8s.io/controller-runtime v0.22.1 sigs.k8s.io/yaml v1.6.0 ) diff --git a/go.sum b/go.sum index d121a52b61..6a974b9be4 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOP k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg= +sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= diff --git a/internal/controller/runtime/client.go b/internal/controller/runtime/client.go index 4cc05c9835..3e2cb681d4 100644 --- a/internal/controller/runtime/client.go +++ b/internal/controller/runtime/client.go @@ -7,6 +7,7 @@ package runtime import ( "context" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -26,6 +27,7 @@ var _ client.Reader = ClientReader{} // Types that implement single methods of the [client.Writer] interface. type ( + ClientApply func(context.Context, runtime.ApplyConfiguration, ...client.ApplyOption) error ClientCreate func(context.Context, client.Object, ...client.CreateOption) error ClientDelete func(context.Context, client.Object, ...client.DeleteOption) error ClientPatch func(context.Context, client.Object, client.Patch, ...client.PatchOption) error @@ -33,13 +35,17 @@ type ( ClientUpdate func(context.Context, client.Object, ...client.UpdateOption) error ) -// ClientWriter implements [client.Writer] by composing assignable functions. type ClientWriter struct { ClientCreate ClientDelete ClientDeleteAll ClientPatch ClientUpdate + ClientApply +} + +func (fn ClientApply) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.ApplyOption) error { + return fn(ctx, obj, opts...) } var _ client.Writer = ClientWriter{} diff --git a/internal/controller/runtime/pod_client.go b/internal/controller/runtime/pod_client.go index 4122303bf5..7397498367 100644 --- a/internal/controller/runtime/pod_client.go +++ b/internal/controller/runtime/pod_client.go @@ -31,7 +31,7 @@ func newPodClient(config *rest.Config) (rest.Interface, error) { if err != nil { return nil, err } - return apiutil.RESTClientForGVK(gvk, false, config, codecs, httpClient) + return apiutil.RESTClientForGVK(gvk, false, false, config, codecs, httpClient) } // +kubebuilder:rbac:groups="",resources="pods/exec",verbs={create} diff --git a/internal/upgradecheck/http_test.go b/internal/upgradecheck/http_test.go index c50b92c41e..1b62770600 100644 --- a/internal/upgradecheck/http_test.go +++ b/internal/upgradecheck/http_test.go @@ -11,6 +11,8 @@ import ( "fmt" "io" "net/http" + "os" + "runtime" "strings" "testing" "time" @@ -163,18 +165,46 @@ func TestCheckForUpgradesScheduler(t *testing.T) { cfg := &rest.Config{Host: server.URL} t.Run("panic from checkForUpgrades doesn't bubble up", func(t *testing.T) { + // Debug: Print comprehensive environment information + t.Logf("=== ENVIRONMENT DEBUG INFO ===") + t.Logf("Go Version: %s", runtime.Version()) + t.Logf("GOOS: %s", runtime.GOOS) + t.Logf("GOARCH: %s", runtime.GOARCH) + t.Logf("NumCPU: %d", runtime.NumCPU()) + t.Logf("Compiler: %s", runtime.Compiler) + + // Log relevant environment variables + envVars := []string{ + "CI", "GITHUB_ACTIONS", "GITHUB_WORKFLOW", "GITHUB_RUN_ID", + "RUNNER_OS", "RUNNER_ARCH", "RUNNER_TEMP", + "GOMAXPROCS", "GODEBUG", "GOTRACEBACK", + "TEST_TMPDIR", "TMPDIR", "TMP", "TEMP", + } + for _, envVar := range envVars { + if val := os.Getenv(envVar); val != "" { + t.Logf("ENV %s=%s", envVar, val) + } + } + ctx := context.Background() // capture logs var calls []string + var loggerCallCount int ctx = logging.NewContext(ctx, funcr.NewJSON(func(object string) { + loggerCallCount++ + t.Logf("Logger callback invoked #%d: %s", loggerCallCount, object) calls = append(calls, object) }, funcr.Options{ Verbosity: 1, })) + // Track if panic was actually triggered + var panicTriggered bool // A panicking call funcFoo = func() (*http.Response, error) { + panicTriggered = true + t.Logf("funcFoo called - about to panic") panic(fmt.Errorf("oh no!")) } @@ -182,10 +212,35 @@ func TestCheckForUpgradesScheduler(t *testing.T) { Client: fakeClient, Config: cfg, } + + t.Logf("About to call s.check(ctx)") s.check(ctx) + t.Logf("s.check(ctx) returned normally") + + // Debug: Print all captured log calls + t.Logf("=== TEST RESULTS ===") + t.Logf("Panic was triggered: %v", panicTriggered) + t.Logf("Total logger callbacks: %d", loggerCallCount) + t.Logf("Total calls captured in slice: %d", len(calls)) + for i, call := range calls { + t.Logf("Call[%d]: %s", i, call) + } + + // Debug: Check environment differences + t.Logf("Server URL: %s", server.URL) + t.Logf("Config Host: %s", cfg.Host) - assert.Equal(t, len(calls), 2) - assert.Assert(t, cmp.Contains(calls[1], `encountered panic in upgrade check`)) + // Debug: Check scheduler state + t.Logf("Scheduler Client: %v", s.Client != nil) + t.Logf("Scheduler Config: %v", s.Config != nil) + + assert.Equal(t, len(calls), 2, "expected exactly 2 log calls") + if len(calls) > 1 { + assert.Assert(t, cmp.Contains(calls[1], `encountered panic in upgrade check`), + "expected second log to contain panic message, got: %s", calls[1]) + } else { + t.Fatalf("expected at least 2 calls but got %d", len(calls)) + } }) t.Run("successful log each loop, ticker works", func(t *testing.T) { @@ -208,8 +263,8 @@ func TestCheckForUpgradesScheduler(t *testing.T) { }, nil } - // Set loop time to 1s and sleep for 2s before sending the done signal - ctx, cancel := context.WithTimeout(ctx, 2*time.Second) + // Set loop time to 1s and sleep for 5s before sending the done signal + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() s := CheckForUpgradesScheduler{ Client: fakeClient, @@ -220,7 +275,7 @@ func TestCheckForUpgradesScheduler(t *testing.T) { // Sleeping leads to some non-deterministic results, but we expect at least 2 executions // plus one log for the failure to apply the configmap - assert.Assert(t, len(calls) >= 4) + assert.Assert(t, len(calls) >= 4, fmt.Sprintf("expected at least 4 calls, got %d", len(calls))) assert.Assert(t, cmp.Contains(calls[1], `{\"pgo_versions\":[{\"tag\":\"v5.0.4\"},{\"tag\":\"v5.0.3\"},{\"tag\":\"v5.0.2\"},{\"tag\":\"v5.0.1\"},{\"tag\":\"v5.0.0\"}]}`)) assert.Assert(t, cmp.Contains(calls[3], `{\"pgo_versions\":[{\"tag\":\"v5.0.4\"},{\"tag\":\"v5.0.3\"},{\"tag\":\"v5.0.2\"},{\"tag\":\"v5.0.1\"},{\"tag\":\"v5.0.0\"}]}`)) diff --git a/pkg/apis/pgv2.percona.com/v2/perconapgbackup_types.go b/pkg/apis/pgv2.percona.com/v2/perconapgbackup_types.go index 5e0fd6d3f1..92be7a6d46 100644 --- a/pkg/apis/pgv2.percona.com/v2/perconapgbackup_types.go +++ b/pkg/apis/pgv2.percona.com/v2/perconapgbackup_types.go @@ -137,6 +137,13 @@ func (t *PITRestoreDateTime) MarshalJSON() ([]byte, error) { return json.Marshal(t.Time.Format("2006-01-02 15:04:05.000000-0700")) } +func (t *PITRestoreDateTime) ToUnstructured() interface{} { + if t.Time == nil { + return nil + } + return t.Time.ToUnstructured() +} + type PGBackupStorageType string const (