-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.go
More file actions
131 lines (113 loc) · 3.96 KB
/
main.go
File metadata and controls
131 lines (113 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package main
import (
"context"
"errors"
"fmt"
"io"
"log/slog"
"os"
"os/signal"
"github.com/cloudfoundry/go-cfclient/v3/client"
cfconfig "github.com/cloudfoundry/go-cfclient/v3/config"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/cloud-gov/billing/internal/api"
"github.com/cloud-gov/billing/internal/config"
"github.com/cloud-gov/billing/internal/db"
"github.com/cloud-gov/billing/internal/dbx"
"github.com/cloud-gov/billing/internal/jobs"
"github.com/cloud-gov/billing/internal/migrate"
"github.com/cloud-gov/billing/internal/server"
"github.com/cloud-gov/billing/internal/usage/meter"
"github.com/cloud-gov/billing/internal/usage/reader"
)
// BuildVersion is a commit SHA or branch ref. It is set during linking if provided and logged when the application starts.
var BuildVersion string = "devel"
var (
ErrBadConfig = errors.New("reading config from environment")
ErrCFClient = errors.New("creating Cloud Foundry client")
ErrCFConfig = errors.New("parsing Cloud Foundry connection configuration")
ErrCrontab = errors.New("parsing crontab for periodic job execution")
ErrDBConn = errors.New("connecting to database")
ErrDBMigration = errors.New("migrating the database")
ErrOIDCProvider = errors.New("discovering OIDC provider")
ErrRiverClientNew = errors.New("creating River client")
ErrRiverClientStart = errors.New("starting River client")
)
func fmtErr(outer, inner error) error {
return fmt.Errorf("%w: %w", outer, inner)
}
// run sets up dependencies, migrates the database to the latest
// migration, calls route registration, and starts the server. It is separate
// from main so it can return errors conventionally and main can handle them
// all in one place, and so the [io.Writer] can be passed as a dependency,
// making it possible to mock and test for outputs.
func run(ctx context.Context, out io.Writer) error {
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
defer cancel()
c, err := config.New()
if err != nil {
return fmtErr(ErrBadConfig, err)
}
logger := slog.New(slog.NewJSONHandler(out, &slog.HandlerOptions{
Level: c.LogLevel,
}))
logger.Info("build version: " + BuildVersion)
logger.Debug("run: initializing CF client")
cfconf, err := cfconfig.New(
c.CFApiUrl,
cfconfig.ClientCredentials(c.CFClientId, c.CFClientSecret),
)
if err != nil {
return fmtErr(ErrCFConfig, err)
}
cfclient, err := client.New(cfconf)
if err != nil {
return fmtErr(ErrCFClient, err)
}
logger.Debug("run: initializing database")
conn, err := pgxpool.New(ctx, "") // Pass empty connString so PG* environment variables will be used.
if err != nil {
return fmtErr(ErrDBConn, err)
}
logger.Debug("run: migrating the database")
err = migrate.Migrate(ctx, conn)
if err != nil {
return fmtErr(ErrDBMigration, err)
}
q := dbx.NewQuerier(db.New(conn))
logger.Debug("run: initializing meters")
mClient := &meter.CFAdapter{Client: cfclient}
meters := []reader.Meter{
meter.NewCFServiceMeter(logger, mClient, q),
meter.NewCFAppMeter(logger, mClient, q),
}
rdr := reader.New(meters)
logger.Debug("run: initializing OIDC provider for JWT verification")
oidcProvider, err := oidc.NewProvider(ctx, c.Issuer)
if err != nil {
return fmtErr(ErrOIDCProvider, err)
}
verifier := oidcProvider.Verifier(&oidc.Config{ClientID: c.CFClientId}) // todo check alg
logger.Debug("run: initializing River workers and client")
riverc, err := jobs.NewClient(conn, logger, q, rdr)
if err != nil {
return fmtErr(ErrRiverClientNew, err)
}
logger.Debug("run: starting River server")
if err = riverc.Start(ctx); err != nil {
return fmtErr(ErrRiverClientStart, err)
}
logger.Debug("run: starting web server")
srv := server.New(c.Host, c.Port, api.Routes(logger, cfclient, q, riverc, verifier, c), logger)
srv.ListenAndServe(ctx)
return nil
}
func main() {
ctx := context.Background()
err := run(ctx, os.Stdout)
if err != nil {
slog.Error(err.Error())
os.Exit(1)
}
}