Skip to content

Using user_data on resource.nutanix_virtual_machine yields immediate diff after initial apply #69

Closed
@rxacevedo

Description

Describe the bug

When using guest_customization_cloud_init_user_data to bootstrap a virtual machine, a subsequent plan after apply yields a diff because a CDROM device is attached to the virtual machine as a means of supplying user_data to the host. This causes a few issues.

Expected behavior

user_data is provisioned onto the virtual machine in such a way that it does not create a diff on the plan.

Logs

Plan/diff:

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  ~ nutanix_virtual_machine.node
      disk_list.2.device_properties.0.disk_address.device_index: "3" => "2"


Plan: 0 to add, 1 to change, 0 to destroy.

------------------------------------------------------------------------

This might be fine normally, except two things:

  1. Devices attached using IDE for the adapter_type cannot be removed while the VM is powered on (I got this from the Prism Central UI).
  2. resourceVirtualMachineUpdate calls changePowerState indiscriminately on any VM attribute update (beginning and end of function declaration), even something such as changing the hostname on an existing VM.

Now, here's where things start to get weird - let's say I have the following disk_list:

curl -X GET \
          --silent \
          --insecure \
          --header "Content-Type: application/json" \
          --header "Accept: application/json" \
          --header "Authorization: Basic authBase64==" \
          https://prism.mydomain.tld:9440/api/nutanix/v3/vms/3db764c8-6d06-4f51-939a-dc42b1fc24c8 \
          | gron \
          | grep -E '\.spec\.resources\.disk_list\[[0-9]\]\.device_properties\.(device_type|disk_address\.device_index)'
json.spec.resources.disk_list[0].device_properties.device_type = "DISK";
json.spec.resources.disk_list[0].device_properties.disk_address.device_index = 0;
json.spec.resources.disk_list[1].device_properties.device_type = "DISK";
json.spec.resources.disk_list[1].device_properties.disk_address.device_index = 1;
json.spec.resources.disk_list[2].device_properties.device_type = "CDROM";
json.spec.resources.disk_list[2].device_properties.disk_address.device_index = 3;

And I then add two more disks (so, index 0 and 1 are in my config, Nutanix (server) has added a CDROM device (implicitly) to my VM to inject user_data, and this device is located at index 3). So my new disk_list looks like:

  disk_list = [
    {
      data_source_reference = [{
          kind = "image"
          uuid = "${data.terraform_remote_state.images.centos_image}"
      }]

      device_properties = [{

        disk_address = {
          device_index = 0
          adapter_type = "SCSI"
        }
        device_type = "DISK"

      }]
    },
    {
      device_properties = [{

        disk_address = {
          device_index = 1
          adapter_type = "SCSI"
        }
        device_type = "DISK"

      }]
      disk_size_mib   = 100000
    },
    {
      device_properties = [{

        disk_address = {
          device_index = 2
          adapter_type = "SCSI"
        }
        device_type = "DISK"

      }]
      disk_size_mib   = 100000
    },
    {
      device_properties = [{

        disk_address = {
          device_index = 3
          adapter_type = "SCSI"
        }
        device_type = "DISK"

      }]
      disk_size_mib   = 100000
    }
  ]

This yields the following plan:

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  ~ nutanix_virtual_machine.node
      disk_list.#:                                               "3" => "4"
      disk_list.2.device_properties.0.device_type:               "CDROM" => "DISK"
      disk_list.2.device_properties.0.disk_address.adapter_type: "IDE" => "SCSI"
      disk_list.2.device_properties.0.disk_address.device_index: "3" => "2"
      disk_list.2.disk_size_mib:                                 "1" => "100000"
      disk_list.3.data_source_reference.%:                       "" => <computed>
      disk_list.3.device_properties.#:                           "0" => "1"
      disk_list.3.device_properties.0.device_type:               "" => "DISK"
      disk_list.3.device_properties.0.disk_address.%:            "0" => "2"
      disk_list.3.device_properties.0.disk_address.adapter_type: "" => "SCSI"
      disk_list.3.device_properties.0.disk_address.device_index: "" => "3"
      disk_list.3.disk_size_mib:                                 "" => "100000"
      disk_list.3.volume_group_reference.%:                      "" => <computed>


Plan: 0 to add, 1 to change, 0 to destroy.

This breaks because Nutanix tries to resize the CDROM device:

Error: Error applying plan:

1 error occurred:
	* nutanix_virtual_machine.node: 1 error occurred:
	* nutanix_virtual_machine.node: error waiting for vm (3db764c8-6d06-4f51-939a-dc42b1fc24c8) to update: error_detail: INTERNAL_ERROR: error_code: 27
error_detail: "NotSupported: Cannot resize cdrom at ide.3 of VM 3db764c8-6d06-4f51-939a-dc42b1fc24c8.", progress_message: update_vm_intentful





Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with
any resources that successfully completed. Please address the error
above and apply again to incrementally change your infrastructure.

Furthermore, this actually prevents subsequent plans from succeeding, returning the same error. This is because:

curl -X GET \
          --silent \
          --insecure \
          --header "Content-Type: application/json" \
          --header "Accept: application/json" \
          --header "Authorization: Basic authBase64==" \
          https://prism.mydomain.tld:9440/api/nutanix/v3/vms/3db764c8-6d06-4f51-939a-dc42b1fc24c8 | jq '.status.message_list'
[
  {
    "message": "error_code: 27\nerror_detail: \"NotSupported: Cannot resize cdrom at ide.3 of VM 3db764c8-6d06-4f51-939a-dc42b1fc24c8.\"",
    "reason": "INTERNAL_ERROR"
  }
]

You either have to destroy the VM (terraform destroy -refresh=false) or clear the error state on the object in the API by posting a new spec (I have not tried this).

Versions (please complete the following information):

  • linux_amd64
  • Terraform v0.11.14
  • Nutanix Cluster (Prism Element / AOS) Version 5.10.3.2 LTS
  • Nutanix Prism Central Version 5.10.3
  • Terraform provider version (compiled locally off of master / 5fd531b)

Additional context
I can trick the provider and match what the API returns so that I don't get a diff in the plan:

{
      device_properties = [{

        disk_address = {
          device_index = 3
          adapter_type = "IDE"
        }
        device_type = "CDROM"

}

But this feels dirty, and requires that the user understand the implementation details of how user_data is injected into the VM. I'm not sure how many users would do this before just submitting a support ticket.

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions