forked from hashicorp/terraform-provider-google-beta
-
Notifications
You must be signed in to change notification settings - Fork 0
/
resource_compute_instance_from_template.go
142 lines (117 loc) · 4.13 KB
/
resource_compute_instance_from_template.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package google
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
strcase "github.com/stoewer/go-strcase"
)
func resourceComputeInstanceFromTemplate() *schema.Resource {
return &schema.Resource{
Create: resourceComputeInstanceFromTemplateCreate,
Read: resourceComputeInstanceRead,
Update: resourceComputeInstanceUpdate,
Delete: resourceComputeInstanceDelete,
// Import doesn't really make sense, because you could just import
// as a google_compute_instance.
Timeouts: resourceComputeInstance().Timeouts,
Schema: computeInstanceFromTemplateSchema(),
CustomizeDiff: resourceComputeInstance().CustomizeDiff,
}
}
func computeInstanceFromTemplateSchema() map[string]*schema.Schema {
s := resourceComputeInstance().Schema
for _, field := range []string{"boot_disk", "machine_type", "network_interface"} {
// The user can set these fields as an override, but doesn't need to -
// the template values will be used if they're unset.
s[field].Required = false
s[field].Optional = true
}
// Remove deprecated/removed fields that are never d.Set. We can't
// programatically remove all of them, because some of them still have d.Set
// calls.
for _, field := range []string{"create_timeout", "disk", "network"} {
delete(s, field)
}
recurseOnSchema(s, func(field *schema.Schema) {
// We don't want to accidentally use default values to override the instance
// template, so remove defaults.
field.Default = nil
// Make non-required fields computed since they'll be set by the template.
// Leave deprecated and removed fields alone because we don't set them.
if !field.Required && !(field.Deprecated != "" || field.Removed != "") {
field.Computed = true
}
})
s["source_instance_template"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
}
return s
}
func recurseOnSchema(s map[string]*schema.Schema, f func(*schema.Schema)) {
for _, field := range s {
f(field)
if e := field.Elem; e != nil {
if r, ok := e.(*schema.Resource); ok {
recurseOnSchema(r.Schema, f)
}
}
}
}
func resourceComputeInstanceFromTemplateCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
// Get the zone
z, err := getZone(d, config)
if err != nil {
return err
}
log.Printf("[DEBUG] Loading zone: %s", z)
zone, err := config.clientCompute.Zones.Get(project, z).Do()
if err != nil {
return fmt.Errorf("Error loading zone '%s': %s", z, err)
}
instance, err := expandComputeInstance(project, zone, d, config)
if err != nil {
return err
}
// Force send all top-level fields in case they're overridden to zero values.
// TODO: consider doing so for nested fields as well.
for f, s := range computeInstanceFromTemplateSchema() {
// It seems that GetOkExists always returns true for sets.
// TODO: confirm this and file issue against Terraform core.
// In the meantime, don't force send sets.
if s.Type == schema.TypeSet {
continue
}
if _, exists := d.GetOkExists(f); exists {
// Assume for now that all fields are exact snake_case versions of the API fields.
// This won't necessarily always be true, but it serves as a good approximation and
// can be adjusted later as we discover issues.
instance.ForceSendFields = append(instance.ForceSendFields, strcase.UpperCamelCase(f))
}
}
tpl, err := ParseInstanceTemplateFieldValue(d.Get("source_instance_template").(string), d, config)
if err != nil {
return err
}
log.Printf("[INFO] Requesting instance creation")
op, err := config.clientComputeBeta.Instances.Insert(project, zone.Name, instance).SourceInstanceTemplate(tpl.RelativeLink()).Do()
if err != nil {
return fmt.Errorf("Error creating instance: %s", err)
}
// Store the ID now
d.SetId(instance.Name)
// Wait for the operation to complete
waitErr := computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), "instance to create")
if waitErr != nil {
// The resource didn't actually create
d.SetId("")
return waitErr
}
return resourceComputeInstanceRead(d, meta)
}