Skip to content

Change hypercore_vm group parameter to list of tags #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/resources/vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ locals {
}

resource "hypercore_vm" "empty-vm" {
group = "my-group"
tags = ["my-group"]
name = "empty-vm"
description = "some description"

Expand All @@ -49,7 +49,7 @@ data "hypercore_vms" "clone_source_vm" {
}

resource "hypercore_vm" "myvm" {
group = "my-group"
tags = ["my-group"]
name = local.vm_name
description = "some description"

Expand All @@ -71,7 +71,7 @@ resource "hypercore_vm" "myvm" {
}

resource "hypercore_vm" "import-from-smb" {
group = "my-group"
tags = ["my-group"]
name = "imported-vm"
description = "some description"

Expand Down Expand Up @@ -104,10 +104,10 @@ output "vm_uuid" {
- `affinity_strategy` (Object) VM node affinity. (see [below for nested schema](#nestedatt--affinity_strategy))
- `clone` (Object) Clone options if the VM is being created as a clone. The `source_vm_uuid` is the UUID of the VM used for cloning, <br>`user_data` and `meta_data` are used for the cloud init data. (see [below for nested schema](#nestedatt--clone))
- `description` (String) Description of this VM
- `group` (String) Group/tag to create this VM in
- `import` (Attributes) Options for importing a VM through a SMB server or some other HTTP location. <br>Use server, username, password for SMB or http_uri for some other HTTP location. Parameters path and file_name are always **required** (see [below for nested schema](#nestedatt--import))
- `memory` (Number) Memory (RAM) size in `MiB`: If the cloned VM was already created <br>and it's memory was modified, the cloned VM will be rebooted (either gracefully or forcefully)
- `snapshot_schedule_uuid` (String) UUID of the snapshot schedule to create automatic snapshots
- `tags` (List of String) List of tags to create this VM in
- `vcpu` (Number) Number of CPUs on this VM. If the cloned VM was already created and it's <br>`VCPU` was modified, the cloned VM will be rebooted (either gracefully or forcefully)

### Read-Only
Expand Down
6 changes: 3 additions & 3 deletions examples/resources/hypercore_vm/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ locals {
}

resource "hypercore_vm" "empty-vm" {
group = "my-group"
tags = ["my-group"]
name = "empty-vm"
description = "some description"

Expand All @@ -18,7 +18,7 @@ data "hypercore_vms" "clone_source_vm" {
}

resource "hypercore_vm" "myvm" {
group = "my-group"
tags = ["my-group"]
name = local.vm_name
description = "some description"

Expand All @@ -40,7 +40,7 @@ resource "hypercore_vm" "myvm" {
}

resource "hypercore_vm" "import-from-smb" {
group = "my-group"
tags = ["my-group"]
name = "imported-vm"
description = "some description"

Expand Down
59 changes: 45 additions & 14 deletions internal/provider/hypercore_vm_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-framework/attr"
Expand Down Expand Up @@ -39,7 +40,7 @@ type HypercoreVMResource struct {

// HypercoreVMResourceModel describes the resource data model.
type HypercoreVMResourceModel struct {
Group types.String `tfsdk:"group"`
Tags types.List `tfsdk:"tags"`
Name types.String `tfsdk:"name"`
Description types.String `tfsdk:"description"`
VCPU types.Int32 `tfsdk:"vcpu"`
Expand Down Expand Up @@ -91,8 +92,9 @@ HC_VM_SHUTDOWN_TIMEOUT can be changed via environ.
The provider will currently try to shutdown VM only before VM delete.`,

Attributes: map[string]schema.Attribute{
"group": schema.StringAttribute{
MarkdownDescription: "Group/tag to create this VM in",
"tags": schema.ListAttribute{
ElementType: types.StringType,
MarkdownDescription: "List of tags to create this VM in",
Optional: true,
},
"name": schema.StringAttribute{
Expand Down Expand Up @@ -240,23 +242,20 @@ func getVMStruct(data *HypercoreVMResourceModel, vmDescription *string, vmTags *
return vmStruct
}

func validateParameters(data *HypercoreVMResourceModel) (*string, *[]string) {
func validateParameters(data *HypercoreVMResourceModel, ctx context.Context) (*string, *[]string, diag.Diagnostics) {
var tags *[]string
var description *string

if data.Group.ValueString() == "" {
tags = nil
} else {
t := []string{data.Group.ValueString()}
tags = &t
}
tflog.Debug(ctx, "TTRT Loading tags")
diags := data.Tags.ElementsAs(ctx, &tags, false)
tflog.Debug(ctx, fmt.Sprintf("TTRT Tags: %v", tags))

if data.Description.ValueString() == "" {
description = nil
} else {
description = data.Description.ValueStringPointer()
}
return description, tags
return description, tags, diags
}
func isHTTPImport(data *HypercoreVMResourceModel) bool {
// Check if HTTP URI is being used for VM import
Expand Down Expand Up @@ -285,6 +284,7 @@ func (r *HypercoreVMResource) handleCloneLogic(data *HypercoreVMResourceModel, c
data.Id = types.StringValue(vmNew.UUID)
tflog.Info(ctx, fmt.Sprintf("Changed: %t, Was VM Rebooted: %t, Diff: %v", changed, vmWasRebooted, vmDiff))
}

func (r *HypercoreVMResource) handleImportFromSMBLogic(data *HypercoreVMResourceModel, ctx context.Context, resp *resource.CreateResponse, vmNew *utils.VM, path string, fileName string) {
smbServer, smbUsername, smbPassword := data.Import.Server.ValueString(), data.Import.Username.ValueString(), data.Import.Password.ValueString()
errorDiagnostic := utils.ValidateSMB(smbServer, smbUsername, smbPassword)
Expand All @@ -299,6 +299,7 @@ func (r *HypercoreVMResource) handleImportFromSMBLogic(data *HypercoreVMResource
data.Id = types.StringValue(vmNew.UUID)
tflog.Info(ctx, fmt.Sprintf("Changed: %t, Was VM Rebooted: %t, Diff: %v", changed, vmWasRebooted, vmDiff))
}

func (r *HypercoreVMResource) handleImportFromURILogic(data *HypercoreVMResourceModel, ctx context.Context, resp *resource.CreateResponse, vmNew *utils.VM, path string, fileName string) {
httpUri := data.Import.HTTPUri.ValueString()
errorDiagnostic := utils.ValidateHTTP(httpUri, path)
Expand All @@ -313,6 +314,7 @@ func (r *HypercoreVMResource) handleImportFromURILogic(data *HypercoreVMResource
data.Id = types.StringValue(vmNew.UUID)
tflog.Info(ctx, fmt.Sprintf("Changed: %t, Was VM Rebooted: %t, Diff: %v", changed, vmWasRebooted, vmDiff))
}

func (r *HypercoreVMResource) doCreateLogic(data *HypercoreVMResourceModel, ctx context.Context, resp *resource.CreateResponse, description *string, tags *[]string) {
vmNew := getVMStruct(data, description, tags)
// Chose which VM create logic we're going with (clone, import, from scratch)
Expand Down Expand Up @@ -354,7 +356,11 @@ func (r *HypercoreVMResource) Create(ctx context.Context, req resource.CreateReq
}

// Validate parameters TODO: Add other inputs here from schema if validation is needed
description, tags := validateParameters(&data)
description, tags, diags := validateParameters(&data, ctx)
if diags.HasError() {
resp.Diagnostics.Append(diags.Errors()...)
return
}

// Right now handles import or clone TODO: Add other VM create options here
r.doCreateLogic(&data, ctx, resp, description, tags)
Expand Down Expand Up @@ -398,7 +404,23 @@ func (r *HypercoreVMResource) Read(ctx context.Context, req resource.ReadRequest

data.Name = types.StringValue(utils.AnyToString(hc3_vm["name"]))
data.Description = types.StringValue(utils.AnyToString(hc3_vm["description"]))
// data.Group TODO - replace "group" string with "tags" list of strings

// Read tags from the hc3 api
// - hc3Tags = "tag1,tag2,..."
// - tfTags = hc3Tags -> ["tag1", "tag2", ...]
hc3Tags := utils.AnyToString(hc3_vm["tags"])
goTagsList := strings.Split(hc3Tags, ",")
tfTagsValues := make([]attr.Value, len(goTagsList))
for i, tag := range goTagsList {
tfTagsValues[i] = types.StringValue(tag)
}

var diags diag.Diagnostics
data.Tags, diags = types.ListValue(types.StringType, tfTagsValues)
if diags.HasError() {
resp.Diagnostics.Append(diags.Errors()...)
return
}

// NOTE: power state not needed here anymore because of the hypercore_vm_power_state resource
// hc3_power_state := utils.AnyToString(hc3_vm["state"])
Expand Down Expand Up @@ -494,7 +516,16 @@ func (r *HypercoreVMResource) Update(ctx context.Context, req resource.UpdateReq
updatePayload["affinityStrategy"] = affinityStrategy
}

taskTag, _ := restClient.UpdateRecord( /**/
// update tags: ["tag1", "tag2", ...] -> "tag1,tag2,..."
var tagsList []string
diags := data.Tags.ElementsAs(ctx, &tagsList, false)
if diags.HasError() {
resp.Diagnostics.Append(diags.Errors()...)
return
}
updatePayload["tags"] = utils.TagsListToCommaString(tagsList)

taskTag, _ := restClient.UpdateRecord(
fmt.Sprintf("/rest/v1/VirDomain/%s", vm_uuid),
updatePayload,
-1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func TestAccHypercoreVMResourceSnapshotSchedule(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("hypercore_vm.test", "description", "testtf-vm-description"),
resource.TestCheckResourceAttr("hypercore_vm.test", "memory", "4096"),
resource.TestCheckResourceAttr("hypercore_vm.test", "group", "testtf"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.#", "1"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.0", "testtf"),
resource.TestCheckNoResourceAttr("hypercore_vm.test", "clone"),
resource.TestCheckResourceAttr("hypercore_vm.test", "name", "testtf-vm"),
resource.TestCheckResourceAttr("hypercore_vm.test", "vcpu", "4"),
Expand Down Expand Up @@ -62,7 +63,8 @@ func TestAccHypercoreVMResourceSnapshotSchedule(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("hypercore_vm.test", "name", "testtf-vm"),
resource.TestCheckResourceAttr("hypercore_vm.test", "description", "testtf-vm-description"),
resource.TestCheckResourceAttr("hypercore_vm.test", "group", "testtf"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.#", "1"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.1", "testtf"),
resource.TestCheckNoResourceAttr("hypercore_vm.test", "clone"),
resource.TestCheckResourceAttr("hypercore_vm.test", "vcpu", "4"),
resource.TestCheckResourceAttr("hypercore_vm.test", "memory", "4096"),
Expand Down Expand Up @@ -92,7 +94,7 @@ func testConfig_NoSnapshotScheduleUUID(vm_name string) string {
return fmt.Sprintf(`
resource "hypercore_vm" "test" {
name = %[1]q
group = "testtf"
tags = ["testtf"]
vcpu = 4
memory = 4096
description = "testtf-vm-description"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ func TestAccHypercoreVMResourceClone(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("hypercore_vm.test", "description", "testtf-vm-description"),
resource.TestCheckResourceAttr("hypercore_vm.test", "memory", "4096"),
resource.TestCheckResourceAttr("hypercore_vm.test", "group", "testtf"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.#", "2"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.0", "testtf-tag-1"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.1", "testtf-tag-2"),
resource.TestCheckResourceAttr("hypercore_vm.test", "clone.meta_data", ""),
resource.TestCheckResourceAttr("hypercore_vm.test", "clone.user_data", ""),
resource.TestCheckResourceAttr("hypercore_vm.test", "clone.source_vm_uuid", source_vm_uuid),
Expand Down Expand Up @@ -69,7 +71,9 @@ func TestAccHypercoreVMResourceClone(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("hypercore_vm.test", "name", "testtf-vm"),
resource.TestCheckResourceAttr("hypercore_vm.test", "description", "testtf-vm-description"),
resource.TestCheckResourceAttr("hypercore_vm.test", "group", "testtf"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.#", "2"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.0", "testtf-tag-1"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.1", "testtf-tag-2"),
resource.TestCheckResourceAttr("hypercore_vm.test", "vcpu", "4"),
resource.TestCheckResourceAttr("hypercore_vm.test", "memory", "4096"),
// resource.TestCheckResourceAttr("hypercore_vm.test", "power_state", requested_power_state),
Expand Down Expand Up @@ -108,7 +112,10 @@ func testAccHypercoreVMResourceCloneConfig(vm_name string) string {
return fmt.Sprintf(`
resource "hypercore_vm" "test" {
name = %[1]q
group = "testtf"
tags = [
"testtf-tag-1",
"testtf-tag-2",
]
vcpu = 4
memory = 4096
description = "testtf-vm-description"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func TestAccHypercoreVMResourceImport(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("hypercore_vm.test", "description", "imported-vm"),
resource.TestCheckResourceAttr("hypercore_vm.test", "memory", "4096"),
resource.TestCheckResourceAttr("hypercore_vm.test", "group", "Xlabintegrationtest"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.#", "1"),
resource.TestCheckResourceAttr("hypercore_vm.test", "tags.0", "Xlabintegrationtest"),
resource.TestCheckResourceAttr("hypercore_vm.test", "name", "imported_vm_integration"),
resource.TestCheckResourceAttr("hypercore_vm.test", "vcpu", "4"),
),
Expand All @@ -34,7 +35,7 @@ func testAccHypercoreVMResourceImportConfig(vm_name string) string {
return fmt.Sprintf(`
resource "hypercore_vm" "test" {
name = %[1]q
group = "Xlabintegrationtest"
tags = ["Xlabintegrationtest"]
vcpu = 4
memory = 4096
description = "imported-vm"
Expand Down
2 changes: 1 addition & 1 deletion internal/utils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func jsonObjectToTaskTag(jsonObj any) *TaskTag {
return taskTag
}

func tagsListToCommaString(tags []string) string {
func TagsListToCommaString(tags []string) string {
tagsHyp := ""
for _, tag := range tags {
tagsHyp += tag + ","
Expand Down
4 changes: 2 additions & 2 deletions internal/utils/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ func (vc *VM) BuildUpdatePayload(changedParams map[string]bool) map[string]any {
updatePayload["description"] = *vc.description
}
if changed, ok := changedParams["tags"]; ok && changed {
updatePayload["tags"] = tagsListToCommaString(*vc.tags)
updatePayload["tags"] = TagsListToCommaString(*vc.tags)
}
if changed, ok := changedParams["memory"]; ok && changed {
vcMemoryBytes := *vc.memory * 1024 * 1024 // MB to B
Expand Down Expand Up @@ -505,7 +505,7 @@ func (vc *VM) BuildImportTemplate() map[string]any {
importTemplate["description"] = *vc.description
}
if vc.tags != nil {
importTemplate["tags"] = tagsListToCommaString(*vc.tags)
importTemplate["tags"] = TagsListToCommaString(*vc.tags)
}
if vc.memory != nil {
vcMemoryBytes := *vc.memory * 1024 * 1024 // MB to B
Expand Down
46 changes: 17 additions & 29 deletions local/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,26 @@ terraform {
}

locals {
vm_name = "testtf-remove-running"
src_vm_name = "testtf-src-empty"
vm_name = "testtf-ana-tags-2"
src_vm_name = "ana"
}

provider "hypercore" {}

data "hypercore_vms" "no-such-vm" {
name = "no-such-vm"
data "hypercore_vms" "src_empty" {
name = local.src_vm_name
}

# resource "hypercore_vm" "vm_on" {
# group = "testtf"
# name = local.vm_name
# description = "VM created from scratch"
# vcpu = 1
# memory = 1234 # MiB

# # clone = {
# # source_vm_uuid = data.hypercore_vm.src_empty.vms.0.uuid
# # meta_data = ""
# # user_data = ""
# # }
# }

# resource "hypercore_vm_power_state" "vm_on" {
# vm_uuid = hypercore_vm.vm_on.id
# state = "SHUTOFF" // RUNNING SHUTOFF
# }

# output "vm_on_uuid" {
# value = hypercore_vm.vm_on.id
# }
# output "power_state" {
# value = hypercore_vm_power_state.vm_on.state
# }
resource "hypercore_vm" "vm_on" {
tags = ["ana-tftag2"]
name = local.vm_name
description = "VM created from scratch"
vcpu = 1
memory = 1234 # MiB

clone = {
source_vm_uuid = data.hypercore_vms.src_empty.vms.0.uuid
meta_data = ""
user_data = ""
}
}
Loading