feat(P5): proxmox native ingress mode — LXC + podman traefik (REQ-175)
Add --ingress-mode flag (native default, floating-ip) + --floating-ip, --gateway, --mac, --net-prefix flags to 'orca node join'. Native mode (default): provision an unprivileged LXC with --features nesting=1,keyctl=1,fuse=1 (research Topic 3), install podman inside it, run orca-traefik container. nft on PVE host DNATs to the LXC bridge IP (DNATTarget parameterization, C-55: discover LXC IP before first nft apply, no downtime window). LXC provisioning: deterministic VMID 200, hostname orca-traefik, --onboot 1, 2GB RAM. Idempotent (C-53: command -v podman check). podman-restart.service enabled inside LXC (research Topic 6). step-ca root CA pushed into LXC via pct exec heredoc. traefik static config rendered + written into LXC. nft ruleset rendered with DNATTarget=LXC-IP + applied on PVE host. Migration 0009_ingress_mode.sql (C-59: NOT 0007 — already taken by certs_serial_unique). ALTER TABLE nodes ADD COLUMN ingress_mode. IngressMode field added to model.Node + set on proxmox node record. ---ci--- project: orca phase: 5 milestone: v0.14 status: execute ---/ci---
This commit is contained in:
+41
-23
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"git.cloudinit.dev/coreci/orca/internal/certpaths"
|
||||
"git.cloudinit.dev/coreci/orca/internal/linux"
|
||||
"git.cloudinit.dev/coreci/orca/internal/engine"
|
||||
"git.cloudinit.dev/coreci/orca/internal/linux"
|
||||
"git.cloudinit.dev/coreci/orca/internal/model"
|
||||
"git.cloudinit.dev/coreci/orca/internal/proxmox"
|
||||
"git.cloudinit.dev/coreci/orca/internal/security"
|
||||
@@ -46,20 +46,25 @@ func nodeRegistry() (*engine.NodeRegistry, func() error, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
joinName string
|
||||
joinAddr string
|
||||
joinCAFinger string
|
||||
joinType string
|
||||
joinHost string
|
||||
joinSSHUser string
|
||||
joinSSHKey string
|
||||
joinSSHPort int
|
||||
joinHostKeyFP string
|
||||
joinName string
|
||||
joinAddr string
|
||||
joinCAFinger string
|
||||
joinType string
|
||||
joinHost string
|
||||
joinSSHUser string
|
||||
joinSSHKey string
|
||||
joinSSHPort int
|
||||
joinHostKeyFP string
|
||||
joinLXCTemplate string
|
||||
proxmoxUser string
|
||||
proxmoxRole string
|
||||
leaveID string
|
||||
nodeWatch bool
|
||||
proxmoxUser string
|
||||
proxmoxRole string
|
||||
ingressMode string
|
||||
floatingIP string
|
||||
gateway string
|
||||
macAddr string
|
||||
netPrefix int
|
||||
leaveID string
|
||||
nodeWatch bool
|
||||
)
|
||||
|
||||
var nodeCmd = &cobra.Command{
|
||||
@@ -178,6 +183,12 @@ func joinProxmox(cmd *cobra.Command) error {
|
||||
ctx, cancel := context.WithTimeout(cmd.Context(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Default ingress mode to "native" if not specified (R-024).
|
||||
effectiveIngressMode := ingressMode
|
||||
if effectiveIngressMode == "" {
|
||||
effectiveIngressMode = "native"
|
||||
}
|
||||
|
||||
result, err := proxmox.BootstrapProxmox(ctx, proxmox.Options{
|
||||
Host: joinHost,
|
||||
SSHUser: joinSSHUser,
|
||||
@@ -188,6 +199,7 @@ func joinProxmox(cmd *cobra.Command) error {
|
||||
HostKeyFingerprint: joinHostKeyFP,
|
||||
Logger: newLogger(),
|
||||
LXCTemplate: joinLXCTemplate,
|
||||
IngressMode: effectiveIngressMode,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("proxmox bootstrap: %w", err)
|
||||
@@ -204,14 +216,15 @@ func joinProxmox(cmd *cobra.Command) error {
|
||||
defer regCancel()
|
||||
|
||||
node := &model.Node{
|
||||
ID: uuid.NewString(),
|
||||
Name: result.NodeName,
|
||||
Address: result.NodeAddress,
|
||||
State: model.NodeStateReady,
|
||||
JoinedAt: time.Now().UTC(),
|
||||
LastSeen: time.Now().UTC(),
|
||||
Kind: string(model.NodeKindProxmox),
|
||||
OS: "pve",
|
||||
ID: uuid.NewString(),
|
||||
Name: result.NodeName,
|
||||
Address: result.NodeAddress,
|
||||
State: model.NodeStateReady,
|
||||
JoinedAt: time.Now().UTC(),
|
||||
LastSeen: time.Now().UTC(),
|
||||
Kind: string(model.NodeKindProxmox),
|
||||
OS: "pve",
|
||||
IngressMode: effectiveIngressMode,
|
||||
}
|
||||
if err := registry.Join(regCtx, node); err != nil {
|
||||
return fmt.Errorf("register proxmox node: %w", err)
|
||||
@@ -252,7 +265,7 @@ func joinLinux(cmd *cobra.Command) error {
|
||||
SSHPort: joinSSHPort,
|
||||
HostKeyFingerprint: joinHostKeyFP,
|
||||
Logger: newLogger(),
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("linux bootstrap: %w", err)
|
||||
}
|
||||
@@ -514,6 +527,11 @@ func init() {
|
||||
nodeJoinCmd.Flags().StringVar(&proxmoxRole, "proxmox-role", "OrcaOperator", "PVE custom role to create (config-overridable)")
|
||||
nodeJoinCmd.Flags().StringVar(&joinHostKeyFP, "host-key-fingerprint", "", "SSH host key SHA256:base64 fingerprint (pre-pin; supersedes TOFU for --type proxmox or --type linux)")
|
||||
nodeJoinCmd.Flags().StringVar(&joinLXCTemplate, "lxc-template", "ubuntu-24.04", "LXC template for Proxmox (default ubuntu-24.04; alternatives: alpine-3.20, debian-12)")
|
||||
nodeJoinCmd.Flags().StringVar(&ingressMode, "ingress-mode", "", "proxmox ingress mode: native (default, traefik in LXC) or floating-ip (ingress LXC owns floating IP)")
|
||||
nodeJoinCmd.Flags().StringVar(&floatingIP, "floating-ip", "", "floating public IP for the ingress LXC (required for --ingress-mode floating-ip)")
|
||||
nodeJoinCmd.Flags().StringVar(&gateway, "gateway", "", "gateway for the ingress LXC (required for --ingress-mode floating-ip)")
|
||||
nodeJoinCmd.Flags().StringVar(&macAddr, "mac", "", "MAC address for the ingress LXC net0 (required for --ingress-mode floating-ip in --json mode; auto-generated in interactive mode)")
|
||||
nodeJoinCmd.Flags().IntVar(&netPrefix, "net-prefix", 24, "network prefix (CIDR) for the ingress LXC IP (default 24; valid 8-32)")
|
||||
nodeLeaveCmd.Flags().StringVar(&leaveID, "id", "", "node id")
|
||||
nodeListCmd.Flags().BoolVar(&nodeWatch, "watch", false, "stream nodes until Ctrl-C (table refresh or --json per-event)")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user