Skip to content

Commit b5f58e7

Browse files
authored
chore: refactor cas backend providers (#243)
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
1 parent 2cba9e0 commit b5f58e7

23 files changed

+439
-187
lines changed

app/artifact-cas/api/cas/v1/status_http.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/cmd/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ import (
2828
"github.com/chainloop-dev/chainloop/app/controlplane/internal/server"
2929
"github.com/chainloop-dev/chainloop/app/controlplane/plugins"
3030
"github.com/chainloop-dev/chainloop/app/controlplane/plugins/sdk/v1"
31+
backends "github.com/chainloop-dev/chainloop/internal/blobmanager"
32+
"github.com/chainloop-dev/chainloop/internal/blobmanager/oci"
33+
"github.com/chainloop-dev/chainloop/internal/credentials"
3134
credsConfig "github.com/chainloop-dev/chainloop/internal/credentials/api/credentials/v1"
3235
"github.com/chainloop-dev/chainloop/internal/servicelogger"
3336

@@ -167,6 +170,15 @@ func maskArgs(keyvals []interface{}) {
167170
}
168171
}
169172

173+
func loadCASBackendProviders(creader credentials.Reader) backends.Providers {
174+
// Initialize CAS backend providers
175+
// For now we only have OCI as a backend provider
176+
p := oci.NewBackendProvider(creader)
177+
return backends.Providers{
178+
p.ID(): p,
179+
}
180+
}
181+
170182
func initSentry(c *conf.Bootstrap, logger log.Logger) (cleanupFunc func(), err error) {
171183
cleanupFunc = func() {
172184
sentry.Flush(2 * time.Second)

app/controlplane/cmd/wire.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ import (
2828
"github.com/chainloop-dev/chainloop/app/controlplane/internal/server"
2929
"github.com/chainloop-dev/chainloop/app/controlplane/internal/service"
3030
"github.com/chainloop-dev/chainloop/app/controlplane/plugins/sdk/v1"
31-
backend "github.com/chainloop-dev/chainloop/internal/blobmanager"
32-
"github.com/chainloop-dev/chainloop/internal/blobmanager/oci"
3331
"github.com/chainloop-dev/chainloop/internal/credentials"
3432
"github.com/go-kratos/kratos/v2/log"
3533
"github.com/google/wire"
@@ -42,10 +40,9 @@ func wireApp(*conf.Bootstrap, credentials.ReaderWriter, log.Logger, sdk.Availabl
4240
server.ProviderSet,
4341
data.ProviderSet,
4442
biz.ProviderSet,
43+
loadCASBackendProviders,
4544
service.ProviderSet,
46-
wire.Bind(new(backend.Provider), new(*oci.BackendProvider)),
4745
wire.Bind(new(biz.CASClient), new(*biz.CASClientUseCase)),
48-
oci.NewBackendProvider,
4946
serviceOpts,
5047
wire.Value([]biz.CASClientOpts{}),
5148
wire.FieldsOf(new(*conf.Bootstrap), "Server", "Auth", "Data", "CasServer"),

app/controlplane/cmd/wire_gen.go

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/internal/biz/casbackend.go

Lines changed: 41 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,12 @@ package biz
1818
import (
1919
"context"
2020
"encoding/json"
21-
"errors"
2221
"fmt"
2322
"io"
2423
"time"
2524

2625
backend "github.com/chainloop-dev/chainloop/internal/blobmanager"
27-
"github.com/chainloop-dev/chainloop/internal/blobmanager/oci"
2826
"github.com/chainloop-dev/chainloop/internal/credentials"
29-
"github.com/chainloop-dev/chainloop/internal/ociauth"
3027
"github.com/chainloop-dev/chainloop/internal/servicelogger"
3128
"github.com/go-kratos/kratos/v2/log"
3229
"github.com/google/uuid"
@@ -47,7 +44,7 @@ type CASBackend struct {
4744
ID uuid.UUID
4845
Location, Description, SecretName string
4946
CreatedAt, ValidatedAt *time.Time
50-
OrganizationID string
47+
OrganizationID uuid.UUID
5148
ValidationStatus CASBackendValidationStatus
5249
// OCI, S3, ...
5350
Provider CASBackendProvider
@@ -56,14 +53,14 @@ type CASBackend struct {
5653
}
5754

5855
type CASBackendOpts struct {
56+
OrgID uuid.UUID
5957
Location, SecretName, Description string
6058
Provider CASBackendProvider
6159
Default bool
6260
}
6361

6462
type CASBackendCreateOpts struct {
6563
*CASBackendOpts
66-
OrgID uuid.UUID
6764
}
6865

6966
type CASBackendUpdateOpts struct {
@@ -85,23 +82,23 @@ type CASBackendRepo interface {
8582

8683
type CASBackendReader interface {
8784
FindDefaultBackend(ctx context.Context, orgID string) (*CASBackend, error)
88-
FindByID(ctx context.Context, ID string) (*CASBackend, error)
85+
FindByIDInOrg(ctx context.Context, OrgID, ID string) (*CASBackend, error)
8986
PerformValidation(ctx context.Context, ID string) error
9087
}
9188

9289
type CASBackendUseCase struct {
93-
repo CASBackendRepo
94-
logger *log.Helper
95-
credsRW credentials.ReaderWriter
96-
ociBackendProvider backend.Provider
90+
repo CASBackendRepo
91+
logger *log.Helper
92+
credsRW credentials.ReaderWriter
93+
providers backend.Providers
9794
}
9895

99-
func NewCASBackendUseCase(repo CASBackendRepo, credsRW credentials.ReaderWriter, p backend.Provider, l log.Logger) *CASBackendUseCase {
96+
func NewCASBackendUseCase(repo CASBackendRepo, credsRW credentials.ReaderWriter, providers backend.Providers, l log.Logger) *CASBackendUseCase {
10097
if l == nil {
10198
l = log.NewStdLogger(io.Discard)
10299
}
103100

104-
return &CASBackendUseCase{repo, servicelogger.ScopedHelper(l, "biz/CASBackend"), credsRW, p}
101+
return &CASBackendUseCase{repo, servicelogger.ScopedHelper(l, "biz/CASBackend"), credsRW, providers}
105102
}
106103

107104
func (uc *CASBackendUseCase) List(ctx context.Context, orgID string) ([]*CASBackend, error) {
@@ -119,16 +116,7 @@ func (uc *CASBackendUseCase) FindDefaultBackend(ctx context.Context, orgID strin
119116
return nil, NewErrInvalidUUID(err)
120117
}
121118

122-
return uc.repo.FindDefaultBackend(ctx, orgUUID)
123-
}
124-
125-
func (uc *CASBackendUseCase) FindByID(ctx context.Context, id string) (*CASBackend, error) {
126-
backendUUID, err := uuid.Parse(id)
127-
if err != nil {
128-
return nil, NewErrInvalidUUID(err)
129-
}
130-
131-
backend, err := uc.repo.FindByID(ctx, backendUUID)
119+
backend, err := uc.repo.FindDefaultBackend(ctx, orgUUID)
132120
if err != nil {
133121
return nil, err
134122
} else if backend == nil {
@@ -138,75 +126,49 @@ func (uc *CASBackendUseCase) FindByID(ctx context.Context, id string) (*CASBacke
138126
return backend, nil
139127
}
140128

141-
func validateAndExtractCredentials(provider CASBackendProvider, location string, credsJSON []byte) (any, error) {
142-
// TODO: (miguel) this logic (marshalling from struct + validation) will be moved to the actual backend implementation
143-
// This endpoint will support other backends in the future
144-
if provider != CASBackendOCI {
145-
return nil, NewErrValidation(errors.New("unsupported provider"))
146-
}
147-
148-
var ociConfig = struct {
149-
Password string `json:"password"`
150-
Username string `json:"username"`
151-
}{}
152-
153-
if err := json.Unmarshal(credsJSON, &ociConfig); err != nil {
154-
return nil, NewErrValidation(err)
155-
}
156-
157-
// Create and validate credentials
158-
k, err := ociauth.NewCredentials(location, ociConfig.Username, ociConfig.Password)
129+
func (uc *CASBackendUseCase) FindByIDInOrg(ctx context.Context, orgID, id string) (*CASBackend, error) {
130+
orgUUID, err := uuid.Parse(orgID)
159131
if err != nil {
160-
return nil, NewErrValidation(err)
132+
return nil, NewErrInvalidUUID(err)
161133
}
162134

163-
// Check credentials
164-
b, err := oci.NewBackend(location, &oci.RegistryOptions{Keychain: k})
135+
backendUUID, err := uuid.Parse(id)
165136
if err != nil {
166-
return nil, fmt.Errorf("checking credentials: %w", err)
167-
}
168-
169-
if err := b.CheckWritePermissions(context.TODO()); err != nil {
170-
return nil, NewErrValidation(fmt.Errorf("wrong credentials: %w", err))
137+
return nil, NewErrInvalidUUID(err)
171138
}
172139

173-
// Validate and store the secret in the external secrets manager
174-
creds := &credentials.OCIKeypair{Repo: location, Username: ociConfig.Username, Password: ociConfig.Password}
175-
if err := creds.Validate(); err != nil {
176-
return nil, NewErrValidation(err)
140+
backend, err := uc.repo.FindByIDInOrg(ctx, orgUUID, backendUUID)
141+
if err != nil {
142+
return nil, err
143+
} else if backend == nil {
144+
return nil, NewErrNotFound("CAS Backend")
177145
}
178146

179-
return creds, nil
147+
return backend, nil
180148
}
181149

182-
func (uc *CASBackendUseCase) Create(ctx context.Context, orgID, location, description string, provider CASBackendProvider, credsJSON []byte, defaultB bool) (*CASBackend, error) {
150+
func (uc *CASBackendUseCase) Create(ctx context.Context, orgID, location, description string, provider CASBackendProvider, creds any, defaultB bool) (*CASBackend, error) {
183151
orgUUID, err := uuid.Parse(orgID)
184152
if err != nil {
185153
return nil, NewErrInvalidUUID(err)
186154
}
187155

188-
// Validate and store the secret in the external secrets manager
189-
creds, err := validateAndExtractCredentials(provider, location, credsJSON)
190-
if err != nil {
191-
return nil, NewErrValidation(err)
192-
}
193-
194156
secretName, err := uc.credsRW.SaveCredentials(ctx, orgID, creds)
195157
if err != nil {
196158
return nil, fmt.Errorf("storing the credentials: %w", err)
197159
}
198160

199161
return uc.repo.Create(ctx, &CASBackendCreateOpts{
200-
OrgID: orgUUID,
201162
CASBackendOpts: &CASBackendOpts{
202163
Location: location, SecretName: secretName, Provider: provider, Default: defaultB,
203164
Description: description,
165+
OrgID: orgUUID,
204166
},
205167
})
206168
}
207169

208170
// Update will update credentials, description or default status
209-
func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description string, credsJSON []byte, defaultB bool) (*CASBackend, error) {
171+
func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description string, creds any, defaultB bool) (*CASBackend, error) {
210172
orgUUID, err := uuid.Parse(orgID)
211173
if err != nil {
212174
return nil, NewErrInvalidUUID(err)
@@ -226,13 +188,7 @@ func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description
226188

227189
var secretName string
228190
// We want to rotate credentials
229-
if credsJSON != nil {
230-
// Validate and store the secret in the external secrets manager
231-
creds, err := validateAndExtractCredentials(repo.Provider, repo.Location, credsJSON)
232-
if err != nil {
233-
return nil, NewErrValidation(err)
234-
}
235-
191+
if creds != nil {
236192
secretName, err = uc.credsRW.SaveCredentials(ctx, orgID, creds)
237193
if err != nil {
238194
return nil, fmt.Errorf("storing the credentials: %w", err)
@@ -242,13 +198,11 @@ func (uc *CASBackendUseCase) Update(ctx context.Context, orgID, id, description
242198
return uc.repo.Update(ctx, &CASBackendUpdateOpts{
243199
ID: uuid,
244200
CASBackendOpts: &CASBackendOpts{
245-
SecretName: secretName, Default: defaultB, Description: description,
201+
SecretName: secretName, Default: defaultB, Description: description, OrgID: orgUUID,
246202
},
247203
})
248204
}
249205

250-
// TODO(miguel): we need to think about the update mechanism and add some guardrails
251-
// for example, we might only allow updating credentials but not the repository itself or the provider
252206
// Deprecated: use Create and update methods separately instead
253207
func (uc *CASBackendUseCase) CreateOrUpdate(ctx context.Context, orgID, name, username, password string, provider CASBackendProvider, defaultB bool) (*CASBackend, error) {
254208
orgUUID, err := uuid.Parse(orgID)
@@ -284,10 +238,10 @@ func (uc *CASBackendUseCase) CreateOrUpdate(ctx context.Context, orgID, name, us
284238
}
285239

286240
return uc.repo.Create(ctx, &CASBackendCreateOpts{
287-
OrgID: orgUUID,
288241
CASBackendOpts: &CASBackendOpts{
289242
Location: name, SecretName: secretName, Provider: provider,
290243
Default: defaultB,
244+
OrgID: orgUUID,
291245
},
292246
})
293247
}
@@ -353,8 +307,6 @@ func (CASBackendValidationStatus) Values() (kinds []string) {
353307
}
354308

355309
// Validate that the repository is valid and reachable
356-
// TODO: run this process periodically in the background
357-
// TODO: we need to support other kinds of repositories this is for the OCI type
358310
func (uc *CASBackendUseCase) PerformValidation(ctx context.Context, id string) (err error) {
359311
validationStatus := CASBackendValidationFailed
360312

@@ -370,10 +322,9 @@ func (uc *CASBackendUseCase) PerformValidation(ctx context.Context, id string) (
370322
return NewErrNotFound("CAS Backend")
371323
}
372324

373-
// Currently this code is just for OCI repositories
374-
if backend.Provider != CASBackendOCI {
375-
uc.logger.Warnw("msg", "validation not supported for this provider", "ID", id, "provider", backend.Provider)
376-
return nil
325+
provider, ok := uc.providers[string(backend.Provider)]
326+
if !ok {
327+
return fmt.Errorf("CAS backend provider not found: %s", backend.Provider)
377328
}
378329

379330
defer func() {
@@ -390,14 +341,21 @@ func (uc *CASBackendUseCase) PerformValidation(ctx context.Context, id string) (
390341
}()
391342

392343
// 1 - Retrieve the credentials from the external secrets manager
393-
b, err := uc.ociBackendProvider.FromCredentials(ctx, backend.SecretName)
394-
if err != nil {
344+
var creds any
345+
if err := uc.credsRW.ReadCredentials(ctx, backend.SecretName, &creds); err != nil {
395346
uc.logger.Infow("msg", "credentials not found or invalid", "ID", id)
396347
return nil
397348
}
398349

399-
// 2 - Perform a write validation
400-
if err = b.CheckWritePermissions(context.TODO()); err != nil {
350+
credsJSON, err := json.Marshal(creds)
351+
if err != nil {
352+
uc.logger.Infow("msg", "credentials invalid", "ID", id)
353+
return nil
354+
}
355+
356+
// 2 - run validation
357+
_, err = provider.ValidateAndExtractCredentials(backend.Location, credsJSON)
358+
if err != nil {
401359
uc.logger.Infow("msg", "permissions validation failed", "ID", id)
402360
return nil
403361
}

0 commit comments

Comments
 (0)