From 3242623b288421013a214e6646d930d9dfd77d95 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Thu, 20 Nov 2014 12:40:17 -0500 Subject: [PATCH 1/7] Multiple gcp improvements and new resources --- .gitignore | 2 + builtin/providers/google/provider.go | 17 +- .../google/resource_compute_address.go | 8 + .../resource_compute_forwarding_rule.go | 220 ++++++++++ .../resource_compute_http_health_check.go | 240 +++++++++++ .../google/resource_compute_instance.go | 58 ++- .../google/resource_compute_target_pool.go | 397 ++++++++++++++++++ 7 files changed, 924 insertions(+), 18 deletions(-) create mode 100644 builtin/providers/google/resource_compute_forwarding_rule.go create mode 100644 builtin/providers/google/resource_compute_http_health_check.go create mode 100644 builtin/providers/google/resource_compute_target_pool.go diff --git a/.gitignore b/.gitignore index 72a053db9d8b..2483501bc163 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ website/node_modules *.tfstate *.log *.bak +*~ +.*.swp diff --git a/builtin/providers/google/provider.go b/builtin/providers/google/provider.go index 593b8559b5c3..350eb3e6cff9 100644 --- a/builtin/providers/google/provider.go +++ b/builtin/providers/google/provider.go @@ -31,12 +31,15 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "google_compute_address": resourceComputeAddress(), - "google_compute_disk": resourceComputeDisk(), - "google_compute_firewall": resourceComputeFirewall(), - "google_compute_instance": resourceComputeInstance(), - "google_compute_network": resourceComputeNetwork(), - "google_compute_route": resourceComputeRoute(), + "google_compute_address": resourceComputeAddress(), + "google_compute_disk": resourceComputeDisk(), + "google_compute_firewall": resourceComputeFirewall(), + "google_compute_forwarding_rule": resourceComputeForwardingRule(), + "google_compute_http_health_check": resourceComputeHttpHealthCheck(), + "google_compute_instance": resourceComputeInstance(), + "google_compute_network": resourceComputeNetwork(), + "google_compute_route": resourceComputeRoute(), + "google_compute_target_pool": resourceComputeTargetPool(), }, ConfigureFunc: providerConfigure, @@ -57,3 +60,5 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { return &config, nil } + +// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_address.go b/builtin/providers/google/resource_compute_address.go index a8f1ecf0c77d..0fec05270369 100644 --- a/builtin/providers/google/resource_compute_address.go +++ b/builtin/providers/google/resource_compute_address.go @@ -27,6 +27,12 @@ func resourceComputeAddress() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "selfLink": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, } } @@ -90,6 +96,7 @@ func resourceComputeAddressRead(d *schema.ResourceData, meta interface{}) error } d.Set("address", addr.Address) + d.Set("selfLink", addr.SelfLink) return nil } @@ -98,6 +105,7 @@ func resourceComputeAddressDelete(d *schema.ResourceData, meta interface{}) erro config := meta.(*Config) // Delete the address + log.Printf("[DEBUG] address delete request") op, err := config.clientCompute.Addresses.Delete( config.Project, config.Region, d.Id()).Do() if err != nil { diff --git a/builtin/providers/google/resource_compute_forwarding_rule.go b/builtin/providers/google/resource_compute_forwarding_rule.go new file mode 100644 index 000000000000..3af6b44d6c7f --- /dev/null +++ b/builtin/providers/google/resource_compute_forwarding_rule.go @@ -0,0 +1,220 @@ +package google + +import ( + "fmt" + "log" + "time" + + "code.google.com/p/google-api-go-client/compute/v1" + "code.google.com/p/google-api-go-client/googleapi" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceComputeForwardingRule() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeForwardingRuleCreate, + Read: resourceComputeForwardingRuleRead, + Delete: resourceComputeForwardingRuleDelete, + Update: resourceComputeForwardingRuleUpdate, + + Schema: map[string]*schema.Schema{ + "IPAddress": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "IPProtocol": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "portRange": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "selfLink": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "target": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + frule := &compute.ForwardingRule{ + IPAddress: d.Get("IPAddress").(string), + IPProtocol: d.Get("IPProtocol").(string), + Description: d.Get("description").(string), + Name: d.Get("name").(string), + PortRange: d.Get("portRange").(string), + Target: d.Get("target").(string), + } + + log.Printf("[DEBUG] ForwardingRule insert request: %#v", frule) + op, err := config.clientCompute.ForwardingRules.Insert( + config.Project, config.Region, frule).Do() + if err != nil { + return fmt.Errorf("Error creating ForwardingRule: %s", err) + } + + // It probably maybe worked, so store the ID now + d.SetId(frule.Name) + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Region: config.Region, + Project: config.Project, + Type: OperationWaitRegion, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + opRaw, err := state.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for ForwardingRule to create: %s", err) + } + op = opRaw.(*compute.Operation) + if op.Error != nil { + // The resource didn't actually create + d.SetId("") + + // Return the error + return OperationError(*op.Error) + } + + return resourceComputeForwardingRuleRead(d, meta) +} + +func resourceComputeForwardingRuleUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + d.Partial(true) + + if d.HasChange("target") { + target_name := d.Get("target").(string) + target_ref := &compute.TargetReference{Target: target_name} + op, err := config.clientCompute.ForwardingRules.SetTarget( + config.Project, config.Region, d.Id(), target_ref).Do() + if err != nil { + return fmt.Errorf("Error updating target: %s", err) + } + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Region: config.Region, + Project: config.Project, + Type: OperationWaitRegion, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + opRaw, err := state.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for ForwardingRule to update target: %s", err) + } + op = opRaw.(*compute.Operation) + if op.Error != nil { + // The resource didn't actually create + d.SetId("") + + // Return the error + return OperationError(*op.Error) + } + d.SetPartial("target") + } + + d.Partial(false) + + return resourceComputeForwardingRuleRead(d, meta) +} + +func resourceComputeForwardingRuleRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + frule, err := config.clientCompute.ForwardingRules.Get( + config.Project, config.Region, d.Id()).Do() + if err != nil { + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + // The resource doesn't exist anymore + d.SetId("") + + return nil + } + + return fmt.Errorf("Error reading ForwardingRule: %s", err) + } + + d.Set("IPAddress", frule.IPAddress) + d.Set("IPProtocol", frule.IPProtocol) + d.Set("selfLink", frule.SelfLink) + + return nil +} + +func resourceComputeForwardingRuleDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Delete the ForwardingRule + log.Printf("[DEBUG] ForwardingRule delete request") + op, err := config.clientCompute.ForwardingRules.Delete( + config.Project, config.Region, d.Id()).Do() + if err != nil { + return fmt.Errorf("Error deleting ForwardingRule: %s", err) + } + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Region: config.Region, + Project: config.Project, + Type: OperationWaitRegion, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + opRaw, err := state.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for ForwardingRule to delete: %s", err) + } + op = opRaw.(*compute.Operation) + if op.Error != nil { + // Return the error + return OperationError(*op.Error) + } + + d.SetId("") + return nil +} + +// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_http_health_check.go b/builtin/providers/google/resource_compute_http_health_check.go new file mode 100644 index 000000000000..e384563160f3 --- /dev/null +++ b/builtin/providers/google/resource_compute_http_health_check.go @@ -0,0 +1,240 @@ +package google + +import ( + "fmt" + "log" + "time" + + "code.google.com/p/google-api-go-client/compute/v1" + "code.google.com/p/google-api-go-client/googleapi" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceComputeHttpHealthCheck() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeHttpHealthCheckCreate, + Read: resourceComputeHttpHealthCheckRead, + Delete: resourceComputeHttpHealthCheckDelete, + Update: resourceComputeHttpHealthCheckUpdate, + + Schema: map[string]*schema.Schema{ + "checkIntervalSec": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "healthyThreshold": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + }, + + "host": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + }, + + "requestPath": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "selfLink": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "timeoutSec": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + }, + + "unhealthyThreshold": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + }, + }, + } +} + +func resourceComputeHttpHealthCheckCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Build the parameter + hchk := &compute.HttpHealthCheck{ + CheckIntervalSec: int64(d.Get("checkIntervalSec").(int)), + Description: d.Get("description").(string), + HealthyThreshold: int64(d.Get("healthyThreshold").(int)), + Host: d.Get("host").(string), + Name: d.Get("name").(string), + Port: int64(d.Get("port").(int)), + RequestPath: d.Get("requestPath").(string), + TimeoutSec: int64(d.Get("timeoutSec").(int)), + UnhealthyThreshold: int64(d.Get("unhealthyThreshold").(int)), + } + log.Printf("[DEBUG] HttpHealthCheck insert request: %#v", hchk) + op, err := config.clientCompute.HttpHealthChecks.Insert( + config.Project, hchk).Do() + if err != nil { + return fmt.Errorf("Error creating HttpHealthCheck: %s", err) + } + + // It probably maybe worked, so store the ID now + d.SetId(hchk.Name) + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Project: config.Project, + Type: OperationWaitGlobal, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + opRaw, err := state.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for HttpHealthCheck to create: %s", err) + } + op = opRaw.(*compute.Operation) + if op.Error != nil { + // The resource didn't actually create + d.SetId("") + + // Return the error + return OperationError(*op.Error) + } + + return resourceComputeHttpHealthCheckRead(d, meta) +} + +func resourceComputeHttpHealthCheckUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Build the parameter + hchk := &compute.HttpHealthCheck{ + CheckIntervalSec: int64(d.Get("checkIntervalSec").(int)), + Description: d.Get("description").(string), + HealthyThreshold: int64(d.Get("healthyThreshold").(int)), + Host: d.Get("host").(string), + Name: d.Get("name").(string), + Port: int64(d.Get("port").(int)), + RequestPath: d.Get("requestPath").(string), + TimeoutSec: int64(d.Get("timeoutSec").(int)), + UnhealthyThreshold: int64(d.Get("unhealthyThreshold").(int)), + } + log.Printf("[DEBUG] HttpHealthCheck patch request: %#v", hchk) + op, err := config.clientCompute.HttpHealthChecks.Patch( + config.Project, hchk.Name, hchk).Do() + if err != nil { + return fmt.Errorf("Error patching HttpHealthCheck: %s", err) + } + + // It probably maybe worked, so store the ID now + d.SetId(hchk.Name) + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Project: config.Project, + Type: OperationWaitGlobal, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + opRaw, err := state.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for HttpHealthCheck to patch: %s", err) + } + op = opRaw.(*compute.Operation) + if op.Error != nil { + // The resource didn't actually create + d.SetId("") + + // Return the error + return OperationError(*op.Error) + } + + return resourceComputeHttpHealthCheckRead(d, meta) +} + +func resourceComputeHttpHealthCheckRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + hchk, err := config.clientCompute.HttpHealthChecks.Get( + config.Project, d.Id()).Do() + if err != nil { + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + // The resource doesn't exist anymore + d.SetId("") + + return nil + } + + return fmt.Errorf("Error reading HttpHealthCheck: %s", err) + } + + d.Set("selfLink", hchk.SelfLink) + + return nil +} + +func resourceComputeHttpHealthCheckDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Delete the HttpHealthCheck + op, err := config.clientCompute.HttpHealthChecks.Delete( + config.Project, d.Id()).Do() + if err != nil { + return fmt.Errorf("Error deleting HttpHealthCheck: %s", err) + } + + // Wait for the operation to complete + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Project: config.Project, + Type: OperationWaitGlobal, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + opRaw, err := state.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for HttpHealthCheck to delete: %s", err) + } + op = opRaw.(*compute.Operation) + if op.Error != nil { + // Return the error + return OperationError(*op.Error) + } + + d.SetId("") + return nil +} + +// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_instance.go b/builtin/providers/google/resource_compute_instance.go index f6b0fde7a17e..5e00baffe751 100644 --- a/builtin/providers/google/resource_compute_instance.go +++ b/builtin/providers/google/resource_compute_instance.go @@ -109,6 +109,30 @@ func resourceComputeInstance() *schema.Resource { }, }, + "serviceAccounts": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "scopes": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "can_ip_forward": &schema.Schema{ Type: schema.TypeBool, Optional: true, @@ -259,6 +283,24 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err networks = append(networks, &iface) } + // TODO(dcunnin): find out how many service accounts there were, allocate the array, create + // service accounts with the same emails + // for each scope inside each service account, check it is gettable and then put it in + servAccsCount := d.Get("serviceAccounts.#").(int) + servAccs := make([]*compute.ServiceAccount, servAccsCount) + for i := 0; i < servAccsCount; i++ { + prefix := fmt.Sprintf("serviceAccounts.%d", i) + schemaScopes := d.Get(prefix + ".scopes").([]interface{}) + scopes := make([]string, len(schemaScopes)) + for j, v := range(schemaScopes) { + scopes[j] = v.(string) + } + servAccs[i] = &compute.ServiceAccount{ + Email: d.Get(prefix + ".email").(string), + Scopes: scopes, + } + } + // Create the instance information instance := compute.Instance{ CanIpForward: d.Get("can_ip_forward").(bool), @@ -269,20 +311,10 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err Name: d.Get("name").(string), NetworkInterfaces: networks, Tags: resourceInstanceTags(d), - /* - ServiceAccounts: []*compute.ServiceAccount{ - &compute.ServiceAccount{ - Email: "default", - Scopes: []string{ - "https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/compute", - "https://www.googleapis.com/auth/devstorage.full_control", - }, - }, - }, - */ + ServiceAccounts: servAccs, } + log.Printf("[INFO] Requesting instance creation") op, err := config.clientCompute.Instances.Insert( config.Project, zone.Name, &instance).Do() @@ -539,3 +571,5 @@ func resourceInstanceNatIP(iface *compute.NetworkInterface) (natIP string) { return natIP } + +// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_target_pool.go b/builtin/providers/google/resource_compute_target_pool.go new file mode 100644 index 000000000000..835e9e58c486 --- /dev/null +++ b/builtin/providers/google/resource_compute_target_pool.go @@ -0,0 +1,397 @@ +package google + +import ( + "fmt" + "log" + "strings" + "time" + + "code.google.com/p/google-api-go-client/compute/v1" + "code.google.com/p/google-api-go-client/googleapi" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceComputeTargetPool() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeTargetPoolCreate, + Read: resourceComputeTargetPoolRead, + Delete: resourceComputeTargetPoolDelete, + Update: resourceComputeTargetPoolUpdate, + + Schema: map[string]*schema.Schema{ + "backupPool": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "healthChecks": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "instances": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "selfLink": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "sessionAffinity": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func convertStringArr(ifaceArr []interface{}) []string { + arr := make([]string, len(ifaceArr)) + for i, v := range ifaceArr { + arr[i] = v.(string) + } + return arr +} + +func waitOp(config *Config, op *compute.Operation, + resource string, action string) (*compute.Operation, error) { + + w := &OperationWaiter{ + Service: config.clientCompute, + Op: op, + Region: config.Region, + Project: config.Project, + Type: OperationWaitRegion, + } + state := w.Conf() + state.Timeout = 2 * time.Minute + state.MinTimeout = 1 * time.Second + opRaw, err := state.WaitForState() + if err != nil { + return nil, fmt.Errorf("Error waiting for %s to %s: %s", resource, action, err) + } + return opRaw.(*compute.Operation), nil +} + +// Healthchecks need to exist before being referred to from the target pool. +func convertHealthChecks(config *Config, names []string) ([]string, error) { + urls := make([]string, len(names)) + for i, name := range names { + // Look up the healthcheck + res, err := config.clientCompute.HttpHealthChecks.Get(config.Project, name).Do() + if err != nil { + return nil, fmt.Errorf("Error reading HealthCheck: %s", err) + } + urls[i] = res.SelfLink + } + return urls, nil +} + +// Instances do not need to exist yet, so we simply generate URLs. +// Instances can be full URLS or zone/name +func convertInstances(config *Config, names []string) ([]string, error) { + urls := make([]string, len(names)) + for i, name := range names { + if strings.HasPrefix(name, "https://www.googleapis.com/compute/v1/") { + urls[i] = name + } else { + splitName := strings.Split(name, "/") + if len(splitName) != 2 { + return nil, fmt.Errorf("Invalid instance name, require URL or zone/name: %s", name) + } else { + urls[i] = fmt.Sprintf( + "https://www.googleapis.com/compute/v1/projects/%s/zones/%s/instances/%s", + config.Project, splitName[0], splitName[1]) + } + } + } + return urls, nil +} + +func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + hchkUrls, err := convertHealthChecks( + config, convertStringArr(d.Get("healthChecks").([]interface{}))) + if err != nil { + return err + } + + instanceUrls, err := convertInstances( + config, convertStringArr(d.Get("instances").([]interface{}))) + if err != nil { + return err + } + + // Build the parameter + tpool := &compute.TargetPool{ + BackupPool: d.Get("backupPool").(string), + Description: d.Get("description").(string), + HealthChecks: hchkUrls, + Instances: instanceUrls, + Name: d.Get("name").(string), + SessionAffinity: d.Get("sessionAffinity").(string), + } + log.Printf("[DEBUG] TargetPool insert request: %#v", tpool) + op, err := config.clientCompute.TargetPools.Insert( + config.Project, config.Region, tpool).Do() + if err != nil { + return fmt.Errorf("Error creating TargetPool: %s", err) + } + + // It probably maybe worked, so store the ID now + d.SetId(tpool.Name) + + op, err = waitOp(config, op, "TargetPool", "create") + if err != nil { + return err + } + if op.Error != nil { + // The resource didn't actually create + d.SetId("") + // Return the error + return OperationError(*op.Error) + } + + return resourceComputeTargetPoolRead(d, meta) +} + +func calcAddRemove(from []string, to []string) ([]string, []string) { + add := make([]string, 0) + remove := make([]string, 0) + for _, u := range to { + found := false + for _, v := range from { + if u == v { + found = true + break + } + } + if !found { + add = append(add, u) + } + } + for _, u := range from { + found := false + for _, v := range to { + if u == v { + found = true + break + } + } + if !found { + remove = append(remove, u) + } + } + return add, remove +} + + +func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + d.Partial(true) + + if d.HasChange("healthChecks") { + + from_, to_ := d.GetChange("healthChecks") + from := convertStringArr(from_.([]interface{})) + to := convertStringArr(to_.([]interface{})) + fromUrls, err := convertHealthChecks(config, from) + if err != nil { + return err + } + toUrls, err := convertHealthChecks(config, to) + if err != nil { + return err + } + add, remove := calcAddRemove(fromUrls, toUrls) + + removeReq := &compute.TargetPoolsRemoveHealthCheckRequest{ + HealthChecks: make([]*compute.HealthCheckReference, len(remove)), + } + for i, v := range remove { + removeReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v} + } + op, err := config.clientCompute.TargetPools.RemoveHealthCheck( + config.Project, config.Region, d.Id(), removeReq).Do() + if err != nil { + return fmt.Errorf("Error updating healthCheck: %s", err) + } + op, err = waitOp(config, op, "TargetPool", "removing HealthChecks") + if err != nil { + return err + } + if op.Error != nil { + return OperationError(*op.Error) + } + + addReq := &compute.TargetPoolsAddHealthCheckRequest{ + HealthChecks: make([]*compute.HealthCheckReference, len(add)), + } + for i, v := range add { + addReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v} + } + op, err = config.clientCompute.TargetPools.AddHealthCheck( + config.Project, config.Region, d.Id(), addReq).Do() + if err != nil { + return fmt.Errorf("Error updating healthCheck: %s", err) + } + op, err = waitOp(config, op, "TargetPool", "adding HealthChecks") + if err != nil { + return err + } + if op.Error != nil { + return OperationError(*op.Error) + } + + d.SetPartial("healthChecks") + } + + if d.HasChange("instances") { + + from_, to_ := d.GetChange("instances") + from := convertStringArr(from_.([]interface{})) + to := convertStringArr(to_.([]interface{})) + fromUrls, err := convertInstances(config, from) + if err != nil { + return err + } + toUrls, err := convertInstances(config, to) + if err != nil { + return err + } + add, remove := calcAddRemove(fromUrls, toUrls) + + addReq := &compute.TargetPoolsAddInstanceRequest{ + Instances: make([]*compute.InstanceReference, len(add)), + } + for i, v := range add { + addReq.Instances[i] = &compute.InstanceReference{Instance: v} + } + op, err := config.clientCompute.TargetPools.AddInstance( + config.Project, config.Region, d.Id(), addReq).Do() + if err != nil { + return fmt.Errorf("Error updating instances: %s", err) + } + op, err = waitOp(config, op, "TargetPool", "adding instances") + if err != nil { + return err + } + if op.Error != nil { + return OperationError(*op.Error) + } + + removeReq := &compute.TargetPoolsRemoveInstanceRequest{ + Instances: make([]*compute.InstanceReference, len(remove)), + } + for i, v := range remove { + removeReq.Instances[i] = &compute.InstanceReference{Instance: v} + } + op, err = config.clientCompute.TargetPools.RemoveInstance( + config.Project, config.Region, d.Id(), removeReq).Do() + if err != nil { + return fmt.Errorf("Error updating instances: %s", err) + } + op, err = waitOp(config, op, "TargetPool", "removing instances") + if err != nil { + return err + } + if op.Error != nil { + return OperationError(*op.Error) + } + + d.SetPartial("instances") + } + + if d.HasChange("backupPool") { + bpool_name := d.Get("backupPool").(string) + tref := &compute.TargetReference{ + Target: bpool_name, + } + op, err := config.clientCompute.TargetPools.SetBackup( + config.Project, config.Region, d.Id(), tref).Do() + if err != nil { + return fmt.Errorf("Error updating backupPool: %s", err) + } + + op, err = waitOp(config, op, "TargetPool", "updating backupPool") + if err != nil { + return err + } + if op.Error != nil { + return OperationError(*op.Error) + } + + d.SetPartial("backupPool") + } + + d.Partial(false) + + return resourceComputeTargetPoolRead(d, meta) +} + +func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + tpool, err := config.clientCompute.TargetPools.Get( + config.Project, config.Region, d.Id()).Do() + if err != nil { + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + // The resource doesn't exist anymore + d.SetId("") + + return nil + } + + return fmt.Errorf("Error reading TargetPool: %s", err) + } + + d.Set("selfLink", tpool.SelfLink) + + return nil +} + +func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Delete the TargetPool + op, err := config.clientCompute.TargetPools.Delete( + config.Project, config.Region, d.Id()).Do() + if err != nil { + return fmt.Errorf("Error deleting TargetPool: %s", err) + } + + op, err = waitOp(config, op, "TargetPool", "delete") + if err != nil { + return err + } + if op.Error != nil { + return OperationError(*op.Error) + } + + d.SetId("") + return nil +} + +// vim: ts=4:sw=4:noet From fdb1995d6011eaf0957788e35d97b48b023184f6 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Thu, 20 Nov 2014 19:55:48 -0500 Subject: [PATCH 2/7] Remove //vim, use _ instead of camelcase --- builtin/providers/google/provider.go | 2 - .../resource_compute_forwarding_rule.go | 21 +++++----- .../resource_compute_http_health_check.go | 34 ++++++++--------- .../google/resource_compute_instance.go | 8 ++-- .../google/resource_compute_target_pool.go | 38 +++++++++---------- 5 files changed, 47 insertions(+), 56 deletions(-) diff --git a/builtin/providers/google/provider.go b/builtin/providers/google/provider.go index 350eb3e6cff9..a3a6c797102b 100644 --- a/builtin/providers/google/provider.go +++ b/builtin/providers/google/provider.go @@ -60,5 +60,3 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { return &config, nil } - -// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_forwarding_rule.go b/builtin/providers/google/resource_compute_forwarding_rule.go index 3af6b44d6c7f..269ff611c1fc 100644 --- a/builtin/providers/google/resource_compute_forwarding_rule.go +++ b/builtin/providers/google/resource_compute_forwarding_rule.go @@ -18,14 +18,14 @@ func resourceComputeForwardingRule() *schema.Resource { Update: resourceComputeForwardingRuleUpdate, Schema: map[string]*schema.Schema{ - "IPAddress": &schema.Schema{ + "ip_address": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, Computed: true, }, - "IPProtocol": &schema.Schema{ + "ip_protocol": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, @@ -44,13 +44,13 @@ func resourceComputeForwardingRule() *schema.Resource { ForceNew: true, }, - "portRange": &schema.Schema{ + "port_range": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "selfLink": &schema.Schema{ + "self_link": &schema.Schema{ Type: schema.TypeString, Computed: true, }, @@ -68,11 +68,11 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{ config := meta.(*Config) frule := &compute.ForwardingRule{ - IPAddress: d.Get("IPAddress").(string), - IPProtocol: d.Get("IPProtocol").(string), + IPAddress: d.Get("ip_address").(string), + IPProtocol: d.Get("ip_protocol").(string), Description: d.Get("description").(string), Name: d.Get("name").(string), - PortRange: d.Get("portRange").(string), + PortRange: d.Get("port_range").(string), Target: d.Get("target").(string), } @@ -174,9 +174,9 @@ func resourceComputeForwardingRuleRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error reading ForwardingRule: %s", err) } - d.Set("IPAddress", frule.IPAddress) - d.Set("IPProtocol", frule.IPProtocol) - d.Set("selfLink", frule.SelfLink) + d.Set("ip_address", frule.IPAddress) + d.Set("ip_protocol", frule.IPProtocol) + d.Set("self_link", frule.SelfLink) return nil } @@ -217,4 +217,3 @@ func resourceComputeForwardingRuleDelete(d *schema.ResourceData, meta interface{ return nil } -// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_http_health_check.go b/builtin/providers/google/resource_compute_http_health_check.go index e384563160f3..421f9681d56b 100644 --- a/builtin/providers/google/resource_compute_http_health_check.go +++ b/builtin/providers/google/resource_compute_http_health_check.go @@ -18,7 +18,7 @@ func resourceComputeHttpHealthCheck() *schema.Resource { Update: resourceComputeHttpHealthCheckUpdate, Schema: map[string]*schema.Schema{ - "checkIntervalSec": &schema.Schema{ + "check_interval_sec": &schema.Schema{ Type: schema.TypeInt, Optional: true, ForceNew: false, @@ -30,7 +30,7 @@ func resourceComputeHttpHealthCheck() *schema.Resource { ForceNew: false, }, - "healthyThreshold": &schema.Schema{ + "healthy_threshold": &schema.Schema{ Type: schema.TypeInt, Optional: true, ForceNew: false, @@ -54,18 +54,18 @@ func resourceComputeHttpHealthCheck() *schema.Resource { ForceNew: false, }, - "requestPath": &schema.Schema{ + "request_path": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "selfLink": &schema.Schema{ + "self_link": &schema.Schema{ Type: schema.TypeString, Computed: true, }, - "timeoutSec": &schema.Schema{ + "timeout_sec": &schema.Schema{ Type: schema.TypeInt, Optional: true, ForceNew: false, @@ -85,15 +85,15 @@ func resourceComputeHttpHealthCheckCreate(d *schema.ResourceData, meta interface // Build the parameter hchk := &compute.HttpHealthCheck{ - CheckIntervalSec: int64(d.Get("checkIntervalSec").(int)), + CheckIntervalSec: int64(d.Get("check_interval_sec").(int)), Description: d.Get("description").(string), - HealthyThreshold: int64(d.Get("healthyThreshold").(int)), + HealthyThreshold: int64(d.Get("healthy_threshold").(int)), Host: d.Get("host").(string), Name: d.Get("name").(string), Port: int64(d.Get("port").(int)), - RequestPath: d.Get("requestPath").(string), - TimeoutSec: int64(d.Get("timeoutSec").(int)), - UnhealthyThreshold: int64(d.Get("unhealthyThreshold").(int)), + RequestPath: d.Get("request_path").(string), + TimeoutSec: int64(d.Get("timeout_sec").(int)), + UnhealthyThreshold: int64(d.Get("unhealthy_threshold").(int)), } log.Printf("[DEBUG] HttpHealthCheck insert request: %#v", hchk) op, err := config.clientCompute.HttpHealthChecks.Insert( @@ -136,15 +136,15 @@ func resourceComputeHttpHealthCheckUpdate(d *schema.ResourceData, meta interface // Build the parameter hchk := &compute.HttpHealthCheck{ - CheckIntervalSec: int64(d.Get("checkIntervalSec").(int)), + CheckIntervalSec: int64(d.Get("check_interval_sec").(int)), Description: d.Get("description").(string), - HealthyThreshold: int64(d.Get("healthyThreshold").(int)), + HealthyThreshold: int64(d.Get("healthy_threshold").(int)), Host: d.Get("host").(string), Name: d.Get("name").(string), Port: int64(d.Get("port").(int)), - RequestPath: d.Get("requestPath").(string), - TimeoutSec: int64(d.Get("timeoutSec").(int)), - UnhealthyThreshold: int64(d.Get("unhealthyThreshold").(int)), + RequestPath: d.Get("request_path").(string), + TimeoutSec: int64(d.Get("timeout_sec").(int)), + UnhealthyThreshold: int64(d.Get("unhealthy_threshold").(int)), } log.Printf("[DEBUG] HttpHealthCheck patch request: %#v", hchk) op, err := config.clientCompute.HttpHealthChecks.Patch( @@ -198,7 +198,7 @@ func resourceComputeHttpHealthCheckRead(d *schema.ResourceData, meta interface{} return fmt.Errorf("Error reading HttpHealthCheck: %s", err) } - d.Set("selfLink", hchk.SelfLink) + d.Set("self_link", hchk.SelfLink) return nil } @@ -236,5 +236,3 @@ func resourceComputeHttpHealthCheckDelete(d *schema.ResourceData, meta interface d.SetId("") return nil } - -// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_instance.go b/builtin/providers/google/resource_compute_instance.go index 5e00baffe751..3bc60d37f3a9 100644 --- a/builtin/providers/google/resource_compute_instance.go +++ b/builtin/providers/google/resource_compute_instance.go @@ -109,7 +109,7 @@ func resourceComputeInstance() *schema.Resource { }, }, - "serviceAccounts": &schema.Schema{ + "service_accounts": &schema.Schema{ Type: schema.TypeList, Optional: true, ForceNew: true, @@ -286,10 +286,10 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err // TODO(dcunnin): find out how many service accounts there were, allocate the array, create // service accounts with the same emails // for each scope inside each service account, check it is gettable and then put it in - servAccsCount := d.Get("serviceAccounts.#").(int) + servAccsCount := d.Get("service_accounts.#").(int) servAccs := make([]*compute.ServiceAccount, servAccsCount) for i := 0; i < servAccsCount; i++ { - prefix := fmt.Sprintf("serviceAccounts.%d", i) + prefix := fmt.Sprintf("service_accounts.%d", i) schemaScopes := d.Get(prefix + ".scopes").([]interface{}) scopes := make([]string, len(schemaScopes)) for j, v := range(schemaScopes) { @@ -571,5 +571,3 @@ func resourceInstanceNatIP(iface *compute.NetworkInterface) (natIP string) { return natIP } - -// vim: ts=4:sw=4:noet diff --git a/builtin/providers/google/resource_compute_target_pool.go b/builtin/providers/google/resource_compute_target_pool.go index 835e9e58c486..b67cace8d0c5 100644 --- a/builtin/providers/google/resource_compute_target_pool.go +++ b/builtin/providers/google/resource_compute_target_pool.go @@ -19,7 +19,7 @@ func resourceComputeTargetPool() *schema.Resource { Update: resourceComputeTargetPoolUpdate, Schema: map[string]*schema.Schema{ - "backupPool": &schema.Schema{ + "backup_pool": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, @@ -31,7 +31,7 @@ func resourceComputeTargetPool() *schema.Resource { ForceNew: true, }, - "healthChecks": &schema.Schema{ + "health_checks": &schema.Schema{ Type: schema.TypeList, Optional: true, ForceNew: false, @@ -51,12 +51,12 @@ func resourceComputeTargetPool() *schema.Resource { ForceNew: true, }, - "selfLink": &schema.Schema{ + "self_link": &schema.Schema{ Type: schema.TypeString, Computed: true, }, - "sessionAffinity": &schema.Schema{ + "session_affinity": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, @@ -132,7 +132,7 @@ func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) e config := meta.(*Config) hchkUrls, err := convertHealthChecks( - config, convertStringArr(d.Get("healthChecks").([]interface{}))) + config, convertStringArr(d.Get("health_checks").([]interface{}))) if err != nil { return err } @@ -145,12 +145,12 @@ func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) e // Build the parameter tpool := &compute.TargetPool{ - BackupPool: d.Get("backupPool").(string), + BackupPool: d.Get("backup_pool").(string), Description: d.Get("description").(string), HealthChecks: hchkUrls, Instances: instanceUrls, Name: d.Get("name").(string), - SessionAffinity: d.Get("sessionAffinity").(string), + SessionAffinity: d.Get("session_affinity").(string), } log.Printf("[DEBUG] TargetPool insert request: %#v", tpool) op, err := config.clientCompute.TargetPools.Insert( @@ -212,9 +212,9 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e d.Partial(true) - if d.HasChange("healthChecks") { + if d.HasChange("health_checks") { - from_, to_ := d.GetChange("healthChecks") + from_, to_ := d.GetChange("health_checks") from := convertStringArr(from_.([]interface{})) to := convertStringArr(to_.([]interface{})) fromUrls, err := convertHealthChecks(config, from) @@ -236,7 +236,7 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e op, err := config.clientCompute.TargetPools.RemoveHealthCheck( config.Project, config.Region, d.Id(), removeReq).Do() if err != nil { - return fmt.Errorf("Error updating healthCheck: %s", err) + return fmt.Errorf("Error updating health_check: %s", err) } op, err = waitOp(config, op, "TargetPool", "removing HealthChecks") if err != nil { @@ -255,7 +255,7 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e op, err = config.clientCompute.TargetPools.AddHealthCheck( config.Project, config.Region, d.Id(), addReq).Do() if err != nil { - return fmt.Errorf("Error updating healthCheck: %s", err) + return fmt.Errorf("Error updating health_check: %s", err) } op, err = waitOp(config, op, "TargetPool", "adding HealthChecks") if err != nil { @@ -265,7 +265,7 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e return OperationError(*op.Error) } - d.SetPartial("healthChecks") + d.SetPartial("health_checks") } if d.HasChange("instances") { @@ -324,18 +324,18 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e d.SetPartial("instances") } - if d.HasChange("backupPool") { - bpool_name := d.Get("backupPool").(string) + if d.HasChange("backup_pool") { + bpool_name := d.Get("backup_pool").(string) tref := &compute.TargetReference{ Target: bpool_name, } op, err := config.clientCompute.TargetPools.SetBackup( config.Project, config.Region, d.Id(), tref).Do() if err != nil { - return fmt.Errorf("Error updating backupPool: %s", err) + return fmt.Errorf("Error updating backup_pool: %s", err) } - op, err = waitOp(config, op, "TargetPool", "updating backupPool") + op, err = waitOp(config, op, "TargetPool", "updating backup_pool") if err != nil { return err } @@ -343,7 +343,7 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e return OperationError(*op.Error) } - d.SetPartial("backupPool") + d.SetPartial("backup_pool") } d.Partial(false) @@ -367,7 +367,7 @@ func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error reading TargetPool: %s", err) } - d.Set("selfLink", tpool.SelfLink) + d.Set("self_link", tpool.SelfLink) return nil } @@ -393,5 +393,3 @@ func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) e d.SetId("") return nil } - -// vim: ts=4:sw=4:noet From 68fdd4a83849bfcd2d7861a4492a6f1356e73094 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Sun, 23 Nov 2014 23:54:26 -0500 Subject: [PATCH 3/7] Documentation (first pass) --- .../resource_compute_http_health_check.go | 42 +++++++++---- .../google/resource_compute_target_pool.go | 11 ++++ .../google/r/compute_address.html.markdown | 6 +- .../r/compute_forwarding_rule.html.markdown | 53 +++++++++++++++++ .../r/compute_http_health_check.html.markdown | 57 ++++++++++++++++++ .../google/r/compute_instance.html.markdown | 26 +++++++- .../r/compute_target_pool.html.markdown | 59 +++++++++++++++++++ 7 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 website/source/docs/providers/google/r/compute_forwarding_rule.html.markdown create mode 100644 website/source/docs/providers/google/r/compute_http_health_check.html.markdown create mode 100644 website/source/docs/providers/google/r/compute_target_pool.html.markdown diff --git a/builtin/providers/google/resource_compute_http_health_check.go b/builtin/providers/google/resource_compute_http_health_check.go index 421f9681d56b..c363ebe4165d 100644 --- a/builtin/providers/google/resource_compute_http_health_check.go +++ b/builtin/providers/google/resource_compute_http_health_check.go @@ -85,16 +85,27 @@ func resourceComputeHttpHealthCheckCreate(d *schema.ResourceData, meta interface // Build the parameter hchk := &compute.HttpHealthCheck{ - CheckIntervalSec: int64(d.Get("check_interval_sec").(int)), Description: d.Get("description").(string), - HealthyThreshold: int64(d.Get("healthy_threshold").(int)), Host: d.Get("host").(string), Name: d.Get("name").(string), - Port: int64(d.Get("port").(int)), RequestPath: d.Get("request_path").(string), - TimeoutSec: int64(d.Get("timeout_sec").(int)), - UnhealthyThreshold: int64(d.Get("unhealthy_threshold").(int)), } + if d.Get("check_interval_sec") != nil { + hchk.CheckIntervalSec = int64(d.Get("check_interval_sec").(int)) + } + if d.Get("health_threshold") != nil { + hchk.HealthyThreshold = int64(d.Get("healthy_threshold").(int)) + } + if d.Get("port") != nil { + hchk.Port = int64(d.Get("port").(int)) + } + if d.Get("timeout") != nil { + hchk.TimeoutSec = int64(d.Get("timeout_sec").(int)) + } + if d.Get("unhealthy_threshold") != nil { + hchk.UnhealthyThreshold = int64(d.Get("unhealthy_threshold").(int)) + } + log.Printf("[DEBUG] HttpHealthCheck insert request: %#v", hchk) op, err := config.clientCompute.HttpHealthChecks.Insert( config.Project, hchk).Do() @@ -136,16 +147,27 @@ func resourceComputeHttpHealthCheckUpdate(d *schema.ResourceData, meta interface // Build the parameter hchk := &compute.HttpHealthCheck{ - CheckIntervalSec: int64(d.Get("check_interval_sec").(int)), Description: d.Get("description").(string), - HealthyThreshold: int64(d.Get("healthy_threshold").(int)), Host: d.Get("host").(string), Name: d.Get("name").(string), - Port: int64(d.Get("port").(int)), RequestPath: d.Get("request_path").(string), - TimeoutSec: int64(d.Get("timeout_sec").(int)), - UnhealthyThreshold: int64(d.Get("unhealthy_threshold").(int)), } + if d.Get("check_interval_sec") != nil { + hchk.CheckIntervalSec = int64(d.Get("check_interval_sec").(int)) + } + if d.Get("health_threshold") != nil { + hchk.HealthyThreshold = int64(d.Get("healthy_threshold").(int)) + } + if d.Get("port") != nil { + hchk.Port = int64(d.Get("port").(int)) + } + if d.Get("timeout") != nil { + hchk.TimeoutSec = int64(d.Get("timeout_sec").(int)) + } + if d.Get("unhealthy_threshold") != nil { + hchk.UnhealthyThreshold = int64(d.Get("unhealthy_threshold").(int)) + } + log.Printf("[DEBUG] HttpHealthCheck patch request: %#v", hchk) op, err := config.clientCompute.HttpHealthChecks.Patch( config.Project, hchk.Name, hchk).Do() diff --git a/builtin/providers/google/resource_compute_target_pool.go b/builtin/providers/google/resource_compute_target_pool.go index b67cace8d0c5..9dcb5fb7adc4 100644 --- a/builtin/providers/google/resource_compute_target_pool.go +++ b/builtin/providers/google/resource_compute_target_pool.go @@ -31,6 +31,14 @@ func resourceComputeTargetPool() *schema.Resource { ForceNew: true, }, +/* + "failover_ratio": &schema.Schema{ + Type: schema.TypeFloat, + Optional: true, + ForceNew: true, + }, +*/ + "health_checks": &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -152,6 +160,9 @@ func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) e Name: d.Get("name").(string), SessionAffinity: d.Get("session_affinity").(string), } + if d.Get("failover_ratio") != nil { + tpool.FailoverRatio = d.Get("failover_ratio").(float64) + } log.Printf("[DEBUG] TargetPool insert request: %#v", tpool) op, err := config.clientCompute.TargetPools.Insert( config.Project, config.Region, tpool).Do() diff --git a/website/source/docs/providers/google/r/compute_address.html.markdown b/website/source/docs/providers/google/r/compute_address.html.markdown index 5365fa2b6e93..c0551c11fb9d 100644 --- a/website/source/docs/providers/google/r/compute_address.html.markdown +++ b/website/source/docs/providers/google/r/compute_address.html.markdown @@ -8,7 +8,10 @@ description: |- # google\_compute\_address -Creates a static IP address resource for Google Compute Engine. +Creates a static IP address resource for Google Compute Engine. For more information see +[the official documentation](https://cloud.google.com/compute/docs/instances-and-network) and +[API](https://cloud.google.com/compute/docs/reference/latest/addresses). + ## Example Usage @@ -31,3 +34,4 @@ The following attributes are exported: * `name` - The name of the resource. * `address` - The IP address that was allocated. +* `self_link` - The URI of the created resource. diff --git a/website/source/docs/providers/google/r/compute_forwarding_rule.html.markdown b/website/source/docs/providers/google/r/compute_forwarding_rule.html.markdown new file mode 100644 index 000000000000..9e8313189fc3 --- /dev/null +++ b/website/source/docs/providers/google/r/compute_forwarding_rule.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "google" +page_title: "Google: google_compute_forwarding_rule" +sidebar_current: "docs-google-resource-forwarding_rule" +description: |- + Manages a Target Pool within GCE. +--- + +# google\_compute\_forwarding\_rule + +Manages a Forwarding Rule within GCE. This binds an ip and port range to a target pool. For more +information see [the official +documentation](https://cloud.google.com/compute/docs/load-balancing/network/forwarding-rules) and +[API](https://cloud.google.com/compute/docs/reference/latest/forwardingRules). + +## Example Usage + +``` +resource "google_compute_forwarding_rule" "default" { + name = "test" + target = "${google_compute_target_pool.default.self_link}" + port_range = "80" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `description` - (Optional) Textual description field. + +* `ip_address` - (Optional) The static IP. (if not set, an ephemeral IP is +used). + +* `ip_protocol` - (Optional) The IP protocol to route, one of "TCP" "UDP" "AH" "ESP" or "SCTP". (default "TCP"). + +* `name` - (Required) A unique name for the resource, required by GCE. Changing + this forces a new resource to be created. + +* `port_range` - (Optional) A range e.g. "1024-2048" or a single port "1024" +(defaults to all ports!). + +* `target` - URL of target pool. + +## Attributes Reference + +The following attributes are exported: + +* `self_link` - The URL of the created resource. + +* `ip_address` - The IP address that was chosen (or specified). + + diff --git a/website/source/docs/providers/google/r/compute_http_health_check.html.markdown b/website/source/docs/providers/google/r/compute_http_health_check.html.markdown new file mode 100644 index 000000000000..4a4cd3481edb --- /dev/null +++ b/website/source/docs/providers/google/r/compute_http_health_check.html.markdown @@ -0,0 +1,57 @@ +--- +layout: "google" +page_title: "Google: google_compute_http_health_check" +sidebar_current: "docs-google-resource-http_health_check" +description: |- + Manages an HTTP Health Check within GCE. +--- + +# google\_compute\_http\_health\_check + +Manages an HTTP health check within GCE. This is used to monitor instances +behind load balancers. Timeouts or HTTP errors cause the instance to be +removed from the pool. For more information, see [the official +documentation](https://cloud.google.com/compute/docs/load-balancing/health-checks) +and +[API](https://cloud.google.com/compute/docs/reference/latest/httpHealthChecks). + +## Example Usage + +``` +resource "google_compute_http_health_check" "default" { + name = "test" + request_path = "/health_check" + check_interval_sec = 1 + timeout_sec = 1 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `check_interval_sec` - (Optional) How often to poll each instance (default 5). + +* `description` - (Optional) Textual description field. + +* `healthy_threshold` - (Optional) Consecutive successes required (default 2). + +* `host` - (Optional) HTTP host header field (default instance's public ip). + +* `name` - (Required) A unique name for the resource, required by GCE. + Changing this forces a new resource to be created. + +* `port` - (Optional) TCP port to connect to (default 80). + +* `request_path` - (Optional) URL path to query (default /). + +* `timeout_sec` - (Optional) How long before declaring failure (default 5). + +* `unhealthy_threshold` - (Optional) Consecutive failures required (default 2). + + +## Attributes Reference + +The following attributes are exported: + +* `self_link` - The URL of the created resource. diff --git a/website/source/docs/providers/google/r/compute_instance.html.markdown b/website/source/docs/providers/google/r/compute_instance.html.markdown index a30860947a34..217cd58f1c84 100644 --- a/website/source/docs/providers/google/r/compute_instance.html.markdown +++ b/website/source/docs/providers/google/r/compute_instance.html.markdown @@ -8,7 +8,11 @@ description: |- # google\_compute\_instance -Manages a VM instance resource within GCE. +Manages a VM instance resource within GCE. For more information see +[the official documentation](https://cloud.google.com/compute/docs/instances) +and +[API](https://cloud.google.com/compute/docs/reference/latest/instances). + ## Example Usage @@ -30,6 +34,12 @@ resource "google_compute_instance" "default" { metadata { foo = "bar" } + + service_accounts { + email = "default" + scopes = [ "https://www.googleapis.com/auth/devstorage.read_only" ] + } + } ``` @@ -60,6 +70,12 @@ The following arguments are supported: specified multiple times for multiple networks. Structure is documented below. +* `service_accounts` - (Optional) This can be specified multiple times + for multiple service accounts. Of particular importance is the + "default" service account which allows instances to access Google + Cloud APIs without needing to authenticate with additional keys. + Structure is documented below. + * `tags` - (Optional) Tags to attach to the instance. The `disk` block supports: @@ -82,6 +98,14 @@ The `network` block supports: * `address` - (Optional) The IP address of a reserved IP address to assign to this interface. +The `service_accounts` block supports: + +* `email` - (Required) The email (username) of the service account. + Use "default" for the default service account. + +* `scopes` - (Optional) The list of OAauth 2.0 scopes (URLs) this instance is + implicitly allowed to use, via this service account. + ## Attributes Reference The following attributes are exported: diff --git a/website/source/docs/providers/google/r/compute_target_pool.html.markdown b/website/source/docs/providers/google/r/compute_target_pool.html.markdown new file mode 100644 index 000000000000..1ce0397ebc37 --- /dev/null +++ b/website/source/docs/providers/google/r/compute_target_pool.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "google" +page_title: "Google: google_compute_target_pool" +sidebar_current: "docs-google-resource-target_pool" +description: |- + Manages a Target Pool within GCE. +--- + +# google\_compute\_target\_pool + +Manages a Target Pool within GCE. This is a collection of instances used as +target of a network load balancer (Forwarding Rule). For more information see +[the official +documentation](https://cloud.google.com/compute/docs/load-balancing/network/target-pools) +and [API](https://cloud.google.com/compute/docs/reference/latest/targetPools). + + +## Example Usage + +``` +resource "google_compute_target_pool" "default" { + name = "test" + instances = [ "us-central1-a/myinstance1", "us-central1-b/myinstance2" ] + health_checks = [ "${google_compute_http_health_check.default.name}" ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `backup_pool` - (Optional) URL to the backup target pool. Must also set + failover\_ratio. + +* `description` - (Optional) Textual description field. + +* `failover_ratio` - (Optional) Ratio (0 to 1) of failed nodes before using the + backup pool (which must also be set). This is currently not implemented, see + issue \#594. + +* `health_checks` - (Optional) List of zero or one healthcheck names. + +* `instances` - (Optional) List of instances in the pool. They can be given as + URLs, or in the form of "zone/name". Note that the instances need not exist + at the time of target pool creation, so there is no need to use the Terraform + interpolators to create a dependency on the instances from the target pool. + +* `name` - (Required) A unique name for the resource, required by GCE. Changing + this forces a new resource to be created. + +* `session_affinity` - (Optional) How to distribute load. Options are "NONE" (no affinity). "CLIENT\_IP" (hash of the source/dest addresses / ports), and "CLIENT\_IP\_PROTO" also includes the protocol (default "NONE"). + + +## Attributes Reference + +The following attributes are exported: + +* `self_link` - The URL of the created resource. + From 607fb93613c0b56747da26450b6cb8a1d0bb9f78 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Mon, 24 Nov 2014 08:13:28 -0500 Subject: [PATCH 4/7] Fix naming in health check attribute, add tests --- .../resource_compute_forwarding_rule_test.go | 125 ++++++++++++++++++ .../resource_compute_http_health_check.go | 2 +- ...resource_compute_http_health_check_test.go | 85 ++++++++++++ .../resource_compute_target_pool_test.go | 80 +++++++++++ 4 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 builtin/providers/google/resource_compute_forwarding_rule_test.go create mode 100644 builtin/providers/google/resource_compute_http_health_check_test.go create mode 100644 builtin/providers/google/resource_compute_target_pool_test.go diff --git a/builtin/providers/google/resource_compute_forwarding_rule_test.go b/builtin/providers/google/resource_compute_forwarding_rule_test.go new file mode 100644 index 000000000000..c3aa365df40c --- /dev/null +++ b/builtin/providers/google/resource_compute_forwarding_rule_test.go @@ -0,0 +1,125 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeForwardingRule_basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeForwardingRule_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeForwardingRuleExists( + "google_compute_forwarding_rule.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeForwardingRule_ip(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeForwardingRule_ip, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeForwardingRuleExists( + "google_compute_forwarding_rule.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeForwardingRuleDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_forwarding_rule" { + continue + } + + _, err := config.clientCompute.ForwardingRules.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("ForwardingRule still exists") + } + } + + return nil +} + +func testAccCheckComputeForwardingRuleExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.ForwardingRules.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("ForwardingRule not found") + } + + return nil + } +} + +const testAccComputeForwardingRule_basic = ` +resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "terraform-test" +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "terraform-test" + port_range = "80-81" + target = "${google_compute_target_pool.foobar-tp.self_link}" +} +` + +const testAccComputeForwardingRule_ip = ` +resource "google_compute_address" "foo" { + name = "foo" +} +resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "terraform-test" +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_address = "${google_compute_address.foo.address}" + ip_protocol = "TCP" + name = "terraform-test" + port_range = "80-81" + target = "${google_compute_target_pool.foobar-tp.self_link}" +} +` + diff --git a/builtin/providers/google/resource_compute_http_health_check.go b/builtin/providers/google/resource_compute_http_health_check.go index c363ebe4165d..f4887641a76f 100644 --- a/builtin/providers/google/resource_compute_http_health_check.go +++ b/builtin/providers/google/resource_compute_http_health_check.go @@ -71,7 +71,7 @@ func resourceComputeHttpHealthCheck() *schema.Resource { ForceNew: false, }, - "unhealthyThreshold": &schema.Schema{ + "unhealthy_threshold": &schema.Schema{ Type: schema.TypeInt, Optional: true, ForceNew: false, diff --git a/builtin/providers/google/resource_compute_http_health_check_test.go b/builtin/providers/google/resource_compute_http_health_check_test.go new file mode 100644 index 000000000000..45181a4cdbf7 --- /dev/null +++ b/builtin/providers/google/resource_compute_http_health_check_test.go @@ -0,0 +1,85 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeHttpHealthCheck_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeHttpHealthCheckDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeHttpHealthCheck_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpHealthCheckExists( + "google_compute_http_health_check.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeHttpHealthCheckDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_http_health_check" { + continue + } + + _, err := config.clientCompute.HttpHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("HttpHealthCheck still exists") + } + } + + return nil +} + +func testAccCheckComputeHttpHealthCheckExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.HttpHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("HttpHealthCheck not found") + } + + return nil + } +} + +const testAccComputeHttpHealthCheck_basic = ` +resource "google_compute_http_health_check" "foobar" { + check_interval_sec = 1 + description = "Resource created for Terraform acceptance testing" + healthy_threshold = 3 + host = "foobar" + name = "terraform-test" + port = "80" + request_path = "/health_check" + timeout_sec = 2 + unhealthy_threshold = 3 +} +` diff --git a/builtin/providers/google/resource_compute_target_pool_test.go b/builtin/providers/google/resource_compute_target_pool_test.go new file mode 100644 index 000000000000..4a65eaac65dd --- /dev/null +++ b/builtin/providers/google/resource_compute_target_pool_test.go @@ -0,0 +1,80 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeTargetPool_basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetPoolDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetPool_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetPoolExists( + "google_compute_target_pool.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeTargetPoolDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_target_pool" { + continue + } + + _, err := config.clientCompute.TargetPools.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("TargetPool still exists") + } + } + + return nil +} + +func testAccCheckComputeTargetPoolExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.TargetPools.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("TargetPool not found") + } + + return nil + } +} + +const testAccComputeTargetPool_basic = ` +resource "google_compute_target_pool" "foobar" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "terraform-test" + session_affinity = "CLIENT_IP_PROTO" +}` From 2509ebc0d138a50ccd26c86ef2b6301cc44b6f65 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Mon, 24 Nov 2014 18:06:57 -0500 Subject: [PATCH 5/7] Add TODO --- builtin/providers/google/resource_compute_target_pool.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/google/resource_compute_target_pool.go b/builtin/providers/google/resource_compute_target_pool.go index 9dcb5fb7adc4..c9b1fab5232d 100644 --- a/builtin/providers/google/resource_compute_target_pool.go +++ b/builtin/providers/google/resource_compute_target_pool.go @@ -32,6 +32,7 @@ func resourceComputeTargetPool() *schema.Resource { }, /* + // TODO(dcunnin): blocked on #594 "failover_ratio": &schema.Schema{ Type: schema.TypeFloat, Optional: true, From f0d502890ae499dc118d77fd570676a7106f26f6 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Wed, 28 Jan 2015 12:41:30 -0500 Subject: [PATCH 6/7] Fix selfLink -> self_link --- builtin/providers/google/resource_compute_address.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/google/resource_compute_address.go b/builtin/providers/google/resource_compute_address.go index 0fec05270369..98aa838c2318 100644 --- a/builtin/providers/google/resource_compute_address.go +++ b/builtin/providers/google/resource_compute_address.go @@ -28,7 +28,7 @@ func resourceComputeAddress() *schema.Resource { Computed: true, }, - "selfLink": &schema.Schema{ + "self_link": &schema.Schema{ Type: schema.TypeString, Computed: true, }, @@ -96,7 +96,7 @@ func resourceComputeAddressRead(d *schema.ResourceData, meta interface{}) error } d.Set("address", addr.Address) - d.Set("selfLink", addr.SelfLink) + d.Set("self_link", addr.SelfLink) return nil } From 5a89196a715ef7d61f7c070133af31c984082a5d Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Wed, 28 Jan 2015 12:41:45 -0500 Subject: [PATCH 7/7] Enable failover_ratio per resolution of 594 --- builtin/providers/google/resource_compute_target_pool.go | 3 --- .../docs/providers/google/r/compute_target_pool.html.markdown | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/builtin/providers/google/resource_compute_target_pool.go b/builtin/providers/google/resource_compute_target_pool.go index c9b1fab5232d..bbf0959001b6 100644 --- a/builtin/providers/google/resource_compute_target_pool.go +++ b/builtin/providers/google/resource_compute_target_pool.go @@ -31,14 +31,11 @@ func resourceComputeTargetPool() *schema.Resource { ForceNew: true, }, -/* - // TODO(dcunnin): blocked on #594 "failover_ratio": &schema.Schema{ Type: schema.TypeFloat, Optional: true, ForceNew: true, }, -*/ "health_checks": &schema.Schema{ Type: schema.TypeList, diff --git a/website/source/docs/providers/google/r/compute_target_pool.html.markdown b/website/source/docs/providers/google/r/compute_target_pool.html.markdown index 1ce0397ebc37..1efc5905e06b 100644 --- a/website/source/docs/providers/google/r/compute_target_pool.html.markdown +++ b/website/source/docs/providers/google/r/compute_target_pool.html.markdown @@ -35,8 +35,7 @@ The following arguments are supported: * `description` - (Optional) Textual description field. * `failover_ratio` - (Optional) Ratio (0 to 1) of failed nodes before using the - backup pool (which must also be set). This is currently not implemented, see - issue \#594. + backup pool (which must also be set). * `health_checks` - (Optional) List of zero or one healthcheck names.