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 }