Documentation
¶
Overview ¶
Package hyperv provides a providersdk.Driver for Microsoft Hyper-V. The agent must run on the Hyper-V host with Administrator privileges; no remote connection config is needed.
Index ¶
- Constants
- Variables
- func Registration() providersdk.Registration
- type CapacityError
- type Config
- type CreateConfig
- type Driver
- func (d *Driver) Allocate(ctx context.Context, id string) (map[string]any, error)
- func (d *Driver) Availability(ctx context.Context) (*providersdk.ResourceAvailability, error)
- func (d *Driver) Create(ctx context.Context, cfg any) (*providersdk.Resource, error)
- func (d *Driver) Delete(ctx context.Context, id string) (err error)
- func (d *Driver) List(ctx context.Context) ([]providersdk.ResourceStatus, error)
- func (d *Driver) NetworkRanges(ctx context.Context, switchName string) ([]providersdk.NetworkRange, error)
- func (d *Driver) PersonalizeGuest(ctx context.Context, id string) (*providersdk.GuestPersonalizationResult, error)
- func (d *Driver) Read(ctx context.Context, id string) (*providersdk.ResourceStatus, error)
- func (d *Driver) SetGuestBootstrapResolver(resolver providersdk.GuestBootstrapResolver)
- func (d *Driver) Type() providersdk.Type
- func (d *Driver) Update(ctx context.Context, id string, op providersdk.Operation) (*providersdk.Result, error)
- func (d *Driver) UpdateStream(ctx context.Context, id string, op providersdk.Operation, ...) (*providersdk.Result, error)
- type ExecOp
- type NetworkConfig
Constants ¶
const DefaultHostReserveMB int64 = 512
const ProviderType = "hyperv"
ProviderType is the registry key for Hyper-V providers.
Variables ¶
var ErrVMBusy = errors.New("hyperv: vm did not reach a terminal power state in time")
ErrVMBusy indicates a VM is stuck transitioning between power states and did not settle within the wait window. Callers should treat this as a signal to back off and retry later rather than forcing removal, which can leave a stale vmwp.exe worker and destabilize the host's Virtual Machine Management service (see #118).
Functions ¶
func Registration ¶
func Registration() providersdk.Registration
Registration returns the providersdk.Registration for the Hyper-V provider.
Types ¶
type CapacityError ¶ added in v0.1.37
type CapacityError = providersdk.CapacityError
CapacityError is providersdk.CapacityError under this package's existing name — see #185's design spec for why the type moved.
type Config ¶
type Config struct {
// HostReserveMB is the memory headroom kept available for the host OS and
// other processes. nil uses the safe 512 MB default; a pointer preserves
// the explicit zero value, which disables the reserve.
HostReserveMB *int64 `json:"host_reserve_mb,omitempty" yaml:"host_reserve_mb,omitempty"`
// DataDir is the directory where this Hyper-V provider's restart-safe
// state is persisted — currently just the range-based IP allocation
// ledger (see NetworkConfig.Range, ADR-0012). Relative paths resolve
// against the boxy config file's own directory when one is known (see
// ResolveRelativePaths); against the process's working directory
// otherwise. Empty defaults to ".boxy-agent/hyperv" via the same
// resolution.
DataDir string `json:"data_dir,omitempty" yaml:"data_dir,omitempty"`
}
Config holds provider-level settings. These settings apply to the entire Hyper-V host, not to an individual pool or VM.
func (*Config) ResolveRelativePaths ¶ added in v0.1.45
ResolveRelativePaths implements providersdk.RelativePathResolver.
Unlike devfactory's ResolveRelativePaths — which leaves an empty DataDir alone, since devfactory falls back to a throwaway temp directory instead — an empty DataDir here is defaulted to ".boxy-agent/hyperv" *before* anchoring against baseDir. A lost ledger directly reproduces the address collision #222 exists to fix, so an installed agent service (which persists ProviderConfigsBaseDir) and an interactive `boxy agent serve` sharing the same --config must resolve to the same ledger file even though their process working directories differ. See ADR-0012.
type CreateConfig ¶
type CreateConfig struct {
// TemplateVHD is the path to the parent VHD/VHDX used for differencing disks.
// Required.
TemplateVHD string `json:"template_vhd" yaml:"template_vhd"`
// VHDDir is the directory where differencing VHDs are created.
// Defaults to the directory containing TemplateVHD.
VHDDir string `json:"vhd_dir" yaml:"vhd_dir"`
// Generation is the Hyper-V VM generation (1 or 2). Default: 2.
Generation int `json:"generation" yaml:"generation"`
// CPUCount is the number of virtual processors. Default: 2.
CPUCount int `json:"cpu_count" yaml:"cpu_count"`
// MemoryMB is startup memory in megabytes. Default: 2048.
MemoryMB int `json:"memory_mb" yaml:"memory_mb"`
// Switch is the name of the virtual switch to connect to. Optional.
Switch string `json:"switch" yaml:"switch"`
// Network holds optional static IP configuration to apply inside the guest
// during personalization. When omitted the guest relies on DHCP or a
// pre-configured address. Use this on Windows Server hosts where Hyper-V
// does not issue DHCP leases automatically.
Network *NetworkConfig `json:"network,omitempty" yaml:"network,omitempty"`
// GuestOS is the guest operating system: "windows" or "linux". Default: "windows".
// Windows guests use PowerShell Direct (psdirect); Linux guests use SSH.
GuestOS string `json:"guest_os" yaml:"guest_os"`
// GuestUser is the guest OS username for exec operations.
// Windows guests: used for PSRP authentication. Default: "Administrator".
// Linux guests: used as the SSH username. Default: "admin".
GuestUser string `json:"guest_user" yaml:"guest_user"`
// GuestPasswordRef is an opaque lookup handle for the guest OS password.
// Windows guests: PSRP password. Linux guests: SSH password.
//
// Supported built-in forms:
// - env:NAME
GuestPasswordRef string `json:"guest_password_ref" yaml:"guest_password_ref"`
// GuestPassword is deprecated and no longer used for bootstrap guest access.
// Use GuestPasswordRef instead so the raw secret does not have to be persisted.
GuestPassword string `json:"guest_password" yaml:"guest_password"`
}
CreateConfig holds pool-level settings for creating a Hyper-V VM.
type Driver ¶
type Driver struct {
// contains filtered or unexported fields
}
Driver implements providersdk.Driver for local Hyper-V. VM lifecycle (New-VM, Start-VM, etc.) uses powershell.exe on the host. Guest exec uses PowerShell Direct via go-psrp (Windows) or SSH (Linux).
func (*Driver) Availability ¶ added in v0.1.37
func (d *Driver) Availability(ctx context.Context) (*providersdk.ResourceAvailability, error)
Availability implements providersdk.AvailabilityReporter.
func (*Driver) List ¶ added in v0.1.39
func (d *Driver) List(ctx context.Context) ([]providersdk.ResourceStatus, error)
List satisfies providersdk.ResourceLister, enumerating every boxy-*-named VM this driver's host currently has — including ones the store has no record of, e.g. left behind by a crash between New-VM succeeding and Create's failure branch running (see #174). Prefix-filtered inside the PowerShell query itself, not client-side, so a host running unrelated VMs alongside Boxy's never returns them to a caller that doesn't expect it.
func (*Driver) NetworkRanges ¶ added in v0.1.46
func (d *Driver) NetworkRanges(ctx context.Context, switchName string) ([]providersdk.NetworkRange, error)
NetworkRanges implements providersdk.NetworkRangeReporter. It discovers switchName's real IPv4 range(s) from the host's own vEthernet adapter for that switch (Get-NetIPAddress) — not from Get-VMSwitch itself, which has no IP address of its own. Hyper-V creates a "vEthernet (<switch name>)" host network adapter for an Internal or Default switch, carrying the address that is that range's gateway; an External switch's host adapter instead carries the physical NIC's own LAN address, which callers should not treat as a per-VM range even though it's still reported here (see validateNetworkRange's containment check, which handles that the same way as any other non-matching discovered range).
Get-NetNat has no field naming which switch it backs, so its InternalIPInterfaceAddressPrefix values are cross-checked in Go against each discovered address's own network prefix instead — best-effort: a mismatch or Get-NetNat failure only affects NetworkRange.NATBacked, never CIDR/Gateway. See ADR-0013.
A switch with no discoverable IPv4 address (e.g. a Private switch, which has no host vNIC at all) is not an error: it returns (nil, nil). A non-nil error means the query itself could not be completed.
func (*Driver) PersonalizeGuest ¶
func (d *Driver) PersonalizeGuest(ctx context.Context, id string) (*providersdk.GuestPersonalizationResult, error)
func (*Driver) Read ¶
func (d *Driver) Read(ctx context.Context, id string) (*providersdk.ResourceStatus, error)
func (*Driver) SetGuestBootstrapResolver ¶ added in v0.1.40
func (d *Driver) SetGuestBootstrapResolver(resolver providersdk.GuestBootstrapResolver)
SetGuestBootstrapResolver injects the control-plane lookup used for new VMs. The callback is evaluated at personalization time so remote-agent reconnects always use their current gRPC connection and the server's current pool credential.
func (*Driver) Type ¶
func (d *Driver) Type() providersdk.Type
func (*Driver) Update ¶
func (d *Driver) Update(ctx context.Context, id string, op providersdk.Operation) (*providersdk.Result, error)
func (*Driver) UpdateStream ¶ added in v0.1.34
func (d *Driver) UpdateStream(ctx context.Context, id string, op providersdk.Operation, sink eventstream.Sink) (*providersdk.Result, error)
UpdateStream forwards a guest's native streaming capability. Hyper-V providers that cannot stream return an explicit capability error rather than buffering unary output and presenting it as live data.
type ExecOp ¶
type ExecOp = providersdk.ExecOperation
ExecOp is retained as a provider-specific spelling of the shared command operation for compatibility with existing callers.
type NetworkConfig ¶ added in v0.1.43
type NetworkConfig struct {
// StaticIP is a single fixed IPv4 address (e.g. "203.0.113.50", an RFC
// 5737 documentation address) assigned to every VM this pool creates.
// Mutually exclusive with Range. Only safe for a pool that never has
// more than one VM alive at once — a pool with min_ready > 1, or that
// preheats multiple VMs before allocation, collides on the wire. Use
// Range for those. See ADR-0012.
StaticIP string `json:"static_ip,omitempty" yaml:"static_ip,omitempty"`
// Range is an IPv4 CIDR (e.g. "203.0.113.0/24") that a per-agent,
// restart-safe ledger allocates distinct addresses from at allocation
// time — one per resource, released back to the range on delete.
// Mutually exclusive with StaticIP. See ADR-0012.
Range string `json:"range,omitempty" yaml:"range,omitempty"`
// PrefixLength is the subnet prefix length (e.g. 24 for /24) applied
// alongside StaticIP. Default: 24. Not used in Range mode — the prefix
// there comes from Range's own CIDR bits, which is authoritative.
PrefixLength int `json:"prefix_length" yaml:"prefix_length"`
// DefaultGateway is the IPv4 default gateway (e.g. "203.0.113.1").
// Optional in both modes; in Range mode it is also excluded from
// allocation so no VM is ever assigned the gateway's own address.
DefaultGateway string `json:"default_gateway,omitempty" yaml:"default_gateway,omitempty"`
// DNSServers is a list of DNS server IPv4 addresses to assign. Optional.
DNSServers []string `json:"dns_servers,omitempty" yaml:"dns_servers,omitempty"`
}
NetworkConfig describes how to assign an IPv4 address to a guest VM during personalization. Exactly one of StaticIP or Range must be set.