mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-03 01:01:06 +00:00
Add generated BackendBucket resource + transport.go (#1064)
* Add generated BackendBucket resource * add ForceNew back to name * Add log statements back
This commit is contained in:
parent
5e907c7186
commit
9900e6a4c7
@ -1,3 +1,17 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE ***
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// This file is automatically generated and manual changes will be
|
||||
// clobbered when the file is regenerated.
|
||||
//
|
||||
// Please read more about how to change this file in
|
||||
// .github/CONTRIBUTING.md.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
package google
|
||||
|
||||
import (
|
||||
@ -5,7 +19,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"google.golang.org/api/compute/v1"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
func resourceComputeBackendBucket() *schema.Resource {
|
||||
@ -16,40 +30,38 @@ func resourceComputeBackendBucket() *schema.Resource {
|
||||
Delete: resourceComputeBackendBucketDelete,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
State: resourceComputeBackendBucketImport,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateGCPName,
|
||||
},
|
||||
|
||||
"bucket_name": &schema.Schema{
|
||||
"bucket_name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"description": &schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateRegexp(`^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`),
|
||||
},
|
||||
"description": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"enable_cdn": &schema.Schema{
|
||||
"enable_cdn": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"creation_timestamp": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"project": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"self_link": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
@ -61,38 +73,43 @@ func resourceComputeBackendBucket() *schema.Resource {
|
||||
func resourceComputeBackendBucketCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
bucket := compute.BackendBucket{
|
||||
Name: d.Get("name").(string),
|
||||
BucketName: d.Get("bucket_name").(string),
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("description"); ok {
|
||||
bucket.Description = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("enable_cdn"); ok {
|
||||
bucket.EnableCdn = v.(bool)
|
||||
}
|
||||
|
||||
project, err := getProject(d, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Creating new Backend Bucket: %#v", bucket)
|
||||
op, err := config.clientCompute.BackendBuckets.Insert(
|
||||
project, &bucket).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating backend bucket: %s", err)
|
||||
obj := map[string]interface{}{
|
||||
"bucketName": expandComputeBackendBucketBucketName(d.Get("bucket_name")),
|
||||
"description": expandComputeBackendBucketDescription(d.Get("description")),
|
||||
"enableCdn": expandComputeBackendBucketEnableCdn(d.Get("enable_cdn")),
|
||||
"name": expandComputeBackendBucketName(d.Get("name")),
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Waiting for new backend bucket, operation: %#v", op)
|
||||
url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/backendBuckets")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Creating new BackendBucket: %#v", obj)
|
||||
res, err := Post(config, url, obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating BackendBucket: %s", err)
|
||||
}
|
||||
|
||||
// Store the ID now
|
||||
d.SetId(bucket.Name)
|
||||
id, err := replaceVars(d, config, "{{name}}")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error constructing id: %s", err)
|
||||
}
|
||||
d.SetId(id)
|
||||
|
||||
// Wait for the operation to complete
|
||||
waitErr := computeOperationWait(config.clientCompute, op, project, "Creating Backend Bucket")
|
||||
op := &compute.Operation{}
|
||||
err = Convert(res, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
waitErr := computeOperationWait(config.clientCompute, op, project, "Creating BackendBucket")
|
||||
if waitErr != nil {
|
||||
// The resource didn't actually create
|
||||
d.SetId("")
|
||||
@ -110,18 +127,23 @@ func resourceComputeBackendBucketRead(d *schema.ResourceData, meta interface{})
|
||||
return err
|
||||
}
|
||||
|
||||
bucket, err := config.clientCompute.BackendBuckets.Get(
|
||||
project, d.Id()).Do()
|
||||
url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/backendBuckets/{{name}}")
|
||||
if err != nil {
|
||||
return handleNotFoundError(err, d, fmt.Sprintf("Backend Bucket %q", d.Get("name").(string)))
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set("name", bucket.Name)
|
||||
d.Set("bucket_name", bucket.BucketName)
|
||||
d.Set("description", bucket.Description)
|
||||
d.Set("enable_cdn", bucket.EnableCdn)
|
||||
res, err := Get(config, url)
|
||||
if err != nil {
|
||||
return handleNotFoundError(err, d, fmt.Sprintf("ComputeBackendBucket %q", d.Id()))
|
||||
}
|
||||
|
||||
d.Set("bucket_name", flattenComputeBackendBucketBucketName(res["bucketName"]))
|
||||
d.Set("creation_timestamp", flattenComputeBackendBucketCreationTimestamp(res["creationTimestamp"]))
|
||||
d.Set("description", flattenComputeBackendBucketDescription(res["description"]))
|
||||
d.Set("enable_cdn", flattenComputeBackendBucketEnableCdn(res["enableCdn"]))
|
||||
d.Set("name", flattenComputeBackendBucketName(res["name"]))
|
||||
d.Set("self_link", res["selfLink"])
|
||||
d.Set("project", project)
|
||||
d.Set("self_link", bucket.SelfLink)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -134,30 +156,31 @@ func resourceComputeBackendBucketUpdate(d *schema.ResourceData, meta interface{}
|
||||
return err
|
||||
}
|
||||
|
||||
bucket := compute.BackendBucket{
|
||||
Name: d.Get("name").(string),
|
||||
BucketName: d.Get("bucket_name").(string),
|
||||
obj := map[string]interface{}{
|
||||
"bucketName": expandComputeBackendBucketBucketName(d.Get("bucket_name")),
|
||||
"description": expandComputeBackendBucketDescription(d.Get("description")),
|
||||
"enableCdn": expandComputeBackendBucketEnableCdn(d.Get("enable_cdn")),
|
||||
"name": expandComputeBackendBucketName(d.Get("name")),
|
||||
}
|
||||
|
||||
// Optional things
|
||||
if v, ok := d.GetOk("description"); ok {
|
||||
bucket.Description = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("enable_cdn"); ok {
|
||||
bucket.EnableCdn = v.(bool)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Updating existing Backend Bucket %q: %#v", d.Id(), bucket)
|
||||
op, err := config.clientCompute.BackendBuckets.Update(
|
||||
project, d.Id(), &bucket).Do()
|
||||
url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/backendBuckets/{{name}}")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating backend bucket: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId(bucket.Name)
|
||||
log.Printf("[DEBUG] Updating BackendBucket %q: %#v", d.Id(), obj)
|
||||
res, err := Put(config, url, obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating BackendBucket %q: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
err = computeOperationWait(config.clientCompute, op, project, "Updating Backend Bucket")
|
||||
op := &compute.Operation{}
|
||||
err = Convert(res, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = computeOperationWait(config.clientCompute, op, project, "Updating BackendBucket")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -173,18 +196,68 @@ func resourceComputeBackendBucketDelete(d *schema.ResourceData, meta interface{}
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Deleting backend bucket %s", d.Id())
|
||||
op, err := config.clientCompute.BackendBuckets.Delete(
|
||||
project, d.Id()).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting backend bucket: %s", err)
|
||||
}
|
||||
|
||||
err = computeOperationWait(config.clientCompute, op, project, "Deleting Backend Bucket")
|
||||
url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/backendBuckets/{{name}}")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Deleting BackendBucket %q", d.Id())
|
||||
res, err := Delete(config, url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting BackendBucket %q: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
op := &compute.Operation{}
|
||||
err = Convert(res, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = computeOperationWait(config.clientCompute, op, project, "Deleting BackendBucket")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceComputeBackendBucketImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
d.Set("name", d.Id())
|
||||
return []*schema.ResourceData{d}, nil
|
||||
}
|
||||
|
||||
func flattenComputeBackendBucketBucketName(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func flattenComputeBackendBucketCreationTimestamp(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func flattenComputeBackendBucketDescription(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func flattenComputeBackendBucketEnableCdn(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func flattenComputeBackendBucketName(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func expandComputeBackendBucketBucketName(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func expandComputeBackendBucketDescription(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func expandComputeBackendBucketEnableCdn(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func expandComputeBackendBucketName(v interface{}) interface{} {
|
||||
return v
|
||||
}
|
||||
|
176
google/transport.go
Normal file
176
google/transport.go
Normal file
@ -0,0 +1,176 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"reflect"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
type serializableBody struct {
|
||||
body map[string]interface{}
|
||||
|
||||
// ForceSendFields is a list of field names (e.g. "UtilizationTarget")
|
||||
// to unconditionally include in API requests. By default, fields with
|
||||
// empty values are omitted from API requests. However, any non-pointer,
|
||||
// non-interface field appearing in ForceSendFields will be sent to the
|
||||
// server regardless of whether the field is empty or not. This may be
|
||||
// used to include empty fields in Patch requests.
|
||||
ForceSendFields []string
|
||||
|
||||
// NullFields is a list of field names (e.g. "UtilizationTarget") to
|
||||
// include in API requests with the JSON null value. By default, fields
|
||||
// with empty values are omitted from API requests. However, any field
|
||||
// with an empty value appearing in NullFields will be sent to the
|
||||
// server as null. It is an error if a field in this list has a
|
||||
// non-empty value. This may be used to include null fields in Patch
|
||||
// requests.
|
||||
NullFields []string
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON encoding of schema containing only selected fields.
|
||||
// A field is selected if any of the following is true:
|
||||
// * it has a non-empty value
|
||||
// * its field name is present in forceSendFields and it is not a nil pointer or nil interface
|
||||
// * its field name is present in nullFields.
|
||||
func (b *serializableBody) MarshalJSON() ([]byte, error) {
|
||||
// By default, all fields in a map are added to the json output
|
||||
// This changes that to remove the entry with an empty value.
|
||||
// This mimics the "omitempty" behavior.
|
||||
|
||||
// The "omitempty" option specifies that the field should be omitted
|
||||
// from the encoding if the field has an empty value, defined as
|
||||
// false, 0, a nil pointer, a nil interface value, and any empty array,
|
||||
// slice, map, or string.
|
||||
|
||||
// TODO: Add support for ForceSendFields and NullFields.
|
||||
for k, v := range b.body {
|
||||
if isEmptyValue(reflect.ValueOf(v)) {
|
||||
delete(b.body, k)
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(b.body)
|
||||
}
|
||||
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Post(config *Config, url string, body map[string]interface{}) (map[string]interface{}, error) {
|
||||
return sendRequest(config, "POST", url, body)
|
||||
}
|
||||
|
||||
func Get(config *Config, url string) (map[string]interface{}, error) {
|
||||
return sendRequest(config, "GET", url, nil)
|
||||
}
|
||||
|
||||
func Put(config *Config, url string, body map[string]interface{}) (map[string]interface{}, error) {
|
||||
return sendRequest(config, "PUT", url, body)
|
||||
}
|
||||
|
||||
func Delete(config *Config, url string) (map[string]interface{}, error) {
|
||||
return sendRequest(config, "DELETE", url, nil)
|
||||
}
|
||||
|
||||
func sendRequest(config *Config, method, url string, body map[string]interface{}) (map[string]interface{}, error) {
|
||||
reqHeaders := make(http.Header)
|
||||
reqHeaders.Set("User-Agent", config.userAgent)
|
||||
reqHeaders.Set("Content-Type", "application/json")
|
||||
|
||||
var buf bytes.Buffer
|
||||
if body != nil {
|
||||
err := json.NewEncoder(&buf).Encode(&serializableBody{
|
||||
body: body})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url+"?alt=json", &buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = reqHeaders
|
||||
res, err := config.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func replaceVars(d TerraformResourceData, config *Config, linkTmpl string) (string, error) {
|
||||
re := regexp.MustCompile("{{([[:word:]]+)}}")
|
||||
var project, region, zone string
|
||||
var err error
|
||||
|
||||
if strings.Contains(linkTmpl, "{{project}}") {
|
||||
project, err = getProject(d, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(linkTmpl, "{{region}}") {
|
||||
region, err = getRegion(d, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(linkTmpl, "{{zone}}") {
|
||||
zone, err = getZone(d, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
replaceFunc := func(s string) string {
|
||||
m := re.FindStringSubmatch(s)[1]
|
||||
if m == "project" {
|
||||
return project
|
||||
}
|
||||
if m == "region" {
|
||||
return region
|
||||
}
|
||||
if m == "zone" {
|
||||
return zone
|
||||
}
|
||||
v, ok := d.GetOk(m)
|
||||
if ok {
|
||||
return v.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
return re.ReplaceAllStringFunc(linkTmpl, replaceFunc), nil
|
||||
}
|
96
google/transport_test.go
Normal file
96
google/transport_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReplaceVars(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Template string
|
||||
SchemaValues map[string]interface{}
|
||||
Config *Config
|
||||
Expected string
|
||||
ExpectedError bool
|
||||
}{
|
||||
"unspecified project fails": {
|
||||
Template: "projects/{{project}}/global/images",
|
||||
ExpectedError: true,
|
||||
},
|
||||
"unspecified region fails": {
|
||||
Template: "projects/{{project}}/regions/{{region}}/subnetworks",
|
||||
Config: &Config{
|
||||
Project: "default-project",
|
||||
},
|
||||
ExpectedError: true,
|
||||
},
|
||||
"unspecified zone fails": {
|
||||
Template: "projects/{{project}}/zones/{{zone}}/instances",
|
||||
Config: &Config{
|
||||
Project: "default-project",
|
||||
},
|
||||
ExpectedError: true,
|
||||
},
|
||||
"regional with default values": {
|
||||
Template: "projects/{{project}}/regions/{{region}}/subnetworks",
|
||||
Config: &Config{
|
||||
Project: "default-project",
|
||||
Region: "default-region",
|
||||
},
|
||||
Expected: "projects/default-project/regions/default-region/subnetworks",
|
||||
},
|
||||
"zonal with default values": {
|
||||
Template: "projects/{{project}}/zones/{{zone}}/instances",
|
||||
Config: &Config{
|
||||
Project: "default-project",
|
||||
Zone: "default-zone",
|
||||
},
|
||||
Expected: "projects/default-project/zones/default-zone/instances",
|
||||
},
|
||||
"regional schema values": {
|
||||
Template: "projects/{{project}}/regions/{{region}}/subnetworks/{{name}}",
|
||||
SchemaValues: map[string]interface{}{
|
||||
"project": "project1",
|
||||
"region": "region1",
|
||||
"name": "subnetwork1",
|
||||
},
|
||||
Expected: "projects/project1/regions/region1/subnetworks/subnetwork1",
|
||||
},
|
||||
"zonal schema values": {
|
||||
Template: "projects/{{project}}/zones/{{zone}}/instances/{{name}}",
|
||||
SchemaValues: map[string]interface{}{
|
||||
"project": "project1",
|
||||
"zone": "zone1",
|
||||
"name": "instance1",
|
||||
},
|
||||
Expected: "projects/project1/zones/zone1/instances/instance1",
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range cases {
|
||||
d := &ResourceDataMock{
|
||||
FieldsInSchema: tc.SchemaValues,
|
||||
}
|
||||
|
||||
config := tc.Config
|
||||
if config == nil {
|
||||
config = &Config{}
|
||||
}
|
||||
|
||||
v, err := replaceVars(d, config, tc.Template)
|
||||
|
||||
if err != nil {
|
||||
if !tc.ExpectedError {
|
||||
t.Errorf("bad: %s; unexpected error %s", tn, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.ExpectedError {
|
||||
t.Errorf("bad: %s; expected error", tn)
|
||||
}
|
||||
|
||||
if v != tc.Expected {
|
||||
t.Errorf("bad: %s; expected %q, got %q", tn, tc.Expected, v)
|
||||
}
|
||||
}
|
||||
}
|
@ -52,6 +52,8 @@ The following arguments are supported:
|
||||
|
||||
In addition to the arguments listed above, the following computed attributes are exported:
|
||||
|
||||
* `creation_timestamp` - Creation timestamp in RFC3339 text format.
|
||||
|
||||
* `self_link` - The URI of the created resource.
|
||||
|
||||
## Import
|
||||
|
Loading…
Reference in New Issue
Block a user