From 56e9f7a375cc254763cfcf77651a857e9040ce00 Mon Sep 17 00:00:00 2001 From: Carlos Lapao Date: Tue, 12 Nov 2024 13:13:41 +0000 Subject: [PATCH] Fix the issue for https host - Fixed an issue where if we would enable the https for a host and registered it with a orchestrator it would fail due to wrong port - Added the new resource called vm_state to set a existing vm to a state - Fixed an issue in the telemetry not being sent correctly --- docs/resources/vm_state.md | 43 +++ internal/deploy/models/resource_models_v2.go | 11 +- internal/deploy/resource.go | 6 +- internal/provider/provider.go | 3 +- internal/remoteimage/resource.go | 5 + internal/telemetry/events.go | 10 +- internal/telemetry/main.go | 2 +- internal/telemetry/telemetry_item.go | 2 + .../models/resource_model_v1.go | 1 + internal/virtualmachinestate/resource.go | 323 +++++++++++++++--- .../schemas/resource_schema_v1.go | 4 + 11 files changed, 353 insertions(+), 57 deletions(-) create mode 100644 docs/resources/vm_state.md diff --git a/docs/resources/vm_state.md b/docs/resources/vm_state.md new file mode 100644 index 0000000..f315a6c --- /dev/null +++ b/docs/resources/vm_state.md @@ -0,0 +1,43 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "parallels-desktop_vm_state Resource - terraform-provider-parallels-desktop" +subcategory: "" +description: |- + Parallels Virtual Machine State Resource + Use this to set a virtual machine to a desired state. +--- + +# parallels-desktop_vm_state (Resource) + +Parallels Virtual Machine State Resource + Use this to set a virtual machine to a desired state. + + + + +## Schema + +### Required + +- `id` (String) Virtual Machine Id +- `operation` (String) Virtual Machine desired state + +### Optional + +- `authenticator` (Block, Optional) Authenticator block, this is used to authenticate with the Parallels Desktop API, if empty it will try to use the root password (see [below for nested schema](#nestedblock--authenticator)) +- `ensure_state` (Boolean) Ensure the virtual machine is in the desired state +- `host` (String) Parallels Desktop DevOps Host +- `orchestrator` (String) Parallels Desktop DevOps Orchestrator + +### Read-Only + +- `current_state` (String) Virtual Machine current state + + +### Nested Schema for `authenticator` + +Optional: + +- `api_key` (String, Sensitive) Parallels desktop API API Key +- `password` (String, Sensitive) Parallels desktop API Password +- `username` (String) Parallels desktop API Username diff --git a/internal/deploy/models/resource_models_v2.go b/internal/deploy/models/resource_models_v2.go index 85cf8f5..57ba9ce 100644 --- a/internal/deploy/models/resource_models_v2.go +++ b/internal/deploy/models/resource_models_v2.go @@ -2,7 +2,6 @@ package models import ( "strings" - "terraform-provider-parallels-desktop/internal/apiclient" "terraform-provider-parallels-desktop/internal/models" "terraform-provider-parallels-desktop/internal/schemas/authenticator" @@ -127,15 +126,19 @@ func (o *DeployResourceModelV2) GenerateApiHostConfig(provider *models.Parallels DisableTlsValidation: provider.DisableTlsValidation.ValueBool(), } + api_port := strings.ReplaceAll(o.ApiConfig.Port.ValueString(), "\"", "") api_schema := "http" - if api_port != "" { - hostConfig.Host = hostConfig.Host + ":" + api_port - } if o.ApiConfig.EnableTLS.ValueBool() { api_schema = "https" + api_port = strings.ReplaceAll(o.ApiConfig.TLSPort.ValueString(), "\"", "") } + + if api_port != "" { + hostConfig.Host = hostConfig.Host + ":" + api_port + } + hostConfig.Host = api_schema + "://" + hostConfig.Host return hostConfig diff --git a/internal/deploy/resource.go b/internal/deploy/resource.go index 554d138..5aca4ca 100644 --- a/internal/deploy/resource.go +++ b/internal/deploy/resource.go @@ -5,9 +5,7 @@ import ( "errors" "fmt" "strings" - "terraform-provider-parallels-desktop/internal/common" - deploy_models "terraform-provider-parallels-desktop/internal/deploy/models" "terraform-provider-parallels-desktop/internal/deploy/schemas" "terraform-provider-parallels-desktop/internal/interfaces" "terraform-provider-parallels-desktop/internal/localclient" @@ -16,9 +14,10 @@ import ( "terraform-provider-parallels-desktop/internal/schemas/orchestrator" "terraform-provider-parallels-desktop/internal/schemas/reverseproxy" "terraform-provider-parallels-desktop/internal/ssh" - "terraform-provider-parallels-desktop/internal/telemetry" + deploy_models "terraform-provider-parallels-desktop/internal/deploy/models" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" @@ -1011,6 +1010,7 @@ func (r *DeployResource) registerWithOrchestrator(ctx context.Context, data, cur password := strings.ReplaceAll(data.ApiConfig.RootPassword.String(), "\"", "") if data.ApiConfig.EnableTLS.ValueBool() { schema = "https" + port = strings.ReplaceAll(data.ApiConfig.TLSPort.String(), "\"", "") } if currentData != nil { diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a68928d..29e043b 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -10,6 +10,7 @@ import ( "terraform-provider-parallels-desktop/internal/remoteimage" "terraform-provider-parallels-desktop/internal/vagrantbox" "terraform-provider-parallels-desktop/internal/virtualmachine" + "terraform-provider-parallels-desktop/internal/virtualmachinestate" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/path" @@ -122,7 +123,7 @@ func (p *ParallelsProvider) DataSources(_ context.Context) []func() datasource.D // Resources defines the resources implemented in the provider. func (p *ParallelsProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ - // virtualmachinestate.NewVirtualMachineStateResource, + virtualmachinestate.NewVirtualMachineStateResource, deploy.NewDeployResource, // packertemplate.NewPackerTemplateVirtualMachineResource, authorization.NewAuthorizationResource, diff --git a/internal/remoteimage/resource.go b/internal/remoteimage/resource.go index a144522..5a6047a 100644 --- a/internal/remoteimage/resource.go +++ b/internal/remoteimage/resource.go @@ -202,6 +202,11 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques return } + if createdVM == nil { + resp.Diagnostics.AddError("VM not found", "There was an issue creating the VM, we could not find it in the host") + return + } + hostConfig.HostId = createdVM.HostId // stopping the machine as it might need some operations where the machine needs to be stopped diff --git a/internal/telemetry/events.go b/internal/telemetry/events.go index 144c279..bb1d78f 100644 --- a/internal/telemetry/events.go +++ b/internal/telemetry/events.go @@ -3,10 +3,12 @@ package telemetry type TelemetryEvent string const ( - EventDeploy TelemetryEvent = "PD-TERRAFORM-PROVIDER::DEPLOY" - EventVagrant TelemetryEvent = "PD-TERRAFORM-PROVIDER::VAGRANT" - EventRemoteImage TelemetryEvent = "PD-TERRAFORM-PROVIDER::REMOTE_IMAGE" - EventCloneVm TelemetryEvent = "PD-TERRAFORM-PROVIDER::CLONE_VM" + EventDeploy TelemetryEvent = "PD-TERRAFORM-PROVIDER::DEPLOY" + EventVagrant TelemetryEvent = "PD-TERRAFORM-PROVIDER::VAGRANT" + EventRemoteImage TelemetryEvent = "PD-TERRAFORM-PROVIDER::REMOTE_IMAGE" + EventCloneVm TelemetryEvent = "PD-TERRAFORM-PROVIDER::CLONE_VM" + EventDataSourceVm TelemetryEvent = "PD-TERRAFORM-PROVIDER::DATA_SOURCE_VM" + EventVirtualMachineState TelemetryEvent = "PD-TERRAFORM-PROVIDER::VIRTUAL_MACHINE_STATE" ) type TelemetryEventMode string diff --git a/internal/telemetry/main.go b/internal/telemetry/main.go index 55518fb..489f320 100644 --- a/internal/telemetry/main.go +++ b/internal/telemetry/main.go @@ -34,7 +34,7 @@ func New(context context.Context) *TelemetryService { config := amplitude.NewConfig(key) config.FlushQueueSize = 100 - config.FlushInterval = time.Second * 5 + config.FlushInterval = time.Second * 3 // adding a callback to read what is the status config.ExecuteCallback = func(result types.ExecuteResult) { svc.Callback(result) diff --git a/internal/telemetry/telemetry_item.go b/internal/telemetry/telemetry_item.go index a602679..902ce83 100644 --- a/internal/telemetry/telemetry_item.go +++ b/internal/telemetry/telemetry_item.go @@ -41,5 +41,7 @@ func NewTelemetryItem(ctx context.Context, userId string, eventType TelemetryEve item.Properties["user_id"] = hashedUserId } + item.UserID = hashedUserId + return item } diff --git a/internal/virtualmachinestate/models/resource_model_v1.go b/internal/virtualmachinestate/models/resource_model_v1.go index c9ea0ed..b196481 100644 --- a/internal/virtualmachinestate/models/resource_model_v1.go +++ b/internal/virtualmachinestate/models/resource_model_v1.go @@ -14,4 +14,5 @@ type VirtualMachineStateResourceModelV1 struct { ID types.String `tfsdk:"id"` Operation types.String `tfsdk:"operation"` CurrentState types.String `tfsdk:"current_state"` + EnsureState types.Bool `tfsdk:"ensure_state"` } diff --git a/internal/virtualmachinestate/resource.go b/internal/virtualmachinestate/resource.go index cbc3c24..28d505c 100644 --- a/internal/virtualmachinestate/resource.go +++ b/internal/virtualmachinestate/resource.go @@ -2,14 +2,19 @@ package virtualmachinestate import ( "context" + "errors" "fmt" "strings" + "time" "terraform-provider-parallels-desktop/internal/apiclient" + "terraform-provider-parallels-desktop/internal/apiclient/apimodels" "terraform-provider-parallels-desktop/internal/models" + "terraform-provider-parallels-desktop/internal/telemetry" resource_models "terraform-provider-parallels-desktop/internal/virtualmachinestate/models" "terraform-provider-parallels-desktop/internal/virtualmachinestate/schemas" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" @@ -18,8 +23,10 @@ import ( // Ensure provider defined types fully satisfy framework interfaces. var ( - _ resource.Resource = &VirtualMachineStateResource{} - _ resource.ResourceWithImportState = &VirtualMachineStateResource{} + _ resource.Resource = &VirtualMachineStateResource{} + _ resource.ResourceWithImportState = &VirtualMachineStateResource{} + maxRetries = 10 + waitBetweenRetries = 10 * time.Second ) func NewVirtualMachineStateResource() resource.Resource { @@ -59,12 +66,27 @@ func (r *VirtualMachineStateResource) Configure(ctx context.Context, req resourc func (r *VirtualMachineStateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var data resource_models.VirtualMachineStateResourceModelV1 + telemetrySvc := telemetry.Get(ctx) + telemetryEvent := telemetry.NewTelemetryItem( + ctx, + r.provider.License.String(), + telemetry.EventVirtualMachineState, telemetry.ModeCreate, + nil, + nil, + ) + telemetrySvc.TrackEvent(telemetryEvent) + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } + if !r.IsValidOperation(data.Operation.ValueString()) { + resp.Diagnostics.AddError("Invalid operation", fmt.Sprintf("Operation %s is not valid", data.Operation.ValueString())) + return + } + // selecting if this is a standalone host or an orchestrator isOrchestrator := false var host string @@ -98,18 +120,59 @@ func (r *VirtualMachineStateResource) Create(ctx context.Context, req resource.C return } - result, diag := apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), r.GetOpState(data.Operation.ValueString())) - if diag.HasError() { - resp.Diagnostics.Append(diag...) - return - } - if !result { - resp.Diagnostics.AddError("error changing the machine state", "Could not change the machine "+vm.Name+" state to +"+data.Operation.ValueString()) + isInState, err := r.IsInDesiredState(vm, data.Operation.ValueString()) + if err != nil { + resp.Diagnostics.AddError("error changing the machine state", err.Error()) return } - data.Operation = types.StringValue(data.Operation.ValueString()) - data.CurrentState = types.StringValue(vm.State) + if !isInState { + result, diag := apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), r.GetOpState(data.Operation.ValueString())) + if diag.HasError() { + resp.Diagnostics.Append(diag...) + return + } + if !result { + resp.Diagnostics.AddError("error changing the machine state", "Could not change the machine "+vm.Name+" state to +"+data.Operation.ValueString()) + return + } + + if data.EnsureState.ValueBool() { + if r.GetDesiredState(data.Operation.ValueString()) == "" { + resp.Diagnostics.AddError("error changing the machine state", "Could not determine the desired state") + return + } + + retries := 0 + for { + refreshedVm, refreshDiag := apiclient.GetVm(ctx, hostConfig, data.ID.ValueString()) + if diag.HasError() { + resp.Diagnostics.Append(refreshDiag...) + return + } + if refreshedVm.State == r.GetDesiredState(data.Operation.ValueString()) { + vm = refreshedVm + break + } + if retries >= maxRetries { + resp.Diagnostics.AddError("error changing the machine state", "Could not change the machine "+vm.Name+" state to +"+data.Operation.ValueString()) + return + } + retries++ + time.Sleep(waitBetweenRetries) + } + + data.CurrentState = types.StringValue(vm.State) + } else { + data.CurrentState = data.Operation + } + + data.Operation = types.StringValue(data.Operation.ValueString()) + data.CurrentState = types.StringValue(vm.State) + } else { + data.Operation = types.StringValue(data.Operation.ValueString()) + data.CurrentState = types.StringValue(vm.State) + } tflog.Trace(ctx, "virtual machine "+vm.Name+" state changed to "+data.Operation.ValueString()) @@ -120,6 +183,16 @@ func (r *VirtualMachineStateResource) Create(ctx context.Context, req resource.C func (r *VirtualMachineStateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { var data resource_models.VirtualMachineStateResourceModelV1 + telemetrySvc := telemetry.Get(ctx) + telemetryEvent := telemetry.NewTelemetryItem( + ctx, + r.provider.License.String(), + telemetry.EventVirtualMachineState, telemetry.ModeRead, + nil, + nil, + ) + telemetrySvc.TrackEvent(telemetryEvent) + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) if resp.Diagnostics.HasError() { @@ -170,13 +243,30 @@ func (r *VirtualMachineStateResource) Read(ctx context.Context, req resource.Rea func (r *VirtualMachineStateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var data resource_models.VirtualMachineStateResourceModelV1 - + var currentData resource_models.VirtualMachineStateResourceModelV1 + + telemetrySvc := telemetry.Get(ctx) + telemetryEvent := telemetry.NewTelemetryItem( + ctx, + r.provider.License.String(), + telemetry.EventVirtualMachineState, telemetry.ModeUpdate, + nil, + nil, + ) + telemetrySvc.TrackEvent(telemetryEvent) + + resp.Diagnostics.Append(req.State.Get(ctx, ¤tData)...) resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } + if !r.IsValidOperation(data.Operation.ValueString()) { + resp.Diagnostics.AddError("Invalid operation", fmt.Sprintf("Operation %s is not valid", data.Operation.ValueString())) + return + } + if data.ID.IsNull() { resp.Diagnostics.AddError("Id is required", "Id is required") return @@ -205,9 +295,9 @@ func (r *VirtualMachineStateResource) Update(ctx context.Context, req resource.U DisableTlsValidation: r.provider.DisableTlsValidation.ValueBool(), } - vm, diag := apiclient.GetVm(ctx, hostConfig, data.ID.ValueString()) - if diag.HasError() { - resp.Diagnostics.Append(diag...) + vm, vmDiag := apiclient.GetVm(ctx, hostConfig, data.ID.ValueString()) + if vmDiag.HasError() { + resp.Diagnostics.Append(vmDiag...) return } if vm == nil { @@ -215,39 +305,72 @@ func (r *VirtualMachineStateResource) Update(ctx context.Context, req resource.U return } - // var proposedState string - // switch strings.ToLower(data.Operation.ValueString()) { - // case "start": - // proposedState = "running" - // case "stop": - // proposedState = "stopped" - // case "suspend": - // proposedState = "suspended" - // case "pause": - // proposedState = "paused" - // case "resume": - // proposedState = "running" - // case "restart": - // proposedState = "running" - // default: - // resp.Diagnostics.AddError("invalid desired_state", "invalid desired_state") - // return - // } - - result, diag := apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), r.GetOpState(data.Operation.ValueString())) - if diag.HasError() { - resp.Diagnostics.Append(diag...) - return - } - if !result { - resp.Diagnostics.AddError("error changing the machine state", "Could not change the machine "+vm.Name+" state to +"+data.Operation.ValueString()) + isInState, err := r.IsInDesiredState(vm, data.Operation.ValueString()) + if err != nil { + resp.Diagnostics.AddError("error changing the machine state", err.Error()) return } - data.Operation = types.StringValue(data.Operation.ValueString()) - data.CurrentState = types.StringValue(vm.State) + if !isInState && currentData.Operation.ValueString() != data.Operation.ValueString() { + result, stateDiag := apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), r.GetOpState(data.Operation.ValueString())) + if stateDiag.HasError() { + resp.Diagnostics.Append(stateDiag...) + return + } + if !result { + resp.Diagnostics.AddError("error changing the machine state", "Could not change the machine "+vm.Name+" state to +"+data.Operation.ValueString()) + return + } + + if data.EnsureState.ValueBool() { + if r.GetDesiredState(data.Operation.ValueString()) == "" { + resp.Diagnostics.AddError("error changing the machine state", "Could not determine the desired state") + return + } + + retries := 0 + for { + refreshedVm, refreshDiag := apiclient.GetVm(ctx, hostConfig, data.ID.ValueString()) + if refreshDiag.HasError() { + resp.Diagnostics.Append(refreshDiag...) + return + } + if refreshedVm.State == r.GetDesiredState(data.Operation.ValueString()) { + if refreshedVm.State == "running" { + echoHelloCommand := apimodels.PostScriptItem{ + Command: "echo 'I am running'", + VirtualMachineId: refreshedVm.ID, + } + + // Only breaking out of the loop if the script executes successfully + if _, execDiag := apiclient.ExecuteScript(ctx, hostConfig, echoHelloCommand); !execDiag.HasError() { + tflog.Info(ctx, "Machine "+vm.Name+" is running") + resp.Diagnostics = diag.Diagnostics{} + vm = refreshedVm + break + } + } else { + vm = refreshedVm + break + } + } + if retries >= maxRetries { + resp.Diagnostics.AddError("error changing the machine state", "Could not change the machine "+vm.Name+" state to +"+data.Operation.ValueString()) + return + } + retries++ + time.Sleep(waitBetweenRetries) + } + + data.CurrentState = types.StringValue(vm.State) + } else { + data.CurrentState = data.Operation + } + } else { + data.CurrentState = types.StringValue(vm.State) + } - resp.Diagnostics.Append(req.State.Set(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) if resp.Diagnostics.HasError() { return } @@ -256,6 +379,16 @@ func (r *VirtualMachineStateResource) Update(ctx context.Context, req resource.U func (r *VirtualMachineStateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { var data resource_models.VirtualMachineStateResourceModelV1 + telemetrySvc := telemetry.Get(ctx) + telemetryEvent := telemetry.NewTelemetryItem( + ctx, + r.provider.License.String(), + telemetry.EventVirtualMachineState, telemetry.ModeDestroy, + nil, + nil, + ) + telemetrySvc.TrackEvent(telemetryEvent) + // Read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &data)...) @@ -268,6 +401,77 @@ func (r *VirtualMachineStateResource) ImportState(ctx context.Context, req resou resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } +func (r *VirtualMachineStateResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { + v0Schema := schemas.VirtualMachineStateResourceSchemaV0 + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &v0Schema, + StateUpgrader: UpgradeStateToV1, + }, + } +} + +func UpgradeStateToV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + var priorStateData resource_models.VirtualMachineStateResourceModelV0 + resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...) + + if resp.Diagnostics.HasError() { + return + } + + upgradedStateData := resource_models.VirtualMachineStateResourceModelV1{ + Authenticator: priorStateData.Authenticator, + Host: priorStateData.Host, + Orchestrator: types.StringUnknown(), + ID: priorStateData.ID, + Operation: priorStateData.Operation, + CurrentState: priorStateData.CurrentState, + } + + println(fmt.Sprintf("Upgrading state from version %v", upgradedStateData)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &upgradedStateData)...) +} + +func (r *VirtualMachineStateResource) IsInDesiredState(vm *apimodels.VirtualMachine, desiredState string) (bool, error) { + switch strings.ToLower(desiredState) { + case "start": + if vm.State == "suspended" || + vm.State == "suspending" || + vm.State == "paused" || + vm.State == "pausing" { + return false, errors.New("cannot start a machine that is suspended, suspending, paused, or pausing") + } + return vm.State == "running" || vm.State == "starting", nil + case "stop": + return vm.State == "stopped" || vm.State == "stopping", nil + case "suspend": + if vm.State == "paused" || + vm.State == "pausing" || + vm.State == "stopped" || + vm.State == "stopping" { + return false, errors.New("cannot suspend a machine that is paused, pausing, stopped, or stopping") + } + return vm.State == "suspended" || vm.State == "suspending", nil + case "pause": + if vm.State == "suspended" || + vm.State == "suspending" || + vm.State == "stopped" || + vm.State == "stopping" { + return false, errors.New("cannot pause a machine that is suspended, suspending, stopped, or stopping") + } + return vm.State == "paused" || vm.State == "pausing", nil + case "resume": + if vm.State == "stopped" || + vm.State == "stopping" { + return false, errors.New("cannot resume a machine that is stopped or stopping") + } + return vm.State == "running" || vm.State == "paused" || vm.State == "suspended", nil + default: + return false, nil + } +} + func (r *VirtualMachineStateResource) GetOpState(value string) apiclient.MachineStateOp { switch strings.ToLower(value) { case "start": @@ -286,3 +490,34 @@ func (r *VirtualMachineStateResource) GetOpState(value string) apiclient.Machine return "" } } + +func (r *VirtualMachineStateResource) IsValidOperation(value string) bool { + switch strings.ToLower(value) { + case "start", "stop", "suspend", "pause", "resume", "restart": + return true + default: + return false + } +} + +func (r *VirtualMachineStateResource) GetDesiredState(operation string) string { + var desiredState string + switch strings.ToLower(operation) { + case "start": + desiredState = "running" + case "stop": + desiredState = "stopped" + case "suspend": + desiredState = "suspended" + case "pause": + desiredState = "paused" + case "resume": + desiredState = "running" + case "restart": + desiredState = "running" + default: + desiredState = "" + } + + return desiredState +} diff --git a/internal/virtualmachinestate/schemas/resource_schema_v1.go b/internal/virtualmachinestate/schemas/resource_schema_v1.go index 75d51a0..782ae07 100644 --- a/internal/virtualmachinestate/schemas/resource_schema_v1.go +++ b/internal/virtualmachinestate/schemas/resource_schema_v1.go @@ -61,5 +61,9 @@ var VirtualMachineStateResourceSchemaV1 = schema.Schema{ Computed: true, MarkdownDescription: "Virtual Machine current state", }, + "ensure_state": schema.BoolAttribute{ + MarkdownDescription: "Ensure the virtual machine is in the desired state", + Optional: true, + }, }, }