2022-10-20 19:59:46 +00:00
package main
2021-04-20 21:31:37 +00:00
import (
"context"
"errors"
"fmt"
2022-10-26 22:23:39 +00:00
"io/fs"
2021-04-20 21:31:37 +00:00
"os"
2023-08-05 21:53:17 +00:00
"isle/bootstrap"
"isle/daemon"
2021-04-20 21:31:37 +00:00
2024-06-22 15:49:56 +00:00
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
2021-04-20 21:31:37 +00:00
)
var subCmdDaemon = subCmd {
name : "daemon" ,
2023-08-05 21:53:17 +00:00
descr : "Runs the isle daemon (Default if no sub-command given)" ,
2021-04-20 21:31:37 +00:00
do : func ( subCmdCtx subCmdCtx ) error {
flags := subCmdCtx . flagSet ( false )
2022-10-26 21:21:31 +00:00
daemonConfigPath := flags . StringP (
2021-04-20 21:31:37 +00:00
"config-path" , "c" , "" ,
"Optional path to a daemon.yml file to load configuration from." ,
)
dumpConfig := flags . Bool (
"dump-config" , false ,
"Write the default configuration file to stdout and exit." ,
)
bootstrapPath := flags . StringP (
"bootstrap-path" , "b" , "" ,
2024-06-10 16:56:36 +00:00
` Path to a bootstrap.json file. This only needs to be provided the first time the daemon is started, after that it is ignored. If the isle binary has a bootstrap built into it then this argument is always optional. ` ,
2021-04-20 21:31:37 +00:00
)
2022-11-13 15:45:42 +00:00
logLevelStr := flags . StringP (
"log-level" , "l" , "info" ,
` Maximum log level which should be output. Values can be "debug", "info", "warn", "error", "fatal". Does not apply to sub-processes ` ,
)
2021-04-20 21:31:37 +00:00
if err := flags . Parse ( subCmdCtx . args ) ; err != nil {
return fmt . Errorf ( "parsing flags: %w" , err )
}
2022-11-13 15:45:42 +00:00
ctx := subCmdCtx . ctx
2021-04-20 21:31:37 +00:00
if * dumpConfig {
2022-10-26 22:37:03 +00:00
return daemon . CopyDefaultConfig ( os . Stdout , envAppDirPath )
2021-04-20 21:31:37 +00:00
}
2022-11-13 15:45:42 +00:00
logLevel := mlog . LevelFromString ( * logLevelStr )
if logLevel == nil {
return fmt . Errorf ( "couldn't parse log level %q" , * logLevelStr )
}
logger := subCmdCtx . logger . WithMaxLevel ( logLevel . Int ( ) )
runtimeDirCleanup , err := setupAndLockRuntimeDir ( ctx , logger )
2022-10-26 22:45:40 +00:00
if err != nil {
return fmt . Errorf ( "setting up runtime directory: %w" , err )
2021-04-20 21:31:37 +00:00
}
2022-10-26 22:45:40 +00:00
defer runtimeDirCleanup ( )
2021-04-20 21:31:37 +00:00
2022-10-26 22:23:39 +00:00
var (
2024-06-24 12:45:57 +00:00
bootstrapStateDirPath = bootstrap . StateDirPath ( daemonEnvVars . StateDirPath )
2024-06-17 12:13:53 +00:00
bootstrapAppDirPath = bootstrap . AppDirPath ( envAppDirPath )
2021-04-20 21:31:37 +00:00
2022-11-05 12:57:21 +00:00
hostBootstrapPath string
hostBootstrap bootstrap . Bootstrap
2022-10-26 22:23:39 +00:00
)
2021-04-20 21:31:37 +00:00
2022-10-26 22:23:39 +00:00
tryLoadBootstrap := func ( path string ) bool {
2024-06-16 21:25:23 +00:00
ctx := mctx . Annotate ( ctx , "bootstrapFilePath" , path )
2021-04-20 21:31:37 +00:00
if err != nil {
2022-10-26 22:23:39 +00:00
return false
} else if hostBootstrap , err = bootstrap . FromFile ( path ) ; errors . Is ( err , fs . ErrNotExist ) {
2024-06-16 21:25:23 +00:00
logger . WarnString ( ctx , "bootstrap file not found" )
2022-10-26 22:23:39 +00:00
err = nil
return false
} else if err != nil {
2024-06-10 16:56:36 +00:00
err = fmt . Errorf ( "parsing bootstrap.json at %q: %w" , path , err )
2022-10-26 22:23:39 +00:00
return false
2021-04-20 21:31:37 +00:00
}
2024-06-16 21:25:23 +00:00
logger . Info ( ctx , "bootstrap file found" )
2022-11-13 15:45:42 +00:00
2022-10-26 22:23:39 +00:00
hostBootstrapPath = path
return true
}
2022-11-05 12:57:21 +00:00
switch {
2024-06-17 12:13:53 +00:00
case tryLoadBootstrap ( bootstrapStateDirPath ) :
2022-11-05 12:57:21 +00:00
case * bootstrapPath != "" && tryLoadBootstrap ( * bootstrapPath ) :
case tryLoadBootstrap ( bootstrapAppDirPath ) :
case err != nil :
2024-06-10 16:56:36 +00:00
return fmt . Errorf ( "attempting to load bootstrap.json file: %w" , err )
2022-11-05 12:57:21 +00:00
default :
2024-06-10 16:56:36 +00:00
return errors . New ( "No bootstrap.json file could be found, and one is not provided with --bootstrap-path" )
2022-11-05 12:57:21 +00:00
}
2022-10-26 22:23:39 +00:00
2024-06-17 12:13:53 +00:00
if hostBootstrapPath != bootstrapStateDirPath {
2022-10-26 22:23:39 +00:00
// If the bootstrap file is not being stored in the data dir, copy
// it there, so it can be loaded from there next time.
2024-06-17 20:15:28 +00:00
if err := writeBootstrapToStateDir ( hostBootstrap ) ; err != nil {
2024-06-10 16:56:36 +00:00
return fmt . Errorf ( "writing bootstrap.json to data dir: %w" , err )
2021-04-20 21:31:37 +00:00
}
}
2022-10-26 22:37:03 +00:00
daemonConfig , err := daemon . LoadConfig ( envAppDirPath , * daemonConfigPath )
2022-10-26 21:21:31 +00:00
if err != nil {
return fmt . Errorf ( "loading daemon config: %w" , err )
2021-04-20 21:31:37 +00:00
}
2022-10-16 18:33:31 +00:00
// we update this Host's data using whatever configuration has been
2022-10-26 21:21:31 +00:00
// provided by the daemon config. This way the daemon has the most
// up-to-date possible bootstrap. This updated bootstrap will later get
2024-06-17 18:51:02 +00:00
// updated in garage as a background daemon task, so other hosts will
// see it as well.
if hostBootstrap , err = coalesceDaemonConfigAndBootstrap ( hostBootstrap , daemonConfig ) ; err != nil {
2022-10-26 21:21:31 +00:00
return fmt . Errorf ( "merging daemon config into bootstrap data: %w" , err )
2022-10-16 13:38:15 +00:00
}
2024-06-24 16:55:36 +00:00
daemonInst := daemon . NewDaemonRestarter (
logger , daemonConfig , envBinDirPath , hostBootstrap , nil ,
)
defer func ( ) {
logger . Info ( ctx , "Stopping child processes" )
if err := daemonInst . Shutdown ( ) ; err != nil {
logger . Error ( ctx , "Shutting down daemon cleanly failed, there may be orphaned child processes" , err )
}
logger . Info ( ctx , "Child processes successfully stopped" )
} ( )
2021-04-20 21:31:37 +00:00
2024-06-24 16:55:36 +00:00
{
logger := logger . WithNamespace ( "http" )
httpSrv , err := newHTTPServer (
ctx , logger , daemon . NewRPC ( daemonInst ) ,
)
if err != nil {
return fmt . Errorf ( "starting HTTP server: %w" , err )
2021-04-20 21:31:37 +00:00
}
2024-06-24 16:55:36 +00:00
defer func ( ) {
// see comment in daemonInst shutdown logic regarding background
// context.
logger . Info ( ctx , "Shutting down HTTP socket" )
if err := httpSrv . Shutdown ( context . Background ( ) ) ; err != nil {
logger . Error ( ctx , "Failed to cleanly shutdown http server" , err )
}
} ( )
2021-04-20 21:31:37 +00:00
}
2024-06-24 16:55:36 +00:00
<- ctx . Done ( )
return nil
2021-04-20 21:31:37 +00:00
} ,
}