blob: a068e84d3254398a556f6f7d87910c6278b4a852 [file] [log] [blame]
Serge Bazanskibee272f2022-09-13 13:52:42 +02001package server
2
3import (
4 "context"
5 "flag"
6 "net"
7 "net/http"
8
9 "github.com/improbable-eng/grpc-web/go/grpcweb"
10 "google.golang.org/grpc"
11 "google.golang.org/grpc/codes"
12 "google.golang.org/grpc/credentials/insecure"
13 "google.golang.org/grpc/reflection"
14 "google.golang.org/grpc/status"
15 "k8s.io/klog/v2"
16
17 apb "source.monogon.dev/cloud/api"
Serge Bazanskia5baa872022-09-15 18:49:35 +020018 "source.monogon.dev/cloud/apigw/model"
Serge Bazanskibee272f2022-09-13 13:52:42 +020019 "source.monogon.dev/cloud/lib/component"
20)
21
22// Config is the main configuration of the apigw server. It's usually populated
23// from flags via RegisterFlags, but can also be set manually (eg. in tests).
24type Config struct {
Serge Bazanskia5baa872022-09-15 18:49:35 +020025 Component component.ComponentConfig
26 Database component.CockroachConfig
Serge Bazanskibee272f2022-09-13 13:52:42 +020027
28 PublicListenAddress string
29}
30
31// RegisterFlags registers the component configuration to be provided by flags.
32// This must be called exactly once before then calling flags.Parse().
33func (c *Config) RegisterFlags() {
Serge Bazanskia5baa872022-09-15 18:49:35 +020034 c.Component.RegisterFlags("apigw")
35 c.Database.RegisterFlags("apigw_db")
Serge Bazanskibee272f2022-09-13 13:52:42 +020036 flag.StringVar(&c.PublicListenAddress, "apigw_public_grpc_listen_address", ":8080", "Address to listen at for public/user gRPC connections for apigw")
37}
38
39// Server runs the apigw server. It listens on two interfaces:
40// - Internal gRPC, which is authenticated using TLS and authorized by CA. This
41// is to be used for internal RPCs, eg. management/debug.
42// - Public gRPC-Web, which is currently unauthenticated.
43type Server struct {
44 Config Config
45
46 // ListenGRPC will contain the address at which the internal gRPC server is
47 // listening after .Start() has been called. This can differ from the configured
48 // value if the configuration requests any port (via :0).
49 ListenGRPC string
50 // ListenPublic will contain the address at which the public API server is
51 // listening after .Start() has been called. This can differ from the configured
52 // value if the configuration requests any port (via :0).
53 ListenPublic string
54}
55
56func (s *Server) startInternalGRPC(ctx context.Context) {
Serge Bazanskia5baa872022-09-15 18:49:35 +020057 g := grpc.NewServer(s.Config.Component.GRPCServerOptions()...)
58 lis, err := net.Listen("tcp", s.Config.Component.GRPCListenAddress)
Serge Bazanskibee272f2022-09-13 13:52:42 +020059 if err != nil {
60 klog.Exitf("Could not listen: %v", err)
61 }
62 s.ListenGRPC = lis.Addr().String()
63
64 reflection.Register(g)
65
66 klog.Infof("Internal gRPC listening on %s", s.ListenGRPC)
67 go func() {
68 err := g.Serve(lis)
69 if err != ctx.Err() {
70 klog.Exitf("Internal gRPC serve failed: %v", err)
71 }
72 }()
73}
74
75func (s *Server) startPublic(ctx context.Context) {
76 g := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))
77 lis, err := net.Listen("tcp", s.Config.PublicListenAddress)
78 if err != nil {
79 klog.Exitf("Could not listen: %v", err)
80 }
81 s.ListenPublic = lis.Addr().String()
82
83 reflection.Register(g)
84 apb.RegisterIAMServer(g, s)
85
86 wrapped := grpcweb.WrapServer(g)
87 server := http.Server{
88 Addr: s.Config.PublicListenAddress,
89 Handler: http.HandlerFunc(wrapped.ServeHTTP),
90 }
91 klog.Infof("Public API listening on %s", s.ListenPublic)
92 go func() {
93 err := server.Serve(lis)
94 if err != ctx.Err() {
95 klog.Exitf("Public API serve failed: %v", err)
96 }
97 }()
98}
99
100// Start runs the two listeners of the server. The process will fail (via
101// klog.Exit) if any of the listeners/servers fail to start.
102func (s *Server) Start(ctx context.Context) {
Serge Bazanskia5baa872022-09-15 18:49:35 +0200103 if s.Config.Database.Migrations == nil {
104 klog.Infof("Using default migrations source.")
105 m, err := model.MigrationsSource()
106 if err != nil {
107 klog.Exitf("failed to prepare migrations source: %w", err)
108 }
109 s.Config.Database.Migrations = m
110 }
111
112 klog.Infof("Running migrations...")
113 if err := s.Config.Database.MigrateUp(); err != nil {
114 klog.Exitf("Migrations failed: %v", err)
115 }
116 klog.Infof("Migrations done.")
Serge Bazanskibee272f2022-09-13 13:52:42 +0200117 s.startInternalGRPC(ctx)
118 s.startPublic(ctx)
119}
120
121func (s *Server) WhoAmI(ctx context.Context, req *apb.WhoAmIRequest) (*apb.WhoAmIResponse, error) {
122 klog.Infof("req: %+v", req)
123 return nil, status.Error(codes.Unimplemented, "unimplemented")
124}