From ca563e8cc07b6e62874f7e7e129a659ee77f93bc Mon Sep 17 00:00:00 2001 From: encodeous Date: Tue, 21 Oct 2025 22:07:43 -0400 Subject: [PATCH] refactor: bring startup logic to entrypoint.go --- cmd/run.go | 99 +--------------------- core/{runtime.go => entrypoint.go} | 128 +++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 96 deletions(-) rename core/{runtime.go => entrypoint.go} (59%) diff --git a/cmd/run.go b/cmd/run.go index 409392d8..4f51c7d7 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -1,16 +1,9 @@ package cmd import ( - "log" - "log/slog" - "net/http" - "os" - "runtime/trace" - "github.com/encodeous/nylon/core" "github.com/encodeous/nylon/state" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" ) import _ "net/http/pprof" // remove in stable version of nylon @@ -21,102 +14,16 @@ var runCmd = &cobra.Command{ Short: "Run nylon", Long: `This will run nylon`, Run: func(cmd *cobra.Command, args []string) { - if state.DBG_trace { - f, err := os.Create("trace.out") - if err != nil { - log.Fatal(err) - } - err = trace.Start(f) - defer trace.Stop() - if err != nil { - return - } - log.Println("Started tracing") - } - if state.DBG_pprof { - go func() { - log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) - }() - } - centralPath := cmd.Flag("config").Value.String() nodePath := cmd.Flag("node").Value.String() logPath := cmd.Flag("log").Value.String() - start: - var centralCfg state.CentralCfg - file, err := os.ReadFile(centralPath) - if err != nil { - if !os.IsNotExist(err) { - panic(err) - } - var nodeCfg state.LocalCfg - file, err = os.ReadFile(nodePath) - if err != nil { - panic(err) - } - err = yaml.Unmarshal(file, &nodeCfg) - if err != nil { - panic(err) - } - if nodeCfg.Dist == nil { - panic("central.yaml not found and node.yaml has no dist config") - } - cfg, err := core.FetchConfig(nodeCfg.Dist.Url, nodeCfg.Dist.Key) - if err != nil { - panic(err) - } - bytes, err := yaml.Marshal(cfg) - if err != nil { - panic(err) - } - err = os.WriteFile(centralPath, bytes, 0700) - if err != nil { - panic(err) - } - centralCfg = *cfg - } else { - err = yaml.Unmarshal(file, ¢ralCfg) - if err != nil { - panic(err) - } - } - - var nodeCfg state.LocalCfg - file, err = os.ReadFile(nodePath) - if err != nil { - panic(err) - } - err = yaml.Unmarshal(file, &nodeCfg) - if err != nil { - panic(err) - } - if logPath != "" { - nodeCfg.LogPath = logPath - } - - err = state.CentralConfigValidator(¢ralCfg) - if err != nil { - panic(err) - } - state.ExpandCentralConfig(¢ralCfg) - err = state.NodeConfigValidator(&nodeCfg) - if err != nil { - panic(err) - } - - level := slog.LevelInfo + isVerbose := false if ok, _ := cmd.Flags().GetBool("verbose"); ok { - level = slog.LevelDebug + isVerbose = true } - restart, err := core.Start(centralCfg, nodeCfg, level, centralPath, nil, nil) - if err != nil { - panic(err) - } - if restart { - goto start - } + core.Bootstrap(centralPath, nodePath, logPath, isVerbose) }, GroupID: "ny", } diff --git a/core/runtime.go b/core/entrypoint.go similarity index 59% rename from core/runtime.go rename to core/entrypoint.go index 7e5a31b3..88691518 100644 --- a/core/runtime.go +++ b/core/entrypoint.go @@ -3,20 +3,148 @@ package core import ( "context" "errors" + "fmt" + "log" "log/slog" + "net/http" "os" "os/signal" "path" "reflect" "runtime" + "runtime/trace" "syscall" "time" "github.com/encodeous/nylon/state" "github.com/encodeous/tint" slogmulti "github.com/samber/slog-multi" + "gopkg.in/yaml.v3" ) +func setupDebugging() { + if state.DBG_trace { + f, err := os.Create("trace.out") + if err != nil { + log.Fatal(err) + } + err = trace.Start(f) + defer trace.Stop() + if err != nil { + return + } + log.Println("Started tracing") + } + if state.DBG_pprof { + go func() { + log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) + }() + } +} + +func readCentralConfig(centralPath, nodePath string) (*state.CentralCfg, error) { + var centralCfg state.CentralCfg + + file, err := os.ReadFile(centralPath) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + // fallback to using dist from node config + + var nodeCfg state.LocalCfg + + file, err = os.ReadFile(nodePath) + if err != nil { + return nil, fmt.Errorf("central.yaml not found and failed to read node.yaml: %w", err) + } + + err = yaml.Unmarshal(file, &nodeCfg) + if err != nil { + return nil, err + } + + if nodeCfg.Dist == nil { + return nil, fmt.Errorf("central.yaml not found and node.yaml has no dist config") + } + + cfg, err := FetchConfig(nodeCfg.Dist.Url, nodeCfg.Dist.Key) + if err != nil { + return nil, err + } + + bytes, err := yaml.Marshal(cfg) + if err != nil { + return nil, err + } + err = os.WriteFile(centralPath, bytes, 0700) + if err != nil { + return nil, err + } + + centralCfg = *cfg + } else { + err = yaml.Unmarshal(file, ¢ralCfg) + if err != nil { + return nil, err + } + } + return ¢ralCfg, nil +} + +func readNodeConfig(nodePath string) (*state.LocalCfg, error) { + var nodeCfg state.LocalCfg + file, err := os.ReadFile(nodePath) + if err != nil { + return nil, err + } + err = yaml.Unmarshal(file, &nodeCfg) + if err != nil { + return nil, err + } + return &nodeCfg, nil +} + +// Bootstrap manages the lifetime of the whole application. Nylon may be restarted multiple times, but Bootstrap is only called once. +func Bootstrap(centralPath, nodePath, logPath string, verbose bool) { + setupDebugging() + level := slog.LevelInfo + if verbose { + level = slog.LevelDebug + } + + for { + centralCfg, err := readCentralConfig(centralPath, nodePath) + if err != nil { + panic(err) + } + nodeCfg, err := readNodeConfig(nodePath) + if err != nil { + panic(err) + } + if logPath != "" { + nodeCfg.LogPath = logPath + } + + err = state.CentralConfigValidator(centralCfg) + if err != nil { + panic(err) + } + state.ExpandCentralConfig(centralCfg) + err = state.NodeConfigValidator(nodeCfg) + if err != nil { + panic(err) + } + restart, err := Start(*centralCfg, *nodeCfg, level, centralPath, nil, nil) + if err != nil { + panic(err) + } + if !restart { + break + } + } +} + func Start(ccfg state.CentralCfg, ncfg state.LocalCfg, logLevel slog.Level, configPath string, aux map[string]any, initState **state.State) (bool, error) { ctx, cancel := context.WithCancelCause(context.Background())