bridge

package
v0.32.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package bridge implements the hybrid edge-to-local cloud routing primitives for Idea 46 (devx bridge). It orchestrates kubectl port-forward subprocesses, manages session state, and generates bridge environment variables for devx shell.

Design: All cluster interactions use the kubectl subprocess pattern, consistent with devx's established pattern of wrapping external CLIs (cloudflared, podman, etc.).

Future: dns.go is reserved for Idea 46.1.5 (DNS Proxy) — a lightweight DNS proxy using Go's net package that resolves *.svc.cluster.local to forwarded local ports without requiring elevated permissions.

Index

Constants

View Source
const (
	// AgentImageDefault is the default agent container image.
	// Override via --agent-image flag or bridge.agent_image in devx.yaml.
	AgentImageDefault = "ghcr.io/VitruvianSoftware/devx-bridge-agent:v0.1.0"

	// AgentControlPort is the Yamux control port inside the agent pod.
	AgentControlPort = 4200

	// AgentHealthPort is the health check port inside the agent pod.
	AgentHealthPort = 4201

	// AgentDefaultDeadline is the default activeDeadlineSeconds for agent Jobs (4 hours).
	AgentDefaultDeadline = 14400
)

Variables

This section is empty.

Functions

func CheckInterceptConflict

func CheckInterceptConflict(kubeconfig, kubeCtx, namespace, service string) error

CheckInterceptConflict checks whether the Service already has an active intercept session.

func ClearSession

func ClearSession() error

ClearSession removes the session file and env file from disk.

func EnvPath

func EnvPath() (string, error)

EnvPath returns the path to the bridge env file (~/.devx/bridge.env).

func GenerateEnvFile

func GenerateEnvFile(entries []SessionEntry) error

GenerateEnvFile creates ~/.devx/bridge.env with BRIDGE_<SERVICE>_URL, BRIDGE_<SERVICE>_HOST, and BRIDGE_<SERVICE>_PORT variables for each active port-forward. This file is consumed by devx shell.

func GetServiceSelector

func GetServiceSelector(kubeconfig, kubeCtx, namespace, service string) (map[string]string, error)

GetServiceSelector returns the current selector of a Service.

func IsActive

func IsActive() bool

IsActive checks whether a bridge session exists and has entries or intercepts.

func ListContexts

func ListContexts(kubeconfig string) ([]string, error)

ListContexts returns available kube contexts from the kubeconfig.

func ListServices

func ListServices(kubeconfig, context, namespace string) ([]string, error)

ListServices returns the names of services in the given namespace for TUI selection.

func LoadEnvVars

func LoadEnvVars() (map[string]string, error)

LoadEnvVars reads the bridge env file and returns the key-value pairs. Returns an empty map if the file does not exist.

func RemoveAgent

func RemoveAgent(kubeconfig, kubeCtx, namespace, sessionID string) error

RemoveAgent deletes the agent Job, ServiceAccount, Role, and RoleBinding.

func ResolveKubeconfig

func ResolveKubeconfig(explicit string) (string, error)

ResolveKubeconfig returns the absolute path to the kubeconfig file. It checks, in order: explicit path > KUBECONFIG env var > ~/.kube/config.

func RestoreServiceSelector

func RestoreServiceSelector(kubeconfig, kubeCtx string, state *ServiceState) error

RestoreServiceSelector restores the original Service selector and removes the session annotation.

func SaveSession

func SaveSession(session *Session) error

SaveSession persists the session state to disk.

func SessionPath

func SessionPath() (string, error)

SessionPath returns the path to the bridge session file (~/.devx/bridge.json).

func ValidateContext

func ValidateContext(kubeconfig, context string) error

ValidateContext checks that the specified context exists and the cluster is reachable.

func ValidateInterceptable

func ValidateInterceptable(info *ServiceInfo) error

ValidateInterceptable checks that a Service is safe to intercept.

func ValidateKubectl

func ValidateKubectl() (string, error)

ValidateKubectl checks that kubectl is on the PATH and returns its version.

func ValidateService

func ValidateService(kubeconfig, context, namespace, service string) error

ValidateService checks that a service exists in the given namespace.

Types

type AgentConfig

type AgentConfig struct {
	Kubeconfig       string
	Context          string
	Namespace        string
	TargetService    string
	InterceptPort    int               // The specific port being intercepted
	ServicePorts     []ServicePortSpec // ALL ports on the target Service (for dynamic Pod spec)
	OriginalSelector map[string]string // For self-healing: passed to agent via env var
	AgentImage       string
	SessionID        string
	Deadline         int // activeDeadlineSeconds (default: AgentDefaultDeadline)
}

AgentConfig defines the parameters for deploying a bridge agent.

type AgentInfo

type AgentInfo struct {
	PodName     string
	PodIP       string
	ControlPort int
	HealthPort  int
	SessionID   string
}

AgentInfo contains runtime information about a deployed agent.

func DeployAgent

func DeployAgent(cfg AgentConfig) (*AgentInfo, error)

DeployAgent creates the agent Job, ServiceAccount, Role, and RoleBinding in the cluster and waits for the agent to be ready.

type InterceptEntry

type InterceptEntry struct {
	Service          string            `json:"service"`
	Namespace        string            `json:"namespace"`
	TargetPort       int               `json:"target_port"`
	LocalPort        int               `json:"local_port"`
	Mode             string            `json:"mode"` // "steal" or "mirror"
	AgentPod         string            `json:"agent_pod"`
	SessionID        string            `json:"session_id"`
	OriginalSelector map[string]string `json:"original_selector"` // for restore on teardown
	StartedAt        time.Time         `json:"started_at"`
	Origin           string            `json:"origin,omitempty"` // "standalone" or "dag" — prevents cross-teardown (Idea 46.3)
}

InterceptEntry represents an active traffic intercept (Idea 46.2).

type PortForward

type PortForward struct {
	Service    string // K8s service name
	Namespace  string
	RemotePort int
	LocalPort  int
	// contains filtered or unexported fields
}

PortForward manages a single kubectl port-forward subprocess.

func NewPortForward

func NewPortForward(kubeconfig, kubeCtx, namespace, service string, remotePort, localPort int) *PortForward

NewPortForward creates a new port-forward manager.

func (*PortForward) LastError

func (pf *PortForward) LastError() error

LastError returns the last error that caused a retry or failure.

func (*PortForward) LocalAddr

func (pf *PortForward) LocalAddr() string

LocalAddr returns the local address string (e.g. "127.0.0.1:9501").

func (*PortForward) ResolveLocalPort

func (pf *PortForward) ResolveLocalPort() (string, error)

ResolveLocalPort acquires a free local port if LocalPort is 0, or resolves port collisions if the desired port is in use.

func (*PortForward) Start

func (pf *PortForward) Start(ctx context.Context) error

Start launches the kubectl port-forward subprocess. It blocks until the context is cancelled or the port-forward fails after all retries.

func (*PortForward) State

func (pf *PortForward) State() PortForwardState

State returns the current state of this port-forward.

func (*PortForward) StateChannel

func (pf *PortForward) StateChannel() <-chan PortForwardState

StateChannel returns a channel that receives state transitions.

func (*PortForward) Stop

func (pf *PortForward) Stop()

Stop gracefully terminates the port-forward subprocess.

type PortForwardState

type PortForwardState int

PortForwardState represents the health of a single port-forward.

const (
	StateStarting PortForwardState = iota
	StateHealthy
	StateFailed
	StateStopped
)

func (PortForwardState) String

func (s PortForwardState) String() string

type ServiceInfo

type ServiceInfo struct {
	Name           string            `json:"name"`
	Namespace      string            `json:"namespace"`
	Type           string            `json:"type"` // ClusterIP, NodePort, etc.
	Selector       map[string]string `json:"selector"`
	Ports          []ServicePortSpec `json:"ports"`
	HasMeshSidecar bool              `json:"has_mesh_sidecar"`
}

ServiceInfo captures the full spec of a target Service for intercept planning.

func InspectService

func InspectService(kubeconfig, kubeCtx, namespace, service string) (*ServiceInfo, error)

InspectService retrieves the full Service spec and validates it for intercept.

type ServicePortSpec

type ServicePortSpec struct {
	Name       string `json:"name"`        // e.g., "http-api", "metrics" (may be empty)
	Port       int    `json:"port"`        // Service port number
	TargetPort string `json:"target_port"` // Container port number or name (e.g., "8080" or "http-api")
	Protocol   string `json:"protocol"`    // Must be "TCP" for 46.2
}

ServicePortSpec captures a single port from the target Service for dynamic agent generation.

type ServiceState

type ServiceState struct {
	Name             string            `json:"name"`
	Namespace        string            `json:"namespace"`
	OriginalSelector map[string]string `json:"original_selector"`
}

ServiceState captures the original state of a Service before patching (for restore).

func PatchServiceSelector

func PatchServiceSelector(kubeconfig, kubeCtx, namespace, service string, newSelector map[string]string, sessionID string) (*ServiceState, error)

PatchServiceSelector replaces the target Service's selector with the agent pod's labels. Also sets the annotation devx-bridge-session=<sessionID> for conflict detection.

type Session

type Session struct {
	Kubeconfig string           `json:"kubeconfig"`
	Context    string           `json:"context"`
	Entries    []SessionEntry   `json:"entries"`
	Intercepts []InterceptEntry `json:"intercepts,omitempty"` // Idea 46.2
	StartedAt  time.Time        `json:"started_at"`
}

Session represents the full bridge session state persisted to ~/.devx/bridge.json.

func LoadSession

func LoadSession() (*Session, error)

LoadSession reads the session state from disk. Returns nil, nil if the file does not exist.

type SessionEntry

type SessionEntry struct {
	Service    string    `json:"service"`
	Namespace  string    `json:"namespace"`
	RemotePort int       `json:"remote_port"`
	LocalPort  int       `json:"local_port"`
	State      string    `json:"state"`
	PID        int       `json:"pid,omitempty"`
	StartedAt  time.Time `json:"started_at"`
	Origin     string    `json:"origin,omitempty"` // "standalone" or "dag" — prevents cross-teardown (Idea 46.3)
}

SessionEntry represents a single active port-forward in the session file.

type Tunnel

type Tunnel struct {
	// contains filtered or unexported fields
}

Tunnel manages the kubectl port-forward subprocess and Yamux client session.

func NewTunnel

func NewTunnel(cfg TunnelConfig) *Tunnel

NewTunnel creates a new Tunnel with the given config.

func (*Tunnel) Done

func (t *Tunnel) Done() <-chan struct{}

Done returns a channel that is closed when the tunnel stops accepting streams.

func (*Tunnel) Healthy

func (t *Tunnel) Healthy() bool

Healthy returns true if the Yamux session is still alive.

func (*Tunnel) Start

func (t *Tunnel) Start(ctx context.Context) error

Start establishes the kubectl port-forward and Yamux client session. It then starts proxying inbound Yamux streams to localhost:<LocalPort>. Returns after the first Yamux handshake succeeds. Blocks on Accept in a goroutine.

func (*Tunnel) Stop

func (t *Tunnel) Stop()

Stop gracefully closes the Yamux session and kills the port-forward subprocess.

type TunnelConfig

type TunnelConfig struct {
	Kubeconfig  string
	Context     string
	Namespace   string
	AgentPod    string
	ControlPort int // Agent's Yamux control port (4200)
	LocalPort   int // Developer's local app port
}

TunnelConfig defines parameters for establishing the Yamux tunnel.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL