Merge branch 'master' into f-oracle-merge

This commit is contained in:
tombuildsstuff 2017-04-07 11:15:36 +01:00
commit 3a084b061a
571 changed files with 53692 additions and 24138 deletions

View File

@ -5,6 +5,7 @@ FEATURES:
* **New Resource:** `aws_lightsail_static_ip` [GH-13175] * **New Resource:** `aws_lightsail_static_ip` [GH-13175]
* **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207] * **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207]
* **New Resource:** `aws_ses_domain_identity` [GH-13098] * **New Resource:** `aws_ses_domain_identity` [GH-13098]
* **New Resource**: `azurerm_managed_disk` [GH-12455]
* **New Resource:** `kubernetes_secret` [GH-12960] * **New Resource:** `kubernetes_secret` [GH-12960]
* **New Data Source:** `aws_iam_role` [GH-13213] * **New Data Source:** `aws_iam_role` [GH-13213]
@ -31,15 +32,22 @@ IMPROVEMENTS:
* provider/aws: Update caller_identity data source [GH-13092] * provider/aws: Update caller_identity data source [GH-13092]
* provider/aws: `aws_subnet_ids` data source for getting a list of subnet ids matching certain criteria [GH-13188] * provider/aws: `aws_subnet_ids` data source for getting a list of subnet ids matching certain criteria [GH-13188]
* provider/aws: Support ip_address_type for aws_alb [GH-13227] * provider/aws: Support ip_address_type for aws_alb [GH-13227]
* provider/aws: Migrate `aws_dms_*` resources away from AWS waiters [GH-13291]
* provider/aws: Add support for treat_missing_data to cloudwatch_metric_alarm [GH-13358]
* provider/aws: Add support for evaluate_low_sample_count_percentiles to cloudwatch_metric_alarm [GH-13371]
* provider/aws: Fix `aws_s3_bucket` drift detection of logging options [GH-13281]
* provider/bitbucket: Improved error handling [GH-13390]
* provider/cloudstack: Do not force a new resource when updating `cloudstack_loadbalancer_rule` members [GH-11786]
* provider/github: Handle the case when issue labels already exist [GH-13182] * provider/github: Handle the case when issue labels already exist [GH-13182]
* provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148]
* provider/triton: Move to joyent/triton-go [GH-13225] * provider/triton: Move to joyent/triton-go [GH-13225]
BUG FIXES: BUG FIXES:
* core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137]
* core: Fix strange issues with computed values in provider configuration that were worked around with `-input=false` [GH-11264], [GH-13264] * core: Fix strange issues with computed values in provider configuration that were worked around with `-input=false` [GH-11264], [GH-13264]
* core: Fix crash when providing nested maps as variable values in a `module` block [GH-13343] * core: Fix crash when providing nested maps as variable values in a `module` block [GH-13343]
* core: `connection` block attributes are now subject to basic validation of attribute names during validate walk [GH-13400]
* provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134]
* provider/aws: Increase timeout for AMI registration [GH-13159] * provider/aws: Increase timeout for AMI registration [GH-13159]
* provider/aws: Increase timeouts for ELB [GH-13161] * provider/aws: Increase timeouts for ELB [GH-13161]
@ -55,8 +63,15 @@ BUG FIXES:
* provider/aws: Set stickiness to computed in alb_target_group [GH-13278] * provider/aws: Set stickiness to computed in alb_target_group [GH-13278]
* provider/aws: Increase timeout for deploying `cloudfront_distribution` from 40 to 70 mins [GH-13319] * provider/aws: Increase timeout for deploying `cloudfront_distribution` from 40 to 70 mins [GH-13319]
* provider/aws: Increase AMI retry timeouts [GH-13324] * provider/aws: Increase AMI retry timeouts [GH-13324]
* provider/aws: Increase subnet deletion timeout [GH-13356]
* provider/aws: Increase launch_configuration creation timeout [GH-13357]
* provider/aws: Increase Beanstalk env 'ready' timeout [GH-13359]
* provider/aws: Recreate opsworks_stack on change of service_role_arn [GH-13325] * provider/aws: Recreate opsworks_stack on change of service_role_arn [GH-13325]
* provider/aws: Fix KMS Key reading with Exists method [GH-13348]
* provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153]
* provider/azurerm: Fix crash when importing Local Network Gateways [GH-13261]
* provider/bitbucket: Fixed issue where provider would fail with an "EOF" error on some operations [GH-13390]
* provider/openstack: Refresh volume_attachment from state if NotFound [GH-13342]
* provider/profitbricks: Changed output type of ips variable of ip_block ProfitBricks resource [GH-13290] * provider/profitbricks: Changed output type of ips variable of ip_block ProfitBricks resource [GH-13290]
## 0.9.2 (March 28, 2017) ## 0.9.2 (March 28, 2017)

View File

@ -121,16 +121,15 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
default: default:
if c.lockCh != nil { if c.lockCh != nil {
// we have an active lock already // we have an active lock already
return "", nil return "", fmt.Errorf("state %q already locked", c.Path)
} }
} }
if c.consulLock == nil { if c.consulLock == nil {
opts := &consulapi.LockOptions{ opts := &consulapi.LockOptions{
Key: c.Path + lockSuffix, Key: c.Path + lockSuffix,
// We currently don't procide any options to block terraform and // only wait briefly, so terraform has the choice to fail fast or
// retry lock acquisition, but we can wait briefly in case the // retry as needed.
// lock is about to be freed.
LockWaitTime: time.Second, LockWaitTime: time.Second,
LockTryOnce: true, LockTryOnce: true,
} }
@ -191,6 +190,10 @@ func (c *RemoteClient) Unlock(id string) error {
err := c.consulLock.Unlock() err := c.consulLock.Unlock()
c.lockCh = nil c.lockCh = nil
// This is only cleanup, and will fail if the lock was immediately taken by
// another client, so we don't report an error to the user here.
c.consulLock.Destroy()
kv := c.Client.KV() kv := c.Client.KV()
_, delErr := kv.Delete(c.Path+lockInfoSuffix, nil) _, delErr := kv.Delete(c.Path+lockInfoSuffix, nil)
if delErr != nil { if delErr != nil {

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/state/remote" "github.com/hashicorp/terraform/state/remote"
) )
@ -98,3 +99,43 @@ func TestConsul_stateLock(t *testing.T) {
remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client) remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client)
} }
func TestConsul_destroyLock(t *testing.T) {
srv := newConsulTestServer(t)
defer srv.Stop()
// Get the backend
b := backend.TestBackendConfig(t, New(), map[string]interface{}{
"address": srv.HTTPAddr,
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
})
// Grab the client
s, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("err: %s", err)
}
c := s.(*remote.State).Client.(*RemoteClient)
info := state.NewLockInfo()
id, err := c.Lock(info)
if err != nil {
t.Fatal(err)
}
lockPath := c.Path + lockSuffix
if err := c.Unlock(id); err != nil {
t.Fatal(err)
}
// get the lock val
pair, _, err := c.Client.KV().Get(lockPath, nil)
if err != nil {
t.Fatal(err)
}
if pair != nil {
t.Fatalf("lock key not cleaned up at: %s", pair.Key)
}
}

View File

@ -2,15 +2,9 @@ package s3
import ( import (
"context" "context"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
cleanhttp "github.com/hashicorp/go-cleanhttp"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -175,48 +169,27 @@ func (b *Backend) configure(ctx context.Context) error {
b.kmsKeyID = data.Get("kms_key_id").(string) b.kmsKeyID = data.Get("kms_key_id").(string)
b.lockTable = data.Get("lock_table").(string) b.lockTable = data.Get("lock_table").(string)
var errs []error cfg := &terraformAWS.Config{
creds, err := terraformAWS.GetCredentials(&terraformAWS.Config{
AccessKey: data.Get("access_key").(string), AccessKey: data.Get("access_key").(string),
SecretKey: data.Get("secret_key").(string),
Token: data.Get("token").(string),
Profile: data.Get("profile").(string),
CredsFilename: data.Get("shared_credentials_file").(string),
AssumeRoleARN: data.Get("role_arn").(string), AssumeRoleARN: data.Get("role_arn").(string),
AssumeRoleSessionName: data.Get("session_name").(string),
AssumeRoleExternalID: data.Get("external_id").(string), AssumeRoleExternalID: data.Get("external_id").(string),
AssumeRolePolicy: data.Get("assume_role_policy").(string), AssumeRolePolicy: data.Get("assume_role_policy").(string),
}) AssumeRoleSessionName: data.Get("session_name").(string),
CredsFilename: data.Get("shared_credentials_file").(string),
Profile: data.Get("profile").(string),
Region: data.Get("region").(string),
S3Endpoint: data.Get("endpoint").(string),
SecretKey: data.Get("secret_key").(string),
Token: data.Get("token").(string),
}
client, err := cfg.Client()
if err != nil { if err != nil {
return err return err
} }
// Call Get to check for credential provider. If nothing found, we'll get an b.s3Client = client.(*terraformAWS.AWSClient).S3()
// error, and we can present it nicely to the user b.dynClient = client.(*terraformAWS.AWSClient).DynamoDB()
_, err = creds.Get()
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS S3 remote.
Please see https://www.terraform.io/docs/state/remote/s3.html for more information on
providing credentials for the AWS S3 remote`))
} else {
errs = append(errs, fmt.Errorf("Error loading credentials for AWS S3 remote: %s", err))
}
return &multierror.Error{Errors: errs}
}
endpoint := data.Get("endpoint").(string)
region := data.Get("region").(string)
awsConfig := &aws.Config{
Credentials: creds,
Endpoint: aws.String(endpoint),
Region: aws.String(region),
HTTPClient: cleanhttp.DefaultClient(),
}
sess := session.New(awsConfig)
b.s3Client = s3.New(sess)
b.dynClient = dynamodb.New(sess)
return nil return nil
} }

View File

@ -29,16 +29,12 @@ func TestBackend_impl(t *testing.T) {
} }
func TestBackendConfig(t *testing.T) { func TestBackendConfig(t *testing.T) {
// This test just instantiates the client. Shouldn't make any actual testACC(t)
// requests nor incur any costs.
config := map[string]interface{}{ config := map[string]interface{}{
"region": "us-west-1", "region": "us-west-1",
"bucket": "tf-test", "bucket": "tf-test",
"key": "state", "key": "state",
"encrypt": true, "encrypt": true,
"access_key": "ACCESS_KEY",
"secret_key": "SECRET_KEY",
"lock_table": "dynamoTable", "lock_table": "dynamoTable",
} }
@ -58,11 +54,11 @@ func TestBackendConfig(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Error when requesting credentials") t.Fatalf("Error when requesting credentials")
} }
if credentials.AccessKeyID != "ACCESS_KEY" { if credentials.AccessKeyID == "" {
t.Fatalf("Incorrect Access Key Id was populated") t.Fatalf("No Access Key Id was populated")
} }
if credentials.SecretAccessKey != "SECRET_KEY" { if credentials.SecretAccessKey == "" {
t.Fatalf("Incorrect Secret Access Key was populated") t.Fatalf("No Secret Access Key was populated")
} }
} }

View File

@ -113,8 +113,7 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
return "", nil return "", nil
} }
stateName := fmt.Sprintf("%s/%s", c.bucketName, c.path) info.Path = c.lockPath()
info.Path = stateName
if info.ID == "" { if info.ID == "" {
lockID, err := uuid.GenerateUUID() lockID, err := uuid.GenerateUUID()
@ -127,7 +126,7 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
putParams := &dynamodb.PutItemInput{ putParams := &dynamodb.PutItemInput{
Item: map[string]*dynamodb.AttributeValue{ Item: map[string]*dynamodb.AttributeValue{
"LockID": {S: aws.String(stateName)}, "LockID": {S: aws.String(c.lockPath())},
"Info": {S: aws.String(string(info.Marshal()))}, "Info": {S: aws.String(string(info.Marshal()))},
}, },
TableName: aws.String(c.lockTable), TableName: aws.String(c.lockTable),
@ -153,7 +152,7 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
func (c *RemoteClient) getLockInfo() (*state.LockInfo, error) { func (c *RemoteClient) getLockInfo() (*state.LockInfo, error) {
getParams := &dynamodb.GetItemInput{ getParams := &dynamodb.GetItemInput{
Key: map[string]*dynamodb.AttributeValue{ Key: map[string]*dynamodb.AttributeValue{
"LockID": {S: aws.String(fmt.Sprintf("%s/%s", c.bucketName, c.path))}, "LockID": {S: aws.String(c.lockPath())},
}, },
ProjectionExpression: aws.String("LockID, Info"), ProjectionExpression: aws.String("LockID, Info"),
TableName: aws.String(c.lockTable), TableName: aws.String(c.lockTable),
@ -202,7 +201,7 @@ func (c *RemoteClient) Unlock(id string) error {
params := &dynamodb.DeleteItemInput{ params := &dynamodb.DeleteItemInput{
Key: map[string]*dynamodb.AttributeValue{ Key: map[string]*dynamodb.AttributeValue{
"LockID": {S: aws.String(fmt.Sprintf("%s/%s", c.bucketName, c.path))}, "LockID": {S: aws.String(c.lockPath())},
}, },
TableName: aws.String(c.lockTable), TableName: aws.String(c.lockTable),
} }
@ -214,3 +213,7 @@ func (c *RemoteClient) Unlock(id string) error {
} }
return nil return nil
} }
func (c *RemoteClient) lockPath() string {
return fmt.Sprintf("%s/%s", c.bucketName, c.path)
}

View File

@ -160,6 +160,14 @@ type AWSClient struct {
wafconn *waf.WAF wafconn *waf.WAF
} }
func (c *AWSClient) S3() *s3.S3 {
return c.s3conn
}
func (c *AWSClient) DynamoDB() *dynamodb.DynamoDB {
return c.dynamodbconn
}
// Client configures and returns a fully initialized AWSClient // Client configures and returns a fully initialized AWSClient
func (c *Config) Client() (interface{}, error) { func (c *Config) Client() (interface{}, error) {
// Get the auth and region. This can fail if keys/regions were not // Get the auth and region. This can fail if keys/regions were not

View File

@ -15,10 +15,10 @@ func TestAccAWSEcsDataSource_ecsTaskDefinition(t *testing.T) {
resource.TestStep{ resource.TestStep{
Config: testAccCheckAwsEcsTaskDefinitionDataSourceConfig, Config: testAccCheckAwsEcsTaskDefinitionDataSourceConfig,
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "id", regexp.MustCompile("^arn:aws:ecs:us-west-2:[0-9]{12}:task-definition/mongodb:[1-9]*[0-9]$")), resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "id", regexp.MustCompile("^arn:aws:ecs:us-west-2:[0-9]{12}:task-definition/mongodb:[1-9][0-9]*$")),
resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "family", "mongodb"), resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "family", "mongodb"),
resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "network_mode", "bridge"), resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "network_mode", "bridge"),
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "revision", regexp.MustCompile("^[1-9]*[0-9]$")), resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "revision", regexp.MustCompile("^[1-9][0-9]*$")),
resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "status", "ACTIVE"), resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "status", "ACTIVE"),
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "task_role_arn", regexp.MustCompile("^arn:aws:iam::[0-9]{12}:role/mongo_role$")), resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "task_role_arn", regexp.MustCompile("^arn:aws:iam::[0-9]{12}:role/mongo_role$")),
), ),

View File

@ -3,10 +3,12 @@ package aws
import ( import (
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
) )
func TestAccAWSCloudWatchMetricAlarm_importBasic(t *testing.T) { func TestAccAWSCloudWatchMetricAlarm_importBasic(t *testing.T) {
rInt := acctest.RandInt()
resourceName := "aws_cloudwatch_metric_alarm.foobar" resourceName := "aws_cloudwatch_metric_alarm.foobar"
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
@ -14,11 +16,11 @@ func TestAccAWSCloudWatchMetricAlarm_importBasic(t *testing.T) {
Providers: testAccProviders, Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy, CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
resource.TestStep{ {
Config: testAccAWSCloudWatchMetricAlarmConfig, Config: testAccAWSCloudWatchMetricAlarmConfig(rInt),
}, },
resource.TestStep{ {
ResourceName: resourceName, ResourceName: resourceName,
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,

View File

@ -1,13 +1,16 @@
package aws package aws
import ( import (
"fmt"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
) )
func TestAccAWSElasticacheParameterGroup_importBasic(t *testing.T) { func TestAccAWSElasticacheParameterGroup_importBasic(t *testing.T) {
resourceName := "aws_elasticache_parameter_group.bar" resourceName := "aws_elasticache_parameter_group.bar"
rName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt())
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -15,7 +18,7 @@ func TestAccAWSElasticacheParameterGroup_importBasic(t *testing.T) {
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy, CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
resource.TestStep{ resource.TestStep{
Config: testAccAWSElasticacheParameterGroupConfig, Config: testAccAWSElasticacheParameterGroupConfig(rName),
}, },
resource.TestStep{ resource.TestStep{

View File

@ -446,7 +446,6 @@ func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) e
d.Set("health_check_type", g.HealthCheckType) d.Set("health_check_type", g.HealthCheckType)
d.Set("launch_configuration", g.LaunchConfigurationName) d.Set("launch_configuration", g.LaunchConfigurationName)
d.Set("load_balancers", flattenStringList(g.LoadBalancerNames)) d.Set("load_balancers", flattenStringList(g.LoadBalancerNames))
d.Set("target_group_arns", flattenStringList(g.TargetGroupARNs))
if err := d.Set("suspended_processes", flattenAsgSuspendedProcesses(g.SuspendedProcesses)); err != nil { if err := d.Set("suspended_processes", flattenAsgSuspendedProcesses(g.SuspendedProcesses)); err != nil {
log.Printf("[WARN] Error setting suspended_processes for %q: %s", d.Id(), err) log.Printf("[WARN] Error setting suspended_processes for %q: %s", d.Id(), err)

View File

@ -445,15 +445,6 @@ func TestAccAWSAutoScalingGroup_ALB_TargetGroups(t *testing.T) {
"aws_autoscaling_group.bar", "target_group_arns.#", "1"), "aws_autoscaling_group.bar", "target_group_arns.#", "1"),
), ),
}, },
resource.TestStep{
Config: testAccAWSAutoScalingGroupConfig_ALB_TargetGroup_pre,
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group),
resource.TestCheckResourceAttr(
"aws_autoscaling_group.bar", "target_group_arns.#", "0"),
),
},
}, },
}) })
} }

View File

@ -103,7 +103,7 @@ func testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "aws_cloudwatch_log_subscription_filter" "test_lambdafunction_logfilter" { resource "aws_cloudwatch_log_subscription_filter" "test_lambdafunction_logfilter" {
name = "test_lambdafunction_logfilter_%s" name = "test_lambdafunction_logfilter_%s"
log_group_name = "example_lambda_name" log_group_name = "${aws_cloudwatch_log_group.logs.name}"
filter_pattern = "logtype test" filter_pattern = "logtype test"
destination_arn = "${aws_lambda_function.test_lambdafunction.arn}" destination_arn = "${aws_lambda_function.test_lambdafunction.arn}"
} }

View File

@ -8,14 +8,18 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/hashicorp/terraform/helper/validation"
) )
func resourceAwsCloudWatchMetricAlarm() *schema.Resource { func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceAwsCloudWatchMetricAlarmCreate, Create: resourceAwsCloudWatchMetricAlarmCreate,
Read: resourceAwsCloudWatchMetricAlarmRead, Read: resourceAwsCloudWatchMetricAlarmRead,
Update: resourceAwsCloudWatchMetricAlarmUpdate, Update: resourceAwsCloudWatchMetricAlarmUpdate,
Delete: resourceAwsCloudWatchMetricAlarmDelete, Delete: resourceAwsCloudWatchMetricAlarmDelete,
SchemaVersion: 1,
MigrateState: resourceAwsCloudWatchMetricAlarmMigrateState,
Importer: &schema.ResourceImporter{ Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough, State: schema.ImportStatePassthrough,
}, },
@ -95,6 +99,18 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
Optional: true, Optional: true,
ConflictsWith: []string{"statistic"}, ConflictsWith: []string{"statistic"},
}, },
"treat_missing_data": {
Type: schema.TypeString,
Optional: true,
Default: "missing",
ValidateFunc: validation.StringInSlice([]string{"breaching", "notBreaching", "ignore", "missing"}, true),
},
"evaluate_low_sample_count_percentiles": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"evaluate", "ignore"}, true),
},
}, },
} }
} }
@ -161,6 +177,8 @@ func resourceAwsCloudWatchMetricAlarmRead(d *schema.ResourceData, meta interface
d.Set("threshold", a.Threshold) d.Set("threshold", a.Threshold)
d.Set("unit", a.Unit) d.Set("unit", a.Unit)
d.Set("extended_statistic", a.ExtendedStatistic) d.Set("extended_statistic", a.ExtendedStatistic)
d.Set("treat_missing_data", a.TreatMissingData)
d.Set("evaluate_low_sample_count_percentiles", a.EvaluateLowSampleCountPercentile)
return nil return nil
} }
@ -214,6 +232,7 @@ func getAwsCloudWatchPutMetricAlarmInput(d *schema.ResourceData) cloudwatch.PutM
Namespace: aws.String(d.Get("namespace").(string)), Namespace: aws.String(d.Get("namespace").(string)),
Period: aws.Int64(int64(d.Get("period").(int))), Period: aws.Int64(int64(d.Get("period").(int))),
Threshold: aws.Float64(d.Get("threshold").(float64)), Threshold: aws.Float64(d.Get("threshold").(float64)),
TreatMissingData: aws.String(d.Get("treat_missing_data").(string)),
} }
if v := d.Get("actions_enabled"); v != nil { if v := d.Get("actions_enabled"); v != nil {
@ -236,6 +255,10 @@ func getAwsCloudWatchPutMetricAlarmInput(d *schema.ResourceData) cloudwatch.PutM
params.ExtendedStatistic = aws.String(v.(string)) params.ExtendedStatistic = aws.String(v.(string))
} }
if v, ok := d.GetOk("evaluate_low_sample_count_percentiles"); ok {
params.EvaluateLowSampleCountPercentile = aws.String(v.(string))
}
var alarmActions []*string var alarmActions []*string
if v := d.Get("alarm_actions"); v != nil { if v := d.Get("alarm_actions"); v != nil {
for _, v := range v.(*schema.Set).List() { for _, v := range v.(*schema.Set).List() {

View File

@ -0,0 +1,33 @@
package aws
import (
"fmt"
"log"
"github.com/hashicorp/terraform/terraform"
)
func resourceAwsCloudWatchMetricAlarmMigrateState(
v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
switch v {
case 0:
log.Println("[INFO] Found AWS CloudWatch Metric Alarm State v0; migrating to v1")
return migrateCloudWatchMetricAlarmStateV0toV1(is)
default:
return is, fmt.Errorf("Unexpected schema version: %d", v)
}
}
func migrateCloudWatchMetricAlarmStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) {
if is.Empty() {
log.Println("[DEBUG] Empty InstanceState; nothing to migrate.")
return is, nil
}
log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes)
is.Attributes["treat_missing_data"] = "missing"
log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes)
return is, nil
}

View File

@ -0,0 +1,41 @@
package aws
import (
"testing"
"github.com/hashicorp/terraform/terraform"
)
func TestAWSCloudWatchMetricAlarmMigrateState(t *testing.T) {
cases := map[string]struct {
StateVersion int
ID string
Attributes map[string]string
Expected string
Meta interface{}
}{
"v0_1": {
StateVersion: 0,
ID: "some_id",
Attributes: map[string]string{},
Expected: "missing",
},
}
for tn, tc := range cases {
is := &terraform.InstanceState{
ID: tc.ID,
Attributes: tc.Attributes,
}
is, err := resourceAwsCloudWatchMetricAlarmMigrateState(
tc.StateVersion, is, tc.Meta)
if err != nil {
t.Fatalf("bad: %s, err: %#v", tn, err)
}
if is.Attributes["treat_missing_data"] != tc.Expected {
t.Fatalf("bad Cloudwatch Metric Alarm Migrate: %s\n\n expected: %s", is.Attributes["treat_missing_data"], tc.Expected)
}
}
}

View File

@ -7,20 +7,21 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func TestAccAWSCloudWatchMetricAlarm_basic(t *testing.T) { func TestAccAWSCloudWatchMetricAlarm_basic(t *testing.T) {
var alarm cloudwatch.MetricAlarm var alarm cloudwatch.MetricAlarm
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders, Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy, CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
{ {
Config: testAccAWSCloudWatchMetricAlarmConfig, Config: testAccAWSCloudWatchMetricAlarmConfig(rInt),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm), testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "metric_name", "CPUUtilization"), resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "metric_name", "CPUUtilization"),
@ -33,8 +34,9 @@ func TestAccAWSCloudWatchMetricAlarm_basic(t *testing.T) {
}) })
} }
func TestAccAWSCloudWatchMetricAlarm_extendedStatistic(t *testing.T) { func TestAccAWSCloudWatchMetricAlarm_treatMissingData(t *testing.T) {
var alarm cloudwatch.MetricAlarm var alarm cloudwatch.MetricAlarm
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -42,7 +44,61 @@ func TestAccAWSCloudWatchMetricAlarm_extendedStatistic(t *testing.T) {
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy, CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
{ {
Config: testAccAWSCloudWatchMetricAlarmConfigExtendedStatistic, Config: testAccAWSCloudWatchMetricAlarmConfigTreatMissingData(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "treat_missing_data", "missing"),
),
},
{
Config: testAccAWSCloudWatchMetricAlarmConfigTreatMissingDataUpdate(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "treat_missing_data", "breaching"),
),
},
},
})
}
func TestAccAWSCloudWatchMetricAlarm_evaluateLowSampleCountPercentiles(t *testing.T) {
var alarm cloudwatch.MetricAlarm
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSCloudWatchMetricAlarmConfigTreatEvaluateLowSampleCountPercentiles(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "evaluate_low_sample_count_percentiles", "evaluate"),
),
},
{
Config: testAccAWSCloudWatchMetricAlarmConfigTreatEvaluateLowSampleCountPercentilesUpdated(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "evaluate_low_sample_count_percentiles", "ignore"),
),
},
},
})
}
func TestAccAWSCloudWatchMetricAlarm_extendedStatistic(t *testing.T) {
var alarm cloudwatch.MetricAlarm
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSCloudWatchMetricAlarmConfigExtendedStatistic(rInt),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm), testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "extended_statistic", "p88.0"), resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "extended_statistic", "p88.0"),
@ -53,13 +109,14 @@ func TestAccAWSCloudWatchMetricAlarm_extendedStatistic(t *testing.T) {
} }
func TestAccAWSCloudWatchMetricAlarm_missingStatistic(t *testing.T) { func TestAccAWSCloudWatchMetricAlarm_missingStatistic(t *testing.T) {
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders, Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy, CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
{ {
Config: testAccAWSCloudWatchMetricAlarmConfigMissingStatistic, Config: testAccAWSCloudWatchMetricAlarmConfigMissingStatistic(rInt),
ExpectError: regexp.MustCompile("One of `statistic` or `extended_statistic` must be set for a cloudwatch metric alarm"), ExpectError: regexp.MustCompile("One of `statistic` or `extended_statistic` must be set for a cloudwatch metric alarm"),
}, },
}, },
@ -133,9 +190,10 @@ func testAccCheckAWSCloudWatchMetricAlarmDestroy(s *terraform.State) error {
return nil return nil
} }
var testAccAWSCloudWatchMetricAlarmConfig = fmt.Sprintf(` func testAccAWSCloudWatchMetricAlarmConfig(rInt int) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" { resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar5" alarm_name = "terraform-test-foobar%d"
comparison_operator = "GreaterThanOrEqualToThreshold" comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2" evaluation_periods = "2"
metric_name = "CPUUtilization" metric_name = "CPUUtilization"
@ -148,12 +206,93 @@ resource "aws_cloudwatch_metric_alarm" "foobar" {
dimensions { dimensions {
InstanceId = "i-abc123" InstanceId = "i-abc123"
} }
}`, rInt)
} }
`)
var testAccAWSCloudWatchMetricAlarmConfigExtendedStatistic = fmt.Sprintf(` func testAccAWSCloudWatchMetricAlarmConfigTreatMissingData(rInt int) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" { resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar6" alarm_name = "terraform-test-foobar%d"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
statistic = "Average"
threshold = "80"
alarm_description = "This metric monitors ec2 cpu utilization"
treat_missing_data = "missing"
insufficient_data_actions = []
dimensions {
InstanceId = "i-abc123"
}
}`, rInt)
}
func testAccAWSCloudWatchMetricAlarmConfigTreatMissingDataUpdate(rInt int) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar%d"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
statistic = "Average"
threshold = "80"
alarm_description = "This metric monitors ec2 cpu utilization"
treat_missing_data = "breaching"
insufficient_data_actions = []
dimensions {
InstanceId = "i-abc123"
}
}`, rInt)
}
func testAccAWSCloudWatchMetricAlarmConfigTreatEvaluateLowSampleCountPercentiles(rInt int) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar%d"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
extended_statistic = "p88.0"
threshold = "80"
alarm_description = "This metric monitors ec2 cpu utilization"
evaluate_low_sample_count_percentiles = "evaluate"
insufficient_data_actions = []
dimensions {
InstanceId = "i-abc123"
}
}`, rInt)
}
func testAccAWSCloudWatchMetricAlarmConfigTreatEvaluateLowSampleCountPercentilesUpdated(rInt int) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar%d"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
extended_statistic = "p88.0"
threshold = "80"
alarm_description = "This metric monitors ec2 cpu utilization"
evaluate_low_sample_count_percentiles = "ignore"
insufficient_data_actions = []
dimensions {
InstanceId = "i-abc123"
}
}`, rInt)
}
func testAccAWSCloudWatchMetricAlarmConfigExtendedStatistic(rInt int) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar%d"
comparison_operator = "GreaterThanOrEqualToThreshold" comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2" evaluation_periods = "2"
metric_name = "CPUUtilization" metric_name = "CPUUtilization"
@ -166,12 +305,13 @@ resource "aws_cloudwatch_metric_alarm" "foobar" {
dimensions { dimensions {
InstanceId = "i-abc123" InstanceId = "i-abc123"
} }
}`, rInt)
} }
`)
var testAccAWSCloudWatchMetricAlarmConfigMissingStatistic = fmt.Sprintf(` func testAccAWSCloudWatchMetricAlarmConfigMissingStatistic(rInt int) string {
return fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" { resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar6" alarm_name = "terraform-test-foobar%d"
comparison_operator = "GreaterThanOrEqualToThreshold" comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2" evaluation_periods = "2"
metric_name = "CPUUtilization" metric_name = "CPUUtilization"
@ -183,5 +323,5 @@ resource "aws_cloudwatch_metric_alarm" "foobar" {
dimensions { dimensions {
InstanceId = "i-abc123" InstanceId = "i-abc123"
} }
}`, rInt)
} }
`)

View File

@ -6,7 +6,6 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/private/waiter"
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice" dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation" "github.com/hashicorp/terraform/helper/validation"
@ -168,6 +167,7 @@ func resourceAwsDmsEndpointRead(d *schema.ResourceData, meta interface{}) error
}) })
if err != nil { if err != nil {
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" { if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
log.Printf("[DEBUG] DMS Replication Endpoint %q Not Found", d.Id())
d.SetId("") d.SetId("")
return nil return nil
} }
@ -283,11 +283,6 @@ func resourceAwsDmsEndpointDelete(d *schema.ResourceData, meta interface{}) erro
return err return err
} }
waitErr := waitForEndpointDelete(conn, d.Get("endpoint_id").(string), 30, 20)
if waitErr != nil {
return waitErr
}
return nil return nil
} }
@ -310,36 +305,3 @@ func resourceAwsDmsEndpointSetState(d *schema.ResourceData, endpoint *dms.Endpoi
return nil return nil
} }
func waitForEndpointDelete(client *dms.DatabaseMigrationService, endpointId string, delay int, maxAttempts int) error {
input := &dms.DescribeEndpointsInput{
Filters: []*dms.Filter{
{
Name: aws.String("endpoint-id"),
Values: []*string{aws.String(endpointId)},
},
},
}
config := waiter.Config{
Operation: "DescribeEndpoints",
Delay: delay,
MaxAttempts: maxAttempts,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "path",
Argument: "length(Endpoints[]) > `0`",
Expected: false,
},
},
}
w := waiter.Waiter{
Client: client,
Input: input,
Config: config,
}
return w.Wait()
}

View File

@ -8,7 +8,6 @@ import (
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice" dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -67,11 +66,6 @@ func dmsEndpointDestroy(s *terraform.State) error {
} }
func checkDmsEndpointExists(n string) resource.TestCheckFunc { func checkDmsEndpointExists(n string) resource.TestCheckFunc {
providers := []*schema.Provider{testAccProvider}
return checkDmsEndpointExistsWithProviders(n, &providers)
}
func checkDmsEndpointExistsWithProviders(n string, providers *[]*schema.Provider) 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 {
@ -81,29 +75,26 @@ func checkDmsEndpointExistsWithProviders(n string, providers *[]*schema.Provider
if rs.Primary.ID == "" { if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set") return fmt.Errorf("No ID is set")
} }
for _, provider := range *providers {
// Ignore if Meta is empty, this can happen for validation providers
if provider.Meta() == nil {
continue
}
conn := provider.Meta().(*AWSClient).dmsconn conn := testAccProvider.Meta().(*AWSClient).dmsconn
_, err := conn.DescribeEndpoints(&dms.DescribeEndpointsInput{ resp, err := conn.DescribeEndpoints(&dms.DescribeEndpointsInput{
Filters: []*dms.Filter{ Filters: []*dms.Filter{
{ {
Name: aws.String("endpoint-id"), Name: aws.String("endpoint-id"),
Values: []*string{aws.String(rs.Primary.ID)}, Values: []*string{aws.String(rs.Primary.ID)},
},
}, },
}) },
})
if err != nil { if err != nil {
return fmt.Errorf("DMS endpoint error: %v", err) return fmt.Errorf("DMS endpoint error: %v", err)
}
return nil
} }
return fmt.Errorf("DMS endpoint not found") if resp.Endpoints == nil {
return fmt.Errorf("DMS endpoint not found")
}
return nil
} }
} }

View File

@ -3,11 +3,12 @@ package aws
import ( import (
"fmt" "fmt"
"log" "log"
"time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/private/waiter"
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice" dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
) )
@ -174,12 +175,23 @@ func resourceAwsDmsReplicationInstanceCreate(d *schema.ResourceData, meta interf
return err return err
} }
err = waitForInstanceCreated(conn, d.Get("replication_instance_id").(string), 30, 20) d.SetId(d.Get("replication_instance_id").(string))
stateConf := &resource.StateChangeConf{
Pending: []string{"creating"},
Target: []string{"available"},
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil { if err != nil {
return err return err
} }
d.SetId(d.Get("replication_instance_id").(string))
return resourceAwsDmsReplicationInstanceRead(d, meta) return resourceAwsDmsReplicationInstanceRead(d, meta)
} }
@ -196,6 +208,7 @@ func resourceAwsDmsReplicationInstanceRead(d *schema.ResourceData, meta interfac
}) })
if err != nil { if err != nil {
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" { if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
log.Printf("[DEBUG] DMS Replication Instance %q Not Found", d.Id())
d.SetId("") d.SetId("")
return nil return nil
} }
@ -287,6 +300,21 @@ func resourceAwsDmsReplicationInstanceUpdate(d *schema.ResourceData, meta interf
return err return err
} }
stateConf := &resource.StateChangeConf{
Pending: []string{"modifying"},
Target: []string{"available"},
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil {
return err
}
return resourceAwsDmsReplicationInstanceRead(d, meta) return resourceAwsDmsReplicationInstanceRead(d, meta)
} }
@ -307,9 +335,19 @@ func resourceAwsDmsReplicationInstanceDelete(d *schema.ResourceData, meta interf
return err return err
} }
waitErr := waitForInstanceDeleted(conn, d.Get("replication_instance_id").(string), 30, 20) stateConf := &resource.StateChangeConf{
if waitErr != nil { Pending: []string{"deleting"},
return waitErr Target: []string{},
Refresh: resourceAwsDmsReplicationInstanceStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil {
return err
} }
return nil return nil
@ -355,68 +393,35 @@ func resourceAwsDmsReplicationInstanceSetState(d *schema.ResourceData, instance
return nil return nil
} }
func waitForInstanceCreated(client *dms.DatabaseMigrationService, id string, delay int, maxAttempts int) error { func resourceAwsDmsReplicationInstanceStateRefreshFunc(
input := &dms.DescribeReplicationInstancesInput{ d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
Filters: []*dms.Filter{ return func() (interface{}, string, error) {
{ conn := meta.(*AWSClient).dmsconn
Name: aws.String("replication-instance-id"),
Values: []*string{aws.String(id)}, v, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
Filters: []*dms.Filter{
{
Name: aws.String("replication-instance-id"),
Values: []*string{aws.String(d.Id())}, // Must use d.Id() to work with import.
},
}, },
}, })
} if err != nil {
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
return nil, "", nil
}
log.Printf("Error on retrieving DMS Replication Instance when waiting: %s", err)
return nil, "", err
}
config := waiter.Config{ if v == nil {
Operation: "DescribeReplicationInstances", return nil, "", nil
Delay: delay, }
MaxAttempts: maxAttempts,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "ReplicationInstances[].ReplicationInstanceStatus",
Expected: "available",
},
},
}
w := waiter.Waiter{ if v.ReplicationInstances == nil {
Client: client, return nil, "", fmt.Errorf("Error on retrieving DMS Replication Instance when waiting for State")
Input: input, }
Config: config,
}
return w.Wait() return v, *v.ReplicationInstances[0].ReplicationInstanceStatus, nil
} }
func waitForInstanceDeleted(client *dms.DatabaseMigrationService, id string, delay int, maxAttempts int) error {
input := &dms.DescribeReplicationInstancesInput{
Filters: []*dms.Filter{
{
Name: aws.String("replication-instance-id"),
Values: []*string{aws.String(id)},
},
},
}
config := waiter.Config{
Operation: "DescribeReplicationInstances",
Delay: delay,
MaxAttempts: maxAttempts,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "path",
Argument: "length(ReplicationInstances[]) > `0`",
Expected: false,
},
},
}
w := waiter.Waiter{
Client: client,
Input: input,
Config: config,
}
return w.Wait()
} }

View File

@ -8,7 +8,6 @@ import (
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice" dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -47,11 +46,6 @@ func TestAccAwsDmsReplicationInstanceBasic(t *testing.T) {
} }
func checkDmsReplicationInstanceExists(n string) resource.TestCheckFunc { func checkDmsReplicationInstanceExists(n string) resource.TestCheckFunc {
providers := []*schema.Provider{testAccProvider}
return checkDmsReplicationInstanceExistsWithProviders(n, &providers)
}
func checkDmsReplicationInstanceExistsWithProviders(n string, providers *[]*schema.Provider) 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 {
@ -61,29 +55,24 @@ func checkDmsReplicationInstanceExistsWithProviders(n string, providers *[]*sche
if rs.Primary.ID == "" { if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set") return fmt.Errorf("No ID is set")
} }
for _, provider := range *providers { conn := testAccProvider.Meta().(*AWSClient).dmsconn
// Ignore if Meta is empty, this can happen for validation providers resp, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
if provider.Meta() == nil { Filters: []*dms.Filter{
continue {
} Name: aws.String("replication-instance-id"),
Values: []*string{aws.String(rs.Primary.ID)},
conn := provider.Meta().(*AWSClient).dmsconn
_, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
Filters: []*dms.Filter{
{
Name: aws.String("replication-instance-id"),
Values: []*string{aws.String(rs.Primary.ID)},
},
}, },
}) },
})
if err != nil { if err != nil {
return fmt.Errorf("DMS replication instance error: %v", err) return fmt.Errorf("DMS replication instance error: %v", err)
} }
return nil if resp.ReplicationInstances == nil {
return fmt.Errorf("DMS replication instance not found")
} }
return fmt.Errorf("DMS replication instance not found") return nil
} }
} }
@ -104,22 +93,11 @@ func dmsReplicationInstanceDestroy(s *terraform.State) error {
func dmsReplicationInstanceConfig(randId string) string { func dmsReplicationInstanceConfig(randId string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "aws_iam_role" "dms_iam_role" {
name = "dms-vpc-role"
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
}
resource "aws_iam_role_policy_attachment" "dms_iam_role_policy" {
role = "${aws_iam_role.dms_iam_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"
}
resource "aws_vpc" "dms_vpc" { resource "aws_vpc" "dms_vpc" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
tags { tags {
Name = "tf-test-dms-vpc-%[1]s" Name = "tf-test-dms-vpc-%[1]s"
} }
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_subnet" "dms_subnet_1" { resource "aws_subnet" "dms_subnet_1" {
@ -146,7 +124,6 @@ resource "aws_dms_replication_subnet_group" "dms_replication_subnet_group" {
replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s" replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s"
replication_subnet_group_description = "terraform test for replication subnet group" replication_subnet_group_description = "terraform test for replication subnet group"
subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"] subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"]
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_replication_instance" "dms_replication_instance" { resource "aws_dms_replication_instance" "dms_replication_instance" {
@ -168,22 +145,11 @@ resource "aws_dms_replication_instance" "dms_replication_instance" {
func dmsReplicationInstanceConfigUpdate(randId string) string { func dmsReplicationInstanceConfigUpdate(randId string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "aws_iam_role" "dms_iam_role" {
name = "dms-vpc-role-%[1]s"
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
}
resource "aws_iam_role_policy_attachment" "dms_iam_role_policy" {
role = "${aws_iam_role.dms_iam_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"
}
resource "aws_vpc" "dms_vpc" { resource "aws_vpc" "dms_vpc" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
tags { tags {
Name = "tf-test-dms-vpc-%[1]s" Name = "tf-test-dms-vpc-%[1]s"
} }
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_subnet" "dms_subnet_1" { resource "aws_subnet" "dms_subnet_1" {
@ -210,7 +176,6 @@ resource "aws_dms_replication_subnet_group" "dms_replication_subnet_group" {
replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s" replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s"
replication_subnet_group_description = "terraform test for replication subnet group" replication_subnet_group_description = "terraform test for replication subnet group"
subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"] subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"]
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_replication_instance" "dms_replication_instance" { resource "aws_dms_replication_instance" "dms_replication_instance" {

View File

@ -101,22 +101,11 @@ func dmsReplicationSubnetGroupDestroy(s *terraform.State) error {
func dmsReplicationSubnetGroupConfig(randId string) string { func dmsReplicationSubnetGroupConfig(randId string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "aws_iam_role" "dms_iam_role" {
name = "dms-vpc-role-%[1]s"
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
}
resource "aws_iam_role_policy_attachment" "dms_iam_role_policy" {
role = "${aws_iam_role.dms_iam_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"
}
resource "aws_vpc" "dms_vpc" { resource "aws_vpc" "dms_vpc" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
tags { tags {
Name = "tf-test-dms-vpc-%[1]s" Name = "tf-test-dms-vpc-%[1]s"
} }
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_subnet" "dms_subnet_1" { resource "aws_subnet" "dms_subnet_1" {
@ -164,22 +153,11 @@ resource "aws_dms_replication_subnet_group" "dms_replication_subnet_group" {
func dmsReplicationSubnetGroupConfigUpdate(randId string) string { func dmsReplicationSubnetGroupConfigUpdate(randId string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "aws_iam_role" "dms_iam_role" {
name = "dms-vpc-role"
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
}
resource "aws_iam_role_policy_attachment" "dms_iam_role_policy" {
role = "${aws_iam_role.dms_iam_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"
}
resource "aws_vpc" "dms_vpc" { resource "aws_vpc" "dms_vpc" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
tags { tags {
Name = "tf-test-dms-vpc-%[1]s" Name = "tf-test-dms-vpc-%[1]s"
} }
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_subnet" "dms_subnet_1" { resource "aws_subnet" "dms_subnet_1" {

View File

@ -8,8 +8,8 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/private/waiter"
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice" dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation" "github.com/hashicorp/terraform/helper/validation"
) )
@ -121,13 +121,23 @@ func resourceAwsDmsReplicationTaskCreate(d *schema.ResourceData, meta interface{
} }
taskId := d.Get("replication_task_id").(string) taskId := d.Get("replication_task_id").(string)
d.SetId(taskId)
err = waitForTaskCreated(conn, taskId, 30, 10) stateConf := &resource.StateChangeConf{
Pending: []string{"creating"},
Target: []string{"ready"},
Refresh: resourceAwsDmsReplicationTaskStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil { if err != nil {
return err return err
} }
d.SetId(taskId)
return resourceAwsDmsReplicationTaskRead(d, meta) return resourceAwsDmsReplicationTaskRead(d, meta)
} }
@ -144,6 +154,7 @@ func resourceAwsDmsReplicationTaskRead(d *schema.ResourceData, meta interface{})
}) })
if err != nil { if err != nil {
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" { if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
log.Printf("[DEBUG] DMS Replication Task %q Not Found", d.Id())
d.SetId("") d.SetId("")
return nil return nil
} }
@ -213,7 +224,17 @@ func resourceAwsDmsReplicationTaskUpdate(d *schema.ResourceData, meta interface{
return err return err
} }
err = waitForTaskUpdated(conn, d.Get("replication_task_id").(string), 30, 10) stateConf := &resource.StateChangeConf{
Pending: []string{"modifying"},
Target: []string{"ready"},
Refresh: resourceAwsDmsReplicationTaskStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil { if err != nil {
return err return err
} }
@ -235,12 +256,27 @@ func resourceAwsDmsReplicationTaskDelete(d *schema.ResourceData, meta interface{
_, err := conn.DeleteReplicationTask(request) _, err := conn.DeleteReplicationTask(request)
if err != nil { if err != nil {
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
log.Printf("[DEBUG] DMS Replication Task %q Not Found", d.Id())
d.SetId("")
return nil
}
return err return err
} }
waitErr := waitForTaskDeleted(conn, d.Get("replication_task_id").(string), 30, 10) stateConf := &resource.StateChangeConf{
if waitErr != nil { Pending: []string{"deleting"},
return waitErr Target: []string{},
Refresh: resourceAwsDmsReplicationTaskStateRefreshFunc(d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting
}
// Wait, catching any errors
_, err = stateConf.WaitForState()
if err != nil {
return err
} }
return nil return nil
@ -261,119 +297,35 @@ func resourceAwsDmsReplicationTaskSetState(d *schema.ResourceData, task *dms.Rep
return nil return nil
} }
func waitForTaskCreated(client *dms.DatabaseMigrationService, id string, delay int, maxAttempts int) error { func resourceAwsDmsReplicationTaskStateRefreshFunc(
input := &dms.DescribeReplicationTasksInput{ d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
Filters: []*dms.Filter{ return func() (interface{}, string, error) {
{ conn := meta.(*AWSClient).dmsconn
Name: aws.String("replication-task-id"),
Values: []*string{aws.String(id)},
},
},
}
config := waiter.Config{ v, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{
Operation: "DescribeReplicationTasks", Filters: []*dms.Filter{
Delay: delay, {
MaxAttempts: maxAttempts, Name: aws.String("replication-task-id"),
Acceptors: []waiter.WaitAcceptor{ Values: []*string{aws.String(d.Id())}, // Must use d.Id() to work with import.
{ },
State: "retry",
Matcher: "pathAll",
Argument: "ReplicationTasks[].Status",
Expected: "creating",
}, },
{ })
State: "success", if err != nil {
Matcher: "pathAll", if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
Argument: "ReplicationTasks[].Status", return nil, "", nil
Expected: "ready", }
}, log.Printf("Error on retrieving DMS Replication Task when waiting: %s", err)
}, return nil, "", err
} }
w := waiter.Waiter{ if v == nil {
Client: client, return nil, "", nil
Input: input, }
Config: config,
}
return w.Wait() if v.ReplicationTasks != nil {
} log.Printf("[DEBUG] DMS Replication Task status for instance %s: %s", d.Id(), *v.ReplicationTasks[0].Status)
}
func waitForTaskUpdated(client *dms.DatabaseMigrationService, id string, delay int, maxAttempts int) error {
input := &dms.DescribeReplicationTasksInput{ return v, *v.ReplicationTasks[0].Status, nil
Filters: []*dms.Filter{ }
{
Name: aws.String("replication-task-id"),
Values: []*string{aws.String(id)},
},
},
}
config := waiter.Config{
Operation: "DescribeReplicationTasks",
Delay: delay,
MaxAttempts: maxAttempts,
Acceptors: []waiter.WaitAcceptor{
{
State: "retry",
Matcher: "pathAll",
Argument: "ReplicationTasks[].Status",
Expected: "modifying",
},
{
State: "success",
Matcher: "pathAll",
Argument: "ReplicationTasks[].Status",
Expected: "ready",
},
},
}
w := waiter.Waiter{
Client: client,
Input: input,
Config: config,
}
return w.Wait()
}
func waitForTaskDeleted(client *dms.DatabaseMigrationService, id string, delay int, maxAttempts int) error {
input := &dms.DescribeReplicationTasksInput{
Filters: []*dms.Filter{
{
Name: aws.String("replication-task-id"),
Values: []*string{aws.String(id)},
},
},
}
config := waiter.Config{
Operation: "DescribeReplicationTasks",
Delay: delay,
MaxAttempts: maxAttempts,
Acceptors: []waiter.WaitAcceptor{
{
State: "retry",
Matcher: "pathAll",
Argument: "ReplicationTasks[].Status",
Expected: "deleting",
},
{
State: "success",
Matcher: "path",
Argument: "length(ReplicationTasks[]) > `0`",
Expected: false,
},
},
}
w := waiter.Waiter{
Client: client,
Input: input,
Config: config,
}
return w.Wait()
} }

View File

@ -8,7 +8,6 @@ import (
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice" dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -44,11 +43,6 @@ func TestAccAwsDmsReplicationTaskBasic(t *testing.T) {
} }
func checkDmsReplicationTaskExists(n string) resource.TestCheckFunc { func checkDmsReplicationTaskExists(n string) resource.TestCheckFunc {
providers := []*schema.Provider{testAccProvider}
return checkDmsReplicationTaskExistsWithProviders(n, &providers)
}
func checkDmsReplicationTaskExistsWithProviders(n string, providers *[]*schema.Provider) 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 {
@ -58,29 +52,25 @@ func checkDmsReplicationTaskExistsWithProviders(n string, providers *[]*schema.P
if rs.Primary.ID == "" { if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set") return fmt.Errorf("No ID is set")
} }
for _, provider := range *providers {
// Ignore if Meta is empty, this can happen for validation providers
if provider.Meta() == nil {
continue
}
conn := provider.Meta().(*AWSClient).dmsconn conn := testAccProvider.Meta().(*AWSClient).dmsconn
_, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{ resp, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{
Filters: []*dms.Filter{ Filters: []*dms.Filter{
{ {
Name: aws.String("replication-task-id"), Name: aws.String("replication-task-id"),
Values: []*string{aws.String(rs.Primary.ID)}, Values: []*string{aws.String(rs.Primary.ID)},
},
}, },
}) },
})
if err != nil { if err != nil {
return fmt.Errorf("DMS replication subnet group error: %v", err) return err
}
return nil
} }
return fmt.Errorf("DMS replication subnet group not found") if resp.ReplicationTasks == nil {
return fmt.Errorf("DMS replication task error: %v", err)
}
return nil
} }
} }
@ -90,9 +80,22 @@ func dmsReplicationTaskDestroy(s *terraform.State) error {
continue continue
} }
err := checkDmsReplicationTaskExists(rs.Primary.ID) conn := testAccProvider.Meta().(*AWSClient).dmsconn
if err == nil { resp, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{
return fmt.Errorf("Found replication subnet group that was not destroyed: %s", rs.Primary.ID) Filters: []*dms.Filter{
{
Name: aws.String("replication-task-id"),
Values: []*string{aws.String(rs.Primary.ID)},
},
},
})
if err != nil {
return nil
}
if resp != nil && len(resp.ReplicationTasks) > 0 {
return fmt.Errorf("DMS replication task still exists: %v", err)
} }
} }
@ -101,22 +104,11 @@ func dmsReplicationTaskDestroy(s *terraform.State) error {
func dmsReplicationTaskConfig(randId string) string { func dmsReplicationTaskConfig(randId string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "aws_iam_role" "dms_iam_role" {
name = "dms-vpc-role-%[1]s"
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
}
resource "aws_iam_role_policy_attachment" "dms_iam_role_policy" {
role = "${aws_iam_role.dms_iam_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"
}
resource "aws_vpc" "dms_vpc" { resource "aws_vpc" "dms_vpc" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
tags { tags {
Name = "tf-test-dms-vpc-%[1]s" Name = "tf-test-dms-vpc-%[1]s"
} }
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_subnet" "dms_subnet_1" { resource "aws_subnet" "dms_subnet_1" {
@ -148,7 +140,6 @@ resource "aws_dms_endpoint" "dms_endpoint_source" {
port = 3306 port = 3306
username = "tftest" username = "tftest"
password = "tftest" password = "tftest"
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_endpoint" "dms_endpoint_target" { resource "aws_dms_endpoint" "dms_endpoint_target" {
@ -160,14 +151,12 @@ resource "aws_dms_endpoint" "dms_endpoint_target" {
port = 3306 port = 3306
username = "tftest" username = "tftest"
password = "tftest" password = "tftest"
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_replication_subnet_group" "dms_replication_subnet_group" { resource "aws_dms_replication_subnet_group" "dms_replication_subnet_group" {
replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s" replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s"
replication_subnet_group_description = "terraform test for replication subnet group" replication_subnet_group_description = "terraform test for replication subnet group"
subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"] subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"]
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_replication_instance" "dms_replication_instance" { resource "aws_dms_replication_instance" "dms_replication_instance" {
@ -199,22 +188,11 @@ resource "aws_dms_replication_task" "dms_replication_task" {
func dmsReplicationTaskConfigUpdate(randId string) string { func dmsReplicationTaskConfigUpdate(randId string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "aws_iam_role" "dms_iam_role" {
name = "dms-vpc-role"
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
}
resource "aws_iam_role_policy_attachment" "dms_iam_role_policy" {
role = "${aws_iam_role.dms_iam_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"
}
resource "aws_vpc" "dms_vpc" { resource "aws_vpc" "dms_vpc" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
tags { tags {
Name = "tf-test-dms-vpc-%[1]s" Name = "tf-test-dms-vpc-%[1]s"
} }
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_subnet" "dms_subnet_1" { resource "aws_subnet" "dms_subnet_1" {
@ -246,7 +224,6 @@ resource "aws_dms_endpoint" "dms_endpoint_source" {
port = 3306 port = 3306
username = "tftest" username = "tftest"
password = "tftest" password = "tftest"
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_endpoint" "dms_endpoint_target" { resource "aws_dms_endpoint" "dms_endpoint_target" {
@ -258,14 +235,12 @@ resource "aws_dms_endpoint" "dms_endpoint_target" {
port = 3306 port = 3306
username = "tftest" username = "tftest"
password = "tftest" password = "tftest"
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_replication_subnet_group" "dms_replication_subnet_group" { resource "aws_dms_replication_subnet_group" "dms_replication_subnet_group" {
replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s" replication_subnet_group_id = "tf-test-dms-replication-subnet-group-%[1]s"
replication_subnet_group_description = "terraform test for replication subnet group" replication_subnet_group_description = "terraform test for replication subnet group"
subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"] subnet_ids = ["${aws_subnet.dms_subnet_1.id}", "${aws_subnet.dms_subnet_2.id}"]
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
} }
resource "aws_dms_replication_instance" "dms_replication_instance" { resource "aws_dms_replication_instance" "dms_replication_instance" {

View File

@ -125,7 +125,7 @@ func resourceAwsElasticBeanstalkEnvironment() *schema.Resource {
"wait_for_ready_timeout": &schema.Schema{ "wait_for_ready_timeout": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Default: "10m", Default: "20m",
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string) value := v.(string)
duration, err := time.ParseDuration(value) duration, err := time.ParseDuration(value)

View File

@ -7,12 +7,14 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/elasticache" "github.com/aws/aws-sdk-go/service/elasticache"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) { func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
var v elasticache.CacheParameterGroup var v elasticache.CacheParameterGroup
rName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt())
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -20,12 +22,12 @@ func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy, CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
resource.TestStep{ resource.TestStep{
Config: testAccAWSElasticacheParameterGroupConfig, Config: testAccAWSElasticacheParameterGroupConfig(rName),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v), testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
testAccCheckAWSElasticacheParameterGroupAttributes(&v), testAccCheckAWSElasticacheParameterGroupAttributes(&v, rName),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"), "aws_elasticache_parameter_group.bar", "name", rName),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "family", "redis2.8"), "aws_elasticache_parameter_group.bar", "family", "redis2.8"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
@ -37,12 +39,12 @@ func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
), ),
}, },
resource.TestStep{ resource.TestStep{
Config: testAccAWSElasticacheParameterGroupAddParametersConfig, Config: testAccAWSElasticacheParameterGroupAddParametersConfig(rName),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v), testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
testAccCheckAWSElasticacheParameterGroupAttributes(&v), testAccCheckAWSElasticacheParameterGroupAttributes(&v, rName),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"), "aws_elasticache_parameter_group.bar", "name", rName),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "family", "redis2.8"), "aws_elasticache_parameter_group.bar", "family", "redis2.8"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
@ -63,6 +65,7 @@ func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
func TestAccAWSElasticacheParameterGroupOnly(t *testing.T) { func TestAccAWSElasticacheParameterGroupOnly(t *testing.T) {
var v elasticache.CacheParameterGroup var v elasticache.CacheParameterGroup
rName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt())
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -70,12 +73,12 @@ func TestAccAWSElasticacheParameterGroupOnly(t *testing.T) {
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy, CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
resource.TestStep{ resource.TestStep{
Config: testAccAWSElasticacheParameterGroupOnlyConfig, Config: testAccAWSElasticacheParameterGroupOnlyConfig(rName),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v), testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
testAccCheckAWSElasticacheParameterGroupAttributes(&v), testAccCheckAWSElasticacheParameterGroupAttributes(&v, rName),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"), "aws_elasticache_parameter_group.bar", "name", rName),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "family", "redis2.8"), "aws_elasticache_parameter_group.bar", "family", "redis2.8"),
), ),
@ -118,10 +121,10 @@ func testAccCheckAWSElasticacheParameterGroupDestroy(s *terraform.State) error {
return nil return nil
} }
func testAccCheckAWSElasticacheParameterGroupAttributes(v *elasticache.CacheParameterGroup) resource.TestCheckFunc { func testAccCheckAWSElasticacheParameterGroupAttributes(v *elasticache.CacheParameterGroup, rName string) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
if *v.CacheParameterGroupName != "parameter-group-test-terraform" { if *v.CacheParameterGroupName != rName {
return fmt.Errorf("bad name: %#v", v.CacheParameterGroupName) return fmt.Errorf("bad name: %#v", v.CacheParameterGroupName)
} }
@ -167,20 +170,22 @@ func testAccCheckAWSElasticacheParameterGroupExists(n string, v *elasticache.Cac
} }
} }
const testAccAWSElasticacheParameterGroupConfig = ` func testAccAWSElasticacheParameterGroupConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_elasticache_parameter_group" "bar" { resource "aws_elasticache_parameter_group" "bar" {
name = "parameter-group-test-terraform" name = "%s"
family = "redis2.8" family = "redis2.8"
parameter { parameter {
name = "appendonly" name = "appendonly"
value = "yes" value = "yes"
} }
}`, rName)
} }
`
const testAccAWSElasticacheParameterGroupAddParametersConfig = ` func testAccAWSElasticacheParameterGroupAddParametersConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_elasticache_parameter_group" "bar" { resource "aws_elasticache_parameter_group" "bar" {
name = "parameter-group-test-terraform" name = "%s"
family = "redis2.8" family = "redis2.8"
description = "Test parameter group for terraform" description = "Test parameter group for terraform"
parameter { parameter {
@ -191,13 +196,14 @@ resource "aws_elasticache_parameter_group" "bar" {
name = "appendfsync" name = "appendfsync"
value = "always" value = "always"
} }
}`, rName)
} }
`
const testAccAWSElasticacheParameterGroupOnlyConfig = ` func testAccAWSElasticacheParameterGroupOnlyConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_elasticache_parameter_group" "bar" { resource "aws_elasticache_parameter_group" "bar" {
name = "parameter-group-test-terraform" name = "%s"
family = "redis2.8" family = "redis2.8"
description = "Test parameter group for terraform" description = "Test parameter group for terraform"
}`, rName)
} }
`

View File

@ -228,10 +228,6 @@ func testAccESDomainConfig_complex(randInt int) string {
resource "aws_elasticsearch_domain" "example" { resource "aws_elasticsearch_domain" "example" {
domain_name = "tf-test-%d" domain_name = "tf-test-%d"
cluster_config {
instance_type = "r3.large.elasticsearch"
}
advanced_options { advanced_options {
"indices.fielddata.cache.size" = 80 "indices.fielddata.cache.size" = 80
} }
@ -243,6 +239,7 @@ resource "aws_elasticsearch_domain" "example" {
cluster_config { cluster_config {
instance_count = 2 instance_count = 2
zone_awareness_enabled = true zone_awareness_enabled = true
instance_type = "r3.large.elasticsearch"
} }
snapshot_options { snapshot_options {

View File

@ -487,6 +487,7 @@ resource "aws_redshift_cluster" "test_cluster" {
master_password = "T3stPass" master_password = "T3stPass"
node_type = "dc1.large" node_type = "dc1.large"
cluster_type = "single-node" cluster_type = "single-node"
skip_final_snapshot = true
}` }`
var testAccKinesisFirehoseDeliveryStreamConfig_RedshiftBasic = testAccKinesisFirehoseDeliveryStreamBaseRedshiftConfig + ` var testAccKinesisFirehoseDeliveryStreamConfig_RedshiftBasic = testAccKinesisFirehoseDeliveryStreamBaseRedshiftConfig + `

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/kms" "github.com/aws/aws-sdk-go/service/kms"
"github.com/hashicorp/errwrap" "github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
@ -18,6 +19,7 @@ func resourceAwsKmsKey() *schema.Resource {
Read: resourceAwsKmsKeyRead, Read: resourceAwsKmsKeyRead,
Update: resourceAwsKmsKeyUpdate, Update: resourceAwsKmsKeyUpdate,
Delete: resourceAwsKmsKeyDelete, Delete: resourceAwsKmsKeyDelete,
Exists: resourceAwsKmsKeyExists,
Importer: &schema.ResourceImporter{ Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough, State: schema.ImportStatePassthrough,
@ -368,6 +370,30 @@ func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error {
return nil return nil
} }
func resourceAwsKmsKeyExists(d *schema.ResourceData, meta interface{}) (bool, error) {
conn := meta.(*AWSClient).kmsconn
req := &kms.DescribeKeyInput{
KeyId: aws.String(d.Id()),
}
resp, err := conn.DescribeKey(req)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
return false, nil
}
}
return false, err
}
metadata := resp.KeyMetadata
if *metadata.KeyState == "PendingDeletion" {
return false, nil
}
return true, nil
}
func resourceAwsKmsKeyDelete(d *schema.ResourceData, meta interface{}) error { func resourceAwsKmsKeyDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).kmsconn conn := meta.(*AWSClient).kmsconn
keyId := d.Get("key_id").(string) keyId := d.Get("key_id").(string)

View File

@ -37,6 +37,29 @@ func TestAccAWSKmsKey_basic(t *testing.T) {
}) })
} }
func TestAccAWSKmsKey_disappears(t *testing.T) {
var key kms.KeyMetadata
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSKmsKeyDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSKmsKey,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSKmsKeyExists("aws_kms_key.foo", &key),
),
},
{
Config: testAccAWSKmsKey_other_region,
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
},
})
}
func TestAccAWSKmsKey_policy(t *testing.T) { func TestAccAWSKmsKey_policy(t *testing.T) {
var key kms.KeyMetadata var key kms.KeyMetadata
expectedPolicyText := `{"Version":"2012-10-17","Id":"kms-tf-1","Statement":[{"Sid":"Enable IAM User Permissions","Effect":"Allow","Principal":{"AWS":"*"},"Action":"kms:*","Resource":"*"}]}` expectedPolicyText := `{"Version":"2012-10-17","Id":"kms-tf-1","Statement":[{"Sid":"Enable IAM User Permissions","Effect":"Allow","Principal":{"AWS":"*"},"Action":"kms:*","Resource":"*"}]}`
@ -238,6 +261,32 @@ resource "aws_kms_key" "foo" {
POLICY POLICY
}`, kmsTimestamp) }`, kmsTimestamp)
var testAccAWSKmsKey_other_region = fmt.Sprintf(`
provider "aws" {
region = "us-east-1"
}
resource "aws_kms_key" "foo" {
description = "Terraform acc test %s"
deletion_window_in_days = 7
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "kms-tf-1",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "kms:*",
"Resource": "*"
}
]
}
POLICY
}`, kmsTimestamp)
var testAccAWSKmsKey_removedPolicy = fmt.Sprintf(` var testAccAWSKmsKey_removedPolicy = fmt.Sprintf(`
resource "aws_kms_key" "foo" { resource "aws_kms_key" "foo" {
description = "Terraform acc test %s" description = "Terraform acc test %s"

View File

@ -473,7 +473,7 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface
// IAM profiles can take ~10 seconds to propagate in AWS: // IAM profiles can take ~10 seconds to propagate in AWS:
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console // http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
err = resource.Retry(30*time.Second, func() *resource.RetryError { err = resource.Retry(90*time.Second, func() *resource.RetryError {
_, err := autoscalingconn.CreateLaunchConfiguration(&createLaunchConfigurationOpts) _, err := autoscalingconn.CreateLaunchConfiguration(&createLaunchConfigurationOpts)
if err != nil { if err != nil {
if awsErr, ok := err.(awserr.Error); ok { if awsErr, ok := err.(awserr.Error); ok {

View File

@ -703,6 +703,7 @@ func testAccAWSRedshiftClusterConfig_loggingDisabled(rInt int) string {
automated_snapshot_retention_period = 0 automated_snapshot_retention_period = 0
allow_version_upgrade = false allow_version_upgrade = false
enable_logging = false enable_logging = false
skip_final_snapshot = true
}`, rInt) }`, rInt)
} }

View File

@ -728,8 +728,8 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
} }
log.Printf("[DEBUG] S3 Bucket: %s, logging: %v", d.Id(), logging) log.Printf("[DEBUG] S3 Bucket: %s, logging: %v", d.Id(), logging)
lcl := make([]map[string]interface{}, 0, 1)
if v := logging.LoggingEnabled; v != nil { if v := logging.LoggingEnabled; v != nil {
lcl := make([]map[string]interface{}, 0, 1)
lc := make(map[string]interface{}) lc := make(map[string]interface{})
if *v.TargetBucket != "" { if *v.TargetBucket != "" {
lc["target_bucket"] = *v.TargetBucket lc["target_bucket"] = *v.TargetBucket
@ -738,9 +738,9 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
lc["target_prefix"] = *v.TargetPrefix lc["target_prefix"] = *v.TargetPrefix
} }
lcl = append(lcl, lc) lcl = append(lcl, lc)
if err := d.Set("logging", lcl); err != nil { }
return err if err := d.Set("logging", lcl); err != nil {
} return err
} }
// Read the lifecycle configuration // Read the lifecycle configuration

View File

@ -215,7 +215,7 @@ func resourceAwsSubnetDelete(d *schema.ResourceData, meta interface{}) error {
wait := resource.StateChangeConf{ wait := resource.StateChangeConf{
Pending: []string{"pending"}, Pending: []string{"pending"},
Target: []string{"destroyed"}, Target: []string{"destroyed"},
Timeout: 5 * time.Minute, Timeout: 10 * time.Minute,
MinTimeout: 1 * time.Second, MinTimeout: 1 * time.Second,
Refresh: func() (interface{}, string, error) { Refresh: func() (interface{}, string, error) {
_, err := conn.DeleteSubnet(req) _, err := conn.DeleteSubnet(req)

View File

@ -11,6 +11,7 @@ import (
"github.com/Azure/azure-sdk-for-go/arm/compute" "github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/Azure/azure-sdk-for-go/arm/containerregistry" "github.com/Azure/azure-sdk-for-go/arm/containerregistry"
"github.com/Azure/azure-sdk-for-go/arm/containerservice" "github.com/Azure/azure-sdk-for-go/arm/containerservice"
"github.com/Azure/azure-sdk-for-go/arm/disk"
"github.com/Azure/azure-sdk-for-go/arm/eventhub" "github.com/Azure/azure-sdk-for-go/arm/eventhub"
"github.com/Azure/azure-sdk-for-go/arm/keyvault" "github.com/Azure/azure-sdk-for-go/arm/keyvault"
"github.com/Azure/azure-sdk-for-go/arm/network" "github.com/Azure/azure-sdk-for-go/arm/network"
@ -47,6 +48,8 @@ type ArmClient struct {
vmImageClient compute.VirtualMachineImagesClient vmImageClient compute.VirtualMachineImagesClient
vmClient compute.VirtualMachinesClient vmClient compute.VirtualMachinesClient
diskClient disk.DisksClient
appGatewayClient network.ApplicationGatewaysClient appGatewayClient network.ApplicationGatewaysClient
ifaceClient network.InterfacesClient ifaceClient network.InterfacesClient
loadBalancerClient network.LoadBalancersClient loadBalancerClient network.LoadBalancersClient
@ -245,6 +248,12 @@ func (c *Config) getArmClient() (*ArmClient, error) {
csc.Sender = autorest.CreateSender(withRequestLogging()) csc.Sender = autorest.CreateSender(withRequestLogging())
client.containerServicesClient = csc client.containerServicesClient = csc
dkc := disk.NewDisksClientWithBaseURI(endpoint, c.SubscriptionID)
setUserAgent(&dkc.Client)
dkc.Authorizer = spt
dkc.Sender = autorest.CreateSender(withRequestLogging())
client.diskClient = dkc
ehc := eventhub.NewEventHubsClientWithBaseURI(endpoint, c.SubscriptionID) ehc := eventhub.NewEventHubsClientWithBaseURI(endpoint, c.SubscriptionID)
setUserAgent(&ehc.Client) setUserAgent(&ehc.Client)
ehc.Authorizer = spt ehc.Authorizer = spt

View File

@ -0,0 +1,30 @@
package azurerm
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccAzureRMManagedDisk_importEmpty(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMManagedDisk_empty, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMManagedDiskDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: "azurerm_managed_disk.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -19,11 +19,39 @@ func TestAccAzureRMVirtualMachine_importBasic(t *testing.T) {
Providers: testAccProviders, Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineDestroy, CheckDestroy: testCheckAzureRMVirtualMachineDestroy,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
resource.TestStep{ {
Config: config, Config: config,
}, },
resource.TestStep{ {
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"delete_data_disks_on_termination",
"delete_os_disk_on_termination",
},
},
},
})
}
func TestAccAzureRMVirtualMachine_importBasic_managedDisk(t *testing.T) {
resourceName := "azurerm_virtual_machine.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMVirtualMachine_basicLinuxMachine_managedDisk_explicit, ri, ri, ri, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName, ResourceName: resourceName,
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,

View File

@ -85,6 +85,8 @@ func Provider() terraform.ResourceProvider {
"azurerm_lb_probe": resourceArmLoadBalancerProbe(), "azurerm_lb_probe": resourceArmLoadBalancerProbe(),
"azurerm_lb_rule": resourceArmLoadBalancerRule(), "azurerm_lb_rule": resourceArmLoadBalancerRule(),
"azurerm_managed_disk": resourceArmManagedDisk(),
"azurerm_key_vault": resourceArmKeyVault(), "azurerm_key_vault": resourceArmKeyVault(),
"azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(),
"azurerm_network_interface": resourceArmNetworkInterface(), "azurerm_network_interface": resourceArmNetworkInterface(),

View File

@ -101,6 +101,9 @@ func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{}
return err return err
} }
name := id.Path["localNetworkGateways"] name := id.Path["localNetworkGateways"]
if name == "" {
return fmt.Errorf("Cannot find 'localNetworkGateways' in '%s', make sure it is specified in the ID parameter", d.Id())
}
resGroup := id.ResourceGroup resGroup := id.ResourceGroup
resp, err := lnetClient.Get(resGroup, name) resp, err := lnetClient.Get(resGroup, name)

View File

@ -0,0 +1,238 @@
package azurerm
import (
"fmt"
"github.com/Azure/azure-sdk-for-go/arm/disk"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"log"
"net/http"
"strings"
)
func resourceArmManagedDisk() *schema.Resource {
return &schema.Resource{
Create: resourceArmManagedDiskCreate,
Read: resourceArmManagedDiskRead,
Update: resourceArmManagedDiskCreate,
Delete: resourceArmManagedDiskDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"location": locationSchema(),
"resource_group_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"storage_account_type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(disk.PremiumLRS),
string(disk.StandardLRS),
}, true),
},
"create_option": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(disk.Import),
string(disk.Empty),
string(disk.Copy),
}, true),
},
"source_uri": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"source_resource_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"os_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(disk.Windows),
string(disk.Linux),
}, true),
},
"disk_size_gb": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validateDiskSizeGB,
},
"tags": tagsSchema(),
},
}
}
func validateDiskSizeGB(v interface{}, k string) (ws []string, errors []error) {
value := v.(int)
if value < 1 || value > 1023 {
errors = append(errors, fmt.Errorf(
"The `disk_size_gb` can only be between 1 and 1023"))
}
return
}
func resourceArmManagedDiskCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient)
diskClient := client.diskClient
log.Printf("[INFO] preparing arguments for Azure ARM Managed Disk creation.")
name := d.Get("name").(string)
location := d.Get("location").(string)
resGroup := d.Get("resource_group_name").(string)
tags := d.Get("tags").(map[string]interface{})
expandedTags := expandTags(tags)
createDisk := disk.Model{
Name: &name,
Location: &location,
Tags: expandedTags,
}
storageAccountType := d.Get("storage_account_type").(string)
osType := d.Get("os_type").(string)
createDisk.Properties = &disk.Properties{
AccountType: disk.StorageAccountTypes(storageAccountType),
OsType: disk.OperatingSystemTypes(osType),
}
if v := d.Get("disk_size_gb"); v != 0 {
diskSize := int32(v.(int))
createDisk.Properties.DiskSizeGB = &diskSize
}
createOption := d.Get("create_option").(string)
creationData := &disk.CreationData{
CreateOption: disk.CreateOption(createOption),
}
if strings.EqualFold(createOption, string(disk.Import)) {
if sourceUri := d.Get("source_uri").(string); sourceUri != "" {
creationData.SourceURI = &sourceUri
} else {
return fmt.Errorf("[ERROR] source_uri must be specified when create_option is `%s`", disk.Import)
}
} else if strings.EqualFold(createOption, string(disk.Copy)) {
if sourceResourceId := d.Get("source_resource_id").(string); sourceResourceId != "" {
creationData.SourceResourceID = &sourceResourceId
} else {
return fmt.Errorf("[ERROR] source_resource_id must be specified when create_option is `%s`", disk.Copy)
}
}
createDisk.CreationData = creationData
_, diskErr := diskClient.CreateOrUpdate(resGroup, name, createDisk, make(chan struct{}))
if diskErr != nil {
return diskErr
}
read, err := diskClient.Get(resGroup, name)
if err != nil {
return err
}
if read.ID == nil {
return fmt.Errorf("[ERROR] Cannot read Managed Disk %s (resource group %s) ID", name, resGroup)
}
d.SetId(*read.ID)
return resourceArmManagedDiskRead(d, meta)
}
func resourceArmManagedDiskRead(d *schema.ResourceData, meta interface{}) error {
diskClient := meta.(*ArmClient).diskClient
id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
name := id.Path["disks"]
resp, err := diskClient.Get(resGroup, name)
if err != nil {
if resp.StatusCode == http.StatusNotFound {
d.SetId("")
return nil
}
return fmt.Errorf("[ERROR] Error making Read request on Azure Managed Disk %s (resource group %s): %s", name, resGroup, err)
}
d.Set("name", resp.Name)
d.Set("resource_group_name", resGroup)
d.Set("location", resp.Location)
if resp.Properties != nil {
flattenAzureRmManagedDiskProperties(d, resp.Properties)
}
if resp.CreationData != nil {
flattenAzureRmManagedDiskCreationData(d, resp.CreationData)
}
flattenAndSetTags(d, resp.Tags)
return nil
}
func resourceArmManagedDiskDelete(d *schema.ResourceData, meta interface{}) error {
diskClient := meta.(*ArmClient).diskClient
id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
name := id.Path["disks"]
if _, err = diskClient.Delete(resGroup, name, make(chan struct{})); err != nil {
return err
}
return nil
}
func flattenAzureRmManagedDiskProperties(d *schema.ResourceData, properties *disk.Properties) {
d.Set("storage_account_type", string(properties.AccountType))
if properties.DiskSizeGB != nil {
d.Set("disk_size_gb", *properties.DiskSizeGB)
}
if properties.OsType != "" {
d.Set("os_type", string(properties.OsType))
}
}
func flattenAzureRmManagedDiskCreationData(d *schema.ResourceData, creationData *disk.CreationData) {
d.Set("create_option", string(creationData.CreateOption))
if creationData.SourceURI != nil {
d.Set("source_uri", *creationData.SourceURI)
}
}

View File

@ -0,0 +1,321 @@
package azurerm
import (
"fmt"
"net/http"
"testing"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/Azure/azure-sdk-for-go/arm/disk"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureRMManagedDisk_empty(t *testing.T) {
var d disk.Model
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMManagedDisk_empty, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMManagedDiskDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMManagedDiskExists("azurerm_managed_disk.test", &d, true),
),
},
},
})
}
func TestAccAzureRMManagedDisk_import(t *testing.T) {
var d disk.Model
var vm compute.VirtualMachine
ri := acctest.RandInt()
vmConfig := fmt.Sprintf(testAccAzureRMVirtualMachine_basicLinuxMachine, ri, ri, ri, ri, ri, ri, ri)
config := fmt.Sprintf(testAccAzureRMManagedDisk_import, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMManagedDiskDestroy,
Steps: []resource.TestStep{
{
//need to create a vm and then delete it so we can use the vhd to test import
Config: vmConfig,
Destroy: false,
ExpectNonEmptyPlan: true,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMVirtualMachineExists("azurerm_virtual_machine.test", &vm),
testDeleteAzureRMVirtualMachine("azurerm_virtual_machine.test"),
),
},
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMManagedDiskExists("azurerm_managed_disk.test", &d, true),
),
},
},
})
}
func TestAccAzureRMManagedDisk_copy(t *testing.T) {
var d disk.Model
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMManagedDisk_copy, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMManagedDiskDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMManagedDiskExists("azurerm_managed_disk.test", &d, true),
),
},
},
})
}
func TestAccAzureRMManagedDisk_update(t *testing.T) {
var d disk.Model
ri := acctest.RandInt()
preConfig := fmt.Sprintf(testAccAzureRMManagedDisk_empty, ri, ri)
postConfig := fmt.Sprintf(testAccAzureRMManagedDisk_empty_updated, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMManagedDiskDestroy,
Steps: []resource.TestStep{
{
Config: preConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMManagedDiskExists("azurerm_managed_disk.test", &d, true),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "tags.%", "2"),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "tags.environment", "acctest"),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "tags.cost-center", "ops"),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "disk_size_gb", "1"),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "storage_account_type", string(disk.StandardLRS)),
),
},
{
Config: postConfig,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMManagedDiskExists("azurerm_managed_disk.test", &d, true),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "tags.%", "1"),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "tags.environment", "acctest"),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "disk_size_gb", "2"),
resource.TestCheckResourceAttr(
"azurerm_managed_disk.test", "storage_account_type", string(disk.PremiumLRS)),
),
},
},
})
}
func testCheckAzureRMManagedDiskExists(name string, d *disk.Model, shouldExist bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
dName := rs.Primary.Attributes["name"]
resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
if !hasResourceGroup {
return fmt.Errorf("Bad: no resource group found in state for disk: %s", dName)
}
conn := testAccProvider.Meta().(*ArmClient).diskClient
resp, err := conn.Get(resourceGroup, dName)
if err != nil {
return fmt.Errorf("Bad: Get on diskClient: %s", err)
}
if resp.StatusCode == http.StatusNotFound && shouldExist {
return fmt.Errorf("Bad: ManagedDisk %q (resource group %q) does not exist", dName, resourceGroup)
}
if resp.StatusCode != http.StatusNotFound && !shouldExist {
return fmt.Errorf("Bad: ManagedDisk %q (resource group %q) still exists", dName, resourceGroup)
}
*d = resp
return nil
}
}
func testCheckAzureRMManagedDiskDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*ArmClient).diskClient
for _, rs := range s.RootModule().Resources {
if rs.Type != "azurerm_managed_disk" {
continue
}
name := rs.Primary.Attributes["name"]
resourceGroup := rs.Primary.Attributes["resource_group_name"]
resp, err := conn.Get(resourceGroup, name)
if err != nil {
return nil
}
if resp.StatusCode != http.StatusNotFound {
return fmt.Errorf("Managed Disk still exists: \n%#v", resp.Properties)
}
}
return nil
}
func testDeleteAzureRMVirtualMachine(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
vmName := rs.Primary.Attributes["name"]
resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
if !hasResourceGroup {
return fmt.Errorf("Bad: no resource group found in state for virtual machine: %s", vmName)
}
conn := testAccProvider.Meta().(*ArmClient).vmClient
_, err := conn.Delete(resourceGroup, vmName, make(chan struct{}))
if err != nil {
return fmt.Errorf("Bad: Delete on vmClient: %s", err)
}
return nil
}
}
var testAccAzureRMManagedDisk_empty = `
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "West US 2"
}
resource "azurerm_managed_disk" "test" {
name = "acctestd-%d"
location = "West US 2"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_type = "Standard_LRS"
create_option = "Empty"
disk_size_gb = "1"
tags {
environment = "acctest"
cost-center = "ops"
}
}`
var testAccAzureRMManagedDisk_import = `
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "West US 2"
}
resource "azurerm_storage_account" "test" {
name = "accsa%d"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "West US 2"
account_type = "Standard_LRS"
tags {
environment = "staging"
}
}
resource "azurerm_storage_container" "test" {
name = "vhds"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
container_access_type = "private"
}
resource "azurerm_managed_disk" "test" {
name = "acctestd-%d"
location = "West US 2"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_type = "Standard_LRS"
create_option = "Import"
source_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd"
disk_size_gb = "45"
tags {
environment = "acctest"
}
}`
var testAccAzureRMManagedDisk_copy = `
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "West US 2"
}
resource "azurerm_managed_disk" "source" {
name = "acctestd1-%d"
location = "West US 2"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_type = "Standard_LRS"
create_option = "Empty"
disk_size_gb = "1"
tags {
environment = "acctest"
cost-center = "ops"
}
}
resource "azurerm_managed_disk" "test" {
name = "acctestd2-%d"
location = "West US 2"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_type = "Standard_LRS"
create_option = "Copy"
source_resource_id = "${azurerm_managed_disk.source.id}"
disk_size_gb = "1"
tags {
environment = "acctest"
cost-center = "ops"
}
}`
var testAccAzureRMManagedDisk_empty_updated = `
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "West US 2"
}
resource "azurerm_managed_disk" "test" {
name = "acctestd-%d"
location = "West US 2"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_type = "Premium_LRS"
create_option = "Empty"
disk_size_gb = "2"
tags {
environment = "acctest"
}
}`

View File

@ -1,7 +1,6 @@
package azurerm package azurerm
import ( import (
"context"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
@ -188,10 +187,8 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
} }
// Create // Create
cancelCtx, cancelFunc := context.WithTimeout(client.StopContext, 1*time.Hour)
_, createErr := storageClient.Create( _, createErr := storageClient.Create(
resourceGroupName, storageAccountName, opts, cancelCtx.Done()) resourceGroupName, storageAccountName, opts, make(chan struct{}))
cancelFunc()
// The only way to get the ID back apparently is to read the resource again // The only way to get the ID back apparently is to read the resource again
read, err := storageClient.GetProperties(resourceGroupName, storageAccountName) read, err := storageClient.GetProperties(resourceGroupName, storageAccountName)

View File

@ -11,6 +11,7 @@ import (
"github.com/Azure/azure-sdk-for-go/arm/compute" "github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
riviera "github.com/jen20/riviera/azure" riviera "github.com/jen20/riviera/azure"
) )
@ -141,10 +142,29 @@ func resourceArmVirtualMachine() *schema.Resource {
"vhd_uri": { "vhd_uri": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Optional: true,
ForceNew: true, ForceNew: true,
}, },
"managed_disk_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ConflictsWith: []string{"storage_os_disk.vhd_uri"},
},
"managed_disk_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ConflictsWith: []string{"storage_os_disk.vhd_uri"},
ValidateFunc: validation.StringInSlice([]string{
string(compute.PremiumLRS),
string(compute.StandardLRS),
}, true),
},
"image_uri": { "image_uri": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
@ -189,7 +209,26 @@ func resourceArmVirtualMachine() *schema.Resource {
"vhd_uri": { "vhd_uri": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Optional: true,
},
"managed_disk_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ConflictsWith: []string{"storage_data_disk.vhd_uri"},
},
"managed_disk_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ConflictsWith: []string{"storage_data_disk.vhd_uri"},
ValidateFunc: validation.StringInSlice([]string{
string(compute.PremiumLRS),
string(compute.StandardLRS),
}, true),
}, },
"create_option": { "create_option": {
@ -204,9 +243,10 @@ func resourceArmVirtualMachine() *schema.Resource {
}, },
"disk_size_gb": { "disk_size_gb": {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
Computed: true, Computed: true,
ValidateFunc: validateDiskSizeGB,
}, },
"lun": { "lun": {
@ -453,15 +493,6 @@ func validateLicenseType(v interface{}, k string) (ws []string, errors []error)
return return
} }
func validateDiskSizeGB(v interface{}, k string) (ws []string, errors []error) {
value := v.(int)
if value < 1 || value > 1023 {
errors = append(errors, fmt.Errorf(
"The `disk_size_gb` can only be between 1 and 1023"))
}
return
}
func resourceArmVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error { func resourceArmVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient) client := meta.(*ArmClient)
vmClient := client.vmClient vmClient := client.vmClient
@ -685,21 +716,29 @@ func resourceArmVirtualMachineDelete(d *schema.ResourceData, meta interface{}) e
// delete OS Disk if opted in // delete OS Disk if opted in
if deleteOsDisk := d.Get("delete_os_disk_on_termination").(bool); deleteOsDisk { if deleteOsDisk := d.Get("delete_os_disk_on_termination").(bool); deleteOsDisk {
log.Printf("[INFO] delete_os_disk_on_termination is enabled, deleting") log.Printf("[INFO] delete_os_disk_on_termination is enabled, deleting disk from %s", name)
osDisk, err := expandAzureRmVirtualMachineOsDisk(d) osDisk, err := expandAzureRmVirtualMachineOsDisk(d)
if err != nil { if err != nil {
return fmt.Errorf("Error expanding OS Disk: %s", err) return fmt.Errorf("Error expanding OS Disk: %s", err)
} }
if err = resourceArmVirtualMachineDeleteVhd(*osDisk.Vhd.URI, meta); err != nil { if osDisk.Vhd != nil {
return fmt.Errorf("Error deleting OS Disk VHD: %s", err) if err = resourceArmVirtualMachineDeleteVhd(*osDisk.Vhd.URI, meta); err != nil {
return fmt.Errorf("Error deleting OS Disk VHD: %s", err)
}
} else if osDisk.ManagedDisk != nil {
if err = resourceArmVirtualMachineDeleteManagedDisk(*osDisk.ManagedDisk.ID, meta); err != nil {
return fmt.Errorf("Error deleting OS Managed Disk: %s", err)
}
} else {
return fmt.Errorf("Unable to locate OS managed disk properties from %s", name)
} }
} }
// delete Data disks if opted in // delete Data disks if opted in
if deleteDataDisks := d.Get("delete_data_disks_on_termination").(bool); deleteDataDisks { if deleteDataDisks := d.Get("delete_data_disks_on_termination").(bool); deleteDataDisks {
log.Printf("[INFO] delete_data_disks_on_termination is enabled, deleting each data disk") log.Printf("[INFO] delete_data_disks_on_termination is enabled, deleting each data disk from %s", name)
disks, err := expandAzureRmVirtualMachineDataDisk(d) disks, err := expandAzureRmVirtualMachineDataDisk(d)
if err != nil { if err != nil {
@ -707,8 +746,16 @@ func resourceArmVirtualMachineDelete(d *schema.ResourceData, meta interface{}) e
} }
for _, disk := range disks { for _, disk := range disks {
if err = resourceArmVirtualMachineDeleteVhd(*disk.Vhd.URI, meta); err != nil { if disk.Vhd != nil {
return fmt.Errorf("Error deleting Data Disk VHD: %s", err) if err = resourceArmVirtualMachineDeleteVhd(*disk.Vhd.URI, meta); err != nil {
return fmt.Errorf("Error deleting Data Disk VHD: %s", err)
}
} else if disk.ManagedDisk != nil {
if err = resourceArmVirtualMachineDeleteManagedDisk(*disk.ManagedDisk.ID, meta); err != nil {
return fmt.Errorf("Error deleting Data Managed Disk: %s", err)
}
} else {
return fmt.Errorf("Unable to locate data managed disk properties from %s", name)
} }
} }
} }
@ -752,6 +799,24 @@ func resourceArmVirtualMachineDeleteVhd(uri string, meta interface{}) error {
return nil return nil
} }
func resourceArmVirtualMachineDeleteManagedDisk(managedDiskID string, meta interface{}) error {
diskClient := meta.(*ArmClient).diskClient
id, err := parseAzureResourceID(managedDiskID)
if err != nil {
return err
}
resGroup := id.ResourceGroup
name := id.Path["disks"]
_, err = diskClient.Delete(resGroup, name, make(chan struct{}))
if err != nil {
return fmt.Errorf("Error deleting Managed Disk (%s %s) %s", name, resGroup, err)
}
return nil
}
func resourceArmVirtualMachinePlanHash(v interface{}) int { func resourceArmVirtualMachinePlanHash(v interface{}) int {
var buf bytes.Buffer var buf bytes.Buffer
m := v.(map[string]interface{}) m := v.(map[string]interface{})
@ -784,8 +849,9 @@ func resourceArmVirtualMachineStorageOsDiskHash(v interface{}) int {
var buf bytes.Buffer var buf bytes.Buffer
m := v.(map[string]interface{}) m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string))) if m["vhd_uri"] != nil {
buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string)))
}
return hashcode.String(buf.String()) return hashcode.String(buf.String())
} }
@ -883,7 +949,13 @@ func flattenAzureRmVirtualMachineDataDisk(disks *[]compute.DataDisk) interface{}
for i, disk := range *disks { for i, disk := range *disks {
l := make(map[string]interface{}) l := make(map[string]interface{})
l["name"] = *disk.Name l["name"] = *disk.Name
l["vhd_uri"] = *disk.Vhd.URI if disk.Vhd != nil {
l["vhd_uri"] = *disk.Vhd.URI
}
if disk.ManagedDisk != nil {
l["managed_disk_type"] = string(disk.ManagedDisk.StorageAccountType)
l["managed_disk_id"] = *disk.ManagedDisk.ID
}
l["create_option"] = disk.CreateOption l["create_option"] = disk.CreateOption
l["caching"] = string(disk.Caching) l["caching"] = string(disk.Caching)
if disk.DiskSizeGB != nil { if disk.DiskSizeGB != nil {
@ -982,7 +1054,13 @@ func flattenAzureRmVirtualMachineOsProfileLinuxConfiguration(config *compute.Lin
func flattenAzureRmVirtualMachineOsDisk(disk *compute.OSDisk) []interface{} { func flattenAzureRmVirtualMachineOsDisk(disk *compute.OSDisk) []interface{} {
result := make(map[string]interface{}) result := make(map[string]interface{})
result["name"] = *disk.Name result["name"] = *disk.Name
result["vhd_uri"] = *disk.Vhd.URI if disk.Vhd != nil {
result["vhd_uri"] = *disk.Vhd.URI
}
if disk.ManagedDisk != nil {
result["managed_disk_type"] = string(disk.ManagedDisk.StorageAccountType)
result["managed_disk_id"] = *disk.ManagedDisk.ID
}
result["create_option"] = disk.CreateOption result["create_option"] = disk.CreateOption
result["caching"] = disk.Caching result["caching"] = disk.Caching
if disk.DiskSizeGB != nil { if disk.DiskSizeGB != nil {
@ -1157,22 +1235,22 @@ func expandAzureRmVirtualMachineOsProfileWindowsConfig(d *schema.ResourceData) (
if v := osProfileConfig["winrm"]; v != nil { if v := osProfileConfig["winrm"]; v != nil {
winRm := v.([]interface{}) winRm := v.([]interface{})
if len(winRm) > 0 { if len(winRm) > 0 {
winRmListners := make([]compute.WinRMListener, 0, len(winRm)) winRmListeners := make([]compute.WinRMListener, 0, len(winRm))
for _, winRmConfig := range winRm { for _, winRmConfig := range winRm {
config := winRmConfig.(map[string]interface{}) config := winRmConfig.(map[string]interface{})
protocol := config["protocol"].(string) protocol := config["protocol"].(string)
winRmListner := compute.WinRMListener{ winRmListener := compute.WinRMListener{
Protocol: compute.ProtocolTypes(protocol), Protocol: compute.ProtocolTypes(protocol),
} }
if v := config["certificate_url"].(string); v != "" { if v := config["certificate_url"].(string); v != "" {
winRmListner.CertificateURL = &v winRmListener.CertificateURL = &v
} }
winRmListners = append(winRmListners, winRmListner) winRmListeners = append(winRmListeners, winRmListener)
} }
config.WinRM = &compute.WinRMConfiguration{ config.WinRM = &compute.WinRMConfiguration{
Listeners: &winRmListners, Listeners: &winRmListeners,
} }
} }
} }
@ -1209,19 +1287,48 @@ func expandAzureRmVirtualMachineDataDisk(d *schema.ResourceData) ([]compute.Data
config := disk_config.(map[string]interface{}) config := disk_config.(map[string]interface{})
name := config["name"].(string) name := config["name"].(string)
vhd := config["vhd_uri"].(string)
createOption := config["create_option"].(string) createOption := config["create_option"].(string)
vhdURI := config["vhd_uri"].(string)
managedDiskType := config["managed_disk_type"].(string)
managedDiskID := config["managed_disk_id"].(string)
lun := int32(config["lun"].(int)) lun := int32(config["lun"].(int))
data_disk := compute.DataDisk{ data_disk := compute.DataDisk{
Name: &name, Name: &name,
Vhd: &compute.VirtualHardDisk{
URI: &vhd,
},
Lun: &lun, Lun: &lun,
CreateOption: compute.DiskCreateOptionTypes(createOption), CreateOption: compute.DiskCreateOptionTypes(createOption),
} }
if vhdURI != "" {
data_disk.Vhd = &compute.VirtualHardDisk{
URI: &vhdURI,
}
}
managedDisk := &compute.ManagedDiskParameters{}
if managedDiskType != "" {
managedDisk.StorageAccountType = compute.StorageAccountTypes(managedDiskType)
data_disk.ManagedDisk = managedDisk
}
if managedDiskID != "" {
managedDisk.ID = &managedDiskID
data_disk.ManagedDisk = managedDisk
}
//BEGIN: code to be removed after GH-13016 is merged
if vhdURI != "" && managedDiskID != "" {
return nil, fmt.Errorf("[ERROR] Conflict between `vhd_uri` and `managed_disk_id` (only one or the other can be used)")
}
if vhdURI != "" && managedDiskType != "" {
return nil, fmt.Errorf("[ERROR] Conflict between `vhd_uri` and `managed_disk_type` (only one or the other can be used)")
}
//END: code to be removed after GH-13016 is merged
if managedDiskID == "" && strings.EqualFold(string(data_disk.CreateOption), string(compute.Attach)) {
return nil, fmt.Errorf("[ERROR] Must specify which disk to attach")
}
if v := config["caching"].(string); v != "" { if v := config["caching"].(string); v != "" {
data_disk.Caching = compute.CachingTypes(v) data_disk.Caching = compute.CachingTypes(v)
} }
@ -1303,28 +1410,57 @@ func expandAzureRmVirtualMachineNetworkProfile(d *schema.ResourceData) compute.N
func expandAzureRmVirtualMachineOsDisk(d *schema.ResourceData) (*compute.OSDisk, error) { func expandAzureRmVirtualMachineOsDisk(d *schema.ResourceData) (*compute.OSDisk, error) {
disks := d.Get("storage_os_disk").(*schema.Set).List() disks := d.Get("storage_os_disk").(*schema.Set).List()
disk := disks[0].(map[string]interface{}) config := disks[0].(map[string]interface{})
name := disk["name"].(string) name := config["name"].(string)
vhdURI := disk["vhd_uri"].(string) imageURI := config["image_uri"].(string)
imageURI := disk["image_uri"].(string) createOption := config["create_option"].(string)
createOption := disk["create_option"].(string) vhdURI := config["vhd_uri"].(string)
managedDiskType := config["managed_disk_type"].(string)
managedDiskID := config["managed_disk_id"].(string)
osDisk := &compute.OSDisk{ osDisk := &compute.OSDisk{
Name: &name, Name: &name,
Vhd: &compute.VirtualHardDisk{
URI: &vhdURI,
},
CreateOption: compute.DiskCreateOptionTypes(createOption), CreateOption: compute.DiskCreateOptionTypes(createOption),
} }
if v := disk["image_uri"].(string); v != "" { if vhdURI != "" {
osDisk.Vhd = &compute.VirtualHardDisk{
URI: &vhdURI,
}
}
managedDisk := &compute.ManagedDiskParameters{}
if managedDiskType != "" {
managedDisk.StorageAccountType = compute.StorageAccountTypes(managedDiskType)
osDisk.ManagedDisk = managedDisk
}
if managedDiskID != "" {
managedDisk.ID = &managedDiskID
osDisk.ManagedDisk = managedDisk
}
//BEGIN: code to be removed after GH-13016 is merged
if vhdURI != "" && managedDiskID != "" {
return nil, fmt.Errorf("[ERROR] Conflict between `vhd_uri` and `managed_disk_id` (only one or the other can be used)")
}
if vhdURI != "" && managedDiskType != "" {
return nil, fmt.Errorf("[ERROR] Conflict between `vhd_uri` and `managed_disk_type` (only one or the other can be used)")
}
//END: code to be removed after GH-13016 is merged
if managedDiskID == "" && strings.EqualFold(string(osDisk.CreateOption), string(compute.Attach)) {
return nil, fmt.Errorf("[ERROR] Must specify which disk to attach")
}
if v := config["image_uri"].(string); v != "" {
osDisk.Image = &compute.VirtualHardDisk{ osDisk.Image = &compute.VirtualHardDisk{
URI: &imageURI, URI: &imageURI,
} }
} }
if v := disk["os_type"].(string); v != "" { if v := config["os_type"].(string); v != "" {
if v == "linux" { if v == "linux" {
osDisk.OsType = compute.Linux osDisk.OsType = compute.Linux
} else if v == "windows" { } else if v == "windows" {
@ -1334,11 +1470,11 @@ func expandAzureRmVirtualMachineOsDisk(d *schema.ResourceData) (*compute.OSDisk,
} }
} }
if v := disk["caching"].(string); v != "" { if v := config["caching"].(string); v != "" {
osDisk.Caching = compute.CachingTypes(v) osDisk.Caching = compute.CachingTypes(v)
} }
if v := disk["disk_size_gb"].(int); v != 0 { if v := config["disk_size_gb"].(int); v != 0 {
diskSize := int32(v) diskSize := int32(v)
osDisk.DiskSizeGB = &diskSize osDisk.DiskSizeGB = &diskSize
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,64 +2,107 @@ package bitbucket
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http" "net/http"
) )
// Error represents a error from the bitbucket api.
type Error struct {
APIError struct {
Message string `json:"message,omitempty"`
} `json:"error,omitempty"`
Type string `json:"type,omitempty"`
StatusCode int
Endpoint string
}
func (e Error) Error() string {
return fmt.Sprintf("API Error: %d %s %s", e.StatusCode, e.Endpoint, e.APIError.Message)
}
const (
// BitbucketEndpoint is the fqdn used to talk to bitbucket
BitbucketEndpoint string = "https://api.bitbucket.org/"
)
type BitbucketClient struct { type BitbucketClient struct {
Username string Username string
Password string Password string
HTTPClient *http.Client
}
func (c *BitbucketClient) Do(method, endpoint string, payload *bytes.Buffer) (*http.Response, error) {
absoluteendpoint := BitbucketEndpoint + endpoint
log.Printf("[DEBUG] Sending request to %s %s", method, absoluteendpoint)
var bodyreader io.Reader
if payload != nil {
log.Printf("[DEBUG] With payload %s", payload.String())
bodyreader = payload
}
req, err := http.NewRequest(method, absoluteendpoint, bodyreader)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
if payload != nil {
// Can cause bad request when putting default reviews if set.
req.Header.Add("Content-Type", "application/json")
}
req.Close = true
resp, err := c.HTTPClient.Do(req)
log.Printf("[DEBUG] Resp: %v Err: %v", resp, err)
if resp.StatusCode >= 400 || resp.StatusCode < 200 {
apiError := Error{
StatusCode: resp.StatusCode,
Endpoint: endpoint,
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
log.Printf("[DEBUG] Resp Body: %s", string(body))
err = json.Unmarshal(body, &apiError)
if err != nil {
apiError.APIError.Message = string(body)
}
return resp, error(apiError)
}
return resp, err
} }
func (c *BitbucketClient) Get(endpoint string) (*http.Response, error) { func (c *BitbucketClient) Get(endpoint string) (*http.Response, error) {
client := &http.Client{} return c.Do("GET", endpoint, nil)
req, err := http.NewRequest("GET", "https://api.bitbucket.org/"+endpoint, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
return client.Do(req)
} }
func (c *BitbucketClient) Post(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) { func (c *BitbucketClient) Post(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) {
client := &http.Client{} return c.Do("POST", endpoint, jsonpayload)
req, err := http.NewRequest("POST", "https://api.bitbucket.org/"+endpoint, jsonpayload)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
req.Header.Add("content-type", "application/json")
return client.Do(req)
} }
func (c *BitbucketClient) Put(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) { func (c *BitbucketClient) Put(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) {
client := &http.Client{} return c.Do("PUT", endpoint, jsonpayload)
req, err := http.NewRequest("PUT", "https://api.bitbucket.org/"+endpoint, jsonpayload)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
req.Header.Add("content-type", "application/json")
return client.Do(req)
} }
func (c *BitbucketClient) PutOnly(endpoint string) (*http.Response, error) { func (c *BitbucketClient) PutOnly(endpoint string) (*http.Response, error) {
client := &http.Client{} return c.Do("PUT", endpoint, nil)
req, err := http.NewRequest("PUT", "https://api.bitbucket.org/"+endpoint, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
return client.Do(req)
} }
func (c *BitbucketClient) Delete(endpoint string) (*http.Response, error) { func (c *BitbucketClient) Delete(endpoint string) (*http.Response, error) {
client := &http.Client{} return c.Do("DELETE", endpoint, nil)
req, err := http.NewRequest("DELETE", "https://api.bitbucket.org/"+endpoint, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
return client.Do(req)
} }

View File

@ -1,6 +1,8 @@
package bitbucket package bitbucket
import ( import (
"net/http"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -30,8 +32,9 @@ func Provider() terraform.ResourceProvider {
func providerConfigure(d *schema.ResourceData) (interface{}, error) { func providerConfigure(d *schema.ResourceData) (interface{}, error) {
client := &BitbucketClient{ client := &BitbucketClient{
Username: d.Get("username").(string), Username: d.Get("username").(string),
Password: d.Get("password").(string), Password: d.Get("password").(string),
HTTPClient: &http.Client{},
} }
return client, nil return client, nil

View File

@ -3,6 +3,7 @@ package bitbucket
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
) )
@ -49,7 +50,7 @@ func resourceDefaultReviewersCreate(d *schema.ResourceData, m interface{}) error
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
for _, user := range d.Get("reviewers").(*schema.Set).List() { for _, user := range d.Get("reviewers").(*schema.Set).List() {
reviewer_resp, err := client.PutOnly(fmt.Sprintf("2.0/repositories/%s/%s/default-reviewers/%s", reviewerResp, err := client.PutOnly(fmt.Sprintf("2.0/repositories/%s/%s/default-reviewers/%s",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("repository").(string), d.Get("repository").(string),
user, user,
@ -59,11 +60,11 @@ func resourceDefaultReviewersCreate(d *schema.ResourceData, m interface{}) error
return err return err
} }
if reviewer_resp.StatusCode != 200 { if reviewerResp.StatusCode != 200 {
return fmt.Errorf("Failed to create reviewer %s got code %d", user.(string), reviewer_resp.StatusCode) return fmt.Errorf("Failed to create reviewer %s got code %d", user.(string), reviewerResp.StatusCode)
} }
defer reviewer_resp.Body.Close() defer reviewerResp.Body.Close()
} }
d.SetId(fmt.Sprintf("%s/%s/reviewers", d.Get("owner").(string), d.Get("repository").(string))) d.SetId(fmt.Sprintf("%s/%s/reviewers", d.Get("owner").(string), d.Get("repository").(string)))
@ -72,26 +73,26 @@ func resourceDefaultReviewersCreate(d *schema.ResourceData, m interface{}) error
func resourceDefaultReviewersRead(d *schema.ResourceData, m interface{}) error { func resourceDefaultReviewersRead(d *schema.ResourceData, m interface{}) error {
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
reviewers_response, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/default-reviewers", reviewersResponse, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/default-reviewers",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("repository").(string), d.Get("repository").(string),
)) ))
var reviewers PaginatedReviewers var reviewers PaginatedReviewers
decoder := json.NewDecoder(reviewers_response.Body) decoder := json.NewDecoder(reviewersResponse.Body)
err = decoder.Decode(&reviewers) err = decoder.Decode(&reviewers)
if err != nil { if err != nil {
return err return err
} }
terraform_reviewers := make([]string, 0, len(reviewers.Values)) terraformReviewers := make([]string, 0, len(reviewers.Values))
for _, reviewer := range reviewers.Values { for _, reviewer := range reviewers.Values {
terraform_reviewers = append(terraform_reviewers, reviewer.Username) terraformReviewers = append(terraformReviewers, reviewer.Username)
} }
d.Set("reviewers", terraform_reviewers) d.Set("reviewers", terraformReviewers)
return nil return nil
} }

View File

@ -4,6 +4,10 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"log"
"net/url"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
) )
@ -81,86 +85,89 @@ func resourceHookCreate(d *schema.ResourceData, m interface{}) error {
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
hook := createHook(d) hook := createHook(d)
var jsonbuffer []byte payload, err := json.Marshal(hook)
jsonpayload := bytes.NewBuffer(jsonbuffer)
enc := json.NewEncoder(jsonpayload)
enc.Encode(hook)
hook_req, err := client.Post(fmt.Sprintf("2.0/repositories/%s/%s/hooks",
d.Get("owner").(string),
d.Get("repository").(string),
), jsonpayload)
decoder := json.NewDecoder(hook_req.Body)
err = decoder.Decode(&hook)
if err != nil { if err != nil {
return err return err
} }
hook_req, err := client.Post(fmt.Sprintf("2.0/repositories/%s/%s/hooks",
d.Get("owner").(string),
d.Get("repository").(string),
), bytes.NewBuffer(payload))
if err != nil {
return err
}
body, readerr := ioutil.ReadAll(hook_req.Body)
if readerr != nil {
return readerr
}
decodeerr := json.Unmarshal(body, &hook)
if decodeerr != nil {
return decodeerr
}
d.SetId(hook.Uuid) d.SetId(hook.Uuid)
d.Set("uuid", hook.Uuid)
return resourceHookRead(d, m) return resourceHookRead(d, m)
} }
func resourceHookRead(d *schema.ResourceData, m interface{}) error { func resourceHookRead(d *schema.ResourceData, m interface{}) error {
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
hook_req, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s",
hook_req, _ := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("repository").(string), d.Get("repository").(string),
d.Get("uuid").(string), url.PathEscape(d.Id()),
)) ))
if err != nil { log.Printf("ID: %s", url.PathEscape(d.Id()))
return err
if hook_req.StatusCode == 200 {
var hook Hook
body, readerr := ioutil.ReadAll(hook_req.Body)
if readerr != nil {
return readerr
}
decodeerr := json.Unmarshal(body, &hook)
if decodeerr != nil {
return decodeerr
}
d.Set("uuid", hook.Uuid)
d.Set("description", hook.Description)
d.Set("active", hook.Active)
d.Set("url", hook.Url)
eventsList := make([]string, 0, len(hook.Events))
for _, event := range hook.Events {
eventsList = append(eventsList, event)
}
d.Set("events", eventsList)
} }
var hook Hook
decoder := json.NewDecoder(hook_req.Body)
err = decoder.Decode(&hook)
if err != nil {
return err
}
d.Set("uuid", hook.Uuid)
d.Set("description", hook.Description)
d.Set("active", hook.Active)
d.Set("url", hook.Url)
eventsList := make([]string, 0, len(hook.Events))
for _, event := range hook.Events {
eventsList = append(eventsList, event)
}
d.Set("events", eventsList)
return nil return nil
} }
func resourceHookUpdate(d *schema.ResourceData, m interface{}) error { func resourceHookUpdate(d *schema.ResourceData, m interface{}) error {
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
hook := createHook(d) hook := createHook(d)
payload, err := json.Marshal(hook)
var jsonbuffer []byte
jsonpayload := bytes.NewBuffer(jsonbuffer)
enc := json.NewEncoder(jsonpayload)
enc.Encode(hook)
hook_req, err := client.Put(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s",
d.Get("owner").(string),
d.Get("repository").(string),
d.Get("uuid").(string),
), jsonpayload)
if err != nil { if err != nil {
return err return err
} }
decoder := json.NewDecoder(hook_req.Body) _, err = client.Put(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s",
err = decoder.Decode(&hook) d.Get("owner").(string),
d.Get("repository").(string),
url.PathEscape(d.Id()),
), bytes.NewBuffer(payload))
if err != nil { if err != nil {
return err return err
} }
@ -174,7 +181,7 @@ func resourceHookExists(d *schema.ResourceData, m interface{}) (bool, error) {
hook_req, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s", hook_req, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("repository").(string), d.Get("repository").(string),
d.Get("uuid").(string), url.PathEscape(d.Id()),
)) ))
if err != nil { if err != nil {
@ -182,15 +189,14 @@ func resourceHookExists(d *schema.ResourceData, m interface{}) (bool, error) {
} }
if hook_req.StatusCode != 200 { if hook_req.StatusCode != 200 {
d.SetId("") return false, err
return false, nil
} }
return true, nil return true, nil
} else {
return false, nil
} }
return false, nil
} }
func resourceHookDelete(d *schema.ResourceData, m interface{}) error { func resourceHookDelete(d *schema.ResourceData, m interface{}) error {
@ -198,11 +204,9 @@ func resourceHookDelete(d *schema.ResourceData, m interface{}) error {
_, err := client.Delete(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s", _, err := client.Delete(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("repository").(string), d.Get("repository").(string),
d.Get("uuid").(string), url.PathEscape(d.Id()),
)) ))
if err != nil { return err
return err
}
return nil
} }

View File

@ -2,6 +2,7 @@ package bitbucket
import ( import (
"fmt" "fmt"
"net/url"
"os" "os"
"testing" "testing"
@ -16,7 +17,7 @@ func TestAccBitbucketHook_basic(t *testing.T) {
testAccBitbucketHookConfig := fmt.Sprintf(` testAccBitbucketHookConfig := fmt.Sprintf(`
resource "bitbucket_repository" "test_repo" { resource "bitbucket_repository" "test_repo" {
owner = "%s" owner = "%s"
name = "test-repo" name = "test-repo-for-webhook-test"
} }
resource "bitbucket_hook" "test_repo_hook" { resource "bitbucket_hook" "test_repo_hook" {
owner = "%s" owner = "%s"
@ -51,10 +52,10 @@ func testAccCheckBitbucketHookDestroy(s *terraform.State) error {
return fmt.Errorf("Not found %s", "bitbucket_hook.test_repo_hook") return fmt.Errorf("Not found %s", "bitbucket_hook.test_repo_hook")
} }
response, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s", rs.Primary.Attributes["owner"], rs.Primary.Attributes["repository"], rs.Primary.Attributes["uuid"])) response, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s", rs.Primary.Attributes["owner"], rs.Primary.Attributes["repository"], url.PathEscape(rs.Primary.Attributes["uuid"])))
if err != nil { if err == nil {
return err return fmt.Errorf("The resource was found should have errored")
} }
if response.StatusCode != 404 { if response.StatusCode != 404 {

View File

@ -4,8 +4,9 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"log"
) )
type CloneUrl struct { type CloneUrl struct {
@ -131,7 +132,7 @@ func resourceRepositoryUpdate(d *schema.ResourceData, m interface{}) error {
enc := json.NewEncoder(jsonpayload) enc := json.NewEncoder(jsonpayload)
enc.Encode(repository) enc.Encode(repository)
repository_response, err := client.Put(fmt.Sprintf("2.0/repositories/%s/%s", _, err := client.Put(fmt.Sprintf("2.0/repositories/%s/%s",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("name").(string), d.Get("name").(string),
), jsonpayload) ), jsonpayload)
@ -140,16 +141,6 @@ func resourceRepositoryUpdate(d *schema.ResourceData, m interface{}) error {
return err return err
} }
if repository_response.StatusCode == 200 {
decoder := json.NewDecoder(repository_response.Body)
err = decoder.Decode(&repository)
if err != nil {
return err
}
} else {
return fmt.Errorf("Failed to put: %d", repository_response.StatusCode)
}
return resourceRepositoryRead(d, m) return resourceRepositoryRead(d, m)
} }
@ -157,29 +148,19 @@ func resourceRepositoryCreate(d *schema.ResourceData, m interface{}) error {
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
repo := newRepositoryFromResource(d) repo := newRepositoryFromResource(d)
var jsonbuffer []byte bytedata, err := json.Marshal(repo)
jsonpayload := bytes.NewBuffer(jsonbuffer)
enc := json.NewEncoder(jsonpayload)
enc.Encode(repo)
log.Printf("Sending %s \n", jsonpayload)
repo_req, err := client.Post(fmt.Sprintf("2.0/repositories/%s/%s",
d.Get("owner").(string),
d.Get("name").(string),
), jsonpayload)
decoder := json.NewDecoder(repo_req.Body)
err = decoder.Decode(&repo)
if err != nil { if err != nil {
return err return err
} }
log.Printf("Received %s \n", repo_req.Body) _, err = client.Post(fmt.Sprintf("2.0/repositories/%s/%s",
d.Get("owner").(string),
d.Get("name").(string),
), bytes.NewBuffer(bytedata))
if repo_req.StatusCode != 200 { if err != nil {
return fmt.Errorf("Failed to create repository got status code %d", repo_req.StatusCode) return err
} }
d.SetId(string(fmt.Sprintf("%s/%s", d.Get("owner").(string), d.Get("name").(string)))) d.SetId(string(fmt.Sprintf("%s/%s", d.Get("owner").(string), d.Get("name").(string))))
@ -189,39 +170,42 @@ func resourceRepositoryCreate(d *schema.ResourceData, m interface{}) error {
func resourceRepositoryRead(d *schema.ResourceData, m interface{}) error { func resourceRepositoryRead(d *schema.ResourceData, m interface{}) error {
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
repo_req, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s", repo_req, _ := client.Get(fmt.Sprintf("2.0/repositories/%s/%s",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("name").(string), d.Get("name").(string),
)) ))
if err != nil { if repo_req.StatusCode == 200 {
return err
}
var repo Repository var repo Repository
decoder := json.NewDecoder(repo_req.Body) body, readerr := ioutil.ReadAll(repo_req.Body)
err = decoder.Decode(&repo) if readerr != nil {
if err != nil { return readerr
return err }
}
d.Set("scm", repo.SCM) decodeerr := json.Unmarshal(body, &repo)
d.Set("is_private", repo.IsPrivate) if decodeerr != nil {
d.Set("has_wiki", repo.HasWiki) return decodeerr
d.Set("has_issues", repo.HasIssues) }
d.Set("name", repo.Name)
d.Set("language", repo.Language)
d.Set("fork_policy", repo.ForkPolicy)
d.Set("website", repo.Website)
d.Set("description", repo.Description)
d.Set("project_key", repo.Project.Key)
for _, clone_url := range repo.Links.Clone { d.Set("scm", repo.SCM)
if clone_url.Name == "https" { d.Set("is_private", repo.IsPrivate)
d.Set("clone_https", clone_url.Href) d.Set("has_wiki", repo.HasWiki)
} else { d.Set("has_issues", repo.HasIssues)
d.Set("clone_ssh", clone_url.Href) d.Set("name", repo.Name)
d.Set("language", repo.Language)
d.Set("fork_policy", repo.ForkPolicy)
d.Set("website", repo.Website)
d.Set("description", repo.Description)
d.Set("project_key", repo.Project.Key)
for _, clone_url := range repo.Links.Clone {
if clone_url.Name == "https" {
d.Set("clone_https", clone_url.Href)
} else {
d.Set("clone_ssh", clone_url.Href)
}
} }
} }
@ -230,18 +214,10 @@ func resourceRepositoryRead(d *schema.ResourceData, m interface{}) error {
func resourceRepositoryDelete(d *schema.ResourceData, m interface{}) error { func resourceRepositoryDelete(d *schema.ResourceData, m interface{}) error {
client := m.(*BitbucketClient) client := m.(*BitbucketClient)
delete_response, err := client.Delete(fmt.Sprintf("2.0/repositories/%s/%s", _, err := client.Delete(fmt.Sprintf("2.0/repositories/%s/%s",
d.Get("owner").(string), d.Get("owner").(string),
d.Get("name").(string), d.Get("name").(string),
)) ))
if err != nil { return err
return err
}
if delete_response.StatusCode != 204 {
return fmt.Errorf("Failed to delete the repository got status code %d", delete_response.StatusCode)
}
return nil
} }

View File

@ -16,9 +16,9 @@ func TestAccBitbucketRepository_basic(t *testing.T) {
testAccBitbucketRepositoryConfig := fmt.Sprintf(` testAccBitbucketRepositoryConfig := fmt.Sprintf(`
resource "bitbucket_repository" "test_repo" { resource "bitbucket_repository" "test_repo" {
owner = "%s" owner = "%s"
name = "%s" name = "test-repo-for-repository-test"
} }
`, testUser, testRepo) `, testUser)
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -42,11 +42,7 @@ func testAccCheckBitbucketRepositoryDestroy(s *terraform.State) error {
return fmt.Errorf("Not found %s", "bitbucket_repository.test_repo") return fmt.Errorf("Not found %s", "bitbucket_repository.test_repo")
} }
response, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s", rs.Primary.Attributes["owner"], rs.Primary.Attributes["name"])) response, _ := client.Get(fmt.Sprintf("2.0/repositories/%s/%s", rs.Primary.Attributes["owner"], rs.Primary.Attributes["name"]))
if err != nil {
return err
}
if response.StatusCode != 404 { if response.StatusCode != 404 {
return fmt.Errorf("Repository still exists") return fmt.Errorf("Repository still exists")

View File

@ -58,10 +58,11 @@ func resourceCloudStackLoadBalancerRule() *schema.Resource {
}, },
"member_ids": &schema.Schema{ "member_ids": &schema.Schema{
Type: schema.TypeList, Type: schema.TypeSet,
Required: true, Required: true,
ForceNew: true, ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString}, Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
}, },
"project": &schema.Schema{ "project": &schema.Schema{
@ -124,7 +125,7 @@ func resourceCloudStackLoadBalancerRuleCreate(d *schema.ResourceData, meta inter
ap := cs.LoadBalancer.NewAssignToLoadBalancerRuleParams(r.Id) ap := cs.LoadBalancer.NewAssignToLoadBalancerRuleParams(r.Id)
var mbs []string var mbs []string
for _, id := range d.Get("member_ids").([]interface{}) { for _, id := range d.Get("member_ids").(*schema.Set).List() {
mbs = append(mbs, id.(string)) mbs = append(mbs, id.(string))
} }
@ -171,6 +172,18 @@ func resourceCloudStackLoadBalancerRuleRead(d *schema.ResourceData, meta interfa
setValueOrID(d, "project", lb.Project, lb.Projectid) setValueOrID(d, "project", lb.Project, lb.Projectid)
p := cs.LoadBalancer.NewListLoadBalancerRuleInstancesParams(d.Id())
l, err := cs.LoadBalancer.ListLoadBalancerRuleInstances(p)
if err != nil {
return err
}
var mbs []string
for _, i := range l.LoadBalancerRuleInstances {
mbs = append(mbs, i.Id)
}
d.Set("member_ids", mbs)
return nil return nil
} }
@ -215,6 +228,41 @@ func resourceCloudStackLoadBalancerRuleUpdate(d *schema.ResourceData, meta inter
"Error updating load balancer rule %s", name) "Error updating load balancer rule %s", name)
} }
} }
if d.HasChange("member_ids") {
o, n := d.GetChange("member_ids")
ombs, nmbs := o.(*schema.Set), n.(*schema.Set)
setToStringList := func(s *schema.Set) []string {
l := make([]string, s.Len())
for i, v := range s.List() {
l[i] = v.(string)
}
return l
}
membersToAdd := setToStringList(nmbs.Difference(ombs))
membersToRemove := setToStringList(ombs.Difference(nmbs))
log.Printf("[DEBUG] Members to add: %v, remove: %v", membersToAdd, membersToRemove)
if len(membersToAdd) > 0 {
p := cs.LoadBalancer.NewAssignToLoadBalancerRuleParams(d.Id())
p.SetVirtualmachineids(membersToAdd)
if _, err := cs.LoadBalancer.AssignToLoadBalancerRule(p); err != nil {
return err
}
}
if len(membersToRemove) > 0 {
p := cs.LoadBalancer.NewRemoveFromLoadBalancerRuleParams(d.Id())
p.SetVirtualmachineids(membersToRemove)
if _, err := cs.LoadBalancer.RemoveFromLoadBalancerRule(p); err != nil {
return err
}
}
}
return resourceCloudStackLoadBalancerRuleRead(d, meta) return resourceCloudStackLoadBalancerRuleRead(d, meta)
} }

View File

@ -121,7 +121,7 @@ func resourceComputeVolumeAttachV2Read(d *schema.ResourceData, meta interface{})
attachment, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract() attachment, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
if err != nil { if err != nil {
return err return CheckDeleted(d, err, "compute_volume_attach")
} }
log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment) log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment)

View File

@ -237,7 +237,6 @@ Options:
-force-copy Suppress prompts about copying state data. This is -force-copy Suppress prompts about copying state data. This is
equivalent to providing a "yes" to all confirmation equivalent to providing a "yes" to all confirmation
prompts. prompts.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)
} }

View File

@ -264,6 +264,10 @@ func (m *Meta) flagSet(n string) *flag.FlagSet {
// Set the default Usage to empty // Set the default Usage to empty
f.Usage = func() {} f.Usage = func() {}
// command that bypass locking will supply their own flag on this var, but
// set the initial meta value to true as a failsafe.
m.stateLock = true
return f return f
} }

View File

@ -2,6 +2,7 @@ package command
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -53,7 +54,7 @@ func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
// Setup defaults // Setup defaults
opts.oneEnv = backend.DefaultStateName opts.oneEnv = backend.DefaultStateName
opts.twoEnv = backend.DefaultStateName opts.twoEnv = backend.DefaultStateName
opts.force = false opts.force = m.forceInitCopy
// Determine migration behavior based on whether the source/destionation // Determine migration behavior based on whether the source/destionation
// supports multi-state. // supports multi-state.
@ -163,7 +164,7 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error { func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
currentEnv := m.Env() currentEnv := m.Env()
migrate := m.forceInitCopy migrate := opts.force
if !migrate { if !migrate {
var err error var err error
// Ask the user if they want to migrate their existing remote state // Ask the user if they want to migrate their existing remote state
@ -218,6 +219,19 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
errMigrateSingleLoadDefault), opts.TwoType, err) errMigrateSingleLoadDefault), opts.TwoType, err)
} }
// Check if we need migration at all.
// This is before taking a lock, because they may also correspond to the same lock.
one := stateOne.State()
two := stateTwo.State()
// no reason to migrate if the state is already there
if one.Equal(two) {
// Equal isn't identical; it doesn't check lineage.
if one != nil && two != nil && one.Lineage == two.Lineage {
return nil
}
}
if m.stateLock { if m.stateLock {
lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout) lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
defer cancel() defer cancel()
@ -241,10 +255,21 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
return fmt.Errorf("Error locking destination state: %s", err) return fmt.Errorf("Error locking destination state: %s", err)
} }
defer clistate.Unlock(stateTwo, lockIDTwo, m.Ui, m.Colorize()) defer clistate.Unlock(stateTwo, lockIDTwo, m.Ui, m.Colorize())
}
one := stateOne.State() // We now own a lock, so double check that we have the version
two := stateTwo.State() // corresponding to the lock.
if err := stateOne.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err)
}
if err := stateTwo.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err)
}
one = stateOne.State()
two = stateTwo.State()
}
// Clear the legacy remote state in both cases. If we're at the migration // Clear the legacy remote state in both cases. If we're at the migration
// step then this won't be used anymore. // step then this won't be used anymore.
@ -281,6 +306,11 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
} }
if !opts.force { if !opts.force {
// Abort if we can't ask for input.
if !m.input {
return errors.New("error asking for state migration action: inptut disabled")
}
// Confirm with the user whether we want to copy state over // Confirm with the user whether we want to copy state over
confirm, err := confirmFunc(stateOne, stateTwo, opts) confirm, err := confirmFunc(stateOne, stateTwo, opts)
if err != nil { if err != nil {
@ -306,10 +336,6 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
} }
func (m *Meta) backendMigrateEmptyConfirm(one, two state.State, opts *backendMigrateOpts) (bool, error) { func (m *Meta) backendMigrateEmptyConfirm(one, two state.State, opts *backendMigrateOpts) (bool, error) {
if m.forceInitCopy {
return true, nil
}
inputOpts := &terraform.InputOpts{ inputOpts := &terraform.InputOpts{
Id: "backend-migrate-copy-to-empty", Id: "backend-migrate-copy-to-empty",
Query: fmt.Sprintf( Query: fmt.Sprintf(
@ -372,10 +398,6 @@ func (m *Meta) backendMigrateNonEmptyConfirm(
return false, fmt.Errorf("Error saving temporary state: %s", err) return false, fmt.Errorf("Error saving temporary state: %s", err)
} }
if m.forceInitCopy {
return true, nil
}
// Ask for confirmation // Ask for confirmation
inputOpts := &terraform.InputOpts{ inputOpts := &terraform.InputOpts{
Id: "backend-migrate-to-backend", Id: "backend-migrate-to-backend",

View File

@ -426,6 +426,57 @@ func TestMetaBackend_configureNewWithState(t *testing.T) {
} }
} }
// Newly configured backend with matching local and remote state doesn't prompt
// for copy.
func TestMetaBackend_configureNewWithoutCopy(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
copy.CopyDir(testFixturePath("backend-new-migrate"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
if err := copy.CopyFile(DefaultStateFilename, "local-state.tfstate"); err != nil {
t.Fatal(err)
}
// Setup the meta
m := testMetaBackend(t, nil)
m.input = false
// init the backend
_, err := m.Backend(&BackendOpts{Init: true})
if err != nil {
t.Fatalf("bad: %s", err)
}
// Verify the state is where we expect
f, err := os.Open("local-state.tfstate")
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := terraform.ReadState(f)
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
}
if actual.Lineage != "backend-new-migrate" {
t.Fatalf("incorrect state lineage: %q", actual.Lineage)
}
// Verify the default paths don't exist
if !isEmptyState(DefaultStateFilename) {
data, _ := ioutil.ReadFile(DefaultStateFilename)
t.Fatal("state should not exist, but contains:\n", string(data))
}
// Verify a backup does exist
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
t.Fatal("backup state is empty or missing")
}
}
// Newly configured backend with prior local state and no remote state, // Newly configured backend with prior local state and no remote state,
// but opting to not migrate. // but opting to not migrate.
func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) { func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) {

View File

@ -62,10 +62,20 @@ type Schema struct {
DiffSuppressFunc SchemaDiffSuppressFunc DiffSuppressFunc SchemaDiffSuppressFunc
// If this is non-nil, then this will be a default value that is used // If this is non-nil, then this will be a default value that is used
// when this item is not set in the configuration/state. // when this item is not set in the configuration.
// //
// DefaultFunc can be specified if you want a dynamic default value. // DefaultFunc can be specified to compute a dynamic default.
// Only one of Default or DefaultFunc can be set. // Only one of Default or DefaultFunc can be set. If DefaultFunc is
// used then its return value should be stable to avoid generating
// confusing/perpetual diffs.
//
// Changing either Default or the return value of DefaultFunc can be
// a breaking change, especially if the attribute in question has
// ForceNew set. If a default needs to change to align with changing
// assumptions in an upstream API then it may be necessary to also use
// the MigrateState function on the resource to change the state to match,
// or have the Read function adjust the state value to align with the
// new default.
// //
// If Required is true above, then Default cannot be set. DefaultFunc // If Required is true above, then Default cannot be set. DefaultFunc
// can be set with Required. If the DefaultFunc returns nil, then there // can be set with Required. If the DefaultFunc returns nil, then there

View File

@ -94,9 +94,9 @@ func LockWithContext(ctx context.Context, s State, info *LockInfo) (string, erro
return "", err return "", err
} }
if le.Info.ID == "" { if le == nil || le.Info == nil || le.Info.ID == "" {
// the lock has no ID, something is wrong so don't keep trying // If we dont' have a complete LockError, there's something wrong with the lock
return "", fmt.Errorf("lock error missing ID: %s", err) return "", err
} }
if postLockHook != nil { if postLockHook != nil {

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/mitchellh/mapstructure"
) )
// EvalValidateError is the error structure returned if there were // EvalValidateError is the error structure returned if there were
@ -85,12 +86,31 @@ func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
type EvalValidateProvisioner struct { type EvalValidateProvisioner struct {
Provisioner *ResourceProvisioner Provisioner *ResourceProvisioner
Config **ResourceConfig Config **ResourceConfig
ConnConfig **ResourceConfig
} }
func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) { func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
provisioner := *n.Provisioner provisioner := *n.Provisioner
config := *n.Config config := *n.Config
warns, errs := provisioner.Validate(config) var warns []string
var errs []error
{
// Validate the provisioner's own config first
w, e := provisioner.Validate(config)
warns = append(warns, w...)
errs = append(errs, e...)
}
{
// Now validate the connection config, which might either be from
// the provisioner block itself or inherited from the resource's
// shared connection info.
w, e := n.validateConnConfig(*n.ConnConfig)
warns = append(warns, w...)
errs = append(errs, e...)
}
if len(warns) == 0 && len(errs) == 0 { if len(warns) == 0 && len(errs) == 0 {
return nil, nil return nil, nil
} }
@ -101,6 +121,64 @@ func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
} }
} }
func (n *EvalValidateProvisioner) validateConnConfig(connConfig *ResourceConfig) (warns []string, errs []error) {
// We can't comprehensively validate the connection config since its
// final structure is decided by the communicator and we can't instantiate
// that until we have a complete instance state. However, we *can* catch
// configuration keys that are not valid for *any* communicator, catching
// typos early rather than waiting until we actually try to run one of
// the resource's provisioners.
type connConfigSuperset struct {
// All attribute types are interface{} here because at this point we
// may still have unresolved interpolation expressions, which will
// appear as strings regardless of the final goal type.
Type interface{} `mapstructure:"type"`
User interface{} `mapstructure:"user"`
Password interface{} `mapstructure:"password"`
Host interface{} `mapstructure:"host"`
Port interface{} `mapstructure:"port"`
Timeout interface{} `mapstructure:"timeout"`
ScriptPath interface{} `mapstructure:"script_path"`
// For type=ssh only (enforced in ssh communicator)
PrivateKey interface{} `mapstructure:"private_key"`
Agent interface{} `mapstructure:"agent"`
BastionHost interface{} `mapstructure:"bastion_host"`
BastionPort interface{} `mapstructure:"bastion_port"`
BastionUser interface{} `mapstructure:"bastion_user"`
BastionPassword interface{} `mapstructure:"bastion_password"`
BastionPrivateKey interface{} `mapstructure:"bastion_private_key"`
// For type=winrm only (enforced in winrm communicator)
HTTPS interface{} `mapstructure:"https"`
Insecure interface{} `mapstructure:"insecure"`
CACert interface{} `mapstructure:"cacert"`
}
var metadata mapstructure.Metadata
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Metadata: &metadata,
Result: &connConfigSuperset{}, // result is disregarded; we only care about unused keys
})
if err != nil {
// should never happen
errs = append(errs, err)
return
}
if err := decoder.Decode(connConfig.Config); err != nil {
errs = append(errs, err)
return
}
for _, attrName := range metadata.Unused {
errs = append(errs, fmt.Errorf("unknown 'connection' argument %q", attrName))
}
return
}
// EvalValidateResource is an EvalNode implementation that validates // EvalValidateResource is an EvalNode implementation that validates
// the configuration of a resource. // the configuration of a resource.
type EvalValidateResource struct { type EvalValidateResource struct {

View File

@ -178,3 +178,117 @@ func TestEvalValidateResource_ignoreWarnings(t *testing.T) {
t.Fatalf("Expected no error, got: %s", err) t.Fatalf("Expected no error, got: %s", err)
} }
} }
func TestEvalValidateProvisioner_valid(t *testing.T) {
mp := &MockResourceProvisioner{}
var p ResourceProvisioner = mp
ctx := &MockEvalContext{}
cfg := &ResourceConfig{}
connInfo, err := config.NewRawConfig(map[string]interface{}{})
if err != nil {
t.Fatalf("failed to make connInfo: %s", err)
}
connConfig := NewResourceConfig(connInfo)
node := &EvalValidateProvisioner{
Provisioner: &p,
Config: &cfg,
ConnConfig: &connConfig,
}
result, err := node.Eval(ctx)
if err != nil {
t.Fatalf("node.Eval failed: %s", err)
}
if result != nil {
t.Errorf("node.Eval returned non-nil result")
}
if !mp.ValidateCalled {
t.Fatalf("p.Config not called")
}
if mp.ValidateConfig != cfg {
t.Errorf("p.Config called with wrong config")
}
}
func TestEvalValidateProvisioner_warning(t *testing.T) {
mp := &MockResourceProvisioner{}
var p ResourceProvisioner = mp
ctx := &MockEvalContext{}
cfg := &ResourceConfig{}
connInfo, err := config.NewRawConfig(map[string]interface{}{})
if err != nil {
t.Fatalf("failed to make connInfo: %s", err)
}
connConfig := NewResourceConfig(connInfo)
node := &EvalValidateProvisioner{
Provisioner: &p,
Config: &cfg,
ConnConfig: &connConfig,
}
mp.ValidateReturnWarns = []string{"foo is deprecated"}
_, err = node.Eval(ctx)
if err == nil {
t.Fatalf("node.Eval succeeded; want error")
}
valErr, ok := err.(*EvalValidateError)
if !ok {
t.Fatalf("node.Eval error is %#v; want *EvalValidateError", valErr)
}
warns := valErr.Warnings
if warns == nil || len(warns) != 1 {
t.Fatalf("wrong number of warnings in %#v; want one warning", warns)
}
if warns[0] != mp.ValidateReturnWarns[0] {
t.Fatalf("wrong warning %q; want %q", warns[0], mp.ValidateReturnWarns[0])
}
}
func TestEvalValidateProvisioner_connectionInvalid(t *testing.T) {
var p ResourceProvisioner = &MockResourceProvisioner{}
ctx := &MockEvalContext{}
cfg := &ResourceConfig{}
connInfo, err := config.NewRawConfig(map[string]interface{}{
"bananananananana": "foo",
"bazaz": "bar",
})
if err != nil {
t.Fatalf("failed to make connInfo: %s", err)
}
connConfig := NewResourceConfig(connInfo)
node := &EvalValidateProvisioner{
Provisioner: &p,
Config: &cfg,
ConnConfig: &connConfig,
}
_, err = node.Eval(ctx)
if err == nil {
t.Fatalf("node.Eval succeeded; want error")
}
valErr, ok := err.(*EvalValidateError)
if !ok {
t.Fatalf("node.Eval error is %#v; want *EvalValidateError", valErr)
}
errs := valErr.Errors
if errs == nil || len(errs) != 2 {
t.Fatalf("wrong number of errors in %#v; want two errors", errs)
}
errStr := errs[0].Error()
if !(strings.Contains(errStr, "bananananananana") || strings.Contains(errStr, "bazaz")) {
t.Fatalf("wrong first error %q; want something about our invalid connInfo keys", errStr)
}
}

View File

@ -129,17 +129,29 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
// Validate all the provisioners // Validate all the provisioners
for _, p := range n.Config.Provisioners { for _, p := range n.Config.Provisioners {
var provisioner ResourceProvisioner var provisioner ResourceProvisioner
seq.Nodes = append(seq.Nodes, &EvalGetProvisioner{ var connConfig *ResourceConfig
Name: p.Type, seq.Nodes = append(
Output: &provisioner, seq.Nodes,
}, &EvalInterpolate{ &EvalGetProvisioner{
Config: p.RawConfig.Copy(), Name: p.Type,
Resource: resource, Output: &provisioner,
Output: &config, },
}, &EvalValidateProvisioner{ &EvalInterpolate{
Provisioner: &provisioner, Config: p.RawConfig.Copy(),
Config: &config, Resource: resource,
}) Output: &config,
},
&EvalInterpolate{
Config: p.ConnInfo.Copy(),
Resource: resource,
Output: &connConfig,
},
&EvalValidateProvisioner{
Provisioner: &provisioner,
Config: &config,
ConnConfig: &connConfig,
},
)
} }
return seq return seq

View File

@ -0,0 +1,58 @@
// Package disk implements the Azure ARM Disk service API version
// 2016-04-30-preview.
//
// The Disk Resource Provider Client.
package disk
// Copyright (c) Microsoft and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
import (
"github.com/Azure/go-autorest/autorest"
)
const (
// APIVersion is the version of the Disk
APIVersion = "2016-04-30-preview"
// DefaultBaseURI is the default URI used for the service Disk
DefaultBaseURI = "https://management.azure.com"
)
// ManagementClient is the base client for Disk.
type ManagementClient struct {
autorest.Client
BaseURI string
APIVersion string
SubscriptionID string
}
// New creates an instance of the ManagementClient client.
func New(subscriptionID string) ManagementClient {
return NewWithBaseURI(DefaultBaseURI, subscriptionID)
}
// NewWithBaseURI creates an instance of the ManagementClient client.
func NewWithBaseURI(baseURI string, subscriptionID string) ManagementClient {
return ManagementClient{
Client: autorest.NewClientWithUserAgent(UserAgent()),
BaseURI: baseURI,
APIVersion: APIVersion,
SubscriptionID: subscriptionID,
}
}

View File

@ -0,0 +1,638 @@
package disk
// Copyright (c) Microsoft and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"net/http"
)
// DisksClient is the the Disk Resource Provider Client.
type DisksClient struct {
ManagementClient
}
// NewDisksClient creates an instance of the DisksClient client.
func NewDisksClient(subscriptionID string) DisksClient {
return NewDisksClientWithBaseURI(DefaultBaseURI, subscriptionID)
}
// NewDisksClientWithBaseURI creates an instance of the DisksClient client.
func NewDisksClientWithBaseURI(baseURI string, subscriptionID string) DisksClient {
return DisksClient{NewWithBaseURI(baseURI, subscriptionID)}
}
// CreateOrUpdate creates or updates a disk. This method may poll for
// completion. Polling can be canceled by passing the cancel channel argument.
// The channel will be used to cancel polling and any outstanding HTTP
// requests.
//
// resourceGroupName is the name of the resource group. diskName is the name of
// the disk within the given subscription and resource group. diskParameter is
// disk object supplied in the body of the Put disk operation.
func (client DisksClient) CreateOrUpdate(resourceGroupName string, diskName string, diskParameter Model, cancel <-chan struct{}) (result autorest.Response, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: diskParameter,
Constraints: []validation.Constraint{{Target: "diskParameter.Properties", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "diskParameter.Properties.CreationData", Name: validation.Null, Rule: true,
Chain: []validation.Constraint{{Target: "diskParameter.Properties.CreationData.ImageReference", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "diskParameter.Properties.CreationData.ImageReference.ID", Name: validation.Null, Rule: true, Chain: nil}}},
}},
{Target: "diskParameter.Properties.EncryptionSettings", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "diskParameter.Properties.EncryptionSettings.DiskEncryptionKey", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "diskParameter.Properties.EncryptionSettings.DiskEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil},
{Target: "diskParameter.Properties.EncryptionSettings.DiskEncryptionKey.SecretURL", Name: validation.Null, Rule: true, Chain: nil},
}},
{Target: "diskParameter.Properties.EncryptionSettings.KeyEncryptionKey", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "diskParameter.Properties.EncryptionSettings.KeyEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil},
{Target: "diskParameter.Properties.EncryptionSettings.KeyEncryptionKey.KeyURL", Name: validation.Null, Rule: true, Chain: nil},
}},
}},
}}}}}); err != nil {
return result, validation.NewErrorWithValidationError(err, "disk.DisksClient", "CreateOrUpdate")
}
req, err := client.CreateOrUpdatePreparer(resourceGroupName, diskName, diskParameter, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "CreateOrUpdate", nil, "Failure preparing request")
}
resp, err := client.CreateOrUpdateSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "CreateOrUpdate", resp, "Failure sending request")
}
result, err = client.CreateOrUpdateResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "CreateOrUpdate", resp, "Failure responding to request")
}
return
}
// CreateOrUpdatePreparer prepares the CreateOrUpdate request.
func (client DisksClient) CreateOrUpdatePreparer(resourceGroupName string, diskName string, diskParameter Model, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"diskName": autorest.Encode("path", diskName),
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsJSON(),
autorest.AsPut(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters),
autorest.WithJSON(diskParameter),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always
// closes the http.Response Body.
func (client DisksClient) CreateOrUpdateResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}
// Delete deletes a disk. This method may poll for completion. Polling can be
// canceled by passing the cancel channel argument. The channel will be used to
// cancel polling and any outstanding HTTP requests.
//
// resourceGroupName is the name of the resource group. diskName is the name of
// the disk within the given subscription and resource group.
func (client DisksClient) Delete(resourceGroupName string, diskName string, cancel <-chan struct{}) (result autorest.Response, err error) {
req, err := client.DeletePreparer(resourceGroupName, diskName, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "Delete", nil, "Failure preparing request")
}
resp, err := client.DeleteSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "Delete", resp, "Failure sending request")
}
result, err = client.DeleteResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "Delete", resp, "Failure responding to request")
}
return
}
// DeletePreparer prepares the Delete request.
func (client DisksClient) DeletePreparer(resourceGroupName string, diskName string, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"diskName": autorest.Encode("path", diskName),
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsDelete(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// DeleteSender sends the Delete request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) DeleteSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// DeleteResponder handles the response to the Delete request. The method always
// closes the http.Response Body.
func (client DisksClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent),
autorest.ByClosing())
result.Response = resp
return
}
// Get gets information about a disk.
//
// resourceGroupName is the name of the resource group. diskName is the name of
// the disk within the given subscription and resource group.
func (client DisksClient) Get(resourceGroupName string, diskName string) (result Model, err error) {
req, err := client.GetPreparer(resourceGroupName, diskName)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "Get", nil, "Failure preparing request")
}
resp, err := client.GetSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "Get", resp, "Failure sending request")
}
result, err = client.GetResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "Get", resp, "Failure responding to request")
}
return
}
// GetPreparer prepares the Get request.
func (client DisksClient) GetPreparer(resourceGroupName string, diskName string) (*http.Request, error) {
pathParameters := map[string]interface{}{
"diskName": autorest.Encode("path", diskName),
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{})
}
// GetSender sends the Get request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) GetSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client, req)
}
// GetResponder handles the response to the Get request. The method always
// closes the http.Response Body.
func (client DisksClient) GetResponder(resp *http.Response) (result Model, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}
// GrantAccess grants access to a disk. This method may poll for completion.
// Polling can be canceled by passing the cancel channel argument. The channel
// will be used to cancel polling and any outstanding HTTP requests.
//
// resourceGroupName is the name of the resource group. diskName is the name of
// the disk within the given subscription and resource group. grantAccessData
// is access data object supplied in the body of the get disk access operation.
func (client DisksClient) GrantAccess(resourceGroupName string, diskName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (result autorest.Response, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: grantAccessData,
Constraints: []validation.Constraint{{Target: "grantAccessData.DurationInSeconds", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil {
return result, validation.NewErrorWithValidationError(err, "disk.DisksClient", "GrantAccess")
}
req, err := client.GrantAccessPreparer(resourceGroupName, diskName, grantAccessData, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "GrantAccess", nil, "Failure preparing request")
}
resp, err := client.GrantAccessSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "GrantAccess", resp, "Failure sending request")
}
result, err = client.GrantAccessResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "GrantAccess", resp, "Failure responding to request")
}
return
}
// GrantAccessPreparer prepares the GrantAccess request.
func (client DisksClient) GrantAccessPreparer(resourceGroupName string, diskName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"diskName": autorest.Encode("path", diskName),
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsJSON(),
autorest.AsPost(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}/beginGetAccess", pathParameters),
autorest.WithJSON(grantAccessData),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// GrantAccessSender sends the GrantAccess request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) GrantAccessSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// GrantAccessResponder handles the response to the GrantAccess request. The method always
// closes the http.Response Body.
func (client DisksClient) GrantAccessResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}
// List lists all the disks under a subscription.
func (client DisksClient) List() (result ListType, err error) {
req, err := client.ListPreparer()
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "List", nil, "Failure preparing request")
}
resp, err := client.ListSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure sending request")
}
result, err = client.ListResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure responding to request")
}
return
}
// ListPreparer prepares the List request.
func (client DisksClient) ListPreparer() (*http.Request, error) {
pathParameters := map[string]interface{}{
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Compute/disks", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{})
}
// ListSender sends the List request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) ListSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client, req)
}
// ListResponder handles the response to the List request. The method always
// closes the http.Response Body.
func (client DisksClient) ListResponder(resp *http.Response) (result ListType, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}
// ListNextResults retrieves the next set of results, if any.
func (client DisksClient) ListNextResults(lastResults ListType) (result ListType, err error) {
req, err := lastResults.ListTypePreparer()
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "List", nil, "Failure preparing next results request")
}
if req == nil {
return
}
resp, err := client.ListSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure sending next results request")
}
result, err = client.ListResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "List", resp, "Failure responding to next results request")
}
return
}
// ListByResourceGroup lists all the disks under a resource group.
//
// resourceGroupName is the name of the resource group.
func (client DisksClient) ListByResourceGroup(resourceGroupName string) (result ListType, err error) {
req, err := client.ListByResourceGroupPreparer(resourceGroupName)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", nil, "Failure preparing request")
}
resp, err := client.ListByResourceGroupSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure sending request")
}
result, err = client.ListByResourceGroupResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure responding to request")
}
return
}
// ListByResourceGroupPreparer prepares the ListByResourceGroup request.
func (client DisksClient) ListByResourceGroupPreparer(resourceGroupName string) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{})
}
// ListByResourceGroupSender sends the ListByResourceGroup request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) ListByResourceGroupSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client, req)
}
// ListByResourceGroupResponder handles the response to the ListByResourceGroup request. The method always
// closes the http.Response Body.
func (client DisksClient) ListByResourceGroupResponder(resp *http.Response) (result ListType, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}
// ListByResourceGroupNextResults retrieves the next set of results, if any.
func (client DisksClient) ListByResourceGroupNextResults(lastResults ListType) (result ListType, err error) {
req, err := lastResults.ListTypePreparer()
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", nil, "Failure preparing next results request")
}
if req == nil {
return
}
resp, err := client.ListByResourceGroupSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure sending next results request")
}
result, err = client.ListByResourceGroupResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "ListByResourceGroup", resp, "Failure responding to next results request")
}
return
}
// RevokeAccess revokes access to a disk. This method may poll for completion.
// Polling can be canceled by passing the cancel channel argument. The channel
// will be used to cancel polling and any outstanding HTTP requests.
//
// resourceGroupName is the name of the resource group. diskName is the name of
// the disk within the given subscription and resource group.
func (client DisksClient) RevokeAccess(resourceGroupName string, diskName string, cancel <-chan struct{}) (result autorest.Response, err error) {
req, err := client.RevokeAccessPreparer(resourceGroupName, diskName, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "RevokeAccess", nil, "Failure preparing request")
}
resp, err := client.RevokeAccessSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "RevokeAccess", resp, "Failure sending request")
}
result, err = client.RevokeAccessResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "RevokeAccess", resp, "Failure responding to request")
}
return
}
// RevokeAccessPreparer prepares the RevokeAccess request.
func (client DisksClient) RevokeAccessPreparer(resourceGroupName string, diskName string, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"diskName": autorest.Encode("path", diskName),
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsPost(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}/endGetAccess", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// RevokeAccessSender sends the RevokeAccess request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) RevokeAccessSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// RevokeAccessResponder handles the response to the RevokeAccess request. The method always
// closes the http.Response Body.
func (client DisksClient) RevokeAccessResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}
// Update updates (patches) a disk. This method may poll for completion.
// Polling can be canceled by passing the cancel channel argument. The channel
// will be used to cancel polling and any outstanding HTTP requests.
//
// resourceGroupName is the name of the resource group. diskName is the name of
// the disk within the given subscription and resource group. diskParameter is
// disk object supplied in the body of the Patch disk operation.
func (client DisksClient) Update(resourceGroupName string, diskName string, diskParameter UpdateType, cancel <-chan struct{}) (result autorest.Response, err error) {
req, err := client.UpdatePreparer(resourceGroupName, diskName, diskParameter, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "Update", nil, "Failure preparing request")
}
resp, err := client.UpdateSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.DisksClient", "Update", resp, "Failure sending request")
}
result, err = client.UpdateResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.DisksClient", "Update", resp, "Failure responding to request")
}
return
}
// UpdatePreparer prepares the Update request.
func (client DisksClient) UpdatePreparer(resourceGroupName string, diskName string, diskParameter UpdateType, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"diskName": autorest.Encode("path", diskName),
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsJSON(),
autorest.AsPatch(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/disks/{diskName}", pathParameters),
autorest.WithJSON(diskParameter),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// UpdateSender sends the Update request. The method will close the
// http.Response Body if it receives an error.
func (client DisksClient) UpdateSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// UpdateResponder handles the response to the Update request. The method always
// closes the http.Response Body.
func (client DisksClient) UpdateResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}

View File

@ -0,0 +1,278 @@
package disk
// Copyright (c) Microsoft and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/date"
"github.com/Azure/go-autorest/autorest/to"
"net/http"
)
// AccessLevel enumerates the values for access level.
type AccessLevel string
const (
// None specifies the none state for access level.
None AccessLevel = "None"
// Read specifies the read state for access level.
Read AccessLevel = "Read"
)
// CreateOption enumerates the values for create option.
type CreateOption string
const (
// Attach specifies the attach state for create option.
Attach CreateOption = "Attach"
// Copy specifies the copy state for create option.
Copy CreateOption = "Copy"
// Empty specifies the empty state for create option.
Empty CreateOption = "Empty"
// FromImage specifies the from image state for create option.
FromImage CreateOption = "FromImage"
// Import specifies the import state for create option.
Import CreateOption = "Import"
// Restore specifies the restore state for create option.
Restore CreateOption = "Restore"
)
// OperatingSystemTypes enumerates the values for operating system types.
type OperatingSystemTypes string
const (
// Linux specifies the linux state for operating system types.
Linux OperatingSystemTypes = "Linux"
// Windows specifies the windows state for operating system types.
Windows OperatingSystemTypes = "Windows"
)
// StorageAccountTypes enumerates the values for storage account types.
type StorageAccountTypes string
const (
// PremiumLRS specifies the premium lrs state for storage account types.
PremiumLRS StorageAccountTypes = "Premium_LRS"
// StandardLRS specifies the standard lrs state for storage account types.
StandardLRS StorageAccountTypes = "Standard_LRS"
)
// AccessURI is a disk access SAS uri.
type AccessURI struct {
autorest.Response `json:"-"`
*AccessURIOutput `json:"properties,omitempty"`
}
// AccessURIOutput is azure properties, including output.
type AccessURIOutput struct {
*AccessURIRaw `json:"output,omitempty"`
}
// AccessURIRaw is this object gets 'bubbled up' through flattening.
type AccessURIRaw struct {
AccessSAS *string `json:"accessSAS,omitempty"`
}
// APIError is api error.
type APIError struct {
Details *[]APIErrorBase `json:"details,omitempty"`
Innererror *InnerError `json:"innererror,omitempty"`
Code *string `json:"code,omitempty"`
Target *string `json:"target,omitempty"`
Message *string `json:"message,omitempty"`
}
// APIErrorBase is api error base.
type APIErrorBase struct {
Code *string `json:"code,omitempty"`
Target *string `json:"target,omitempty"`
Message *string `json:"message,omitempty"`
}
// CreationData is data used when creating a disk.
type CreationData struct {
CreateOption CreateOption `json:"createOption,omitempty"`
StorageAccountID *string `json:"storageAccountId,omitempty"`
ImageReference *ImageDiskReference `json:"imageReference,omitempty"`
SourceURI *string `json:"sourceUri,omitempty"`
SourceResourceID *string `json:"sourceResourceId,omitempty"`
}
// EncryptionSettings is encryption settings for disk or snapshot
type EncryptionSettings struct {
Enabled *bool `json:"enabled,omitempty"`
DiskEncryptionKey *KeyVaultAndSecretReference `json:"diskEncryptionKey,omitempty"`
KeyEncryptionKey *KeyVaultAndKeyReference `json:"keyEncryptionKey,omitempty"`
}
// GrantAccessData is data used for requesting a SAS.
type GrantAccessData struct {
Access AccessLevel `json:"access,omitempty"`
DurationInSeconds *int32 `json:"durationInSeconds,omitempty"`
}
// ImageDiskReference is the source image used for creating the disk.
type ImageDiskReference struct {
ID *string `json:"id,omitempty"`
Lun *int32 `json:"lun,omitempty"`
}
// InnerError is inner error details.
type InnerError struct {
Exceptiontype *string `json:"exceptiontype,omitempty"`
Errordetail *string `json:"errordetail,omitempty"`
}
// KeyVaultAndKeyReference is key Vault Key Url and vault id of KeK, KeK is
// optional and when provided is used to unwrap the encryptionKey
type KeyVaultAndKeyReference struct {
SourceVault *SourceVault `json:"sourceVault,omitempty"`
KeyURL *string `json:"keyUrl,omitempty"`
}
// KeyVaultAndSecretReference is key Vault Secret Url and vault id of the
// encryption key
type KeyVaultAndSecretReference struct {
SourceVault *SourceVault `json:"sourceVault,omitempty"`
SecretURL *string `json:"secretUrl,omitempty"`
}
// ListType is the List Disks operation response.
type ListType struct {
autorest.Response `json:"-"`
Value *[]Model `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// ListTypePreparer prepares a request to retrieve the next set of results. It returns
// nil if no more results exist.
func (client ListType) ListTypePreparer() (*http.Request, error) {
if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 {
return nil, nil
}
return autorest.Prepare(&http.Request{},
autorest.AsJSON(),
autorest.AsGet(),
autorest.WithBaseURL(to.String(client.NextLink)))
}
// Model is disk resource.
type Model struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
*Properties `json:"properties,omitempty"`
}
// OperationStatusResponse is operation status response
type OperationStatusResponse struct {
autorest.Response `json:"-"`
Name *string `json:"name,omitempty"`
Status *string `json:"status,omitempty"`
StartTime *date.Time `json:"startTime,omitempty"`
EndTime *date.Time `json:"endTime,omitempty"`
Error *APIError `json:"error,omitempty"`
}
// Properties is disk resource properties.
type Properties struct {
AccountType StorageAccountTypes `json:"accountType,omitempty"`
TimeCreated *date.Time `json:"timeCreated,omitempty"`
OsType OperatingSystemTypes `json:"osType,omitempty"`
CreationData *CreationData `json:"creationData,omitempty"`
DiskSizeGB *int32 `json:"diskSizeGB,omitempty"`
EncryptionSettings *EncryptionSettings `json:"encryptionSettings,omitempty"`
OwnerID *string `json:"ownerId,omitempty"`
ProvisioningState *string `json:"provisioningState,omitempty"`
}
// Resource is the Resource model definition.
type Resource struct {
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
}
// ResourceUpdate is the Resource model definition.
type ResourceUpdate struct {
Tags *map[string]*string `json:"tags,omitempty"`
}
// Snapshot is snapshot resource.
type Snapshot struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
*Properties `json:"properties,omitempty"`
}
// SnapshotList is the List Snapshots operation response.
type SnapshotList struct {
autorest.Response `json:"-"`
Value *[]Snapshot `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// SnapshotListPreparer prepares a request to retrieve the next set of results. It returns
// nil if no more results exist.
func (client SnapshotList) SnapshotListPreparer() (*http.Request, error) {
if client.NextLink == nil || len(to.String(client.NextLink)) <= 0 {
return nil, nil
}
return autorest.Prepare(&http.Request{},
autorest.AsJSON(),
autorest.AsGet(),
autorest.WithBaseURL(to.String(client.NextLink)))
}
// SnapshotUpdate is snapshot update resource.
type SnapshotUpdate struct {
Tags *map[string]*string `json:"tags,omitempty"`
*UpdateProperties `json:"properties,omitempty"`
}
// SourceVault is the vault id is an Azure Resource Manager Resoure id in the
// form
// /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{vaultName}
type SourceVault struct {
ID *string `json:"id,omitempty"`
}
// UpdateProperties is disk resource update properties.
type UpdateProperties struct {
AccountType StorageAccountTypes `json:"accountType,omitempty"`
OsType OperatingSystemTypes `json:"osType,omitempty"`
CreationData *CreationData `json:"creationData,omitempty"`
DiskSizeGB *int32 `json:"diskSizeGB,omitempty"`
EncryptionSettings *EncryptionSettings `json:"encryptionSettings,omitempty"`
}
// UpdateType is disk update resource.
type UpdateType struct {
Tags *map[string]*string `json:"tags,omitempty"`
*UpdateProperties `json:"properties,omitempty"`
}

View File

@ -0,0 +1,643 @@
package disk
// Copyright (c) Microsoft and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"net/http"
)
// SnapshotsClient is the the Disk Resource Provider Client.
type SnapshotsClient struct {
ManagementClient
}
// NewSnapshotsClient creates an instance of the SnapshotsClient client.
func NewSnapshotsClient(subscriptionID string) SnapshotsClient {
return NewSnapshotsClientWithBaseURI(DefaultBaseURI, subscriptionID)
}
// NewSnapshotsClientWithBaseURI creates an instance of the SnapshotsClient
// client.
func NewSnapshotsClientWithBaseURI(baseURI string, subscriptionID string) SnapshotsClient {
return SnapshotsClient{NewWithBaseURI(baseURI, subscriptionID)}
}
// CreateOrUpdate creates or updates a snapshot. This method may poll for
// completion. Polling can be canceled by passing the cancel channel argument.
// The channel will be used to cancel polling and any outstanding HTTP
// requests.
//
// resourceGroupName is the name of the resource group. snapshotName is the
// name of the snapshot within the given subscription and resource group.
// snapshot is snapshot object supplied in the body of the Put disk operation.
func (client SnapshotsClient) CreateOrUpdate(resourceGroupName string, snapshotName string, snapshot Snapshot, cancel <-chan struct{}) (result autorest.Response, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: snapshot,
Constraints: []validation.Constraint{{Target: "snapshot.Properties", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "snapshot.Properties.CreationData", Name: validation.Null, Rule: true,
Chain: []validation.Constraint{{Target: "snapshot.Properties.CreationData.ImageReference", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "snapshot.Properties.CreationData.ImageReference.ID", Name: validation.Null, Rule: true, Chain: nil}}},
}},
{Target: "snapshot.Properties.EncryptionSettings", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "snapshot.Properties.EncryptionSettings.DiskEncryptionKey", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "snapshot.Properties.EncryptionSettings.DiskEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil},
{Target: "snapshot.Properties.EncryptionSettings.DiskEncryptionKey.SecretURL", Name: validation.Null, Rule: true, Chain: nil},
}},
{Target: "snapshot.Properties.EncryptionSettings.KeyEncryptionKey", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "snapshot.Properties.EncryptionSettings.KeyEncryptionKey.SourceVault", Name: validation.Null, Rule: true, Chain: nil},
{Target: "snapshot.Properties.EncryptionSettings.KeyEncryptionKey.KeyURL", Name: validation.Null, Rule: true, Chain: nil},
}},
}},
}}}}}); err != nil {
return result, validation.NewErrorWithValidationError(err, "disk.SnapshotsClient", "CreateOrUpdate")
}
req, err := client.CreateOrUpdatePreparer(resourceGroupName, snapshotName, snapshot, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "CreateOrUpdate", nil, "Failure preparing request")
}
resp, err := client.CreateOrUpdateSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "CreateOrUpdate", resp, "Failure sending request")
}
result, err = client.CreateOrUpdateResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "CreateOrUpdate", resp, "Failure responding to request")
}
return
}
// CreateOrUpdatePreparer prepares the CreateOrUpdate request.
func (client SnapshotsClient) CreateOrUpdatePreparer(resourceGroupName string, snapshotName string, snapshot Snapshot, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"snapshotName": autorest.Encode("path", snapshotName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsJSON(),
autorest.AsPut(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters),
autorest.WithJSON(snapshot),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) CreateOrUpdateResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}
// Delete deletes a snapshot. This method may poll for completion. Polling can
// be canceled by passing the cancel channel argument. The channel will be used
// to cancel polling and any outstanding HTTP requests.
//
// resourceGroupName is the name of the resource group. snapshotName is the
// name of the snapshot within the given subscription and resource group.
func (client SnapshotsClient) Delete(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (result autorest.Response, err error) {
req, err := client.DeletePreparer(resourceGroupName, snapshotName, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Delete", nil, "Failure preparing request")
}
resp, err := client.DeleteSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Delete", resp, "Failure sending request")
}
result, err = client.DeleteResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Delete", resp, "Failure responding to request")
}
return
}
// DeletePreparer prepares the Delete request.
func (client SnapshotsClient) DeletePreparer(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"snapshotName": autorest.Encode("path", snapshotName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsDelete(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// DeleteSender sends the Delete request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) DeleteSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// DeleteResponder handles the response to the Delete request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted, http.StatusNoContent),
autorest.ByClosing())
result.Response = resp
return
}
// Get gets information about a snapshot.
//
// resourceGroupName is the name of the resource group. snapshotName is the
// name of the snapshot within the given subscription and resource group.
func (client SnapshotsClient) Get(resourceGroupName string, snapshotName string) (result Snapshot, err error) {
req, err := client.GetPreparer(resourceGroupName, snapshotName)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Get", nil, "Failure preparing request")
}
resp, err := client.GetSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Get", resp, "Failure sending request")
}
result, err = client.GetResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Get", resp, "Failure responding to request")
}
return
}
// GetPreparer prepares the Get request.
func (client SnapshotsClient) GetPreparer(resourceGroupName string, snapshotName string) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"snapshotName": autorest.Encode("path", snapshotName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{})
}
// GetSender sends the Get request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) GetSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client, req)
}
// GetResponder handles the response to the Get request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) GetResponder(resp *http.Response) (result Snapshot, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}
// GrantAccess grants access to a snapshot. This method may poll for
// completion. Polling can be canceled by passing the cancel channel argument.
// The channel will be used to cancel polling and any outstanding HTTP
// requests.
//
// resourceGroupName is the name of the resource group. snapshotName is the
// name of the snapshot within the given subscription and resource group.
// grantAccessData is access data object supplied in the body of the get
// snapshot access operation.
func (client SnapshotsClient) GrantAccess(resourceGroupName string, snapshotName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (result autorest.Response, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: grantAccessData,
Constraints: []validation.Constraint{{Target: "grantAccessData.DurationInSeconds", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil {
return result, validation.NewErrorWithValidationError(err, "disk.SnapshotsClient", "GrantAccess")
}
req, err := client.GrantAccessPreparer(resourceGroupName, snapshotName, grantAccessData, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "GrantAccess", nil, "Failure preparing request")
}
resp, err := client.GrantAccessSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "GrantAccess", resp, "Failure sending request")
}
result, err = client.GrantAccessResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "GrantAccess", resp, "Failure responding to request")
}
return
}
// GrantAccessPreparer prepares the GrantAccess request.
func (client SnapshotsClient) GrantAccessPreparer(resourceGroupName string, snapshotName string, grantAccessData GrantAccessData, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"snapshotName": autorest.Encode("path", snapshotName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsJSON(),
autorest.AsPost(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}/beginGetAccess", pathParameters),
autorest.WithJSON(grantAccessData),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// GrantAccessSender sends the GrantAccess request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) GrantAccessSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// GrantAccessResponder handles the response to the GrantAccess request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) GrantAccessResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}
// List lists snapshots under a subscription.
func (client SnapshotsClient) List() (result SnapshotList, err error) {
req, err := client.ListPreparer()
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", nil, "Failure preparing request")
}
resp, err := client.ListSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure sending request")
}
result, err = client.ListResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure responding to request")
}
return
}
// ListPreparer prepares the List request.
func (client SnapshotsClient) ListPreparer() (*http.Request, error) {
pathParameters := map[string]interface{}{
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/Microsoft.Compute/snapshots", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{})
}
// ListSender sends the List request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) ListSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client, req)
}
// ListResponder handles the response to the List request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) ListResponder(resp *http.Response) (result SnapshotList, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}
// ListNextResults retrieves the next set of results, if any.
func (client SnapshotsClient) ListNextResults(lastResults SnapshotList) (result SnapshotList, err error) {
req, err := lastResults.SnapshotListPreparer()
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", nil, "Failure preparing next results request")
}
if req == nil {
return
}
resp, err := client.ListSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure sending next results request")
}
result, err = client.ListResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "List", resp, "Failure responding to next results request")
}
return
}
// ListByResourceGroup lists snapshots under a resource group.
//
// resourceGroupName is the name of the resource group.
func (client SnapshotsClient) ListByResourceGroup(resourceGroupName string) (result SnapshotList, err error) {
req, err := client.ListByResourceGroupPreparer(resourceGroupName)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", nil, "Failure preparing request")
}
resp, err := client.ListByResourceGroupSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure sending request")
}
result, err = client.ListByResourceGroupResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure responding to request")
}
return
}
// ListByResourceGroupPreparer prepares the ListByResourceGroup request.
func (client SnapshotsClient) ListByResourceGroupPreparer(resourceGroupName string) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsGet(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{})
}
// ListByResourceGroupSender sends the ListByResourceGroup request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) ListByResourceGroupSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client, req)
}
// ListByResourceGroupResponder handles the response to the ListByResourceGroup request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) ListByResourceGroupResponder(resp *http.Response) (result SnapshotList, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}
// ListByResourceGroupNextResults retrieves the next set of results, if any.
func (client SnapshotsClient) ListByResourceGroupNextResults(lastResults SnapshotList) (result SnapshotList, err error) {
req, err := lastResults.SnapshotListPreparer()
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", nil, "Failure preparing next results request")
}
if req == nil {
return
}
resp, err := client.ListByResourceGroupSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure sending next results request")
}
result, err = client.ListByResourceGroupResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "ListByResourceGroup", resp, "Failure responding to next results request")
}
return
}
// RevokeAccess revokes access to a snapshot. This method may poll for
// completion. Polling can be canceled by passing the cancel channel argument.
// The channel will be used to cancel polling and any outstanding HTTP
// requests.
//
// resourceGroupName is the name of the resource group. snapshotName is the
// name of the snapshot within the given subscription and resource group.
func (client SnapshotsClient) RevokeAccess(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (result autorest.Response, err error) {
req, err := client.RevokeAccessPreparer(resourceGroupName, snapshotName, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "RevokeAccess", nil, "Failure preparing request")
}
resp, err := client.RevokeAccessSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "RevokeAccess", resp, "Failure sending request")
}
result, err = client.RevokeAccessResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "RevokeAccess", resp, "Failure responding to request")
}
return
}
// RevokeAccessPreparer prepares the RevokeAccess request.
func (client SnapshotsClient) RevokeAccessPreparer(resourceGroupName string, snapshotName string, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"snapshotName": autorest.Encode("path", snapshotName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsPost(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}/endGetAccess", pathParameters),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// RevokeAccessSender sends the RevokeAccess request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) RevokeAccessSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// RevokeAccessResponder handles the response to the RevokeAccess request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) RevokeAccessResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}
// Update updates (patches) a snapshot. This method may poll for completion.
// Polling can be canceled by passing the cancel channel argument. The channel
// will be used to cancel polling and any outstanding HTTP requests.
//
// resourceGroupName is the name of the resource group. snapshotName is the
// name of the snapshot within the given subscription and resource group.
// snapshot is snapshot object supplied in the body of the Patch snapshot
// operation.
func (client SnapshotsClient) Update(resourceGroupName string, snapshotName string, snapshot SnapshotUpdate, cancel <-chan struct{}) (result autorest.Response, err error) {
req, err := client.UpdatePreparer(resourceGroupName, snapshotName, snapshot, cancel)
if err != nil {
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Update", nil, "Failure preparing request")
}
resp, err := client.UpdateSender(req)
if err != nil {
result.Response = resp
return result, autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Update", resp, "Failure sending request")
}
result, err = client.UpdateResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "disk.SnapshotsClient", "Update", resp, "Failure responding to request")
}
return
}
// UpdatePreparer prepares the Update request.
func (client SnapshotsClient) UpdatePreparer(resourceGroupName string, snapshotName string, snapshot SnapshotUpdate, cancel <-chan struct{}) (*http.Request, error) {
pathParameters := map[string]interface{}{
"resourceGroupName": autorest.Encode("path", resourceGroupName),
"snapshotName": autorest.Encode("path", snapshotName),
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsJSON(),
autorest.AsPatch(),
autorest.WithBaseURL(client.BaseURI),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/snapshots/{snapshotName}", pathParameters),
autorest.WithJSON(snapshot),
autorest.WithQueryParameters(queryParameters))
return preparer.Prepare(&http.Request{Cancel: cancel})
}
// UpdateSender sends the Update request. The method will close the
// http.Response Body if it receives an error.
func (client SnapshotsClient) UpdateSender(req *http.Request) (*http.Response, error) {
return autorest.SendWithSender(client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
}
// UpdateResponder handles the response to the Update request. The method always
// closes the http.Response Body.
func (client SnapshotsClient) UpdateResponder(resp *http.Response) (result autorest.Response, err error) {
err = autorest.Respond(
resp,
client.ByInspecting(),
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusAccepted),
autorest.ByClosing())
result.Response = resp
return
}

View File

@ -0,0 +1,60 @@
package disk
// Copyright (c) Microsoft and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
import (
"bytes"
"fmt"
"strings"
)
const (
major = "8"
minor = "1"
patch = "0"
tag = "beta"
userAgentFormat = "Azure-SDK-For-Go/%s arm-%s/%s"
)
// cached results of UserAgent and Version to prevent repeated operations.
var (
userAgent string
version string
)
// UserAgent returns the UserAgent string to use when sending http.Requests.
func UserAgent() string {
if userAgent == "" {
userAgent = fmt.Sprintf(userAgentFormat, Version(), "disk", "2016-04-30-preview")
}
return userAgent
}
// Version returns the semantic version (see http://semver.org) of the client.
func Version() string {
if version == "" {
versionBuilder := bytes.NewBufferString(fmt.Sprintf("%s.%s.%s", major, minor, patch))
if tag != "" {
versionBuilder.WriteRune('-')
versionBuilder.WriteString(strings.TrimPrefix(tag, "-"))
}
version = string(versionBuilder.Bytes())
}
return version
}

View File

@ -1,3 +1,143 @@
Release v1.8.8 (2017-04-04)
===
### Service Client Updates
* `service/cloudwatch`: Updates service API, documentation, and paginators
* Amazon Web Services announced the immediate availability of two additional alarm configuration rules for Amazon CloudWatch Alarms. The first rule is for configuring missing data treatment. Customers have the options to treat missing data as alarm threshold breached, alarm threshold not breached, maintain alarm state and the current default treatment. The second rule is for alarms based on percentiles metrics that can trigger unnecassarily if the percentile is calculated from a small number of samples. The new rule can treat percentiles with low sample counts as same as missing data. If the first rule is enabled, the same treatment will be applied when an alarm encounters a percentile with low sample counts.
Release v1.8.7 (2017-04-03)
===
### Service Client Updates
* `service/lexruntimeservice`: Updates service API and documentation
* Adds support to PostContent for speech input
### SDK Enhancements
* `aws/request`: Improve handler copy, push back, push front performance (#1171)
* Minor optimization to the handler list's handling of copying and pushing request handlers to the handler list.
* Update codegen header to use Go std wording (#1172)
* Go recently accepted the proposal for standard generated file header wording in, https://golang.org/s/generatedcode.
### SDK Bugs
* `service/dynamodb`: Fix DynamoDB using custom retryer (#1170)
* Fixes (#1139) the DynamoDB service client clobbering any custom retryer that was passed into the service client or Session's config.
Release v1.8.6 (2017-04-01)
===
### Service Client Updates
* `service/clouddirectory`: Updates service API and documentation
* ListObjectAttributes now supports filtering by facet.
* `aws/endpoints`: Updated Regions and Endpoints metadata.
Release v1.8.5 (2017-03-30)
===
### Service Client Updates
* `service/cloudformation`: Updates service waiters and paginators
* Adding paginators for ListExports and ListImports
* `service/cloudfront`: Adds new service
* Amazon CloudFront now supports user configurable HTTP Read and Keep-Alive Idle Timeouts for your Custom Origin Servers
* `service/configservice`: Updates service documentation
* `aws/endpoints`: Updated Regions and Endpoints metadata.
* `service/resourcegroupstaggingapi`: Adds new service
* `service/storagegateway`: Updates service API and documentation
* File gateway mode in AWS Storage gateway provides access to objects in S3 as files on a Network File System (NFS) mount point. Once a file share is created, any changes made externally to the S3 bucket will not be reflected by the gateway. Using the cache refresh feature in this update, the customer can trigger an on-demand scan of the keys in their S3 bucket and refresh the file namespace cached on the gateway. It takes as an input the fileShare ARN and refreshes the cache for only that file share. Additionally there is new functionality on file gateway that allows you configure what squash options they would like on their file share, this allows a customer to configure their gateway to not squash root permissions. This can be done by setting options in NfsOptions for CreateNfsFileShare and UpdateNfsFileShare APIs.
Release v1.8.4 (2017-03-28)
===
### Service Client Updates
* `service/batch`: Updates service API, documentation, and paginators
* Customers can now provide a retryStrategy as part of the RegisterJobDefinition and SubmitJob API calls. The retryStrategy object has a number value for attempts. This is the number of non successful executions before a job is considered FAILED. In addition, the JobDetail object now has an attempts field and shows all execution attempts.
* `service/ec2`: Updates service API and documentation
* Customers can now tag their Amazon EC2 Instances and Amazon EBS Volumes at
the time of their creation. You can do this from the EC2 Instance launch
wizard or through the RunInstances or CreateVolume APIs. By tagging
resources at the time of creation, you can eliminate the need to run custom
tagging scripts after resource creation. In addition, you can now set
resource-level permissions on the CreateVolume, CreateTags, DeleteTags, and
the RunInstances APIs. This allows you to implement stronger security
policies by giving you more granular control over which users and groups
have access to these APIs. You can also enforce the use of tagging and
control what tag keys and values are set on your resources. When you combine
tag usage and resource-level IAM policies together, you can ensure your
instances and volumes are properly secured upon creation and achieve more
accurate cost allocation reporting. These new features are provided at no
additional cost.
### SDK Enhancements
* `aws/request`: Add retry support for RequestTimeoutException (#1158)
* Adds support for retrying RequestTimeoutException error code that is returned by some services.
### SDK Bugs
* `private/model/api`: Fix Waiter and Paginators panic on nil param inputs (#1157)
* Corrects the code generation for Paginators and waiters that caused a panic if nil input parameters were used with the operations.
Release v1.8.3 (2017-03-27)
===
## Service Client Updates
* `service/ssm`: Updates service API, documentation, and paginators
* Updated validation rules for SendCommand and RegisterTaskWithMaintenanceWindow APIs.
Release v1.8.2 (2017-03-24)
===
Service Client Updates
---
* `service/applicationautoscaling`: Updates service API, documentation, and paginators
* Application AutoScaling is launching support for a new target resource (AppStream 2.0 Fleets) as a scalable target.
* `service/cloudtrail`: Updates service API and documentation
* Doc-only Update for CloudTrail: Add required parameters for GetEventSelectors and PutEventSelectors
Release v1.8.1 (2017-03-23)
===
Service Client Updates
---
* `service/applicationdiscoveryservice`: Updates service API, documentation, and paginators
* Adds export configuration options to the AWS Discovery Service API.
* `service/elbv2`: Updates waiters
* `aws/endpoints`: Updated Regions and Endpoints metadata.
* `service/lambda`: Updates service API and paginators
* Adds support for new runtime Node.js v6.10 for AWS Lambda service
Release v1.8.0 (2017-03-22)
===
Service Client Updates
---
* `service/codebuild`: Updates service documentation
* `service/directconnect`: Updates service API
* Deprecated DescribeConnectionLoa, DescribeInterconnectLoa, AllocateConnectionOnInterconnect and DescribeConnectionsOnInterconnect operations in favor of DescribeLoa, DescribeLoa, AllocateHostedConnection and DescribeHostedConnections respectively.
* `service/marketplacecommerceanalytics`: Updates service API, documentation, and paginators
* This update adds a new data set, us_sales_and_use_tax_records, which enables AWS Marketplace sellers to programmatically access to their U.S. Sales and Use Tax report data.
* `service/pinpoint`: Updates service API and documentation
* Amazon Pinpoint User Segmentation
* Added ability to segment endpoints by user attributes in addition to endpoint attributes. Amazon Pinpoint Event Stream Preview
* Added functionality to publish raw app analytics and campaign events data as events streams to Kinesis and Kinesis Firehose
* The feature provides developers with increased flexibility of exporting raw events to S3, Redshift, Elasticsearch using a Kinesis Firehose stream or enable real time event processing use cases using a Kinesis stream
* `service/rekognition`: Updates service documentation.
SDK Features
---
* `aws/request`: Add support for context.Context to SDK API operation requests (#1132)
* Adds support for context.Context to the SDK by adding `WithContext` methods for each API operation, Paginators and Waiters. e.g `PutObjectWithContext`. This change also adds the ability to provide request functional options to the method calls instead of requiring you to use the `Request` API operation method (e.g `PutObjectRequest`).
* Adds a `Complete` Request handler list that will be called ever time a request is completed. This includes both success and failure. Complete will only be called once per API operation request.
* `private/waiter` package moved from the private group to `aws/request/waiter` and made publicly available.
* Adds Context support to all API operations, Waiters(WaitUntil) and Paginators(Pages) methods.
* Adds Context support for s3manager and s3crypto clients.
SDK Enhancements
---
* `aws/signer/v4`: Adds support for unsigned payload signer config (#1130)
* Adds configuration option to the v4.Signer to specify the request's body should not be signed. This will only correclty function on services that support unsigned payload. e.g. S3, Glacier.
SDK Bug Fixes
---
* `service/s3`: Fix S3 HostID to be available in S3 request error message (#1131)
* Adds a new type s3.RequestFailure which exposes the S3 HostID value from a S3 API operation response. This is helpful when you have an error with S3, and need to contact support. Both RequestID and HostID are needed.
* `private/model/api`: Do not return a link if uid is empty (#1133)
* Fixes SDK's doc generation to not generate API reference doc links if the SDK us unable to create a valid link.
* `aws/request`: Optimization to handler list copy to prevent multiple alloc calls. (#1134)
Release v1.7.9 (2017-03-13) Release v1.7.9 (2017-03-13)
=== ===

View File

@ -0,0 +1,5 @@
### SDK Features
### SDK Enhancements
### SDK Bugs

View File

@ -64,6 +64,11 @@ Please be aware of the following notes prior to opening a pull request:
SDK's test coverage percentage are unlikely to be merged until tests have SDK's test coverage percentage are unlikely to be merged until tests have
been added. been added.
5. The JSON files under the SDK's `models` folder are sourced from outside the SDK.
Such as `models/apis/ec2/2016-11-15/api.json`. We will not accept pull requests
directly on these models. If you discover an issue with the models please
create a Github [issue](issues) describing the issue.
### Testing ### Testing
To run the tests locally, running the `make unit` command will `go get` the To run the tests locally, running the `make unit` command will `go get` the

View File

@ -1,11 +1,4 @@
# AWS SDK for Go # AWS SDK for Go [![API Reference](http://img.shields.io/badge/api-reference-blue.svg)](http://docs.aws.amazon.com/sdk-for-go/api) [![Join the chat at https://gitter.im/aws/aws-sdk-go](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aws/aws-sdk-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://img.shields.io/travis/aws/aws-sdk-go.svg)](https://travis-ci.org/aws/aws-sdk-go) [![Apache V2 License](http://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/aws/aws-sdk-go/blob/master/LICENSE.txt)
<span style="display: inline-block;">
[![API Reference](http://img.shields.io/badge/api-reference-blue.svg)](http://docs.aws.amazon.com/sdk-for-go/api)
[![Join the chat at https://gitter.im/aws/aws-sdk-go](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aws/aws-sdk-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://img.shields.io/travis/aws/aws-sdk-go.svg)](https://travis-ci.org/aws/aws-sdk-go)
[![Apache V2 License](http://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/aws/aws-sdk-go/blob/master/LICENSE.txt)
</span>
aws-sdk-go is the official AWS SDK for the Go programming language. aws-sdk-go is the official AWS SDK for the Go programming language.
@ -77,7 +70,7 @@ AWS_SECRET_ACCESS_KEY=MY-SECRET-KEY
``` ```
### AWS shared config file (`~/.aws/config`) ### AWS shared config file (`~/.aws/config`)
The AWS SDK for Go added support the shared config file in release [v1.3.0](https://github.com/aws/aws-sdk-go/releases/tag/v1.3.0). You can opt into enabling support for the shared config by setting the environment variable `AWS_SDK_LOAD_CONFIG` to a truthy value. See the [Session](https://github.com/aws/aws-sdk-go/wiki/sessions) wiki for more information about this feature. The AWS SDK for Go added support the shared config file in release [v1.3.0](https://github.com/aws/aws-sdk-go/releases/tag/v1.3.0). You can opt into enabling support for the shared config by setting the environment variable `AWS_SDK_LOAD_CONFIG` to a truthy value. See the [Session](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/sessions.html) docs for more information about this feature.
## Using the Go SDK ## Using the Go SDK
@ -85,44 +78,77 @@ To use a service in the SDK, create a service variable by calling the `New()`
function. Once you have a service client, you can call API operations which each function. Once you have a service client, you can call API operations which each
return response data and a possible error. return response data and a possible error.
To list a set of instance IDs from EC2, you could run: For example the following code shows how to upload an object to Amazon S3 with a Context timeout.
```go ```go
package main package main
import ( import (
"context"
"flag"
"fmt" "fmt"
"os"
"time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/s3"
) )
// Uploads a file to S3 given a bucket and object key. Also takes a duration
// value to terminate the update if it doesn't complete within that time.
//
// The AWS Region needs to be provided in the AWS shared config or on the
// environment variable as `AWS_REGION`. Credentials also must be provided
// Will default to shared config file, but can load from environment if provided.
//
// Usage:
// # Upload myfile.txt to myBucket/myKey. Must complete within 10 minutes or will fail
// go run withContext.go -b mybucket -k myKey -d 10m < myfile.txt
func main() { func main() {
sess, err := session.NewSession() var bucket, key string
if err != nil { var timeout time.Duration
panic(err)
flag.StringVar(&bucket, "b", "", "Bucket name.")
flag.StringVar(&key, "k", "", "Object key name.")
flag.DurationVar(&timeout, "d", 0, "Upload timeout.")
flag.Parse()
sess := session.Must(session.NewSession())
svc := s3.New(sess)
// Create a context with a timeout that will abort the upload if it takes
// more than the passed in timeout.
ctx := context.Background()
var cancelFn func()
if timeout > 0 {
ctx, cancelFn = context.WithTimeout(ctx, timeout)
} }
// Ensure the context is canceled to prevent leaking.
// See context package for more information, https://golang.org/pkg/context/
defer cancelFn()
// Create an EC2 service object in the "us-west-2" region // Uploads the object to S3. The Context will interrupt the request if the
// Note that you can also configure your region globally by // timeout expires.
// exporting the AWS_REGION environment variable _, err := svc.PutObjectWithContext(ctx, &s3.PutObjectInput{
svc := ec2.New(sess, &aws.Config{Region: aws.String("us-west-2")}) Bucket: aws.String(bucket),
Key: aws.String(key),
// Call the DescribeInstances Operation Body: os.Stdin,
resp, err := svc.DescribeInstances(nil) })
if err != nil { if err != nil {
panic(err) if aerr, ok := err.(awserr.Error); ok && aerr.Code() == request.CanceledErrorCode {
} // If the SDK can determine the request or retry delay was canceled
// by a context the CanceledErrorCode error code will be returned.
// resp has all of the response data, pull out instance IDs: fmt.Fprintf(os.Stderr, "upload canceled due to timeout, %v\n", err)
fmt.Println("> Number of reservation sets: ", len(resp.Reservations)) } else {
for idx, res := range resp.Reservations { fmt.Fprintf(os.Stderr, "failed to upload object, %v\n", err)
fmt.Println(" > Number of instances: ", len(res.Instances))
for _, inst := range resp.Reservations[idx].Instances {
fmt.Println(" - Instance ID: ", *inst.InstanceId)
} }
os.Exit(1)
} }
fmt.Printf("successfully uploaded file to %s/%s\n", bucket, key)
} }
``` ```

View File

@ -187,6 +187,10 @@ type Config struct {
// request delays. This value should only be used for testing. To adjust // request delays. This value should only be used for testing. To adjust
// the delay of a request see the aws/client.DefaultRetryer and // the delay of a request see the aws/client.DefaultRetryer and
// aws/request.Retryer. // aws/request.Retryer.
//
// SleepDelay will prevent any Context from being used for canceling retry
// delay of an API operation. It is recommended to not use SleepDelay at all
// and specify a Retryer instead.
SleepDelay func(time.Duration) SleepDelay func(time.Duration)
// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests. // DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.

71
vendor/github.com/aws/aws-sdk-go/aws/context.go generated vendored Normal file
View File

@ -0,0 +1,71 @@
package aws
import (
"time"
)
// Context is an copy of the Go v1.7 stdlib's context.Context interface.
// It is represented as a SDK interface to enable you to use the "WithContext"
// API methods with Go v1.6 and a Context type such as golang.org/x/net/context.
//
// See https://golang.org/pkg/context on how to use contexts.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
Value(key interface{}) interface{}
}
// BackgroundContext returns a context that will never be canceled, has no
// values, and no deadline. This context is used by the SDK to provide
// backwards compatibility with non-context API operations and functionality.
//
// Go 1.6 and before:
// This context function is equivalent to context.Background in the Go stdlib.
//
// Go 1.7 and later:
// The context returned will be the value returned by context.Background()
//
// See https://golang.org/pkg/context for more information on Contexts.
func BackgroundContext() Context {
return backgroundCtx
}
// SleepWithContext will wait for the timer duration to expire, or the context
// is canceled. Which ever happens first. If the context is canceled the Context's
// error will be returned.
//
// Expects Context to always return a non-nil error if the Done channel is closed.
func SleepWithContext(ctx Context, dur time.Duration) error {
t := time.NewTimer(dur)
defer t.Stop()
select {
case <-t.C:
break
case <-ctx.Done():
return ctx.Err()
}
return nil
}

41
vendor/github.com/aws/aws-sdk-go/aws/context_1_6.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
// +build !go1.7
package aws
import "time"
// An emptyCtx is a copy of the the Go 1.7 context.emptyCtx type. This
// is copied to provide a 1.6 and 1.5 safe version of context that is compatible
// with Go 1.7's Context.
//
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case backgroundCtx:
return "aws.BackgroundContext"
}
return "unknown empty Context"
}
var (
backgroundCtx = new(emptyCtx)
)

9
vendor/github.com/aws/aws-sdk-go/aws/context_1_7.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
// +build go1.7
package aws
import "context"
var (
backgroundCtx = context.Background()
)

View File

@ -134,6 +134,16 @@ var SendHandler = request.NamedHandler{Name: "core.SendHandler", Fn: func(r *req
// Catch all other request errors. // Catch all other request errors.
r.Error = awserr.New("RequestError", "send request failed", err) r.Error = awserr.New("RequestError", "send request failed", err)
r.Retryable = aws.Bool(true) // network errors are retryable r.Retryable = aws.Bool(true) // network errors are retryable
// Override the error with a context canceled error, if that was canceled.
ctx := r.Context()
select {
case <-ctx.Done():
r.Error = awserr.New(request.CanceledErrorCode,
"request context canceled", ctx.Err())
r.Retryable = aws.Bool(false)
default:
}
} }
}} }}
@ -156,7 +166,16 @@ var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn:
if r.WillRetry() { if r.WillRetry() {
r.RetryDelay = r.RetryRules(r) r.RetryDelay = r.RetryRules(r)
r.Config.SleepDelay(r.RetryDelay)
if sleepFn := r.Config.SleepDelay; sleepFn != nil {
// Support SleepDelay for backwards compatibility and testing
sleepFn(r.RetryDelay)
} else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil {
r.Error = awserr.New(request.CanceledErrorCode,
"request context canceled", err)
r.Retryable = aws.Bool(false)
return
}
// when the expired token exception occurs the credentials // when the expired token exception occurs the credentials
// need to be expired locally so that the next request to // need to be expired locally so that the next request to

View File

@ -88,7 +88,7 @@ type Value struct {
// The Provider should not need to implement its own mutexes, because // The Provider should not need to implement its own mutexes, because
// that will be managed by Credentials. // that will be managed by Credentials.
type Provider interface { type Provider interface {
// Refresh returns nil if it successfully retrieved the value. // Retrieve returns nil if it successfully retrieved the value.
// Error is returned if the value were not obtainable, or empty. // Error is returned if the value were not obtainable, or empty.
Retrieve() (Value, error) Retrieve() (Value, error)

View File

@ -56,7 +56,6 @@ func Config() *aws.Config {
WithMaxRetries(aws.UseServiceDefaultRetries). WithMaxRetries(aws.UseServiceDefaultRetries).
WithLogger(aws.NewDefaultLogger()). WithLogger(aws.NewDefaultLogger()).
WithLogLevel(aws.LogOff). WithLogLevel(aws.LogOff).
WithSleepDelay(time.Sleep).
WithEndpointResolver(endpoints.DefaultResolver()) WithEndpointResolver(endpoints.DefaultResolver())
} }

View File

@ -1,4 +1,4 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. // Code generated by aws/endpoints/v3model_codegen.go. DO NOT EDIT.
package endpoints package endpoints
@ -131,6 +131,7 @@ const (
StsServiceID = "sts" // Sts. StsServiceID = "sts" // Sts.
SupportServiceID = "support" // Support. SupportServiceID = "support" // Support.
SwfServiceID = "swf" // Swf. SwfServiceID = "swf" // Swf.
TaggingServiceID = "tagging" // Tagging.
WafServiceID = "waf" // Waf. WafServiceID = "waf" // Waf.
WafRegionalServiceID = "waf-regional" // WafRegional. WafRegionalServiceID = "waf-regional" // WafRegional.
WorkdocsServiceID = "workdocs" // Workdocs. WorkdocsServiceID = "workdocs" // Workdocs.
@ -249,6 +250,7 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{}, "ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{}, "ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
@ -435,10 +437,14 @@ var awsPartition = partition{
"codebuild": service{ "codebuild": service{
Endpoints: endpoints{ Endpoints: endpoints{
"eu-west-1": endpoint{}, "ap-northeast-1": endpoint{},
"us-east-1": endpoint{}, "ap-southeast-1": endpoint{},
"us-east-2": endpoint{}, "ap-southeast-2": endpoint{},
"us-west-2": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-2": endpoint{},
}, },
}, },
"codecommit": service{ "codecommit": service{
@ -755,10 +761,11 @@ var awsPartition = partition{
"elasticfilesystem": service{ "elasticfilesystem": service{
Endpoints: endpoints{ Endpoints: endpoints{
"eu-west-1": endpoint{}, "ap-southeast-2": endpoint{},
"us-east-1": endpoint{}, "eu-west-1": endpoint{},
"us-east-2": endpoint{}, "us-east-1": endpoint{},
"us-west-2": endpoint{}, "us-east-2": endpoint{},
"us-west-2": endpoint{},
}, },
}, },
"elasticloadbalancing": service{ "elasticloadbalancing": service{
@ -1022,6 +1029,7 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-northeast-1": endpoint{}, "ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{}, "ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{}, "ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
@ -1380,7 +1388,6 @@ var awsPartition = partition{
Endpoints: endpoints{ Endpoints: endpoints{
"ap-south-1": endpoint{}, "ap-south-1": endpoint{},
"ap-southeast-2": endpoint{}, "ap-southeast-2": endpoint{},
"ca-central-1": endpoint{},
"eu-central-1": endpoint{}, "eu-central-1": endpoint{},
"eu-west-1": endpoint{}, "eu-west-1": endpoint{},
"eu-west-2": endpoint{}, "eu-west-2": endpoint{},
@ -1567,6 +1574,25 @@ var awsPartition = partition{
"us-west-2": endpoint{}, "us-west-2": endpoint{},
}, },
}, },
"tagging": service{
Endpoints: endpoints{
"ap-northeast-1": endpoint{},
"ap-northeast-2": endpoint{},
"ap-south-1": endpoint{},
"ap-southeast-1": endpoint{},
"ap-southeast-2": endpoint{},
"ca-central-1": endpoint{},
"eu-central-1": endpoint{},
"eu-west-1": endpoint{},
"eu-west-2": endpoint{},
"sa-east-1": endpoint{},
"us-east-1": endpoint{},
"us-east-2": endpoint{},
"us-west-1": endpoint{},
"us-west-2": endpoint{},
},
},
"waf": service{ "waf": service{
PartitionEndpoint: "aws-global", PartitionEndpoint: "aws-global",
IsRegionalized: boxedFalse, IsRegionalized: boxedFalse,
@ -1678,6 +1704,12 @@ var awscnPartition = partition{
"cn-north-1": endpoint{}, "cn-north-1": endpoint{},
}, },
}, },
"codedeploy": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
},
},
"config": service{ "config": service{
Endpoints: endpoints{ Endpoints: endpoints{
@ -1855,6 +1887,12 @@ var awscnPartition = partition{
}, },
"swf": service{ "swf": service{
Endpoints: endpoints{
"cn-north-1": endpoint{},
},
},
"tagging": service{
Endpoints: endpoints{ Endpoints: endpoints{
"cn-north-1": endpoint{}, "cn-north-1": endpoint{},
}, },

View File

@ -158,7 +158,7 @@ var funcMap = template.FuncMap{
const v3Tmpl = ` const v3Tmpl = `
{{ define "defaults" -}} {{ define "defaults" -}}
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. // Code generated by aws/endpoints/v3model_codegen.go. DO NOT EDIT.
package endpoints package endpoints

11
vendor/github.com/aws/aws-sdk-go/aws/jsonvalue.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
package aws
// JSONValue is a representation of a grab bag type that will be marshaled
// into a json string. This type can be used just like any other map.
//
// Example:
// values := JSONValue{
// "Foo": "Bar",
// }
// values["Baz"] = "Qux"
type JSONValue map[string]interface{}

View File

@ -18,6 +18,7 @@ type Handlers struct {
UnmarshalError HandlerList UnmarshalError HandlerList
Retry HandlerList Retry HandlerList
AfterRetry HandlerList AfterRetry HandlerList
Complete HandlerList
} }
// Copy returns of this handler's lists. // Copy returns of this handler's lists.
@ -33,6 +34,7 @@ func (h *Handlers) Copy() Handlers {
UnmarshalMeta: h.UnmarshalMeta.copy(), UnmarshalMeta: h.UnmarshalMeta.copy(),
Retry: h.Retry.copy(), Retry: h.Retry.copy(),
AfterRetry: h.AfterRetry.copy(), AfterRetry: h.AfterRetry.copy(),
Complete: h.Complete.copy(),
} }
} }
@ -48,6 +50,7 @@ func (h *Handlers) Clear() {
h.ValidateResponse.Clear() h.ValidateResponse.Clear()
h.Retry.Clear() h.Retry.Clear()
h.AfterRetry.Clear() h.AfterRetry.Clear()
h.Complete.Clear()
} }
// A HandlerListRunItem represents an entry in the HandlerList which // A HandlerListRunItem represents an entry in the HandlerList which
@ -85,13 +88,17 @@ func (l *HandlerList) copy() HandlerList {
n := HandlerList{ n := HandlerList{
AfterEachFn: l.AfterEachFn, AfterEachFn: l.AfterEachFn,
} }
n.list = append([]NamedHandler{}, l.list...) if len(l.list) == 0 {
return n
}
n.list = append(make([]NamedHandler, 0, len(l.list)), l.list...)
return n return n
} }
// Clear clears the handler list. // Clear clears the handler list.
func (l *HandlerList) Clear() { func (l *HandlerList) Clear() {
l.list = []NamedHandler{} l.list = l.list[0:0]
} }
// Len returns the number of handlers in the list. // Len returns the number of handlers in the list.
@ -101,33 +108,49 @@ func (l *HandlerList) Len() int {
// PushBack pushes handler f to the back of the handler list. // PushBack pushes handler f to the back of the handler list.
func (l *HandlerList) PushBack(f func(*Request)) { func (l *HandlerList) PushBack(f func(*Request)) {
l.list = append(l.list, NamedHandler{"__anonymous", f}) l.PushBackNamed(NamedHandler{"__anonymous", f})
}
// PushFront pushes handler f to the front of the handler list.
func (l *HandlerList) PushFront(f func(*Request)) {
l.list = append([]NamedHandler{{"__anonymous", f}}, l.list...)
} }
// PushBackNamed pushes named handler f to the back of the handler list. // PushBackNamed pushes named handler f to the back of the handler list.
func (l *HandlerList) PushBackNamed(n NamedHandler) { func (l *HandlerList) PushBackNamed(n NamedHandler) {
if cap(l.list) == 0 {
l.list = make([]NamedHandler, 0, 5)
}
l.list = append(l.list, n) l.list = append(l.list, n)
} }
// PushFront pushes handler f to the front of the handler list.
func (l *HandlerList) PushFront(f func(*Request)) {
l.PushFrontNamed(NamedHandler{"__anonymous", f})
}
// PushFrontNamed pushes named handler f to the front of the handler list. // PushFrontNamed pushes named handler f to the front of the handler list.
func (l *HandlerList) PushFrontNamed(n NamedHandler) { func (l *HandlerList) PushFrontNamed(n NamedHandler) {
l.list = append([]NamedHandler{n}, l.list...) if cap(l.list) == len(l.list) {
// Allocating new list required
l.list = append([]NamedHandler{n}, l.list...)
} else {
// Enough room to prepend into list.
l.list = append(l.list, NamedHandler{})
copy(l.list[1:], l.list)
l.list[0] = n
}
} }
// Remove removes a NamedHandler n // Remove removes a NamedHandler n
func (l *HandlerList) Remove(n NamedHandler) { func (l *HandlerList) Remove(n NamedHandler) {
newlist := []NamedHandler{} for i := 0; i < len(l.list); i++ {
for _, m := range l.list { m := l.list[i]
if m.Name != n.Name { if m.Name == n.Name {
newlist = append(newlist, m) // Shift array preventing creating new arrays
copy(l.list[i:], l.list[i+1:])
l.list[len(l.list)-1] = NamedHandler{}
l.list = l.list[:len(l.list)-1]
// decrement list so next check to length is correct
i--
} }
} }
l.list = newlist
} }
// Run executes all handlers in the list with a given request object. // Run executes all handlers in the list with a given request object.
@ -163,6 +186,16 @@ func HandlerListStopOnError(item HandlerListRunItem) bool {
return item.Request.Error == nil return item.Request.Error == nil
} }
// WithAppendUserAgent will add a string to the user agent prefixed with a
// single white space.
func WithAppendUserAgent(s string) Option {
return func(r *Request) {
r.Handlers.Build.PushBack(func(r2 *Request) {
AddToUserAgent(r, s)
})
}
}
// MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request // MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request
// header. If the extra parameters are provided they will be added as metadata to the // header. If the extra parameters are provided they will be added as metadata to the
// name/version pair resulting in the following format. // name/version pair resulting in the following format.

View File

@ -16,6 +16,11 @@ import (
"github.com/aws/aws-sdk-go/aws/client/metadata" "github.com/aws/aws-sdk-go/aws/client/metadata"
) )
// CanceledErrorCode is the error code that will be returned by an
// API request that was canceled. Requests given a aws.Context may
// return this error when canceled.
const CanceledErrorCode = "RequestCanceled"
// A Request is the service request to be made. // A Request is the service request to be made.
type Request struct { type Request struct {
Config aws.Config Config aws.Config
@ -41,12 +46,14 @@ type Request struct {
SignedHeaderVals http.Header SignedHeaderVals http.Header
LastSignedAt time.Time LastSignedAt time.Time
context aws.Context
built bool built bool
// Need to persist an intermideant body betweend the input Body and HTTP // Need to persist an intermediate body between the input Body and HTTP
// request body because the HTTP Client's transport can maintain a reference // request body because the HTTP Client's transport can maintain a reference
// to the HTTP request's body after the client has returned. This value is // to the HTTP request's body after the client has returned. This value is
// safe to use concurrently and rewraps the input Body for each HTTP request. // safe to use concurrently and wrap the input Body for each HTTP request.
safeBody *offsetReader safeBody *offsetReader
} }
@ -60,14 +67,6 @@ type Operation struct {
BeforePresignFn func(r *Request) error BeforePresignFn func(r *Request) error
} }
// Paginator keeps track of pagination configuration for an API operation.
type Paginator struct {
InputTokens []string
OutputTokens []string
LimitToken string
TruncationToken string
}
// New returns a new Request pointer for the service API // New returns a new Request pointer for the service API
// operation and parameters. // operation and parameters.
// //
@ -111,6 +110,94 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
return r return r
} }
// A Option is a functional option that can augment or modify a request when
// using a WithContext API operation method.
type Option func(*Request)
// WithGetResponseHeader builds a request Option which will retrieve a single
// header value from the HTTP Response. If there are multiple values for the
// header key use WithGetResponseHeaders instead to access the http.Header
// map directly. The passed in val pointer must be non-nil.
//
// This Option can be used multiple times with a single API operation.
//
// var id2, versionID string
// svc.PutObjectWithContext(ctx, params,
// request.WithGetResponseHeader("x-amz-id-2", &id2),
// request.WithGetResponseHeader("x-amz-version-id", &versionID),
// )
func WithGetResponseHeader(key string, val *string) Option {
return func(r *Request) {
r.Handlers.Complete.PushBack(func(req *Request) {
*val = req.HTTPResponse.Header.Get(key)
})
}
}
// WithGetResponseHeaders builds a request Option which will retrieve the
// headers from the HTTP response and assign them to the passed in headers
// variable. The passed in headers pointer must be non-nil.
//
// var headers http.Header
// svc.PutObjectWithContext(ctx, params, request.WithGetResponseHeaders(&headers))
func WithGetResponseHeaders(headers *http.Header) Option {
return func(r *Request) {
r.Handlers.Complete.PushBack(func(req *Request) {
*headers = req.HTTPResponse.Header
})
}
}
// WithLogLevel is a request option that will set the request to use a specific
// log level when the request is made.
//
// svc.PutObjectWithContext(ctx, params, request.WithLogLevel(aws.LogDebugWithHTTPBody)
func WithLogLevel(l aws.LogLevelType) Option {
return func(r *Request) {
r.Config.LogLevel = aws.LogLevel(l)
}
}
// ApplyOptions will apply each option to the request calling them in the order
// the were provided.
func (r *Request) ApplyOptions(opts ...Option) {
for _, opt := range opts {
opt(r)
}
}
// Context will always returns a non-nil context. If Request does not have a
// context aws.BackgroundContext will be returned.
func (r *Request) Context() aws.Context {
if r.context != nil {
return r.context
}
return aws.BackgroundContext()
}
// SetContext adds a Context to the current request that can be used to cancel
// a in-flight request. The Context value must not be nil, or this method will
// panic.
//
// Unlike http.Request.WithContext, SetContext does not return a copy of the
// Request. It is not safe to use use a single Request value for multiple
// requests. A new Request should be created for each API operation request.
//
// Go 1.6 and below:
// The http.Request's Cancel field will be set to the Done() value of
// the context. This will overwrite the Cancel field's value.
//
// Go 1.7 and above:
// The http.Request.WithContext will be used to set the context on the underlying
// http.Request. This will create a shallow copy of the http.Request. The SDK
// may create sub contexts in the future for nested requests such as retries.
func (r *Request) SetContext(ctx aws.Context) {
if ctx == nil {
panic("context cannot be nil")
}
setRequestContext(r, ctx)
}
// WillRetry returns if the request's can be retried. // WillRetry returns if the request's can be retried.
func (r *Request) WillRetry() bool { func (r *Request) WillRetry() bool {
return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries() return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
@ -344,6 +431,12 @@ func (r *Request) GetBody() io.ReadSeeker {
// //
// Send will not close the request.Request's body. // Send will not close the request.Request's body.
func (r *Request) Send() error { func (r *Request) Send() error {
defer func() {
// Regardless of success or failure of the request trigger the Complete
// request handlers.
r.Handlers.Complete.Run(r)
}()
for { for {
if aws.BoolValue(r.Retryable) { if aws.BoolValue(r.Retryable) {
if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) { if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
@ -446,6 +539,9 @@ func shouldRetryCancel(r *Request) bool {
timeoutErr := false timeoutErr := false
errStr := r.Error.Error() errStr := r.Error.Error()
if ok { if ok {
if awsErr.Code() == CanceledErrorCode {
return false
}
err := awsErr.OrigErr() err := awsErr.OrigErr()
netErr, netOK := err.(net.Error) netErr, netOK := err.(net.Error)
timeoutErr = netOK && netErr.Temporary() timeoutErr = netOK && netErr.Temporary()

View File

@ -0,0 +1,14 @@
// +build go1.7
package request
import "github.com/aws/aws-sdk-go/aws"
// setContext updates the Request to use the passed in context for cancellation.
// Context will also be used for request retry delay.
//
// Creates shallow copy of the http.Request with the WithContext method.
func setRequestContext(r *Request, ctx aws.Context) {
r.context = ctx
r.HTTPRequest = r.HTTPRequest.WithContext(ctx)
}

View File

@ -0,0 +1,14 @@
// +build !go1.7
package request
import "github.com/aws/aws-sdk-go/aws"
// setContext updates the Request to use the passed in context for cancellation.
// Context will also be used for request retry delay.
//
// Creates shallow copy of the http.Request with the WithContext method.
func setRequestContext(r *Request, ctx aws.Context) {
r.context = ctx
r.HTTPRequest.Cancel = ctx.Done()
}

View File

@ -2,29 +2,125 @@ package request
import ( import (
"reflect" "reflect"
"sync/atomic"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/awsutil"
) )
//type Paginater interface { // A Pagination provides paginating of SDK API operations which are paginatable.
// HasNextPage() bool // Generally you should not use this type directly, but use the "Pages" API
// NextPage() *Request // operations method to automatically perform pagination for you. Such as,
// EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error // "S3.ListObjectsPages", and "S3.ListObjectsPagesWithContext" methods.
//} //
// Pagination differs from a Paginator type in that pagination is the type that
// does the pagination between API operations, and Paginator defines the
// configuration that will be used per page request.
//
// cont := true
// for p.Next() && cont {
// data := p.Page().(*s3.ListObjectsOutput)
// // process the page's data
// }
// return p.Err()
//
// See service client API operation Pages methods for examples how the SDK will
// use the Pagination type.
type Pagination struct {
// Function to return a Request value for each pagination request.
// Any configuration or handlers that need to be applied to the request
// prior to getting the next page should be done here before the request
// returned.
//
// NewRequest should always be built from the same API operations. It is
// undefined if different API operations are returned on subsequent calls.
NewRequest func() (*Request, error)
// HasNextPage returns true if this request has more pages of data available. started bool
func (r *Request) HasNextPage() bool { nextTokens []interface{}
return len(r.nextPageTokens()) > 0
err error
curPage interface{}
} }
// nextPageTokens returns the tokens to use when asking for the next page of // HasNextPage will return true if Pagination is able to determine that the API
// data. // operation has additional pages. False will be returned if there are no more
// pages remaining.
//
// Will always return true if Next has not been called yet.
func (p *Pagination) HasNextPage() bool {
return !(p.started && len(p.nextTokens) == 0)
}
// Err returns the error Pagination encountered when retrieving the next page.
func (p *Pagination) Err() error {
return p.err
}
// Page returns the current page. Page should only be called after a successful
// call to Next. It is undefined what Page will return if Page is called after
// Next returns false.
func (p *Pagination) Page() interface{} {
return p.curPage
}
// Next will attempt to retrieve the next page for the API operation. When a page
// is retrieved true will be returned. If the page cannot be retrieved, or there
// are no more pages false will be returned.
//
// Use the Page method to retrieve the current page data. The data will need
// to be cast to the API operation's output type.
//
// Use the Err method to determine if an error occurred if Page returns false.
func (p *Pagination) Next() bool {
if !p.HasNextPage() {
return false
}
req, err := p.NewRequest()
if err != nil {
p.err = err
return false
}
if p.started {
for i, intok := range req.Operation.InputTokens {
awsutil.SetValueAtPath(req.Params, intok, p.nextTokens[i])
}
}
p.started = true
err = req.Send()
if err != nil {
p.err = err
return false
}
p.nextTokens = req.nextPageTokens()
p.curPage = req.Data
return true
}
// A Paginator is the configuration data that defines how an API operation
// should be paginated. This type is used by the API service models to define
// the generated pagination config for service APIs.
//
// The Pagination type is what provides iterating between pages of an API. It
// is only used to store the token metadata the SDK should use for performing
// pagination.
type Paginator struct {
InputTokens []string
OutputTokens []string
LimitToken string
TruncationToken string
}
// nextPageTokens returns the tokens to use when asking for the next page of data.
func (r *Request) nextPageTokens() []interface{} { func (r *Request) nextPageTokens() []interface{} {
if r.Operation.Paginator == nil { if r.Operation.Paginator == nil {
return nil return nil
} }
if r.Operation.TruncationToken != "" { if r.Operation.TruncationToken != "" {
tr, _ := awsutil.ValuesAtPath(r.Data, r.Operation.TruncationToken) tr, _ := awsutil.ValuesAtPath(r.Data, r.Operation.TruncationToken)
if len(tr) == 0 { if len(tr) == 0 {
@ -61,9 +157,40 @@ func (r *Request) nextPageTokens() []interface{} {
return tokens return tokens
} }
// Ensure a deprecated item is only logged once instead of each time its used.
func logDeprecatedf(logger aws.Logger, flag *int32, msg string) {
if logger == nil {
return
}
if atomic.CompareAndSwapInt32(flag, 0, 1) {
logger.Log(msg)
}
}
var (
logDeprecatedHasNextPage int32
logDeprecatedNextPage int32
logDeprecatedEachPage int32
)
// HasNextPage returns true if this request has more pages of data available.
//
// Deprecated Use Pagination type for configurable pagination of API operations
func (r *Request) HasNextPage() bool {
logDeprecatedf(r.Config.Logger, &logDeprecatedHasNextPage,
"Request.HasNextPage deprecated. Use Pagination type for configurable pagination of API operations")
return len(r.nextPageTokens()) > 0
}
// NextPage returns a new Request that can be executed to return the next // NextPage returns a new Request that can be executed to return the next
// page of result data. Call .Send() on this request to execute it. // page of result data. Call .Send() on this request to execute it.
//
// Deprecated Use Pagination type for configurable pagination of API operations
func (r *Request) NextPage() *Request { func (r *Request) NextPage() *Request {
logDeprecatedf(r.Config.Logger, &logDeprecatedNextPage,
"Request.NextPage deprecated. Use Pagination type for configurable pagination of API operations")
tokens := r.nextPageTokens() tokens := r.nextPageTokens()
if len(tokens) == 0 { if len(tokens) == 0 {
return nil return nil
@ -90,7 +217,12 @@ func (r *Request) NextPage() *Request {
// as the structure "T". The lastPage value represents whether the page is // as the structure "T". The lastPage value represents whether the page is
// the last page of data or not. The return value of this function should // the last page of data or not. The return value of this function should
// return true to keep iterating or false to stop. // return true to keep iterating or false to stop.
//
// Deprecated Use Pagination type for configurable pagination of API operations
func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error { func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error {
logDeprecatedf(r.Config.Logger, &logDeprecatedEachPage,
"Request.EachPage deprecated. Use Pagination type for configurable pagination of API operations")
for page := r; page != nil; page = page.NextPage() { for page := r; page != nil; page = page.NextPage() {
if err := page.Send(); err != nil { if err := page.Send(); err != nil {
return err return err

View File

@ -26,8 +26,9 @@ func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config {
// retryableCodes is a collection of service response codes which are retry-able // retryableCodes is a collection of service response codes which are retry-able
// without any further action. // without any further action.
var retryableCodes = map[string]struct{}{ var retryableCodes = map[string]struct{}{
"RequestError": {}, "RequestError": {},
"RequestTimeout": {}, "RequestTimeout": {},
"RequestTimeoutException": {}, // Glacier's flavor of RequestTimeout
} }
var throttleCodes = map[string]struct{}{ var throttleCodes = map[string]struct{}{

293
vendor/github.com/aws/aws-sdk-go/aws/request/waiter.go generated vendored Normal file
View File

@ -0,0 +1,293 @@
package request
import (
"fmt"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
)
// WaiterResourceNotReadyErrorCode is the error code returned by a waiter when
// the waiter's max attempts have been exhausted.
const WaiterResourceNotReadyErrorCode = "ResourceNotReady"
// A WaiterOption is a function that will update the Waiter value's fields to
// configure the waiter.
type WaiterOption func(*Waiter)
// WithWaiterMaxAttempts returns the maximum number of times the waiter should
// attempt to check the resource for the target state.
func WithWaiterMaxAttempts(max int) WaiterOption {
return func(w *Waiter) {
w.MaxAttempts = max
}
}
// WaiterDelay will return a delay the waiter should pause between attempts to
// check the resource state. The passed in attempt is the number of times the
// Waiter has checked the resource state.
//
// Attempt is the number of attempts the Waiter has made checking the resource
// state.
type WaiterDelay func(attempt int) time.Duration
// ConstantWaiterDelay returns a WaiterDelay that will always return a constant
// delay the waiter should use between attempts. It ignores the number of
// attempts made.
func ConstantWaiterDelay(delay time.Duration) WaiterDelay {
return func(attempt int) time.Duration {
return delay
}
}
// WithWaiterDelay will set the Waiter to use the WaiterDelay passed in.
func WithWaiterDelay(delayer WaiterDelay) WaiterOption {
return func(w *Waiter) {
w.Delay = delayer
}
}
// WithWaiterLogger returns a waiter option to set the logger a waiter
// should use to log warnings and errors to.
func WithWaiterLogger(logger aws.Logger) WaiterOption {
return func(w *Waiter) {
w.Logger = logger
}
}
// WithWaiterRequestOptions returns a waiter option setting the request
// options for each request the waiter makes. Appends to waiter's request
// options already set.
func WithWaiterRequestOptions(opts ...Option) WaiterOption {
return func(w *Waiter) {
w.RequestOptions = append(w.RequestOptions, opts...)
}
}
// A Waiter provides the functionality to performing blocking call which will
// wait for an resource state to be satisfied a service.
//
// This type should not be used directly. The API operations provided in the
// service packages prefixed with "WaitUntil" should be used instead.
type Waiter struct {
Name string
Acceptors []WaiterAcceptor
Logger aws.Logger
MaxAttempts int
Delay WaiterDelay
RequestOptions []Option
NewRequest func([]Option) (*Request, error)
}
// ApplyOptions updates the waiter with the list of waiter options provided.
func (w *Waiter) ApplyOptions(opts ...WaiterOption) {
for _, fn := range opts {
fn(w)
}
}
// WaiterState are states the waiter uses based on WaiterAcceptor definitions
// to identify if the resource state the waiter is waiting on has occurred.
type WaiterState int
// String returns the string representation of the waiter state.
func (s WaiterState) String() string {
switch s {
case SuccessWaiterState:
return "success"
case FailureWaiterState:
return "failure"
case RetryWaiterState:
return "retry"
default:
return "unknown waiter state"
}
}
// States the waiter acceptors will use to identify target resource states.
const (
SuccessWaiterState WaiterState = iota // waiter successful
FailureWaiterState // waiter failed
RetryWaiterState // waiter needs to be retried
)
// WaiterMatchMode is the mode that the waiter will use to match the WaiterAcceptor
// definition's Expected attribute.
type WaiterMatchMode int
// Modes the waiter will use when inspecting API response to identify target
// resource states.
const (
PathAllWaiterMatch WaiterMatchMode = iota // match on all paths
PathWaiterMatch // match on specific path
PathAnyWaiterMatch // match on any path
PathListWaiterMatch // match on list of paths
StatusWaiterMatch // match on status code
ErrorWaiterMatch // match on error
)
// String returns the string representation of the waiter match mode.
func (m WaiterMatchMode) String() string {
switch m {
case PathAllWaiterMatch:
return "pathAll"
case PathWaiterMatch:
return "path"
case PathAnyWaiterMatch:
return "pathAny"
case PathListWaiterMatch:
return "pathList"
case StatusWaiterMatch:
return "status"
case ErrorWaiterMatch:
return "error"
default:
return "unknown waiter match mode"
}
}
// WaitWithContext will make requests for the API operation using NewRequest to
// build API requests. The request's response will be compared against the
// Waiter's Acceptors to determine the successful state of the resource the
// waiter is inspecting.
//
// The passed in context must not be nil. If it is nil a panic will occur. The
// Context will be used to cancel the waiter's pending requests and retry delays.
// Use aws.BackgroundContext if no context is available.
//
// The waiter will continue until the target state defined by the Acceptors,
// or the max attempts expires.
//
// Will return the WaiterResourceNotReadyErrorCode error code if the waiter's
// retryer ShouldRetry returns false. This normally will happen when the max
// wait attempts expires.
func (w Waiter) WaitWithContext(ctx aws.Context) error {
for attempt := 1; ; attempt++ {
req, err := w.NewRequest(w.RequestOptions)
if err != nil {
waiterLogf(w.Logger, "unable to create request %v", err)
return err
}
req.Handlers.Build.PushBack(MakeAddToUserAgentFreeFormHandler("Waiter"))
err = req.Send()
// See if any of the acceptors match the request's response, or error
for _, a := range w.Acceptors {
var matched bool
matched, err = a.match(w.Name, w.Logger, req, err)
if err != nil {
// Error occurred during current waiter call
return err
} else if matched {
// Match was found can stop here and return
return nil
}
}
// The Waiter should only check the resource state MaxAttempts times
// This is here instead of in the for loop above to prevent delaying
// unnecessary when the waiter will not retry.
if attempt == w.MaxAttempts {
break
}
// Delay to wait before inspecting the resource again
delay := w.Delay(attempt)
if sleepFn := req.Config.SleepDelay; sleepFn != nil {
// Support SleepDelay for backwards compatibility and testing
sleepFn(delay)
} else if err := aws.SleepWithContext(ctx, delay); err != nil {
return awserr.New(CanceledErrorCode, "waiter context canceled", err)
}
}
return awserr.New(WaiterResourceNotReadyErrorCode, "exceeded wait attempts", nil)
}
// A WaiterAcceptor provides the information needed to wait for an API operation
// to complete.
type WaiterAcceptor struct {
State WaiterState
Matcher WaiterMatchMode
Argument string
Expected interface{}
}
// match returns if the acceptor found a match with the passed in request
// or error. True is returned if the acceptor made a match, error is returned
// if there was an error attempting to perform the match.
func (a *WaiterAcceptor) match(name string, l aws.Logger, req *Request, err error) (bool, error) {
result := false
var vals []interface{}
switch a.Matcher {
case PathAllWaiterMatch, PathWaiterMatch:
// Require all matches to be equal for result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
if len(vals) == 0 {
break
}
result = true
for _, val := range vals {
if !awsutil.DeepEqual(val, a.Expected) {
result = false
break
}
}
case PathAnyWaiterMatch:
// Only a single match needs to equal for the result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
for _, val := range vals {
if awsutil.DeepEqual(val, a.Expected) {
result = true
break
}
}
case PathListWaiterMatch:
// ignored matcher
case StatusWaiterMatch:
s := a.Expected.(int)
result = s == req.HTTPResponse.StatusCode
case ErrorWaiterMatch:
if aerr, ok := err.(awserr.Error); ok {
result = aerr.Code() == a.Expected.(string)
}
default:
waiterLogf(l, "WARNING: Waiter %s encountered unexpected matcher: %s",
name, a.Matcher)
}
if !result {
// If there was no matching result found there is nothing more to do
// for this response, retry the request.
return false, nil
}
switch a.State {
case SuccessWaiterState:
// waiter completed
return true, nil
case FailureWaiterState:
// Waiter failure state triggered
return false, awserr.New("ResourceNotReady",
"failed waiting for successful resource state", err)
case RetryWaiterState:
// clear the error and retry the operation
return false, nil
default:
waiterLogf(l, "WARNING: Waiter %s encountered unexpected state: %s",
name, a.State)
return false, nil
}
}
func waiterLogf(logger aws.Logger, msg string, args ...interface{}) {
if logger != nil {
logger.Log(fmt.Sprintf(msg, args...))
}
}

View File

@ -0,0 +1,7 @@
package v4
// WithUnsignedPayload will enable and set the UnsignedPayload field to
// true of the signer.
func WithUnsignedPayload(v4 *Signer) {
v4.UnsignedPayload = true
}

View File

@ -194,6 +194,10 @@ type Signer struct {
// This value should only be used for testing. If it is nil the default // This value should only be used for testing. If it is nil the default
// time.Now will be used. // time.Now will be used.
currentTimeFn func() time.Time currentTimeFn func() time.Time
// UnsignedPayload will prevent signing of the payload. This will only
// work for services that have support for this.
UnsignedPayload bool
} }
// NewSigner returns a Signer pointer configured with the credentials and optional // NewSigner returns a Signer pointer configured with the credentials and optional
@ -227,6 +231,7 @@ type signingCtx struct {
isPresign bool isPresign bool
formattedTime string formattedTime string
formattedShortTime string formattedShortTime string
unsignedPayload bool
bodyDigest string bodyDigest string
signedHeaders string signedHeaders string
@ -317,6 +322,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
ServiceName: service, ServiceName: service,
Region: region, Region: region,
DisableURIPathEscaping: v4.DisableURIPathEscaping, DisableURIPathEscaping: v4.DisableURIPathEscaping,
unsignedPayload: v4.UnsignedPayload,
} }
for key := range ctx.Query { for key := range ctx.Query {
@ -409,7 +415,18 @@ var SignRequestHandler = request.NamedHandler{
func SignSDKRequest(req *request.Request) { func SignSDKRequest(req *request.Request) {
signSDKRequestWithCurrTime(req, time.Now) signSDKRequestWithCurrTime(req, time.Now)
} }
func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time) {
// BuildNamedHandler will build a generic handler for signing.
func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler {
return request.NamedHandler{
Name: name,
Fn: func(req *request.Request) {
signSDKRequestWithCurrTime(req, time.Now, opts...)
},
}
}
func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) {
// If the request does not need to be signed ignore the signing of the // If the request does not need to be signed ignore the signing of the
// request if the AnonymousCredentials object is used. // request if the AnonymousCredentials object is used.
if req.Config.Credentials == credentials.AnonymousCredentials { if req.Config.Credentials == credentials.AnonymousCredentials {
@ -441,6 +458,10 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time
v4.DisableRequestBodyOverwrite = true v4.DisableRequestBodyOverwrite = true
}) })
for _, opt := range opts {
opt(v4)
}
signingTime := req.Time signingTime := req.Time
if !req.LastSignedAt.IsZero() { if !req.LastSignedAt.IsZero() {
signingTime = req.LastSignedAt signingTime = req.LastSignedAt
@ -634,14 +655,14 @@ func (ctx *signingCtx) buildSignature() {
func (ctx *signingCtx) buildBodyDigest() { func (ctx *signingCtx) buildBodyDigest() {
hash := ctx.Request.Header.Get("X-Amz-Content-Sha256") hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
if hash == "" { if hash == "" {
if ctx.isPresign && ctx.ServiceName == "s3" { if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
hash = "UNSIGNED-PAYLOAD" hash = "UNSIGNED-PAYLOAD"
} else if ctx.Body == nil { } else if ctx.Body == nil {
hash = emptyStringSHA256 hash = emptyStringSHA256
} else { } else {
hash = hex.EncodeToString(makeSha256Reader(ctx.Body)) hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
} }
if ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" { if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
ctx.Request.Header.Set("X-Amz-Content-Sha256", hash) ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
} }
} }

View File

@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go" const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK // SDKVersion is the version of this SDK
const SDKVersion = "1.7.9" const SDKVersion = "1.8.8"

View File

@ -4,6 +4,7 @@ package rest
import ( import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -82,8 +83,12 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo
if name == "" { if name == "" {
name = field.Name name = field.Name
} }
if m.Kind() == reflect.Ptr { if kind := m.Kind(); kind == reflect.Ptr {
m = m.Elem() m = m.Elem()
} else if kind == reflect.Interface {
if !m.Elem().IsValid() {
continue
}
} }
if !m.IsValid() { if !m.IsValid() {
continue continue
@ -95,16 +100,16 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo
var err error var err error
switch field.Tag.Get("location") { switch field.Tag.Get("location") {
case "headers": // header maps case "headers": // header maps
err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag.Get("locationName")) err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag)
case "header": case "header":
err = buildHeader(&r.HTTPRequest.Header, m, name) err = buildHeader(&r.HTTPRequest.Header, m, name, field.Tag)
case "uri": case "uri":
err = buildURI(r.HTTPRequest.URL, m, name) err = buildURI(r.HTTPRequest.URL, m, name, field.Tag)
case "querystring": case "querystring":
err = buildQueryString(query, m, name) err = buildQueryString(query, m, name, field.Tag)
default: default:
if buildGETQuery { if buildGETQuery {
err = buildQueryString(query, m, name) err = buildQueryString(query, m, name, field.Tag)
} }
} }
r.Error = err r.Error = err
@ -145,8 +150,8 @@ func buildBody(r *request.Request, v reflect.Value) {
} }
} }
func buildHeader(header *http.Header, v reflect.Value, name string) error { func buildHeader(header *http.Header, v reflect.Value, name string, tag reflect.StructTag) error {
str, err := convertType(v) str, err := convertType(v, tag)
if err == errValueNotSet { if err == errValueNotSet {
return nil return nil
} else if err != nil { } else if err != nil {
@ -158,9 +163,10 @@ func buildHeader(header *http.Header, v reflect.Value, name string) error {
return nil return nil
} }
func buildHeaderMap(header *http.Header, v reflect.Value, prefix string) error { func buildHeaderMap(header *http.Header, v reflect.Value, tag reflect.StructTag) error {
prefix := tag.Get("locationName")
for _, key := range v.MapKeys() { for _, key := range v.MapKeys() {
str, err := convertType(v.MapIndex(key)) str, err := convertType(v.MapIndex(key), tag)
if err == errValueNotSet { if err == errValueNotSet {
continue continue
} else if err != nil { } else if err != nil {
@ -173,8 +179,8 @@ func buildHeaderMap(header *http.Header, v reflect.Value, prefix string) error {
return nil return nil
} }
func buildURI(u *url.URL, v reflect.Value, name string) error { func buildURI(u *url.URL, v reflect.Value, name string, tag reflect.StructTag) error {
value, err := convertType(v) value, err := convertType(v, tag)
if err == errValueNotSet { if err == errValueNotSet {
return nil return nil
} else if err != nil { } else if err != nil {
@ -190,7 +196,7 @@ func buildURI(u *url.URL, v reflect.Value, name string) error {
return nil return nil
} }
func buildQueryString(query url.Values, v reflect.Value, name string) error { func buildQueryString(query url.Values, v reflect.Value, name string, tag reflect.StructTag) error {
switch value := v.Interface().(type) { switch value := v.Interface().(type) {
case []*string: case []*string:
for _, item := range value { for _, item := range value {
@ -207,7 +213,7 @@ func buildQueryString(query url.Values, v reflect.Value, name string) error {
} }
} }
default: default:
str, err := convertType(v) str, err := convertType(v, tag)
if err == errValueNotSet { if err == errValueNotSet {
return nil return nil
} else if err != nil { } else if err != nil {
@ -246,7 +252,7 @@ func EscapePath(path string, encodeSep bool) string {
return buf.String() return buf.String()
} }
func convertType(v reflect.Value) (string, error) { func convertType(v reflect.Value, tag reflect.StructTag) (string, error) {
v = reflect.Indirect(v) v = reflect.Indirect(v)
if !v.IsValid() { if !v.IsValid() {
return "", errValueNotSet return "", errValueNotSet
@ -266,6 +272,16 @@ func convertType(v reflect.Value) (string, error) {
str = strconv.FormatFloat(value, 'f', -1, 64) str = strconv.FormatFloat(value, 'f', -1, 64)
case time.Time: case time.Time:
str = value.UTC().Format(RFC822) str = value.UTC().Format(RFC822)
case aws.JSONValue:
b, err := json.Marshal(value)
if err != nil {
return "", err
}
if tag.Get("location") == "header" {
str = base64.StdEncoding.EncodeToString(b)
} else {
str = string(b)
}
default: default:
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type()) err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
return "", err return "", err

View File

@ -3,6 +3,7 @@ package rest
import ( import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -12,6 +13,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
) )
@ -111,7 +113,7 @@ func unmarshalLocationElements(r *request.Request, v reflect.Value) {
case "statusCode": case "statusCode":
unmarshalStatusCode(m, r.HTTPResponse.StatusCode) unmarshalStatusCode(m, r.HTTPResponse.StatusCode)
case "header": case "header":
err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name)) err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name), field.Tag)
if err != nil { if err != nil {
r.Error = awserr.New("SerializationError", "failed to decode REST response", err) r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
break break
@ -158,8 +160,13 @@ func unmarshalHeaderMap(r reflect.Value, headers http.Header, prefix string) err
return nil return nil
} }
func unmarshalHeader(v reflect.Value, header string) error { func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) error {
if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) { isJSONValue := tag.Get("type") == "jsonvalue"
if isJSONValue {
if len(header) == 0 {
return nil
}
} else if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) {
return nil return nil
} }
@ -196,6 +203,22 @@ func unmarshalHeader(v reflect.Value, header string) error {
return err return err
} }
v.Set(reflect.ValueOf(&t)) v.Set(reflect.ValueOf(&t))
case aws.JSONValue:
b := []byte(header)
var err error
if tag.Get("location") == "header" {
b, err = base64.StdEncoding.DecodeString(header)
if err != nil {
return err
}
}
m := aws.JSONValue{}
err = json.Unmarshal(b, &m)
if err != nil {
return err
}
v.Set(reflect.ValueOf(m))
default: default:
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type()) err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
return err return err

View File

@ -1,134 +0,0 @@
package waiter
import (
"fmt"
"reflect"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/request"
)
// A Config provides a collection of configuration values to setup a generated
// waiter code with.
type Config struct {
Name string
Delay int
MaxAttempts int
Operation string
Acceptors []WaitAcceptor
}
// A WaitAcceptor provides the information needed to wait for an API operation
// to complete.
type WaitAcceptor struct {
Expected interface{}
Matcher string
State string
Argument string
}
// A Waiter provides waiting for an operation to complete.
type Waiter struct {
Config
Client interface{}
Input interface{}
}
// Wait waits for an operation to complete, expire max attempts, or fail. Error
// is returned if the operation fails.
func (w *Waiter) Wait() error {
client := reflect.ValueOf(w.Client)
in := reflect.ValueOf(w.Input)
method := client.MethodByName(w.Config.Operation + "Request")
for i := 0; i < w.MaxAttempts; i++ {
res := method.Call([]reflect.Value{in})
req := res[0].Interface().(*request.Request)
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Waiter"))
err := req.Send()
for _, a := range w.Acceptors {
result := false
var vals []interface{}
switch a.Matcher {
case "pathAll", "path":
// Require all matches to be equal for result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
if len(vals) == 0 {
break
}
result = true
for _, val := range vals {
if !awsutil.DeepEqual(val, a.Expected) {
result = false
break
}
}
case "pathAny":
// Only a single match needs to equal for the result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
for _, val := range vals {
if awsutil.DeepEqual(val, a.Expected) {
result = true
break
}
}
case "status":
s := a.Expected.(int)
result = s == req.HTTPResponse.StatusCode
case "error":
if aerr, ok := err.(awserr.Error); ok {
result = aerr.Code() == a.Expected.(string)
}
case "pathList":
// ignored matcher
default:
logf(client, "WARNING: Waiter for %s encountered unexpected matcher: %s",
w.Config.Operation, a.Matcher)
}
if !result {
// If there was no matching result found there is nothing more to do
// for this response, retry the request.
continue
}
switch a.State {
case "success":
// waiter completed
return nil
case "failure":
// Waiter failure state triggered
return awserr.New("ResourceNotReady",
fmt.Sprintf("failed waiting for successful resource state"), err)
case "retry":
// clear the error and retry the operation
err = nil
default:
logf(client, "WARNING: Waiter for %s encountered unexpected state: %s",
w.Config.Operation, a.State)
}
}
if err != nil {
return err
}
time.Sleep(time.Second * time.Duration(w.Delay))
}
return awserr.New("ResourceNotReady",
fmt.Sprintf("exceeded %d wait attempts", w.MaxAttempts), nil)
}
func logf(client reflect.Value, msg string, args ...interface{}) {
cfgVal := client.FieldByName("Config")
if !cfgVal.IsValid() {
return
}
if cfg, ok := cfgVal.Interface().(*aws.Config); ok && cfg.Logger != nil {
cfg.Logger.Log(fmt.Sprintf(msg, args...))
}
}

View File

@ -1,4 +1,4 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. // Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package acm provides a client for AWS Certificate Manager. // Package acm provides a client for AWS Certificate Manager.
package acm package acm
@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/protocol" "github.com/aws/aws-sdk-go/private/protocol"
@ -103,8 +104,23 @@ func (c *ACM) AddTagsToCertificateRequest(input *AddTagsToCertificateInput) (req
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/AddTagsToCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/AddTagsToCertificate
func (c *ACM) AddTagsToCertificate(input *AddTagsToCertificateInput) (*AddTagsToCertificateOutput, error) { func (c *ACM) AddTagsToCertificate(input *AddTagsToCertificateInput) (*AddTagsToCertificateOutput, error) {
req, out := c.AddTagsToCertificateRequest(input) req, out := c.AddTagsToCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// AddTagsToCertificateWithContext is the same as AddTagsToCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See AddTagsToCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) AddTagsToCertificateWithContext(ctx aws.Context, input *AddTagsToCertificateInput, opts ...request.Option) (*AddTagsToCertificateOutput, error) {
req, out := c.AddTagsToCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opDeleteCertificate = "DeleteCertificate" const opDeleteCertificate = "DeleteCertificate"
@ -186,8 +202,23 @@ func (c *ACM) DeleteCertificateRequest(input *DeleteCertificateInput) (req *requ
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/DeleteCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/DeleteCertificate
func (c *ACM) DeleteCertificate(input *DeleteCertificateInput) (*DeleteCertificateOutput, error) { func (c *ACM) DeleteCertificate(input *DeleteCertificateInput) (*DeleteCertificateOutput, error) {
req, out := c.DeleteCertificateRequest(input) req, out := c.DeleteCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// DeleteCertificateWithContext is the same as DeleteCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See DeleteCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) DeleteCertificateWithContext(ctx aws.Context, input *DeleteCertificateInput, opts ...request.Option) (*DeleteCertificateOutput, error) {
req, out := c.DeleteCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opDescribeCertificate = "DescribeCertificate" const opDescribeCertificate = "DescribeCertificate"
@ -255,8 +286,23 @@ func (c *ACM) DescribeCertificateRequest(input *DescribeCertificateInput) (req *
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/DescribeCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/DescribeCertificate
func (c *ACM) DescribeCertificate(input *DescribeCertificateInput) (*DescribeCertificateOutput, error) { func (c *ACM) DescribeCertificate(input *DescribeCertificateInput) (*DescribeCertificateOutput, error) {
req, out := c.DescribeCertificateRequest(input) req, out := c.DescribeCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// DescribeCertificateWithContext is the same as DescribeCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See DescribeCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) DescribeCertificateWithContext(ctx aws.Context, input *DescribeCertificateInput, opts ...request.Option) (*DescribeCertificateOutput, error) {
req, out := c.DescribeCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opGetCertificate = "GetCertificate" const opGetCertificate = "GetCertificate"
@ -336,8 +382,23 @@ func (c *ACM) GetCertificateRequest(input *GetCertificateInput) (req *request.Re
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/GetCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/GetCertificate
func (c *ACM) GetCertificate(input *GetCertificateInput) (*GetCertificateOutput, error) { func (c *ACM) GetCertificate(input *GetCertificateInput) (*GetCertificateOutput, error) {
req, out := c.GetCertificateRequest(input) req, out := c.GetCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// GetCertificateWithContext is the same as GetCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See GetCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) GetCertificateWithContext(ctx aws.Context, input *GetCertificateInput, opts ...request.Option) (*GetCertificateOutput, error) {
req, out := c.GetCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opImportCertificate = "ImportCertificate" const opImportCertificate = "ImportCertificate"
@ -434,8 +495,23 @@ func (c *ACM) ImportCertificateRequest(input *ImportCertificateInput) (req *requ
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ImportCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ImportCertificate
func (c *ACM) ImportCertificate(input *ImportCertificateInput) (*ImportCertificateOutput, error) { func (c *ACM) ImportCertificate(input *ImportCertificateInput) (*ImportCertificateOutput, error) {
req, out := c.ImportCertificateRequest(input) req, out := c.ImportCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// ImportCertificateWithContext is the same as ImportCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See ImportCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) ImportCertificateWithContext(ctx aws.Context, input *ImportCertificateInput, opts ...request.Option) (*ImportCertificateOutput, error) {
req, out := c.ImportCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opListCertificates = "ListCertificates" const opListCertificates = "ListCertificates"
@ -502,8 +578,23 @@ func (c *ACM) ListCertificatesRequest(input *ListCertificatesInput) (req *reques
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ListCertificates // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ListCertificates
func (c *ACM) ListCertificates(input *ListCertificatesInput) (*ListCertificatesOutput, error) { func (c *ACM) ListCertificates(input *ListCertificatesInput) (*ListCertificatesOutput, error) {
req, out := c.ListCertificatesRequest(input) req, out := c.ListCertificatesRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// ListCertificatesWithContext is the same as ListCertificates with the addition of
// the ability to pass a context and additional request options.
//
// See ListCertificates for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) ListCertificatesWithContext(ctx aws.Context, input *ListCertificatesInput, opts ...request.Option) (*ListCertificatesOutput, error) {
req, out := c.ListCertificatesRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
// ListCertificatesPages iterates over the pages of a ListCertificates operation, // ListCertificatesPages iterates over the pages of a ListCertificates operation,
@ -523,12 +614,37 @@ func (c *ACM) ListCertificates(input *ListCertificatesInput) (*ListCertificatesO
// return pageNum <= 3 // return pageNum <= 3
// }) // })
// //
func (c *ACM) ListCertificatesPages(input *ListCertificatesInput, fn func(p *ListCertificatesOutput, lastPage bool) (shouldContinue bool)) error { func (c *ACM) ListCertificatesPages(input *ListCertificatesInput, fn func(*ListCertificatesOutput, bool) bool) error {
page, _ := c.ListCertificatesRequest(input) return c.ListCertificatesPagesWithContext(aws.BackgroundContext(), input, fn)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator")) }
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*ListCertificatesOutput), lastPage) // ListCertificatesPagesWithContext same as ListCertificatesPages except
}) // it takes a Context and allows setting request options on the pages.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) ListCertificatesPagesWithContext(ctx aws.Context, input *ListCertificatesInput, fn func(*ListCertificatesOutput, bool) bool, opts ...request.Option) error {
p := request.Pagination{
NewRequest: func() (*request.Request, error) {
var inCpy *ListCertificatesInput
if input != nil {
tmp := *input
inCpy = &tmp
}
req, _ := c.ListCertificatesRequest(inCpy)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return req, nil
},
}
cont := true
for p.Next() && cont {
cont = fn(p.Page().(*ListCertificatesOutput), !p.HasNextPage())
}
return p.Err()
} }
const opListTagsForCertificate = "ListTagsForCertificate" const opListTagsForCertificate = "ListTagsForCertificate"
@ -599,8 +715,23 @@ func (c *ACM) ListTagsForCertificateRequest(input *ListTagsForCertificateInput)
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ListTagsForCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ListTagsForCertificate
func (c *ACM) ListTagsForCertificate(input *ListTagsForCertificateInput) (*ListTagsForCertificateOutput, error) { func (c *ACM) ListTagsForCertificate(input *ListTagsForCertificateInput) (*ListTagsForCertificateOutput, error) {
req, out := c.ListTagsForCertificateRequest(input) req, out := c.ListTagsForCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// ListTagsForCertificateWithContext is the same as ListTagsForCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See ListTagsForCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) ListTagsForCertificateWithContext(ctx aws.Context, input *ListTagsForCertificateInput, opts ...request.Option) (*ListTagsForCertificateOutput, error) {
req, out := c.ListTagsForCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opRemoveTagsFromCertificate = "RemoveTagsFromCertificate" const opRemoveTagsFromCertificate = "RemoveTagsFromCertificate"
@ -681,8 +812,23 @@ func (c *ACM) RemoveTagsFromCertificateRequest(input *RemoveTagsFromCertificateI
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/RemoveTagsFromCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/RemoveTagsFromCertificate
func (c *ACM) RemoveTagsFromCertificate(input *RemoveTagsFromCertificateInput) (*RemoveTagsFromCertificateOutput, error) { func (c *ACM) RemoveTagsFromCertificate(input *RemoveTagsFromCertificateInput) (*RemoveTagsFromCertificateOutput, error) {
req, out := c.RemoveTagsFromCertificateRequest(input) req, out := c.RemoveTagsFromCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// RemoveTagsFromCertificateWithContext is the same as RemoveTagsFromCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See RemoveTagsFromCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) RemoveTagsFromCertificateWithContext(ctx aws.Context, input *RemoveTagsFromCertificateInput, opts ...request.Option) (*RemoveTagsFromCertificateOutput, error) {
req, out := c.RemoveTagsFromCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opRequestCertificate = "RequestCertificate" const opRequestCertificate = "RequestCertificate"
@ -759,8 +905,23 @@ func (c *ACM) RequestCertificateRequest(input *RequestCertificateInput) (req *re
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/RequestCertificate // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/RequestCertificate
func (c *ACM) RequestCertificate(input *RequestCertificateInput) (*RequestCertificateOutput, error) { func (c *ACM) RequestCertificate(input *RequestCertificateInput) (*RequestCertificateOutput, error) {
req, out := c.RequestCertificateRequest(input) req, out := c.RequestCertificateRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// RequestCertificateWithContext is the same as RequestCertificate with the addition of
// the ability to pass a context and additional request options.
//
// See RequestCertificate for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) RequestCertificateWithContext(ctx aws.Context, input *RequestCertificateInput, opts ...request.Option) (*RequestCertificateOutput, error) {
req, out := c.RequestCertificateRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
const opResendValidationEmail = "ResendValidationEmail" const opResendValidationEmail = "ResendValidationEmail"
@ -847,8 +1008,23 @@ func (c *ACM) ResendValidationEmailRequest(input *ResendValidationEmailInput) (r
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ResendValidationEmail // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/ResendValidationEmail
func (c *ACM) ResendValidationEmail(input *ResendValidationEmailInput) (*ResendValidationEmailOutput, error) { func (c *ACM) ResendValidationEmail(input *ResendValidationEmailInput) (*ResendValidationEmailOutput, error) {
req, out := c.ResendValidationEmailRequest(input) req, out := c.ResendValidationEmailRequest(input)
err := req.Send() return out, req.Send()
return out, err }
// ResendValidationEmailWithContext is the same as ResendValidationEmail with the addition of
// the ability to pass a context and additional request options.
//
// See ResendValidationEmail for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *ACM) ResendValidationEmailWithContext(ctx aws.Context, input *ResendValidationEmailInput, opts ...request.Option) (*ResendValidationEmailOutput, error) {
req, out := c.ResendValidationEmailRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
} }
// Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/AddTagsToCertificateRequest // Please also see https://docs.aws.amazon.com/goto/WebAPI/acm-2015-12-08/AddTagsToCertificateRequest

View File

@ -1,4 +1,4 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. // Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package acm package acm

View File

@ -1,4 +1,4 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. // Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package acm package acm

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. // Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package apigateway package apigateway

View File

@ -1,4 +1,4 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. // Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package apigateway package apigateway

Some files were not shown because too many files have changed in this diff Show More