From 1bb638adb2fa034e352df3d789da72450dada121 Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 3 Sep 2025 11:43:57 +0200 Subject: [PATCH 1/5] feat(cas-backends): When updating CAS Backends events are sent Signed-off-by: Javier Rodriguez --- .../pkg/auditor/events/casbackend.go | 8 ++-- app/controlplane/pkg/biz/casbackend.go | 42 ++++++++++++++++--- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/app/controlplane/pkg/auditor/events/casbackend.go b/app/controlplane/pkg/auditor/events/casbackend.go index f6b370301..588d6f469 100644 --- a/app/controlplane/pkg/auditor/events/casbackend.go +++ b/app/controlplane/pkg/auditor/events/casbackend.go @@ -47,7 +47,7 @@ type CASBackendBase struct { CASBackendName string `json:"cas_backend_name,omitempty"` Provider string `json:"provider,omitempty"` Location string `json:"location,omitempty"` - Default bool `json:"default,omitempty"` + Default bool `json:"default"` } func (c *CASBackendBase) RequiresActor() bool { @@ -100,8 +100,8 @@ func (c *CASBackendCreated) Description() string { type CASBackendUpdated struct { *CASBackendBase NewDescription *string `json:"new_description,omitempty"` - CredentialsChanged bool `json:"credentials_changed,omitempty"` - PreviousDefault bool `json:"previous_default,omitempty"` + CredentialsChanged bool `json:"credentials_changed"` + PreviousDefault bool `json:"previous_default"` } func (c *CASBackendUpdated) ActionType() string { @@ -184,7 +184,7 @@ type CASBackendStatusChanged struct { PreviousStatus string `json:"previous_status,omitempty"` NewStatus string `json:"new_status,omitempty"` StatusError string `json:"status_error,omitempty"` - IsRecovery bool `json:"is_recovery,omitempty"` + IsRecovery bool `json:"is_recovery"` } func (c *CASBackendStatusChanged) ActionType() string { diff --git a/app/controlplane/pkg/biz/casbackend.go b/app/controlplane/pkg/biz/casbackend.go index ca8c3cd5c..fa1627ab4 100644 --- a/app/controlplane/pkg/biz/casbackend.go +++ b/app/controlplane/pkg/biz/casbackend.go @@ -336,26 +336,56 @@ func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description } var secretName string + credentialsUpdated := false // We want to rotate credentials if creds != nil { secretName, err = uc.credsRW.SaveCredentials(ctx, orgID, creds) if err != nil { return nil, fmt.Errorf("storing the credentials: %w", err) } + credentialsUpdated = true } - after, err := uc.repo.Update(ctx, &CASBackendUpdateOpts{ + // Update the backend without modifying validation status directly + // The validation status will be updated through PerformValidation if needed + // Don't set validation status here - let PerformValidation handle it + updateOpts := &CASBackendUpdateOpts{ ID: uuid, CASBackendOpts: &CASBackendOpts{ - SecretName: secretName, Default: defaultB, Description: description, OrgID: orgUUID, - ValidationStatus: CASBackendValidationOK, - ValidationError: ToPtr(""), + SecretName: secretName, + Default: defaultB, + Description: description, + OrgID: orgUUID, }, - }) + } + + // If we're not updating credentials, preserve the current validation status + if !credentialsUpdated { + updateOpts.CASBackendOpts.ValidationStatus = before.ValidationStatus + updateOpts.CASBackendOpts.ValidationError = before.ValidationError + } + + after, err := uc.repo.Update(ctx, updateOpts) if err != nil { return nil, err } + // If credentials were updated, perform validation to check if they work + // This will properly update validation status and send events + if credentialsUpdated { + if err := uc.PerformValidation(ctx, id); err != nil { + // Log the validation error but don't fail the update operation + // The validation status will be updated by PerformValidation + uc.logger.Warnw("msg", "validation failed after credential update", "ID", id, "error", err) + } + + // Reload the backend to get the updated validation status + after, err = uc.repo.FindByIDInOrg(ctx, orgUUID, uuid) + if err != nil { + return nil, fmt.Errorf("reloading backend after validation: %w", err) + } + } + // If we just updated the backend from default=true => default=false, we need to set up the fallback as default if before.Default && !after.Default { if _, err := uc.defaultFallbackBackend(ctx, orgID); err != nil { @@ -374,7 +404,7 @@ func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description Default: after.Default, }, NewDescription: &description, - CredentialsChanged: creds != nil, + CredentialsChanged: credentialsUpdated, PreviousDefault: before.Default, }, &orgUUID) } From 8085dbc1476d1eb72ba1e534ff2f97305cac7e65 Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 3 Sep 2025 11:51:38 +0200 Subject: [PATCH 2/5] make linter happy Signed-off-by: Javier Rodriguez --- app/controlplane/pkg/biz/casbackend.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controlplane/pkg/biz/casbackend.go b/app/controlplane/pkg/biz/casbackend.go index fa1627ab4..a6d761f91 100644 --- a/app/controlplane/pkg/biz/casbackend.go +++ b/app/controlplane/pkg/biz/casbackend.go @@ -361,8 +361,8 @@ func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description // If we're not updating credentials, preserve the current validation status if !credentialsUpdated { - updateOpts.CASBackendOpts.ValidationStatus = before.ValidationStatus - updateOpts.CASBackendOpts.ValidationError = before.ValidationError + updateOpts.ValidationStatus = before.ValidationStatus + updateOpts.ValidationError = before.ValidationError } after, err := uc.repo.Update(ctx, updateOpts) From e8ea4fc0a280425b483285803213d97e6b1e4dcb Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 3 Sep 2025 12:37:24 +0200 Subject: [PATCH 3/5] fix tests Signed-off-by: Javier Rodriguez --- .../pkg/biz/casbackend_integration_test.go | 14 +++++++++++++- app/controlplane/pkg/biz/testhelpers/database.go | 6 ++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/controlplane/pkg/biz/casbackend_integration_test.go b/app/controlplane/pkg/biz/casbackend_integration_test.go index 1d7741287..c1781404d 100644 --- a/app/controlplane/pkg/biz/casbackend_integration_test.go +++ b/app/controlplane/pkg/biz/casbackend_integration_test.go @@ -21,6 +21,8 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers" + backends "github.com/chainloop-dev/chainloop/pkg/blobmanager" + blobM "github.com/chainloop-dev/chainloop/pkg/blobmanager/mocks" "github.com/chainloop-dev/chainloop/pkg/blobmanager/oci" creds "github.com/chainloop-dev/chainloop/pkg/credentials/mocks" "github.com/google/go-cmp/cmp" @@ -317,6 +319,8 @@ func (s *CASBackendIntegrationTestSuite) TestUpdate() { ctx := context.TODO() s.credsWriter.Mock = mock.Mock{} s.credsWriter.On("SaveCredentials", ctx, s.orgNoBackend.ID, creds).Return("new-secret", nil) + s.credsWriter.On("ReadCredentials", ctx, "new-secret", mock.Anything).Return(nil) + s.backendProvider.On("ValidateAndExtractCredentials", location, mock.Anything).Return(nil, nil) defaultB, err = s.CASBackend.Update(ctx, s.orgNoBackend.ID, defaultB.ID.String(), description, creds, true) assert.NoError(err) assert.Equal(description, defaultB.Description) @@ -439,7 +443,14 @@ func (s *CASBackendIntegrationTestSuite) SetupTest() { "SaveCredentials", mock.Anything, mock.Anything, mock.Anything, ).Return("stored-OCI-secret", nil) - s.TestingUseCases = testhelpers.NewTestingUseCases(s.T(), testhelpers.WithCredsReaderWriter(s.credsWriter)) + // Create backend provider mock + s.backendProvider = blobM.NewProvider(s.T()) + + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T(), + testhelpers.WithCredsReaderWriter(s.credsWriter), + testhelpers.WithBackendProviders(backends.Providers{ + "OCI": s.backendProvider, + })) s.orgOne, err = s.Organization.Create(ctx, "testing-org-1-with-one-backend") assert.NoError(err) @@ -465,6 +476,7 @@ type CASBackendIntegrationTestSuite struct { orgTwo, orgOne, orgNoBackend *biz.Organization casBackend1, casBackend2, casBackend3 *biz.CASBackend credsWriter *creds.ReaderWriter + backendProvider *blobM.Provider } func TestIntegrationCASBackend(t *testing.T) { diff --git a/app/controlplane/pkg/biz/testhelpers/database.go b/app/controlplane/pkg/biz/testhelpers/database.go index e3358a87e..da16ce209 100644 --- a/app/controlplane/pkg/biz/testhelpers/database.go +++ b/app/controlplane/pkg/biz/testhelpers/database.go @@ -123,6 +123,12 @@ func WithOnboardingConfiguration(conf []*config.OnboardingSpec) NewTestingUCOpt } } +func WithBackendProviders(providers backends.Providers) NewTestingUCOpt { + return func(tu *newTestingOpts) { + tu.providers = providers + } +} + func NewTestingUseCases(t *testing.T, opts ...NewTestingUCOpt) *TestingUseCases { // default args newArgs := &newTestingOpts{credsReaderWriter: creds.NewReaderWriter(t), From cda7050798c0c4a7da4966052e24fd6035262347 Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 3 Sep 2025 12:39:26 +0200 Subject: [PATCH 4/5] feedback Signed-off-by: Javier Rodriguez --- app/controlplane/pkg/biz/casbackend.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controlplane/pkg/biz/casbackend.go b/app/controlplane/pkg/biz/casbackend.go index a6d761f91..116139bbc 100644 --- a/app/controlplane/pkg/biz/casbackend.go +++ b/app/controlplane/pkg/biz/casbackend.go @@ -336,14 +336,13 @@ func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description } var secretName string - credentialsUpdated := false + credentialsUpdated := creds != nil // We want to rotate credentials if creds != nil { secretName, err = uc.credsRW.SaveCredentials(ctx, orgID, creds) if err != nil { return nil, fmt.Errorf("storing the credentials: %w", err) } - credentialsUpdated = true } // Update the backend without modifying validation status directly From 24babe8aaf5898ce8a8729676814dc6c9da68e7f Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 3 Sep 2025 12:59:07 +0200 Subject: [PATCH 5/5] fix auditor test Signed-off-by: Javier Rodriguez --- .../testdata/casbackends/casbackend_status_change.json | 5 +++-- .../events/testdata/casbackends/casbackend_updated.json | 5 +++-- .../casbackends/casbackend_updated_default_change.json | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_status_change.json b/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_status_change.json index 05c393ffe..5682892b2 100644 --- a/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_status_change.json +++ b/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_status_change.json @@ -15,7 +15,8 @@ "location": "test-location", "default": true, "previous_status": "OK", - "new_status": "Invalid" + "new_status": "Invalid", + "is_recovery": false }, - "Digest": "sha256:2112ea486d9e36c40a6869efcbf745836742c70dc97a17cffe30ae3651b0579c" + "Digest": "sha256:496bc2b996945222ab4f96cf62d3ff5c08dcc92a4647a6e4bdd3def7de7288ab" } \ No newline at end of file diff --git a/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated.json b/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated.json index 1966ef324..42e2a2ada 100644 --- a/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated.json +++ b/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated.json @@ -15,7 +15,8 @@ "location": "test-location", "default": true, "new_description": "test description", - "credentials_changed": true + "credentials_changed": true, + "previous_default": false }, - "Digest": "sha256:2c738aacd42a4257f9496c270586c1edf0be13d0d10160e8c564a96fedf0a726" + "Digest": "sha256:5d893a99885dcb465c30558736487493c5f24a61a7f28d1738b9b237f4c8fdc5" } \ No newline at end of file diff --git a/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated_default_change.json b/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated_default_change.json index 6a0b54799..2a1f6deac 100644 --- a/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated_default_change.json +++ b/app/controlplane/pkg/auditor/events/testdata/casbackends/casbackend_updated_default_change.json @@ -13,8 +13,10 @@ "cas_backend_name": "test-backend", "provider": "OCI", "location": "test-location", + "default": false, "new_description": "test description", + "credentials_changed": false, "previous_default": true }, - "Digest": "sha256:c229b657b7f984a60a2e7c5b6eab1137f026e50eecd76bc718f6548c006d625f" + "Digest": "sha256:8d7db84ea1e85b5c033ccb0c7b553bd57292554383498094fd1eb36d4dc3c1cd" } \ No newline at end of file