tunnel

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: May 24, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

README

pkg/tunnel

The tunnel package exposes local Kubernetes services to the public internet without touching DNS, firewalls, or cloud infrastructure. It is used by ork doctor deploy --expose and ork tunnel expose to make an Orkestra app or the Control Center reachable from outside the cluster.

What lives here

File Role
provider.go Provider interface, auto-selection logic, shared helpers (orkestaBinDir, binaryInPath)
cloudflare.go Cloudflare Quick Tunnels (trycloudflare.com) — no account required
ngrok.go ngrok provider — requires a free-tier account and auth token
expose.go High-level Expose() function, port resolution, startPortForward, reuse logic
state.go Multi-tunnel daemon state persisted to ~/.orkestra/tunnel-state.json

Quick reference

// Expose an app (auto-detects port, falls back to kubectl port-forward)
url, err := tunnel.Expose(ctx, tunnel.ExposeOptions{
    Name:        "my-app",
    ServiceName: "my-app-orkestra-svc",
    Namespace:   "my-app-orkestra-ns",
    ServicePort: "8080",
})

// Expose the Control Center (always port-forwards, never uses ingress port 80)
url, err := tunnel.Expose(ctx, tunnel.ExposeOptions{
    Name:        "controlcenter",
    ServiceName: doctor.OrkestraControlCenter,   // "orkestra-cc"
    Namespace:   doctor.OrkestraNamespace,
    ServicePort: doctor.OrkestraControlCenterPort, // "8081"
    PortForward: true,
})

Developer documentation

Full documentation is in docs/.

I want to… Go to
Understand the Provider interface and how providers are selected 01 — Providers
Understand how tunnel state is persisted and reused 02 — State
Understand the Expose() function and its options 03 — Expose
Understand how the local port is detected or forwarded 04 — Port Detection
Understand how tunnels survive parent process exit 05 — Process Survival

Documentation

Overview

pkg/tunnel/cloudflare.go

Cloudflared provider — anonymous tunnel, no account required. Uses trycloudflare.com (Cloudflare Quick Tunnels).

pkg/tunnel/expose.go

High-level Expose function used by `ork doctor deploy --expose` and `ork tunnel expose`. Detects the local port, starts the tunnel, and persists state under a named key.

pkg/tunnel/ngrok.go

ngrok provider — free-tier tunnels, requires an account and auth token.

pkg/tunnel/provider.go

Provider interface and auto-selection logic for tunnel providers. Cloudflared is the default (no account required); ngrok is the fallback.

pkg/tunnel/state.go

Multi-tunnel daemon state — persisted to ~/.orkestra/doctor/tunnel/tunnel-state.json as a map[name]State so multiple tunnels (per app, controlcenter) coexist.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Expose

func Expose(ctx context.Context, opts ExposeOptions) (string, error)

Expose starts or reuses a named tunnel and returns the public URL. State is persisted to ~/.orkestra/tunnel-state.json under opts.Name.

func LoadAllStates

func LoadAllStates() (map[string]State, error)

LoadAllStates reads all persisted tunnel entries.

func RemoveAllStates

func RemoveAllStates() error

RemoveAllStates deletes the tunnel state file.

func RemoveTunnelState

func RemoveTunnelState(name string) error

RemoveTunnelState removes one entry from the state map. The file is deleted entirely when the map becomes empty.

func SaveTunnelState

func SaveTunnelState(name string, s State) error

SaveTunnelState writes or updates the named tunnel entry.

Types

type CloudflaredProvider

type CloudflaredProvider struct{}

CloudflaredProvider implements Provider for Cloudflare Quick Tunnels.

func (*CloudflaredProvider) Authenticate

func (c *CloudflaredProvider) Authenticate(_ context.Context, _ string) error

Authenticate is a no-op — trycloudflare.com requires no account or token.

func (*CloudflaredProvider) Available

func (c *CloudflaredProvider) Available() bool

func (*CloudflaredProvider) Install

func (c *CloudflaredProvider) Install(ctx context.Context) error

func (*CloudflaredProvider) Name

func (c *CloudflaredProvider) Name() string

func (*CloudflaredProvider) Start

func (c *CloudflaredProvider) Start(ctx context.Context, localPort int) (string, int, error)

Start launches cloudflared as a detached background process and returns the public URL once it appears in cloudflared's log output.

cloudflared's stderr is redirected to a log FILE (not a pipe). This is critical: OS pipes cause SIGPIPE in cloudflared when our process exits and the read end is closed. Files don't have this problem — cloudflared keeps writing to the file after we exit.

func (*CloudflaredProvider) Stop

func (c *CloudflaredProvider) Stop(pid int) error

type ExposeOptions

type ExposeOptions struct {
	// Name identifies the tunnel in the state map (e.g. "my-app", "controlcenter").
	// Falls back to ServiceName when empty.
	Name string

	// Provider is the explicit provider name ("cloudflared" or "ngrok").
	// Empty means auto-select.
	Provider string

	// Token is passed to Provider.Authenticate when non-empty.
	Token string

	// LocalPort overrides auto-detection when non-zero.
	LocalPort int

	// ServiceName is the Kubernetes service name to port-forward to as a
	// fallback (e.g. "my-app-orkestra-svc", "orkestra-cc").
	ServiceName string

	// Namespace is the target namespace for the service port-forward.
	Namespace string

	// ServicePort is the container port on the service (default "80").
	ServicePort string

	// PortForward is kept for compatibility but has no effect when ServiceName
	// and Namespace are set — port-forward is always preferred in that case.
	// Left here so callers that explicitly set it true do not break.
	PortForward bool
}

ExposeOptions controls how the tunnel is started.

type NgrokProvider

type NgrokProvider struct{}

NgrokProvider implements Provider for ngrok.

func (*NgrokProvider) Authenticate

func (n *NgrokProvider) Authenticate(_ context.Context, token string) error

Authenticate configures the ngrok auth token.

func (*NgrokProvider) Available

func (n *NgrokProvider) Available() bool

func (*NgrokProvider) Install

func (n *NgrokProvider) Install(_ context.Context) error

Install prints guidance — ngrok is not auto-downloaded because it requires an account and the installer is platform-specific.

func (*NgrokProvider) Name

func (n *NgrokProvider) Name() string

func (*NgrokProvider) Start

func (n *NgrokProvider) Start(ctx context.Context, localPort int) (string, int, error)

Start launches ngrok as a detached background process.

ngrok's stdout is redirected to a log file (not a pipe) so that our process exiting doesn't deliver SIGPIPE to ngrok while it's still initializing.

func (*NgrokProvider) Stop

func (n *NgrokProvider) Stop(pid int) error

type Provider

type Provider interface {
	// Name returns the provider identifier used in state files and output.
	Name() string

	// Available reports whether the provider binary is in PATH or ~/.orkestra/bin.
	Available() bool

	// Install downloads and installs the provider binary when not available.
	Install(ctx context.Context) error

	// Authenticate stores credentials (no-op for cloudflared).
	Authenticate(ctx context.Context, token string) error

	// Start launches a background tunnel to localPort and returns the public URL
	// once it is available. The daemon outlives the process that called Start.
	Start(ctx context.Context, localPort int) (url string, pid int, err error)

	// Stop terminates the daemon identified by pid.
	Stop(pid int) error
}

Provider is implemented by every supported tunnel backend.

func Select

func Select() (Provider, error)

Select returns the first available provider in priority order. cloudflared comes first (no account). ngrok is second.

func SelectByName

func SelectByName(name string) (Provider, error)

SelectByName returns a provider by explicit name.

type State

type State struct {
	Name           string    `json:"name"`
	Provider       string    `json:"provider"`
	PID            int       `json:"pid"`
	PortForwardPID int       `json:"portForwardPid,omitempty"`
	URL            string    `json:"url"`
	LocalPort      int       `json:"localPort"`
	StartedAt      time.Time `json:"startedAt"`
}

State records one running tunnel daemon.

func LoadTunnelState

func LoadTunnelState(name string) (*State, error)

LoadTunnelState returns the state for name, or nil when not found.

func (*State) IsAlive

func (s *State) IsAlive() bool

IsAlive reports whether the tunnel process is still running.

func (*State) Stop

func (s *State) Stop() error

Stop kills the tunnel daemon (and any port-forward) and removes the state entry.

func (*State) Uptime

func (s *State) Uptime() string

Uptime returns a human-readable duration since the tunnel started.

Jump to

Keyboard shortcuts

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