Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

small changes for deployment with docker compose #498

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,33 +1,55 @@
ENV=development
# port the servers listens on
PORT=8080
# replace me in a production environment
AUTHENTICATION_JWT_SIGNING_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAs+6r50m7qqLHHy7CvfmJPnAi+t/tubi7DPSM2jvA1etT1jEX\nrwbFbmooOu9LTgmjmxOq01p+XwkW1f7iPZViKrf7dEDEuqmpqYG9jPX4G/7xFcci\nGn1iSOiNx9awIKSYZa1wodlMCRM081DGqFNDMf1PScWIyM40nIwaGqLZht4HcOAq\nLbKDa15bxubBqZ9o/YnE1KmyBfq1tTnk0KzAb12Axt0xN4qB2zktsV/LLds+szMk\n/gRHjann1+fCZvxw1JzzRPtgeHLLYzn4ks3mwzy67RO3q/663KPZCsuYhlNCsMqp\n/HAbrF5PaihqzCZqLTDoIXXciCFFgwtLwm951wIDAQABAoIBAQCpb60tJX+1VYeQ\n06XK43rb8xjdiZUA+PYbYwZoUzBpwSq3Xo9g4E12hjzQEpqlJ+qKk+CfGm457AM3\nDMfbGhrRA2Oku4EGDdKYrnXikZVMN6yqx1RUAZJV+bfZYU+Fzbk8tjCEGG3DdfS8\n02nfBFkYb+MEIyGFhriAWmYSgxu4JTN0XRTyPqBytoSLqVCFbv0/yV2oJQDaXW08\nWAA8JtWhzqxACbFnPYe0hYUnrCA71t0v1P/N5uB4kKxI0tulGtW84noSyWA2LSdn\nJlKQW5WsyeMulGBMnIpj/OQJtQErupoITsh1TNi+6ffGgmuMCT1za70DHXVq9Ihu\nkpKBe0wRAoGBAOSarLfNvsS2lTH/8zPyhWBddCS5CfQAeUFLD5xWhQ7/6SenYzYY\n+oiiH2uL7d8grkobX5QLVvJ5ZXziYWoKgJe3SlrvRuNJZCAxvuynUCahhCT+chwW\nGz7ihXh3bGD0gtO6iogGBfrAkvRQnorkdSmVEZd1PsJV/lXp8LKgxJ91AoGBAMl+\ny/6NbzVHt9oQsrVrG/sCAOlqlfTt5KW6pI1WC4LoKBaGe+hy4emZ0G/M2feAJEPR\n92QrPRkVF5bVCjalJj42/7gQIl6r+DQ4+08gLB1MSpWua2M3UtEi/2gsMcQff/wg\n6kmNZObW5Jcnqpp6u72zQTQwF4H29XucV/Yw93abAoGADGvfIKmcSQIGv03CADuY\nRbEuQ2SOhuSTshmLApqs5jC/kXkF6gWXb18nx+c1iJ80+S/dlKS9F7XC7vM6CdIC\nRLwf3SsNNgJh32H0ltVMhJzYGk59EsuctWEHkZEjoW0HwstrBZMWNhbKpV3QD4n0\nV8sSxqEHRPX5ON/aRUp5BJUCgYEAlsymr2P6js2V80X7+Xqn/juJoyd6A0znioEd\nFgoHo3lMR09u/JC+Mq5DKOkPWAQ3H+rMU9NobpUyilf2xN7kuDtBNugcUO4zXCIp\nMxbI7URjrZJUHHUTLiIbNEOfG0DX8EJSFaoUkg7SFa5CKEsipt65Ne2oKkRBhLmF\nu2L6UXECgYBH1bpi0R6j7lIADtZtIJII/TezQbp+VK2R9qoNgkTnHoDjkRVR7v3m\n75wReMvTy1h0Qx/ROtStZz8d5uQuhdeJvbQPQR8KGFUFZDmVWxU+y15WI2H39FMA\nMireKxzCfGGtTsZnhDqYl9NuRPcAGYt5jvoERXlz7b69rkqQUrfy+Q==\n-----END RSA PRIVATE KEY-----"

# Env variables for the postgresql database
PG_HOSTNAME=localhost
PG_PORT=5432
PG_USER=postgres
PG_PASSWORD=marble

AUTHENTICATION_JWT_SIGNING_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAs+6r50m7qqLHHy7CvfmJPnAi+t/tubi7DPSM2jvA1etT1jEX\nrwbFbmooOu9LTgmjmxOq01p+XwkW1f7iPZViKrf7dEDEuqmpqYG9jPX4G/7xFcci\nGn1iSOiNx9awIKSYZa1wodlMCRM081DGqFNDMf1PScWIyM40nIwaGqLZht4HcOAq\nLbKDa15bxubBqZ9o/YnE1KmyBfq1tTnk0KzAb12Axt0xN4qB2zktsV/LLds+szMk\n/gRHjann1+fCZvxw1JzzRPtgeHLLYzn4ks3mwzy67RO3q/663KPZCsuYhlNCsMqp\n/HAbrF5PaihqzCZqLTDoIXXciCFFgwtLwm951wIDAQABAoIBAQCpb60tJX+1VYeQ\n06XK43rb8xjdiZUA+PYbYwZoUzBpwSq3Xo9g4E12hjzQEpqlJ+qKk+CfGm457AM3\nDMfbGhrRA2Oku4EGDdKYrnXikZVMN6yqx1RUAZJV+bfZYU+Fzbk8tjCEGG3DdfS8\n02nfBFkYb+MEIyGFhriAWmYSgxu4JTN0XRTyPqBytoSLqVCFbv0/yV2oJQDaXW08\nWAA8JtWhzqxACbFnPYe0hYUnrCA71t0v1P/N5uB4kKxI0tulGtW84noSyWA2LSdn\nJlKQW5WsyeMulGBMnIpj/OQJtQErupoITsh1TNi+6ffGgmuMCT1za70DHXVq9Ihu\nkpKBe0wRAoGBAOSarLfNvsS2lTH/8zPyhWBddCS5CfQAeUFLD5xWhQ7/6SenYzYY\n+oiiH2uL7d8grkobX5QLVvJ5ZXziYWoKgJe3SlrvRuNJZCAxvuynUCahhCT+chwW\nGz7ihXh3bGD0gtO6iogGBfrAkvRQnorkdSmVEZd1PsJV/lXp8LKgxJ91AoGBAMl+\ny/6NbzVHt9oQsrVrG/sCAOlqlfTt5KW6pI1WC4LoKBaGe+hy4emZ0G/M2feAJEPR\n92QrPRkVF5bVCjalJj42/7gQIl6r+DQ4+08gLB1MSpWua2M3UtEi/2gsMcQff/wg\n6kmNZObW5Jcnqpp6u72zQTQwF4H29XucV/Yw93abAoGADGvfIKmcSQIGv03CADuY\nRbEuQ2SOhuSTshmLApqs5jC/kXkF6gWXb18nx+c1iJ80+S/dlKS9F7XC7vM6CdIC\nRLwf3SsNNgJh32H0ltVMhJzYGk59EsuctWEHkZEjoW0HwstrBZMWNhbKpV3QD4n0\nV8sSxqEHRPX5ON/aRUp5BJUCgYEAlsymr2P6js2V80X7+Xqn/juJoyd6A0znioEd\nFgoHo3lMR09u/JC+Mq5DKOkPWAQ3H+rMU9NobpUyilf2xN7kuDtBNugcUO4zXCIp\nMxbI7URjrZJUHHUTLiIbNEOfG0DX8EJSFaoUkg7SFa5CKEsipt65Ne2oKkRBhLmF\nu2L6UXECgYBH1bpi0R6j7lIADtZtIJII/TezQbp+VK2R9qoNgkTnHoDjkRVR7v3m\n75wReMvTy1h0Qx/ROtStZz8d5uQuhdeJvbQPQR8KGFUFZDmVWxU+y15WI2H39FMA\nMireKxzCfGGtTsZnhDqYl9NuRPcAGYt5jvoERXlz7b69rkqQUrfy+Q==\n-----END RSA PRIVATE KEY-----"
GCS_INGESTION_BUCKET="data-ingestion-bucket"
GCS_CASE_MANAGER_BUCKET="case-manager-bucket"

# The frontend sign
# Used by the firebase sdk to validate the id tokens
FIREBASE_AUTH_EMULATOR_HOST="localhost:9099"

# When using firebase emulator, JWT Token are issued for this audience
GOOGLE_CLOUD_PROJECT="fake-project-id"
# Used:
# - by the firebase sdk to validate the id tokens
# - by the opentelemetry tracing agent to send traces to the collector
GOOGLE_CLOUD_PROJECT="tokyo-country-381508"

[email protected]
# Enable to activate GCP tracing
# If activated, you MUST have GCP application default credentials set up - see https://cloud.google.com/trace/docs/setup/go-ot
# Will send error logs (but the app will still run) if an unexisting project is specified in GOOGLE_CLOUD_PROJECT or
# if the runner does not have the correct permissions to use tracing on the project
ENABLE_GCP_TRACING=false

# Required if FAKE_AWS_S3 is false
# AWS_REGION=eu-west-3
# AWS_ACCESS_KEY=
# AWS_SECRET_KEY=
# 'liveness' (only log liveness requests) || 'all' (log all requests) || any other value for no request logs
REQUEST_LOGGING_LEVEL=all
# 'json' || 'text'
LOGGING_FORMAT=text

FAKE_AWS_S3=true
# configure the document storage backend with optional fake backends
GCS_INGESTION_BUCKET="data-ingestion-bucket"
GCS_CASE_MANAGER_BUCKET="case-manager-bucket"
FAKE_GCS=true

# Configure the AWS S3 backend for sending decision files
FAKE_AWS_S3=true
# The 3 below are required if FAKE_AWS_S3 is false
AWS_REGION=eu-west-3
AWS_ACCESS_KEY=
AWS_SECRET_KEY=

# Othe dependency configurations
SEGMENT_WRITE_KEY=UgkImFmHmBZAWh5fxIKBY3QtvlcBrhqQ
SENTRY_DSN=

# Metabase configuration
METABASE_SITE_URL="https://your_subdomain.metabaseapp.com"
METABASE_JWT_SIGNING_KEY="dummy"
METABASE_GLOBAL_DASHBOARD_ID=123
METABASE_GLOBAL_DASHBOARD_ID=123

# Used to create a first default admin user if no user exists
[email protected]
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ RUN go get

RUN CGO_ENABLED=0 go build -o /go/bin/app -ldflags="-X 'main.version=`git rev-parse --short HEAD`'"

FROM gcr.io/distroless/static
balzdur marked this conversation as resolved.
Show resolved Hide resolved
FROM alpine:3.19

COPY --from=build /go/bin/app /

ENV PORT=8080
ENV PORT=${PORT:-8080}
EXPOSE $PORT

ENTRYPOINT ["/app"]
4 changes: 1 addition & 3 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package api

import (
"fmt"
"log/slog"
"net/http"
"time"

Expand All @@ -26,14 +25,13 @@ func New(
usecases usecases.Usecases,
auth *Authentication,
tokenHandler *TokenHandler,
logger *slog.Logger,
) *http.Server {
s := &API{
router: router,
usecases: usecases,
}

s.routes(auth, tokenHandler, logger)
s.routes(auth, tokenHandler)

return &http.Server{
Addr: fmt.Sprintf("0.0.0.0:%s", port),
Expand Down
13 changes: 12 additions & 1 deletion api/middleware/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func WithIgnorePath(s []string) LoggerOption {
}
}

func NewLogging(logger *slog.Logger, options ...LoggerOption) gin.HandlerFunc {
func NewLogging(logger *slog.Logger, level string, options ...LoggerOption) gin.HandlerFunc {
l := &config{
logger: logger,
defaultLevel: slog.LevelInfo,
Expand All @@ -44,6 +44,17 @@ func NewLogging(logger *slog.Logger, options ...LoggerOption) gin.HandlerFunc {
}

return func(c *gin.Context) {
switch level {
case "liveness":
if c.Request.URL.Path != "/liveness" {
return
}
case "all":
// continue
default:
return
}

if _, ok := ignore[c.Request.URL.Path]; ok {
return
}
Expand Down
6 changes: 2 additions & 4 deletions api/routes.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package api

import (
"log/slog"
"net/http"
"time"

"github.com/checkmarble/marble-backend/api/middleware"
limits "github.com/gin-contrib/size"
"github.com/gin-contrib/timeout"
"github.com/gin-gonic/gin"
Expand All @@ -25,8 +23,8 @@ func timeoutMiddleware(duration time.Duration) gin.HandlerFunc {
)
}

func (api *API) routes(auth *Authentication, tokenHandler *TokenHandler, logger *slog.Logger) {
api.router.GET("/liveness", middleware.NewLogging(logger), HandleLivenessProbe)
func (api *API) routes(auth *Authentication, tokenHandler *TokenHandler) {
api.router.GET("/liveness", HandleLivenessProbe)
api.router.POST("/crash", HandleCrash)
api.router.POST("/token", tokenHandler.GenerateToken)

Expand Down
3 changes: 3 additions & 0 deletions infra/parse_signing_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"crypto/x509"
"encoding/pem"
"log"
"strings"
)

func MustParseSigningKey(privateKeyString string) *rsa.PrivateKey {
// when a multi-line env variable is passed to the docker container by docker-compose, it escapes the newlines
privateKeyString = strings.Replace(privateKeyString, "\\n", "\n", -1)
block, _ := pem.Decode([]byte(privateKeyString))
if block == nil || block.Type != "RSA PRIVATE KEY" {
log.Fatalf("failed to decode PEM block containing RSA private key")
Expand Down
37 changes: 22 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ type dependencies struct {

func initDependencies(conf AppConfiguration, signingKey *rsa.PrivateKey) (dependencies, error) {
tracer, err := tracing.Init(tracing.Configuration{
Enabled: conf.env != "development",
ApplicationName: "marble-backend",
Enabled: conf.enableGcpTracing,
ProjectID: conf.gcpProject,
})
if err != nil {
return dependencies{}, fmt.Errorf("tracing.Init error: %w", err)
}

database, err := postgres.New(postgres.Configuration{
Host: conf.pgConfig.Hostname,
Port: conf.pgConfig.Port,
Expand All @@ -67,9 +68,9 @@ func initDependencies(conf AppConfiguration, signingKey *rsa.PrivateKey) (depend

return dependencies{
Authentication: api.NewAuthentication(tokenValidator),
TokenHandler: api.NewTokenHandler(tokenGenerator),
SegmentClient: segmentClient,
OpenTelemetryTracer: tracer,
SegmentClient: segmentClient,
TokenHandler: api.NewTokenHandler(tokenGenerator),
}, nil
}

Expand Down Expand Up @@ -109,7 +110,7 @@ func runServer(ctx context.Context, appConfig AppConfiguration) {
}

router := initRouter(ctx, appConfig, deps)
server := api.New(router, appConfig.port, uc, deps.Authentication, deps.TokenHandler, logger)
server := api.New(router, appConfig.port, uc, deps.Authentication, deps.TokenHandler)

notify, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
Expand All @@ -133,20 +134,26 @@ func runServer(ctx context.Context, appConfig AppConfiguration) {
}

type AppConfiguration struct {
env string
port string
gcpProject string
pgConfig utils.PGConfig
config models.GlobalConfiguration
sentryDsn string
metabase models.MetabaseConfiguration
env string
port string
gcpProject string
enableGcpTracing bool
requestLoggingLevel string
loggingFormat string
pgConfig utils.PGConfig
config models.GlobalConfiguration
sentryDsn string
metabase models.MetabaseConfiguration
}

func main() {
appConfig := AppConfiguration{
env: utils.GetEnv("ENV", "development"),
port: utils.GetRequiredEnv[string]("PORT"),
gcpProject: os.Getenv("GOOGLE_CLOUD_PROJECT"),
env: utils.GetEnv("ENV", "development"),
port: utils.GetRequiredEnv[string]("PORT"),
gcpProject: os.Getenv("GOOGLE_CLOUD_PROJECT"),
enableGcpTracing: utils.GetEnv("ENABLE_GCP_TRACING", false),
requestLoggingLevel: utils.GetEnv("REQUEST_LOGGING_LEVEL", "all"),
loggingFormat: utils.GetEnv("LOGGING_FORMAT", "text"),
pgConfig: utils.PGConfig{
Database: "marble",
DbConnectWithSocket: utils.GetEnv("PG_CONNECT_WITH_SOCKET", false),
Expand Down Expand Up @@ -178,7 +185,7 @@ func main() {
// Setup dependencies
////////////////////////////////////////////////////////////

logger := utils.NewLogger(appConfig.env)
logger := utils.NewLogger(appConfig.loggingFormat)
appContext := utils.StoreLoggerInContext(context.Background(), logger)

shouldRunMigrations := flag.Bool("migrations", false, "Run migrations")
Expand Down
4 changes: 0 additions & 4 deletions repositories/firebase/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

type tokenCookieVerifier interface {
VerifyIDToken(ctx context.Context, idToken string) (*auth.Token, error)
VerifySessionCookie(ctx context.Context, sessionCookie string) (*auth.Token, error)
}

type Client struct {
Expand All @@ -20,9 +19,6 @@ type Client struct {

func (c *Client) verifyTokenOrCookie(ctx context.Context, firebaseToken string) (*auth.Token, error) {
token, err := c.verifier.VerifyIDToken(ctx, firebaseToken)
if err != nil {
token, err = c.verifier.VerifySessionCookie(ctx, firebaseToken)
}
if err != nil {
return nil, err
}
Expand Down
26 changes: 0 additions & 26 deletions repositories/firebase/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ func (m *mockTokenCookieVerifier) VerifyIDToken(ctx context.Context, idToken str
return args.Get(0).(*auth.Token), args.Error(1)
}

func (m *mockTokenCookieVerifier) VerifySessionCookie(ctx context.Context, sessionCookie string) (*auth.Token, error) {
args := m.Called(ctx, sessionCookie)
return args.Get(0).(*auth.Token), args.Error(1)
}

func TestClient_VerifyFirebaseToken(t *testing.T) {
token := auth.Token{
Subject: "token_subject",
Expand Down Expand Up @@ -56,27 +51,6 @@ func TestClient_VerifyFirebaseToken(t *testing.T) {
mockVerifier := new(mockTokenCookieVerifier)
mockVerifier.On("VerifyIDToken", mock.Anything, "token").
Return(&auth.Token{}, assert.AnError)
mockVerifier.On("VerifySessionCookie", mock.Anything, "token").
Return(&token, nil)

c := Client{
verifier: mockVerifier,
}

identity, err := c.VerifyFirebaseToken(context.Background(), "token")
assert.NoError(t, err)
assert.Equal(t, models.FirebaseIdentity{
Email: "[email protected]",
}, identity)
mockVerifier.AssertExpectations(t)
})

t.Run("VerifySessionCookie error", func(t *testing.T) {
mockVerifier := new(mockTokenCookieVerifier)
mockVerifier.On("VerifyIDToken", mock.Anything, "token").
Return(&auth.Token{}, assert.AnError)
mockVerifier.On("VerifySessionCookie", mock.Anything, "token").
Return(&auth.Token{}, assert.AnError)

c := Client{
verifier: mockVerifier,
Expand Down
5 changes: 1 addition & 4 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,7 @@ func initRouter(ctx context.Context, conf AppConfiguration, deps dependencies) *
r.Use(gin.Recovery())
r.Use(sentrygin.New(sentrygin.Options{Repanic: true}))
r.Use(cors.New(corsOption(conf.env)))
if conf.env == "development" {
// GCP already logs those elements
r.Use(middleware.NewLogging(logger))
}
r.Use(middleware.NewLogging(logger, conf.requestLoggingLevel))
r.Use(otelgin.Middleware("marble-backend"))
r.Use(utils.StoreLoggerInContextMiddleware(logger))
r.Use(utils.StoreSegmentClientInContextMiddleware(deps.SegmentClient))
Expand Down
2 changes: 1 addition & 1 deletion usecases/data_model_usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (suite *DatamodelUsecaseTestSuite) SetupTest() {

suite.repositoryError = errors.New("some repository error")
suite.securityError = errors.New("some security error")
suite.ctx = utils.StoreLoggerInContext(context.Background(), utils.NewLogger("test"))
suite.ctx = utils.StoreLoggerInContext(context.Background(), utils.NewLogger("text"))
}

func (suite *DatamodelUsecaseTestSuite) makeUsecase() *DataModelUseCase {
Expand Down
2 changes: 1 addition & 1 deletion usecases/indexes/aggregate_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

func makeTestContext() context.Context {
ctx := context.Background()
return utils.StoreLoggerInContext(ctx, utils.NewLogger("test"))
return utils.StoreLoggerInContext(ctx, utils.NewLogger("text"))
}

func TestAggregationNodeToQueryFamily(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion usecases/indexes/index_editor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (suite *ClientDbIndexEditorTestSuite) SetupTest() {

suite.repositoryError = errors.New("some repository error")
suite.securityError = errors.New("some security error")
suite.ctx = utils.StoreLoggerInContext(context.Background(), utils.NewLogger("test"))
suite.ctx = utils.StoreLoggerInContext(context.Background(), utils.NewLogger("text"))
}

func (suite *ClientDbIndexEditorTestSuite) makeUsecase() ClientDbIndexEditor {
Expand Down
2 changes: 1 addition & 1 deletion usecases/scenario_publication_usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (suite *ScenarioPublicationUsecaseTestSuite) SetupTest() {

suite.repositoryError = errors.New("some repository error")
suite.securityError = errors.New("some security error")
suite.ctx = utils.StoreLoggerInContext(context.Background(), utils.NewLogger("test"))
suite.ctx = utils.StoreLoggerInContext(context.Background(), utils.NewLogger("text"))
}

func (suite *ScenarioPublicationUsecaseTestSuite) makeUsecase() *ScenarioPublicationUsecase {
Expand Down
2 changes: 1 addition & 1 deletion usecases/scenarios/scenario_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

func TestValidateScenarioIterationImpl_Validate(t *testing.T) {
ctx := utils.StoreLoggerInContext(context.Background(), utils.NewLogger("test"))
ctx := utils.StoreLoggerInContext(context.Background(), utils.NewLogger("text"))
scenario := models.Scenario{
Id: uuid.New().String(),
OrganizationId: uuid.New().String(),
Expand Down
Loading
Loading