image_file

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: May 21, 2026 License: MPL-2.0 Imports: 27 Imported by: 0

Documentation

Overview

Package image_file implements the hyperv_image_file resource. Wraps the image_file/{get,new,remove}.ps1 contract via the typed hyperv.Client.

Index

Constants

This section is empty.

Variables

View Source
var URLAttrTypes = map[string]attr.Type{
	"url":         types.StringType,
	"checksum":    types.StringType,
	"compression": types.StringType,
}

URLAttrTypes mirrors the SingleNestedAttribute "url" shape in schema.go. Used by types.Object construction (ObjectValueFrom) and decode (Object.As).

Functions

func New

func New() resource.Resource

New is the framework factory.

func URLObjectFromConfig added in v0.3.0

func URLObjectFromConfig(ctx context.Context, u *URLConfig) (types.Object, diag.Diagnostics)

URLObjectFromConfig builds a types.Object from a *URLConfig. A nil pointer becomes a null Object (matching the "URL not set" semantics of the previous *URLConfig field). Used by modelFromImageFile and any test code that constructs a Model with a known URL block.

Types

type Model

type Model struct {
	ID                  pathtype.Path `tfsdk:"id"`
	DestinationPath     pathtype.Path `tfsdk:"destination_path"`
	URL                 types.Object  `tfsdk:"url"`
	LocalPath           pathtype.Path `tfsdk:"local_path"`
	ContentBase64       types.String  `tfsdk:"content_base64"`
	ReplaceWhileMounted types.Bool    `tfsdk:"replace_while_mounted"`
	Sha256              types.String  `tfsdk:"sha256"`
	SizeBytes           types.Int64   `tfsdk:"size_bytes"`
	KeepOnDestroy       types.Bool    `tfsdk:"keep_on_destroy"`
	ForceDestroy        types.Bool    `tfsdk:"force_destroy"`
}

Model is the tfsdk-bound struct backing the resource state. Field tags align with schema.go attribute names; conversion to/from the typed hyperv.ImageFile DTO lives in resource.go.

Four source modes, discriminated by which of URL / LocalPath / ContentBase64 is set:

  • URL non-nil => url mode (HttpClient fetch)
  • URL nil, LocalPath non-null => local_path mode (runner streams bytes from a runner-side file via Connection.StreamFile)
  • URL nil, LocalPath null, ContentBase64 non-null => literal_bytes mode (runner decodes base64, writes to a tmpfile, streams via the same wire path local_path mode uses)
  • all three nil/null => host_path mode (verify only)

All three placement-mode discriminators (URL, LocalPath, ContentBase64) are mutually exclusive; the ConfigValidator on the resource rejects configs that set more than one. All three carry RequiresReplace at the schema layer, so any mode switch destroys and recreates. The Delete path keys on the same discriminators to gate the host-side remove (host_path mode never removes, since the user attested the file already existed).

ReplaceWhileMounted is the opt-in escape hatch for re-streaming over a destination that's currently mounted as a DVD on a running VM. Only honored in local_path and literal_bytes modes -- the modes with a re-stream Update path; url-mode forces replacement on any change, and host_path-mode never writes the destination.

ForceDestroy is the opt-in escape hatch for destroying a file that is currently mounted as a DVD on a running VM. When true, Delete asks remove.ps1 to detach the holding slot(s) via Set-VMDvdDrive -Path $null before retrying the delete. Honored in url, local_path, and literal_bytes modes -- those are the modes that actually run the host-side delete. Host_path-mode skips the delete entirely so the flag is a no-op there.

DestinationPath uses the pathtype.Path custom type so users can write either `C:/foo` or `C:\foo` without the framework rejecting the apply with "Provider produced inconsistent result after apply" when Hyper-V returns the canonical backslash form. ID mirrors destination_path and is also Path so the same semantic-equality covers the Computed mirror's refresh path. LocalPath uses Path for the same slash-folding reason -- users on macOS / Linux runners typically write forward slashes for the local path even when the destination is a Windows-form path.

func (*Model) URLConfig added in v0.3.0

func (m *Model) URLConfig(ctx context.Context) (*URLConfig, diag.Diagnostics)

URLConfig returns the decoded user-supplied URL config, or nil if the model's URL is null or unknown. Callers that need to distinguish null from unknown should inspect m.URL directly via IsNull / IsUnknown.

type Resource

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

Resource implements hyperv_image_file.

func (*Resource) ConfigValidators added in v0.2.0

func (r *Resource) ConfigValidators(_ context.Context) []resource.ConfigValidator

ConfigValidators rejects mode-attribute combinations that the wire contract can't honor, surfacing a clear attribute-anchored diagnostic at plan time instead of an opaque cmdlet error at apply time.

func (*Resource) Configure

Configure stashes the typed Hyper-V client built by the provider's Configure pass. Skips when ProviderData is nil (validate-time invocation before the provider has resolved its config).

func (*Resource) Create

Create dispatches on source mode (url, local_path, or host_path) and writes the post-create read shape back to state.

url-mode: the provider fetches via HttpClient and verifies the checksum. ErrChecksumMismatch is surfaced on path.Root("url").AtName("checksum") so the diagnostic anchors to the offending attribute, not the resource.

local_path-mode: the provider streams the runner-side file through the active connection backend, then asks new.ps1 to verify the streamed bytes' SHA against the runner-computed value and atomic-rename. A host-side hash mismatch surfaces ErrChecksumMismatch on local_path (transport corruption rather than user-supplied checksum drift).

host_path-mode: the provider verifies the file already exists at destination_path. ErrNotFound is anchored to destination_path.

func (*Resource) Delete

Delete runs remove.ps1 for url-mode and local_path-mode resources -- both modes mean the provider put the file on the host, so removing it on destroy is the symmetric operation. host_path-mode (URL nil AND LocalPath null) leaves the file alone: the user attested it already existed, so removing on destroy would surprise them.

`force_destroy=true` forwards into remove.ps1's detach-then-retry branch: when the initial Remove-Item hits a sharing violation whose holders are Hyper-V DVDs, the script detaches each slot and retries. Default (false) preserves the locked-file diagnostic so cross-state drift surfaces explicitly rather than silently mutating VM state.

ErrNotFound from RemoveImageFile is treated as success (the file is already gone, no need to error).

func (*Resource) ImportState

ImportState lets `terraform import hyperv_image_file.foo C:\path\file.vhdx` work by treating the import ID as the destination path. The imported resource lands in host_path mode (no url block) -- importing inherently means "this file already exists on the host." Users can convert to url-mode later by adding the block, which will trigger replacement.

func (*Resource) Metadata

Metadata sets the resource's TF type name.

func (*Resource) ModifyPlan added in v0.2.0

ModifyPlan computes the runner-side SHA-256 and size of the bytes that will land on the host (read from `local_path` for local_path- mode, decoded from `content_base64` for literal_bytes-mode) at plan time and writes them into the planned `sha256` / `size_bytes` attributes. This is what makes content changes (same destination, different bytes) surface as a plan diff -- without it, `UseStateForUnknown` would carry the prior values forward and the framework would either skip the Update entirely or reject the apply with a "Provider produced inconsistent result" check on the Computed attribute that didn't match its planned value.

Both attributes must be updated together: a content change generally changes both, and the framework's post-apply consistency check triggers on either one drifting from plan to apply.

Skipped for url-mode and host_path-mode (none of the runner-side inputs are set), during destroy (no plan), and when the relevant runner-side input is itself unknown at plan time (driven from a not-yet-applied dependency).

func (*Resource) Read

Read fetches the current shape via get.ps1 and reconciles state.

ErrNotFound -> RemoveResource so Terraform plans recreate. ErrUnauthorized / ErrPSExecution -> AddError so a transient fault doesn't silently drop the resource from state.

func (*Resource) Schema

Schema returns the locked-in schema (see schema.go).

func (*Resource) Update

Update is reached for two reasons: local_path-mode bytes changed (ModifyPlan-recomputed SHA differs from state) or a non-RequiresReplace flag toggled (replace_while_mounted / keep_on_destroy). Only local_path bytes-changed actually re-streams; the flag-only path takes the SHA-equality shortcut and skips the wire entirely so a cidata-seed flag flip doesn't re-stream the full ISO over WinRM.

literal_bytes-mode bytes-changed never enters Update -- content_base64 is RequiresReplace, so a byte change triggers Destroy+Create. A literal_bytes flag toggle reaches Update with plan.Sha256 == state.Sha256 and exits via the shortcut. Same shape for url-mode and host_path-mode: every user-settable source field is RequiresReplace, so only the flag-only path is reachable.

type URLConfig

type URLConfig struct {
	URL         types.String `tfsdk:"url"`
	Checksum    types.String `tfsdk:"checksum"`
	Compression types.String `tfsdk:"compression"`
}

URLConfig is the user-supplied URL-mode source configuration. `url` is required when the block is present; `checksum` is optional (when omitted, the download is trusted TLS-only). `compression` is optional -- absence means "no decompression, host fetches directly"; presence flips the typed client to a runner-pipelined fetch (download + decompress on the runner, then stream decompressed bytes to the host).

The Model carries `url` as types.Object rather than *URLConfig because the framework's pointer-to-struct shape can represent null (nil) but not unknown -- and unknown is exactly what the framework marshals when the attribute is driven from a parent variable that hasn't resolved yet (e.g. each.value.url before for_each materializes). types.Object handles all three states (known/null/unknown), and the helpers below give resource code typed access when the value is known.

Jump to

Keyboard shortcuts

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