2015-08-20 19:18:41 +00:00
|
|
|
package google
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
|
|
"google.golang.org/api/compute/v1"
|
|
|
|
)
|
|
|
|
|
Add Alias IP and Guest Accelerator support to Instance Templates (#639)
* Move AliasIpRange helpers into utils
To reflect the fact they'll be used by multiple resources.
* Pass Config to build helpers, not meta
It's the only thing meta is used for.
* Refactor getNetwork util methods to return early for the happy path.
* Update compute APIs
compute.Instance.MinCpuPlatform is now GA.
* Fix panic in TestComputeInstanceMigrateState
This seemed to be a pre-existing issue, i.e. I could repro it in master.
--- FAIL: TestComputeInstanceMigrateState (0.00s)
panic: interface conversion: interface {} is nil, not *google.Config [recovered]
panic: interface conversion: interface {} is nil, not *google.Config
goroutine 85 [running]:
testing.tRunner.func1(0xc4205d60f0)
/usr/local/Cellar/go/1.9.1/libexec/src/testing/testing.go:711 +0x2d2
panic(0x203acc0, 0xc4205d2080)
/usr/local/Cellar/go/1.9.1/libexec/src/runtime/panic.go:491 +0x283
github.com/terraform-providers/terraform-provider-google/google.migrateStateV3toV4(0xc4205f2000, 0x0, 0x0, 0x0, 0x48, 0xc4205f2000)
/Users/negz/control/go/src/github.com/terraform-providers/terraform-provider-google/google/resource_compute_instance_migrate.go:182 +0x2405
github.com/terraform-providers/terraform-provider-google/google.resourceComputeInstanceMigrateState(0x2, 0xc4205f2000, 0x0, 0x0, 0x0, 0x0, 0xe0000000000)
/Users/negz/control/go/src/github.com/terraform-providers/terraform-provider-google/google/resource_compute_instance_migrate.go:48 +0x21a
github.com/terraform-providers/terraform-provider-google/google.runInstanceMigrateTest(0xc4205d60f0, 0x2260816, 0x8, 0x227d23a, 0x20, 0x2, 0xc4205ec0f0, 0xc4205ec120, 0x0,
0x0)
/Users/negz/control/go/src/github.com/terraform-providers/terraform-provider-google/google/resource_compute_instance_migrate_test.go:803 +0xc1
github.com/terraform-providers/terraform-provider-google/google.TestComputeInstanceMigrateState(0xc4205d60f0)
/Users/negz/control/go/src/github.com/terraform-providers/terraform-provider-google/google/resource_compute_instance_migrate_test.go:71 +0xc84
testing.tRunner(0xc4205d60f0, 0x22d81c0)
/usr/local/Cellar/go/1.9.1/libexec/src/testing/testing.go:746 +0xd0
created by testing.(*T).Run
/usr/local/Cellar/go/1.9.1/libexec/src/testing/testing.go:789 +0x2de
FAIL github.com/terraform-providers/terraform-provider-google/google 0.035s
* Use only the v1 API for resource_compute_instance
Alias IP ranges, Accelerators, and min CPU platform are now GA.
* Move common instance code into utils.go
Methods used by both resource_compute_instance and
resource_compute_instance_template are currently spread between their respective
files, and utils.go.
This commit moves them all into utils.go for the sake of consistency. It may be
worth considering an instance_common.go file or similar.
* Unify compute_instance and compute_instance_template network_interface and service_account code
This has the side effect of enabling Alias IP range support for
compute_instance_templates.
* Add tests for compute instance template Alias IP ranges
* Mark instance template region as computed
We compute it from the subnet its network interfaces are in. Note this
is not new behaviour - I believe it was erroneously missing the computed
flag.
* Support guest accelerators for instance templates
Since most of the code is already there.
* Add a test for using 'address' rather than 'network_ip' for instance templates
* Don't mark assigned_nat_ip as deprecated
* Remove network_interface schema fields that don't make sense for a compute instance template
* Add newline after count in instance template docs
* Don't try to dedupe guest accelerator expansion code
The API calls to Google to create guest accelerators take different values
for instances and instance templates. Instance templates don't have a zone
and can thus *only* be passed a guest accelerator name.
* Use ParseNetworkFieldValue instead of getNetworkLink
* Add support for parsing regional fields, and subnetworks specifically
Currently unused because subnetworks may have a separate project from that
of the instance using them, which complicates looking up the project field.
* Fall back to provider region when parsing regional field values
Also slightly refactors getXFromSchema field helper functions for readability.
* Revert to assigned_nat_ip in compute instance docs
* Add beta scaffolding to compute instance and compute instance template
Note these resources don't currently use beta features - this is futureproofing.
* Fix indentation in comment about instance template alias IP ranges
* Consolidate metadata helper functions in metadata.go
* Move compute instance (and template) related helpers into their own file
2017-11-28 18:01:27 +00:00
|
|
|
var ProjectMetadataBaseApiVersion = v1
|
|
|
|
var ProjectMetadataVersionedFeatures = []Feature{}
|
|
|
|
|
2015-08-20 19:18:41 +00:00
|
|
|
func resourceComputeProjectMetadata() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceComputeProjectMetadataCreate,
|
2015-08-31 14:06:25 +00:00
|
|
|
Read: resourceComputeProjectMetadataRead,
|
2015-08-20 19:18:41 +00:00
|
|
|
Update: resourceComputeProjectMetadataUpdate,
|
|
|
|
Delete: resourceComputeProjectMetadataDelete,
|
|
|
|
|
|
|
|
SchemaVersion: 0,
|
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
2015-08-31 14:06:25 +00:00
|
|
|
"metadata": &schema.Schema{
|
2018-01-12 18:09:06 +00:00
|
|
|
Elem: schema.TypeString,
|
2015-08-31 14:06:25 +00:00
|
|
|
Type: schema.TypeMap,
|
2015-08-20 19:18:41 +00:00
|
|
|
Required: true,
|
|
|
|
},
|
2016-04-10 16:59:57 +00:00
|
|
|
|
|
|
|
"project": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2017-11-28 00:32:20 +00:00
|
|
|
Computed: true,
|
2016-04-10 16:59:57 +00:00
|
|
|
ForceNew: true,
|
|
|
|
},
|
2015-08-20 19:18:41 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
projectID, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-08-31 21:33:02 +00:00
|
|
|
createMD := func() error {
|
2015-08-20 19:18:41 +00:00
|
|
|
// Load project service
|
2016-04-10 16:59:57 +00:00
|
|
|
log.Printf("[DEBUG] Loading project service: %s", projectID)
|
|
|
|
project, err := config.clientCompute.Projects.Get(projectID).Do()
|
2015-08-20 19:18:41 +00:00
|
|
|
if err != nil {
|
2016-04-10 16:59:57 +00:00
|
|
|
return fmt.Errorf("Error loading project '%s': %s", projectID, err)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
md := project.CommonInstanceMetadata
|
|
|
|
|
|
|
|
newMDMap := d.Get("metadata").(map[string]interface{})
|
|
|
|
// Ensure that we aren't overwriting entries that already exist
|
2015-08-31 14:06:25 +00:00
|
|
|
for _, kv := range md.Items {
|
2015-08-20 19:18:41 +00:00
|
|
|
if _, ok := newMDMap[kv.Key]; ok {
|
2016-04-10 16:59:57 +00:00
|
|
|
return fmt.Errorf("Error, key '%s' already exists in project '%s'", kv.Key, projectID)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append new metadata to existing metadata
|
2015-08-31 14:06:25 +00:00
|
|
|
for key, val := range newMDMap {
|
|
|
|
v := val.(string)
|
|
|
|
md.Items = append(md.Items, &compute.MetadataItems{
|
2015-08-20 19:18:41 +00:00
|
|
|
Key: key,
|
2015-08-31 14:06:25 +00:00
|
|
|
Value: &v,
|
2015-08-20 19:18:41 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
|
2015-08-20 19:18:41 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2015-08-31 14:06:25 +00:00
|
|
|
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
|
|
|
|
2017-10-13 22:36:03 +00:00
|
|
|
return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
|
2015-08-31 21:33:02 +00:00
|
|
|
}
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
err = MetadataRetryWrapper(createMD)
|
2015-08-31 21:33:02 +00:00
|
|
|
if err != nil {
|
2015-10-07 20:35:06 +00:00
|
|
|
return err
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
2015-10-07 20:35:06 +00:00
|
|
|
return resourceComputeProjectMetadataRead(d, meta)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func resourceComputeProjectMetadataRead(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
projectID, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-08-20 19:18:41 +00:00
|
|
|
// Load project service
|
2016-04-10 16:59:57 +00:00
|
|
|
log.Printf("[DEBUG] Loading project service: %s", projectID)
|
|
|
|
project, err := config.clientCompute.Projects.Get(projectID).Do()
|
2015-08-20 19:18:41 +00:00
|
|
|
if err != nil {
|
2017-05-09 23:00:47 +00:00
|
|
|
return handleNotFoundError(err, d, fmt.Sprintf("Project metadata for project %q", projectID))
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
2017-11-29 17:47:42 +00:00
|
|
|
md := flattenMetadata(project.CommonInstanceMetadata)
|
2017-08-01 17:47:58 +00:00
|
|
|
existingMetadata := d.Get("metadata").(map[string]interface{})
|
|
|
|
// Remove all keys not explicitly mentioned in the terraform config
|
|
|
|
for k := range md {
|
|
|
|
if _, ok := existingMetadata[k]; !ok {
|
|
|
|
delete(md, k)
|
|
|
|
}
|
|
|
|
}
|
2015-08-20 19:18:41 +00:00
|
|
|
|
2017-08-01 17:47:58 +00:00
|
|
|
if err = d.Set("metadata", md); err != nil {
|
2015-08-31 14:06:25 +00:00
|
|
|
return fmt.Errorf("Error setting metadata: %s", err)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
2018-03-19 16:47:33 +00:00
|
|
|
d.Set("project", projectID)
|
2015-08-20 19:18:41 +00:00
|
|
|
d.SetId("common_metadata")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceComputeProjectMetadataUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
projectID, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-08-20 19:18:41 +00:00
|
|
|
if d.HasChange("metadata") {
|
|
|
|
o, n := d.GetChange("metadata")
|
|
|
|
|
2015-08-31 21:33:02 +00:00
|
|
|
updateMD := func() error {
|
2015-08-20 19:18:41 +00:00
|
|
|
// Load project service
|
2016-04-10 16:59:57 +00:00
|
|
|
log.Printf("[DEBUG] Loading project service: %s", projectID)
|
|
|
|
project, err := config.clientCompute.Projects.Get(projectID).Do()
|
2015-08-20 19:18:41 +00:00
|
|
|
if err != nil {
|
2016-04-10 16:59:57 +00:00
|
|
|
return fmt.Errorf("Error loading project '%s': %s", projectID, err)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
md := project.CommonInstanceMetadata
|
|
|
|
|
2015-10-07 20:35:06 +00:00
|
|
|
MetadataUpdate(o.(map[string]interface{}), n.(map[string]interface{}), md)
|
2015-08-20 19:18:41 +00:00
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
|
2015-08-20 19:18:41 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2015-08-31 14:06:25 +00:00
|
|
|
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
|
|
|
|
2015-09-11 18:56:20 +00:00
|
|
|
// Optimistic locking requires the fingerprint received to match
|
2015-08-20 19:18:41 +00:00
|
|
|
// the fingerprint we send the server, if there is a mismatch then we
|
|
|
|
// are working on old data, and must retry
|
2017-10-13 22:36:03 +00:00
|
|
|
return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
|
2015-08-31 21:33:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := MetadataRetryWrapper(updateMD)
|
|
|
|
if err != nil {
|
2015-10-07 20:35:06 +00:00
|
|
|
return err
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
2015-10-07 20:35:06 +00:00
|
|
|
return resourceComputeProjectMetadataRead(d, meta)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
projectID, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-08-20 19:18:41 +00:00
|
|
|
// Load project service
|
2016-04-10 16:59:57 +00:00
|
|
|
log.Printf("[DEBUG] Loading project service: %s", projectID)
|
|
|
|
project, err := config.clientCompute.Projects.Get(projectID).Do()
|
2015-08-20 19:18:41 +00:00
|
|
|
if err != nil {
|
2016-04-10 16:59:57 +00:00
|
|
|
return fmt.Errorf("Error loading project '%s': %s", projectID, err)
|
2015-08-20 19:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
md := project.CommonInstanceMetadata
|
|
|
|
|
|
|
|
// Remove all items
|
|
|
|
md.Items = nil
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
|
2015-08-20 19:18:41 +00:00
|
|
|
|
2017-02-16 10:36:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error removing metadata from project %s: %s", projectID, err)
|
|
|
|
}
|
|
|
|
|
2015-08-20 19:18:41 +00:00
|
|
|
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
|
|
|
|
2017-10-13 22:36:03 +00:00
|
|
|
err = computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
|
2015-08-20 19:18:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return resourceComputeProjectMetadataRead(d, meta)
|
|
|
|
}
|