Skip to content
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

r/virtual_machine: add vApp properties #303

Merged
merged 23 commits into from
Jan 13, 2018
Merged

r/virtual_machine: add vApp properties #303

merged 23 commits into from
Jan 13, 2018

Conversation

rowanjacobs
Copy link
Contributor

In order to support configurable vApp properties on VMs cloned from an OVF (#243), we have added a vapp subresource with a properties key-value map. In order to run acceptance tests, the template needs to have vApp properties with the keys example_key and another_key, whose initial and default values should be blank.

Note that we haven't been able to successfully implement zeroing out a property. This seems to likely be an issue with the vSphere API or client. But we think this should not block adding this feature in the mean time.

cc @mcwumbly @desmondrawls @genevievelesperance @evanfarrar

rowanjacobs and others added 3 commits December 8, 2017 12:24
- Currently removing vApp properties doesn't work, which seems to
potentially be a vSphere API or client issue.

Signed-off-by: Rowan Jacobs <[email protected]>
@vancluever vancluever added the enhancement Type: Enhancement label Dec 10, 2017
@rowanjacobs
Copy link
Contributor Author

rowanjacobs commented Dec 13, 2017

@charandas I've verified that setting vapp.properties.guestinfo.coreos.config.data in coreos.config.data when cloning a VM sets the vApp property on the VM cloned from the CoreOS OVA. Are you using the clone setting? If not, how are you building the VM from the CoreOS OVA?

Here's what the VM configuration I'm using looks like:

resource "vsphere_virtual_machine" "vm" {
  name             = "terraform-test"
  resource_pool_id = "${data.vsphere_resource_pool.pool.id}"
  datastore_id     = "${data.vsphere_datastore.datastore.id}"

  num_cpus = 2
  memory   = 2048
  guest_id = "${data.vsphere_virtual_machine.template.guest_id}"

  network_interface {
    network_id   = "${data.vsphere_network.network.id}"
    adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}"
  }

  disk {
    name = "terraform-test.vmdk"
    size = "${data.vsphere_virtual_machine.template.disks.0.size}"
  }

  vapp {
    properties {
      "guestinfo.hostname" = "khaleesi-terraform-test"
      "guestinfo.coreos.config.data" = "some guest data"
    }
  }

  clone {
    template_uuid = "${data.vsphere_virtual_machine.template.id}"
  }
}

@rowanjacobs
Copy link
Contributor Author

I'm testing against vSphere 5.5.

@charandas
Copy link

I had missed the file changes in vsphere/virtual_machine_config_structure.go when building last night. Sorry for the false alarm. It works now. The reason I reverted to manually copying changes from your fork, is because of the following:

# would not build for me, with go 1.9.2
❯ go get -u github.com/rowanjacobs/terraform-provider-vsphere
# github.com/rowanjacobs/terraform-provider-vsphere
serve/gopath/src/github.com/rowanjacobs/terraform-provider-vsphere/main.go:10:15: cannot use vsphere.Provider (type func() "github.com/terraform-providers/terraform-provider-vsphere/vendor/github.com/hashicorp/terraform/terraform".ResourceProvider) as type "github.com/rowanjacobs/terraform-provider-vsphere/vendor/github.com/hashicorp/terraform/plugin".ProviderFunc in field value

# I basically tried to get it to build by copying over these changes
# into  github.com/terraform-providers/terraform-provider-vsphere in my gopath

@vancluever
Copy link
Contributor

Hey @rowanjacobs, just wanted to say I have been watching this and it looks promising - I will give this a much closer look later this week!

One thing that I did notice when I looked earlier - does this support removal of the vApp properties? It looks like another attribute needs to be set to make this happen (vAppConfigRemoved). I'm kind of on the fence of how I would prefer this to be implemented as from what I remember, wiping the config removes them for good, and I don't know how easy they would be to re-construct after the fact. I'm wondering if maybe we can support that and source the proper settings from somewhere else, like a template?

In any case - will look later this week as mentioned.

@tevaum
Copy link

tevaum commented Dec 14, 2017

Just want to thank you guys for working on this stuff... I use terraform with vsphere to deploy some images that doesn't support guest customization and this is the only way to go for me, so I'm very excited seing you guys working on it!

@vancluever vancluever self-assigned this Dec 15, 2017
@vancluever
Copy link
Contributor

Hey @rowanjacobs, I'm about to give this a spin to see how it goes. I'll also see if I can solve the config zeroing problem as well.

Re: What I mentioned about removing vApp config, I don't think we need to concern ourselves with this right now 100%. A VM's OVF configuration is a complex thing that I think that we might not even want to touch with TF, treating vApp and OVF template configuration as kind of a template that that is out of our scope to manage, but rather something that would be up to something like Packer to configure as part of the image building process.

Anyway, will update soon!

@vancluever
Copy link
Contributor

Hey @rowanjacobs, alright, finished checking, looks like there is an issue here.

Here is the vapp snippet of my config using the CoreOS OVA:

  vapp {
    properties {
      "guestinfo.hostname"                        = "${var.vm_name}.foobar.local"
      "guestinfo.interface.0.name"                = "ens192"
      "guestinfo.interface.0.ip.0.address"        = "172.16.1.150/24"
      "guestinfo.interface.0.route.0.gateway"     = "172.16.1.1"
      "guestinfo.interface.0.route.0.destination" = "0.0.0.0/0"
      "guestinfo.dns.server.0"                    = "172.16.1.1"
    }
  }

This is not writing a correct configuration. I think the best way to illustrate it is via how it seems to be overwriting some of the config parameters. Here is an example of the first three from the MOB.

The first parameter - guestinfo.hostname - seems to be writing correctly:

Before
hostname-before

After
hostname-after

However, the next couple get odd looking. This is the next parameter, the full cloud-config data, which should have not been touched. However if you look at the second picture here closely, you'll see that the key has been overwritten with the guestinfo.dns.server.0 key, which is empty:

Before
cloud-config-before

After
cloud-config-after

Finally, the last example overwrites the cloud-config URL property with the key and value for guestinfo.hostname.

Before
url-before

After
url-after

Finally, there is a non-empty diff on the next plan:

  ~ vsphere_virtual_machine.foo
      vapp.0.properties.guestinfo.dns.server.0:                    "" => "172.16.1.1"
      vapp.0.properties.guestinfo.interface.0.ip.0.address:        "" => "172.16.1.150/24"
      vapp.0.properties.guestinfo.interface.0.route.0.destination: "" => "0.0.0.0/0"
      vapp.0.properties.guestinfo.interface.0.route.0.gateway:     "" => "172.16.1.1"

Looking deeper, I'm guessing there has to be something up with the way the VAppPropertySpec/ArrayUpdateSpec is being built to have these kinds of conflicts in them and it needs to be fixed.

Also thinking about this a bit more, and given the potential for corruption here: I think we should be a bit more absolute when working with the vApp settings - we should only allow keys that are already in the vApp configuration (ie: are already valid parameters in the template). Also, we should make sure that we are saving non-zero values for any keys that are a part of the vApp configuration but are not defined in Terraform config (this way they can be properly zeroed out on the next run).

I'll add some notes in the code in the next couple of hours.

//
// This sub-resource allows the customization of vApp properties
// on cloned VMs.
func VAppSubresourceSchema() map[string]*schema.Schema {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could probably just put this directly into schemaVirtualMachineConfigSpec as it's so small, along with the vapp key. I'm almost tempted to also just say we flatten it and name it vapp_properties, but we might need to add more keys at a later time.

@@ -836,6 +836,45 @@ func TestAccResourceVSphereVirtualMachine(t *testing.T) {
},
},
},
{
"clone with vapp properties",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts on testing this while enforcing present keys only - we might need to test this against a CoreOS template or something else well known, and work on setting well-known cloud-config keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've started testing against the CoreOS OVA instead of against a custom-built template.

Info: &types.VAppPropertyInfo{
Key: int32(i) + 1,
Id: k,
Value: "",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed this here - the original comment said that there was problems zeroing out a property - is this code non-functional, or were you referring to something else (maybe deleting a key completely)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issues with zeroing out properties are due to the fact that at least in version 5.5, the vSphere API doesn't actually do anything when it receives an empty string for a vApp property value. We've gotten around this by changing the empty string to " ", and the corresponding portion of the new code should work properly now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! This is great info actually as I can now update some of my code on the work I was doing on govc. Good find! 👍

}
vac := make(map[string]interface{})
vApp := d.Get("vapp").([]interface{})
if len(vApp) > 0 && vApp[0] != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in the comments before the change request, I think we need to add config values that have non-zero values set on them as well, that way, on the next apply, they get zeroed out and any defaults apply. That way, removing a key always removes the corresponding value from config, even if someone adds OOB changes.

_, isOld := oldMap[k]
_, isNew := newMap[k]

if isNew {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annotating as I'm guessing it would be around here - just noting that we will want to somehow check to ensure the key is actually present in the vApp config before working with it. This may be here, or in another part of the function, or in another function altogether (maybe it actually is part of diff customization so that we can keep the method signature clean here and as consistent as possible with the rest of the expander/flattener logic).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it changes the signature, I decided to check if the key is present in this function because it does clean up the logic of the function significantly, now that we no longer have to merge two different lists of vApp properties and can rely on the canonical list of properties from the template.

// We track changes to keys to determine if any have been removed from
// configuration - if they have, we add them with an empty value to ensure
// they are removed from vAppConfig on the update.
func expandVAppConfig(d *schema.ResourceData) types.BaseVmConfigSpec {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BaseVmConfigSpec is an interface, but we aren't returning anything else other than VmConfigSpec so this is abstracting things in a way that they don't need to be abstracted. Can this be changed to return a *types.VmConfigSpec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@rowanjacobs
Copy link
Contributor Author

Thanks for getting back to us with your feedback! Unfortunately I'm about to go on vacation and the rest of the team will be short-staffed next week, so it's unlikely we'll be able to make required changes until the beginning of January. We'll definitely get to it when we can!

@vancluever
Copy link
Contributor

Hey @rowanjacobs - just FYI I will probably be reviewing this next week.

Thanks!

desmondrawls and others added 2 commits January 3, 2018 09:56
- so that terraform always knows about all keys in vApp properties

Signed-off-by: Rowan Jacobs <[email protected]>
Signed-off-by: Desmond Pompa Alarcon Rawls <[email protected]>
@vancluever
Copy link
Contributor

Hey all, looks like this is coming along great. A few things are still outstanding, save maybe documentation (still haven't looked at this part yet):

  • Changing vApp properties should require a restart - this is to ensure that they will be sent along on the next boot (this will allow things like reconfiguration of IP addresses in CoreOS). You can do this by setting the reboot_required computed attribute when values have changed (there is a getWithRestart helper as well).
  • While it looks like you can no longer add configuration keys to the vApp properties configuration, the operation still goes through in Terraform, and creates a perpetual diff as such. We need to ensure that these are blocked in the plan.

Do you know when you all will be able to work on these items? If it's going to be too much later, I can actually get these over the line for you all. We are actually doing some work here internally that could benefit from this and I'd be happy to finish it up. But let me know either way 🙂

@rowanjacobs
Copy link
Contributor Author

I can definitely take care of the first change. I'll see if I can get to the second one this week, and if not then I'll pass it back to you since I'm going to be unavailable for a while starting next week.

@vancluever
Copy link
Contributor

Fair enough! Will keep an eye out 👍

This is looking very good by the way! I was excited to see a CoreOS VM come up purely from guestinfo!

@rowanjacobs
Copy link
Contributor Author

Thanks! Regarding the second change: should the plan fail and provide an error message indicating that you can't add new vApp property keys? Or should the new keys be silently removed from the plan? (Or, for my own reference, do you have an existing example somewhere in this or another provider that implements this in a preferred way?)

@vancluever
Copy link
Contributor

Hey @rowanjacobs sorry for missing this.

It should be set up so that TF explicitly fails with an error. We don't want silent behaviour as that gives the wrong impression to the user, and the bad keys risk ending up propagating to state, which would be bad as well.

@rowanjacobs
Copy link
Contributor Author

We were thinking of returning an error from expandVAppConfig, but none of the other expand functions, nor expandVirtualMachineConfigSpec, do this, so we'd have to change several function signatures. We can go ahead and add this to the PR; was this what you had in mind, or did you have another way you prefer of getting that error? We looked into adding a ValidatorFunc but it seems like that function wouldn't be able to access client or template information, and so wouldn't have access to the original list of vApp properties on the template.

Genevieve LEsperance and others added 2 commits January 11, 2018 16:07
Signed-off-by: Genevieve LEsperance <[email protected]>
@rowanjacobs
Copy link
Contributor Author

rowanjacobs commented Jan 12, 2018

Here's an example of what the error looks like:

--- FAIL: TestAccResourceVSphereVirtualMachine_Basic (104.52s)
    --- FAIL: TestAccResourceVSphereVirtualMachine_Basic/clone_with_vapp_properties (104.52s)
    	testing.go:513: Step 2 error: Error applying: 1 error(s) occurred:

* vsphere_virtual_machine.vm: 1 error(s) occurred:

* vsphere_virtual_machine.vm: error expanding virtual machine config spec: Unsupported vApp properties in vapp.properties: [fake.property]

@vancluever
Copy link
Contributor

Hey @rowanjacobs, the error handling looks good for now. I want to maybe move this over to CustomizeDiff in the long run both so that the validation can be done at plan-time, and also so that we keep the expander/flattener interface clean, but that can be done later.

I'm reviewing this today - if it looks good, is it ready to merge?

Thanks!

@rowanjacobs
Copy link
Contributor Author

rowanjacobs commented Jan 12, 2018

Yes, it should be ready to merge. Thanks!

@vancluever
Copy link
Contributor

vancluever commented Jan 12, 2018

Outstanding checklist:

  • vApp property change requires a restart
  • Block changes to unknown properties (create)
  • ^ Does not look like VM is rolled back on clone. Looking at the code for that
  • Block changes to unknown properties (update)
  • Block changes to unknown properties (non-clones)
  • ^ This crashes right now - working on a fix
  • ^^ And another fix for blocking updates when no vApp config exists
  • Check docs
  • ^ No docs - adding docs
  • Check value zeroing functionality
  • Check tests
  • ^ Add tests for the following error scenarios:
  • ^^ Unknown properties (create)
  • ^^ Unknown properties (update)
  • ^^ vApp props on non-clones
  • ^^ Attempt to add vApp props on non-clones after creation
  • ^^ Attempt to add vApp props on clones after creation (that lack vApp properties)
  • Run VM suite

The interface for expandVirtualMachineConfigSpec has changed to
accommodate errors during config expansion (namely to check for invalid
vApp properties). However, as the expansion happens post-clone but
before the initial configuration, errors risk putting the Terraform
state in an irrecoverable situation should the configuration fail here.

Moved the rollback functionality that we have for the post-clone
reconfigure into a helper function so that we can leverage it here.

Also, changed the wording of the error message so that it's a bit
friendlier to the average user.
This blocks vApp properties from being set on virtual machines created
from scratch. As this support is targeted towards VMs that haven been
cloned from a template that has been imported from OVF/OVA, we expect
the vApp configuration to exist (and further to that we don't support
setting unknown keys). This basically means that it's impossible to set
vApp properties on new virtual machines with this resource, or it should
be.

This commit corrects a few crash scenarios that are happening when
trying to use this block on new virtual machines as it stands. It also
blocks situations where someone tries to add configuration after the
fact or on a virtual machine where no vApp configuration exists, which
would be possible on both the from-scratch and clone paths.
* Unexported the schema helper for vApp configurations.
* Fixed a hit on the standard "else" linter.
Also corrected some things that were actually valid for extra_config
already, but had not been updated since discovering that vApp
properties/OVF environment was blocking the ability to use guestinfo.
We have a different Linux template (Ubuntu) for base testing, I don't
want to mess with that specifically. As such, I've added a variable
exclusively for CoreOS templates and set the vApp tests to use those.
The tests were not working with our lab as the seemed to be expecting
DHCP for network configuration. Added in the appropriate configuration
options that are set up for the rest of the clone tests, with the
customization options just moved to their appropriate cloud-config
key/value settings. This may need to be changed if CoreOS ever drops
support for the old cloud-init guestinfo keys in favour of Ignition.
Final update of the vApp property tests, adding tests for all determined
failure scenarios to make sure we are trapping errors correctly.

This should be the last commit before merge.
@vancluever
Copy link
Contributor

vancluever commented Jan 13, 2018

FYI - this LGTM, I've made several updates and resolved the merge conflicts and I think we are in a good place now. I'm just waiting for the whole VM test suite to finish and then I'm merging!

Great job and hats off to everyone at @pivotal and @pivotal-cf that made this happen!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement Type: Enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants