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 generated resources for http(s)_health_check, updates to backend_bucket, and import helpers #1177

Merged
merged 6 commits into from
Mar 10, 2018
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
66 changes: 66 additions & 0 deletions google/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package google

import (
"fmt"
"regexp"
"strings"
)

// Parse an import id extracting field values using the given list of regexes.
// They are applied in order. The first in the list is tried first.
//
// e.g:
// - projects/(?P<project>[^/]+)/regions/(?P<region>[^/]+)/subnetworks/(?P<name>[^/]+) (applied first)
// - (?P<project>[^/]+)/(?P<region>[^/]+)/(?P<name>[^/]+),
// - (?P<name>[^/]+) (applied last)
func parseImportId(idRegexes []string, d TerraformResourceData, config *Config) error {
for _, idFormat := range idRegexes {
re, err := regexp.Compile(idFormat)

if err != nil {
return fmt.Errorf("Import is not supported. Invalid regex formats.")
}

if fieldValues := re.FindStringSubmatch(d.Id()); fieldValues != nil {
// Starting at index 1, the first match is the full string.
for i := 1; i < len(fieldValues); i++ {
fieldName := re.SubexpNames()[i]
d.Set(fieldName, fieldValues[i])
}

// The first id format is applied first and contains all the fields.
err := setDefaultValues(idRegexes[0], d, config)
if err != nil {
return err
}

return nil
}
}
return fmt.Errorf("Import id %q doesn't match any of the accepted formats: %v", d.Id(), idRegexes)
}

func setDefaultValues(idRegex string, d TerraformResourceData, config *Config) error {
if _, ok := d.GetOk("project"); !ok && strings.Contains(idRegex, "?P<project>") {
project, err := getProject(d, config)
if err != nil {
return err
}
d.Set("project", project)
}
if _, ok := d.GetOk("region"); !ok && strings.Contains(idRegex, "?P<region>") {
region, err := getRegion(d, config)
if err != nil {
return err
}
d.Set("region", region)
}
if _, ok := d.GetOk("zone"); !ok && strings.Contains(idRegex, "?P<zone>") {
zone, err := getZone(d, config)
if err != nil {
return err
}
d.Set("zone", zone)
}
return nil
}
147 changes: 147 additions & 0 deletions google/import_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package google

import (
"testing"
)

func TestParseImportId(t *testing.T) {
regionalIdRegexes := []string{
"projects/(?P<project>[^/]+)/regions/(?P<region>[^/]+)/subnetworks/(?P<name>[^/]+)",
"(?P<project>[^/]+)/(?P<region>[^/]+)/(?P<name>[^/]+)",
"(?P<name>[^/]+)",
}
zonalIdRegexes := []string{
"projects/(?P<project>[^/]+)/zones/(?P<zone>[^/]+)/instances/(?P<name>[^/]+)",
"(?P<project>[^/]+)/(?P<zone>[^/]+)/(?P<name>[^/]+)",
"(?P<name>[^/]+)",
}
multipleNondefaultIdRegexes := []string{
"projects/(?P<project>[^/]+)/zones/(?P<zone>[^/]+)/clusters/(?P<cluster>[^/]+)/nodePools/(?P<name>[^/]+)",
"(?P<project>[^/]+)/(?P<zone>[^/]+)/(?P<cluster>[^/]+)/(?P<name>[^/]+)",
"(?P<cluster>[^/]+)/(?P<name>[^/]+)",
}

cases := map[string]struct {
ImportId string
IdRegexes []string
Config *Config
ExpectedSchemaValues map[string]interface{}
ExpectError bool
}{
"full self_link": {
ImportId: "https://www.googleapis.com/compute/v1/projects/my-project/regions/my-region/subnetworks/my-subnetwork",
IdRegexes: regionalIdRegexes,
ExpectedSchemaValues: map[string]interface{}{
"project": "my-project",
"region": "my-region",
"name": "my-subnetwork",
},
},
"relative self_link": {
ImportId: "projects/my-project/regions/my-region/subnetworks/my-subnetwork",
IdRegexes: regionalIdRegexes,
ExpectedSchemaValues: map[string]interface{}{
"project": "my-project",
"region": "my-region",
"name": "my-subnetwork",
},
},
"short id": {
ImportId: "my-project/my-region/my-subnetwork",
IdRegexes: regionalIdRegexes,
ExpectedSchemaValues: map[string]interface{}{
"project": "my-project",
"region": "my-region",
"name": "my-subnetwork",
},
},
"short id with default project and region": {
ImportId: "my-subnetwork",
Config: &Config{
Project: "default-project",
Region: "default-region",
},
IdRegexes: regionalIdRegexes,
ExpectedSchemaValues: map[string]interface{}{
"project": "default-project",
"region": "default-region",
"name": "my-subnetwork",
},
},
"short id with default project and zone": {
ImportId: "my-instance",
Config: &Config{
Project: "default-project",
Zone: "default-zone",
},
IdRegexes: zonalIdRegexes,
ExpectedSchemaValues: map[string]interface{}{
"project": "default-project",
"zone": "default-zone",
"name": "my-instance",
},
},
"short id with two nondefault fields with default project and zone": {
ImportId: "my-cluster/my-node-pool",
Config: &Config{
Project: "default-project",
Zone: "default-zone",
},
IdRegexes: multipleNondefaultIdRegexes,
ExpectedSchemaValues: map[string]interface{}{
"project": "default-project",
"zone": "default-zone",
"cluster": "my-cluster",
"name": "my-node-pool",
},
},
"short id with default project and region inferred from default zone": {
ImportId: "my-subnetwork",
Config: &Config{
Project: "default-project",
Zone: "us-east1-a",
},
IdRegexes: regionalIdRegexes,
ExpectedSchemaValues: map[string]interface{}{
"project": "default-project",
"region": "us-east1",
"name": "my-subnetwork",
},
},
"invalid import id": {
ImportId: "i/n/v/a/l/i/d",
IdRegexes: regionalIdRegexes,
ExpectError: true,
},
"provider-level defaults not set": {
ImportId: "my-subnetwork",
IdRegexes: regionalIdRegexes,
ExpectError: true,
},
}

for tn, tc := range cases {
d := &ResourceDataMock{
FieldsInSchema: make(map[string]interface{}),
id: tc.ImportId,
}
config := tc.Config
if config == nil {
config = &Config{}
}

if err := parseImportId(tc.IdRegexes, d, config); err == nil {
for k, expectedValue := range tc.ExpectedSchemaValues {
if v, ok := d.GetOk(k); ok {
if v != expectedValue {
t.Errorf("%s failed; Expected value %q for field %q, got %q", tn, expectedValue, k, v)
}
} else {
t.Errorf("%s failed; Expected a value for field %q", tn, k)
}
}
} else if !tc.ExpectError {
t.Errorf("%s failed; unexpected error: %s", tn, err)
}
}
}
36 changes: 31 additions & 5 deletions google/resource_compute_backend_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package google
import (
"fmt"
"log"
"time"

"github.com/hashicorp/terraform/helper/schema"
compute "google.golang.org/api/compute/v1"
Expand All @@ -33,6 +34,12 @@ func resourceComputeBackendBucket() *schema.Resource {
State: resourceComputeBackendBucketImport,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(4 * time.Minute),
Update: schema.DefaultTimeout(4 * time.Minute),
Delete: schema.DefaultTimeout(4 * time.Minute),
},

Schema: map[string]*schema.Schema{
"bucket_name": {
Type: schema.TypeString,
Expand Down Expand Up @@ -109,7 +116,10 @@ func resourceComputeBackendBucketCreate(d *schema.ResourceData, meta interface{}
return err
}

waitErr := computeOperationWait(config.clientCompute, op, project, "Creating BackendBucket")
waitErr := computeOperationWaitTime(
config.clientCompute, op, project, "Creating BackendBucket",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if waitErr != nil {
// The resource didn't actually create
d.SetId("")
Expand Down Expand Up @@ -169,7 +179,8 @@ func resourceComputeBackendBucketUpdate(d *schema.ResourceData, meta interface{}
}

log.Printf("[DEBUG] Updating BackendBucket %q: %#v", d.Id(), obj)
res, err := Put(config, url, obj)
res, err := sendRequest(config, "PUT", url, obj)

if err != nil {
return fmt.Errorf("Error updating BackendBucket %q: %s", d.Id(), err)
}
Expand All @@ -180,7 +191,10 @@ func resourceComputeBackendBucketUpdate(d *schema.ResourceData, meta interface{}
return err
}

err = computeOperationWait(config.clientCompute, op, project, "Updating BackendBucket")
err = computeOperationWaitTime(
config.clientCompute, op, project, "Updating BackendBucket",
int(d.Timeout(schema.TimeoutUpdate).Minutes()))

if err != nil {
return err
}
Expand Down Expand Up @@ -213,7 +227,10 @@ func resourceComputeBackendBucketDelete(d *schema.ResourceData, meta interface{}
return err
}

err = computeOperationWait(config.clientCompute, op, project, "Deleting BackendBucket")
err = computeOperationWaitTime(
config.clientCompute, op, project, "Deleting BackendBucket",
int(d.Timeout(schema.TimeoutDelete).Minutes()))

if err != nil {
return err
}
Expand All @@ -222,7 +239,16 @@ func resourceComputeBackendBucketDelete(d *schema.ResourceData, meta interface{}
}

func resourceComputeBackendBucketImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
d.Set("name", d.Id())
config := meta.(*Config)
parseImportId([]string{"projects/(?P<project>[^/]+)/global/backendBuckets/(?P<name>[^/]+)", "(?P<project>[^/]+)/(?P<name>[^/]+)", "(?P<name>[^/]+)"}, d, config)

// Replace import id for the resource id
id, err := replaceVars(d, config, "{{name}}")
if err != nil {
return nil, fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)

return []*schema.ResourceData{d}, nil
}

Expand Down
Loading