mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-07 03:01:06 +00:00
Merge branch 'master' into paddy_10984_better_image_resolution
This commit is contained in:
commit
a720f10b27
@ -13,6 +13,7 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
"golang.org/x/oauth2/google"
|
||||||
"golang.org/x/oauth2/jwt"
|
"golang.org/x/oauth2/jwt"
|
||||||
|
"google.golang.org/api/cloudbilling/v1"
|
||||||
"google.golang.org/api/cloudresourcemanager/v1"
|
"google.golang.org/api/cloudresourcemanager/v1"
|
||||||
"google.golang.org/api/compute/v1"
|
"google.golang.org/api/compute/v1"
|
||||||
"google.golang.org/api/container/v1"
|
"google.golang.org/api/container/v1"
|
||||||
@ -31,6 +32,7 @@ type Config struct {
|
|||||||
Project string
|
Project string
|
||||||
Region string
|
Region string
|
||||||
|
|
||||||
|
clientBilling *cloudbilling.Service
|
||||||
clientCompute *compute.Service
|
clientCompute *compute.Service
|
||||||
clientContainer *container.Service
|
clientContainer *container.Service
|
||||||
clientDns *dns.Service
|
clientDns *dns.Service
|
||||||
@ -160,6 +162,13 @@ func (c *Config) loadAndValidate() error {
|
|||||||
}
|
}
|
||||||
c.clientServiceMan.UserAgent = userAgent
|
c.clientServiceMan.UserAgent = userAgent
|
||||||
|
|
||||||
|
log.Printf("[INFO] Instantiating Google Cloud Billing Client...")
|
||||||
|
c.clientBilling, err = cloudbilling.New(client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.clientBilling.UserAgent = userAgent
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
80
data_source_google_compute_zones.go
Normal file
80
data_source_google_compute_zones.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
compute "google.golang.org/api/compute/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dataSourceGoogleComputeZones() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Read: dataSourceGoogleComputeZonesRead,
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"region": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"names": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if value != "UP" && value != "DOWN" {
|
||||||
|
es = append(es, fmt.Errorf("%q can only be 'UP' or 'DOWN' (%q given)", k, value))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dataSourceGoogleComputeZonesRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
region := config.Region
|
||||||
|
if r, ok := d.GetOk("region"); ok {
|
||||||
|
region = r.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
regionUrl := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/regions/%s",
|
||||||
|
config.Project, region)
|
||||||
|
filter := fmt.Sprintf("(region eq %s)", regionUrl)
|
||||||
|
|
||||||
|
if s, ok := d.GetOk("status"); ok {
|
||||||
|
filter += fmt.Sprintf(" (status eq %s)", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
call := config.clientCompute.Zones.List(config.Project).Filter(filter)
|
||||||
|
|
||||||
|
resp, err := call.Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
zones := flattenZones(resp.Items)
|
||||||
|
log.Printf("[DEBUG] Received Google Compute Zones: %q", zones)
|
||||||
|
|
||||||
|
d.Set("names", zones)
|
||||||
|
d.SetId(time.Now().UTC().String())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenZones(zones []*compute.Zone) []string {
|
||||||
|
result := make([]string, len(zones), len(zones))
|
||||||
|
for i, zone := range zones {
|
||||||
|
result[i] = zone.Name
|
||||||
|
}
|
||||||
|
sort.Strings(result)
|
||||||
|
return result
|
||||||
|
}
|
70
data_source_google_compute_zones_test.go
Normal file
70
data_source_google_compute_zones_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccGoogleComputeZones_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccCheckGoogleComputeZonesConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleComputeZonesMeta("data.google_compute_zones.available"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckGoogleComputeZonesMeta(n string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Can't find zones data source: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return errors.New("zones data source ID not set.")
|
||||||
|
}
|
||||||
|
|
||||||
|
count, ok := rs.Primary.Attributes["names.#"]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("can't find 'names' attribute")
|
||||||
|
}
|
||||||
|
|
||||||
|
noOfNames, err := strconv.Atoi(count)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to read number of zones")
|
||||||
|
}
|
||||||
|
if noOfNames < 2 {
|
||||||
|
return fmt.Errorf("expected at least 2 zones, received %d, this is most likely a bug",
|
||||||
|
noOfNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < noOfNames; i++ {
|
||||||
|
idx := "names." + strconv.Itoa(i)
|
||||||
|
v, ok := rs.Primary.Attributes[idx]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("zone list is corrupt (%q not found), this is definitely a bug", idx)
|
||||||
|
}
|
||||||
|
if len(v) < 1 {
|
||||||
|
return fmt.Errorf("Empty zone name (%q), this is definitely a bug", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccCheckGoogleComputeZonesConfig = `
|
||||||
|
data "google_compute_zones" "available" {}
|
||||||
|
`
|
@ -58,6 +58,7 @@ func Provider() terraform.ResourceProvider {
|
|||||||
|
|
||||||
DataSourcesMap: map[string]*schema.Resource{
|
DataSourcesMap: map[string]*schema.Resource{
|
||||||
"google_iam_policy": dataSourceGoogleIamPolicy(),
|
"google_iam_policy": dataSourceGoogleIamPolicy(),
|
||||||
|
"google_compute_zones": dataSourceGoogleComputeZones(),
|
||||||
},
|
},
|
||||||
|
|
||||||
ResourcesMap: map[string]*schema.Resource{
|
ResourcesMap: map[string]*schema.Resource{
|
||||||
|
@ -671,6 +671,10 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
|
|||||||
|
|
||||||
d.Set("can_ip_forward", instance.CanIpForward)
|
d.Set("can_ip_forward", instance.CanIpForward)
|
||||||
|
|
||||||
|
machineTypeResource := strings.Split(instance.MachineType, "/")
|
||||||
|
machineType := machineTypeResource[len(machineTypeResource)-1]
|
||||||
|
d.Set("machine_type", machineType)
|
||||||
|
|
||||||
// Set the service accounts
|
// Set the service accounts
|
||||||
serviceAccounts := make([]map[string]interface{}, 0, 1)
|
serviceAccounts := make([]map[string]interface{}, 0, 1)
|
||||||
for _, serviceAccount := range instance.ServiceAccounts {
|
for _, serviceAccount := range instance.ServiceAccounts {
|
||||||
@ -797,6 +801,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
|
|||||||
"auto_delete": d.Get(fmt.Sprintf("disk.%d.auto_delete", i)),
|
"auto_delete": d.Get(fmt.Sprintf("disk.%d.auto_delete", i)),
|
||||||
"size": d.Get(fmt.Sprintf("disk.%d.size", i)),
|
"size": d.Get(fmt.Sprintf("disk.%d.size", i)),
|
||||||
"device_name": d.Get(fmt.Sprintf("disk.%d.device_name", i)),
|
"device_name": d.Get(fmt.Sprintf("disk.%d.device_name", i)),
|
||||||
|
"disk_encryption_key_raw": d.Get(fmt.Sprintf("disk.%d.disk_encryption_key_raw", i)),
|
||||||
}
|
}
|
||||||
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
|
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
|
||||||
di["disk_encryption_key_sha256"] = disk.DiskEncryptionKey.Sha256
|
di["disk_encryption_key_sha256"] = disk.DiskEncryptionKey.Sha256
|
||||||
|
@ -216,17 +216,33 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf
|
|||||||
return config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
|
return config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
|
||||||
}
|
}
|
||||||
|
|
||||||
resource, err := getZonalResourceFromRegion(getInstanceGroupManager, region, config.clientCompute, project)
|
var manager *compute.InstanceGroupManager
|
||||||
if err != nil {
|
var e error
|
||||||
return err
|
if zone, ok := d.GetOk("zone"); ok {
|
||||||
|
manager, e = config.clientCompute.InstanceGroupManagers.Get(project, zone.(string), d.Id()).Do()
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
}
|
}
|
||||||
if resource == nil {
|
} else {
|
||||||
|
// If the resource was imported, the only info we have is the ID. Try to find the resource
|
||||||
|
// by searching in the region of the project.
|
||||||
|
var resource interface{}
|
||||||
|
resource, e = getZonalResourceFromRegion(getInstanceGroupManager, region, config.clientCompute, project)
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
manager = resource.(*compute.InstanceGroupManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
if manager == nil {
|
||||||
log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string))
|
log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string))
|
||||||
// The resource doesn't exist anymore
|
// The resource doesn't exist anymore
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
manager := resource.(*compute.InstanceGroupManager)
|
|
||||||
|
|
||||||
zoneUrl := strings.Split(manager.Zone, "/")
|
zoneUrl := strings.Split(manager.Zone, "/")
|
||||||
d.Set("base_instance_name", manager.BaseInstanceName)
|
d.Set("base_instance_name", manager.BaseInstanceName)
|
||||||
|
@ -135,6 +135,30 @@ func TestAccInstanceGroupManager_updateStrategy(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccInstanceGroupManager_separateRegions(t *testing.T) {
|
||||||
|
var manager compute.InstanceGroupManager
|
||||||
|
|
||||||
|
igm1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10))
|
||||||
|
igm2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10))
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckInstanceGroupManagerDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccInstanceGroupManager_separateRegions(igm1, igm2),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckInstanceGroupManagerExists(
|
||||||
|
"google_compute_instance_group_manager.igm-basic", &manager),
|
||||||
|
testAccCheckInstanceGroupManagerExists(
|
||||||
|
"google_compute_instance_group_manager.igm-basic-2", &manager),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckInstanceGroupManagerDestroy(s *terraform.State) error {
|
func testAccCheckInstanceGroupManagerDestroy(s *terraform.State) error {
|
||||||
config := testAccProvider.Meta().(*Config)
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
@ -571,6 +595,52 @@ func testAccInstanceGroupManager_updateStrategy(igm string) string {
|
|||||||
}`, igm)
|
}`, igm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccInstanceGroupManager_separateRegions(igm1, igm2 string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "google_compute_instance_template" "igm-basic" {
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
can_ip_forward = false
|
||||||
|
tags = ["foo", "bar"]
|
||||||
|
|
||||||
|
disk {
|
||||||
|
source_image = "debian-cloud/debian-8-jessie-v20160803"
|
||||||
|
auto_delete = true
|
||||||
|
boot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
network = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
service_account {
|
||||||
|
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_instance_group_manager" "igm-basic" {
|
||||||
|
description = "Terraform test instance group manager"
|
||||||
|
name = "%s"
|
||||||
|
instance_template = "${google_compute_instance_template.igm-basic.self_link}"
|
||||||
|
base_instance_name = "igm-basic"
|
||||||
|
zone = "us-central1-c"
|
||||||
|
target_size = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_instance_group_manager" "igm-basic-2" {
|
||||||
|
description = "Terraform test instance group manager"
|
||||||
|
name = "%s"
|
||||||
|
instance_template = "${google_compute_instance_template.igm-basic.self_link}"
|
||||||
|
base_instance_name = "igm-basic-2"
|
||||||
|
zone = "us-west1-b"
|
||||||
|
target_size = 2
|
||||||
|
}
|
||||||
|
`, igm1, igm2)
|
||||||
|
}
|
||||||
|
|
||||||
func resourceSplitter(resource string) string {
|
func resourceSplitter(resource string) string {
|
||||||
splits := strings.Split(resource, "/")
|
splits := strings.Split(resource, "/")
|
||||||
|
|
||||||
|
@ -207,11 +207,13 @@ func resourceComputeInstanceTemplate() *schema.Resource {
|
|||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"access_config": &schema.Schema{
|
"access_config": &schema.Schema{
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"nat_ip": &schema.Schema{
|
"nat_ip": &schema.Schema{
|
||||||
|
@ -547,6 +547,66 @@ func TestAccComputeInstance_invalid_disk(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccComputeInstance_forceChangeMachineTypeManually(t *testing.T) {
|
||||||
|
var instance compute.Instance
|
||||||
|
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeInstance_basic(instanceName),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance),
|
||||||
|
testAccCheckComputeInstanceUpdateMachineType("google_compute_instance.foobar"),
|
||||||
|
),
|
||||||
|
ExpectNonEmptyPlan: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceUpdateMachineType(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)
|
||||||
|
|
||||||
|
op, err := config.clientCompute.Instances.Stop(config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not stop instance: %s", err)
|
||||||
|
}
|
||||||
|
err = computeOperationWaitZone(config, op, config.Project, rs.Primary.Attributes["zone"], "Waiting on stop")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not stop instance: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
machineType := compute.InstancesSetMachineTypeRequest{
|
||||||
|
MachineType: "zones/us-central1-a/machineTypes/f1-micro",
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err = config.clientCompute.Instances.SetMachineType(
|
||||||
|
config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID, &machineType).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not change machine type: %s", err)
|
||||||
|
}
|
||||||
|
err = computeOperationWaitZone(config, op, config.Project, rs.Primary.Attributes["zone"], "Waiting machine type change")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not change machine type: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckComputeInstanceDestroy(s *terraform.State) error {
|
func testAccCheckComputeInstanceDestroy(s *terraform.State) error {
|
||||||
config := testAccProvider.Meta().(*Config)
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
@ -192,6 +192,10 @@ func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface
|
|||||||
|
|
||||||
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
|
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error removing metadata from project %s: %s", projectID, err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
||||||
|
|
||||||
err = computeOperationWaitGlobal(config, op, project.Name, "SetCommonMetadata")
|
err = computeOperationWaitGlobal(config, op, project.Name, "SetCommonMetadata")
|
||||||
|
@ -65,6 +65,15 @@ func resourceComputeVpnTunnel() *schema.Resource {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"local_traffic_selector": &schema.Schema{
|
"local_traffic_selector": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"remote_traffic_selector": &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
@ -124,6 +133,14 @@ func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var remoteTrafficSelectors []string
|
||||||
|
if v := d.Get("remote_traffic_selector").(*schema.Set); v.Len() > 0 {
|
||||||
|
remoteTrafficSelectors = make([]string, v.Len())
|
||||||
|
for i, v := range v.List() {
|
||||||
|
remoteTrafficSelectors[i] = v.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vpnTunnelsService := compute.NewVpnTunnelsService(config.clientCompute)
|
vpnTunnelsService := compute.NewVpnTunnelsService(config.clientCompute)
|
||||||
|
|
||||||
vpnTunnel := &compute.VpnTunnel{
|
vpnTunnel := &compute.VpnTunnel{
|
||||||
@ -133,6 +150,7 @@ func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
TargetVpnGateway: targetVpnGateway,
|
TargetVpnGateway: targetVpnGateway,
|
||||||
IkeVersion: int64(ikeVersion),
|
IkeVersion: int64(ikeVersion),
|
||||||
LocalTrafficSelector: localTrafficSelectors,
|
LocalTrafficSelector: localTrafficSelectors,
|
||||||
|
RemoteTrafficSelector: remoteTrafficSelectors,
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := d.GetOk("description"); ok {
|
if v, ok := d.GetOk("description"); ok {
|
||||||
@ -182,6 +200,18 @@ func resourceComputeVpnTunnelRead(d *schema.ResourceData, meta interface{}) erro
|
|||||||
return fmt.Errorf("Error Reading VPN Tunnel %s: %s", name, err)
|
return fmt.Errorf("Error Reading VPN Tunnel %s: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
localTrafficSelectors := []string{}
|
||||||
|
for _, lts := range vpnTunnel.LocalTrafficSelector {
|
||||||
|
localTrafficSelectors = append(localTrafficSelectors, lts)
|
||||||
|
}
|
||||||
|
d.Set("local_traffic_selector", localTrafficSelectors)
|
||||||
|
|
||||||
|
remoteTrafficSelectors := []string{}
|
||||||
|
for _, rts := range vpnTunnel.RemoteTrafficSelector {
|
||||||
|
remoteTrafficSelectors = append(remoteTrafficSelectors, rts)
|
||||||
|
}
|
||||||
|
d.Set("remote_traffic_selector", remoteTrafficSelectors)
|
||||||
|
|
||||||
d.Set("detailed_status", vpnTunnel.DetailedStatus)
|
d.Set("detailed_status", vpnTunnel.DetailedStatus)
|
||||||
d.Set("self_link", vpnTunnel.SelfLink)
|
d.Set("self_link", vpnTunnel.SelfLink)
|
||||||
|
|
||||||
|
@ -22,12 +22,32 @@ func TestAccComputeVpnTunnel_basic(t *testing.T) {
|
|||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckComputeVpnTunnelExists(
|
testAccCheckComputeVpnTunnelExists(
|
||||||
"google_compute_vpn_tunnel.foobar"),
|
"google_compute_vpn_tunnel.foobar"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"google_compute_vpn_tunnel.foobar", "local_traffic_selector.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"google_compute_vpn_tunnel.foobar", "remote_traffic_selector.#", "2"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccComputeVpnTunnel_defaultTrafficSelectors(t *testing.T) {
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeVpnTunnelDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeVpnTunnelDefaultTrafficSelectors,
|
||||||
|
Check: testAccCheckComputeVpnTunnelExists(
|
||||||
|
"google_compute_vpn_tunnel.foobar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckComputeVpnTunnelDestroy(s *terraform.State) error {
|
func testAccCheckComputeVpnTunnelDestroy(s *terraform.State) error {
|
||||||
config := testAccProvider.Meta().(*Config)
|
config := testAccProvider.Meta().(*Config)
|
||||||
project := config.Project
|
project := config.Project
|
||||||
@ -83,7 +103,61 @@ func testAccCheckComputeVpnTunnelExists(n string) resource.TestCheckFunc {
|
|||||||
var testAccComputeVpnTunnel_basic = fmt.Sprintf(`
|
var testAccComputeVpnTunnel_basic = fmt.Sprintf(`
|
||||||
resource "google_compute_network" "foobar" {
|
resource "google_compute_network" "foobar" {
|
||||||
name = "tunnel-test-%s"
|
name = "tunnel-test-%s"
|
||||||
ipv4_range = "10.0.0.0/16"
|
}
|
||||||
|
resource "google_compute_subnetwork" "foobar" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
network = "${google_compute_network.foobar.self_link}"
|
||||||
|
ip_cidr_range = "10.0.0.0/16"
|
||||||
|
region = "us-central1"
|
||||||
|
}
|
||||||
|
resource "google_compute_address" "foobar" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
region = "${google_compute_subnetwork.foobar.region}"
|
||||||
|
}
|
||||||
|
resource "google_compute_vpn_gateway" "foobar" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
network = "${google_compute_network.foobar.self_link}"
|
||||||
|
region = "${google_compute_subnetwork.foobar.region}"
|
||||||
|
}
|
||||||
|
resource "google_compute_forwarding_rule" "foobar_esp" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
region = "${google_compute_vpn_gateway.foobar.region}"
|
||||||
|
ip_protocol = "ESP"
|
||||||
|
ip_address = "${google_compute_address.foobar.address}"
|
||||||
|
target = "${google_compute_vpn_gateway.foobar.self_link}"
|
||||||
|
}
|
||||||
|
resource "google_compute_forwarding_rule" "foobar_udp500" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
region = "${google_compute_forwarding_rule.foobar_esp.region}"
|
||||||
|
ip_protocol = "UDP"
|
||||||
|
port_range = "500-500"
|
||||||
|
ip_address = "${google_compute_address.foobar.address}"
|
||||||
|
target = "${google_compute_vpn_gateway.foobar.self_link}"
|
||||||
|
}
|
||||||
|
resource "google_compute_forwarding_rule" "foobar_udp4500" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
region = "${google_compute_forwarding_rule.foobar_udp500.region}"
|
||||||
|
ip_protocol = "UDP"
|
||||||
|
port_range = "4500-4500"
|
||||||
|
ip_address = "${google_compute_address.foobar.address}"
|
||||||
|
target = "${google_compute_vpn_gateway.foobar.self_link}"
|
||||||
|
}
|
||||||
|
resource "google_compute_vpn_tunnel" "foobar" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
region = "${google_compute_forwarding_rule.foobar_udp4500.region}"
|
||||||
|
target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}"
|
||||||
|
shared_secret = "unguessable"
|
||||||
|
peer_ip = "8.8.8.8"
|
||||||
|
local_traffic_selector = ["${google_compute_subnetwork.foobar.ip_cidr_range}"]
|
||||||
|
remote_traffic_selector = ["192.168.0.0/24", "192.168.1.0/24"]
|
||||||
|
}`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10),
|
||||||
|
acctest.RandString(10), acctest.RandString(10), acctest.RandString(10),
|
||||||
|
acctest.RandString(10), acctest.RandString(10))
|
||||||
|
|
||||||
|
var testAccComputeVpnTunnelDefaultTrafficSelectors = fmt.Sprintf(`
|
||||||
|
resource "google_compute_network" "foobar" {
|
||||||
|
name = "tunnel-test-%s"
|
||||||
|
auto_create_subnetworks = "true"
|
||||||
}
|
}
|
||||||
resource "google_compute_address" "foobar" {
|
resource "google_compute_address" "foobar" {
|
||||||
name = "tunnel-test-%s"
|
name = "tunnel-test-%s"
|
||||||
|
@ -95,6 +95,7 @@ func resourceContainerCluster() *schema.Resource {
|
|||||||
"additional_zones": &schema.Schema{
|
"additional_zones": &schema.Schema{
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
},
|
},
|
||||||
@ -292,18 +293,14 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
if v, ok := d.GetOk("additional_zones"); ok {
|
if v, ok := d.GetOk("additional_zones"); ok {
|
||||||
locationsList := v.([]interface{})
|
locationsList := v.([]interface{})
|
||||||
locations := []string{}
|
locations := []string{}
|
||||||
zoneInLocations := false
|
|
||||||
for _, v := range locationsList {
|
for _, v := range locationsList {
|
||||||
location := v.(string)
|
location := v.(string)
|
||||||
locations = append(locations, location)
|
locations = append(locations, location)
|
||||||
if location == zoneName {
|
if location == zoneName {
|
||||||
zoneInLocations = true
|
return fmt.Errorf("additional_zones should not contain the original 'zone'.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !zoneInLocations {
|
|
||||||
// zone must be in locations if specified separately
|
|
||||||
locations = append(locations, zoneName)
|
locations = append(locations, zoneName)
|
||||||
}
|
|
||||||
cluster.Locations = locations
|
cluster.Locations = locations
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,7 +441,17 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro
|
|||||||
|
|
||||||
d.Set("name", cluster.Name)
|
d.Set("name", cluster.Name)
|
||||||
d.Set("zone", cluster.Zone)
|
d.Set("zone", cluster.Zone)
|
||||||
d.Set("additional_zones", cluster.Locations)
|
|
||||||
|
locations := []string{}
|
||||||
|
if len(cluster.Locations) > 1 {
|
||||||
|
for _, location := range cluster.Locations {
|
||||||
|
if location != cluster.Zone {
|
||||||
|
locations = append(locations, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.Set("additional_zones", locations)
|
||||||
|
|
||||||
d.Set("endpoint", cluster.Endpoint)
|
d.Set("endpoint", cluster.Endpoint)
|
||||||
|
|
||||||
masterAuth := []map[string]interface{}{
|
masterAuth := []map[string]interface{}{
|
||||||
|
@ -39,7 +39,7 @@ func TestAccContainerCluster_withAdditionalZones(t *testing.T) {
|
|||||||
testAccCheckContainerClusterExists(
|
testAccCheckContainerClusterExists(
|
||||||
"google_container_cluster.with_additional_zones"),
|
"google_container_cluster.with_additional_zones"),
|
||||||
testAccCheckContainerClusterAdditionalZonesExist(
|
testAccCheckContainerClusterAdditionalZonesExist(
|
||||||
"google_container_cluster.with_additional_zones"),
|
"google_container_cluster.with_additional_zones", 2),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -163,23 +163,19 @@ func testAccCheckContainerClusterExists(n string) resource.TestCheckFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccCheckContainerClusterAdditionalZonesExist(n string) resource.TestCheckFunc {
|
func testAccCheckContainerClusterAdditionalZonesExist(n string, num int) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[n]
|
rs, ok := s.RootModule().Resources[n]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Not found: %s", n)
|
return fmt.Errorf("Not found: %s", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
additionalZonesSize, err := strconv.Atoi(rs.Primary.Attributes["additional_zones.#"])
|
||||||
additionalZonesSize int
|
if err != nil {
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if additionalZonesSize, err = strconv.Atoi(rs.Primary.Attributes["additional_zones.#"]); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if additionalZonesSize != 2 {
|
if additionalZonesSize != num {
|
||||||
return fmt.Errorf("number of additional zones did not match 2")
|
return fmt.Errorf("number of additional zones did not match %d, was %d", num, additionalZonesSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -219,7 +215,7 @@ var testAccContainerCluster_withVersion = fmt.Sprintf(`
|
|||||||
resource "google_container_cluster" "with_version" {
|
resource "google_container_cluster" "with_version" {
|
||||||
name = "cluster-test-%s"
|
name = "cluster-test-%s"
|
||||||
zone = "us-central1-a"
|
zone = "us-central1-a"
|
||||||
node_version = "1.4.7"
|
node_version = "1.5.2"
|
||||||
initial_node_count = 1
|
initial_node_count = 1
|
||||||
|
|
||||||
master_auth {
|
master_auth {
|
||||||
|
@ -79,5 +79,5 @@ func testAccCheckDnsManagedZoneExists(n string, zone *dns.ManagedZone) resource.
|
|||||||
var testAccDnsManagedZone_basic = fmt.Sprintf(`
|
var testAccDnsManagedZone_basic = fmt.Sprintf(`
|
||||||
resource "google_dns_managed_zone" "foobar" {
|
resource "google_dns_managed_zone" "foobar" {
|
||||||
name = "mzone-test-%s"
|
name = "mzone-test-%s"
|
||||||
dns_name = "terraform.test."
|
dns_name = "hashicorptest.com."
|
||||||
}`, acctest.RandString(10))
|
}`, acctest.RandString(10))
|
||||||
|
@ -138,12 +138,12 @@ func testAccDnsRecordSet_basic(zoneName string, addr2 string, ttl int) string {
|
|||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_dns_managed_zone" "parent-zone" {
|
resource "google_dns_managed_zone" "parent-zone" {
|
||||||
name = "%s"
|
name = "%s"
|
||||||
dns_name = "terraform.test."
|
dns_name = "hashicorptest.com."
|
||||||
description = "Test Description"
|
description = "Test Description"
|
||||||
}
|
}
|
||||||
resource "google_dns_record_set" "foobar" {
|
resource "google_dns_record_set" "foobar" {
|
||||||
managed_zone = "${google_dns_managed_zone.parent-zone.name}"
|
managed_zone = "${google_dns_managed_zone.parent-zone.name}"
|
||||||
name = "test-record.terraform.test."
|
name = "test-record.hashicorptest.com."
|
||||||
type = "A"
|
type = "A"
|
||||||
rrdatas = ["127.0.0.1", "%s"]
|
rrdatas = ["127.0.0.1", "%s"]
|
||||||
ttl = %d
|
ttl = %d
|
||||||
@ -155,12 +155,12 @@ func testAccDnsRecordSet_bigChange(zoneName string, ttl int) string {
|
|||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_dns_managed_zone" "parent-zone" {
|
resource "google_dns_managed_zone" "parent-zone" {
|
||||||
name = "%s"
|
name = "%s"
|
||||||
dns_name = "terraform.test."
|
dns_name = "hashicorptest.com."
|
||||||
description = "Test Description"
|
description = "Test Description"
|
||||||
}
|
}
|
||||||
resource "google_dns_record_set" "foobar" {
|
resource "google_dns_record_set" "foobar" {
|
||||||
managed_zone = "${google_dns_managed_zone.parent-zone.name}"
|
managed_zone = "${google_dns_managed_zone.parent-zone.name}"
|
||||||
name = "test-record.terraform.test."
|
name = "test-record.hashicorptest.com."
|
||||||
type = "CNAME"
|
type = "CNAME"
|
||||||
rrdatas = ["www.terraform.io."]
|
rrdatas = ["www.terraform.io."]
|
||||||
ttl = %d
|
ttl = %d
|
||||||
|
@ -6,8 +6,10 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"google.golang.org/api/cloudbilling/v1"
|
||||||
"google.golang.org/api/cloudresourcemanager/v1"
|
"google.golang.org/api/cloudresourcemanager/v1"
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
)
|
)
|
||||||
@ -86,6 +88,10 @@ func resourceGoogleProject() *schema.Resource {
|
|||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
"billing_account": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,6 +178,22 @@ func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the billing account
|
||||||
|
if v, ok := d.GetOk("billing_account"); ok {
|
||||||
|
name := v.(string)
|
||||||
|
ba := cloudbilling.ProjectBillingInfo{
|
||||||
|
BillingAccountName: "billingAccounts/" + name,
|
||||||
|
}
|
||||||
|
_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
|
||||||
|
if err != nil {
|
||||||
|
d.Set("billing_account", "")
|
||||||
|
if _err, ok := err.(*googleapi.Error); ok {
|
||||||
|
return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), _err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return resourceGoogleProjectRead(d, meta)
|
return resourceGoogleProjectRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,23 +218,30 @@ func resourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error {
|
|||||||
d.Set("org_id", p.Parent.Id)
|
d.Set("org_id", p.Parent.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the IAM policy
|
// Read the billing account
|
||||||
pol, err := getProjectIamPolicy(pid, config)
|
ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err)
|
||||||
}
|
}
|
||||||
|
if ba.BillingAccountName != "" {
|
||||||
polBytes, err := json.Marshal(pol)
|
// BillingAccountName is contains the resource name of the billing account
|
||||||
if err != nil {
|
// associated with the project, if any. For example,
|
||||||
return err
|
// `billingAccounts/012345-567890-ABCDEF`. We care about the ID and not
|
||||||
|
// the `billingAccounts/` prefix, so we need to remove that. If the
|
||||||
|
// prefix ever changes, we'll validate to make sure it's something we
|
||||||
|
// recognize.
|
||||||
|
_ba := strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/")
|
||||||
|
if ba.BillingAccountName == _ba {
|
||||||
|
return fmt.Errorf("Error parsing billing account for project %q. Expected value to begin with 'billingAccounts/' but got %s", prefixedProject(pid), ba.BillingAccountName)
|
||||||
|
}
|
||||||
|
d.Set("billing_account", _ba)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("policy_etag", pol.Etag)
|
|
||||||
d.Set("policy_data", string(polBytes))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prefixedProject(pid string) string {
|
||||||
|
return "projects/" + pid
|
||||||
|
}
|
||||||
func resourceGoogleProjectUpdate(d *schema.ResourceData, meta interface{}) error {
|
func resourceGoogleProjectUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
pid := d.Id()
|
pid := d.Id()
|
||||||
@ -238,6 +267,21 @@ func resourceGoogleProjectUpdate(d *schema.ResourceData, meta interface{}) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Billing account has changed
|
||||||
|
if ok := d.HasChange("billing_account"); ok {
|
||||||
|
name := d.Get("billing_account").(string)
|
||||||
|
ba := cloudbilling.ProjectBillingInfo{
|
||||||
|
BillingAccountName: "billingAccounts/" + name,
|
||||||
|
}
|
||||||
|
_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
|
||||||
|
if err != nil {
|
||||||
|
d.Set("billing_account", "")
|
||||||
|
if _err, ok := err.(*googleapi.Error); ok {
|
||||||
|
return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), _err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return updateProjectIamPolicy(d, config, pid)
|
return updateProjectIamPolicy(d, config, pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ func setProjectIamPolicy(policy *cloudresourcemanager.Policy, config *Config, pi
|
|||||||
&cloudresourcemanager.SetIamPolicyRequest{Policy: policy}).Do()
|
&cloudresourcemanager.SetIamPolicyRequest{Policy: policy}).Do()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error applying IAM policy for project %q. Policy is %+s, error is %s", pid, policy, err)
|
return fmt.Errorf("Error applying IAM policy for project %q. Policy is %#v, error is %s", pid, policy, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -624,3 +624,13 @@ resource "google_project" "acceptance" {
|
|||||||
org_id = "%s"
|
org_id = "%s"
|
||||||
}`, pid, name, org)
|
}`, pid, name, org)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccGoogleProject_createBilling(pid, name, org, billing string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "google_project" "acceptance" {
|
||||||
|
project_id = "%s"
|
||||||
|
name = "%s"
|
||||||
|
org_id = "%s"
|
||||||
|
billing_account = "%s"
|
||||||
|
}`, pid, name, org, billing)
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package google
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/acctest"
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
@ -48,6 +49,104 @@ func TestAccGoogleProject_create(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that a Project resource can be created with an associated
|
||||||
|
// billing account
|
||||||
|
func TestAccGoogleProject_createBilling(t *testing.T) {
|
||||||
|
skipIfEnvNotSet(t,
|
||||||
|
[]string{
|
||||||
|
"GOOGLE_ORG",
|
||||||
|
"GOOGLE_BILLING_ACCOUNT",
|
||||||
|
}...,
|
||||||
|
)
|
||||||
|
|
||||||
|
billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
|
||||||
|
pid := "terraform-" + acctest.RandString(10)
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
// This step creates a new project with a billing account
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccGoogleProject_createBilling(pid, pname, org, billingId),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a Project resource can be created and updated
|
||||||
|
// with billing account information
|
||||||
|
func TestAccGoogleProject_updateBilling(t *testing.T) {
|
||||||
|
skipIfEnvNotSet(t,
|
||||||
|
[]string{
|
||||||
|
"GOOGLE_ORG",
|
||||||
|
"GOOGLE_BILLING_ACCOUNT",
|
||||||
|
"GOOGLE_BILLING_ACCOUNT_2",
|
||||||
|
}...,
|
||||||
|
)
|
||||||
|
|
||||||
|
billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
|
||||||
|
billingId2 := os.Getenv("GOOGLE_BILLING_ACCOUNT_2")
|
||||||
|
pid := "terraform-" + acctest.RandString(10)
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
// This step creates a new project without a billing account
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccGoogleProject_create(pid, pname, org),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleProjectExists("google_project.acceptance", pid),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Update to include a billing account
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccGoogleProject_createBilling(pid, pname, org, billingId),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Update to a different billing account
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccGoogleProject_createBilling(pid, pname, org, billingId2),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId2),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a Project resource merges the IAM policies that already
|
||||||
|
// exist, and won't lock people out.
|
||||||
|
func TestAccGoogleProject_merge(t *testing.T) {
|
||||||
|
pid := "terraform-" + acctest.RandString(10)
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
// when policy_data is set, merge
|
||||||
|
{
|
||||||
|
Config: testAccGoogleProject_toMerge(pid, pname, org),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleProjectExists("google_project.acceptance", pid),
|
||||||
|
testAccCheckGoogleProjectHasMoreBindingsThan(pid, 1),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// when policy_data is unset, restore to what it was
|
||||||
|
{
|
||||||
|
Config: testAccGoogleProject_mergeEmpty(pid, pname, org),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleProjectExists("google_project.acceptance", pid),
|
||||||
|
testAccCheckGoogleProjectHasMoreBindingsThan(pid, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckGoogleProjectExists(r, pid string) resource.TestCheckFunc {
|
func testAccCheckGoogleProjectExists(r, pid string) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[r]
|
rs, ok := s.RootModule().Resources[r]
|
||||||
@ -67,6 +166,45 @@ func testAccCheckGoogleProjectExists(r, pid string) resource.TestCheckFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccCheckGoogleProjectHasBillingAccount(r, pid, billingId string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[r]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// State should match expected
|
||||||
|
if rs.Primary.Attributes["billing_account"] != billingId {
|
||||||
|
return fmt.Errorf("Billing ID in state (%s) does not match expected value (%s)", rs.Primary.Attributes["billing_account"], billingId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual value in API should match state and expected
|
||||||
|
// Read the billing account
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err)
|
||||||
|
}
|
||||||
|
if billingId != strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/") {
|
||||||
|
return fmt.Errorf("Billing ID returned by API (%s) did not match expected value (%s)", ba.BillingAccountName, billingId)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckGoogleProjectHasMoreBindingsThan(pid string, count int) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
policy, err := getProjectIamPolicy(pid, testAccProvider.Meta().(*Config))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(policy.Bindings) <= count {
|
||||||
|
return fmt.Errorf("Expected more than %d bindings, got %d: %#v", count, len(policy.Bindings), policy.Bindings)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testAccGoogleProjectImportExisting(pid string) string {
|
func testAccGoogleProjectImportExisting(pid string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_project" "acceptance" {
|
resource "google_project" "acceptance" {
|
||||||
@ -98,3 +236,39 @@ data "google_iam_policy" "admin" {
|
|||||||
}
|
}
|
||||||
}`, pid)
|
}`, pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccGoogleProject_toMerge(pid, name, org string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "google_project" "acceptance" {
|
||||||
|
project_id = "%s"
|
||||||
|
name = "%s"
|
||||||
|
org_id = "%s"
|
||||||
|
policy_data = "${data.google_iam_policy.acceptance.policy_data}"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "google_iam_policy" "acceptance" {
|
||||||
|
binding {
|
||||||
|
role = "roles/storage.objectViewer"
|
||||||
|
members = [
|
||||||
|
"user:evanbrown@google.com",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`, pid, name, org)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccGoogleProject_mergeEmpty(pid, name, org string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "google_project" "acceptance" {
|
||||||
|
project_id = "%s"
|
||||||
|
name = "%s"
|
||||||
|
org_id = "%s"
|
||||||
|
}`, pid, name, org)
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipIfEnvNotSet(t *testing.T, envs ...string) {
|
||||||
|
for _, k := range envs {
|
||||||
|
if os.Getenv(k) == "" {
|
||||||
|
t.Skipf("Environment variable %s is not set", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package google
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
@ -70,6 +71,7 @@ func resourceSqlDatabaseInstance() *schema.Resource {
|
|||||||
"crash_safe_replication": &schema.Schema{
|
"crash_safe_replication": &schema.Schema{
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
},
|
},
|
||||||
"database_flags": &schema.Schema{
|
"database_flags": &schema.Schema{
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
@ -87,6 +89,18 @@ func resourceSqlDatabaseInstance() *schema.Resource {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"disk_autoresize": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"disk_size": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"disk_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
"ip_configuration": &schema.Schema{
|
"ip_configuration": &schema.Schema{
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
@ -139,6 +153,33 @@ func resourceSqlDatabaseInstance() *schema.Resource {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"maintenance_window": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
MaxItems: 1,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"day": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
return validateNumericRange(v, k, 1, 7)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"hour": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
return validateNumericRange(v, k, 0, 23)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"update_track": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"pricing_plan": &schema.Schema{
|
"pricing_plan": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
@ -323,6 +364,18 @@ func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{})
|
|||||||
settings.CrashSafeReplicationEnabled = v.(bool)
|
settings.CrashSafeReplicationEnabled = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_autoresize"]; ok && v.(bool) {
|
||||||
|
settings.StorageAutoResize = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_size"]; ok && v.(int) > 0 {
|
||||||
|
settings.DataDiskSizeGb = int64(v.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_type"]; ok && len(v.(string)) > 0 {
|
||||||
|
settings.DataDiskType = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := _settings["database_flags"]; ok {
|
if v, ok := _settings["database_flags"]; ok {
|
||||||
settings.DatabaseFlags = make([]*sqladmin.DatabaseFlags, 0)
|
settings.DatabaseFlags = make([]*sqladmin.DatabaseFlags, 0)
|
||||||
_databaseFlagsList := v.([]interface{})
|
_databaseFlagsList := v.([]interface{})
|
||||||
@ -405,6 +458,25 @@ func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{})
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["maintenance_window"]; ok && len(v.([]interface{})) > 0 {
|
||||||
|
settings.MaintenanceWindow = &sqladmin.MaintenanceWindow{}
|
||||||
|
_maintenanceWindow := v.([]interface{})[0].(map[string]interface{})
|
||||||
|
|
||||||
|
if vp, okp := _maintenanceWindow["day"]; okp {
|
||||||
|
settings.MaintenanceWindow.Day = int64(vp.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
if vp, okp := _maintenanceWindow["hour"]; okp {
|
||||||
|
settings.MaintenanceWindow.Hour = int64(vp.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
if vp, ok := _maintenanceWindow["update_track"]; ok {
|
||||||
|
if len(vp.(string)) > 0 {
|
||||||
|
settings.MaintenanceWindow.UpdateTrack = vp.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := _settings["pricing_plan"]; ok {
|
if v, ok := _settings["pricing_plan"]; ok {
|
||||||
settings.PricingPlan = v.(string)
|
settings.PricingPlan = v.(string)
|
||||||
}
|
}
|
||||||
@ -500,7 +572,30 @@ func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{})
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceSqlDatabaseInstanceRead(d, meta)
|
err = resourceSqlDatabaseInstanceRead(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a root user exists with a wildcard ('%') hostname, delete it.
|
||||||
|
users, err := config.clientSqlAdmin.Users.List(project, instance.Name).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error, attempting to list users associated with instance %s: %s", instance.Name, err)
|
||||||
|
}
|
||||||
|
for _, u := range users.Items {
|
||||||
|
if u.Name == "root" && u.Host == "%" {
|
||||||
|
op, err = config.clientSqlAdmin.Users.Delete(project, instance.Name, u.Host, u.Name).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error, failed to delete default 'root'@'*' user, but the database was created successfully: %s", err)
|
||||||
|
}
|
||||||
|
err = sqladminOperationWait(config, op, "Delete default root User")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
@ -564,7 +659,7 @@ func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) e
|
|||||||
_backupConfiguration["enabled"] = settings.BackupConfiguration.Enabled
|
_backupConfiguration["enabled"] = settings.BackupConfiguration.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
if vp, okp := _backupConfiguration["start_time"]; okp && vp != nil {
|
if vp, okp := _backupConfiguration["start_time"]; okp && len(vp.(string)) > 0 {
|
||||||
_backupConfiguration["start_time"] = settings.BackupConfiguration.StartTime
|
_backupConfiguration["start_time"] = settings.BackupConfiguration.StartTime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,6 +672,24 @@ func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) e
|
|||||||
_settings["crash_safe_replication"] = settings.CrashSafeReplicationEnabled
|
_settings["crash_safe_replication"] = settings.CrashSafeReplicationEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_autoresize"]; ok && v != nil {
|
||||||
|
if v.(bool) {
|
||||||
|
_settings["disk_autoresize"] = settings.StorageAutoResize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_size"]; ok && v != nil {
|
||||||
|
if v.(int) > 0 && settings.DataDiskSizeGb < int64(v.(int)) {
|
||||||
|
_settings["disk_size"] = settings.DataDiskSizeGb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_type"]; ok && v != nil {
|
||||||
|
if len(v.(string)) > 0 {
|
||||||
|
_settings["disk_type"] = settings.DataDiskType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := _settings["database_flags"]; ok && len(v.([]interface{})) > 0 {
|
if v, ok := _settings["database_flags"]; ok && len(v.([]interface{})) > 0 {
|
||||||
_flag_map := make(map[string]string)
|
_flag_map := make(map[string]string)
|
||||||
// First keep track of localy defined flag pairs
|
// First keep track of localy defined flag pairs
|
||||||
@ -678,6 +791,25 @@ func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["maintenance_window"]; ok && len(v.([]interface{})) > 0 &&
|
||||||
|
settings.MaintenanceWindow != nil {
|
||||||
|
_maintenanceWindow := v.([]interface{})[0].(map[string]interface{})
|
||||||
|
|
||||||
|
if vp, okp := _maintenanceWindow["day"]; okp && vp != nil {
|
||||||
|
_maintenanceWindow["day"] = settings.MaintenanceWindow.Day
|
||||||
|
}
|
||||||
|
|
||||||
|
if vp, okp := _maintenanceWindow["hour"]; okp && vp != nil {
|
||||||
|
_maintenanceWindow["hour"] = settings.MaintenanceWindow.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if vp, ok := _maintenanceWindow["update_track"]; ok && vp != nil {
|
||||||
|
if len(vp.(string)) > 0 {
|
||||||
|
_maintenanceWindow["update_track"] = settings.MaintenanceWindow.UpdateTrack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := _settings["pricing_plan"]; ok && len(v.(string)) > 0 {
|
if v, ok := _settings["pricing_plan"]; ok && len(v.(string)) > 0 {
|
||||||
_settings["pricing_plan"] = settings.PricingPlan
|
_settings["pricing_plan"] = settings.PricingPlan
|
||||||
}
|
}
|
||||||
@ -758,7 +890,7 @@ func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) e
|
|||||||
d.Set("ip_address", _ipAddresses)
|
d.Set("ip_address", _ipAddresses)
|
||||||
|
|
||||||
if v, ok := d.GetOk("master_instance_name"); ok && v != nil {
|
if v, ok := d.GetOk("master_instance_name"); ok && v != nil {
|
||||||
d.Set("master_instance_name", instance.MasterInstanceName)
|
d.Set("master_instance_name", strings.TrimPrefix(instance.MasterInstanceName, project+":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("self_link", instance.SelfLink)
|
d.Set("self_link", instance.SelfLink)
|
||||||
@ -840,6 +972,20 @@ func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{})
|
|||||||
settings.CrashSafeReplicationEnabled = v.(bool)
|
settings.CrashSafeReplicationEnabled = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_autoresize"]; ok && v.(bool) {
|
||||||
|
settings.StorageAutoResize = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_size"]; ok {
|
||||||
|
if v.(int) > 0 && int64(v.(int)) > instance.Settings.DataDiskSizeGb {
|
||||||
|
settings.DataDiskSizeGb = int64(v.(int))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["disk_type"]; ok && len(v.(string)) > 0 {
|
||||||
|
settings.DataDiskType = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
_oldDatabaseFlags := make([]interface{}, 0)
|
_oldDatabaseFlags := make([]interface{}, 0)
|
||||||
if ov, ook := _o["database_flags"]; ook {
|
if ov, ook := _o["database_flags"]; ook {
|
||||||
_oldDatabaseFlags = ov.([]interface{})
|
_oldDatabaseFlags = ov.([]interface{})
|
||||||
@ -981,6 +1127,25 @@ func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{})
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := _settings["maintenance_window"]; ok && len(v.([]interface{})) > 0 {
|
||||||
|
settings.MaintenanceWindow = &sqladmin.MaintenanceWindow{}
|
||||||
|
_maintenanceWindow := v.([]interface{})[0].(map[string]interface{})
|
||||||
|
|
||||||
|
if vp, okp := _maintenanceWindow["day"]; okp {
|
||||||
|
settings.MaintenanceWindow.Day = int64(vp.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
if vp, okp := _maintenanceWindow["hour"]; okp {
|
||||||
|
settings.MaintenanceWindow.Hour = int64(vp.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
if vp, ok := _maintenanceWindow["update_track"]; ok {
|
||||||
|
if len(vp.(string)) > 0 {
|
||||||
|
settings.MaintenanceWindow.UpdateTrack = vp.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := _settings["pricing_plan"]; ok {
|
if v, ok := _settings["pricing_plan"]; ok {
|
||||||
settings.PricingPlan = v.(string)
|
settings.PricingPlan = v.(string)
|
||||||
}
|
}
|
||||||
@ -1028,3 +1193,12 @@ func resourceSqlDatabaseInstanceDelete(d *schema.ResourceData, meta interface{})
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateNumericRange(v interface{}, k string, min int, max int) (ws []string, errors []error) {
|
||||||
|
value := v.(int)
|
||||||
|
if min > value || value > max {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q outside range %d-%d.", k, min, max))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ package google
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/acctest"
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
@ -63,6 +64,30 @@ func TestAccGoogleSqlDatabaseInstance_basic2(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccGoogleSqlDatabaseInstance_basic3(t *testing.T) {
|
||||||
|
var instance sqladmin.DatabaseInstance
|
||||||
|
databaseID := acctest.RandInt()
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccGoogleSqlDatabaseInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(
|
||||||
|
testGoogleSqlDatabaseInstance_basic3, databaseID),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceExists(
|
||||||
|
"google_sql_database_instance.instance", &instance),
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceEquals(
|
||||||
|
"google_sql_database_instance.instance", &instance),
|
||||||
|
testAccCheckGoogleSqlDatabaseRootUserDoesNotExist(
|
||||||
|
&instance),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
func TestAccGoogleSqlDatabaseInstance_settings_basic(t *testing.T) {
|
func TestAccGoogleSqlDatabaseInstance_settings_basic(t *testing.T) {
|
||||||
var instance sqladmin.DatabaseInstance
|
var instance sqladmin.DatabaseInstance
|
||||||
databaseID := acctest.RandInt()
|
databaseID := acctest.RandInt()
|
||||||
@ -86,6 +111,80 @@ func TestAccGoogleSqlDatabaseInstance_settings_basic(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccGoogleSqlDatabaseInstance_slave(t *testing.T) {
|
||||||
|
var instance sqladmin.DatabaseInstance
|
||||||
|
masterID := acctest.RandInt()
|
||||||
|
slaveID := acctest.RandInt()
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccGoogleSqlDatabaseInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(
|
||||||
|
testGoogleSqlDatabaseInstance_slave, masterID, slaveID),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceExists(
|
||||||
|
"google_sql_database_instance.instance_master", &instance),
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceEquals(
|
||||||
|
"google_sql_database_instance.instance_master", &instance),
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceExists(
|
||||||
|
"google_sql_database_instance.instance_slave", &instance),
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceEquals(
|
||||||
|
"google_sql_database_instance.instance_slave", &instance),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccGoogleSqlDatabaseInstance_diskspecs(t *testing.T) {
|
||||||
|
var instance sqladmin.DatabaseInstance
|
||||||
|
masterID := acctest.RandInt()
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccGoogleSqlDatabaseInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(
|
||||||
|
testGoogleSqlDatabaseInstance_diskspecs, masterID),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceExists(
|
||||||
|
"google_sql_database_instance.instance", &instance),
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceEquals(
|
||||||
|
"google_sql_database_instance.instance", &instance),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccGoogleSqlDatabaseInstance_maintenance(t *testing.T) {
|
||||||
|
var instance sqladmin.DatabaseInstance
|
||||||
|
masterID := acctest.RandInt()
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccGoogleSqlDatabaseInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(
|
||||||
|
testGoogleSqlDatabaseInstance_maintenance, masterID),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceExists(
|
||||||
|
"google_sql_database_instance.instance", &instance),
|
||||||
|
testAccCheckGoogleSqlDatabaseInstanceEquals(
|
||||||
|
"google_sql_database_instance.instance", &instance),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccGoogleSqlDatabaseInstance_settings_upgrade(t *testing.T) {
|
func TestAccGoogleSqlDatabaseInstance_settings_upgrade(t *testing.T) {
|
||||||
var instance sqladmin.DatabaseInstance
|
var instance sqladmin.DatabaseInstance
|
||||||
databaseID := acctest.RandInt()
|
databaseID := acctest.RandInt()
|
||||||
@ -199,7 +298,7 @@ func testAccCheckGoogleSqlDatabaseInstanceEquals(n string,
|
|||||||
return fmt.Errorf("Error settings.tier mismatch, (%s, %s)", server, local)
|
return fmt.Errorf("Error settings.tier mismatch, (%s, %s)", server, local)
|
||||||
}
|
}
|
||||||
|
|
||||||
server = instance.MasterInstanceName
|
server = strings.TrimPrefix(instance.MasterInstanceName, instance.Project+":")
|
||||||
local = attributes["master_instance_name"]
|
local = attributes["master_instance_name"]
|
||||||
if server != local && len(server) > 0 && len(local) > 0 {
|
if server != local && len(server) > 0 && len(local) > 0 {
|
||||||
return fmt.Errorf("Error master_instance_name mismatch, (%s, %s)", server, local)
|
return fmt.Errorf("Error master_instance_name mismatch, (%s, %s)", server, local)
|
||||||
@ -237,6 +336,24 @@ func testAccCheckGoogleSqlDatabaseInstanceEquals(n string,
|
|||||||
return fmt.Errorf("Error settings.crash_safe_replication mismatch, (%s, %s)", server, local)
|
return fmt.Errorf("Error settings.crash_safe_replication mismatch, (%s, %s)", server, local)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server = strconv.FormatBool(instance.Settings.StorageAutoResize)
|
||||||
|
local = attributes["settings.0.disk_autoresize"]
|
||||||
|
if server != local && len(server) > 0 && len(local) > 0 {
|
||||||
|
return fmt.Errorf("Error settings.disk_autoresize mismatch, (%s, %s)", server, local)
|
||||||
|
}
|
||||||
|
|
||||||
|
server = strconv.FormatInt(instance.Settings.DataDiskSizeGb, 10)
|
||||||
|
local = attributes["settings.0.disk_size"]
|
||||||
|
if server != local && len(server) > 0 && len(local) > 0 && local != "0" {
|
||||||
|
return fmt.Errorf("Error settings.disk_size mismatch, (%s, %s)", server, local)
|
||||||
|
}
|
||||||
|
|
||||||
|
server = instance.Settings.DataDiskType
|
||||||
|
local = attributes["settings.0.disk_type"]
|
||||||
|
if server != local && len(server) > 0 && len(local) > 0 {
|
||||||
|
return fmt.Errorf("Error settings.disk_type mismatch, (%s, %s)", server, local)
|
||||||
|
}
|
||||||
|
|
||||||
if instance.Settings.IpConfiguration != nil {
|
if instance.Settings.IpConfiguration != nil {
|
||||||
server = strconv.FormatBool(instance.Settings.IpConfiguration.Ipv4Enabled)
|
server = strconv.FormatBool(instance.Settings.IpConfiguration.Ipv4Enabled)
|
||||||
local = attributes["settings.0.ip_configuration.0.ipv4_enabled"]
|
local = attributes["settings.0.ip_configuration.0.ipv4_enabled"]
|
||||||
@ -265,6 +382,26 @@ func testAccCheckGoogleSqlDatabaseInstanceEquals(n string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if instance.Settings.MaintenanceWindow != nil {
|
||||||
|
server = strconv.FormatInt(instance.Settings.MaintenanceWindow.Day, 10)
|
||||||
|
local = attributes["settings.0.maintenance_window.0.day"]
|
||||||
|
if server != local && len(server) > 0 && len(local) > 0 {
|
||||||
|
return fmt.Errorf("Error settings.maintenance_window.day mismatch, (%s, %s)", server, local)
|
||||||
|
}
|
||||||
|
|
||||||
|
server = strconv.FormatInt(instance.Settings.MaintenanceWindow.Hour, 10)
|
||||||
|
local = attributes["settings.0.maintenance_window.0.hour"]
|
||||||
|
if server != local && len(server) > 0 && len(local) > 0 {
|
||||||
|
return fmt.Errorf("Error settings.maintenance_window.hour mismatch, (%s, %s)", server, local)
|
||||||
|
}
|
||||||
|
|
||||||
|
server = instance.Settings.MaintenanceWindow.UpdateTrack
|
||||||
|
local = attributes["settings.0.maintenance_window.0.update_track"]
|
||||||
|
if server != local && len(server) > 0 && len(local) > 0 {
|
||||||
|
return fmt.Errorf("Error settings.maintenance_window.update_track mismatch, (%s, %s)", server, local)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
server = instance.Settings.PricingPlan
|
server = instance.Settings.PricingPlan
|
||||||
local = attributes["settings.0.pricing_plan"]
|
local = attributes["settings.0.pricing_plan"]
|
||||||
if server != local && len(server) > 0 && len(local) > 0 {
|
if server != local && len(server) > 0 && len(local) > 0 {
|
||||||
@ -377,6 +514,27 @@ func testAccGoogleSqlDatabaseInstanceDestroy(s *terraform.State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccCheckGoogleSqlDatabaseRootUserDoesNotExist(
|
||||||
|
instance *sqladmin.DatabaseInstance) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
users, err := config.clientSqlAdmin.Users.List(config.Project, instance.Name).Do()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not list database users for %q: %s", instance.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range users.Items {
|
||||||
|
if u.Name == "root" && u.Host == "%" {
|
||||||
|
return fmt.Errorf("%v@%v user still exists", u.Name, u.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var testGoogleSqlDatabaseInstance_basic = `
|
var testGoogleSqlDatabaseInstance_basic = `
|
||||||
resource "google_sql_database_instance" "instance" {
|
resource "google_sql_database_instance" "instance" {
|
||||||
name = "tf-lw-%d"
|
name = "tf-lw-%d"
|
||||||
@ -397,6 +555,15 @@ resource "google_sql_database_instance" "instance" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
var testGoogleSqlDatabaseInstance_basic3 = `
|
||||||
|
resource "google_sql_database_instance" "instance" {
|
||||||
|
name = "tf-lw-%d"
|
||||||
|
region = "us-central"
|
||||||
|
settings {
|
||||||
|
tier = "db-f1-micro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
var testGoogleSqlDatabaseInstance_settings = `
|
var testGoogleSqlDatabaseInstance_settings = `
|
||||||
resource "google_sql_database_instance" "instance" {
|
resource "google_sql_database_instance" "instance" {
|
||||||
@ -474,6 +641,64 @@ resource "google_sql_database_instance" "instance" {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var testGoogleSqlDatabaseInstance_slave = `
|
||||||
|
resource "google_sql_database_instance" "instance_master" {
|
||||||
|
name = "tf-lw-%d"
|
||||||
|
region = "us-central1"
|
||||||
|
|
||||||
|
settings {
|
||||||
|
tier = "db-f1-micro"
|
||||||
|
|
||||||
|
backup_configuration {
|
||||||
|
enabled = true
|
||||||
|
binary_log_enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_sql_database_instance" "instance_slave" {
|
||||||
|
name = "tf-lw-%d"
|
||||||
|
region = "us-central1"
|
||||||
|
|
||||||
|
master_instance_name = "${google_sql_database_instance.instance_master.name}"
|
||||||
|
|
||||||
|
settings {
|
||||||
|
tier = "db-f1-micro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testGoogleSqlDatabaseInstance_diskspecs = `
|
||||||
|
resource "google_sql_database_instance" "instance" {
|
||||||
|
name = "tf-lw-%d"
|
||||||
|
region = "us-central1"
|
||||||
|
|
||||||
|
settings {
|
||||||
|
tier = "db-f1-micro"
|
||||||
|
disk_autoresize = true
|
||||||
|
disk_size = 15
|
||||||
|
disk_type = "PD_HDD"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testGoogleSqlDatabaseInstance_maintenance = `
|
||||||
|
resource "google_sql_database_instance" "instance" {
|
||||||
|
name = "tf-lw-%d"
|
||||||
|
region = "us-central1"
|
||||||
|
|
||||||
|
settings {
|
||||||
|
tier = "db-f1-micro"
|
||||||
|
|
||||||
|
maintenance_window {
|
||||||
|
day = 7
|
||||||
|
hour = 3
|
||||||
|
update_track = "canary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
var testGoogleSqlDatabaseInstance_authNets_step1 = `
|
var testGoogleSqlDatabaseInstance_authNets_step1 = `
|
||||||
resource "google_sql_database_instance" "instance" {
|
resource "google_sql_database_instance" "instance" {
|
||||||
name = "tf-lw-%d"
|
name = "tf-lw-%d"
|
||||||
|
@ -68,12 +68,12 @@ func TestAccStorageStorageClass(t *testing.T) {
|
|||||||
CheckDestroy: testAccGoogleStorageDestroy,
|
CheckDestroy: testAccGoogleStorageDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testGoogleStorageBucketsReaderStorageClass(bucketName, "STANDARD"),
|
Config: testGoogleStorageBucketsReaderStorageClass(bucketName, "MULTI_REGIONAL"),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudStorageBucketExists(
|
testAccCheckCloudStorageBucketExists(
|
||||||
"google_storage_bucket.bucket", bucketName),
|
"google_storage_bucket.bucket", bucketName),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"google_storage_bucket.bucket", "storage_class", "STANDARD"),
|
"google_storage_bucket.bucket", "storage_class", "MULTI_REGIONAL"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -86,12 +86,12 @@ func TestAccStorageStorageClass(t *testing.T) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config: testGoogleStorageBucketsReaderStorageClass(bucketName, "DURABLE_REDUCED_AVAILABILITY"),
|
Config: testGoogleStorageBucketsReaderStorageClass(bucketName, "REGIONAL"),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudStorageBucketExists(
|
testAccCheckCloudStorageBucketExists(
|
||||||
"google_storage_bucket.bucket", bucketName),
|
"google_storage_bucket.bucket", bucketName),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"google_storage_bucket.bucket", "storage_class", "DURABLE_REDUCED_AVAILABILITY"),
|
"google_storage_bucket.bucket", "storage_class", "REGIONAL"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user