Documentation
¶
Overview ¶
Package connection defines the transport abstraction the typed Hyper-V client (internal/hyperv) uses to ship PowerShell scripts to a Windows host and read their results back. Three backends are planned: local (exec pwsh.exe directly), ssh (golang.org/x/crypto/ssh), and winrm (github.com/masterzen/winrm). local and ssh are implemented; winrm ships in M3.
Design notes are in docs/PLAN.md §4. Spike findings shaped the contract: docs/spikes/02-json-contract.md (script body via -EncodedCommand, stdin for data, stderr stripped of CLIXML), and docs/spikes/04-ps-startup-latency.md (per-call cost dominated by PS startup, not transport).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUnreachable means the transport could not reach the host (DNS, // TCP, TLS handshake, auth failure). Resource code typically retries. ErrUnreachable = errors.New("transport unreachable") // ErrTimeout means the call exceeded its context deadline. Distinct // from ErrUnreachable so callers can decide whether to retry vs. // surface as `timeouts.Diagnostics`. ErrTimeout = errors.New("transport timeout") // ErrUnsupportedBackend is returned by the backend selector when the // requested backend identifier is not yet implemented. Removed once // SSH/WinRM backends ship. ErrUnsupportedBackend = errors.New("backend not implemented") )
Functions ¶
This section is empty.
Types ¶
type Connection ¶
type Connection interface {
Runner
// Open establishes any persistent state the backend needs (an SSH
// client, a pooled HTTP transport, etc.). Local is stateless and
// returns nil.
Open(ctx context.Context) error
// Close releases the backend's persistent state. Idempotent. Local
// is a no-op.
Close() error
// Healthcheck returns nil if the backend can reach the host and run
// a trivial command. Used at provider Configure time to fail fast on
// misconfiguration.
Healthcheck(ctx context.Context) error
// Backend returns the lowercase identifier of the implementation —
// "local" | "ssh" | "winrm". Used for tflog field decoration; the
// schema's `backend` attribute is the user-facing form.
Backend() string
}
Connection is the abstract transport. Each provider instance holds a single Connection per (backend, host, user) tuple. Resources never reach for a new one; they share the configured Connection via the typed client in internal/hyperv.
func NewLocal ¶
func NewLocal(opts LocalOptions) (Connection, error)
NewLocal returns a Connection backed by a local pwsh / powershell.exe process per call. Opens nothing — the local backend is stateless.
func NewSSH ¶
func NewSSH(opts SSHOptions) (Connection, error)
NewSSH builds a Connection backed by ssh.Client. It resolves authentication methods and the known_hosts callback up-front so misconfiguration surfaces before Open dials. The client itself is established lazily by Open.
func NewWinRM ¶ added in v0.2.0
func NewWinRM(opts WinRMOptions) (Connection, error)
NewWinRM builds a Connection backed by masterzen/winrm. Validates required fields and applies defaults so callers get a fully-resolved backend; the actual HTTP client is constructed lazily by Open so a unit test that only exercises NewWinRM doesn't pay the construction cost.
Auth methods supported: ntlm (default), basic, kerberos.
Kerberos uses jcmturner/gokrb5 under the hood (pure-Go MIT Kerberos, no GSSAPI library on the runner) and supports two credential modes: password (inline AS-REQ) or ccache (re-use an existing TGT). The caller picks the mode by setting Password or KrbCCachePath; setting both, or neither, is rejected.
type LocalOptions ¶
type LocalOptions struct {
// PwshPath, if non-empty, is used as-is. Set via the provider's
// `local.pwsh_path` attribute or the HYPERV_PWSH_PATH env var.
PwshPath string
}
LocalOptions configures the local backend. Empty values mean "discover from PATH": prefer pwsh (faster cold start), fall back to powershell.exe.
type Result ¶
Result is what every script invocation returns. The transport layer captures four pieces of information; the typed Hyper-V client maps them into typed Go errors per docs/PLAN.md §5.
`Stderr` has CLIXML progress noise stripped (see docs/spikes/02-json-contract.md finding 6) before reaching this struct. Real PS errors arrive as a JSON envelope on stderr per the Write-HypervError contract.
`error` from RunScript is reserved for transport failures (connection refused, auth failed, ctx canceled). PS-level failures come back via `ExitCode != 0` plus the structured envelope on `Stderr`.
type Runner ¶
type Runner interface {
RunScript(ctx context.Context, script string, stdinJSON []byte) (Result, error)
// StreamFile copies the bytes at localPath on the runner to
// remotePath on the host. Both paths are absolute. Backends create
// (or truncate) the destination file and write streamed bytes —
// nothing is buffered in memory beyond the backend's transport
// chunk size.
//
// No SHA-256 verification is performed at this layer: the typed
// client (internal/hyperv) computes the local hash before calling
// StreamFile and verifies the remote hash via the existing
// image_file/get.ps1 path after the destination rename. Keeping
// the primitive a pure bytes-copy lets each backend pick its own
// optimal transport without dragging hashing into the protocol.
//
// Cancellation through ctx interrupts the stream; partial files at
// the remote path are the caller's problem to clean up. Resources
// that need atomicity stage to a `.part` sibling and rename, the
// same pattern image_file's url-mode uses.
StreamFile(ctx context.Context, localPath, remotePath string) error
}
Runner is the narrowest useful interface — just "run a script, get a result." Connection composes Runner with lifecycle methods (Open/Close/ Healthcheck) that backends with persistent state need.
The split exists so unit tests can implement just Runner via the fake in internal/testutil, without faking lifecycle calls that don't matter for the typed-client tests.
Calling convention (verified by spike #2):
`script` is the full PowerShell body. The caller has already concatenated common/preamble.ps1 to the top. Backends transmit it as UTF-16LE base64 via -EncodedCommand. **Never** via stdin or as a command-line argument — multi-line scripts get mis-parsed otherwise.
`stdinJSON` is structured input. Empty for scripts that don't need input. Backends pipe these bytes to the PS process's stdin. Scripts read with `$input_json = $Input | ConvertFrom-Json -Compress`.
The returned `Result` carries the four useful streams. The error return is reserved for transport-level failures (connection refused, ctx canceled). Non-zero `ExitCode` is the application-level signal.
type SSHOptions ¶
type SSHOptions struct {
Host string // required
Port int // default 22
Username string // required
// Auth methods, in priority order:
//
// 1. PrivateKey (raw key contents -- wins if both PrivateKey and PrivateKeyPath are set)
// 2. PrivateKeyPath (path read at Open time)
// 3. Password (fallback only -- key auth is preferred)
//
// Passphrase decrypts the key when set.
PrivateKey []byte
PrivateKeyPath string
Passphrase []byte
Password string
// KnownHostsPath is the file used for host key verification. Default:
// ~/.ssh/known_hosts. Empty path falls back to the default. A missing
// file is a hard error -- silently disabling host-key checking would be
// a security regression.
KnownHostsPath string
// Timeout is the dial timeout. Default 30s.
Timeout time.Duration
// CommandTimeout bounds an individual RunScript call. Default
// 5m. A wedged remote cmdlet surfaces as ErrTimeout instead of
// blocking the whole apply. Set to 0 to disable.
CommandTimeout time.Duration
// KeepaliveInterval is how often the backend sends an SSH
// keepalive request while the persistent client is open.
// Default 30s. Prevents NAT/firewall mid-apply drops. Set to
// 0 to disable.
KeepaliveInterval time.Duration
// PwshPath is the binary the remote shell invokes per call. Default:
// "powershell.exe" -- universally available on Windows. Set to "pwsh"
// or "pwsh.exe" to prefer PS 7+ if installed.
PwshPath string
}
SSHOptions configures the SSH backend. The provider's Configure pass resolves env vars + provider attributes into this struct (see internal/provider/backend_select.go).
type WinRMOptions ¶ added in v0.2.0
type WinRMOptions struct {
Host string // required
Port int // default 5986 (HTTPS) or 5985 (HTTP)
Username string // required
Password string // required for ntlm/basic
UseHTTPS bool // default true; flip to false only for diagnosing TLS-only failures
Insecure bool // skip TLS certificate verification (default false)
Auth string // "ntlm" | "basic" | "kerberos"; default "ntlm"
CACert string // path to a CA bundle PEM; empty = system roots
// Kerberos auth fields. Only meaningful when Auth=="kerberos"; ignored
// otherwise. The provider-config layer is responsible for catching the
// "kerberos fields set without auth=kerberos" misconfig at plan time;
// this struct just transports the values.
//
// KrbRealm is required (NewWinRM rejects empty when Auth=="kerberos").
// KrbSpn defaults to "HTTP/<Host>" when empty.
// KrbConfigPath defaults to first-existing of $KRB5_CONFIG,
// ~/.config/krb5.conf, /etc/krb5.conf when empty.
// KrbCCachePath, when set, switches from password-mode (inline AS-REQ)
// to ccache-mode (re-use a pre-existing TGT). When set, Password is
// ignored.
KrbRealm string
KrbSpn string
KrbConfigPath string
KrbCCachePath string
// Timeout bounds an individual WinRM operation (dial, auth, request).
// Default 30s. Distinct from CommandTimeout, which bounds the remote
// PowerShell call.
Timeout time.Duration
// CommandTimeout bounds a single RunScript call. Default 5m. A wedged
// remote cmdlet surfaces as ErrTimeout instead of blocking the whole
// apply. Set to 0 to disable.
CommandTimeout time.Duration
// PwshPath is the binary the remote shell invokes per call. Default:
// "powershell.exe" -- universally available on Windows. Set to "pwsh"
// or "pwsh.exe" to prefer PS 7+ if installed.
PwshPath string
}
WinRMOptions configures the WinRM backend. The provider's Configure pass resolves env vars + provider attributes into this struct (see internal/provider/backend_select.go).