blob: b3b80bfadedaa5b1a6a3395723d3e5007b4450d2 [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"
18 "source.monogon.dev/cloud/lib/component"
19)
20
21// Config is the main configuration of the apigw server. It's usually populated
22// from flags via RegisterFlags, but can also be set manually (eg. in tests).
23type Config struct {
24 component.Configuration
25
26 PublicListenAddress string
27}
28
29// RegisterFlags registers the component configuration to be provided by flags.
30// This must be called exactly once before then calling flags.Parse().
31func (c *Config) RegisterFlags() {
32 c.Configuration.RegisterFlags("apigw")
33 flag.StringVar(&c.PublicListenAddress, "apigw_public_grpc_listen_address", ":8080", "Address to listen at for public/user gRPC connections for apigw")
34}
35
36// Server runs the apigw server. It listens on two interfaces:
37// - Internal gRPC, which is authenticated using TLS and authorized by CA. This
38// is to be used for internal RPCs, eg. management/debug.
39// - Public gRPC-Web, which is currently unauthenticated.
40type Server struct {
41 Config Config
42
43 // ListenGRPC will contain the address at which the internal gRPC server is
44 // listening after .Start() has been called. This can differ from the configured
45 // value if the configuration requests any port (via :0).
46 ListenGRPC string
47 // ListenPublic will contain the address at which the public API server is
48 // listening after .Start() has been called. This can differ from the configured
49 // value if the configuration requests any port (via :0).
50 ListenPublic string
51}
52
53func (s *Server) startInternalGRPC(ctx context.Context) {
54 g := grpc.NewServer(s.Config.GRPCServerOptions()...)
55 lis, err := net.Listen("tcp", s.Config.GRPCListenAddress)
56 if err != nil {
57 klog.Exitf("Could not listen: %v", err)
58 }
59 s.ListenGRPC = lis.Addr().String()
60
61 reflection.Register(g)
62
63 klog.Infof("Internal gRPC listening on %s", s.ListenGRPC)
64 go func() {
65 err := g.Serve(lis)
66 if err != ctx.Err() {
67 klog.Exitf("Internal gRPC serve failed: %v", err)
68 }
69 }()
70}
71
72func (s *Server) startPublic(ctx context.Context) {
73 g := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))
74 lis, err := net.Listen("tcp", s.Config.PublicListenAddress)
75 if err != nil {
76 klog.Exitf("Could not listen: %v", err)
77 }
78 s.ListenPublic = lis.Addr().String()
79
80 reflection.Register(g)
81 apb.RegisterIAMServer(g, s)
82
83 wrapped := grpcweb.WrapServer(g)
84 server := http.Server{
85 Addr: s.Config.PublicListenAddress,
86 Handler: http.HandlerFunc(wrapped.ServeHTTP),
87 }
88 klog.Infof("Public API listening on %s", s.ListenPublic)
89 go func() {
90 err := server.Serve(lis)
91 if err != ctx.Err() {
92 klog.Exitf("Public API serve failed: %v", err)
93 }
94 }()
95}
96
97// Start runs the two listeners of the server. The process will fail (via
98// klog.Exit) if any of the listeners/servers fail to start.
99func (s *Server) Start(ctx context.Context) {
100 s.startInternalGRPC(ctx)
101 s.startPublic(ctx)
102}
103
104func (s *Server) WhoAmI(ctx context.Context, req *apb.WhoAmIRequest) (*apb.WhoAmIResponse, error) {
105 klog.Infof("req: %+v", req)
106 return nil, status.Error(codes.Unimplemented, "unimplemented")
107}