Skip to content

Commit

Permalink
refactor: add all other cobra commands to work with fx
Browse files Browse the repository at this point in the history
  • Loading branch information
HilkopterBob committed Oct 14, 2024
1 parent 0709a0a commit 9a7b5a8
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 91 deletions.
94 changes: 69 additions & 25 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ package cmd
import (
"context"
"fmt"
"os"
"packagelock/certs"
"packagelock/config"
"packagelock/db"
"packagelock/logger"
"packagelock/structs"
"time"

configPkg "packagelock/config"

"github.com/google/uuid"
"github.com/k0kubun/pp"
"github.com/sethvargo/go-password/password"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -20,56 +24,77 @@ import (
"golang.org/x/crypto/bcrypt"
)

func NewGenerateCmd(rootParams RootParams) *cobra.Command {
func NewGenerateCmd() *cobra.Command {
generateCmd := &cobra.Command{
Use: "generate [certs|config|admin]",
Short: "Generate certificates, configuration files, or an admin",
Long: "Generate certificates, configuration files, or an admin user required by the application.",
Args: cobra.ExactValidArgs(1),

Check failure on line 32 in cmd/generate.go

View workflow job for this annotation

GitHub Actions / lint

SA1019: cobra.ExactValidArgs is deprecated: use MatchAll(ExactArgs(n), OnlyValidArgs) instead (staticcheck)
ValidArgs: []string{"certs", "config", "admin"},
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
fmt.Println("Please specify an argument: certs, config, or admin.")
return
}
switch args[0] {
case "certs":
app := fx.New(
fx.Supply(rootParams),
fx.Provide(func() string { return "Command Runner" }),
logger.Module,
config.Module,
configPkg.Module,
certs.Module,
fx.Invoke(func(certGen *certs.CertGenerator, logger *zap.Logger, config *viper.Viper) {
err := certGen.CreateSelfSignedCert(
config.GetString("network.ssl-config.certificatepath"),
config.GetString("network.ssl-config.privatekeypath"),
)
if err != nil {
fmt.Printf("Error generating self-signed certs: %v\n", err)
logger.Warn("Error generating self-signed certs", zap.Error(err))
}
}),
fx.Invoke(runGenerateCerts),
)

if err := app.Start(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to start application for certificate generation", zap.Error(err))
fmt.Println("Failed to start application for certificate generation:", err)
os.Exit(1)
}

if err := app.Stop(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to stop application after certificate generation", zap.Error(err))
fmt.Println("Failed to stop application after certificate generation:", err)
os.Exit(1)
}
os.Exit(0)
case "config":
config.CreateDefaultConfig(rootParams.Config, rootParams.Logger)
app := fx.New(
fx.Provide(func() string { return "Command Runner" }),
certs.Module,
logger.Module,
configPkg.Module,
fx.Invoke(runGenerateConfig),
)

if err := app.Start(context.Background()); err != nil {
fmt.Println("Failed to start application for config generation:", err)
os.Exit(1)
}

if err := app.Stop(context.Background()); err != nil {
fmt.Println("Failed to stop application after config generation:", err)
os.Exit(1)
}
os.Exit(0)
case "admin":
app := fx.New(
fx.Supply(rootParams),
fx.Provide(func() string { return "Command Runner" }),
certs.Module,
config.Module,
logger.Module,
db.Module,
fx.Invoke(generateAdmin),
fx.Invoke(runGenerateAdmin),
)

if err := app.Start(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to start application for admin generation", zap.Error(err))
fmt.Println("Failed to start application for admin generation:", err)
os.Exit(1)
}

if err := app.Stop(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to stop application after admin generation", zap.Error(err))
fmt.Println("Failed to stop application after admin generation:", err)
os.Exit(1)
}
os.Exit(0)
default:
fmt.Println("Invalid argument. Use 'certs', 'config', or 'admin'.")
}
Expand All @@ -79,8 +104,27 @@ func NewGenerateCmd(rootParams RootParams) *cobra.Command {
return generateCmd
}

// Add the missing generateAdmin function
func generateAdmin(db *db.Database, logger *zap.Logger) {
func runGenerateCerts(certGen *certs.CertGenerator, logger *zap.Logger, config *viper.Viper) {
err := certGen.CreateSelfSignedCert(
config.GetString("network.ssl-config.certificatepath"),
config.GetString("network.ssl-config.privatekeypath"),
)
if err != nil {
fmt.Printf("Error generating self-signed certs: %v\n", err)
logger.Warn("Error generating self-signed certs", zap.Error(err))
} else {
logger.Info("Successfully generated self-signed certificates.")
fmt.Println("Certificates generated successfully.")
}
}

func runGenerateConfig(config *viper.Viper, logger *zap.Logger) {
configPkg.CreateDefaultConfig(config, logger)
logger.Info("Default configuration file created.")
fmt.Println("Configuration file generated successfully.")
}

func runGenerateAdmin(db *db.Database, logger *zap.Logger) {
adminPw, err := password.Generate(64, 10, 10, false, false)
if err != nil {
logger.Fatal("Error generating admin password", zap.Error(err))
Expand Down Expand Up @@ -116,8 +160,8 @@ func generateAdmin(db *db.Database, logger *zap.Logger) {
logger.Fatal("Error querying default admin", zap.Error(err))
}

fmt.Println("Admin Username:", createdUser.Username)
fmt.Println("Admin Password:", adminPw) // Display the original password
pp.Println("Admin Username:", createdUser.Username)
pp.Println("Admin Password:", adminPw) // Display the original password

// For security, consider providing the password securely rather than printing
logger.Info("Admin user created successfully.")
}
29 changes: 22 additions & 7 deletions cmd/print_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ package cmd
import (
"context"
"fmt"
"os"
"packagelock/certs"
"packagelock/config"
"packagelock/db"
"packagelock/handler"
"packagelock/logger"
"packagelock/server"

"github.com/gofiber/fiber/v2"
Expand All @@ -11,36 +17,45 @@ import (
"go.uber.org/zap"
)

func NewPrintRoutesCmd(rootParams RootParams) *cobra.Command {
func NewPrintRoutesCmd() *cobra.Command {
printRoutesCmd := &cobra.Command{
Use: "print-routes",
Short: "Prints out all registered routes",
Run: func(cmd *cobra.Command, args []string) {
app := fx.New(
fx.Supply(rootParams),
fx.Provide(func() string { return "Command Runner" }),
logger.Module,
server.Module,
fx.Invoke(printRoutes),
handler.Module,
config.Module,
certs.Module,
db.Module,
fx.Invoke(runPrintRoutes),
)

if err := app.Start(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to start application for printing routes", zap.Error(err))
fmt.Println("Failed to start application for printing routes:", err)
os.Exit(1)
}

// Since runPrintRoutes runs synchronously, we can stop the app immediately
if err := app.Stop(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to stop application after printing routes", zap.Error(err))
fmt.Println("Failed to stop application after printing routes:", err)
os.Exit(1)
}
},
}

return printRoutesCmd
}

func printRoutes(server *fiber.App, logger *zap.Logger) {
routes := server.Stack() // Get all registered routes
func runPrintRoutes(app *fiber.App, logger *zap.Logger) {
routes := app.Stack() // Get all registered routes
for _, route := range routes {
for _, r := range route {
fmt.Printf("%s %s\n", r.Method, r.Path)
}
}
logger.Info("Printed all routes.")
os.Exit(0)
}
111 changes: 67 additions & 44 deletions cmd/restart.go
Original file line number Diff line number Diff line change
@@ -1,75 +1,98 @@
package cmd

import (
"context"
"fmt"
"packagelock/config"
"packagelock/db"
"packagelock/logger"
"packagelock/server"
"os"
"os/exec"
"strconv"
"syscall"
"time"

"github.com/spf13/cobra"
"go.uber.org/fx"
"go.uber.org/zap"
)

// NewRestartCmd creates the restart command.
func NewRestartCmd(rootParams RootParams) *cobra.Command {
func NewRestartCmd() *cobra.Command {
restartCmd := &cobra.Command{
Use: "restart",
Short: "Restart the running server",
Run: func(cmd *cobra.Command, args []string) {
// Create the Fx application
app := fx.New(
fx.Supply(rootParams),
logger.Module,
config.Module,
db.Module,
server.Module,
)

// Start the application
if err := app.Start(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to start application for restart", zap.Error(err))
// Stop the running server
fmt.Println("Stopping the server...")
if err := stopServer(); err != nil {
fmt.Println("Failed to stop the server:", err)
os.Exit(1)
}

// Perform the restart
if err := restartApplication(app, rootParams.Logger); err != nil {
rootParams.Logger.Fatal("Failed to restart application", zap.Error(err))
}

// Wait for the application to stop
<-app.Done()
// Wait before restarting
time.Sleep(5 * time.Second)

// Stop the application
if err := app.Stop(context.Background()); err != nil {
rootParams.Logger.Fatal("Failed to stop application after restart", zap.Error(err))
// Start the server
fmt.Println("Starting the server...")
if err := startServer(); err != nil {
fmt.Println("Failed to start the server:", err)
os.Exit(1)
}
},
}

return restartCmd
}

func restartApplication(app *fx.App, logger *zap.Logger) error {
// Stop the application
if err := app.Stop(context.Background()); err != nil {
logger.Error("Failed to stop application", zap.Error(err))
return err
// stopServer stops the running server by reading the PID file and sending SIGTERM.
func stopServer() error {
// Read the PID from the file
data, err := os.ReadFile("packagelock.pid")
if err != nil {
return fmt.Errorf("could not read PID file: %w", err)
}

pid, err := strconv.Atoi(string(data))
if err != nil {
return fmt.Errorf("invalid PID found in file: %w", err)
}

// Send SIGTERM to the process
fmt.Printf("Stopping the server with PID: %d\n", pid)

process, err := os.FindProcess(pid)
if err != nil {
return fmt.Errorf("failed to find the process: %w", err)
}

err = process.Signal(syscall.SIGTERM)
if err != nil {
return fmt.Errorf("failed to stop the server: %w", err)
}

fmt.Println("Server stopped.")

// Remove the PID file
err = os.Remove("packagelock.pid")
if err != nil {
fmt.Println("Failed to remove PID file:", err)
} else {
fmt.Println("PID file removed successfully.")
}

// Wait before restarting
time.Sleep(5 * time.Second)
return nil
}

// startServer starts the server by creating a new Fx application and running it in a separate process.
func startServer() error {
// Execute the start command as a new process
executable, err := os.Executable()
if err != nil {
return fmt.Errorf("failed to get executable path: %w", err)
}

fmt.Println("Restarting the application...")
logger.Info("Restarting the application...")
cmd := exec.Command(executable, "start")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// Start the application again
if err := app.Start(context.Background()); err != nil {
logger.Error("Failed to restart application", zap.Error(err))
return err
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start server process: %w", err)
}

fmt.Printf("Server started with PID: %d\n", cmd.Process.Pid)
return nil
}
10 changes: 5 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ func NewRootCmd() *cobra.Command {

// Add subcommands to the root command
rootCmd.AddCommand(NewStartCmd())
// rootCmd.AddCommand(NewStopCmd())
// rootCmd.AddCommand(NewRestartCmd())
// rootCmd.AddCommand(NewSetupCmd())
// rootCmd.AddCommand(NewGenerateCmd())
// rootCmd.AddCommand(NewPrintRoutesCmd())
rootCmd.AddCommand(NewStopCmd())
rootCmd.AddCommand(NewRestartCmd())
rootCmd.AddCommand(NewSetupCmd())
rootCmd.AddCommand(NewGenerateCmd())
rootCmd.AddCommand(NewPrintRoutesCmd())

return rootCmd
}
Expand Down
Loading

0 comments on commit 9a7b5a8

Please sign in to comment.