Merge remote-tracking branch 'upstream/master' into source_nat_ip

This commit is contained in:
Hany Fahim 2015-10-02 21:19:43 -04:00
commit ea89339a43
42 changed files with 832 additions and 829 deletions

View File

@ -6,6 +6,7 @@ FEATURES:
* **New resource: `cloudstack_loadbalancer_rule`** [GH-2934] * **New resource: `cloudstack_loadbalancer_rule`** [GH-2934]
* **New resource: `google_compute_project_metadata`** [GH-3065] * **New resource: `google_compute_project_metadata`** [GH-3065]
* **New resources: `aws_ami`, `aws_ami_copy`, `aws_ami_from_instance`** [GH-2874] * **New resources: `aws_ami`, `aws_ami_copy`, `aws_ami_from_instance`** [GH-2874]
* **New resources: `aws_cloudwatch_log_group`** [GH-2415]
* **New resource: `google_storage_bucket_object`** [GH-3192] * **New resource: `google_storage_bucket_object`** [GH-3192]
* **New resources: `google_compute_vpn_gateway`, `google_compute_vpn_tunnel`** [GH-3213] * **New resources: `google_compute_vpn_gateway`, `google_compute_vpn_tunnel`** [GH-3213]
@ -27,8 +28,10 @@ IMPROVEMENTS:
BUG FIXES: BUG FIXES:
* core: Fix problems referencing list attributes in interpolations [GH-2157] * core: Fix problems referencing list attributes in interpolations [GH-2157]
* core: don't error on computed value during input walk [GH-2988]
* provider/google: Crashes with interface conversion in GCE Instance Template [GH-3027] * provider/google: Crashes with interface conversion in GCE Instance Template [GH-3027]
* provider/google: Convert int to int64 when building the GKE cluster.NodeConfig struct [GH-2978] * provider/google: Convert int to int64 when building the GKE cluster.NodeConfig struct [GH-2978]
* provider/google: google_compute_instance_template.network_interface.network should be a URL [GH-3226]
* provider/aws: Retry creation of `aws_ecs_service` if IAM policy isn't ready yet [GH-3061] * provider/aws: Retry creation of `aws_ecs_service` if IAM policy isn't ready yet [GH-3061]
* provider/aws: Fix issue with mixed capitalization for RDS Instances [GH-3053] * provider/aws: Fix issue with mixed capitalization for RDS Instances [GH-3053]
* provider/aws: Fix issue with RDS to allow major version upgrades [GH-3053] * provider/aws: Fix issue with RDS to allow major version upgrades [GH-3053]

View File

@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/service/autoscaling" "github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs" "github.com/aws/aws-sdk-go/service/ecs"
@ -42,6 +43,7 @@ type Config struct {
type AWSClient struct { type AWSClient struct {
cloudwatchconn *cloudwatch.CloudWatch cloudwatchconn *cloudwatch.CloudWatch
cloudwatchlogsconn *cloudwatchlogs.CloudWatchLogs
dynamodbconn *dynamodb.DynamoDB dynamodbconn *dynamodb.DynamoDB
ec2conn *ec2.EC2 ec2conn *ec2.EC2
ecsconn *ecs.ECS ecsconn *ecs.ECS
@ -156,6 +158,9 @@ func (c *Config) Client() (interface{}, error) {
log.Println("[INFO] Initializing CloudWatch SDK connection") log.Println("[INFO] Initializing CloudWatch SDK connection")
client.cloudwatchconn = cloudwatch.New(awsConfig) client.cloudwatchconn = cloudwatch.New(awsConfig)
log.Println("[INFO] Initializing CloudWatch Logs connection")
client.cloudwatchlogsconn = cloudwatchlogs.New(awsConfig)
} }
if len(errs) > 0 { if len(errs) > 0 {

View File

@ -163,6 +163,7 @@ func Provider() terraform.ResourceProvider {
"aws_autoscaling_group": resourceAwsAutoscalingGroup(), "aws_autoscaling_group": resourceAwsAutoscalingGroup(),
"aws_autoscaling_notification": resourceAwsAutoscalingNotification(), "aws_autoscaling_notification": resourceAwsAutoscalingNotification(),
"aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(),
"aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(),
"aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(),
"aws_customer_gateway": resourceAwsCustomerGateway(), "aws_customer_gateway": resourceAwsCustomerGateway(),
"aws_db_instance": resourceAwsDbInstance(), "aws_db_instance": resourceAwsDbInstance(),

View File

@ -0,0 +1,146 @@
package aws
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
)
func resourceAwsCloudWatchLogGroup() *schema.Resource {
return &schema.Resource{
Create: resourceAwsCloudWatchLogGroupCreate,
Read: resourceAwsCloudWatchLogGroupRead,
Update: resourceAwsCloudWatchLogGroupUpdate,
Delete: resourceAwsCloudWatchLogGroupDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"retention_in_days": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 0,
},
"arn": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceAwsCloudWatchLogGroupCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatchlogsconn
log.Printf("[DEBUG] Creating CloudWatch Log Group: %s", d.Get("name").(string))
_, err := conn.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{
LogGroupName: aws.String(d.Get("name").(string)),
})
if err != nil {
return fmt.Errorf("Creating CloudWatch Log Group failed: %s", err)
}
d.SetId(d.Get("name").(string))
log.Println("[INFO] CloudWatch Log Group created")
return resourceAwsCloudWatchLogGroupUpdate(d, meta)
}
func resourceAwsCloudWatchLogGroupRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatchlogsconn
log.Printf("[DEBUG] Reading CloudWatch Log Group: %q", d.Get("name").(string))
lg, err := lookupCloudWatchLogGroup(conn, d.Get("name").(string), nil)
if err != nil {
return err
}
log.Printf("[DEBUG] Found Log Group: %#v", *lg)
d.Set("arn", *lg.Arn)
d.Set("name", *lg.LogGroupName)
if lg.RetentionInDays != nil {
d.Set("retention_in_days", *lg.RetentionInDays)
}
return nil
}
func lookupCloudWatchLogGroup(conn *cloudwatchlogs.CloudWatchLogs,
name string, nextToken *string) (*cloudwatchlogs.LogGroup, error) {
input := &cloudwatchlogs.DescribeLogGroupsInput{
LogGroupNamePrefix: aws.String(name),
NextToken: nextToken,
}
resp, err := conn.DescribeLogGroups(input)
if err != nil {
return nil, err
}
for _, lg := range resp.LogGroups {
if *lg.LogGroupName == name {
return lg, nil
}
}
if resp.NextToken != nil {
return lookupCloudWatchLogGroup(conn, name, resp.NextToken)
}
return nil, fmt.Errorf("CloudWatch Log Group %q not found", name)
}
func resourceAwsCloudWatchLogGroupUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatchlogsconn
name := d.Get("name").(string)
log.Printf("[DEBUG] Updating CloudWatch Log Group: %q", name)
if d.HasChange("retention_in_days") {
var err error
if v, ok := d.GetOk("retention_in_days"); ok {
input := cloudwatchlogs.PutRetentionPolicyInput{
LogGroupName: aws.String(name),
RetentionInDays: aws.Int64(int64(v.(int))),
}
log.Printf("[DEBUG] Setting retention for CloudWatch Log Group: %q: %s", name, input)
_, err = conn.PutRetentionPolicy(&input)
} else {
log.Printf("[DEBUG] Deleting retention for CloudWatch Log Group: %q", name)
_, err = conn.DeleteRetentionPolicy(&cloudwatchlogs.DeleteRetentionPolicyInput{
LogGroupName: aws.String(name),
})
}
return err
}
return resourceAwsCloudWatchLogGroupRead(d, meta)
}
func resourceAwsCloudWatchLogGroupDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatchlogsconn
log.Printf("[INFO] Deleting CloudWatch Log Group: %s", d.Id())
_, err := conn.DeleteLogGroup(&cloudwatchlogs.DeleteLogGroupInput{
LogGroupName: aws.String(d.Get("name").(string)),
})
if err != nil {
return fmt.Errorf("Error deleting CloudWatch Log Group: %s", err)
}
log.Println("[INFO] CloudWatch Log Group deleted")
d.SetId("")
return nil
}

View File

@ -0,0 +1,147 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSCloudWatchLogGroup_basic(t *testing.T) {
var lg cloudwatchlogs.LogGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSCloudWatchLogGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg),
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "retention_in_days", "0"),
),
},
},
})
}
func TestAccAWSCloudWatchLogGroup_retentionPolicy(t *testing.T) {
var lg cloudwatchlogs.LogGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSCloudWatchLogGroupConfig_withRetention,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg),
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "retention_in_days", "365"),
),
},
resource.TestStep{
Config: testAccAWSCloudWatchLogGroupConfigModified_withRetention,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg),
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "retention_in_days", "0"),
),
},
},
})
}
func TestAccAWSCloudWatchLogGroup_multiple(t *testing.T) {
var lg cloudwatchlogs.LogGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSCloudWatchLogGroupConfig_multiple,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.alpha", &lg),
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.alpha", "retention_in_days", "14"),
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.beta", &lg),
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.beta", "retention_in_days", "0"),
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.charlie", &lg),
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.charlie", "retention_in_days", "3653"),
),
},
},
})
}
func testAccCheckCloudWatchLogGroupExists(n string, lg *cloudwatchlogs.LogGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
logGroup, err := lookupCloudWatchLogGroup(conn, rs.Primary.ID, nil)
if err != nil {
return err
}
*lg = *logGroup
return nil
}
}
func testAccCheckAWSCloudWatchLogGroupDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_cloudwatch_log_group" {
continue
}
_, err := lookupCloudWatchLogGroup(conn, rs.Primary.ID, nil)
if err == nil {
return fmt.Errorf("LogGroup Still Exists: %s", rs.Primary.ID)
}
}
return nil
}
var testAccAWSCloudWatchLogGroupConfig = `
resource "aws_cloudwatch_log_group" "foobar" {
name = "foo-bar"
}
`
var testAccAWSCloudWatchLogGroupConfig_withRetention = `
resource "aws_cloudwatch_log_group" "foobar" {
name = "foo-bang"
retention_in_days = 365
}
`
var testAccAWSCloudWatchLogGroupConfigModified_withRetention = `
resource "aws_cloudwatch_log_group" "foobar" {
name = "foo-bang"
}
`
var testAccAWSCloudWatchLogGroupConfig_multiple = `
resource "aws_cloudwatch_log_group" "alpha" {
name = "foo-bar"
retention_in_days = 14
}
resource "aws_cloudwatch_log_group" "beta" {
name = "foo-bara"
}
resource "aws_cloudwatch_log_group" "charlie" {
name = "foo-baraa"
retention_in_days = 3653
}
`

View File

@ -414,11 +414,6 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
}) })
} }
// Set our attributes
if err := resourceAwsInstanceRead(d, meta); err != nil {
return err
}
// Update if we need to // Update if we need to
return resourceAwsInstanceUpdate(d, meta) return resourceAwsInstanceUpdate(d, meta)
} }
@ -548,7 +543,8 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
} }
// SourceDestCheck can only be set on VPC instances // SourceDestCheck can only be set on VPC instances
if d.Get("subnet_id").(string) != "" { // AWS will return an error of InvalidParameterCombination if we attempt
// to modify the source_dest_check of an instance in EC2 Classic
log.Printf("[INFO] Modifying instance %s", d.Id()) log.Printf("[INFO] Modifying instance %s", d.Id())
_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{ _, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
InstanceId: aws.String(d.Id()), InstanceId: aws.String(d.Id()),
@ -557,8 +553,14 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
}, },
}) })
if err != nil { if err != nil {
if ec2err, ok := err.(awserr.Error); ok {
// Toloerate InvalidParameterCombination error in Classic, otherwise
// return the error
if "InvalidParameterCombination" != ec2err.Code() {
return err return err
} }
log.Printf("[WARN] Attempted to modify SourceDestCheck on non VPC instance: %s", ec2err.Message())
}
} }
if d.HasChange("vpc_security_group_ids") { if d.HasChange("vpc_security_group_ids") {

View File

@ -190,6 +190,9 @@ func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
testCheck := func(enabled bool) resource.TestCheckFunc { testCheck := func(enabled bool) resource.TestCheckFunc {
return func(*terraform.State) error { return func(*terraform.State) error {
if v.SourceDestCheck == nil {
return fmt.Errorf("bad source_dest_check: got nil")
}
if *v.SourceDestCheck != enabled { if *v.SourceDestCheck != enabled {
return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck) return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck)
} }

View File

@ -2,6 +2,7 @@ package aws
import ( import (
"fmt" "fmt"
"os"
"testing" "testing"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
@ -212,12 +213,16 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec
} }
} }
// TODO: re-enable this test.
// VPC Peering connections are prefixed with pcx // VPC Peering connections are prefixed with pcx
// Right now there is no VPC Peering resource // Right now there is no VPC Peering resource
func _TestAccAWSRouteTable_vpcPeering(t *testing.T) { func TestAccAWSRouteTable_vpcPeering(t *testing.T) {
var v ec2.RouteTable var v ec2.RouteTable
acctId := os.Getenv("TF_ACC_ID")
if acctId == "" {
t.Fatal("Error: Test TestAccAWSRouteTable_vpcPeering requires an Account ID in TF_ACC_ID ")
}
testCheck := func(*terraform.State) error { testCheck := func(*terraform.State) error {
if len(v.Routes) != 2 { if len(v.Routes) != 2 {
return fmt.Errorf("bad routes: %#v", v.Routes) return fmt.Errorf("bad routes: %#v", v.Routes)
@ -243,7 +248,7 @@ func _TestAccAWSRouteTable_vpcPeering(t *testing.T) {
CheckDestroy: testAccCheckRouteTableDestroy, CheckDestroy: testAccCheckRouteTableDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
resource.TestStep{ resource.TestStep{
Config: testAccRouteTableVpcPeeringConfig, Config: testAccRouteTableVpcPeeringConfig(acctId),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckRouteTableExists( testAccCheckRouteTableExists(
"aws_route_table.foo", &v), "aws_route_table.foo", &v),
@ -395,11 +400,10 @@ resource "aws_route_table" "foo" {
} }
` `
// TODO: re-enable this test.
// VPC Peering connections are prefixed with pcx // VPC Peering connections are prefixed with pcx
// Right now there is no VPC Peering resource // This test requires an ENV var, TF_ACC_ID, with a valid AWS Account ID
const testAccRouteTableVpcPeeringConfig = ` func testAccRouteTableVpcPeeringConfig(acc string) string {
resource "aws_vpc" "foo" { cfg := `resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
} }
@ -407,15 +411,34 @@ resource "aws_internet_gateway" "foo" {
vpc_id = "${aws_vpc.foo.id}" vpc_id = "${aws_vpc.foo.id}"
} }
resource "aws_vpc" "bar" {
cidr_block = "10.3.0.0/16"
}
resource "aws_internet_gateway" "bar" {
vpc_id = "${aws_vpc.bar.id}"
}
resource "aws_vpc_peering_connection" "foo" {
vpc_id = "${aws_vpc.foo.id}"
peer_vpc_id = "${aws_vpc.bar.id}"
peer_owner_id = "%s"
tags {
foo = "bar"
}
}
resource "aws_route_table" "foo" { resource "aws_route_table" "foo" {
vpc_id = "${aws_vpc.foo.id}" vpc_id = "${aws_vpc.foo.id}"
route { route {
cidr_block = "10.2.0.0/16" cidr_block = "10.2.0.0/16"
vpc_peering_connection_id = "pcx-12345" vpc_peering_connection_id = "${aws_vpc_peering_connection.foo.id}"
} }
} }
` `
return fmt.Sprintf(cfg, acc)
}
const testAccRouteTableVgwRoutePropagationConfig = ` const testAccRouteTableVgwRoutePropagationConfig = `
resource "aws_vpc" "foo" { resource "aws_vpc" "foo" {

View File

@ -40,6 +40,12 @@ func resourceCloudStackVPC() *schema.Resource {
ForceNew: true, ForceNew: true,
}, },
"network_domain": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"project": &schema.Schema{ "project": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
@ -84,7 +90,19 @@ func resourceCloudStackVPCCreate(d *schema.ResourceData, meta interface{}) error
} }
// Create a new parameter struct // Create a new parameter struct
p := cs.VPC.NewCreateVPCParams(d.Get("cidr").(string), displaytext.(string), name, vpcofferingid, zoneid) p := cs.VPC.NewCreateVPCParams(
d.Get("cidr").(string),
displaytext.(string),
name,
vpcofferingid,
zoneid,
)
// If there is a network domain supplied, make sure to add it to the request
if networkDomain, ok := d.GetOk("network_domain"); ok {
// Set the network domain
p.SetNetworkdomain(networkDomain.(string))
}
// If there is a project supplied, we retrieve and set the project id // If there is a project supplied, we retrieve and set the project id
if project, ok := d.GetOk("project"); ok { if project, ok := d.GetOk("project"); ok {
@ -127,6 +145,7 @@ func resourceCloudStackVPCRead(d *schema.ResourceData, meta interface{}) error {
d.Set("name", v.Name) d.Set("name", v.Name)
d.Set("display_text", v.Displaytext) d.Set("display_text", v.Displaytext)
d.Set("cidr", v.Cidr) d.Set("cidr", v.Cidr)
d.Set("network_domain", v.Networkdomain)
// Get the VPC offering details // Get the VPC offering details
o, _, err := cs.VPC.GetVPCOfferingByID(v.Vpcofferingid) o, _, err := cs.VPC.GetVPCOfferingByID(v.Vpcofferingid)

View File

@ -76,6 +76,10 @@ func testAccCheckCloudStackVPCAttributes(
return fmt.Errorf("Bad VPC CIDR: %s", vpc.Cidr) return fmt.Errorf("Bad VPC CIDR: %s", vpc.Cidr)
} }
if vpc.Networkdomain != "terraform-domain" {
return fmt.Errorf("Bad network domain: %s", vpc.Networkdomain)
}
return nil return nil
} }
} }
@ -107,6 +111,7 @@ resource "cloudstack_vpc" "foo" {
display_text = "terraform-vpc-text" display_text = "terraform-vpc-text"
cidr = "%s" cidr = "%s"
vpc_offering = "%s" vpc_offering = "%s"
network_domain = "terraform-domain"
zone = "%s" zone = "%s"
}`, }`,
CLOUDSTACK_VPC_CIDR_1, CLOUDSTACK_VPC_CIDR_1,

View File

@ -0,0 +1,158 @@
package google
import (
"bytes"
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/compute/v1"
)
// OperationWaitType is an enum specifying what type of operation
// we're waiting on.
type ComputeOperationWaitType byte
const (
ComputeOperationWaitInvalid ComputeOperationWaitType = iota
ComputeOperationWaitGlobal
ComputeOperationWaitRegion
ComputeOperationWaitZone
)
type ComputeOperationWaiter struct {
Service *compute.Service
Op *compute.Operation
Project string
Region string
Type ComputeOperationWaitType
Zone string
}
func (w *ComputeOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *compute.Operation
var err error
switch w.Type {
case ComputeOperationWaitGlobal:
op, err = w.Service.GlobalOperations.Get(
w.Project, w.Op.Name).Do()
case ComputeOperationWaitRegion:
op, err = w.Service.RegionOperations.Get(
w.Project, w.Region, w.Op.Name).Do()
case ComputeOperationWaitZone:
op, err = w.Service.ZoneOperations.Get(
w.Project, w.Zone, w.Op.Name).Do()
default:
return nil, "bad-type", fmt.Errorf(
"Invalid wait type: %#v", w.Type)
}
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %q when asking for operation %q", op.Status, w.Op.Name)
return op, op.Status, nil
}
}
func (w *ComputeOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: "DONE",
Refresh: w.RefreshFunc(),
}
}
// ComputeOperationError wraps compute.OperationError and implements the
// error interface so it can be returned.
type ComputeOperationError compute.OperationError
func (e ComputeOperationError) Error() string {
var buf bytes.Buffer
for _, err := range e.Errors {
buf.WriteString(err.Message + "\n")
}
return buf.String()
}
func computeOperationWaitGlobal(config *Config, op *compute.Operation, activity string) error {
w := &ComputeOperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: ComputeOperationWaitGlobal,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 4 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
return ComputeOperationError(*op.Error)
}
return nil
}
func computeOperationWaitRegion(config *Config, op *compute.Operation, region, activity string) error {
w := &ComputeOperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: ComputeOperationWaitRegion,
Region: region,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 4 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
return ComputeOperationError(*op.Error)
}
return nil
}
func computeOperationWaitZone(config *Config, op *compute.Operation, zone, activity string) error {
w := &ComputeOperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Zone: zone,
Type: ComputeOperationWaitZone,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 4 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return ComputeOperationError(*op.Error)
}
return nil
}

View File

@ -1,82 +0,0 @@
package google
import (
"bytes"
"fmt"
"log"
"github.com/hashicorp/terraform/helper/resource"
"google.golang.org/api/compute/v1"
)
// OperationWaitType is an enum specifying what type of operation
// we're waiting on.
type OperationWaitType byte
const (
OperationWaitInvalid OperationWaitType = iota
OperationWaitGlobal
OperationWaitRegion
OperationWaitZone
)
type OperationWaiter struct {
Service *compute.Service
Op *compute.Operation
Project string
Region string
Type OperationWaitType
Zone string
}
func (w *OperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *compute.Operation
var err error
switch w.Type {
case OperationWaitGlobal:
op, err = w.Service.GlobalOperations.Get(
w.Project, w.Op.Name).Do()
case OperationWaitRegion:
op, err = w.Service.RegionOperations.Get(
w.Project, w.Region, w.Op.Name).Do()
case OperationWaitZone:
op, err = w.Service.ZoneOperations.Get(
w.Project, w.Zone, w.Op.Name).Do()
default:
return nil, "bad-type", fmt.Errorf(
"Invalid wait type: %#v", w.Type)
}
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %q when asking for operation %q", op.Status, w.Op.Name)
return op, op.Status, nil
}
}
func (w *OperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: "DONE",
Refresh: w.RefreshFunc(),
}
}
// OperationError wraps compute.OperationError and implements the
// error interface so it can be returned.
type OperationError compute.OperationError
func (e OperationError) Error() string {
var buf bytes.Buffer
for _, err := range e.Errors {
buf.WriteString(err.Message + "\n")
}
return buf.String()
}

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
@ -65,28 +64,9 @@ func resourceComputeAddressCreate(d *schema.ResourceData, meta interface{}) erro
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(addr.Name) d.SetId(addr.Name)
// Wait for the operation to complete err = computeOperationWaitRegion(config, op, region, "Creating Address")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Region: region,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for address to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeAddressRead(d, meta) return resourceComputeAddressRead(d, meta)
@ -128,25 +108,9 @@ func resourceComputeAddressDelete(d *schema.ResourceData, meta interface{}) erro
return fmt.Errorf("Error deleting address: %s", err) return fmt.Errorf("Error deleting address: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitRegion(config, op, region, "Deleting Address")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Region: region,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for address to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
@ -224,28 +223,9 @@ func resourceComputeAutoscalerCreate(d *schema.ResourceData, meta interface{}) e
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(scaler.Name) d.SetId(scaler.Name)
// Wait for the operation to complete err = computeOperationWaitZone(config, op, zone.Name, "Creating Autoscaler")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitZone,
Zone: zone.Name,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for Autoscaler to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeAutoscalerRead(d, meta) return resourceComputeAutoscalerRead(d, meta)
@ -292,25 +272,9 @@ func resourceComputeAutoscalerUpdate(d *schema.ResourceData, meta interface{}) e
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(scaler.Name) d.SetId(scaler.Name)
// Wait for the operation to complete err = computeOperationWaitZone(config, op, zone, "Updating Autoscaler")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitZone,
Zone: zone,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for Autoscaler to update: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeAutoscalerRead(d, meta) return resourceComputeAutoscalerRead(d, meta)
@ -326,25 +290,9 @@ func resourceComputeAutoscalerDelete(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error deleting autoscaler: %s", err) return fmt.Errorf("Error deleting autoscaler: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitZone(config, op, zone, "Deleting Autoscaler")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitZone,
Zone: zone,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for Autoscaler to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"log" "log"
"regexp" "regexp"
"time"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -165,28 +164,9 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{
d.SetId(service.Name) d.SetId(service.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Creating Backend Service")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Region: config.Region,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for backend service to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeBackendServiceRead(d, meta) return resourceComputeBackendServiceRead(d, meta)
@ -261,25 +241,9 @@ func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{
d.SetId(service.Name) d.SetId(service.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Updating Backend Service")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Region: config.Region,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for backend service to update: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeBackendServiceRead(d, meta) return resourceComputeBackendServiceRead(d, meta)
@ -295,25 +259,9 @@ func resourceComputeBackendServiceDelete(d *schema.ResourceData, meta interface{
return fmt.Errorf("Error deleting backend service: %s", err) return fmt.Errorf("Error deleting backend service: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Deleting Backend Service")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Region: config.Region,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for backend service to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
@ -128,37 +127,10 @@ func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error {
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(disk.Name) d.SetId(disk.Name)
// Wait for the operation to complete err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Creating Disk")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Zone: d.Get("zone").(string),
Type: OperationWaitZone,
}
state := w.Conf()
if disk.SourceSnapshot != "" {
//creating disk from snapshot takes some time
state.Timeout = 10 * time.Minute
} else {
state.Timeout = 2 * time.Minute
}
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for disk to create: %s", err) return err
} }
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
}
return resourceComputeDiskRead(d, meta) return resourceComputeDiskRead(d, meta)
} }
@ -193,25 +165,10 @@ func resourceComputeDiskDelete(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error deleting disk: %s", err) return fmt.Errorf("Error deleting disk: %s", err)
} }
// Wait for the operation to complete zone := d.Get("zone").(string)
w := &OperationWaiter{ err = computeOperationWaitZone(config, op, zone, "Creating Disk")
Service: config.clientCompute,
Op: op,
Project: config.Project,
Zone: d.Get("zone").(string),
Type: OperationWaitZone,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for disk to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"sort" "sort"
"time"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -135,27 +134,9 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) err
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(firewall.Name) d.SetId(firewall.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Creating Firewall")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for firewall to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeFirewallRead(d, meta) return resourceComputeFirewallRead(d, meta)
@ -198,24 +179,9 @@ func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) err
return fmt.Errorf("Error updating firewall: %s", err) return fmt.Errorf("Error updating firewall: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Updating Firewall")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for firewall to update: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.Partial(false) d.Partial(false)
@ -233,24 +199,9 @@ func resourceComputeFirewallDelete(d *schema.ResourceData, meta interface{}) err
return fmt.Errorf("Error deleting firewall: %s", err) return fmt.Errorf("Error deleting firewall: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Deleting Firewall")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for firewall to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
@ -94,28 +93,9 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(frule.Name) d.SetId(frule.Name)
// Wait for the operation to complete err = computeOperationWaitRegion(config, op, region, "Creating Fowarding Rule")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for ForwardingRule to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeForwardingRuleRead(d, meta) return resourceComputeForwardingRuleRead(d, meta)
@ -137,29 +117,11 @@ func resourceComputeForwardingRuleUpdate(d *schema.ResourceData, meta interface{
return fmt.Errorf("Error updating target: %s", err) return fmt.Errorf("Error updating target: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitRegion(config, op, region, "Updating Forwarding Rule")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for ForwardingRule to update target: %s", err) return err
} }
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
}
d.SetPartial("target") d.SetPartial("target")
} }
@ -206,25 +168,9 @@ func resourceComputeForwardingRuleDelete(d *schema.ResourceData, meta interface{
return fmt.Errorf("Error deleting ForwardingRule: %s", err) return fmt.Errorf("Error deleting ForwardingRule: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitRegion(config, op, region, "Deleting Forwarding Rule")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for ForwardingRule to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
@ -121,27 +120,9 @@ func resourceComputeHttpHealthCheckCreate(d *schema.ResourceData, meta interface
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(hchk.Name) d.SetId(hchk.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Creating Http Health Check")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for HttpHealthCheck to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeHttpHealthCheckRead(d, meta) return resourceComputeHttpHealthCheckRead(d, meta)
@ -190,27 +171,9 @@ func resourceComputeHttpHealthCheckUpdate(d *schema.ResourceData, meta interface
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(hchk.Name) d.SetId(hchk.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Updating Http Health Check")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for HttpHealthCheck to patch: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeHttpHealthCheckRead(d, meta) return resourceComputeHttpHealthCheckRead(d, meta)
@ -254,24 +217,9 @@ func resourceComputeHttpHealthCheckDelete(d *schema.ResourceData, meta interface
return fmt.Errorf("Error deleting HttpHealthCheck: %s", err) return fmt.Errorf("Error deleting HttpHealthCheck: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Deleting Http Health Check")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for HttpHealthCheck to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -273,32 +272,6 @@ func getInstance(config *Config, d *schema.ResourceData) (*compute.Instance, err
return instance, nil return instance, nil
} }
func resourceOperationWaitZone(
config *Config, op *compute.Operation, zone string, activity string) error {
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Zone: zone,
Type: OperationWaitZone,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 10 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
}
return nil
}
func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
@ -521,7 +494,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
d.SetId(instance.Name) d.SetId(instance.Name)
// Wait for the operation to complete // Wait for the operation to complete
waitErr := resourceOperationWaitZone(config, op, zone.Name, "instance to create") waitErr := computeOperationWaitZone(config, op, zone.Name, "instance to create")
if waitErr != nil { if waitErr != nil {
// The resource didn't actually create // The resource didn't actually create
d.SetId("") d.SetId("")
@ -703,7 +676,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
return fmt.Errorf("Error updating metadata: %s", err) return fmt.Errorf("Error updating metadata: %s", err)
} }
opErr := resourceOperationWaitZone(config, op, zone, "metadata to update") opErr := computeOperationWaitZone(config, op, zone, "metadata to update")
if opErr != nil { if opErr != nil {
return opErr return opErr
} }
@ -723,7 +696,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
return fmt.Errorf("Error updating tags: %s", err) return fmt.Errorf("Error updating tags: %s", err)
} }
opErr := resourceOperationWaitZone(config, op, zone, "tags to update") opErr := computeOperationWaitZone(config, op, zone, "tags to update")
if opErr != nil { if opErr != nil {
return opErr return opErr
} }
@ -764,7 +737,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
if err != nil { if err != nil {
return fmt.Errorf("Error deleting old access_config: %s", err) return fmt.Errorf("Error deleting old access_config: %s", err)
} }
opErr := resourceOperationWaitZone(config, op, zone, "old access_config to delete") opErr := computeOperationWaitZone(config, op, zone, "old access_config to delete")
if opErr != nil { if opErr != nil {
return opErr return opErr
} }
@ -783,7 +756,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
if err != nil { if err != nil {
return fmt.Errorf("Error adding new access_config: %s", err) return fmt.Errorf("Error adding new access_config: %s", err)
} }
opErr := resourceOperationWaitZone(config, op, zone, "new access_config to add") opErr := computeOperationWaitZone(config, op, zone, "new access_config to add")
if opErr != nil { if opErr != nil {
return opErr return opErr
} }
@ -809,7 +782,7 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err
} }
// Wait for the operation to complete // Wait for the operation to complete
opErr := resourceOperationWaitZone(config, op, zone, "instance to delete") opErr := computeOperationWaitZone(config, op, zone, "instance to delete")
if opErr != nil { if opErr != nil {
return opErr return opErr
} }

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
@ -82,26 +81,6 @@ func resourceComputeInstanceGroupManager() *schema.Resource {
} }
} }
func waitOpZone(config *Config, op *compute.Operation, zone string,
resource string, action string) (*compute.Operation, error) {
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Zone: zone,
Type: OperationWaitZone,
}
state := w.Conf()
state.Timeout = 8 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return nil, fmt.Errorf("Error waiting for %s to %s: %s", resource, action, err)
}
return opRaw.(*compute.Operation), nil
}
func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
@ -143,16 +122,10 @@ func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta inte
d.SetId(manager.Name) d.SetId(manager.Name)
// Wait for the operation to complete // Wait for the operation to complete
op, err = waitOpZone(config, op, d.Get("zone").(string), "InstanceGroupManager", "create") err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Creating InstanceGroupManager")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
}
return resourceComputeInstanceGroupManagerRead(d, meta) return resourceComputeInstanceGroupManagerRead(d, meta)
} }
@ -208,13 +181,10 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
} }
// Wait for the operation to complete // Wait for the operation to complete
op, err = waitOpZone(config, op, d.Get("zone").(string), "InstanceGroupManager", "update TargetPools") err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Updating InstanceGroupManager")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("target_pools") d.SetPartial("target_pools")
} }
@ -233,13 +203,10 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
} }
// Wait for the operation to complete // Wait for the operation to complete
op, err = waitOpZone(config, op, d.Get("zone").(string), "InstanceGroupManager", "update instance template") err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Updating InstanceGroupManager")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("instance_template") d.SetPartial("instance_template")
} }
@ -257,13 +224,10 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
} }
// Wait for the operation to complete // Wait for the operation to complete
op, err = waitOpZone(config, op, d.Get("zone").(string), "InstanceGroupManager", "update target_size") err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Updating InstanceGroupManager")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
} }
d.SetPartial("target_size") d.SetPartial("target_size")
@ -284,17 +248,10 @@ func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta inte
} }
// Wait for the operation to complete // Wait for the operation to complete
op, err = waitOpZone(config, op, d.Get("zone").(string), "InstanceGroupManager", "delete") err = computeOperationWaitZone(config, op, d.Get("zone").(string), "Deleting InstanceGroupManager")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
}
d.SetId("") d.SetId("")
return nil return nil

View File

@ -2,7 +2,6 @@ package google
import ( import (
"fmt" "fmt"
"time"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -305,11 +304,9 @@ func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.
for i := 0; i < networksCount; i++ { for i := 0; i < networksCount; i++ {
prefix := fmt.Sprintf("network_interface.%d", i) prefix := fmt.Sprintf("network_interface.%d", i)
source := "global/networks/default" source := "global/networks/"
if v, ok := d.GetOk(prefix + ".network"); ok { if v, ok := d.GetOk(prefix + ".network"); ok {
if v.(string) != "default" { source += v.(string)
source = v.(string)
}
} }
// Build the networkInterface // Build the networkInterface
@ -401,28 +398,9 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac
// Store the ID now // Store the ID now
d.SetId(instanceTemplate.Name) d.SetId(instanceTemplate.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Creating Instance Template")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 10 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for instance template to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeInstanceTemplateRead(d, meta) return resourceComputeInstanceTemplateRead(d, meta)
@ -467,25 +445,9 @@ func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interfac
return fmt.Errorf("Error deleting instance template: %s", err) return fmt.Errorf("Error deleting instance template: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Deleting Instance Template")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Delay = 5 * time.Second
state.Timeout = 5 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for instance template to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
@ -60,27 +59,9 @@ func resourceComputeNetworkCreate(d *schema.ResourceData, meta interface{}) erro
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(network.Name) d.SetId(network.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Creating Network")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for network to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeNetworkRead(d, meta) return resourceComputeNetworkRead(d, meta)
@ -118,24 +99,9 @@ func resourceComputeNetworkDelete(d *schema.ResourceData, meta interface{}) erro
return fmt.Errorf("Error deleting network: %s", err) return fmt.Errorf("Error deleting network: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Deleting Network")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for network to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
// "github.com/hashicorp/terraform/helper/hashcode" // "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -30,30 +29,6 @@ func resourceComputeProjectMetadata() *schema.Resource {
} }
} }
func resourceOperationWaitGlobal(config *Config, op *compute.Operation, activity string) error {
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
return OperationError(*op.Error)
}
return nil
}
func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
@ -92,7 +67,7 @@ func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink) log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
return resourceOperationWaitGlobal(config, op, "SetCommonMetadata") return computeOperationWaitGlobal(config, op, "SetCommonMetadata")
} }
err := MetadataRetryWrapper(createMD) err := MetadataRetryWrapper(createMD)
@ -153,7 +128,7 @@ func resourceComputeProjectMetadataUpdate(d *schema.ResourceData, meta interface
// Optimistic locking requires the fingerprint received to match // Optimistic locking requires the fingerprint received to match
// the fingerprint we send the server, if there is a mismatch then we // the fingerprint we send the server, if there is a mismatch then we
// are working on old data, and must retry // are working on old data, and must retry
return resourceOperationWaitGlobal(config, op, "SetCommonMetadata") return computeOperationWaitGlobal(config, op, "SetCommonMetadata")
} }
err := MetadataRetryWrapper(updateMD) err := MetadataRetryWrapper(updateMD)
@ -186,7 +161,7 @@ func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink) log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
err = resourceOperationWaitGlobal(config, op, "SetCommonMetadata") err = computeOperationWaitGlobal(config, op, "SetCommonMetadata")
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,7 +3,6 @@ package google
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -171,27 +170,9 @@ func resourceComputeRouteCreate(d *schema.ResourceData, meta interface{}) error
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(route.Name) d.SetId(route.Name)
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Creating Route")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for route to create: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
} }
return resourceComputeRouteRead(d, meta) return resourceComputeRouteRead(d, meta)
@ -228,24 +209,9 @@ func resourceComputeRouteDelete(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error deleting route: %s", err) return fmt.Errorf("Error deleting route: %s", err)
} }
// Wait for the operation to complete err = computeOperationWaitGlobal(config, op, "Deleting Route")
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil { if err != nil {
return fmt.Errorf("Error waiting for route to delete: %s", err) return err
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
} }
d.SetId("") d.SetId("")

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"log" "log"
"strings" "strings"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1" "google.golang.org/api/compute/v1"
@ -79,26 +78,6 @@ func convertStringArr(ifaceArr []interface{}) []string {
return arr return arr
} }
func waitOp(config *Config, op *compute.Operation,
resource string, action string) (*compute.Operation, error) {
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: config.Region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return nil, fmt.Errorf("Error waiting for %s to %s: %s", resource, action, err)
}
return opRaw.(*compute.Operation), nil
}
// Healthchecks need to exist before being referred to from the target pool. // Healthchecks need to exist before being referred to from the target pool.
func convertHealthChecks(config *Config, names []string) ([]string, error) { func convertHealthChecks(config *Config, names []string) ([]string, error) {
urls := make([]string, len(names)) urls := make([]string, len(names))
@ -171,16 +150,10 @@ func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) e
// It probably maybe worked, so store the ID now // It probably maybe worked, so store the ID now
d.SetId(tpool.Name) d.SetId(tpool.Name)
op, err = waitOp(config, op, "TargetPool", "create") err = computeOperationWaitRegion(config, op, config.Region, "Creating Target Pool")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
}
return resourceComputeTargetPoolRead(d, meta) return resourceComputeTargetPoolRead(d, meta)
} }
@ -246,14 +219,11 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e
if err != nil { if err != nil {
return fmt.Errorf("Error updating health_check: %s", err) return fmt.Errorf("Error updating health_check: %s", err)
} }
op, err = waitOp(config, op, "TargetPool", "removing HealthChecks")
err = computeOperationWaitRegion(config, op, config.Region, "Updating Target Pool")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
addReq := &compute.TargetPoolsAddHealthCheckRequest{ addReq := &compute.TargetPoolsAddHealthCheckRequest{
HealthChecks: make([]*compute.HealthCheckReference, len(add)), HealthChecks: make([]*compute.HealthCheckReference, len(add)),
} }
@ -265,14 +235,11 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e
if err != nil { if err != nil {
return fmt.Errorf("Error updating health_check: %s", err) return fmt.Errorf("Error updating health_check: %s", err)
} }
op, err = waitOp(config, op, "TargetPool", "adding HealthChecks")
err = computeOperationWaitRegion(config, op, config.Region, "Updating Target Pool")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("health_checks") d.SetPartial("health_checks")
} }
@ -302,14 +269,11 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e
if err != nil { if err != nil {
return fmt.Errorf("Error updating instances: %s", err) return fmt.Errorf("Error updating instances: %s", err)
} }
op, err = waitOp(config, op, "TargetPool", "adding instances")
err = computeOperationWaitRegion(config, op, config.Region, "Updating Target Pool")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
removeReq := &compute.TargetPoolsRemoveInstanceRequest{ removeReq := &compute.TargetPoolsRemoveInstanceRequest{
Instances: make([]*compute.InstanceReference, len(remove)), Instances: make([]*compute.InstanceReference, len(remove)),
} }
@ -321,14 +285,11 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e
if err != nil { if err != nil {
return fmt.Errorf("Error updating instances: %s", err) return fmt.Errorf("Error updating instances: %s", err)
} }
op, err = waitOp(config, op, "TargetPool", "removing instances")
err = computeOperationWaitRegion(config, op, config.Region, "Updating Target Pool")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("instances") d.SetPartial("instances")
} }
@ -343,14 +304,10 @@ func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error updating backup_pool: %s", err) return fmt.Errorf("Error updating backup_pool: %s", err)
} }
op, err = waitOp(config, op, "TargetPool", "updating backup_pool") err = computeOperationWaitRegion(config, op, config.Region, "Updating Target Pool")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("backup_pool") d.SetPartial("backup_pool")
} }
@ -390,14 +347,10 @@ func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error deleting TargetPool: %s", err) return fmt.Errorf("Error deleting TargetPool: %s", err)
} }
op, err = waitOp(config, op, "TargetPool", "delete") err = computeOperationWaitRegion(config, op, config.Region, "Deleting Target Pool")
if err != nil { if err != nil {
return err return err
} }
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetId("") d.SetId("")
return nil return nil
} }

View File

@ -69,7 +69,7 @@ func resourceComputeVpnGatewayCreate(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error Inserting VPN Gateway %s into network %s: %s", name, network, err) return fmt.Errorf("Error Inserting VPN Gateway %s into network %s: %s", name, network, err)
} }
err = resourceOperationWaitRegion(config, op, region, "Inserting VPN Gateway") err = computeOperationWaitRegion(config, op, region, "Inserting VPN Gateway")
if err != nil { if err != nil {
return fmt.Errorf("Error Waiting to Insert VPN Gateway %s into network %s: %s", name, network, err) return fmt.Errorf("Error Waiting to Insert VPN Gateway %s into network %s: %s", name, network, err)
} }
@ -111,7 +111,7 @@ func resourceComputeVpnGatewayDelete(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error Reading VPN Gateway %s: %s", name, err) return fmt.Errorf("Error Reading VPN Gateway %s: %s", name, err)
} }
err = resourceOperationWaitRegion(config, op, region, "Deleting VPN Gateway") err = computeOperationWaitRegion(config, op, region, "Deleting VPN Gateway")
if err != nil { if err != nil {
return fmt.Errorf("Error Waiting to Delete VPN Gateway %s: %s", name, err) return fmt.Errorf("Error Waiting to Delete VPN Gateway %s: %s", name, err)
} }

View File

@ -2,7 +2,6 @@ package google
import ( import (
"fmt" "fmt"
"time"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -66,31 +65,6 @@ func resourceComputeVpnTunnel() *schema.Resource {
} }
} }
func resourceOperationWaitRegion(config *Config, op *compute.Operation, region, activity string) error {
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitRegion,
Region: region,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
return OperationError(*op.Error)
}
return nil
}
func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)
@ -125,7 +99,7 @@ func resourceComputeVpnTunnelCreate(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error Inserting VPN Tunnel %s : %s", name, err) return fmt.Errorf("Error Inserting VPN Tunnel %s : %s", name, err)
} }
err = resourceOperationWaitRegion(config, op, region, "Inserting VPN Tunnel") err = computeOperationWaitRegion(config, op, region, "Inserting VPN Tunnel")
if err != nil { if err != nil {
return fmt.Errorf("Error Waiting to Insert VPN Tunnel %s: %s", name, err) return fmt.Errorf("Error Waiting to Insert VPN Tunnel %s: %s", name, err)
} }
@ -169,7 +143,7 @@ func resourceComputeVpnTunnelDelete(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error Reading VPN Tunnel %s: %s", name, err) return fmt.Errorf("Error Reading VPN Tunnel %s: %s", name, err)
} }
err = resourceOperationWaitRegion(config, op, region, "Deleting VPN Tunnel") err = computeOperationWaitRegion(config, op, region, "Deleting VPN Tunnel")
if err != nil { if err != nil {
return fmt.Errorf("Error Waiting to Delete VPN Tunnel %s: %s", name, err) return fmt.Errorf("Error Waiting to Delete VPN Tunnel %s: %s", name, err)
} }

View File

@ -310,6 +310,8 @@ validation_client_name "validator"
node_name "nodename1" node_name "nodename1"
http_proxy "http://proxy.local" http_proxy "http://proxy.local"
ENV['http_proxy'] = "http://proxy.local" ENV['http_proxy'] = "http://proxy.local"
ENV['HTTP_PROXY'] = "http://proxy.local" ENV['HTTP_PROXY'] = "http://proxy.local"

View File

@ -41,6 +41,12 @@ chef_server_url "{{ .ServerURL }}"
validation_client_name "{{ .ValidationClientName }}" validation_client_name "{{ .ValidationClientName }}"
node_name "{{ .NodeName }}" node_name "{{ .NodeName }}"
{{ if .UsePolicyfile }}
use_policyfile true
policy_group "{{ .PolicyGroup }}"
policy_name "{{ .PolicyName }}"
{{ end }}
{{ if .HTTPProxy }} {{ if .HTTPProxy }}
http_proxy "{{ .HTTPProxy }}" http_proxy "{{ .HTTPProxy }}"
ENV['http_proxy'] = "{{ .HTTPProxy }}" ENV['http_proxy'] = "{{ .HTTPProxy }}"
@ -62,6 +68,9 @@ type Provisioner struct {
Attributes interface{} `mapstructure:"attributes"` Attributes interface{} `mapstructure:"attributes"`
Environment string `mapstructure:"environment"` Environment string `mapstructure:"environment"`
LogToFile bool `mapstructure:"log_to_file"` LogToFile bool `mapstructure:"log_to_file"`
UsePolicyfile bool `mapstructure:"use_policyfile"`
PolicyGroup string `mapstructure:"policy_group"`
PolicyName string `mapstructure:"policy_name"`
HTTPProxy string `mapstructure:"http_proxy"` HTTPProxy string `mapstructure:"http_proxy"`
HTTPSProxy string `mapstructure:"https_proxy"` HTTPSProxy string `mapstructure:"https_proxy"`
NOProxy []string `mapstructure:"no_proxy"` NOProxy []string `mapstructure:"no_proxy"`
@ -183,6 +192,12 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string
if p.ValidationKeyPath == "" { if p.ValidationKeyPath == "" {
es = append(es, fmt.Errorf("Key not found: validation_key_path")) es = append(es, fmt.Errorf("Key not found: validation_key_path"))
} }
if p.UsePolicyfile && p.PolicyName == "" {
es = append(es, fmt.Errorf("Policyfile enabled but key not found: policy_name"))
}
if p.UsePolicyfile && p.PolicyGroup == "" {
es = append(es, fmt.Errorf("Policyfile enabled but key not found: policy_group"))
}
return ws, es return ws, es
} }
@ -302,7 +317,15 @@ func (p *Provisioner) runChefClientFunc(
confDir string) func(terraform.UIOutput, communicator.Communicator) error { confDir string) func(terraform.UIOutput, communicator.Communicator) error {
return func(o terraform.UIOutput, comm communicator.Communicator) error { return func(o terraform.UIOutput, comm communicator.Communicator) error {
fb := path.Join(confDir, firstBoot) fb := path.Join(confDir, firstBoot)
cmd := fmt.Sprintf("%s -j %q -E %q", chefCmd, fb, p.Environment) var cmd string
// Policyfiles do not support chef environments, so don't pass the `-E` flag.
if p.UsePolicyfile {
cmd = fmt.Sprintf("%s -j %q", chefCmd, fb)
} else {
cmd = fmt.Sprintf("%s -j %q -E %q", chefCmd, fb, p.Environment)
}
if p.LogToFile { if p.LogToFile {
if err := os.MkdirAll(logfileDir, 0755); err != nil { if err := os.MkdirAll(logfileDir, 0755); err != nil {
@ -413,7 +436,9 @@ func (p *Provisioner) deployConfigFiles(
} }
// Add the initial runlist to the first boot settings // Add the initial runlist to the first boot settings
if !p.UsePolicyfile {
fb["run_list"] = p.RunList fb["run_list"] = p.RunList
}
// Marshal the first boot settings to JSON // Marshal the first boot settings to JSON
d, err := json.Marshal(fb) d, err := json.Marshal(fb)

View File

@ -342,6 +342,8 @@ validation_client_name "validator"
node_name "nodename1" node_name "nodename1"
http_proxy "http://proxy.local" http_proxy "http://proxy.local"
ENV['http_proxy'] = "http://proxy.local" ENV['http_proxy'] = "http://proxy.local"
ENV['HTTP_PROXY'] = "http://proxy.local" ENV['HTTP_PROXY'] = "http://proxy.local"

View File

@ -510,3 +510,62 @@ aws_instance.foo:
t.Fatalf("expected: \n%s\ngot: \n%s\n", expectedStr, actualStr) t.Fatalf("expected: \n%s\ngot: \n%s\n", expectedStr, actualStr)
} }
} }
func TestContext2Input_varPartiallyComputed(t *testing.T) {
input := new(MockUIInput)
m := testModule(t, "input-var-partially-computed")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
Variables: map[string]string{
"foo": "foovalue",
},
UIInput: input,
State: &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_instance.foo": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "i-abc123",
Attributes: map[string]string{
"id": "i-abc123",
},
},
},
},
},
&ModuleState{
Path: append(rootModulePath, "child"),
Resources: map[string]*ResourceState{
"aws_instance.mod": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "i-bcd345",
Attributes: map[string]string{
"id": "i-bcd345",
"value": "one,i-abc123",
},
},
},
},
},
},
},
})
if err := ctx.Input(InputModeStd); err != nil {
t.Fatalf("err: %s", err)
}
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
}

View File

@ -378,7 +378,13 @@ MISSING:
// be unknown. Instead, we return that the value is computed so // be unknown. Instead, we return that the value is computed so
// that the graph can continue to refresh other nodes. It doesn't // that the graph can continue to refresh other nodes. It doesn't
// matter because the config isn't interpolated anyways. // matter because the config isn't interpolated anyways.
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy { //
// For a Destroy, we're also fine with computed values, since our goal is
// only to get destroy nodes for existing resources.
//
// For an input walk, computed values are okay to return because we're only
// looking for missing variables to prompt the user for.
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkInput {
return config.UnknownVariableValue, nil return config.UnknownVariableValue, nil
} }
@ -469,7 +475,13 @@ func (i *Interpolater) computeResourceMultiVariable(
// be unknown. Instead, we return that the value is computed so // be unknown. Instead, we return that the value is computed so
// that the graph can continue to refresh other nodes. It doesn't // that the graph can continue to refresh other nodes. It doesn't
// matter because the config isn't interpolated anyways. // matter because the config isn't interpolated anyways.
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy { //
// For a Destroy, we're also fine with computed values, since our goal is
// only to get destroy nodes for existing resources.
//
// For an input walk, computed values are okay to return because we're only
// looking for missing variables to prompt the user for.
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkInput {
return config.UnknownVariableValue, nil return config.UnknownVariableValue, nil
} }

View File

@ -0,0 +1,5 @@
variable "in" {}
resource "aws_instance" "mod" {
value = "${var.in}"
}

View File

@ -0,0 +1,7 @@
resource "aws_instance" "foo" { }
resource "aws_instance" "bar" { }
module "child" {
source = "./child"
in = "one,${aws_instance.foo.id},${aws_instance.bar.id}"
}

View File

@ -0,0 +1,33 @@
---
layout: "aws"
page_title: "AWS: aws_cloudwatch_log_group"
sidebar_current: "docs-aws-resource-cloudwatch-log-group"
description: |-
Provides a CloudWatch Log Group resource.
---
# aws\_cloudwatch\_log\_group
Provides a CloudWatch Log Group resource.
## Example Usage
```
resource "aws_cloudwatch_log_group" "yada" {
name = "Yada"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the log group
* `retention_in_days` - (Optional) Specifies the number of days
you want to retain log events in the specified log group.
## Attributes Reference
The following attributes are exported:
* `arn` - The Amazon Resource Name (ARN) specifying the log group.

View File

@ -65,7 +65,7 @@ The following arguments are supported:
* `vpc_security_group_ids` - (Optional) List of VPC security groups to associate. * `vpc_security_group_ids` - (Optional) List of VPC security groups to associate.
* `security_group_names` - (Optional/Deprecated) List of DB Security Groups to associate. * `security_group_names` - (Optional/Deprecated) List of DB Security Groups to associate.
Only used for [DB Instances on the _EC2-Classic_ Platform](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.html#USER_VPC.FindDefaultVPC). Only used for [DB Instances on the _EC2-Classic_ Platform](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.html#USER_VPC.FindDefaultVPC).
* `db_subnet_group_name` - (Optional) Name of DB subnet group * `db_subnet_group_name` - (Optional) Name of DB subnet group. DB instance will be created in the VPC associated with the DB subnet group. If unspecified, will be created in the `default` VPC, or in EC2 Classic, if available.
* `parameter_group_name` - (Optional) Name of the DB parameter group to associate. * `parameter_group_name` - (Optional) Name of the DB parameter group to associate.
* `storage_encrypted` - (Optional) Specifies whether the DB instance is encrypted. The default is `false` if not specified. * `storage_encrypted` - (Optional) Specifies whether the DB instance is encrypted. The default is `false` if not specified.
* `apply_immediately` - (Optional) Specifies whether any database modifications * `apply_immediately` - (Optional) Specifies whether any database modifications

View File

@ -53,6 +53,35 @@ With this setup Terraform generates a unique name for your Launch
Configuration and can then update the AutoScaling Group without conflict before Configuration and can then update the AutoScaling Group without conflict before
destroying the previous Launch Configuration. destroying the previous Launch Configuration.
## Using with Spot Instances
Launch configurations can set the spot instance pricing to be used for the
Auto Scaling Group to reserve instances. Simply specifying the `spot_price`
parameter will set the price on the Launch Configuration which will attempt to
reserve your instances at this price. See the [AWS Spot Instance
documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html)
for more information or how to launch [Spot Instances][3] with Terraform.
```
resource "aws_launch_configuration" "as_conf" {
image_id = "ami-1234"
instance_type = "m1.small"
spot_price = "0.001"
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "bar" {
name = "terraform-asg-example"
launch_configuration = "${aws_launch_configuration.as_conf.name}"
lifecycle {
create_before_destroy = true
}
}
```
## Argument Reference ## Argument Reference
The following arguments are supported: The following arguments are supported:
@ -69,7 +98,13 @@ The following arguments are supported:
* `user_data` - (Optional) The user data to provide when launching the instance. * `user_data` - (Optional) The user data to provide when launching the instance.
* `enable_monitoring` - (Optional) Enables/disables detailed monitoring. This is enabled by default. * `enable_monitoring` - (Optional) Enables/disables detailed monitoring. This is enabled by default.
* `ebs_optimized` - (Optional) If true, the launched EC2 instance will be EBS-optimized. * `ebs_optimized` - (Optional) If true, the launched EC2 instance will be EBS-optimized.
* `block_device_mapping` - (Optional) A list of block devices to add. Their keys are documented below. * `root_block_device` - (Optional) Customize details about the root block
device of the instance. See [Block Devices](#block-devices) below for details.
* `ebs_block_device` - (Optional) Additional EBS block devices to attach to the
instance. See [Block Devices](#block-devices) below for details.
* `ephemeral_block_device` - (Optional) Customize Ephemeral (also known as
"Instance Store") volumes on the instance. See [Block Devices](#block-devices) below for details.
* `spot_price` - (Optional) The price to use for reserving spot instances.
<a id="block-devices"></a> <a id="block-devices"></a>
## Block devices ## Block devices
@ -134,3 +169,4 @@ The following attributes are exported:
[1]: /docs/providers/aws/r/autoscaling_group.html [1]: /docs/providers/aws/r/autoscaling_group.html
[2]: /docs/configuration/resources.html#lifecycle [2]: /docs/configuration/resources.html#lifecycle
[3]: /docs/providers/aws/r/spot_instance_request.html

View File

@ -27,7 +27,7 @@ The following arguments are supported:
* `vpc_id` - (Required) The ID of the VPC in which the endpoint will be used. * `vpc_id` - (Required) The ID of the VPC in which the endpoint will be used.
* `service_name` - (Required) The AWS service name, in the form `com.amazonaws.region.service`. * `service_name` - (Required) The AWS service name, in the form `com.amazonaws.region.service`.
* `policy_document` - (Optional) A policy to attach to the endpoint that controls access to the service. * `policy` - (Optional) A policy to attach to the endpoint that controls access to the service.
* `route_table_ids` - (Optional) One or more route table IDs. * `route_table_ids` - (Optional) One or more route table IDs.
## Attributes Reference ## Attributes Reference

View File

@ -70,6 +70,6 @@ resource "rundeck_public_key" "anvils" {
resource "rundeck_private_key" "anvils" { resource "rundeck_private_key" "anvils" {
path = "anvils/id_rsa" path = "anvils/id_rsa"
key_material_file = "${path.module}/id_rsa.pub" key_material = "${file(\"id_rsa.pub\")}"
} }
``` ```

View File

@ -17,7 +17,7 @@ it runs commands.
``` ```
resource "rundeck_private_key" "anvils" { resource "rundeck_private_key" "anvils" {
path = "anvils/id_rsa" path = "anvils/id_rsa"
key_material = "${file(\"/id_rsa\")}" key_material = "${file("/id_rsa")}"
} }
``` ```

View File

@ -15,6 +15,10 @@
<a href="#">CloudWatch Resources</a> <a href="#">CloudWatch Resources</a>
<ul class="nav nav-visible"> <ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-cloudwatch-log-group") %>>
<a href="/docs/providers/aws/r/cloudwatch_log_group.html">aws_cloudwatch_log_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-cloudwatch-metric-alarm") %>> <li<%= sidebar_current("docs-aws-resource-cloudwatch-metric-alarm") %>>
<a href="/docs/providers/aws/r/cloudwatch_metric_alarm.html">aws_cloudwatch_metric_alarm</a> <a href="/docs/providers/aws/r/cloudwatch_metric_alarm.html">aws_cloudwatch_metric_alarm</a>
</li> </li>