| package server | 
 |  | 
 | import ( | 
 | 	"context" | 
 | 	"flag" | 
 | 	"net" | 
 | 	"net/http" | 
 |  | 
 | 	"github.com/improbable-eng/grpc-web/go/grpcweb" | 
 | 	"google.golang.org/grpc" | 
 | 	"google.golang.org/grpc/codes" | 
 | 	"google.golang.org/grpc/credentials/insecure" | 
 | 	"google.golang.org/grpc/reflection" | 
 | 	"google.golang.org/grpc/status" | 
 | 	"k8s.io/klog/v2" | 
 |  | 
 | 	apb "source.monogon.dev/cloud/api" | 
 | 	"source.monogon.dev/cloud/apigw/model" | 
 | 	"source.monogon.dev/cloud/lib/component" | 
 | ) | 
 |  | 
 | // Config is the main configuration of the apigw server. It's usually populated | 
 | // from flags via RegisterFlags, but can also be set manually (eg. in tests). | 
 | type Config struct { | 
 | 	Component component.ComponentConfig | 
 | 	Database  component.CockroachConfig | 
 |  | 
 | 	PublicListenAddress string | 
 | } | 
 |  | 
 | // RegisterFlags registers the component configuration to be provided by flags. | 
 | // This must be called exactly once before then calling flags.Parse(). | 
 | func (c *Config) RegisterFlags() { | 
 | 	c.Component.RegisterFlags("apigw") | 
 | 	c.Database.RegisterFlags("apigw_db") | 
 | 	flag.StringVar(&c.PublicListenAddress, "apigw_public_grpc_listen_address", ":8080", "Address to listen at for public/user gRPC connections for apigw") | 
 | } | 
 |  | 
 | // Server runs the apigw server. It listens on two interfaces: | 
 | //  - Internal gRPC, which is authenticated using TLS and authorized by CA. This | 
 | //    is to be used for internal RPCs, eg. management/debug. | 
 | //  - Public gRPC-Web, which is currently unauthenticated. | 
 | type Server struct { | 
 | 	Config Config | 
 |  | 
 | 	// ListenGRPC will contain the address at which the internal gRPC server is | 
 | 	// listening after .Start() has been called. This can differ from the configured | 
 | 	// value if the configuration requests any port (via :0). | 
 | 	ListenGRPC string | 
 | 	// ListenPublic will contain the address at which the public API server is | 
 | 	// listening after .Start() has been called. This can differ from the configured | 
 | 	// value if the configuration requests any port (via :0). | 
 | 	ListenPublic string | 
 | } | 
 |  | 
 | func (s *Server) startInternalGRPC(ctx context.Context) { | 
 | 	g := grpc.NewServer(s.Config.Component.GRPCServerOptions()...) | 
 | 	lis, err := net.Listen("tcp", s.Config.Component.GRPCListenAddress) | 
 | 	if err != nil { | 
 | 		klog.Exitf("Could not listen: %v", err) | 
 | 	} | 
 | 	s.ListenGRPC = lis.Addr().String() | 
 |  | 
 | 	reflection.Register(g) | 
 |  | 
 | 	klog.Infof("Internal gRPC listening on %s", s.ListenGRPC) | 
 | 	go func() { | 
 | 		err := g.Serve(lis) | 
 | 		if err != ctx.Err() { | 
 | 			klog.Exitf("Internal gRPC serve failed: %v", err) | 
 | 		} | 
 | 	}() | 
 | } | 
 |  | 
 | func (s *Server) startPublic(ctx context.Context) { | 
 | 	g := grpc.NewServer(grpc.Creds(insecure.NewCredentials())) | 
 | 	lis, err := net.Listen("tcp", s.Config.PublicListenAddress) | 
 | 	if err != nil { | 
 | 		klog.Exitf("Could not listen: %v", err) | 
 | 	} | 
 | 	s.ListenPublic = lis.Addr().String() | 
 |  | 
 | 	reflection.Register(g) | 
 | 	apb.RegisterIAMServer(g, s) | 
 |  | 
 | 	wrapped := grpcweb.WrapServer(g) | 
 | 	server := http.Server{ | 
 | 		Addr:    s.Config.PublicListenAddress, | 
 | 		Handler: http.HandlerFunc(wrapped.ServeHTTP), | 
 | 	} | 
 | 	klog.Infof("Public API listening on %s", s.ListenPublic) | 
 | 	go func() { | 
 | 		err := server.Serve(lis) | 
 | 		if err != ctx.Err() { | 
 | 			klog.Exitf("Public API serve failed: %v", err) | 
 | 		} | 
 | 	}() | 
 | } | 
 |  | 
 | // Start runs the two listeners of the server. The process will fail (via | 
 | // klog.Exit) if any of the listeners/servers fail to start. | 
 | func (s *Server) Start(ctx context.Context) { | 
 | 	if s.Config.Database.Migrations == nil { | 
 | 		klog.Infof("Using default migrations source.") | 
 | 		m, err := model.MigrationsSource() | 
 | 		if err != nil { | 
 | 			klog.Exitf("failed to prepare migrations source: %w", err) | 
 | 		} | 
 | 		s.Config.Database.Migrations = m | 
 | 	} | 
 |  | 
 | 	klog.Infof("Running migrations...") | 
 | 	if err := s.Config.Database.MigrateUp(); err != nil { | 
 | 		klog.Exitf("Migrations failed: %v", err) | 
 | 	} | 
 | 	klog.Infof("Migrations done.") | 
 | 	s.startInternalGRPC(ctx) | 
 | 	s.startPublic(ctx) | 
 | } | 
 |  | 
 | func (s *Server) WhoAmI(ctx context.Context, req *apb.WhoAmIRequest) (*apb.WhoAmIResponse, error) { | 
 | 	klog.Infof("req: %+v", req) | 
 | 	return nil, status.Error(codes.Unimplemented, "unimplemented") | 
 | } |