Skip to content

Commit c767a08

Browse files
committed
Add host_url field
- Added the host_url to the output of the remote_vm - Fixes a nil pointer in the ensure_machine_stopped
1 parent 0f7e7f5 commit c767a08

File tree

5 files changed

+259
-8
lines changed

5 files changed

+259
-8
lines changed

internal/apiclient/apimodels/virtual_machine.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package apimodels
33
type VirtualMachine struct {
44
User string `json:"user"`
55
ID string `json:"ID"`
6+
Host string `json:"Host"`
7+
HostUrl string `json:"host_url"`
68
HostId string `json:"host_id"`
79
HostExternalIpAddress string `json:"host_external_ip_address"`
810
InternalIpAddress string `json:"internal_ip_address"`

internal/common/ensure_machine_stopped.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ func EnsureMachineStopped(ctx context.Context, hostConfig apiclient.HostConfig,
4343
if checkVmDiag.HasError() {
4444
diagnostics.Append(checkVmDiag...)
4545
}
46+
if updatedVm == nil {
47+
diagnostics.AddError("error stopping vm", "VM not found")
48+
return returnVm, diagnostics
49+
}
4650

4751
// All if good, break out of the loop
4852
if updatedVm.State == "stopped" {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package models
2+
3+
import (
4+
"terraform-provider-parallels-desktop/internal/schemas/authenticator"
5+
"terraform-provider-parallels-desktop/internal/schemas/postprocessorscript"
6+
"terraform-provider-parallels-desktop/internal/schemas/prlctl"
7+
"terraform-provider-parallels-desktop/internal/schemas/reverseproxy"
8+
"terraform-provider-parallels-desktop/internal/schemas/sharedfolder"
9+
"terraform-provider-parallels-desktop/internal/schemas/vmconfig"
10+
"terraform-provider-parallels-desktop/internal/schemas/vmspecs"
11+
12+
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
13+
"github.com/hashicorp/terraform-plugin-framework/types"
14+
)
15+
16+
// VirtualMachineStateResourceModel describes the resource data model.
17+
type RemoteVmResourceModelV2 struct {
18+
Authenticator *authenticator.Authentication `tfsdk:"authenticator"`
19+
Host types.String `tfsdk:"host"`
20+
HostUrl types.String `tfsdk:"host_url"`
21+
Orchestrator types.String `tfsdk:"orchestrator"`
22+
ID types.String `tfsdk:"id"`
23+
ExternalIp types.String `tfsdk:"external_ip"`
24+
InternalIp types.String `tfsdk:"internal_ip"`
25+
OrchestratorHostId types.String `tfsdk:"orchestrator_host_id"`
26+
OsType types.String `tfsdk:"os_type"`
27+
CatalogId types.String `tfsdk:"catalog_id"`
28+
Version types.String `tfsdk:"version"`
29+
Architecture types.String `tfsdk:"architecture"`
30+
Name types.String `tfsdk:"name"`
31+
Owner types.String `tfsdk:"owner"`
32+
CatalogConnection types.String `tfsdk:"catalog_connection"`
33+
Path types.String `tfsdk:"path"`
34+
Specs *vmspecs.VmSpecs `tfsdk:"specs"`
35+
PostProcessorScripts []*postprocessorscript.PostProcessorScript `tfsdk:"post_processor_script"`
36+
OnDestroyScript []*postprocessorscript.PostProcessorScript `tfsdk:"on_destroy_script"`
37+
SharedFolder []*sharedfolder.SharedFolder `tfsdk:"shared_folder"`
38+
Config *vmconfig.VmConfig `tfsdk:"config"`
39+
PrlCtl []*prlctl.PrlCtlCmd `tfsdk:"prlctl"`
40+
RunAfterCreate types.Bool `tfsdk:"run_after_create"`
41+
Timeouts timeouts.Value `tfsdk:"timeouts"`
42+
ForceChanges types.Bool `tfsdk:"force_changes"`
43+
KeepRunning types.Bool `tfsdk:"keep_running"`
44+
ReverseProxyHosts []*reverseproxy.ReverseProxyHost `tfsdk:"reverse_proxy_host"`
45+
}

internal/remoteimage/resource.go

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (r *RemoteVmResource) Metadata(ctx context.Context, req resource.MetadataRe
4343
}
4444

4545
func (r *RemoteVmResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
46-
resp.Schema = schemas.GetRemoteImageSchemaV1(ctx)
46+
resp.Schema = schemas.GetRemoteImageSchemaV2(ctx)
4747
}
4848

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

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

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

370370
externalIp := ""
371371
internalIp := ""
372+
data.HostUrl = types.StringValue(stoppedVm.HostUrl)
372373
retryAttempts := 10
373374
var refreshVm *apimodels.VirtualMachine
374375
var refreshDiag diag.Diagnostics
375376
for {
376-
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, refreshVm.ID)
377+
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, stoppedVm.ID)
377378
if !refreshDiag.HasError() {
378379
externalIp = refreshVm.HostExternalIpAddress
379380
internalIp = refreshVm.InternalIpAddress
@@ -450,7 +451,7 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques
450451
}
451452

452453
func (r *RemoteVmResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
453-
var data models.RemoteVmResourceModelV1
454+
var data models.RemoteVmResourceModelV2
454455

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

524525
func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
525-
var data models.RemoteVmResourceModelV1
526-
var currentData models.RemoteVmResourceModelV1
526+
var data models.RemoteVmResourceModelV2
527+
var currentData models.RemoteVmResourceModelV2
527528

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

754755
externalIp := ""
755756
internalIp := ""
757+
data.HostUrl = types.StringValue(vm.HostUrl)
756758
retryAttempts := 10
757759
var refreshVm *apimodels.VirtualMachine
758760
var refreshDiag diag.Diagnostics
@@ -860,7 +862,7 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques
860862
}
861863

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

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

989991
func (r *RemoteVmResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
990992
v0Schema := schemas.GetRemoteImageSchemaV0(ctx)
993+
V1Schema := schemas.GetRemoteImageSchemaV1(ctx)
991994
return map[int64]resource.StateUpgrader{
992995
0: {
993996
PriorSchema: &v0Schema,
994997
StateUpgrader: UpgradeStateToV1,
995998
},
999+
1: {
1000+
PriorSchema: &V1Schema,
1001+
StateUpgrader: UpgradeStateToV2,
1002+
},
9961003
}
9971004
}
9981005

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

1041-
func updateReverseProxyHostsTarget(ctx context.Context, data *models.RemoteVmResourceModelV1, hostConfig apiclient.HostConfig, targetVm *apimodels.VirtualMachine) ([]reverseproxy.ReverseProxyHost, diag.Diagnostics) {
1048+
func UpgradeStateToV2(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
1049+
var priorStateData models.RemoteVmResourceModelV1
1050+
resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...)
1051+
1052+
if resp.Diagnostics.HasError() {
1053+
return
1054+
}
1055+
1056+
upgradedStateData := models.RemoteVmResourceModelV2{
1057+
Authenticator: priorStateData.Authenticator,
1058+
Host: priorStateData.Host,
1059+
HostUrl: types.StringUnknown(),
1060+
Orchestrator: priorStateData.Orchestrator,
1061+
ID: priorStateData.ID,
1062+
OsType: priorStateData.OsType,
1063+
ExternalIp: priorStateData.ExternalIp,
1064+
InternalIp: priorStateData.InternalIp,
1065+
OrchestratorHostId: priorStateData.OrchestratorHostId,
1066+
CatalogId: priorStateData.CatalogId,
1067+
Version: priorStateData.Version,
1068+
Architecture: priorStateData.Architecture,
1069+
Name: priorStateData.Name,
1070+
Owner: priorStateData.Owner,
1071+
CatalogConnection: priorStateData.CatalogConnection,
1072+
Path: priorStateData.Path,
1073+
Specs: priorStateData.Specs,
1074+
PostProcessorScripts: priorStateData.PostProcessorScripts,
1075+
OnDestroyScript: priorStateData.OnDestroyScript,
1076+
SharedFolder: priorStateData.SharedFolder,
1077+
Config: priorStateData.Config,
1078+
PrlCtl: priorStateData.PrlCtl,
1079+
RunAfterCreate: priorStateData.RunAfterCreate,
1080+
Timeouts: priorStateData.Timeouts,
1081+
ForceChanges: priorStateData.ForceChanges,
1082+
KeepRunning: priorStateData.KeepRunning,
1083+
ReverseProxyHosts: priorStateData.ReverseProxyHosts,
1084+
}
1085+
1086+
println(fmt.Sprintf("Upgrading state from version %v", upgradedStateData))
1087+
1088+
resp.Diagnostics.Append(resp.State.Set(ctx, &upgradedStateData)...)
1089+
}
1090+
1091+
func updateReverseProxyHostsTarget(ctx context.Context, data *models.RemoteVmResourceModelV2, hostConfig apiclient.HostConfig, targetVm *apimodels.VirtualMachine) ([]reverseproxy.ReverseProxyHost, diag.Diagnostics) {
10421092
resultDiagnostic := diag.Diagnostics{}
10431093
var refreshedVm *apimodels.VirtualMachine
10441094
var rpDiag diag.Diagnostics
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package schemas
2+
3+
import (
4+
"context"
5+
6+
"terraform-provider-parallels-desktop/internal/schemas/authenticator"
7+
"terraform-provider-parallels-desktop/internal/schemas/postprocessorscript"
8+
"terraform-provider-parallels-desktop/internal/schemas/prlctl"
9+
"terraform-provider-parallels-desktop/internal/schemas/reverseproxy"
10+
"terraform-provider-parallels-desktop/internal/schemas/sharedfolder"
11+
"terraform-provider-parallels-desktop/internal/schemas/vmconfig"
12+
"terraform-provider-parallels-desktop/internal/schemas/vmspecs"
13+
14+
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
15+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
16+
"github.com/hashicorp/terraform-plugin-framework/path"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
18+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
19+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
20+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
21+
)
22+
23+
func GetRemoteImageSchemaV2(ctx context.Context) schema.Schema {
24+
return schema.Schema{
25+
// This description is used by the documentation generator and the language server.
26+
MarkdownDescription: "Parallels Virtual Machine State Resource",
27+
Blocks: map[string]schema.Block{
28+
authenticator.SchemaName: authenticator.SchemaBlock,
29+
vmspecs.SchemaName: vmspecs.SchemaBlock,
30+
postprocessorscript.SchemaName: postprocessorscript.SchemaBlock,
31+
"on_destroy_script": postprocessorscript.SchemaBlock,
32+
sharedfolder.SchemaName: sharedfolder.SchemaBlock,
33+
vmconfig.SchemaName: vmconfig.SchemaBlock,
34+
prlctl.SchemaName: prlctl.SchemaBlock,
35+
reverseproxy.SchemaName: reverseproxy.HostBlockV0,
36+
},
37+
Version: 1,
38+
Attributes: map[string]schema.Attribute{
39+
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
40+
Create: true,
41+
}),
42+
"force_changes": schema.BoolAttribute{
43+
MarkdownDescription: "Force changes, this will force the VM to be stopped and started again",
44+
Optional: true,
45+
},
46+
"host": schema.StringAttribute{
47+
MarkdownDescription: "Parallels Desktop DevOps Host",
48+
Optional: true,
49+
PlanModifiers: []planmodifier.String{
50+
stringplanmodifier.RequiresReplace(),
51+
},
52+
Validators: []validator.String{
53+
stringvalidator.AtLeastOneOf(path.Expressions{
54+
path.MatchRoot("orchestrator"),
55+
path.MatchRoot("host"),
56+
}...),
57+
},
58+
},
59+
"orchestrator": schema.StringAttribute{
60+
MarkdownDescription: "Parallels Desktop DevOps Orchestrator",
61+
Optional: true,
62+
Validators: []validator.String{
63+
stringvalidator.AtLeastOneOf(path.Expressions{
64+
path.MatchRoot("orchestrator"),
65+
path.MatchRoot("host"),
66+
}...),
67+
},
68+
PlanModifiers: []planmodifier.String{
69+
stringplanmodifier.RequiresReplace(),
70+
},
71+
},
72+
"id": schema.StringAttribute{
73+
MarkdownDescription: "Virtual Machine Id",
74+
Computed: true,
75+
},
76+
"orchestrator_host_id": schema.StringAttribute{
77+
MarkdownDescription: "Orchestrator Host Id if the VM is running in an orchestrator",
78+
Computed: true,
79+
},
80+
"os_type": schema.StringAttribute{
81+
MarkdownDescription: "Virtual Machine OS type",
82+
Computed: true,
83+
},
84+
"catalog_id": schema.StringAttribute{
85+
MarkdownDescription: "Catalog Id to pull",
86+
Required: true,
87+
PlanModifiers: []planmodifier.String{
88+
stringplanmodifier.RequiresReplace(),
89+
},
90+
},
91+
"version": schema.StringAttribute{
92+
MarkdownDescription: "Catalog version to pull, if empty will pull the 'latest' version",
93+
Optional: true,
94+
},
95+
"architecture": schema.StringAttribute{
96+
MarkdownDescription: "Virtual Machine architecture",
97+
Optional: true,
98+
PlanModifiers: []planmodifier.String{
99+
stringplanmodifier.RequiresReplace(),
100+
},
101+
},
102+
"name": schema.StringAttribute{
103+
MarkdownDescription: "Virtual Machine name to create, this needs to be unique in the host",
104+
Required: true,
105+
},
106+
"owner": schema.StringAttribute{
107+
MarkdownDescription: "Virtual Machine owner",
108+
Optional: true,
109+
PlanModifiers: []planmodifier.String{
110+
stringplanmodifier.RequiresReplace(),
111+
},
112+
},
113+
"catalog_connection": schema.StringAttribute{
114+
MarkdownDescription: "Parallels DevOps Catalog Connection",
115+
Required: true,
116+
PlanModifiers: []planmodifier.String{
117+
stringplanmodifier.RequiresReplace(),
118+
},
119+
},
120+
"path": schema.StringAttribute{
121+
MarkdownDescription: "Path",
122+
Required: true,
123+
PlanModifiers: []planmodifier.String{
124+
stringplanmodifier.RequiresReplace(),
125+
},
126+
},
127+
"run_after_create": schema.BoolAttribute{
128+
MarkdownDescription: "Run after create, this will make the VM to run after creation",
129+
Optional: true,
130+
DeprecationMessage: "Use the `keep_running` attribute instead",
131+
},
132+
"external_ip": schema.StringAttribute{
133+
MarkdownDescription: "VM external IP address",
134+
Computed: true,
135+
},
136+
"internal_ip": schema.StringAttribute{
137+
MarkdownDescription: "VM internal IP address",
138+
Computed: true,
139+
},
140+
"keep_running": schema.BoolAttribute{
141+
MarkdownDescription: "This will keep the VM running after the terraform apply",
142+
Optional: true,
143+
},
144+
"host_url": schema.StringAttribute{
145+
MarkdownDescription: "Parallels Desktop DevOps Host URL",
146+
Computed: true,
147+
},
148+
},
149+
}
150+
}

0 commit comments

Comments
 (0)