github.com/square/finch@v0.0.0-20240412205204-6530c03e2b96/boot/boot.go (about)

     1  // Copyright 2023 Block, Inc.
     2  
     3  package boot
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"log"
     9  	"os"
    10  	"os/signal"
    11  	"time"
    12  
    13  	"github.com/square/finch"
    14  	"github.com/square/finch/compute"
    15  	"github.com/square/finch/config"
    16  )
    17  
    18  func init() {
    19  	log.SetOutput(os.Stdout)
    20  	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
    21  }
    22  
    23  // Env is the startup environment: command line args and environment variables.
    24  // This is mostly used for testing to override the defaults.
    25  type Env struct {
    26  	Args []string
    27  	Env  []string
    28  }
    29  
    30  // Up is called in main.go to boot up and run Finch.
    31  func Up(env Env) error {
    32  	if len(env.Args) == 0 && len(env.Env) == 0 {
    33  		env = Env{
    34  			Args: os.Args,
    35  			Env:  os.Environ(),
    36  		}
    37  	}
    38  
    39  	// Parse command line
    40  	cmdline, err := ParseCommandLine(env.Args)
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	// Set global debug var first because all code calls finch.Debug
    46  	finch.Debugging = cmdline.Options.Debug
    47  	finch.Debug("finch %s %+v", finch.VERSION, cmdline)
    48  
    49  	// Return early (don't boot/run) --help and --verison
    50  	if cmdline.Options.Help {
    51  		printHelp()
    52  		return nil
    53  	}
    54  	if cmdline.Options.Version {
    55  		fmt.Println("finch", finch.VERSION)
    56  		return nil
    57  	}
    58  
    59  	log.Println(finch.SystemParams)
    60  
    61  	// Catch CTRL-C and cancel the main context, which should cause a clean shutdown
    62  	ctxFinch, cancelFinch := context.WithCancel(context.Background())
    63  	go func() {
    64  		c := make(chan os.Signal, 1)
    65  		signal.Notify(c, os.Interrupt)
    66  		<-c
    67  		log.Println("Caught CTRL-C")
    68  		cancelFinch()
    69  		// Fail-safe: if something doesn't respond to the ctx cancellation,
    70  		// this guarantees that Finch will terminate on CTRL-C after 7.5s.
    71  		<-time.After(7500 * time.Millisecond) // 7.5s
    72  		log.Println("Forcing exit(1) because stage did not respond to context cancellation")
    73  		os.Exit(1)
    74  	}()
    75  
    76  	// Set up --cpu-profile that's started/stopped in stage just around execution
    77  	if cmdline.Options.CPUProfile != "" {
    78  		f, err := os.Create(cmdline.Options.CPUProfile)
    79  		if err != nil {
    80  			log.Fatal(err)
    81  		}
    82  		finch.CPUProfile = f
    83  	}
    84  
    85  	//  If --client specified, run in client mode connected to a Finch server.
    86  	// In client mode, we don't need a config file because everything is fetched
    87  	// from the server.
    88  	if serverAddr := cmdline.Options.Client; serverAddr != "" {
    89  		clientName, _ := os.Hostname()
    90  		client := compute.NewClient(clientName, finch.WithPort(serverAddr, finch.DEFAULT_SERVER_PORT))
    91  		return client.Run(ctxFinch)
    92  	}
    93  
    94  	// ----------------------------------------------------------------------
    95  	// Server mode (default)
    96  
    97  	// Load and validate all stage config files specified on the command line
    98  	if len(cmdline.Args) == 1 {
    99  		log.Fatal("No stage file specified. Run finch --help for usage. See https://square.github.io/finch/ for documentation.")
   100  	}
   101  	stages, err := config.Load(
   102  		cmdline.Args[1:],
   103  		cmdline.Options.Params,
   104  		cmdline.Options.DSN,
   105  		cmdline.Options.Database,
   106  	)
   107  	if err != nil {
   108  		log.Fatal(err)
   109  	}
   110  
   111  	// Boot and run each stage specified on the command line
   112  	server := compute.NewServer("local", cmdline.Options.Server, cmdline.Options.Test)
   113  	return server.Run(ctxFinch, stages)
   114  }