diff --git a/.gitignore b/.gitignore index e339fd7..79ea117 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ certs/** !certs/*.go +config.yaml +config.yml +packagelock.pid diff --git a/Dockerfile b/Dockerfile index bf5f574..40dd72e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN go mod download # Build ARG APP_VERSION="v0.1.0+hotfixes" RUN \ - CGO_ENABLED=0 GOOS=linux go build -ldflags "-X 'main.AppVersion=$APP_VERSION'" -o /app/packagelock + CGO_ENABLED=0 GOOS=linux go build -ldflags "-X 'main.AppVersion=$APP_VERSION'" -o /packagelock # Optional: # To bind to a TCP port, runtime parameters must be supplied to the docker command. @@ -28,4 +28,5 @@ RUN \ EXPOSE 8080 # Run -CMD ["/app/packagelock start"] +CMD ["/packagelock", "start"] + diff --git a/README.md b/README.md index 0c7891c..ca76ab5 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,21 @@ TODO: explain usage - [ ] Check Vars and Func-Names for naming convention - [ ] persistent storage - [ ] implement interfaces for external functions for easier mocking in tests +- [ ] systemd service start/stop/enable/disable +- [ ] copy app file (.deb/rpm/binary) via SFTP to host and start stop +- [ ] binary self-Update +- [ ] agent can run docker/podman containers +- [ ] agent fetches running docker/podman containers, updates, restarts etc +- [ ] user management & SSH keys +- [ ] system definition in mpackagelock file for easy recovery & scaling +- [ ] CLI-Commands to add: + - [ ] sync now|timestamp - force sync the server with the Agents + - [ ] logs -s (severity) info|warning|error -d (date to start) 2024-08-23-10-00-00 (date-time) + - [ ] backup - Creates a backup from server, server config, database + - [ ] generate certs letsencrypt - lets encrypt certs + - [ ] generate certs letsencrypt renew - renews + - [ ] test - runs healthchecks on server + - [ ] test agents - runs healthchecks on agents diff --git a/config.yaml b/config.yaml deleted file mode 100644 index 50f78fe..0000000 --- a/config.yaml +++ /dev/null @@ -1,12 +0,0 @@ -general: - debug: true - production: false -network: - fqdn: 0.0.0.0 - port: 8080 - ssl: true - ssl-config: - redirecthttp: true - allowselfsigned: true - certificatepath: ./certs/testing.crt - privatekeypath: ./certs/testing.key diff --git a/config/conf-init.go b/config/conf-init.go index 3db2a00..8f07d7a 100644 --- a/config/conf-init.go +++ b/config/conf-init.go @@ -55,17 +55,17 @@ func CreateDefaultConfig(config ConfigProvider) { // TODO: Add default config yamlExample := []byte(` general: - debug: True - production: False - Port: 8080 - -Network: - FQDN: "packagelock.company.com" - ForceHTTP: False - SSL: - CertificatePath: "/etc/packagelock/ssl/cert.pem" - PrivateKeyPath: "/etc/packagelock/ssl/privkey.pem" - AllowSelfSigned: False + debug: true + production: false +network: + fqdn: 0.0.0.0 + port: 8080 + ssl: true + ssl-config: + redirecthttp: true + allowselfsigned: true + certificatepath: ./certs/testing.crt + privatekeypath: ./certs/testing.key `) err := config.ReadConfig(bytes.NewBuffer(yamlExample)) diff --git a/main.go b/main.go index 930619a..763783a 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "packagelock/certs" "packagelock/config" "packagelock/server" + "strconv" "syscall" "time" @@ -38,9 +39,64 @@ var startCmd = &cobra.Command{ }, } +var restartCmd = &cobra.Command{ + Use: "restart", + Short: "Restart the running server", + Run: func(cmd *cobra.Command, args []string) { + restartServer() + }, +} + +var stopCmd = &cobra.Command{ + Use: "stop", + Short: "Stop the running server", + Run: func(cmd *cobra.Command, args []string) { + stopServer() + }, +} + +// Generate command +var generateCmd = &cobra.Command{ + Use: "generate [certs|config]", + Short: "Generate certs or config files", + Long: "Generate certificates or configuration files required by the application.", + Args: cobra.MatchAll(cobra.ExactArgs(1), validGenerateArgs()), // Expect exactly one argument: either "certs" or "config" + ValidArgs: []string{"certs", "config"}, // Restrict arguments to these options + Run: func(cmd *cobra.Command, args []string) { + switch args[0] { + case "certs": + err := certs.CreateSelfSignedCert( + config.Config.GetString("network.ssl-config.certificatepath"), + config.Config.GetString("network.ssl-config.privatekeypath")) + if err != nil { + fmt.Println("There was an error generating the self signed certs: %w", err) + } + case "config": + config.CreateDefaultConfig(config.Config) + default: + fmt.Println("Invalid argument. Use 'certs' or 'config'.") + } + }, +} + +func validGenerateArgs() cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + validArgs := []string{"certs", "config"} + for _, valid := range validArgs { + if args[0] == valid { + return nil + } + } + return fmt.Errorf("invalid argument: '%s'. Must be one of 'certs' or 'config'", args[0]) + } +} + func init() { // Add commands to rootCmd rootCmd.AddCommand(startCmd) + rootCmd.AddCommand(generateCmd) + rootCmd.AddCommand(restartCmd) + rootCmd.AddCommand(stopCmd) // Initialize Viper config cobra.OnInitialize(initConfig) @@ -70,6 +126,13 @@ func initConfig() { // startServer starts the Fiber server with appropriate configuration func startServer() { + pid := os.Getpid() + err := os.WriteFile("packagelock.pid", []byte(strconv.Itoa(pid)), 0644) + if err != nil { + fmt.Printf("Failed to write PID file: %v\n", err) + return + } + fmt.Println(config.Config.AllSettings()) signal.Notify(quitChan, os.Interrupt, syscall.SIGTERM) @@ -86,11 +149,12 @@ func startServer() { go func() { if config.Config.GetBool("network.ssl") { fmt.Printf("Starting Fiber HTTPS server at https://%s...\n", serverAddr) - if err := server.ListenAndServeTLS( + err := server.ListenAndServeTLS( router.Router, config.Config.GetString("network.ssl-config.certificatepath"), config.Config.GetString("network.ssl-config.privatekeypath"), - serverAddr); err != nil { + serverAddr) + if err != nil { fmt.Printf("Server error: %s\n", err) } } else { @@ -112,6 +176,7 @@ func startServer() { } else { fmt.Println("Server stopped.") } + startServer() case <-quitChan: fmt.Println("Shutting down Fiber server...") @@ -140,6 +205,44 @@ func startServer() { fmt.Println("Main process exiting.") } +func restartServer() { + stopServer() + fmt.Println("Restarting the Server...") + time.Sleep(5 * time.Second) + startServer() +} + +func stopServer() { + // Read the PID from the file using os.ReadFile + data, err := os.ReadFile("packagelock.pid") + if err != nil { + fmt.Printf("Could not read PID file: %v\n", err) + return + } + + pid, err := strconv.Atoi(string(data)) + if err != nil { + fmt.Printf("Invalid PID found in file: %v\n", err) + return + } + + // Send SIGTERM to the process + fmt.Printf("Stopping the server with PID: %d\n", pid) + err = syscall.Kill(pid, syscall.SIGTERM) + if err != nil { + fmt.Printf("Failed to stop the server: %v\n", err) + } else { + fmt.Println("Server stopped.") + // After successful stop, remove the PID file + err = os.Remove("packagelock.pid") + if err != nil { + fmt.Printf("Failed to remove PID file: %v\n", err) + } else { + fmt.Println("PID file removed successfully.") + } + } +} + func main() { // Execute the Cobra root command if err := rootCmd.Execute(); err != nil {