diff --git a/internal/cmd/proglog/main.go b/internal/cmd/proglog/main.go new file mode 100644 index 0000000..a07fc31 --- /dev/null +++ b/internal/cmd/proglog/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "errors" + "log" + "os" + "os/signal" + "path" + "syscall" + + "github.com/justagabriel/proglog/internal/agent" + "github.com/justagabriel/proglog/internal/config" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func main() { + cli := &cli{} + cmd := &cobra.Command{ + Use: "proglog", + PreRunE: cli.setupConfig, + RunE: cli.run, + } + if err := setupFlags(cmd); err != nil { + log.Fatal(err) + } + if err := cmd.Execute(); err != nil { + log.Fatal(err) + } +} + +type cli struct { + cfg cfg +} + +type cfg struct { + agent.Config + ServerTLSConfig config.TLSConfig + PeerTLSConfig config.TLSConfig +} + +func setupFlags(cmd *cobra.Command) error { + hostname, err := os.Hostname() + if err != nil { + return err + } + + cmd.Flags().String("node-name", hostname, "Unique server ID.") + + dataDir := path.Join(os.TempDir(), "proglog") + cmd.Flags().String("data-dir", dataDir, "Directory to store log and Raft data.") + + cmd.Flags().String("config-file", "", "Path to config file.") + cmd.Flags().String("bind-addr", "127.0.01:8401", "Address to bind Serf on.") + cmd.Flags().Int("rpc-port", 8400, "Port for RPC clients (and Raft) connections.") + cmd.Flags().StringSlice("start-join-addrs", nil, "Serf addresses to join.") + cmd.Flags().Bool("bootstrap", false, "Bootstrap the cluster.") + + cmd.Flags().String("acl-model-file", "", "Path to ACL model.") + cmd.Flags().String("acl-policy-file", "", "Path to ACL policy.") + + cmd.Flags().String("server-tls-cert-file", "", "Path to server tls cert.") + cmd.Flags().String("server-tls-key-file", "", "Path to server tls key.") + cmd.Flags().String("server-tls-ca-file", "", "Path to server certificate authority.") + + cmd.Flags().String("peer-tls-cert-file", "", "Path to peer tls cert.") + cmd.Flags().String("peer-tls-key-file", "", "Path to peer tls key.") + cmd.Flags().String("peer-tls-ca-file", "", "Path to peer certificate authority.") + + return viper.BindPFlags(cmd.Flags()) +} + +func (c *cli) setupConfig(cmd *cobra.Command, args []string) error { + configFile, err := cmd.Flags().GetString("config-file") + if err != nil { + return err + } + + viper.SetConfigFile(configFile) + if err = viper.ReadInConfig(); err != nil { + // it's ok if the file doesn't exist + if !errors.Is(err, viper.ConfigFileNotFoundError{}) { + return err + } + } + + c.cfg.DataDir = viper.GetString("data-dir") + c.cfg.NodeName = viper.GetString("node-name") + c.cfg.BindAddr = viper.GetString("rpc-port") + c.cfg.StartJoinAddr = viper.GetStringSlice("start-join-addrs") + c.cfg.Bootstrap = viper.GetBool("bootstrap") + + c.cfg.ACLModelFile = viper.GetString("acl-model-file") + c.cfg.ACLPolicyFile = viper.GetString("acl-policy-file") + + c.cfg.ServerTLSConfig.CertFile = viper.GetString("server-tls-cert-file") + c.cfg.ServerTLSConfig.KeyFile = viper.GetString("server-tls-key-file") + c.cfg.ServerTLSConfig.CAFile = viper.GetString("server-tls-ca-file") + + c.cfg.PeerTLSConfig.CertFile = viper.GetString("peer-tls-cert-file") + c.cfg.PeerTLSConfig.KeyFile = viper.GetString("peer-tls-key-file") + c.cfg.PeerTLSConfig.CAFile = viper.GetString("peer-tls-ca-file") + + if c.cfg.ServerTLSConfig.CertFile != "" && c.cfg.ServerTLSConfig.KeyFile != "" { + c.cfg.ServerTLSConfig.Server = true + c.cfg.Config.ServerTLSConfig, err = config.SetupTLSConfig(c.cfg.ServerTLSConfig) + if err != nil { + return err + } + } + + if c.cfg.PeerTLSConfig.CertFile != "" && c.cfg.PeerTLSConfig.KeyFile != "" { + c.cfg.Config.PeerTLSConfig, err = config.SetupTLSConfig(c.cfg.PeerTLSConfig) + if err != nil { + return err + } + } + + return nil +} + +func (c *cli) run(cmd *cobra.Command, args []string) error { + var err error + agent, err := agent.New(c.cfg.Config) + if err != nil { + return err + } + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) + <-sigc + return agent.Shutdown() +}