chore: sync from fn-registry agent
This commit is contained in:
+285
@@ -0,0 +1,285 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func registryRoot() string {
|
||||
if r := os.Getenv("FN_REGISTRY_ROOT"); r != "" {
|
||||
return r
|
||||
}
|
||||
// Subir dos niveles desde apps/deploy_server/
|
||||
exe, _ := os.Executable()
|
||||
return filepath.Dir(filepath.Dir(filepath.Dir(exe)))
|
||||
}
|
||||
|
||||
func storeDir() string {
|
||||
// operations.db en el directorio de la app
|
||||
return "operations.db"
|
||||
}
|
||||
|
||||
func openStoreOrDie() *Store {
|
||||
s, err := OpenStore(storeDir())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening store: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// --- target ---
|
||||
|
||||
func cmdTarget(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: deploy_server target add|list|remove")
|
||||
os.Exit(1)
|
||||
}
|
||||
switch args[0] {
|
||||
case "add":
|
||||
cmdTargetAdd(args[1:])
|
||||
case "list":
|
||||
cmdTargetList()
|
||||
case "remove":
|
||||
cmdTargetRemove(args[1:])
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown target subcommand: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdTargetAdd(args []string) {
|
||||
fs := flag.NewFlagSet("target add", flag.ExitOnError)
|
||||
app := fs.String("app", "", "app name (matches apps/ directory or repo name)")
|
||||
host := fs.String("host", "", "SSH alias from ~/.ssh/config")
|
||||
remoteDir := fs.String("remote-dir", "", "remote directory (default: /opt/apps/<app>)")
|
||||
binaryName := fs.String("binary", "", "binary name (default: app name for systemd strategies)")
|
||||
buildCmd := fs.String("build", "", "build command (local for systemd, remote for systemd-remote)")
|
||||
serviceUser := fs.String("user", "", "systemd service user")
|
||||
port := fs.Int("port", 0, "service port")
|
||||
healthPath := fs.String("health", "", "health check path (e.g. /api/health)")
|
||||
envJSON := fs.String("env", "{}", "env vars as JSON object")
|
||||
strategy := fs.String("strategy", "systemd", "deploy strategy: systemd, systemd-remote, docker-compose")
|
||||
sourceDir := fs.String("source-dir", "", "local source dir relative to registry root (overrides apps/<app>)")
|
||||
branch := fs.String("branch", "main", "git branch for remote deploys")
|
||||
composeFiles := fs.String("compose-files", "", "comma-separated extra compose files for docker-compose strategy")
|
||||
fs.Parse(args)
|
||||
|
||||
if *app == "" || *host == "" {
|
||||
fmt.Fprintln(os.Stderr, "required: --app and --host")
|
||||
fs.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *remoteDir == "" {
|
||||
*remoteDir = "/opt/apps/" + *app
|
||||
}
|
||||
if *binaryName == "" && *strategy != "docker-compose" {
|
||||
*binaryName = *app
|
||||
}
|
||||
|
||||
var env map[string]string
|
||||
json.Unmarshal([]byte(*envJSON), &env)
|
||||
|
||||
s := openStoreOrDie()
|
||||
defer s.Close()
|
||||
|
||||
t := DeployTarget{
|
||||
App: *app,
|
||||
Host: *host,
|
||||
RemoteDir: *remoteDir,
|
||||
BinaryName: *binaryName,
|
||||
BuildCmd: *buildCmd,
|
||||
ServiceUser: *serviceUser,
|
||||
Port: *port,
|
||||
HealthPath: *healthPath,
|
||||
Env: env,
|
||||
Strategy: *strategy,
|
||||
SourceDir: *sourceDir,
|
||||
Branch: *branch,
|
||||
ComposeFiles: *composeFiles,
|
||||
}
|
||||
|
||||
if err := s.AddTarget(t); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("target added: %s [%s] → %s:%s\n", *app, *strategy, *host, *remoteDir)
|
||||
}
|
||||
|
||||
func cmdTargetList() {
|
||||
s := openStoreOrDie()
|
||||
defer s.Close()
|
||||
|
||||
targets, err := s.ListTargets()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(targets) == 0 {
|
||||
fmt.Println("No deploy targets configured.")
|
||||
return
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "APP\tSTRATEGY\tHOST\tREMOTE DIR\tPORT\tHEALTH")
|
||||
for _, t := range targets {
|
||||
health := t.HealthPath
|
||||
if health == "" {
|
||||
health = "-"
|
||||
}
|
||||
strategy := t.Strategy
|
||||
if strategy == "" {
|
||||
strategy = "systemd"
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\n", t.App, strategy, t.Host, t.RemoteDir, t.Port, health)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func cmdTargetRemove(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: deploy_server target remove <app>")
|
||||
os.Exit(1)
|
||||
}
|
||||
s := openStoreOrDie()
|
||||
defer s.Close()
|
||||
|
||||
if err := s.RemoveTarget(args[0]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("target removed: %s\n", args[0])
|
||||
}
|
||||
|
||||
// --- deploy ---
|
||||
|
||||
func cmdDeploy(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: deploy_server deploy <app> [--host HOST]")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fs := flag.NewFlagSet("deploy", flag.ExitOnError)
|
||||
host := fs.String("host", "", "deploy to specific host only")
|
||||
fs.Parse(args[1:])
|
||||
|
||||
app := args[0]
|
||||
s := openStoreOrDie()
|
||||
defer s.Close()
|
||||
|
||||
targets, err := s.GetTargets(app)
|
||||
if err != nil || len(targets) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no deploy targets for %q\n", app)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
d := NewDeployer(s, registryRoot())
|
||||
|
||||
for _, t := range targets {
|
||||
if *host != "" && t.Host != *host {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("deploying %s → %s:%s\n", t.App, t.Host, t.RemoteDir)
|
||||
if err := d.Deploy(t, "manual"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, " FAILED: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" OK\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- setup ---
|
||||
|
||||
func cmdSetup(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: deploy_server setup <app> --host HOST")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fs := flag.NewFlagSet("setup", flag.ExitOnError)
|
||||
host := fs.String("host", "", "SSH host alias (required)")
|
||||
fs.Parse(args[1:])
|
||||
|
||||
if *host == "" {
|
||||
fmt.Fprintln(os.Stderr, "required: --host")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
app := args[0]
|
||||
s := openStoreOrDie()
|
||||
defer s.Close()
|
||||
|
||||
targets, err := s.GetTargets(app)
|
||||
if err != nil || len(targets) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no deploy target for %q — add one first with 'target add'\n", app)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
d := NewDeployer(s, registryRoot())
|
||||
|
||||
for _, t := range targets {
|
||||
if t.Host != *host {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("setting up %s on %s...\n", t.App, t.Host)
|
||||
if err := d.Setup(t); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FAILED: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "no target for %s on host %s\n", app, *host)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// --- status ---
|
||||
|
||||
func cmdStatus(args []string) {
|
||||
fs := flag.NewFlagSet("status", flag.ExitOnError)
|
||||
all := fs.Bool("all", false, "show all targets")
|
||||
fs.Parse(args)
|
||||
|
||||
s := openStoreOrDie()
|
||||
defer s.Close()
|
||||
|
||||
d := NewDeployer(s, registryRoot())
|
||||
|
||||
var targets []DeployTarget
|
||||
if *all {
|
||||
targets, _ = s.ListTargets()
|
||||
} else if fs.NArg() > 0 {
|
||||
targets, _ = s.GetTargets(fs.Arg(0))
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "usage: deploy_server status <app> | --all")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, t := range targets {
|
||||
fmt.Printf("=== %s @ %s ===\n", t.App, t.Host)
|
||||
out, err := d.Status(t)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, " error: %v\n", err)
|
||||
} else {
|
||||
fmt.Println(out)
|
||||
}
|
||||
|
||||
// Últimos 3 deploys
|
||||
logs, _ := s.RecentLogs(t.App, 3)
|
||||
if len(logs) > 0 {
|
||||
fmt.Println(" Recent deploys:")
|
||||
for _, l := range logs {
|
||||
errStr := ""
|
||||
if l.Error != "" {
|
||||
errStr = " — " + l.Error
|
||||
}
|
||||
fmt.Printf(" %s %s %s %dms%s\n", l.StartedAt.Format("2006-01-02 15:04"), l.Trigger, l.Status, l.Duration, errStr)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user