diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go index 5802ce3..96c1da7 100644 --- a/internal/bootstrap/bootstrap.go +++ b/internal/bootstrap/bootstrap.go @@ -1 +1,97 @@ package bootstrap + +import ( + "context" + "errors" + "fmt" + "log" + "os" + "os/signal" + "syscall" + + "git.fossy.my.id/bagas/tunnel-please-controller/internal/config" + "git.fossy.my.id/bagas/tunnel-please-controller/internal/db/sqlc/repository" + "git.fossy.my.id/bagas/tunnel-please-controller/internal/server" + "github.com/jackc/pgx/v5" + "github.com/lestrrat-go/httprc/v3" + "github.com/lestrrat-go/jwx/v3/jwk" +) + +type bootstrap struct { + config config.Config + errChan chan error + signalChan chan os.Signal +} + +type Bootstrap interface { + Run() error +} + +func New(config config.Config) Bootstrap { + errChan := make(chan error, 5) + signalChan := make(chan os.Signal, 1) + return &bootstrap{ + config: config, + errChan: errChan, + signalChan: signalChan, + } +} + +func (b *bootstrap) Run() error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + signal.Notify(b.signalChan, os.Interrupt, syscall.SIGTERM) + + dbURL := b.config.DatabaseURL() + repo, err := startDatabase(ctx, dbURL) + if err != nil { + return err + } + + client := httprc.NewClient() + jwkCache, err := jwk.NewCache(ctx, client) + if err != nil { + return err + } + s := server.New(repo, jwkCache, b.config) + + b.errChan = make(chan error, 2) + + go func() { + if err = s.StartAPI(ctx, b.config.ApiAddr()); err != nil && !errors.Is(err, context.Canceled) { + b.errChan <- err + } + }() + + go func() { + if err = s.StartController(ctx, b.config.ControllerAddr()); err != nil && !errors.Is(err, context.Canceled) { + b.errChan <- err + } + }() + + select { + case err = <-b.errChan: + return fmt.Errorf("service error: %w", err) + case sig := <-b.signalChan: + log.Printf("Received signal %s, initiating graceful shutdown", sig) + cancel() + return nil + } +} + +func startDatabase(ctx context.Context, dbURL string) (*repository.Queries, error) { + connect, err := pgx.Connect(ctx, dbURL) + if err != nil { + return nil, err + } + + defer func(connect *pgx.Conn, ctx context.Context) { + err = connect.Close(ctx) + if err != nil { + panic(err) + } + }(connect, ctx) + + return repository.New(connect), nil +}