this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: initial API server and agent integration

Khue Doan 1b9de2fb 96890a0e

+129 -36
+41
cmd/nixie-agent/client.go
··· 1 + package main 2 + 3 + import ( 4 + "fmt" 5 + "log" 6 + "math/rand" 7 + "net/http" 8 + "time" 9 + ) 10 + 11 + func ping(address string) error { 12 + const maxBackoff = time.Minute 13 + backoff := time.Second 14 + client := &http.Client{Timeout: 5 * time.Second} 15 + 16 + for { 17 + // TODO maybe add http:// to kernel params 18 + resp, err := client.Get(fmt.Sprintf("http://%s/ping", address)) 19 + if resp != nil { 20 + resp.Body.Close() 21 + } 22 + 23 + if err != nil { 24 + log.Printf("failed to ping Nixie API: %v", err) 25 + } else { 26 + if resp.StatusCode == http.StatusOK { 27 + return nil 28 + } 29 + log.Printf("unexpected response from Nixie API: %s", resp.Status) 30 + } 31 + 32 + log.Printf("retrying in %s", backoff) 33 + // Sleep with random jitter to avoid thundering herd 34 + time.Sleep(backoff + time.Duration(rand.Int63n(int64(backoff/2)))) 35 + 36 + backoff *= 2 37 + if backoff > maxBackoff { 38 + backoff = maxBackoff 39 + } 40 + } 41 + }
+1 -1
cmd/nixie-agent/kernel.go
··· 11 11 API string 12 12 } 13 13 14 - func GetNixieParams() (*NixieParams, error) { 14 + func getNixieParams() (*NixieParams, error) { 15 15 data, err := os.ReadFile("/proc/cmdline") 16 16 if err != nil { 17 17 return nil, err
+10 -2
cmd/nixie-agent/main.go
··· 1 1 package main 2 2 3 3 import ( 4 - "fmt" 4 + "log" 5 5 ) 6 6 7 7 func main() { 8 - fmt.Println("TODO nixie agent") 8 + params, err := getNixieParams() 9 + if err != nil { 10 + log.Fatalf("failed to get Nixie params: %v", err) 11 + } 12 + log.Printf("nixie-agent params: %+v", params) 13 + 14 + if err = ping(params.API); err != nil { 15 + log.Fatalf("failed to ping Nixie API server: %v", err) 16 + } 9 17 }
+34
cmd/nixie/flags.go
··· 1 + package main 2 + 3 + import ( 4 + "errors" 5 + "flag" 6 + ) 7 + 8 + type Flags struct { 9 + Address string 10 + Debug bool 11 + Flake string 12 + HostsFile string 13 + Installer string 14 + SSHKey string 15 + } 16 + 17 + func parseFlags() (*Flags, error) { 18 + var flags Flags 19 + 20 + flag.BoolVar(&flags.Debug, "debug", false, "Enable debug logging") 21 + flag.StringVar(&flags.Address, "address", "", "Address to listen on (default auto)") 22 + flag.StringVar(&flags.Flake, "flake", "", "NixOS configuration flake (for example, .)") 23 + flag.StringVar(&flags.HostsFile, "hosts", "", "Path to hosts.json file (for example, ./hosts.json)") 24 + flag.StringVar(&flags.Installer, "installer", "", "NixOS installer flake output (for example, .#nixosConfigurations.installer)") 25 + flag.StringVar(&flags.SSHKey, "ssh-key", "", "Path to the SSH private key (for example, ~/.ssh/id_ed25519)") 26 + 27 + flag.Parse() 28 + 29 + if flags.HostsFile == "" || flags.Flake == "" || flags.Installer == "" { 30 + return nil, errors.New("missing flags, usage: nixie --hosts <hosts.json> --flake <flake> --installer <installer-output>") 31 + } 32 + 33 + return &flags, nil 34 + }
+12 -33
cmd/nixie/main.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "flag" 6 5 "os" 7 6 "os/signal" 8 7 "syscall" ··· 15 14 "github.com/charmbracelet/log" 16 15 ) 17 16 18 - type Flags struct { 19 - Address string 20 - Debug bool 21 - Flake string 22 - HostsFile string 23 - Installer string 24 - SSHKey string 25 - } 26 - 27 - func parseFlags() Flags { 28 - var flags Flags 29 - 30 - flag.BoolVar(&flags.Debug, "debug", false, "Enable debug logging") 31 - flag.StringVar(&flags.Address, "address", "", "Address to listen on (default auto)") 32 - flag.StringVar(&flags.Flake, "flake", "", "NixOS configuration flake (for example, .)") 33 - flag.StringVar(&flags.HostsFile, "hosts", "", "Path to hosts.json file (for example, ./hosts.json)") 34 - flag.StringVar(&flags.Installer, "installer", "", "NixOS installer flake output (for example, .#nixosConfigurations.installer)") 35 - flag.StringVar(&flags.SSHKey, "ssh-key", "", "Path to the SSH private key (for example, ~/.ssh/id_ed25519)") 36 - 37 - flag.Parse() 38 - 39 - if flags.HostsFile == "" || flags.Flake == "" || flags.Installer == "" { 40 - log.Fatal("usage: nixie --hosts <hosts.json> --flake <flake> --installer <installer-output>") 17 + func main() { 18 + flags, err := parseFlags() 19 + if err != nil { 20 + log.Fatal("failed to parse command-line flags", "error", err) 41 21 } 42 22 43 - return flags 44 - } 45 - 46 - func setupLogging(debug bool) { 47 - if debug { 23 + if flags.Debug { 48 24 log.SetLevel(log.DebugLevel) 49 25 } 50 - } 51 26 52 - func main() { 53 - flags := parseFlags() 54 - setupLogging(flags.Debug) 55 27 log.Debug("parsed command line flags", "flags", flags) 56 28 57 29 hostsConfig, err := hosts.LoadHostsConfig(flags.HostsFile) ··· 98 70 } 99 71 }() 100 72 log.Info("PXE server started", "address", address) 73 + 74 + go func() { 75 + if err := serve.StartAPIServer(ctx, flags.Debug); err != nil { 76 + log.Fatal("failed to start API server", "error", err) 77 + } 78 + }() 79 + log.Info("API server started", "address", address) 101 80 102 81 sigCh := make(chan os.Signal, 1) 103 82 signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
+1
internal/network/ip.go
··· 9 9 10 10 // Adapted from Pixiecore's DHCP logic (Apache License 2.0) 11 11 // https://github.com/danderson/netboot/blob/main/pixiecore/dhcp.go#L247-L278 12 + // We need this because the nixie-agent also needs to know the API server address to send its API requests 12 13 func DetectServerAddress() (string, error) { 13 14 addresses, err := net.InterfaceAddrs() 14 15 if err != nil {
+30
internal/serve/api.go
··· 1 + package serve 2 + 3 + import ( 4 + "context" 5 + "io" 6 + "net" 7 + "net/http" 8 + 9 + "github.com/charmbracelet/log" 10 + ) 11 + 12 + func extractClientIP(r *http.Request) string { 13 + ip, _, err := net.SplitHostPort(r.RemoteAddr) 14 + if err != nil { 15 + return r.RemoteAddr 16 + } 17 + 18 + return ip 19 + } 20 + 21 + func handlePing(w http.ResponseWriter, r *http.Request) { 22 + log.Info("received ping from agent", "ip", extractClientIP(r)) 23 + io.WriteString(w, "pong") 24 + } 25 + 26 + func StartAPIServer(ctx context.Context, debug bool) error { 27 + http.HandleFunc("GET /ping", handlePing) 28 + 29 + return http.ListenAndServe(":5000", nil) 30 + }