diff --git a/internal/backupcontroller/default_objects_gate.go b/internal/backupcontroller/default_objects_gate.go index b99f67b41..9b6fc0ede 100644 --- a/internal/backupcontroller/default_objects_gate.go +++ b/internal/backupcontroller/default_objects_gate.go @@ -438,7 +438,9 @@ func (g *DefaultObjectsGate) Check(ctx context.Context) ([]string, bool, error) // writes for it. The COSI Secret being absent (the BucketAccess is being // re-granted) leaves nothing to compare against and is not staleness, and // neither is a source Secret the bucket chart does not render: the COSI Secret -// itself (the projector accepts one) or one without the endpoint key. +// itself (the projector accepts one) or one without the endpoint key. A +// BucketInfo without an endpoint is not a target either: rendering from it +// writes an empty endpoint, or fails the release when the key is absent. func (g *DefaultObjectsGate) staleCredentials(ctx context.Context, src *corev1.Secret) (string, error) { if g.CredentialsCOSISecretName == "" || g.CredentialsHelmRelease.Name == "" || g.CredentialsHelmRelease.Namespace == "" { return "", nil @@ -466,7 +468,7 @@ func (g *DefaultObjectsGate) staleCredentials(ctx context.Context, src *corev1.S } want := strings.TrimPrefix(strings.TrimPrefix(info.Spec.SecretS3.Endpoint, "https://"), "http://") have := string(src.Data["endpoint"]) - if have == want { + if want == "" || have == want { return "", nil } return fmt.Sprintf("Secret/%s (endpoint %q, COSI Secret has %q)", g.Config.SourceSecretName, have, want), nil diff --git a/internal/backupcontroller/default_objects_gate_test.go b/internal/backupcontroller/default_objects_gate_test.go index befaedabe..bed60053f 100644 --- a/internal/backupcontroller/default_objects_gate_test.go +++ b/internal/backupcontroller/default_objects_gate_test.go @@ -851,6 +851,10 @@ func TestCheckCredentialsEndpointComparison(t *testing.T) { {"bare host", "s3.example.com", false}, {"other host", "https://s3.new.example.com", true}, {"other port", "https://s3.example.com:8443", true}, + // A forced render from a BucketInfo with no endpoint would write an + // empty endpoint into the Secret every driver reads (or, with the key + // absent, fail the bucket release's render), never a working one. + {"no endpoint in BucketInfo", "", false}, } { t.Run(tc.name, func(t *testing.T) { objs := append([]runtime.Object{helmReleaseObject(), credentialsHelmReleaseObject()}, allDefaultObjects()...)