Skip to content

Commit

Permalink
Add host_url field
Browse files Browse the repository at this point in the history
- Added the host_url to the output of the remote_vm
- Fixes a nil pointer in the ensure_machine_stopped
  • Loading branch information
cjlapao committed Nov 25, 2024
1 parent 0f7e7f5 commit c767a08
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 8 deletions.
2 changes: 2 additions & 0 deletions internal/apiclient/apimodels/virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package apimodels
type VirtualMachine struct {
User string `json:"user"`
ID string `json:"ID"`
Host string `json:"Host"`
HostUrl string `json:"host_url"`
HostId string `json:"host_id"`
HostExternalIpAddress string `json:"host_external_ip_address"`
InternalIpAddress string `json:"internal_ip_address"`
Expand Down
4 changes: 4 additions & 0 deletions internal/common/ensure_machine_stopped.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func EnsureMachineStopped(ctx context.Context, hostConfig apiclient.HostConfig,
if checkVmDiag.HasError() {
diagnostics.Append(checkVmDiag...)
}
if updatedVm == nil {
diagnostics.AddError("error stopping vm", "VM not found")
return returnVm, diagnostics
}

// All if good, break out of the loop
if updatedVm.State == "stopped" {
Expand Down
45 changes: 45 additions & 0 deletions internal/remoteimage/models/resource_models_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package models

import (
"terraform-provider-parallels-desktop/internal/schemas/authenticator"
"terraform-provider-parallels-desktop/internal/schemas/postprocessorscript"
"terraform-provider-parallels-desktop/internal/schemas/prlctl"
"terraform-provider-parallels-desktop/internal/schemas/reverseproxy"
"terraform-provider-parallels-desktop/internal/schemas/sharedfolder"
"terraform-provider-parallels-desktop/internal/schemas/vmconfig"
"terraform-provider-parallels-desktop/internal/schemas/vmspecs"

"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// VirtualMachineStateResourceModel describes the resource data model.
type RemoteVmResourceModelV2 struct {
Authenticator *authenticator.Authentication `tfsdk:"authenticator"`
Host types.String `tfsdk:"host"`
HostUrl types.String `tfsdk:"host_url"`
Orchestrator types.String `tfsdk:"orchestrator"`
ID types.String `tfsdk:"id"`
ExternalIp types.String `tfsdk:"external_ip"`
InternalIp types.String `tfsdk:"internal_ip"`
OrchestratorHostId types.String `tfsdk:"orchestrator_host_id"`
OsType types.String `tfsdk:"os_type"`
CatalogId types.String `tfsdk:"catalog_id"`
Version types.String `tfsdk:"version"`
Architecture types.String `tfsdk:"architecture"`
Name types.String `tfsdk:"name"`
Owner types.String `tfsdk:"owner"`
CatalogConnection types.String `tfsdk:"catalog_connection"`
Path types.String `tfsdk:"path"`
Specs *vmspecs.VmSpecs `tfsdk:"specs"`
PostProcessorScripts []*postprocessorscript.PostProcessorScript `tfsdk:"post_processor_script"`
OnDestroyScript []*postprocessorscript.PostProcessorScript `tfsdk:"on_destroy_script"`
SharedFolder []*sharedfolder.SharedFolder `tfsdk:"shared_folder"`
Config *vmconfig.VmConfig `tfsdk:"config"`
PrlCtl []*prlctl.PrlCtlCmd `tfsdk:"prlctl"`
RunAfterCreate types.Bool `tfsdk:"run_after_create"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
ForceChanges types.Bool `tfsdk:"force_changes"`
KeepRunning types.Bool `tfsdk:"keep_running"`
ReverseProxyHosts []*reverseproxy.ReverseProxyHost `tfsdk:"reverse_proxy_host"`
}
66 changes: 58 additions & 8 deletions internal/remoteimage/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (r *RemoteVmResource) Metadata(ctx context.Context, req resource.MetadataRe
}

func (r *RemoteVmResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schemas.GetRemoteImageSchemaV1(ctx)
resp.Schema = schemas.GetRemoteImageSchemaV2(ctx)
}

func (r *RemoteVmResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
Expand All @@ -64,7 +64,7 @@ func (r *RemoteVmResource) Configure(ctx context.Context, req resource.Configure
}

func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data models.RemoteVmResourceModelV1
var data models.RemoteVmResourceModelV2

telemetrySvc := telemetry.Get(ctx)
telemetryEvent := telemetry.NewTelemetryItem(
Expand Down Expand Up @@ -369,11 +369,12 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques

externalIp := ""
internalIp := ""
data.HostUrl = types.StringValue(stoppedVm.HostUrl)
retryAttempts := 10
var refreshVm *apimodels.VirtualMachine
var refreshDiag diag.Diagnostics
for {
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, refreshVm.ID)
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, stoppedVm.ID)
if !refreshDiag.HasError() {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
Expand Down Expand Up @@ -450,7 +451,7 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques
}

func (r *RemoteVmResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data models.RemoteVmResourceModelV1
var data models.RemoteVmResourceModelV2

telemetrySvc := telemetry.Get(ctx)
telemetryEvent := telemetry.NewTelemetryItem(
Expand Down Expand Up @@ -522,8 +523,8 @@ func (r *RemoteVmResource) Read(ctx context.Context, req resource.ReadRequest, r
}

func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data models.RemoteVmResourceModelV1
var currentData models.RemoteVmResourceModelV1
var data models.RemoteVmResourceModelV2
var currentData models.RemoteVmResourceModelV2

telemetrySvc := telemetry.Get(ctx)
telemetryEvent := telemetry.NewTelemetryItem(
Expand Down Expand Up @@ -753,6 +754,7 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques

externalIp := ""
internalIp := ""
data.HostUrl = types.StringValue(vm.HostUrl)
retryAttempts := 10
var refreshVm *apimodels.VirtualMachine
var refreshDiag diag.Diagnostics
Expand Down Expand Up @@ -860,7 +862,7 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques
}

func (r *RemoteVmResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data models.RemoteVmResourceModelV1
var data models.RemoteVmResourceModelV2
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

Expand Down Expand Up @@ -988,11 +990,16 @@ func (r *RemoteVmResource) ImportState(ctx context.Context, req resource.ImportS

func (r *RemoteVmResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
v0Schema := schemas.GetRemoteImageSchemaV0(ctx)
V1Schema := schemas.GetRemoteImageSchemaV1(ctx)
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: &v0Schema,
StateUpgrader: UpgradeStateToV1,
},
1: {
PriorSchema: &V1Schema,
StateUpgrader: UpgradeStateToV2,
},
}
}

Expand Down Expand Up @@ -1038,7 +1045,50 @@ func UpgradeStateToV1(ctx context.Context, req resource.UpgradeStateRequest, res
resp.Diagnostics.Append(resp.State.Set(ctx, &upgradedStateData)...)
}

func updateReverseProxyHostsTarget(ctx context.Context, data *models.RemoteVmResourceModelV1, hostConfig apiclient.HostConfig, targetVm *apimodels.VirtualMachine) ([]reverseproxy.ReverseProxyHost, diag.Diagnostics) {
func UpgradeStateToV2(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var priorStateData models.RemoteVmResourceModelV1
resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...)

if resp.Diagnostics.HasError() {
return
}

upgradedStateData := models.RemoteVmResourceModelV2{
Authenticator: priorStateData.Authenticator,
Host: priorStateData.Host,
HostUrl: types.StringUnknown(),
Orchestrator: priorStateData.Orchestrator,
ID: priorStateData.ID,
OsType: priorStateData.OsType,
ExternalIp: priorStateData.ExternalIp,
InternalIp: priorStateData.InternalIp,
OrchestratorHostId: priorStateData.OrchestratorHostId,
CatalogId: priorStateData.CatalogId,
Version: priorStateData.Version,
Architecture: priorStateData.Architecture,
Name: priorStateData.Name,
Owner: priorStateData.Owner,
CatalogConnection: priorStateData.CatalogConnection,
Path: priorStateData.Path,
Specs: priorStateData.Specs,
PostProcessorScripts: priorStateData.PostProcessorScripts,
OnDestroyScript: priorStateData.OnDestroyScript,
SharedFolder: priorStateData.SharedFolder,
Config: priorStateData.Config,
PrlCtl: priorStateData.PrlCtl,
RunAfterCreate: priorStateData.RunAfterCreate,
Timeouts: priorStateData.Timeouts,
ForceChanges: priorStateData.ForceChanges,
KeepRunning: priorStateData.KeepRunning,
ReverseProxyHosts: priorStateData.ReverseProxyHosts,
}

println(fmt.Sprintf("Upgrading state from version %v", upgradedStateData))

resp.Diagnostics.Append(resp.State.Set(ctx, &upgradedStateData)...)
}

func updateReverseProxyHostsTarget(ctx context.Context, data *models.RemoteVmResourceModelV2, hostConfig apiclient.HostConfig, targetVm *apimodels.VirtualMachine) ([]reverseproxy.ReverseProxyHost, diag.Diagnostics) {
resultDiagnostic := diag.Diagnostics{}
var refreshedVm *apimodels.VirtualMachine
var rpDiag diag.Diagnostics
Expand Down
150 changes: 150 additions & 0 deletions internal/remoteimage/schemas/resource_schema_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package schemas

import (
"context"

"terraform-provider-parallels-desktop/internal/schemas/authenticator"
"terraform-provider-parallels-desktop/internal/schemas/postprocessorscript"
"terraform-provider-parallels-desktop/internal/schemas/prlctl"
"terraform-provider-parallels-desktop/internal/schemas/reverseproxy"
"terraform-provider-parallels-desktop/internal/schemas/sharedfolder"
"terraform-provider-parallels-desktop/internal/schemas/vmconfig"
"terraform-provider-parallels-desktop/internal/schemas/vmspecs"

"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
)

func GetRemoteImageSchemaV2(ctx context.Context) schema.Schema {
return schema.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Parallels Virtual Machine State Resource",
Blocks: map[string]schema.Block{
authenticator.SchemaName: authenticator.SchemaBlock,
vmspecs.SchemaName: vmspecs.SchemaBlock,
postprocessorscript.SchemaName: postprocessorscript.SchemaBlock,
"on_destroy_script": postprocessorscript.SchemaBlock,
sharedfolder.SchemaName: sharedfolder.SchemaBlock,
vmconfig.SchemaName: vmconfig.SchemaBlock,
prlctl.SchemaName: prlctl.SchemaBlock,
reverseproxy.SchemaName: reverseproxy.HostBlockV0,
},
Version: 1,
Attributes: map[string]schema.Attribute{
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
}),
"force_changes": schema.BoolAttribute{
MarkdownDescription: "Force changes, this will force the VM to be stopped and started again",
Optional: true,
},
"host": schema.StringAttribute{
MarkdownDescription: "Parallels Desktop DevOps Host",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Validators: []validator.String{
stringvalidator.AtLeastOneOf(path.Expressions{
path.MatchRoot("orchestrator"),
path.MatchRoot("host"),
}...),
},
},
"orchestrator": schema.StringAttribute{
MarkdownDescription: "Parallels Desktop DevOps Orchestrator",
Optional: true,
Validators: []validator.String{
stringvalidator.AtLeastOneOf(path.Expressions{
path.MatchRoot("orchestrator"),
path.MatchRoot("host"),
}...),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"id": schema.StringAttribute{
MarkdownDescription: "Virtual Machine Id",
Computed: true,
},
"orchestrator_host_id": schema.StringAttribute{
MarkdownDescription: "Orchestrator Host Id if the VM is running in an orchestrator",
Computed: true,
},
"os_type": schema.StringAttribute{
MarkdownDescription: "Virtual Machine OS type",
Computed: true,
},
"catalog_id": schema.StringAttribute{
MarkdownDescription: "Catalog Id to pull",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"version": schema.StringAttribute{
MarkdownDescription: "Catalog version to pull, if empty will pull the 'latest' version",
Optional: true,
},
"architecture": schema.StringAttribute{
MarkdownDescription: "Virtual Machine architecture",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"name": schema.StringAttribute{
MarkdownDescription: "Virtual Machine name to create, this needs to be unique in the host",
Required: true,
},
"owner": schema.StringAttribute{
MarkdownDescription: "Virtual Machine owner",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"catalog_connection": schema.StringAttribute{
MarkdownDescription: "Parallels DevOps Catalog Connection",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"path": schema.StringAttribute{
MarkdownDescription: "Path",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"run_after_create": schema.BoolAttribute{
MarkdownDescription: "Run after create, this will make the VM to run after creation",
Optional: true,
DeprecationMessage: "Use the `keep_running` attribute instead",
},
"external_ip": schema.StringAttribute{
MarkdownDescription: "VM external IP address",
Computed: true,
},
"internal_ip": schema.StringAttribute{
MarkdownDescription: "VM internal IP address",
Computed: true,
},
"keep_running": schema.BoolAttribute{
MarkdownDescription: "This will keep the VM running after the terraform apply",
Optional: true,
},
"host_url": schema.StringAttribute{
MarkdownDescription: "Parallels Desktop DevOps Host URL",
Computed: true,
},
},
}
}

0 comments on commit c767a08

Please sign in to comment.