diff --git a/cmd/kat-agent/main.go b/cmd/kat-agent/main.go index 4993211..2a99992 100644 --- a/cmd/kat-agent/main.go +++ b/cmd/kat-agent/main.go @@ -46,6 +46,14 @@ and obtains the necessary credentials to participate in the cluster.`, Run: runJoin, } + verifyCmd = &cobra.Command{ + Use: "verify", + Short: "Verifies node registration in etcd.", + Long: `Connects to etcd and verifies that a node is properly registered. +This is useful for testing and debugging.`, + Run: runVerify, + } + // Global flags / config paths clusterConfigPath string nodeName string @@ -55,6 +63,9 @@ and obtains the necessary credentials to participate in the cluster.`, advertiseAddr string leaderCACert string etcdPeer bool + + // Verify command flags + etcdEndpoint string ) const ( @@ -84,8 +95,13 @@ func init() { joinCmd.MarkFlagRequired("leader-api") joinCmd.MarkFlagRequired("advertise-address") + // Verify command flags + verifyCmd.Flags().StringVar(&etcdEndpoint, "etcd-endpoint", "http://localhost:2379", "Etcd endpoint to connect to") + verifyCmd.Flags().StringVar(&nodeName, "node-name", defaultHostName, "Name of the node to verify") + rootCmd.AddCommand(initCmd) rootCmd.AddCommand(joinCmd) + rootCmd.AddCommand(verifyCmd) } func runInit(cmd *cobra.Command, args []string) { @@ -322,8 +338,44 @@ func runJoin(cmd *cobra.Command, args []string) { } log.Printf("Successfully joined cluster. Node is ready.") - // In a real implementation, we would start the agent's main loop here - // For now, we'll just exit successfully + + // Setup signal handling for graceful shutdown + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + // Stay up in an idle loop until interrupted + log.Printf("Node %s is now running. Press Ctrl+C to exit.", nodeName) + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + log.Println("Received shutdown signal. Exiting...") + return + case <-ticker.C: + log.Printf("Node %s is still running...", nodeName) + } + } +} + +func runVerify(cmd *cobra.Command, args []string) { + log.Printf("Verifying node registration for node: %s", nodeName) + log.Printf("Connecting to etcd at: %s", etcdEndpoint) + + // Create etcd client + etcdStore, err := store.NewEtcdStore([]string{etcdEndpoint}, nil) + if err != nil { + log.Fatalf("Failed to create etcd store client: %v", err) + } + defer etcdStore.Close() + + // Verify node registration + if err := cli.VerifyNodeRegistration(etcdStore, nodeName); err != nil { + log.Fatalf("Failed to verify node registration: %v", err) + } + + log.Printf("Node registration verification complete.") } func main() { diff --git a/internal/cli/verify_registration.go b/internal/cli/verify_registration.go new file mode 100644 index 0000000..7a77ca8 --- /dev/null +++ b/internal/cli/verify_registration.go @@ -0,0 +1,53 @@ +package cli + +import ( + "context" + "encoding/json" + "fmt" + "log" + "time" + + "git.dws.rip/dubey/kat/internal/store" +) + +// NodeRegistration represents the data stored in etcd for a node +type NodeRegistration struct { + UID string `json:"uid"` + AdvertiseAddr string `json:"advertiseAddr"` + WireguardPubKey string `json:"wireguardPubKey"` + JoinTimestamp int64 `json:"joinTimestamp"` +} + +// VerifyNodeRegistration checks if a node is registered in etcd +func VerifyNodeRegistration(etcdStore store.StateStore, nodeName string) error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Construct the key for the node registration + nodeRegKey := fmt.Sprintf("/kat/nodes/registration/%s", nodeName) + + // Get the node registration from etcd + kv, err := etcdStore.Get(ctx, nodeRegKey) + if err != nil { + return fmt.Errorf("failed to get node registration from etcd: %w", err) + } + + // Parse the node registration + var nodeReg NodeRegistration + if err := json.Unmarshal(kv.Value, &nodeReg); err != nil { + return fmt.Errorf("failed to parse node registration: %w", err) + } + + // Print the node registration details + log.Printf("Node Registration Details:") + log.Printf(" Node Name: %s", nodeName) + log.Printf(" Node UID: %s", nodeReg.UID) + log.Printf(" Advertise Address: %s", nodeReg.AdvertiseAddr) + log.Printf(" WireGuard Public Key: %s", nodeReg.WireguardPubKey) + + // Convert timestamp to human-readable format + joinTime := time.Unix(nodeReg.JoinTimestamp, 0) + log.Printf(" Join Timestamp: %s (%d)", joinTime.Format(time.RFC3339), nodeReg.JoinTimestamp) + + return nil +}