github.com/emcfarlane/larking@v0.0.0-20220605172417-1704b45ee6c3/cmd/larking/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/x509"
     6  	"flag"
     7  	"fmt"
     8  	"log"
     9  	"net"
    10  	"os"
    11  
    12  	"github.com/emcfarlane/larking"
    13  	"github.com/emcfarlane/larking/apipb/controlpb"
    14  	"github.com/emcfarlane/larking/apipb/healthpb"
    15  	"github.com/emcfarlane/larking/apipb/workerpb"
    16  	_ "github.com/emcfarlane/larking/cmd/internal/bindings"
    17  	"github.com/emcfarlane/larking/control"
    18  	"github.com/emcfarlane/larking/health"
    19  	"github.com/emcfarlane/larking/starlib"
    20  	"github.com/emcfarlane/larking/starlib/starlarkthread"
    21  	"github.com/emcfarlane/larking/worker"
    22  	"github.com/go-logr/logr"
    23  	"github.com/go-logr/stdr"
    24  	"go.starlark.net/starlark"
    25  	"google.golang.org/grpc"
    26  	"google.golang.org/grpc/credentials"
    27  )
    28  
    29  const defaultAddr = "localhost:6060" // default webserver address
    30  
    31  func env(key, def string) string {
    32  	if e := os.Getenv(key); e != "" {
    33  		return e
    34  	}
    35  	return def
    36  }
    37  
    38  var (
    39  	flagAddr        = flag.String("addr", env("LARKING_ADDRESS", defaultAddr), "Local address to listen on.")
    40  	flagControlAddr = flag.String("control", "https://larking.io", "Control server for credentials.")
    41  	flagInsecure    = flag.Bool("insecure", false, "Insecure, disabled credentials.")
    42  	flagCreds       = flag.String("credentials", env("WORKER_CREDENTIALS", ""), "Runtime variable for credentials.")
    43  
    44  	flagDir = flag.String("dir", env("LARKING_DIR", "file://./?metadata=skip"), "Set the module loading directory")
    45  )
    46  
    47  type logStream struct {
    48  	grpc.ServerStream
    49  	log logr.Logger
    50  }
    51  
    52  func (s logStream) Context() context.Context {
    53  	return logr.NewContext(s.ServerStream.Context(), s.log)
    54  }
    55  
    56  func run(ctx context.Context) (err error) {
    57  	ctx, cancel := context.WithCancel(ctx)
    58  	defer cancel()
    59  
    60  	l, err := net.Listen("tcp", *flagAddr)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	defer l.Close()
    65  
    66  	stdr.SetVerbosity(1)
    67  	log := stdr.NewWithOptions(log.New(os.Stderr, "", log.LstdFlags), stdr.Options{LogCaller: stdr.All})
    68  	log = log.WithName("Larking")
    69  
    70  	unary := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
    71  		log.Info("unary request", "info", info)
    72  		ctx = logr.NewContext(ctx, log)
    73  		defer log.Info("unary end", info)
    74  		return handler(ctx, req)
    75  	}
    76  
    77  	stream := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    78  		log.Info("stream request", "info", info)
    79  		defer log.Info("stream end", info)
    80  		return handler(srv, logStream{
    81  			ServerStream: ss,
    82  			log:          log,
    83  		})
    84  	}
    85  
    86  	var muxOpts = []larking.MuxOption{
    87  		larking.UnaryServerInterceptorOption(unary),
    88  		larking.StreamServerInterceptorOption(stream),
    89  	}
    90  
    91  	mux, err := larking.NewMux(muxOpts...)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	healthServer := health.NewServer()
    97  	defer healthServer.Shutdown()
    98  	mux.RegisterService(&healthpb.Health_ServiceDesc, healthServer)
    99  
   100  	globals := starlib.NewGlobals()
   101  	loader := starlib.NewLoader(globals)
   102  	defer loader.Close()
   103  
   104  	var (
   105  		controlClient controlpb.ControlClient = control.InsecureControlClient{}
   106  		name          string
   107  	)
   108  	if !*flagInsecure || *flagCreds != "" {
   109  		log.Info("loading worker credentials")
   110  
   111  		perRPC, err := control.OpenRPCCredentials(ctx, *flagCreds)
   112  		if err != nil {
   113  			return err
   114  		}
   115  		defer perRPC.Close()
   116  
   117  		name = perRPC.Name()
   118  
   119  		// TODO: load creds.
   120  		pool, err := x509.SystemCertPool()
   121  		if err != nil {
   122  			return err
   123  		}
   124  		creds := credentials.NewClientTLSFromCert(pool, "")
   125  
   126  		cc, err := grpc.DialContext(
   127  			ctx, *flagControlAddr,
   128  			grpc.WithTransportCredentials(creds),
   129  			grpc.WithPerRPCCredentials(perRPC),
   130  		)
   131  		if err != nil {
   132  			return err
   133  		}
   134  		defer cc.Close()
   135  
   136  		controlClient = controlpb.NewControlClient(cc)
   137  	}
   138  
   139  	workerServer := worker.NewServer(
   140  		loader.Load,
   141  		controlClient,
   142  		name,
   143  	)
   144  	mux.RegisterService(&workerpb.Worker_ServiceDesc, workerServer)
   145  	healthServer.SetServingStatus(
   146  		workerpb.Worker_ServiceDesc.ServiceName,
   147  		healthpb.HealthCheckResponse_SERVING,
   148  	)
   149  
   150  	var svrOpts []larking.ServerOption
   151  
   152  	if *flagInsecure {
   153  		svrOpts = append(svrOpts, larking.InsecureServerOption())
   154  	}
   155  
   156  	s, err := larking.NewServer(mux, svrOpts...)
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	// Run script
   162  	switch flag.NArg() {
   163  	case 0:
   164  		// nothing
   165  	case 1:
   166  		name := flag.Arg(0)
   167  
   168  		src, err := loader.LoadSource(ctx, *flagDir, name)
   169  		if err != nil {
   170  			return err
   171  		}
   172  
   173  		globals := starlib.NewGlobals()
   174  		globals["mux"] = mux // add mux!
   175  		thread := &starlark.Thread{
   176  			Name: name, // TODO: name encoding...
   177  			Load: loader.Load,
   178  			Print: func(_ *starlark.Thread, msg string) {
   179  				log.Info(msg, "thread", name)
   180  			},
   181  		}
   182  		starlarkthread.SetContext(thread, ctx)
   183  		close := starlarkthread.WithResourceStore(thread)
   184  		defer func() {
   185  			if cerr := close(); err == nil {
   186  				err = cerr
   187  			}
   188  		}()
   189  
   190  		module, err := starlark.ExecFile(thread, name, src, globals)
   191  		if err != nil {
   192  			return err
   193  		}
   194  		if mainFn, ok := module["main"]; ok {
   195  			if _, err := starlark.Call(thread, mainFn, nil, nil); err != nil {
   196  				return err
   197  			}
   198  		}
   199  
   200  	default:
   201  		return fmt.Errorf("unexpected number of args")
   202  	}
   203  
   204  	go func() {
   205  		log.Info("listening", "address", l.Addr().String())
   206  		if err := s.Serve(l); err != nil {
   207  			log.Error(err, "server stopped")
   208  		}
   209  		cancel()
   210  	}()
   211  	<-ctx.Done()
   212  	return s.Shutdown(ctx)
   213  }
   214  
   215  func usage() {
   216  	fmt.Fprintf(os.Stderr, "usage: larking -addr="+defaultAddr+" -svc=service1 -svc=service2\n")
   217  	flag.PrintDefaults()
   218  	os.Exit(2)
   219  }
   220  
   221  func main() {
   222  	flag.Usage = usage
   223  	flag.Parse()
   224  
   225  	//if len(services) == 0 {
   226  	//	usage()
   227  	//}
   228  
   229  	ctx := context.Background()
   230  	ctx, cancel := larking.NewOSSignalContext(ctx)
   231  	defer cancel()
   232  
   233  	if err := run(ctx); err != nil {
   234  		fmt.Println(err)
   235  		os.Exit(1)
   236  	}
   237  }