-
Notifications
You must be signed in to change notification settings - Fork 3
/
main.go
175 lines (139 loc) · 4.71 KB
/
main.go
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package main
import (
"context"
"fmt"
"net/http"
"os"
"runtime/debug"
"strings"
"time"
"github.com/jamestelfer/chinmina-bridge/internal/audit"
"github.com/jamestelfer/chinmina-bridge/internal/buildkite"
"github.com/jamestelfer/chinmina-bridge/internal/config"
"github.com/jamestelfer/chinmina-bridge/internal/github"
"github.com/jamestelfer/chinmina-bridge/internal/jwt"
"github.com/jamestelfer/chinmina-bridge/internal/observe"
"github.com/jamestelfer/chinmina-bridge/internal/vendor"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/justinas/alice"
)
func configureServerRoutes(ctx context.Context, cfg config.Config) (http.Handler, error) {
// wrap a mux such that HTTP telemetry is configured by default
muxWithoutTelemetry := http.NewServeMux()
mux := observe.NewMux(muxWithoutTelemetry)
// configure middleware
auditor := audit.Middleware()
authorizer, err := jwt.Middleware(cfg.Authorization)
if err != nil {
return nil, fmt.Errorf("authorizer configuration failed: %w", err)
}
// The request body size is fairly limited to prevent accidental or
// deliberate abuse. Given the current API shape, this is not configurable.
requestLimitBytes := int64(20 << 10) // 20 KB
requestLimiter := maxRequestSize(requestLimitBytes)
authorizedRouteMiddleware := alice.New(requestLimiter, auditor, authorizer)
standardRouteMiddleware := alice.New(requestLimiter)
// setup token handler and dependencies
bk, err := buildkite.New(cfg.Buildkite)
if err != nil {
return nil, fmt.Errorf("buildkite configuration failed: %w", err)
}
gh, err := github.New(ctx, cfg.Github)
if err != nil {
return nil, fmt.Errorf("github configuration failed: %w", err)
}
vendorCache, err := vendor.Cached(45 * time.Minute)
if err != nil {
return nil, fmt.Errorf("vendor cache configuration failed: %w", err)
}
tokenVendor := vendor.Auditor(vendorCache(vendor.New(bk.RepositoryLookup, gh.CreateAccessToken)))
mux.Handle("POST /token", authorizedRouteMiddleware.Then(handlePostToken(tokenVendor)))
mux.Handle("POST /git-credentials", authorizedRouteMiddleware.Then(handlePostGitCredentials(tokenVendor)))
// healthchecks are not included in telemetry or authorization
muxWithoutTelemetry.Handle("GET /healthcheck", standardRouteMiddleware.Then(handleHealthCheck()))
return mux, nil
}
func main() {
configureLogging()
logBuildInfo()
err := launchServer()
if err != nil {
log.Fatal().Err(err).Msg("server failed to start")
}
}
func launchServer() error {
ctx := context.Background()
cfg, err := config.Load(context.Background())
if err != nil {
return fmt.Errorf("configuration load failed: %w", err)
}
// configure telemetry, including wrapping default HTTP client
shutdownTelemetry, err := observe.Configure(ctx, cfg.Observe)
if err != nil {
return fmt.Errorf("telemetry bootstrap failed: %w", err)
}
http.DefaultTransport = observe.HttpTransport(
configureHttpTransport(cfg.Server),
cfg.Observe,
)
http.DefaultClient = &http.Client{
Transport: http.DefaultTransport,
}
// setup routing and dependencies
handler, err := configureServerRoutes(ctx, cfg)
if err != nil {
return fmt.Errorf("server routing configuration failed: %w", err)
}
// start the server
server := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Server.Port),
Handler: handler,
MaxHeaderBytes: 20 << 10, // 20 KB
}
server.RegisterOnShutdown(func() {
log.Info().Msg("telemetry: shutting down")
shutdownTelemetry(ctx)
log.Info().Msg("telemetry: shutdown complete")
})
err = serveHTTP(cfg.Server, server)
if err != nil {
return fmt.Errorf("server failed: %w", err)
}
return nil
}
func configureLogging() {
// Set global level to the minimum: allows the Open Telemetry logging to be
// configured separately. However, it means that any logger that sets its
// level will log as this effectively disables the global level.
zerolog.SetGlobalLevel(zerolog.Level(-128))
// default level is Info
log.Logger = log.Level(zerolog.InfoLevel)
if os.Getenv("ENV") == "development" {
log.Logger = log.
Output(zerolog.ConsoleWriter{Out: os.Stdout}).
Level(zerolog.DebugLevel)
}
zerolog.DefaultContextLogger = &log.Logger
}
func logBuildInfo() {
buildInfo, ok := debug.ReadBuildInfo()
if !ok {
return
}
ev := log.Info()
for _, v := range buildInfo.Settings {
if strings.HasPrefix(v.Key, "vcs.") ||
strings.HasPrefix(v.Key, "GO") ||
v.Key == "CGO_ENABLED" {
ev = ev.Str(v.Key, v.Value)
}
}
ev.Msg("build information")
}
func configureHttpTransport(cfg config.ServerConfig) *http.Transport {
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.MaxIdleConns = cfg.OutgoingHttpMaxIdleConns
transport.MaxConnsPerHost = cfg.OutgoingHttpMaxConnsPerHost
return transport
}