iop/apps/node/cmd/node/main.go

137 lines
3.8 KiB
Go

package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
"go.uber.org/fx"
"gopkg.in/yaml.v3"
"iop/apps/node/internal/bootstrap"
"iop/packages/go/config"
"iop/packages/go/hostsetup"
"iop/packages/go/version"
)
var cfgFile string
func main() {
if err := rootCmd().Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func rootCmd() *cobra.Command {
root := &cobra.Command{
Use: "node",
Short: "IOP Node Agent — runs adapter executions on this device",
}
root.PersistentFlags().StringVarP(&cfgFile, "config", "c", "configs/node.yaml", "config file path")
root.AddCommand(serveCmd(), versionCmd(), configCmd(), setupCmd())
return root
}
func serveCmd() *cobra.Command {
return &cobra.Command{
Use: "serve",
Short: "Start the IOP node (connects to edge)",
RunE: func(cmd *cobra.Command, _ []string) error {
cfg, err := config.Load(cfgFile)
if err != nil {
return fmt.Errorf("load config: %w", err)
}
app := fx.New(bootstrap.Module(cfg))
app.Run()
return nil
},
}
}
func versionCmd() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Print IOP node version",
Run: func(cmd *cobra.Command, _ []string) {
fmt.Fprintln(cmd.OutOrStdout(), version.Version)
},
}
}
func configCmd() *cobra.Command {
cfgGroup := &cobra.Command{
Use: "config",
Short: "Config management commands",
}
cfgGroup.AddCommand(configPrintCmd(), configCheckCmd())
return cfgGroup
}
func configPrintCmd() *cobra.Command {
return &cobra.Command{
Use: "print",
Short: "Print the resolved configuration",
RunE: func(cmd *cobra.Command, _ []string) error {
cfg, err := config.Load(cfgFile)
if err != nil {
return fmt.Errorf("load config: %w", err)
}
enc := yaml.NewEncoder(cmd.OutOrStdout())
enc.SetIndent(2)
return enc.Encode(cfg)
},
}
}
func configCheckCmd() *cobra.Command {
return &cobra.Command{
Use: "check",
Short: "Validate node config can be loaded",
RunE: func(cmd *cobra.Command, _ []string) error {
if _, err := config.Load(cfgFile); err != nil {
return fmt.Errorf("load config: %w", err)
}
fmt.Fprintf(cmd.OutOrStdout(), "OK %s\n", cfgFile)
return nil
},
}
}
func setupCmd() *cobra.Command {
var opts hostsetup.SetupOptions
c := &cobra.Command{
Use: "setup",
Short: "Prepare host environment and systemd unit for iop-node",
RunE: func(cmd *cobra.Command, _ []string) error {
if cmd.Flags().Changed("config") {
opts.ConfigPath = cfgFile
} else {
opts.ConfigPath = "/etc/iop/node.yaml"
}
return hostsetup.Run(cmd.Context(), hostsetup.NodeSpec(), opts, cmd.OutOrStdout())
},
}
// setup --help shows /etc/iop/node.yaml as the --config default, not the dev default.
c.SetHelpFunc(func(cmd *cobra.Command, args []string) {
if f := cmd.Root().PersistentFlags().Lookup("config"); f != nil {
prev := f.DefValue
f.DefValue = "/etc/iop/node.yaml"
defer func() { f.DefValue = prev }()
}
cmd.Root().HelpFunc()(cmd, args)
})
c.Flags().StringVar(&opts.BinaryPath, "binary", "", "path to iop-node binary (defaults to current executable)")
c.Flags().StringVar(&opts.DataDir, "data-dir", "", "data directory (defaults to spec)")
c.Flags().StringVar(&opts.UnitPath, "unit", "", "systemd unit path (defaults to spec)")
c.Flags().StringVar(&opts.User, "user", "", "service user")
c.Flags().StringVar(&opts.Group, "group", "", "service group")
c.Flags().BoolVar(&opts.Enable, "enable", false, "enable the systemd unit")
c.Flags().BoolVar(&opts.Start, "start", false, "start the systemd unit")
c.Flags().BoolVar(&opts.Restart, "restart", false, "restart the systemd unit")
c.Flags().BoolVar(&opts.DryRun, "dry-run", false, "print plan and previews without making changes")
c.Flags().BoolVar(&opts.OverwriteConfig, "overwrite-config", false, "overwrite an existing config file")
return c
}