diff --git a/ctrl/internal/application/app.go b/ctrl/internal/application/app.go index efbcbc62a0e24e0757448bf4bbd972483a5d85b9..f17d22d8797cb1d0e59bae11b3a6ceebea8660d7 100644 --- a/ctrl/internal/application/app.go +++ b/ctrl/internal/application/app.go @@ -4,16 +4,20 @@ import ( "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/core/ports" "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/infrastructure/interaction" "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/infrastructure/store" + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/service" "code.fbi.h-da.de/danet/costaquanta/libs/logging" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.uber.org/zap" ) type Application struct { - tracer *sdktrace.TracerProvider - HealthServer *interaction.HealthServer - RoutingServer *interaction.RoutingServer - KmsStore ports.KmsStore + tracer *sdktrace.TracerProvider + + HealthServer *interaction.HealthServer + + RoutingService ports.RoutingService + RoutingServer *interaction.RoutingServer + KmsStore ports.KmsStore } func NewApplication(log *zap.Logger, tracer *sdktrace.TracerProvider) *Application { @@ -21,9 +25,13 @@ func NewApplication(log *zap.Logger, tracer *sdktrace.TracerProvider) *Applicati healthTracer := tracer.Tracer("healthServer") healthSrv := interaction.NewHealthServer(healthLogger, healthTracer) + routingServiceLogger := logging.CreateChildLogger(log, "routingService") + routingServiceTracer := tracer.Tracer("routingService") + rSrv := service.NewRoutingService(routingServiceLogger, routingServiceTracer) + routingLogger := logging.CreateChildLogger(log, "routingServer") routingTracer := tracer.Tracer("routingServer") - routerSrv := interaction.NewRoutingServer(routingLogger, routingTracer) + routerSrv := interaction.NewRoutingServer(routingLogger, routingTracer, rSrv) // Create the in-memory KMS store, as we currently have no other option. // Can be replaced by a store choice via flags or config file later. @@ -32,9 +40,10 @@ func NewApplication(log *zap.Logger, tracer *sdktrace.TracerProvider) *Applicati kmsStore := store.NewInMemoryKmsStore(kmsStoreLogger, kmsStoreTracer) return &Application{ - tracer: tracer, - HealthServer: healthSrv, - RoutingServer: routerSrv, - KmsStore: kmsStore, + tracer: tracer, + HealthServer: healthSrv, + RoutingServer: routerSrv, + RoutingService: rSrv, + KmsStore: kmsStore, } } diff --git a/ctrl/internal/core/model/route.go b/ctrl/internal/core/model/route.go new file mode 100644 index 0000000000000000000000000000000000000000..efffdab924594601fa94a231369c53580eea2d5c --- /dev/null +++ b/ctrl/internal/core/model/route.go @@ -0,0 +1,17 @@ +package model + +type RoutingCryptoAlgorithm int + +const ( + Unspecified RoutingCryptoAlgorithm = iota + AES256GCM + OTP + Hybrid +) + +type Route struct { + ID string + NextHopID string + PrevHopID string + CryptoAlgorithm RoutingCryptoAlgorithm +} diff --git a/ctrl/internal/core/ports/routing.go b/ctrl/internal/core/ports/routing.go new file mode 100644 index 0000000000000000000000000000000000000000..2cdd282edf7085f3fa479e4a051cc78e72971f67 --- /dev/null +++ b/ctrl/internal/core/ports/routing.go @@ -0,0 +1,16 @@ +package ports + +import ( + "context" + + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/core/model" + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/service" +) + +type RoutingService interface { + RequestRoute(context.Context, service.RequestRouteDTO) (model.Route, error) + AnnouncePayloadRelay(context.Context, service.AnnouncePayloadRelayRequestDTO) error + PayloadRelayFinished(context.Context, service.PayloadRelayFinishedRequestDTO) error + PayloadRelayError(context.Context, service.PayloadRelayErrorDTO) error + PushKeyStoreFillLevel(context.Context, service.PushKeyStoreFillLevelRequestDTO) error +} diff --git a/ctrl/internal/infrastructure/interaction/health_test.go b/ctrl/internal/infrastructure/interaction/health_test.go new file mode 100644 index 0000000000000000000000000000000000000000..78ffa296c69c8bf1376ded40fd7e95818fea785d --- /dev/null +++ b/ctrl/internal/infrastructure/interaction/health_test.go @@ -0,0 +1,112 @@ +package interaction + +import ( + "context" + "log" + "net" + "testing" + + pb "code.fbi.h-da.de/danet/costaquanta/gen/go/ctrl/v1" + "code.fbi.h-da.de/danet/costaquanta/libs/tracing" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/test/bufconn" +) + +func newHealthServer() pb.HealthCtrlServer { + logs := zap.NewExample() + + traceProvider := tracing.GetTestTracer() + defer func() { + if err := traceProvider.Shutdown(context.Background()); err != nil { + logs.Info(err.Error()) + } + }() + + srv := NewHealthServer( + zap.NewExample().Sugar(), + traceProvider.Tracer("test"), + ) + + return srv +} + +//nolint:staticcheck +func getTestServer(ctx context.Context) (pb.HealthCtrlClient, func()) { + buffer := 101024 * 1024 + lis := bufconn.Listen(buffer) + + baseServer := grpc.NewServer() + pb.RegisterHealthCtrlServer(baseServer, newHealthServer()) + go func() { + if err := baseServer.Serve(lis); err != nil { + log.Printf("error serving server: %v", err) + } + }() + + conn, err := grpc.DialContext(ctx, "", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Printf("error connecting to server: %v", err) + } + + closer := func() { + err := lis.Close() + if err != nil { + log.Printf("error closing listener: %v", err) + } + baseServer.Stop() + } + + client := pb.NewHealthCtrlClient(conn) + + return client, closer +} + +//nolint:paralleltest +func TestHealthServer_Health(t *testing.T) { + ctx := context.Background() + + client, closer := getTestServer(ctx) + defer closer() + + type expectation struct { + out *pb.HealthResponse + err error + } + + tests := map[string]struct { + in *pb.HealthRequest + expected expectation + }{ + "echo-should-succeed": { + in: &pb.HealthRequest{ + Message: "hello-world", + }, + expected: expectation{ + out: &pb.HealthResponse{ + Message: "hello-world", + }, + err: nil, + }, + }, + } + + for scenario, tt := range tests { + t.Run(scenario, func(t *testing.T) { + out, err := client.Health(ctx, tt.in) + if err != nil { + if tt.expected.err.Error() != err.Error() { + t.Errorf("Err -> \nWant: %q\nGot: %q\n", tt.expected.err, err) + } + } else { + if tt.expected.out.GetMessage() != out.GetMessage() { + t.Errorf("Out -> \nWant: %q\nGot : %q", tt.expected.out, out) + } + } + }) + } +} diff --git a/ctrl/internal/infrastructure/interaction/routing.go b/ctrl/internal/infrastructure/interaction/routing.go index 52ebd7bd620ddc787cedcbfd8d109f503910a496..d3d0d0a2c157762a2607feb3072c3a3b6d64fed0 100644 --- a/ctrl/internal/infrastructure/interaction/routing.go +++ b/ctrl/internal/infrastructure/interaction/routing.go @@ -1,9 +1,17 @@ package interaction import ( + "context" + "fmt" + + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/core/model" + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/core/ports" + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/service" pb "code.fbi.h-da.de/danet/costaquanta/gen/go/ctrl/v1" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type RoutingServer struct { @@ -11,13 +19,128 @@ type RoutingServer struct { logger *zap.SugaredLogger tracer trace.Tracer + + srv ports.RoutingService } -func NewRoutingServer(logger *zap.SugaredLogger, tracer trace.Tracer) *RoutingServer { +func NewRoutingServer( + logger *zap.SugaredLogger, + tracer trace.Tracer, + srv *service.Routing, +) *RoutingServer { s := &RoutingServer{ logger: logger, tracer: tracer, + srv: srv, } return s } + +func (r *RoutingServer) RequestRoute( + ctx context.Context, + req *pb.RequestRouteRequest, +) (*pb.RequestRouteResponse, error) { + _, span := r.tracer.Start(ctx, "request-route") + defer span.End() + + dto := service.RequestRouteDTO{ + SourceKMSID: req.GetSourceKmsId(), + TargetKMSID: req.GetTargetKmsId(), + // TODO: I'm pretty sure this doesn't work yet. + // I need to figure out if there is an pretty way to convert an proto + // enum into a Go enum. + CryptoAlgorithm: model.RoutingCryptoAlgorithm(req.GetCryptoAlgorithm()), + } + + route, err := r.srv.RequestRoute(ctx, dto) + if err != nil { + return nil, status.Errorf(codes.Internal, "%s", fmt.Sprintf("%s", err)) + } + + return &pb.RequestRouteResponse{ + RouteId: route.ID, + }, nil +} + +func (r *RoutingServer) AnnouncePayloadRelay( + ctx context.Context, + req *pb.AnnouncePayloadRelayRequest, +) (*pb.AnnouncePayloadRelayResponse, error) { + _, span := r.tracer.Start(ctx, "announce-payload-relay") + defer span.End() + + dto := service.AnnouncePayloadRelayRequestDTO{ + RequestID: req.GetRequestId(), + RouteID: req.GetRouteId(), + } + + err := r.srv.AnnouncePayloadRelay(ctx, dto) + if err != nil { + return nil, status.Errorf(codes.Internal, "%s", fmt.Sprintf("%s", err)) + } + + return &pb.AnnouncePayloadRelayResponse{}, nil +} + +func (r *RoutingServer) PayloadRelayFinished( + ctx context.Context, + req *pb.PayloadRelayFinishedRequest, +) (*pb.PayloadRelayFinishedResponse, error) { + _, span := r.tracer.Start(ctx, "payload-relay-finished") + defer span.End() + + dto := service.PayloadRelayFinishedRequestDTO{ + RequestID: req.GetRequestId(), + RouteID: req.GetRouteId(), + } + + err := r.srv.PayloadRelayFinished(ctx, dto) + if err != nil { + return nil, status.Errorf(codes.Internal, "%s", fmt.Sprintf("%s", err)) + } + + return &pb.PayloadRelayFinishedResponse{}, nil +} + +func (r *RoutingServer) PayloadRelayError( + ctx context.Context, + req *pb.PayloadRelayErrorRequest, +) (*pb.PayloadRelayErrorResponse, error) { + _, span := r.tracer.Start(ctx, "payload-relay-error") + defer span.End() + + dto := service.PayloadRelayErrorDTO{ + RequestID: req.GetRequestId(), + RouteID: req.GetRouteId(), + ErrorType: req.GetError().Enum().String(), + } + + err := r.srv.PayloadRelayError(ctx, dto) + if err != nil { + return nil, status.Errorf(codes.Internal, "%s", fmt.Sprintf("%s", err)) + } + + return &pb.PayloadRelayErrorResponse{}, nil +} + +func (r *RoutingServer) PushKeyStoreFillLevel( + ctx context.Context, + req *pb.PushKeyStoreFillLevelRequest, +) (*pb.PushKeyStoreFillLevelResponse, error) { + _, span := r.tracer.Start(ctx, "push-key-store-fill-level") + defer span.End() + + dto := service.PushKeyStoreFillLevelRequestDTO{ + KMSID: req.GetKmsId(), + PeerID: req.GetPeerId(), + FillLevel: req.GetFillLevel(), + } + + err := r.srv.PushKeyStoreFillLevel(ctx, dto) + if err != nil { + return nil, status.Errorf(codes.Internal, "%s", fmt.Sprintf("%s", err)) + } + + return &pb.PushKeyStoreFillLevelResponse{}, nil +} diff --git a/ctrl/internal/infrastructure/interaction/routing_test.go b/ctrl/internal/infrastructure/interaction/routing_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5b2f5ff987898beed151fb5e1d082d7fce549868 --- /dev/null +++ b/ctrl/internal/infrastructure/interaction/routing_test.go @@ -0,0 +1,118 @@ +package interaction + +import ( + "context" + "log" + "net" + "testing" + + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/service" + pb "code.fbi.h-da.de/danet/costaquanta/gen/go/ctrl/v1" + sharedv1 "code.fbi.h-da.de/danet/costaquanta/gen/go/shared/v1" + "code.fbi.h-da.de/danet/costaquanta/libs/tracing" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/test/bufconn" +) + +func newRoutingServer() pb.CtrlRoutingServiceServer { + logs := zap.NewExample() + + traceProvider := tracing.GetTestTracer() + defer func() { + if err := traceProvider.Shutdown(context.Background()); err != nil { + logs.Info(err.Error()) + } + }() + + svc := service.NewRoutingService(logs.Sugar(), traceProvider.Tracer("test")) + srv := NewRoutingServer( + zap.NewExample().Sugar(), + traceProvider.Tracer("test"), + svc, + ) + + return srv +} + +//nolint:staticcheck +func getTestRoutingServer(ctx context.Context) (pb.CtrlRoutingServiceClient, func()) { + buffer := 101024 * 1024 + lis := bufconn.Listen(buffer) + + baseServer := grpc.NewServer() + pb.RegisterCtrlRoutingServiceServer(baseServer, newRoutingServer()) + go func() { + if err := baseServer.Serve(lis); err != nil { + log.Printf("error serving server: %v", err) + } + }() + + conn, err := grpc.DialContext(ctx, "", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Printf("error connecting to server: %v", err) + } + + closer := func() { + err := lis.Close() + if err != nil { + log.Printf("error closing listener: %v", err) + } + baseServer.Stop() + } + + client := pb.NewCtrlRoutingServiceClient(conn) + + return client, closer +} + +//nolint:paralleltest +func TestRoutingServer_RequestRoute(t *testing.T) { + ctx := context.Background() + + client, closer := getTestRoutingServer(ctx) + defer closer() + + type expectation struct { + out *pb.RequestRouteResponse + err error + } + + tests := map[string]struct { + in *pb.RequestRouteRequest + expected expectation + }{ + "first-test": { + in: &pb.RequestRouteRequest{ + SourceKmsId: "1337", + TargetKmsId: "42", + CryptoAlgorithm: sharedv1.CryptoAlgorithm_CRYPTO_ALGORITHM_AES_256_GCM, + }, + expected: expectation{ + out: &pb.RequestRouteResponse{ + RouteId: "1337", + }, + err: nil, + }, + }, + } + + for scenario, tt := range tests { + t.Run(scenario, func(t *testing.T) { + out, err := client.RequestRoute(ctx, tt.in) + if err != nil { + if tt.expected.err.Error() != err.Error() { + t.Errorf("Err -> \nWant: %q\nGot: %q\n", tt.expected.err, err) + } + } else { + if tt.expected.out.GetRouteId() != out.GetRouteId() { + t.Errorf("Out -> \nWant: %q\nGot : %q", tt.expected.out, out) + } + } + }) + } +} diff --git a/ctrl/internal/service/routing.go b/ctrl/internal/service/routing.go new file mode 100644 index 0000000000000000000000000000000000000000..7d29c80bf0c61c54d9532c31adf7784a084fab0f --- /dev/null +++ b/ctrl/internal/service/routing.go @@ -0,0 +1,101 @@ +package service + +import ( + "context" + "time" + + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/core/model" + "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +type Routing struct { + logger *zap.SugaredLogger + tracer trace.Tracer +} + +type RequestRouteDTO struct { + SourceKMSID string + TargetKMSID string + CryptoAlgorithm model.RoutingCryptoAlgorithm +} + +type AnnouncePayloadRelayRequestDTO struct { + RequestID string + RouteID string +} + +type PayloadRelayFinishedRequestDTO struct { + RequestID string + RouteID string +} + +type PayloadRelayErrorDTO struct { + RequestID string + RouteID string + ErrorType string +} + +type PushKeyStoreFillLevelRequestDTO struct { + KMSID string + PeerID string + FillLevel int64 +} + +func NewRoutingService(logger *zap.SugaredLogger, tracer trace.Tracer) *Routing { + return &Routing{ + logger: logger, + tracer: tracer, + } +} + +func (r *Routing) RequestRoute(ctx context.Context, req RequestRouteDTO) (model.Route, error) { + _, span := r.tracer.Start(ctx, "request-route") + defer span.End() + + // calculate route + time.Sleep(time.Second) + + return model.Route{ + ID: "1337", + }, nil +} + +func (r *Routing) AnnouncePayloadRelay( + ctx context.Context, + req AnnouncePayloadRelayRequestDTO, +) error { + _, span := r.tracer.Start(ctx, "announce-payload-relay") + defer span.End() + + // gossip routes to KMS + + return nil +} + +func (r *Routing) PayloadRelayFinished( + ctx context.Context, + req PayloadRelayFinishedRequestDTO, +) error { + _, span := r.tracer.Start(ctx, "payload-relay-finished") + defer span.End() + + return nil +} + +func (r *Routing) PayloadRelayError(ctx context.Context, req PayloadRelayErrorDTO) error { + _, span := r.tracer.Start(ctx, "payload-relay-error") + defer span.End() + + return nil +} + +func (r *Routing) PushKeyStoreFillLevel( + ctx context.Context, + req PushKeyStoreFillLevelRequestDTO, +) error { + _, span := r.tracer.Start(ctx, "push-key-store-fill-level") + defer span.End() + + return nil +} diff --git a/ctrl/internal/service/routing_test.go b/ctrl/internal/service/routing_test.go new file mode 100644 index 0000000000000000000000000000000000000000..277c2674449c292cd82bc3425779954b34350f1c --- /dev/null +++ b/ctrl/internal/service/routing_test.go @@ -0,0 +1,221 @@ +package service + +import ( + "context" + "reflect" + "testing" + + "code.fbi.h-da.de/danet/costaquanta/ctrl/internal/core/model" + "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" +) + +func TestNewRouting(t *testing.T) { + t.Parallel() + + type args struct { + logger *zap.SugaredLogger + tracer trace.Tracer + } + tests := []struct { + name string + args args + want *Routing + }{ + { + name: "test", + args: args{ + logger: zap.NewNop().Sugar(), + tracer: noop.NewTracerProvider().Tracer("test"), + }, + want: &Routing{ + logger: zap.NewNop().Sugar(), + tracer: noop.NewTracerProvider().Tracer("test"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := NewRoutingService(tt.args.logger, tt.args.tracer); !reflect.DeepEqual( + got, + tt.want, + ) { + t.Errorf("NewRouting() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRouting_RequestRoute(t *testing.T) { + t.Parallel() + + type fields struct { + logger *zap.SugaredLogger + tracer trace.Tracer + } + type args struct { + ctx context.Context + req RequestRouteDTO + } + tests := []struct { + name string + fields fields + args args + want model.Route + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + r := &Routing{ + logger: tt.fields.logger, + tracer: tt.fields.tracer, + } + got, err := r.RequestRoute(tt.args.ctx, tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("Routing.RequestRoute() error = %v, wantErr %v", err, tt.wantErr) + + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Routing.RequestRoute() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRouting_AnnouncePayloadRelay(t *testing.T) { + t.Parallel() + + type fields struct { + logger *zap.SugaredLogger + tracer trace.Tracer + } + type args struct { + ctx context.Context + req AnnouncePayloadRelayRequestDTO + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + r := &Routing{ + logger: tt.fields.logger, + tracer: tt.fields.tracer, + } + if err := r.AnnouncePayloadRelay(tt.args.ctx, tt.args.req); (err != nil) != tt.wantErr { + t.Errorf("Routing.AnnouncePayloadRelay() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestRouting_PayloadRelayFinished(t *testing.T) { + t.Parallel() + + type fields struct { + logger *zap.SugaredLogger + tracer trace.Tracer + } + type args struct { + ctx context.Context + req PayloadRelayFinishedRequestDTO + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + r := &Routing{ + logger: tt.fields.logger, + tracer: tt.fields.tracer, + } + if err := r.PayloadRelayFinished(tt.args.ctx, tt.args.req); (err != nil) != tt.wantErr { + t.Errorf("Routing.PayloadRelayFinished() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestRouting_PayloadRelayError(t *testing.T) { + t.Parallel() + + type fields struct { + logger *zap.SugaredLogger + tracer trace.Tracer + } + type args struct { + ctx context.Context + req PayloadRelayErrorDTO + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + r := &Routing{ + logger: tt.fields.logger, + tracer: tt.fields.tracer, + } + if err := r.PayloadRelayError(tt.args.ctx, tt.args.req); (err != nil) != tt.wantErr { + t.Errorf("Routing.PayloadRelayError() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestRouting_PushKeyStoreFillLevel(t *testing.T) { + t.Parallel() + + type fields struct { + logger *zap.SugaredLogger + tracer trace.Tracer + } + type args struct { + ctx context.Context + req PushKeyStoreFillLevelRequestDTO + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + r := &Routing{ + logger: tt.fields.logger, + tracer: tt.fields.tracer, + } + if err := r.PushKeyStoreFillLevel(tt.args.ctx, tt.args.req); (err != nil) != tt.wantErr { + t.Errorf("Routing.PushKeyStoreFillLevel() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/libs/tracing/tracing.go b/libs/tracing/tracing.go index 67e5a9c6603b68cee86ce2529709cef38f98c207..64fd6099f36207a983dd163cbac08e24fa66a092 100644 --- a/libs/tracing/tracing.go +++ b/libs/tracing/tracing.go @@ -58,3 +58,12 @@ func GetTracer(ctx context.Context, target TraceExportTarget) (*sdktrace.TracerP func SetRuntimeSettings(serviceName string) { _ = os.Setenv("OTEL_SERVICE_NAME", serviceName) } + +func GetTestTracer() *sdktrace.TracerProvider { + traceProvider, err := GetTracer(context.Background(), Stdout) + if err != nil { + panic(err) + } + + return traceProvider +}