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

Add node_affinities to instance/template #3553

Merged
merged 1 commit into from
May 6, 2019
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
115 changes: 107 additions & 8 deletions google/compute_instance_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,36 @@ import (
"fmt"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/googleapi"
)

func instanceSchedulingNodeAffinitiesElemSchema() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"operator": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"IN", "NOT"}, false),
},
"values": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
}
}

func expandAliasIpRanges(ranges []interface{}) []*computeBeta.AliasIpRange {
ipRanges := make([]*computeBeta.AliasIpRange, 0, len(ranges))
for _, raw := range ranges {
Expand All @@ -30,17 +57,89 @@ func flattenAliasIpRange(ranges []*computeBeta.AliasIpRange) []map[string]interf
return rangesSchema
}

func flattenScheduling(scheduling *computeBeta.Scheduling) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)
func expandScheduling(v interface{}) (*computeBeta.Scheduling, error) {
if v == nil {
// We can't set default values for lists.
return &computeBeta.Scheduling{
AutomaticRestart: googleapi.Bool(true),
}, nil
}

ls := v.([]interface{})
if len(ls) == 0 {
// We can't set default values for lists
return &computeBeta.Scheduling{
AutomaticRestart: googleapi.Bool(true),
}, nil
}

if len(ls) > 1 || ls[0] == nil {
return nil, fmt.Errorf("expected exactly one scheduling block")
}

original := ls[0].(map[string]interface{})
scheduling := &computeBeta.Scheduling{
ForceSendFields: make([]string, 0, 4),
}

if v, ok := original["automatic_restart"]; ok {
scheduling.AutomaticRestart = googleapi.Bool(v.(bool))
scheduling.ForceSendFields = append(scheduling.ForceSendFields, "AutomaticRestart")
}

if v, ok := original["preemptible"]; ok {
scheduling.Preemptible = v.(bool)
scheduling.ForceSendFields = append(scheduling.ForceSendFields, "Preemptible")

}

if v, ok := original["on_host_maintenance"]; ok {
scheduling.OnHostMaintenance = v.(string)
scheduling.ForceSendFields = append(scheduling.ForceSendFields, "OnHostMaintenance")
}

if v, ok := original["node_affinities"]; ok && v != nil {
naSet := v.(*schema.Set).List()
scheduling.NodeAffinities = make([]*computeBeta.SchedulingNodeAffinity, len(ls))
scheduling.ForceSendFields = append(scheduling.ForceSendFields, "NodeAffinities")
for _, nodeAffRaw := range naSet {
if nodeAffRaw == nil {
continue
}
nodeAff := nodeAffRaw.(map[string]interface{})
tranformed := &computeBeta.SchedulingNodeAffinity{
Key: nodeAff["key"].(string),
Operator: nodeAff["operator"].(string),
Values: convertStringArr(nodeAff["values"].(*schema.Set).List()),
}
scheduling.NodeAffinities = append(scheduling.NodeAffinities, tranformed)
}
}

return scheduling, nil
}

func flattenScheduling(resp *computeBeta.Scheduling) []map[string]interface{} {
schedulingMap := map[string]interface{}{
"on_host_maintenance": scheduling.OnHostMaintenance,
"preemptible": scheduling.Preemptible,
"on_host_maintenance": resp.OnHostMaintenance,
"preemptible": resp.Preemptible,
}
if scheduling.AutomaticRestart != nil {
schedulingMap["automatic_restart"] = *scheduling.AutomaticRestart

if resp.AutomaticRestart != nil {
schedulingMap["automatic_restart"] = *resp.AutomaticRestart
}
result = append(result, schedulingMap)
return result

nodeAffinities := schema.NewSet(schema.HashResource(instanceSchedulingNodeAffinitiesElemSchema()), nil)
for _, na := range resp.NodeAffinities {
nodeAffinities.Add(map[string]interface{}{
"key": na.Key,
"operator": na.Operator,
"values": schema.NewSet(schema.HashString, convertStringArrToInterface(na.Values)),
})
}
schedulingMap["node_affinities"] = nodeAffinities

return []map[string]interface{}{schedulingMap}
}

func flattenAccessConfigs(accessConfigs []*computeBeta.AccessConfig) ([]map[string]interface{}, string) {
Expand Down
46 changes: 19 additions & 27 deletions google/resource_compute_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/mitchellh/hashstructure"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)

func resourceComputeInstance() *schema.Resource {
Expand Down Expand Up @@ -441,6 +440,14 @@ func resourceComputeInstance() *schema.Resource {
Default: false,
ForceNew: true,
},

"node_affinities": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: instanceSchedulingNodeAffinitiesElemSchema(),
DiffSuppressFunc: emptyOrDefaultStringSuppress(""),
},
},
},
},
Expand Down Expand Up @@ -625,22 +632,9 @@ func expandComputeInstance(project string, zone *compute.Zone, d *schema.Resourc
disks = append(disks, disk)
}

sch := d.Get("scheduling").([]interface{})
var scheduling *computeBeta.Scheduling
if len(sch) == 0 {
// TF doesn't do anything about defaults inside of nested objects, so if
// scheduling hasn't been set, then send it with its default values.
scheduling = &computeBeta.Scheduling{
AutomaticRestart: googleapi.Bool(true),
}
} else {
prefix := "scheduling.0"
scheduling = &computeBeta.Scheduling{
AutomaticRestart: googleapi.Bool(d.Get(prefix + ".automatic_restart").(bool)),
Preemptible: d.Get(prefix + ".preemptible").(bool),
OnHostMaintenance: d.Get(prefix + ".on_host_maintenance").(string),
ForceSendFields: []string{"AutomaticRestart", "Preemptible"},
}
scheduling, err := expandScheduling(d.Get("scheduling"))
if err != nil {
return nil, fmt.Errorf("Error creating scheduling: %s", err)
}

metadata, err := resourceInstanceMetadata(d)
Expand Down Expand Up @@ -1005,22 +999,20 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
}

if d.HasChange("scheduling") {
prefix := "scheduling.0"
scheduling := &compute.Scheduling{
AutomaticRestart: googleapi.Bool(d.Get(prefix + ".automatic_restart").(bool)),
Preemptible: d.Get(prefix + ".preemptible").(bool),
OnHostMaintenance: d.Get(prefix + ".on_host_maintenance").(string),
ForceSendFields: []string{"AutomaticRestart", "Preemptible"},
scheduling, err := expandScheduling(d.Get("scheduling"))
if err != nil {
return fmt.Errorf("Error creating request data to update scheduling: %s", err)
}

op, err := config.clientCompute.Instances.SetScheduling(project,
zone, d.Id(), scheduling).Do()

op, err := config.clientComputeBeta.Instances.SetScheduling(
project, zone, d.Id(), scheduling).Do()
if err != nil {
return fmt.Errorf("Error updating scheduling policy: %s", err)
}

opErr := computeOperationWaitTime(config.clientCompute, op, project, "scheduling policy update", int(d.Timeout(schema.TimeoutUpdate).Minutes()))
opErr := computeBetaOperationWaitTime(
config.clientCompute, op, project, "scheduling policy update",
int(d.Timeout(schema.TimeoutUpdate).Minutes()))
if opErr != nil {
return opErr
}
Expand Down
98 changes: 50 additions & 48 deletions google/resource_compute_instance_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/googleapi"
)

func resourceComputeInstanceTemplate() *schema.Resource {
Expand Down Expand Up @@ -320,6 +319,7 @@ func resourceComputeInstanceTemplate() *schema.Resource {
Optional: true,
Computed: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"preemptible": {
Expand All @@ -342,6 +342,14 @@ func resourceComputeInstanceTemplate() *schema.Resource {
Computed: true,
ForceNew: true,
},

"node_affinities": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: instanceSchedulingNodeAffinitiesElemSchema(),
DiffSuppressFunc: emptyOrDefaultStringSuppress(""),
},
},
},
},
Expand Down Expand Up @@ -604,70 +612,40 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac
return err
}

instanceProperties := &computeBeta.InstanceProperties{
CanIpForward: d.Get("can_ip_forward").(bool),
Description: d.Get("instance_description").(string),
MachineType: d.Get("machine_type").(string),
MinCpuPlatform: d.Get("min_cpu_platform").(string),
}

disks, err := buildDisks(d, config)
if err != nil {
return err
}
instanceProperties.Disks = disks

metadata, err := resourceInstanceMetadata(d)
if err != nil {
return err
}
instanceProperties.Metadata = metadata

networks, err := expandNetworkInterfaces(d, config)
if err != nil {
return err
}
instanceProperties.NetworkInterfaces = networks

instanceProperties.Scheduling = &computeBeta.Scheduling{}
instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"

forceSendFieldsScheduling := make([]string, 0, 3)
var hasSendMaintenance bool
hasSendMaintenance = false
if v, ok := d.GetOk("scheduling"); ok {
_schedulings := v.([]interface{})
if len(_schedulings) > 1 {
return fmt.Errorf("Error, at most one `scheduling` block can be defined")
}
_scheduling := _schedulings[0].(map[string]interface{})

// "automatic_restart" has a default value and is always safe to dereference
automaticRestart := _scheduling["automatic_restart"].(bool)
instanceProperties.Scheduling.AutomaticRestart = googleapi.Bool(automaticRestart)
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "AutomaticRestart")

if vp, okp := _scheduling["on_host_maintenance"]; okp {
instanceProperties.Scheduling.OnHostMaintenance = vp.(string)
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
hasSendMaintenance = true
}

if vp, okp := _scheduling["preemptible"]; okp {
instanceProperties.Scheduling.Preemptible = vp.(bool)
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "Preemptible")
if vp.(bool) && !hasSendMaintenance {
instanceProperties.Scheduling.OnHostMaintenance = "TERMINATE"
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
}
}
scheduling, err := expandResourceComputeInstanceTemplateScheduling(d, config)
if err != nil {
return err
}
instanceProperties.Scheduling.ForceSendFields = forceSendFieldsScheduling

instanceProperties.ServiceAccounts = expandServiceAccounts(d.Get("service_account").([]interface{}))

instanceProperties.GuestAccelerators = expandInstanceTemplateGuestAccelerators(d, config)
instanceProperties := &computeBeta.InstanceProperties{
CanIpForward: d.Get("can_ip_forward").(bool),
Description: d.Get("instance_description").(string),
GuestAccelerators: expandInstanceTemplateGuestAccelerators(d, config),
MachineType: d.Get("machine_type").(string),
MinCpuPlatform: d.Get("min_cpu_platform").(string),
Disks: disks,
Metadata: metadata,
NetworkInterfaces: networks,
Scheduling: scheduling,
ServiceAccounts: expandServiceAccounts(d.Get("service_account").([]interface{})),
Tags: resourceInstanceTags(d),
}

instanceProperties.Tags = resourceInstanceTags(d)
if _, ok := d.GetOk("labels"); ok {
instanceProperties.Labels = expandLabels(d)
}
Expand Down Expand Up @@ -888,3 +866,27 @@ func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interfac
d.SetId("")
return nil
}

// This wraps the general compute instance helper expandScheduling.
// Default value of OnHostMaintenance depends on the value of Preemptible,
// so we can't set a default in schema
func expandResourceComputeInstanceTemplateScheduling(d *schema.ResourceData, meta interface{}) (*computeBeta.Scheduling, error) {
v, ok := d.GetOk("scheduling")
if !ok || v == nil {
// We can't set defaults for lists (e.g. scheduling)
return &computeBeta.Scheduling{
OnHostMaintenance: "MIGRATE",
}, nil
}

expanded, err := expandScheduling(v)
if err != nil {
return nil, err
}

// Make sure we have an appropriate value for OnHostMaintenance if Preemptible
if expanded.Preemptible && expanded.OnHostMaintenance == "" {
expanded.OnHostMaintenance = "TERMINATE"
}
return expanded, nil
}
Loading