Skip to content

Commit 4774567

Browse files
authored
feature(controlplane): upload attestations through CAS instead (#45)
Signed-off-by: Miguel Martinez Trivino <[email protected]>
1 parent 948ae54 commit 4774567

32 files changed

+2729
-257
lines changed

app/controlplane/api/controlplane/v1/integrations.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ service IntegrationsService {
3939
}
4040

4141
message AddDependencyTrackRequest {
42-
IntegrationConfig.DependencyTrack config = 1 [(validate.rules).message.required = true];;
42+
IntegrationConfig.DependencyTrack config = 1 [(validate.rules).message.required = true];
4343
string api_key = 2 [(validate.rules).string.min_len = 1];
4444
}
4545

@@ -55,7 +55,7 @@ message IntegrationsServiceListResponse{
5555
message IntegrationsServiceAttachRequest{
5656
string workflow_id = 1 [(validate.rules).string.uuid = true];
5757
string integration_id = 2 [(validate.rules).string.uuid = true];
58-
IntegrationAttachmentConfig config = 3 [(validate.rules).message.required = true];;
58+
IntegrationAttachmentConfig config = 3 [(validate.rules).message.required = true];
5959
}
6060

6161
message IntegrationsServiceAttachResponse{

app/controlplane/cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func main() {
109109
panic(err)
110110
}
111111

112-
app, cleanup, err := wireApp(bc.Server, bc.Auth, bc.Data, credsWriter, logger)
112+
app, cleanup, err := wireApp(&bc, credsWriter, logger)
113113
if err != nil {
114114
panic(err)
115115
}

app/controlplane/cmd/wire.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ import (
3333
"github.com/google/wire"
3434
)
3535

36-
// wireApp init kratos application.
37-
func wireApp(*conf.Server, *conf.Auth, *conf.Data, credentials.ReaderWriter, log.Logger) (*app, func(), error) {
36+
func wireApp(*conf.Bootstrap, credentials.ReaderWriter, log.Logger) (*app, func(), error) {
3837
panic(
3938
wire.Build(
4039
wire.Bind(new(credentials.Reader), new(credentials.ReaderWriter)),
@@ -43,8 +42,10 @@ func wireApp(*conf.Server, *conf.Auth, *conf.Data, credentials.ReaderWriter, log
4342
biz.ProviderSet,
4443
service.ProviderSet,
4544
wire.Bind(new(backend.Provider), new(*oci.BackendProvider)),
45+
wire.Bind(new(biz.CASUploader), new(*biz.CASClientUseCase)),
4646
oci.NewBackendProvider,
4747
serviceOpts,
48+
wire.FieldsOf(new(*conf.Bootstrap), "Server", "Auth", "Data", "CasServer"),
4849
newApp,
4950
),
5051
)

app/controlplane/cmd/wire_gen.go

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

app/controlplane/configs/config.devel.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ server:
1616

1717
# Local CAS server for development
1818
cas_server:
19-
addr: 0.0.0.0:9001
19+
grpc:
20+
addr: 0.0.0.0:9001
21+
insecure: true
2022

2123
credentials_service:
2224
vault:

app/controlplane/configs/samples/config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ auth:
1212
# allow_list:
1313
1414

15+
# CAS server where the controlplane will push artifacts to
16+
cas_server:
17+
addr: 0.0.0.0:9001
18+
1519
# Where to store credentials such as OCI registries or third party integrations secrets
1620
credentials_service:
1721
# You can use either vault or aws secret manager

app/controlplane/internal/biz/attestation.go

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
casAPI "github.com/chainloop-dev/chainloop/app/artifact-cas/api/cas/v1"
2727

2828
backend "github.com/chainloop-dev/chainloop/internal/blobmanager"
29+
"github.com/chainloop-dev/chainloop/internal/servicelogger"
2930
"github.com/go-kratos/kratos/v2/log"
3031
"github.com/secure-systems-lab/go-securesystemslib/dsse"
3132
)
@@ -36,6 +37,11 @@ type Attestation struct {
3637

3738
type AttestationUseCase struct {
3839
logger *log.Helper
40+
CASUploader
41+
42+
// DEPRECATED
43+
// We will remove it once we force all the clients to use the CAS instead
44+
backendProvider backend.Provider
3945
}
4046

4147
type AttestationRef struct {
@@ -45,13 +51,15 @@ type AttestationRef struct {
4551
SecretRef string
4652
}
4753

48-
func NewAttestationUseCase(logger log.Logger) *AttestationUseCase {
54+
func NewAttestationUseCase(uploader CASUploader, p backend.Provider, logger log.Logger) *AttestationUseCase {
4955
if logger == nil {
5056
logger = log.NewStdLogger(io.Discard)
5157
}
5258

5359
return &AttestationUseCase{
54-
logger: log.NewHelper(logger),
60+
logger: servicelogger.ScopedHelper(logger, "biz/attestation"),
61+
CASUploader: uploader,
62+
backendProvider: p,
5563
}
5664
}
5765

@@ -71,18 +79,8 @@ func (uc *AttestationUseCase) FetchFromStore(ctx context.Context, downloader bac
7179
return &Attestation{Envelope: &envelope}, nil
7280
}
7381

74-
// UploadAttestationToOCI uploads the attestation to the OCI CAS returning the reference to the attestation
75-
func (uc *AttestationUseCase) UploadAttestationToOCI(ctx context.Context, envelope *dsse.Envelope, uploader backend.Uploader, workflowRunID string) (string, error) {
76-
digest, err := doUploadToOCI(ctx, uploader, workflowRunID, envelope, uc.logger)
77-
if err != nil {
78-
return "", err
79-
}
80-
81-
return digest, nil
82-
}
83-
84-
func doUploadToOCI(ctx context.Context, backend backend.Uploader, runID string, envelope *dsse.Envelope, logger *log.Helper) (string, error) {
85-
fileName := fmt.Sprintf("attestation-%s.json", runID)
82+
func (uc *AttestationUseCase) UploadToCAS(ctx context.Context, envelope *dsse.Envelope, secretID, workflowRunID string) (string, error) {
83+
filename := fmt.Sprintf("attestation-%s.json", workflowRunID)
8684
jsonContent, err := json.Marshal(envelope)
8785
if err != nil {
8886
return "", fmt.Errorf("marshaling the envelope: %w", err)
@@ -92,13 +90,28 @@ func doUploadToOCI(ctx context.Context, backend backend.Uploader, runID string,
9290
hash.Write(jsonContent)
9391
digest := fmt.Sprintf("%x", hash.Sum(nil))
9492

95-
if err := backend.Upload(ctx, bytes.NewBuffer(jsonContent), &casAPI.CASResource{
96-
FileName: fileName, Digest: digest,
93+
if uc.CASUploader.Configured() {
94+
if err := uc.CASUploader.Upload(ctx, secretID, bytes.NewBuffer(jsonContent), filename, digest); err != nil {
95+
return "", fmt.Errorf("uploading to CAS: %w", err)
96+
}
97+
98+
return digest, nil
99+
}
100+
101+
uc.logger.Warnw("msg", "no CAS configured, falling back to old mechanism")
102+
103+
// fallback to old mechanism, this will be removed once we force all the clients to use the CAS
104+
// TODO: remove
105+
uploader, err := uc.backendProvider.FromCredentials(ctx, secretID)
106+
if err != nil {
107+
return "", err
108+
}
109+
110+
if err := uploader.Upload(ctx, bytes.NewBuffer(jsonContent), &casAPI.CASResource{
111+
FileName: filename, Digest: digest,
97112
}); err != nil {
98113
return "", fmt.Errorf("uploading to OCI: %w", err)
99114
}
100115

101-
logger.Infow("msg", "attestation uploaded to OCI", "digest", digest, "filename", fileName, "runID", runID)
102-
103116
return digest, nil
104117
}

app/controlplane/internal/biz/attestation_test.go

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
package biz
16+
package biz_test
1717

1818
import (
1919
"context"
@@ -23,7 +23,9 @@ import (
2323
"testing"
2424

2525
casAPI "github.com/chainloop-dev/chainloop/app/artifact-cas/api/cas/v1"
26-
"github.com/chainloop-dev/chainloop/internal/blobmanager/mocks"
26+
"github.com/chainloop-dev/chainloop/app/controlplane/internal/biz"
27+
"github.com/chainloop-dev/chainloop/app/controlplane/internal/biz/mocks"
28+
blobmock "github.com/chainloop-dev/chainloop/internal/blobmanager/mocks"
2729
"github.com/google/uuid"
2830
"github.com/secure-systems-lab/go-securesystemslib/dsse"
2931
"github.com/stretchr/testify/assert"
@@ -32,7 +34,8 @@ import (
3234
"github.com/stretchr/testify/suite"
3335
)
3436

35-
func (s *attestationTestSuite) TestUploadToOCI() {
37+
// Deprecated method
38+
func (s *attestationTestSuite) TestUploadToCASFallbackOCI() {
3639
runID := uuid.NewString()
3740
envelope := &dsse.Envelope{}
3841
const expectedDigest = "f845058d865c3d4d491c9019f6afe9c543ad2cd11b31620cc512e341fb03d3d8"
@@ -42,14 +45,34 @@ func (s *attestationTestSuite) TestUploadToOCI() {
4245
FileName: fmt.Sprintf("attestation-%s.json", runID), Digest: expectedDigest,
4346
}).Return(nil)
4447

45-
gotDigest, err := s.uc.UploadAttestationToOCI(context.Background(), envelope, s.uploader, runID)
48+
s.casUploader.On("Configured").Return(false)
49+
50+
gotDigest, err := s.uc.UploadToCAS(ctx, envelope, "my-secret", runID)
51+
assert.NoError(s.T(), err)
52+
assert.Equal(s.T(), expectedDigest, gotDigest)
53+
}
54+
55+
func (s *attestationTestSuite) TestUploadToCAS() {
56+
runID := uuid.NewString()
57+
envelope := &dsse.Envelope{}
58+
const expectedDigest = "f845058d865c3d4d491c9019f6afe9c543ad2cd11b31620cc512e341fb03d3d8"
59+
60+
ctx := context.Background()
61+
s.casUploader.On(
62+
"Upload", ctx, "my-secret", mock.Anything,
63+
fmt.Sprintf("attestation-%s.json", runID), expectedDigest,
64+
).Return(nil)
65+
66+
s.casUploader.On("Configured").Return(true)
67+
68+
gotDigest, err := s.uc.UploadToCAS(ctx, envelope, "my-secret", runID)
4669
assert.NoError(s.T(), err)
4770
assert.Equal(s.T(), expectedDigest, gotDigest)
4871
}
4972

5073
func (s *attestationTestSuite) TestFetchFromStore() {
5174
const expectedDigest = "f845058d865c3d4d491c9019f6afe9c543ad2cd11b31620cc512e341fb03d3d8"
52-
want := &Attestation{Envelope: &dsse.Envelope{}}
75+
want := &biz.Attestation{Envelope: &dsse.Envelope{}}
5376

5477
ctx := context.Background()
5578
s.downloader.On("Download", ctx, mock.Anything, expectedDigest).Return(nil).Run(
@@ -69,15 +92,22 @@ func TestAttestation(t *testing.T) {
6992
}
7093

7194
func (s *attestationTestSuite) SetupTest() {
72-
s.uc = NewAttestationUseCase(nil)
73-
s.uploader = mocks.NewUploader(s.T())
74-
s.downloader = mocks.NewDownloader(s.T())
95+
backendProvider := blobmock.NewProvider(s.T())
96+
ociBackend := blobmock.NewUploaderDownloader(s.T())
97+
backendProvider.On("FromCredentials", mock.Anything, "my-secret").Maybe().Return(ociBackend, nil)
98+
99+
s.casUploader = mocks.NewCASUploader(s.T())
100+
s.uc = biz.NewAttestationUseCase(s.casUploader, backendProvider, nil)
101+
s.uploader = (*blobmock.Uploader)(ociBackend)
102+
s.downloader = blobmock.NewDownloader(s.T())
75103
}
76104

77105
// Utility struct to hold the test suite
78106
type attestationTestSuite struct {
79107
suite.Suite
80-
uc *AttestationUseCase
81-
uploader *mocks.Uploader
82-
downloader *mocks.Downloader
108+
uc *biz.AttestationUseCase
109+
// Deprecated: attestation should use the casclient instead of the blobmanager
110+
uploader *blobmock.Uploader
111+
downloader *blobmock.Downloader
112+
casUploader *mocks.CASUploader
83113
}

app/controlplane/internal/biz/biz.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ var ProviderSet = wire.NewSet(
2222
NewWorkflowUsecase,
2323
NewUserUseCase,
2424
NewRootAccountUseCase,
25-
NewWorkflowRunUsecase,
26-
NewOrganizationUsecase,
25+
NewWorkflowRunUseCase,
26+
NewOrganizationUseCase,
2727
NewAttestationUseCase,
28-
NewWorkflowContractUsecase,
28+
NewWorkflowContractUseCase,
2929
NewCASCredentialsUseCase,
30-
NewOCIRepositoryUsecase,
30+
NewOCIRepositoryUseCase,
3131
NewOrgMetricsUseCase,
32-
NewIntegrationUsecase,
33-
NewMembershipUsecase,
32+
NewIntegrationUseCase,
33+
NewMembershipUseCase,
34+
NewCASClientUseCase,
3435
NewWorkflowRunExpirerUseCase,
35-
wire.Struct(new(NewIntegrationUsecaseOpts), "*"),
36+
wire.Struct(new(NewIntegrationUseCaseOpts), "*"),
3637
wire.Struct(new(NewUserUseCaseParams), "*"),
3738
)

0 commit comments

Comments
 (0)