diff --git a/go.mod b/go.mod index 01643e10c5..b28c3f00ef 100644 --- a/go.mod +++ b/go.mod @@ -19,11 +19,12 @@ require ( github.com/open-policy-agent/opa v0.61.0 github.com/opentdf/backend-go v0.1.17 github.com/opentdf/platform/protocol/go v0.0.0-00010101000000-000000000000 + github.com/opentdf/platform/sdk v0.0.0-00010101000000-000000000000 github.com/pressly/goose/v3 v3.18.0 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 - github.com/testcontainers/testcontainers-go v0.27.0 + github.com/testcontainers/testcontainers-go v0.28.0 github.com/valyala/fasthttp v1.51.0 github.com/virtru/access-pdp v1.11.0 google.golang.org/grpc v1.61.0 @@ -33,7 +34,10 @@ require ( gotest.tools/v3 v3.5.0 ) -replace github.com/opentdf/platform/protocol/go => ./protocol/go +replace ( + github.com/opentdf/platform/protocol/go => ./protocol/go + github.com/opentdf/platform/sdk => ./sdk +) require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 // indirect @@ -54,8 +58,8 @@ require ( github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.7+incompatible // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/docker v25.0.2+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -68,6 +72,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/google/cel-go v0.18.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -88,6 +93,7 @@ require ( github.com/moby/locker v1.0.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -107,7 +113,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/sethvargo/go-retry v0.2.4 // indirect - github.com/shirou/gopsutil/v3 v3.23.11 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/go.sum b/go.sum index baa431dd66..4a74ec02ec 100644 --- a/go.sum +++ b/go.sum @@ -71,12 +71,12 @@ github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWa github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY= +github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -232,6 +232,8 @@ github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vyg github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -288,8 +290,8 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= -github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= -github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -328,8 +330,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/testcontainers/testcontainers-go v0.27.0 h1:IeIrJN4twonTDuMuBNQdKZ+K97yd7VrmNGu+lDpYcDk= -github.com/testcontainers/testcontainers-go v0.27.0/go.mod h1:+HgYZcd17GshBUZv9b+jKFJ198heWPQq3KQIp2+N+7U= +github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8= +github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -371,6 +373,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruO go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= diff --git a/internal/config/config.go b/internal/config/config.go index aeab9aa580..15b612143c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -17,15 +17,20 @@ import ( type ServiceConfig struct { Enabled bool `yaml:"enabled"` + Remote RemoteServiceConfig `yaml:"remote"` ExtraProps map[string]interface{} `json:"-"` } +type RemoteServiceConfig struct { + Endpoint string `yaml:"endpoint"` +} + type Config struct { DB db.Config `yaml:"db"` OPA opa.Config `yaml:"opa"` Server server.Config `yaml:"server"` Logger logger.Config `yaml:"logger"` - Services map[string]ServiceConfig `yaml:"services" default:"{\"policy\": {\"enabled\": true}, \"health\": {\"enabled\": true}, \"wellknown\": {\"enabled\": true}}"` + Services map[string]ServiceConfig `yaml:"services" default:"{\"policy\": {\"enabled\": true}, \"health\": {\"enabled\": true}, \"authorization\": {\"enabled\": true}, \"wellknown\": {\"enabled\": true}}"` } type Error string diff --git a/pkg/server/services.go b/pkg/server/services.go index d5a1c7e664..c833088463 100644 --- a/pkg/server/services.go +++ b/pkg/server/services.go @@ -2,6 +2,7 @@ package server import ( "github.com/opentdf/platform/pkg/serviceregistry" + "github.com/opentdf/platform/services/authorization" "github.com/opentdf/platform/services/health" "github.com/opentdf/platform/services/kas" "github.com/opentdf/platform/services/kasregistry" @@ -21,6 +22,7 @@ func registerServices() error { attributes.NewRegistration(), kasregistry.NewRegistration(), health.NewRegistration(), + authorization.NewRegistration(), kas.NewRegistration(), wellknown.NewRegistration(), } { diff --git a/pkg/server/start.go b/pkg/server/start.go index 59579dfca7..067d9deafc 100644 --- a/pkg/server/start.go +++ b/pkg/server/start.go @@ -14,6 +14,7 @@ import ( "github.com/opentdf/platform/internal/opa" "github.com/opentdf/platform/internal/server" "github.com/opentdf/platform/pkg/serviceregistry" + "github.com/opentdf/platform/sdk" wellknown "github.com/opentdf/platform/services/wellknownconfiguration" ) @@ -89,8 +90,29 @@ func Start(f ...StartOptions) error { return fmt.Errorf("issue registering services: %w", err) } + // Create the SDK client for services to use + var sdkOptions []sdk.Option + for name, service := range conf.Services { + if service.Remote.Endpoint == "" && service.Enabled { + switch name { + case "policy": + sdkOptions = append(sdkOptions, sdk.WithCustomPolicyConnection(otdf.GrpcInProcess.Conn())) + case "authorization": + sdkOptions = append(sdkOptions, sdk.WithCustomAuthorizationConnection(otdf.GrpcInProcess.Conn())) + } + } + } + + client, err := sdk.New("", sdkOptions...) + if err != nil { + slog.Error("issue creating sdk client", slog.String("error", err.Error())) + return fmt.Errorf("issue creating sdk client: %w", err) + } + + defer client.Close() + slog.Info("starting services") - if err := startServices(*conf, otdf, dbClient, eng); err != nil { + if err := startServices(*conf, otdf, dbClient, eng, client); err != nil { slog.Error("issue starting services", slog.String("error", err.Error())) return fmt.Errorf("issue starting services: %w", err) } @@ -132,7 +154,7 @@ func createDatabaseClient(ctx context.Context, conf db.Config) (*db.Client, erro return dbClient, nil } -func startServices(cfg config.Config, otdf *server.OpenTDFServer, dbClient *db.Client, eng *opa.Engine) error { +func startServices(cfg config.Config, otdf *server.OpenTDFServer, dbClient *db.Client, eng *opa.Engine, client *sdk.SDK) error { // Iterate through the registered namespaces for ns, registers := range serviceregistry.RegisteredServices { // Check if the service is enabled @@ -148,12 +170,16 @@ func startServices(cfg config.Config, otdf *server.OpenTDFServer, dbClient *db.C OTDF: otdf, DBClient: dbClient, Engine: eng, + SDK: client, WellKnownConfig: wellknown.RegisterConfiguration, }) // Register the service with the gRPC server otdf.GrpcServer.RegisterService(r.ServiceDesc, impl) + // Register the service with in process gRPC server + otdf.GrpcInProcess.GetGrpcServer().RegisterService(r.ServiceDesc, impl) + // Register the service with the gRPC gateway if err := handler(context.Background(), otdf.Mux, impl); err != nil { slog.Error("failed to start service", slog.String("namespace", r.Namespace), slog.String("error", err.Error())) diff --git a/pkg/server/start_test.go b/pkg/server/start_test.go index 9a6415495f..8f6022cdac 100644 --- a/pkg/server/start_test.go +++ b/pkg/server/start_test.go @@ -67,7 +67,7 @@ func Test_Start_When_Extra_Service_Registered_Expect_Response(t *testing.T) { Enabled: true, }, }, - }, s, nil, nil) + }, s, nil, nil, nil) assert.Nil(t, err) s.Start() diff --git a/pkg/serviceregistry/serviceregistry.go b/pkg/serviceregistry/serviceregistry.go index f0d2b7e211..72b71cfe46 100644 --- a/pkg/serviceregistry/serviceregistry.go +++ b/pkg/serviceregistry/serviceregistry.go @@ -5,6 +5,8 @@ import ( "fmt" "log/slog" + "github.com/opentdf/platform/sdk" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/opentdf/platform/internal/config" "github.com/opentdf/platform/internal/db" @@ -18,6 +20,7 @@ type RegistrationParams struct { OTDF *server.OpenTDFServer DBClient *db.Client Engine *opa.Engine + SDK *sdk.SDK WellKnownConfig func(namespace string, config any) error } type HandlerServer func(ctx context.Context, mux *runtime.ServeMux, server any) error diff --git a/sdk/options.go b/sdk/options.go index 783b2a1bd2..592aef6820 100644 --- a/sdk/options.go +++ b/sdk/options.go @@ -16,6 +16,8 @@ type config struct { clientCredentials oauth.ClientCredentials tokenEndpoint string scopes []string + policyConn *grpc.ClientConn + authorizationConn *grpc.ClientConn unwrapper Unwrapper } @@ -59,3 +61,15 @@ func WithAuthConfig(authConfig AuthConfig) Option { c.unwrapper = &authConfig } } + +func WithCustomPolicyConnection(conn *grpc.ClientConn) Option { + return func(c *config) { + c.policyConn = conn + } +} + +func WithCustomAuthorizationConnection(conn *grpc.ClientConn) Option { + return func(c *config) { + c.authorizationConn = conn + } +} diff --git a/sdk/sdk.go b/sdk/sdk.go index fa5be48fdd..5a8969866f 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -54,20 +54,41 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) { unwrapper := cfg.unwrapper - conn, err := grpc.Dial(platformEndpoint, cfg.build()...) - if err != nil { - return nil, errors.Join(ErrGrpcDialFailed, err) + var ( + defaultConn *grpc.ClientConn + policyConn *grpc.ClientConn + authorizationConn *grpc.ClientConn + ) + + if platformEndpoint != "" { + var err error + defaultConn, err = grpc.Dial(platformEndpoint, cfg.build()...) + if err != nil { + return nil, errors.Join(ErrGrpcDialFailed, err) + } + } + + if cfg.policyConn != nil { + policyConn = cfg.policyConn + } else { + policyConn = defaultConn + } + + if cfg.authorizationConn != nil { + authorizationConn = cfg.authorizationConn + } else { + authorizationConn = defaultConn } return &SDK{ - conn: conn, + conn: defaultConn, unwrapper: unwrapper, - Attributes: attributes.NewAttributesServiceClient(conn), - Namespaces: namespaces.NewNamespaceServiceClient(conn), - ResourceMapping: resourcemapping.NewResourceMappingServiceClient(conn), - SubjectMapping: subjectmapping.NewSubjectMappingServiceClient(conn), - KeyAccessServerRegistry: kasregistry.NewKeyAccessServerRegistryServiceClient(conn), - Authorization: authorization.NewAuthorizationServiceClient(conn), + Attributes: attributes.NewAttributesServiceClient(policyConn), + Namespaces: namespaces.NewNamespaceServiceClient(policyConn), + ResourceMapping: resourcemapping.NewResourceMappingServiceClient(policyConn), + SubjectMapping: subjectmapping.NewSubjectMappingServiceClient(policyConn), + KeyAccessServerRegistry: kasregistry.NewKeyAccessServerRegistryServiceClient(policyConn), + Authorization: authorization.NewAuthorizationServiceClient(authorizationConn), }, nil } diff --git a/services/authorization/authorization.go b/services/authorization/authorization.go index e6ff4e4531..e3991505e1 100644 --- a/services/authorization/authorization.go +++ b/services/authorization/authorization.go @@ -15,37 +15,35 @@ import ( "github.com/open-policy-agent/opa/sdk" "github.com/opentdf/platform/internal/entitlements" "github.com/opentdf/platform/internal/opa" + "github.com/opentdf/platform/pkg/serviceregistry" "github.com/opentdf/platform/protocol/go/authorization" attr "github.com/opentdf/platform/protocol/go/policy/attributes" "github.com/opentdf/platform/protocol/go/policy/subjectmapping" + otdf "github.com/opentdf/platform/sdk" "github.com/opentdf/platform/services" - "google.golang.org/grpc" ) type AuthorizationService struct { authorization.UnimplementedAuthorizationServiceServer eng *opa.Engine - cc *grpc.ClientConn + sdk *otdf.SDK } -func NewAuthorizationServer(g *grpc.Server, cc *grpc.ClientConn, s *runtime.ServeMux, eng *opa.Engine) error { - as := &AuthorizationService{ - eng: eng, - cc: cc, - } - authorization.RegisterAuthorizationServiceServer(g, as) - err := authorization.RegisterAuthorizationServiceHandlerServer(context.Background(), s, as) - if err != nil { - return fmt.Errorf("failed to register authorization service handler: %w", err) +func NewRegistration() serviceregistry.Registration { + return serviceregistry.Registration{ + Namespace: "authorization", + ServiceDesc: &authorization.AuthorizationService_ServiceDesc, + RegisterFunc: func(srp serviceregistry.RegistrationParams) (any, serviceregistry.HandlerServer) { + return &AuthorizationService{eng: srp.Engine, sdk: srp.SDK}, func(ctx context.Context, mux *runtime.ServeMux, server any) error { + return authorization.RegisterAuthorizationServiceHandlerServer(ctx, mux, server.(authorization.AuthorizationServiceServer)) + } + }, } - return nil } func (as AuthorizationService) GetDecisions(ctx context.Context, req *authorization.GetDecisionsRequest) (*authorization.GetDecisionsResponse, error) { slog.DebugContext(ctx, "getting decisions") - attrClient := attr.NewAttributesServiceClient(as.cc) - // Temporary canned echo response with permit decision for all requested decision/entity/ra combos rsp := &authorization.GetDecisionsResponse{ DecisionResponses: make([]*authorization.DecisionResponse, 0), @@ -54,7 +52,7 @@ func (as AuthorizationService) GetDecisions(ctx context.Context, req *authorizat for _, ra := range dr.ResourceAttributes { slog.Debug("getting resource attributes", slog.String("FQNs", strings.Join(ra.AttributeFqns, ", "))) - attrs, err := attrClient.GetAttributesByValueFqns(ctx, &attr.GetAttributesByValueFqnsRequest{ + attrs, err := as.sdk.Attributes.GetAttributesByValueFqns(ctx, &attr.GetAttributesByValueFqnsRequest{ Fqns: ra.AttributeFqns, }) if err != nil {