mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/khuedoan/homelab.git
synced 2026-09-20 08:03:58 +08:00
refactor(metal): more robust installer callback with colored logs
This commit is contained in:
9
Makefile
9
Makefile
@@ -12,13 +12,8 @@ configure:
|
||||
git status
|
||||
|
||||
metal:
|
||||
make -C metal
|
||||
# TODO new install flow
|
||||
# sudo -v
|
||||
# sudo nix run .#nixosPxeServer
|
||||
# echo 'waiting until installer is booted' && sleep 30
|
||||
# ssh root@192.168.1.2 cat /etc/os-release | grep VARIANT_ID=installer && nixos-anywhere --flake .#metal1 --target-host root@192.168.1.2
|
||||
# nixos-rebuild --flake . --target-host root@metal1.local switch
|
||||
@echo 'Running PXE server as root for privileged ports'
|
||||
sudo nix run .#homelabInstall
|
||||
|
||||
system:
|
||||
make -C system
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
pname = "homelab-install";
|
||||
version = "0.1.0";
|
||||
src = ./tools;
|
||||
vendorHash = "sha256-cYS7Jel5ARf+FioPxP5/Wu+LHQLX1U94kfMQ+K26Rnk=";
|
||||
# TODO better way to build this shit
|
||||
vendorHash = "sha256-rTJt3UWRUyhRDx1Sdno0fFBYMb4RPtzB7Z7sg45ZJ8o=";
|
||||
|
||||
postInstall = ''
|
||||
wrapProgram $out/bin/homelab-install \
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
{ pkgs, lib, config, modulesPath, ... }:
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
modulesPath,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [
|
||||
@@ -23,16 +29,26 @@
|
||||
];
|
||||
};
|
||||
|
||||
systemd.services."phone-home" =
|
||||
systemd.services."installer-callback" =
|
||||
let
|
||||
reportScript = pkgs.writeShellScript "phone-home.sh" ''
|
||||
reportScript = pkgs.writeShellScript "installer-callback.sh" ''
|
||||
set -euo pipefail
|
||||
|
||||
sleep 30
|
||||
# Wait for default route and IP address to be ready
|
||||
echo "Waiting for network to be ready..."
|
||||
while true; do
|
||||
if ${pkgs.iproute2}/bin/ip route show default > /dev/null 2>&1; then
|
||||
iface=$(${pkgs.iproute2}/bin/ip route show default | ${pkgs.coreutils}/bin/cut -d ' ' -f 5)
|
||||
ip=$(${pkgs.iproute2}/bin/ip route show default | ${pkgs.coreutils}/bin/cut -d ' ' -f 9)
|
||||
if [ -n "$iface" ] && [ -n "$ip" ] && [ "$ip" != "0.0.0.0" ]; then
|
||||
echo "Network is ready: interface=$iface ip=$ip"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
iface=$(${pkgs.iproute2}/bin/ip route show default | ${pkgs.coreutils}/bin/cut -d ' ' -f 5)
|
||||
mac=$(cat /sys/class/net/$iface/address)
|
||||
ip=$(${pkgs.iproute2}/bin/ip route show default | ${pkgs.coreutils}/bin/cut -d ' ' -f 9)
|
||||
|
||||
${pkgs.curl}/bin/curl -sf -X POST "http://192.168.1.15:5000/report" \
|
||||
--data-urlencode "mac=$mac" \
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -12,11 +11,13 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/log"
|
||||
"go.universe.tf/netboot/out/ipxe"
|
||||
"go.universe.tf/netboot/pixiecore"
|
||||
)
|
||||
|
||||
// Hardcoded map of MAC → flake target
|
||||
// TODO do not harcode map of MAC → flake target
|
||||
var hostMap = map[string]string{
|
||||
"bc:24:11:d0:28:34": ".#metal2",
|
||||
"bc:24:11:0d:2f:20": ".#metal1",
|
||||
@@ -32,19 +33,38 @@ var (
|
||||
shutdownCh = make(chan struct{})
|
||||
)
|
||||
|
||||
func getSubsystemStyle(subsystem string) lipgloss.Style {
|
||||
switch subsystem {
|
||||
case "DHCP":
|
||||
return lipgloss.NewStyle().Foreground(lipgloss.Color("1"))
|
||||
case "TFTP":
|
||||
return lipgloss.NewStyle().Foreground(lipgloss.Color("2"))
|
||||
case "HTTP":
|
||||
return lipgloss.NewStyle().Foreground(lipgloss.Color("3"))
|
||||
case "CALLBACK":
|
||||
return lipgloss.NewStyle().Foreground(lipgloss.Color("4"))
|
||||
case "INSTALL":
|
||||
return lipgloss.NewStyle().Foreground(lipgloss.Color("5"))
|
||||
case "PXE":
|
||||
return lipgloss.NewStyle().Foreground(lipgloss.Color("6"))
|
||||
default:
|
||||
return lipgloss.NewStyle().Foreground(lipgloss.Color("7"))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
kernelPath = flag.String("kernel", "", "Path to kernel bzImage")
|
||||
initrdPath = flag.String("initrd", "", "Path to initrd")
|
||||
initPath = flag.String("init", "", "Path to init in the system")
|
||||
address = flag.String("address", "0.0.0.0", "Address to listen on (default: 0.0.0.0 for all interfaces)")
|
||||
debug = flag.Bool("debug", false, "Enable debug mode")
|
||||
)
|
||||
|
||||
type phoneHome struct {
|
||||
type callback struct {
|
||||
MAC string
|
||||
IP string
|
||||
}
|
||||
|
||||
// Pixiecore boot handler
|
||||
type bootHandler struct {
|
||||
kernelPath string
|
||||
initrdPath string
|
||||
@@ -54,7 +74,6 @@ type bootHandler struct {
|
||||
func (b bootHandler) BootSpec(m pixiecore.Machine) (*pixiecore.Spec, error) {
|
||||
mac := m.MAC.String()
|
||||
if _, ok := hostMap[mac]; ok {
|
||||
// Serve the NixOS installer kernel/initrd
|
||||
return &pixiecore.Spec{
|
||||
Kernel: pixiecore.ID("kernel"),
|
||||
Initrd: []pixiecore.ID{"initrd"},
|
||||
@@ -100,34 +119,19 @@ func main() {
|
||||
log.Fatal("Usage: homelab-install -kernel <path> -initrd <path> -init <path>")
|
||||
}
|
||||
|
||||
fmt.Println("Servers to install:", hostMap)
|
||||
if *debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
// Load pixiecore's embedded iPXE binaries
|
||||
ipxeMap := make(map[pixiecore.Firmware][]byte)
|
||||
|
||||
// Load BIOS/PXE binary
|
||||
biosData, err := ipxe.Asset("third_party/ipxe/src/bin/undionly.kpxe")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load embedded BIOS iPXE binary: %v", err)
|
||||
}
|
||||
ipxeMap[pixiecore.FirmwareX86PC] = biosData
|
||||
|
||||
// Load EFI64 binary
|
||||
efi64Data, err := ipxe.Asset("third_party/ipxe/src/bin-x86_64-efi/ipxe.efi")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load embedded EFI64 iPXE binary: %v", err)
|
||||
log.Fatal("Failed to load embedded EFI64 iPXE binary", "error", err)
|
||||
}
|
||||
ipxeMap[pixiecore.FirmwareEFI64] = efi64Data
|
||||
ipxeMap[pixiecore.FirmwareEFIBC] = efi64Data
|
||||
|
||||
// Load EFI32 binary
|
||||
efi32Data, err := ipxe.Asset("third_party/ipxe/src/bin-i386-efi/ipxe.efi")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load embedded EFI32 iPXE binary: %v", err)
|
||||
}
|
||||
ipxeMap[pixiecore.FirmwareEFI32] = efi32Data
|
||||
|
||||
// Start Pixiecore PXE server
|
||||
pxeServer = &pixiecore.Server{
|
||||
Booter: bootHandler{
|
||||
kernelPath: *kernelPath,
|
||||
@@ -139,32 +143,44 @@ func main() {
|
||||
HTTPPort: 8080,
|
||||
Ipxe: ipxeMap,
|
||||
Debug: func(subsystem, msg string) {
|
||||
log.Printf("[DEBUG] %s: %s", subsystem, msg)
|
||||
coloredSubsystem := getSubsystemStyle(subsystem).Render(fmt.Sprintf("[%s]", subsystem))
|
||||
log.Debugf("%s %s", coloredSubsystem, msg)
|
||||
},
|
||||
Log: func(subsystem, msg string) {
|
||||
log.Printf("[INFO] %s: %s", subsystem, msg)
|
||||
coloredSubsystem := getSubsystemStyle(subsystem).Render(fmt.Sprintf("[%s]", subsystem))
|
||||
log.Infof("%s %s", coloredSubsystem, msg)
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := pxeServer.Serve(); err != nil {
|
||||
log.Fatalf("PXE server error: %v", err)
|
||||
log.Fatal("PXE server error", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Start HTTP phone-home server
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/report", reportHandler)
|
||||
server = &http.Server{Addr: ":5000", Handler: mux}
|
||||
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("HTTP server error: %v", err)
|
||||
log.Fatal("HTTP server error", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Println("PXE server listening on port 8080")
|
||||
fmt.Println("Phone-home server listening on port 5000")
|
||||
// These ports can technically be set for testing, but the
|
||||
// protocols burned in firmware on the client side hardcode these,
|
||||
// so if you change them in production, nothing will work.
|
||||
log.Info(
|
||||
"Running on ports",
|
||||
"DHCP", 67,
|
||||
"TFTP", 69,
|
||||
"PXE", 4001,
|
||||
"HTTP", 8080,
|
||||
"CALLBACK", 5000,
|
||||
)
|
||||
|
||||
log.Info("Servers to install", "hosts", hostMap)
|
||||
|
||||
// Handle SIGINT for cleanup
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
@@ -196,44 +212,50 @@ func reportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Got phone-home from %s at %s\n", mac, ip)
|
||||
coloredSubsystem := getSubsystemStyle("CALLBACK").Render("[CALLBACK]")
|
||||
log.Infof("%s Got callback mac=%s ip=%s", coloredSubsystem, mac, ip)
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if flake, ok := hostMap[mac]; ok {
|
||||
if !installed[mac] && !inFlight[mac] {
|
||||
fmt.Printf("→ Starting nixos-anywhere for %s on %s\n", flake, ip)
|
||||
coloredSubsystem := getSubsystemStyle("INSTALL").Render("[INSTALL]")
|
||||
log.Infof("%s Starting installation flake=%s ip=%s", coloredSubsystem, flake, ip)
|
||||
inFlight[mac] = true
|
||||
|
||||
go monitorInstall(mac, ip, flake)
|
||||
} else {
|
||||
fmt.Printf("%s already installed or in progress, ignoring\n", mac)
|
||||
log.Warn("Host already installed or in progress", "mac", mac)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Unknown MAC, ignoring")
|
||||
log.Warn("Unknown MAC, ignoring", "mac", mac)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func monitorInstall(mac, ip, flake string) {
|
||||
fmt.Printf("Running nixos-anywhere install of %s on %s\n", flake, ip)
|
||||
coloredSubsystem := getSubsystemStyle("INSTALL").Render("[INSTALL]")
|
||||
log.Infof("%s Running installation flake=%s ip=%s", coloredSubsystem, flake, ip)
|
||||
|
||||
// Run nixos-anywhere with password authentication
|
||||
cmd := exec.Command("nixos-anywhere",
|
||||
"--env-password",
|
||||
"--no-substitute-on-destination",
|
||||
"--flake", flake,
|
||||
fmt.Sprintf("root@%s", ip),
|
||||
)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
// Set SSHPASS environment variable with the hardcoded installer password
|
||||
|
||||
cmd.Env = append(os.Environ(), "SSHPASS=nixos-installer")
|
||||
|
||||
if *debug {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Printf("Error installing %s: %v\n", mac, err)
|
||||
coloredSubsystem := getSubsystemStyle("INSTALL").Render("[INSTALL]")
|
||||
log.Errorf("%s Error installing host mac=%s error=%v", coloredSubsystem, mac, err)
|
||||
mu.Lock()
|
||||
delete(inFlight, mac)
|
||||
mu.Unlock()
|
||||
@@ -245,8 +267,9 @@ func monitorInstall(mac, ip, flake string) {
|
||||
delete(inFlight, mac)
|
||||
installed[mac] = true
|
||||
|
||||
fmt.Printf("Successfully installed %s\n", mac)
|
||||
fmt.Printf("Installed servers: %+v\n", installed)
|
||||
coloredSubsystem = getSubsystemStyle("INSTALL").Render("[INSTALL]")
|
||||
log.Infof("%s Successfully installed host mac=%s", coloredSubsystem, mac)
|
||||
log.Infof("%s Installation status: installed=%v", coloredSubsystem, installed)
|
||||
|
||||
pending := make(map[string]struct{})
|
||||
for k := range hostMap {
|
||||
@@ -254,23 +277,23 @@ func monitorInstall(mac, ip, flake string) {
|
||||
pending[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
fmt.Printf("Pending servers: %+v\n", pending)
|
||||
log.Infof("%s Pending servers: %v", coloredSubsystem, pending)
|
||||
|
||||
if len(pending) == 0 {
|
||||
fmt.Println("All servers installed, shutting down.")
|
||||
log.Infof("%s All servers installed, shutting down", coloredSubsystem)
|
||||
close(shutdownCh)
|
||||
}
|
||||
}
|
||||
|
||||
func stopServer() {
|
||||
fmt.Println("Stopping servers...")
|
||||
log.Info("Stopping servers...")
|
||||
if server != nil {
|
||||
server.Close()
|
||||
fmt.Println("HTTP server shutdown complete.")
|
||||
log.Info("HTTP server shutdown complete")
|
||||
}
|
||||
if pxeServer != nil {
|
||||
pxeServer.Shutdown()
|
||||
fmt.Println("PXE server stopped.")
|
||||
log.Info("PXE server stopped")
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
17
tools/go.mod
17
tools/go.mod
@@ -5,7 +5,22 @@ go 1.24.6
|
||||
require go.universe.tf/netboot v0.0.0-20240531232330-2ed7bd30206a
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||
github.com/charmbracelet/log v0.4.2 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/crypto v0.6.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
)
|
||||
|
||||
34
tools/go.sum
34
tools/go.sum
@@ -397,6 +397,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
|
||||
github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
@@ -406,6 +408,18 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
|
||||
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -453,6 +467,8 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -604,6 +620,8 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@@ -616,6 +634,10 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
@@ -631,6 +653,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@@ -661,6 +685,9 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
@@ -696,6 +723,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -747,6 +776,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -957,6 +988,9 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
|
||||
Reference in New Issue
Block a user