package main import ( "encoding/json" "flag" "fmt" "log" "os" "fuego/internal/runner" ) type SecretsFile struct { RunnerID int64 `json:"runner_id"` RunnerSecret string `json:"runner_secret"` ManagerSecret string `json:"manager_secret"` } func main() { var ( managerURL = flag.String("manager", getEnv("MANAGER_URL", "http://localhost:8080"), "Manager URL") name = flag.String("name", getEnv("RUNNER_NAME", ""), "Runner name") hostname = flag.String("hostname", getEnv("RUNNER_HOSTNAME", ""), "Runner hostname") ipAddress = flag.String("ip", getEnv("RUNNER_IP", ""), "Runner IP address") token = flag.String("token", getEnv("REGISTRATION_TOKEN", ""), "Registration token") secretsFile = flag.String("secrets-file", getEnv("SECRETS_FILE", ""), "Path to secrets file for persistent storage") ) flag.Parse() if *name == "" { hostname, _ := os.Hostname() *name = fmt.Sprintf("runner-%s", hostname) } if *hostname == "" { *hostname, _ = os.Hostname() } if *ipAddress == "" { *ipAddress = "127.0.0.1" } client := runner.NewClient(*managerURL, *name, *hostname, *ipAddress) // Try to load secrets from file var runnerID int64 var runnerSecret, managerSecret string if *secretsFile != "" { if secrets, err := loadSecrets(*secretsFile); err == nil { runnerID = secrets.RunnerID runnerSecret = secrets.RunnerSecret managerSecret = secrets.ManagerSecret client.SetSecrets(runnerID, runnerSecret, managerSecret) log.Printf("Loaded secrets from %s", *secretsFile) } } // If no secrets loaded, register with token if runnerID == 0 { if *token == "" { log.Fatalf("Registration token required (use --token or set REGISTRATION_TOKEN env var)") } var err error runnerID, runnerSecret, managerSecret, err = client.Register(*token) if err != nil { log.Fatalf("Failed to register runner: %v", err) } log.Printf("Registered runner with ID: %d", runnerID) // Save secrets to file if specified if *secretsFile != "" { secrets := SecretsFile{ RunnerID: runnerID, RunnerSecret: runnerSecret, ManagerSecret: managerSecret, } if err := saveSecrets(*secretsFile, secrets); err != nil { log.Printf("Warning: Failed to save secrets: %v", err) } else { log.Printf("Saved secrets to %s", *secretsFile) } } } // Start WebSocket connection with reconnection go client.ConnectWebSocketWithReconnect() // Start heartbeat loop (for WebSocket ping/pong and HTTP fallback) go client.HeartbeatLoop() // ProcessTasks is now handled via WebSocket, but kept for HTTP fallback // WebSocket will handle task assignment automatically log.Printf("Runner started, connecting to manager via WebSocket...") // Block forever select {} } func loadSecrets(path string) (*SecretsFile, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var secrets SecretsFile if err := json.Unmarshal(data, &secrets); err != nil { return nil, err } return &secrets, nil } func saveSecrets(path string, secrets SecretsFile) error { data, err := json.MarshalIndent(secrets, "", " ") if err != nil { return err } return os.WriteFile(path, data, 0600) } func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue }