Skip to content

Commit 0f0f818

Browse files
authored
refactor(CAS proxy): multiple CAS backend providers support (#359)
Signed-off-by: Miguel Martinez Trivino <[email protected]>
1 parent 6375911 commit 0f0f818

31 files changed

+416
-190
lines changed

.golangci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,8 @@ severity:
7171
- linters:
7272
- staticcheck
7373
text: "SA1019:"
74-
severity: info
74+
severity: info
75+
issues:
76+
exclude-rules:
77+
- path: _test\.go
78+
text: "Potential hardcoded credentials"

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

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

app/artifact-cas/api/cas/v1/status.proto

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,31 @@ syntax = "proto3";
1717

1818
package cas.v1;
1919

20-
option go_package = "github.com/chainloop-dev/chainloop/app/artifact-cas/api/cas/v1;v1";
21-
2220
import "google/api/annotations.proto";
2321

22+
option go_package = "github.com/chainloop-dev/chainloop/app/artifact-cas/api/cas/v1;v1";
23+
2424
service StatusService {
25-
rpc Infoz (InfozRequest) returns (InfozResponse) {
26-
option (google.api.http) = {
27-
get: "/infoz"
28-
};
29-
}
30-
rpc Statusz (StatuszRequest) returns (StatuszResponse) {
31-
option (google.api.http) = {
32-
get: "/statusz"
33-
};
34-
}
25+
rpc Infoz(InfozRequest) returns (InfozResponse) {
26+
option (google.api.http) = {get: "/infoz"};
27+
}
28+
rpc Statusz(StatuszRequest) returns (StatuszResponse) {
29+
option (google.api.http) = {get: "/statusz"};
30+
}
3531
}
3632

37-
message InfozRequest { }
33+
message InfozRequest {}
3834

3935
message InfozResponse {
40-
string version = 1;
36+
string version = 1;
37+
repeated string backends = 2;
4138
}
4239

4340
message StatuszRequest {
44-
// Parameter that can be used by readiness probes
45-
// The main difference is that readiness probes will take into account that all
46-
// dependent services are up and ready
47-
bool readiness = 1;
41+
// Parameter that can be used by readiness probes
42+
// The main difference is that readiness probes will take into account that all
43+
// dependent services are up and ready
44+
bool readiness = 1;
4845
}
4946

5047
message StatuszResponse {}

app/artifact-cas/cmd/main.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424

2525
"github.com/chainloop-dev/chainloop/app/artifact-cas/internal/conf"
2626
"github.com/chainloop-dev/chainloop/app/artifact-cas/internal/server"
27+
backend "github.com/chainloop-dev/chainloop/internal/blobmanager"
28+
"github.com/chainloop-dev/chainloop/internal/blobmanager/oci"
2729
"github.com/chainloop-dev/chainloop/internal/credentials"
2830
"github.com/chainloop-dev/chainloop/internal/credentials/manager"
2931
"github.com/chainloop-dev/chainloop/internal/servicelogger"
@@ -56,19 +58,36 @@ func init() {
5658
flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
5759
}
5860

59-
func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server, ms *server.HTTPMetricsServer) *kratos.App {
60-
return kratos.New(
61-
kratos.ID(id),
62-
kratos.Name(Name),
63-
kratos.Version(Version),
64-
kratos.Metadata(map[string]string{}),
65-
kratos.Logger(logger),
66-
kratos.Server(
67-
gs,
68-
hs,
69-
ms,
61+
type app struct {
62+
*kratos.App
63+
backend.Providers
64+
}
65+
66+
func loadCASBackendProviders(creader credentials.Reader) backend.Providers {
67+
// Currently only OCI is supported
68+
// Here we will load the rest of providers, S3, GCS, etc
69+
p := oci.NewBackendProvider(creader)
70+
return backend.Providers{
71+
p.ID(): p,
72+
}
73+
}
74+
75+
func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server, ms *server.HTTPMetricsServer, providers backend.Providers) *app {
76+
return &app{
77+
kratos.New(
78+
kratos.ID(id),
79+
kratos.Name(Name),
80+
kratos.Version(Version),
81+
kratos.Metadata(map[string]string{}),
82+
kratos.Logger(logger),
83+
kratos.Server(
84+
gs,
85+
hs,
86+
ms,
87+
),
7088
),
71-
)
89+
providers,
90+
}
7291
}
7392

7493
func main() {
@@ -115,6 +134,10 @@ func main() {
115134
}
116135
defer cleanup()
117136

137+
for k := range app.Providers {
138+
_ = logger.Log(log.LevelInfo, "msg", "CAS backend provider loaded", "provider", k)
139+
}
140+
118141
// start and wait for stop signal
119142
if err := app.Run(); err != nil {
120143
panic(err)

app/artifact-cas/cmd/wire.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,18 @@ import (
2424
"github.com/chainloop-dev/chainloop/app/artifact-cas/internal/conf"
2525
"github.com/chainloop-dev/chainloop/app/artifact-cas/internal/server"
2626
"github.com/chainloop-dev/chainloop/app/artifact-cas/internal/service"
27-
backend "github.com/chainloop-dev/chainloop/internal/blobmanager"
28-
"github.com/chainloop-dev/chainloop/internal/blobmanager/oci"
2927
"github.com/chainloop-dev/chainloop/internal/credentials"
30-
"github.com/go-kratos/kratos/v2"
3128
"github.com/go-kratos/kratos/v2/log"
3229
"github.com/google/wire"
3330
)
3431

3532
// wireApp init kratos application.
36-
func wireApp(*conf.Server, *conf.Auth, credentials.Reader, log.Logger) (*kratos.App, func(), error) {
33+
func wireApp(*conf.Server, *conf.Auth, credentials.Reader, log.Logger) (*app, func(), error) {
3734
panic(
3835
wire.Build(
3936
server.ProviderSet,
4037
service.ProviderSet,
41-
wire.Bind(new(backend.Provider), new(*oci.BackendProvider)),
42-
oci.NewBackendProvider,
38+
loadCASBackendProviders,
4339
newApp,
4440
serviceOpts,
4541
),

app/artifact-cas/cmd/wire_gen.go

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

app/artifact-cas/internal/server/grpc.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
v1 "github.com/chainloop-dev/chainloop/app/artifact-cas/api/cas/v1"
2626
"github.com/chainloop-dev/chainloop/app/artifact-cas/internal/conf"
2727
"github.com/chainloop-dev/chainloop/app/artifact-cas/internal/service"
28+
backend "github.com/chainloop-dev/chainloop/internal/blobmanager"
2829
casJWT "github.com/chainloop-dev/chainloop/internal/robotaccount/cas"
2930
"github.com/getsentry/sentry-go"
3031
"github.com/go-kratos/kratos/v2/errors"
@@ -43,7 +44,7 @@ import (
4344
)
4445

4546
// NewGRPCServer new a gRPC server.
46-
func NewGRPCServer(c *conf.Server, authConf *conf.Auth, byteService *service.ByteStreamService, rSvc *service.ResourceService, logger log.Logger) (*grpc.Server, error) {
47+
func NewGRPCServer(c *conf.Server, authConf *conf.Auth, byteService *service.ByteStreamService, rSvc *service.ResourceService, providers backend.Providers, logger log.Logger) (*grpc.Server, error) {
4748
log := log.NewHelper(logger)
4849
// Load the key on initialization instead of on every request
4950
// TODO: implement jwks endpoint
@@ -119,7 +120,7 @@ func NewGRPCServer(c *conf.Server, authConf *conf.Auth, byteService *service.Byt
119120

120121
bytestream.RegisterByteStreamServer(srv.Server, byteService)
121122
v1.RegisterResourceServiceServer(srv.Server, rSvc)
122-
v1.RegisterStatusServiceServer(srv.Server, service.NewStatusService(Version))
123+
v1.RegisterStatusServiceServer(srv.Server, service.NewStatusService(Version, providers))
123124

124125
// Register and set metrics to 0
125126
grpc_prometheus.Register(srv.Server)

app/artifact-cas/internal/server/grpc_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func TestJWTAuthFunc(t *testing.T) {
9696

9797
b, err := robotaccount.NewBuilder(opts...)
9898
require.NoError(t, err)
99-
token, err := b.GenerateJWT("secret-id", tc.audience, robotaccount.Downloader)
99+
token, err := b.GenerateJWT("backend-type", "secret-id", tc.audience, robotaccount.Downloader)
100100
require.NoError(t, err)
101101

102102
// add bearer token to context
@@ -127,6 +127,7 @@ func TestJWTAuthFunc(t *testing.T) {
127127
claims := infoFromAuth(ctx, t)
128128
assert.NoError(t, claims.Valid())
129129
assert.Equal(t, "secret-id", claims.StoredSecretID)
130+
assert.Equal(t, "backend-type", claims.BackendType)
130131
assert.Equal(t, robotaccount.Downloader, claims.Role)
131132
assert.Equal(t, "my-issuer", claims.Issuer)
132133
assert.Contains(t, claims.Audience, "artifact-cas.chainloop")

app/artifact-cas/internal/server/http.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
nhttp "net/http"
2828

2929
api "github.com/chainloop-dev/chainloop/app/artifact-cas/api/cas/v1"
30+
backend "github.com/chainloop-dev/chainloop/internal/blobmanager"
3031
"github.com/go-kratos/kratos/v2/log"
3132
jwtMiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
3233
"github.com/go-kratos/kratos/v2/middleware/logging"
@@ -35,7 +36,7 @@ import (
3536
)
3637

3738
// NewHTTPServer new a HTTP server.
38-
func NewHTTPServer(c *conf.Server, authConf *conf.Auth, downloadSvc *service.DownloadService, logger log.Logger) (*http.Server, error) {
39+
func NewHTTPServer(c *conf.Server, authConf *conf.Auth, downloadSvc *service.DownloadService, providers backend.Providers, logger log.Logger) (*http.Server, error) {
3940
var opts = []http.ServerOption{
4041
http.Middleware(
4142
recovery.Recovery(),
@@ -68,7 +69,7 @@ func NewHTTPServer(c *conf.Server, authConf *conf.Auth, downloadSvc *service.Dow
6869
srv := http.NewServer(opts...)
6970

7071
srv.Handle(service.DownloadPath, authFromQueryMiddleware(loadPublicKey(rawKey), casJWT.SigningMethod, downloadSvc))
71-
api.RegisterStatusServiceHTTPServer(srv, service.NewStatusService(Version))
72+
api.RegisterStatusServiceHTTPServer(srv, service.NewStatusService(Version, providers))
7273
return srv, nil
7374
}
7475

app/artifact-cas/internal/service/bytestream.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type ByteStreamService struct {
4141
*commonService
4242
}
4343

44-
func NewByteStreamService(bp backend.Provider, opts ...NewOpt) *ByteStreamService {
44+
func NewByteStreamService(bp backend.Providers, opts ...NewOpt) *ByteStreamService {
4545
return &ByteStreamService{
4646
commonService: newCommonService(bp, opts...),
4747
}
@@ -70,9 +70,10 @@ func (s *ByteStreamService) Write(stream bytestream.ByteStream_WriteServer) erro
7070
return kerrors.BadRequest("resource name", err.Error())
7171
}
7272

73-
// Load OCI backend based on a reference stored in the token
74-
backend, err := s.backendP.FromCredentials(ctx, info.StoredSecretID)
75-
if err != nil {
73+
backend, err := s.loadBackend(ctx, info.BackendType, info.StoredSecretID)
74+
if err != nil && kerrors.IsNotFound(err) {
75+
return err
76+
} else if err != nil {
7677
return sl.LogAndMaskErr(err, s.log)
7778
}
7879

@@ -142,9 +143,10 @@ func (s *ByteStreamService) Read(req *bytestream.ReadRequest, stream bytestream.
142143
return kerrors.BadRequest("resource name", "empty resource name")
143144
}
144145

145-
// Retrieve the OCI backend from where to download the file
146-
backend, err := s.backendP.FromCredentials(ctx, info.StoredSecretID)
147-
if err != nil {
146+
backend, err := s.loadBackend(ctx, info.BackendType, info.StoredSecretID)
147+
if err != nil && kerrors.IsNotFound(err) {
148+
return err
149+
} else if err != nil {
148150
return sl.LogAndMaskErr(err, s.log)
149151
}
150152

0 commit comments

Comments
 (0)