Merge branch 'master' into f-oracle-merge
This commit is contained in:
commit
3a084b061a
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -5,6 +5,7 @@ FEATURES:
|
|||
* **New Resource:** `aws_lightsail_static_ip` [GH-13175]
|
||||
* **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207]
|
||||
* **New Resource:** `aws_ses_domain_identity` [GH-13098]
|
||||
* **New Resource**: `azurerm_managed_disk` [GH-12455]
|
||||
* **New Resource:** `kubernetes_secret` [GH-12960]
|
||||
* **New Data Source:** `aws_iam_role` [GH-13213]
|
||||
|
||||
|
@ -31,15 +32,22 @@ IMPROVEMENTS:
|
|||
* 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: 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/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]
|
||||
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* 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 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: Increase timeout for AMI registration [GH-13159]
|
||||
* 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: Increase timeout for deploying `cloudfront_distribution` from 40 to 70 mins [GH-13319]
|
||||
* 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: Fix KMS Key reading with Exists method [GH-13348]
|
||||
* 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]
|
||||
|
||||
## 0.9.2 (March 28, 2017)
|
||||
|
|
|
@ -121,16 +121,15 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
|
|||
default:
|
||||
if c.lockCh != nil {
|
||||
// we have an active lock already
|
||||
return "", nil
|
||||
return "", fmt.Errorf("state %q already locked", c.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if c.consulLock == nil {
|
||||
opts := &consulapi.LockOptions{
|
||||
Key: c.Path + lockSuffix,
|
||||
// We currently don't procide any options to block terraform and
|
||||
// retry lock acquisition, but we can wait briefly in case the
|
||||
// lock is about to be freed.
|
||||
// only wait briefly, so terraform has the choice to fail fast or
|
||||
// retry as needed.
|
||||
LockWaitTime: time.Second,
|
||||
LockTryOnce: true,
|
||||
}
|
||||
|
@ -191,6 +190,10 @@ func (c *RemoteClient) Unlock(id string) error {
|
|||
err := c.consulLock.Unlock()
|
||||
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()
|
||||
_, delErr := kv.Delete(c.Path+lockInfoSuffix, nil)
|
||||
if delErr != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,9 @@ package s3
|
|||
|
||||
import (
|
||||
"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/s3"
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"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.lockTable = data.Get("lock_table").(string)
|
||||
|
||||
var errs []error
|
||||
creds, err := terraformAWS.GetCredentials(&terraformAWS.Config{
|
||||
cfg := &terraformAWS.Config{
|
||||
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),
|
||||
AssumeRoleSessionName: data.Get("session_name").(string),
|
||||
AssumeRoleExternalID: data.Get("external_id").(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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// Call Get to check for credential provider. If nothing found, we'll get an
|
||||
// error, and we can present it nicely to the user
|
||||
_, 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)
|
||||
b.s3Client = client.(*terraformAWS.AWSClient).S3()
|
||||
b.dynClient = client.(*terraformAWS.AWSClient).DynamoDB()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,16 +29,12 @@ func TestBackend_impl(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBackendConfig(t *testing.T) {
|
||||
// This test just instantiates the client. Shouldn't make any actual
|
||||
// requests nor incur any costs.
|
||||
|
||||
testACC(t)
|
||||
config := map[string]interface{}{
|
||||
"region": "us-west-1",
|
||||
"bucket": "tf-test",
|
||||
"key": "state",
|
||||
"encrypt": true,
|
||||
"access_key": "ACCESS_KEY",
|
||||
"secret_key": "SECRET_KEY",
|
||||
"lock_table": "dynamoTable",
|
||||
}
|
||||
|
||||
|
@ -58,11 +54,11 @@ func TestBackendConfig(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Error when requesting credentials")
|
||||
}
|
||||
if credentials.AccessKeyID != "ACCESS_KEY" {
|
||||
t.Fatalf("Incorrect Access Key Id was populated")
|
||||
if credentials.AccessKeyID == "" {
|
||||
t.Fatalf("No Access Key Id was populated")
|
||||
}
|
||||
if credentials.SecretAccessKey != "SECRET_KEY" {
|
||||
t.Fatalf("Incorrect Secret Access Key was populated")
|
||||
if credentials.SecretAccessKey == "" {
|
||||
t.Fatalf("No Secret Access Key was populated")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,8 +113,7 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
|
|||
return "", nil
|
||||
}
|
||||
|
||||
stateName := fmt.Sprintf("%s/%s", c.bucketName, c.path)
|
||||
info.Path = stateName
|
||||
info.Path = c.lockPath()
|
||||
|
||||
if info.ID == "" {
|
||||
lockID, err := uuid.GenerateUUID()
|
||||
|
@ -127,7 +126,7 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
|
|||
|
||||
putParams := &dynamodb.PutItemInput{
|
||||
Item: map[string]*dynamodb.AttributeValue{
|
||||
"LockID": {S: aws.String(stateName)},
|
||||
"LockID": {S: aws.String(c.lockPath())},
|
||||
"Info": {S: aws.String(string(info.Marshal()))},
|
||||
},
|
||||
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) {
|
||||
getParams := &dynamodb.GetItemInput{
|
||||
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"),
|
||||
TableName: aws.String(c.lockTable),
|
||||
|
@ -202,7 +201,7 @@ func (c *RemoteClient) Unlock(id string) error {
|
|||
|
||||
params := &dynamodb.DeleteItemInput{
|
||||
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),
|
||||
}
|
||||
|
@ -214,3 +213,7 @@ func (c *RemoteClient) Unlock(id string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RemoteClient) lockPath() string {
|
||||
return fmt.Sprintf("%s/%s", c.bucketName, c.path)
|
||||
}
|
||||
|
|
|
@ -160,6 +160,14 @@ type AWSClient struct {
|
|||
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
|
||||
func (c *Config) Client() (interface{}, error) {
|
||||
// Get the auth and region. This can fail if keys/regions were not
|
||||
|
|
|
@ -15,10 +15,10 @@ func TestAccAWSEcsDataSource_ecsTaskDefinition(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccCheckAwsEcsTaskDefinitionDataSourceConfig,
|
||||
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", "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.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "task_role_arn", regexp.MustCompile("^arn:aws:iam::[0-9]{12}:role/mongo_role$")),
|
||||
),
|
||||
|
|
|
@ -3,10 +3,12 @@ package aws
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSCloudWatchMetricAlarm_importBasic(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
resourceName := "aws_cloudwatch_metric_alarm.foobar"
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
|
@ -14,11 +16,11 @@ func TestAccAWSCloudWatchMetricAlarm_importBasic(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSCloudWatchMetricAlarmConfig,
|
||||
{
|
||||
Config: testAccAWSCloudWatchMetricAlarmConfig(rInt),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSElasticacheParameterGroup_importBasic(t *testing.T) {
|
||||
resourceName := "aws_elasticache_parameter_group.bar"
|
||||
rName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt())
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -15,7 +18,7 @@ func TestAccAWSElasticacheParameterGroup_importBasic(t *testing.T) {
|
|||
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSElasticacheParameterGroupConfig,
|
||||
Config: testAccAWSElasticacheParameterGroupConfig(rName),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
|
|
|
@ -446,7 +446,6 @@ func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) e
|
|||
d.Set("health_check_type", g.HealthCheckType)
|
||||
d.Set("launch_configuration", g.LaunchConfigurationName)
|
||||
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 {
|
||||
log.Printf("[WARN] Error setting suspended_processes for %q: %s", d.Id(), err)
|
||||
|
|
|
@ -445,15 +445,6 @@ func TestAccAWSAutoScalingGroup_ALB_TargetGroups(t *testing.T) {
|
|||
"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"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring string) string {
|
|||
return fmt.Sprintf(`
|
||||
resource "aws_cloudwatch_log_subscription_filter" "test_lambdafunction_logfilter" {
|
||||
name = "test_lambdafunction_logfilter_%s"
|
||||
log_group_name = "example_lambda_name"
|
||||
log_group_name = "${aws_cloudwatch_log_group.logs.name}"
|
||||
filter_pattern = "logtype test"
|
||||
destination_arn = "${aws_lambda_function.test_lambdafunction.arn}"
|
||||
}
|
||||
|
|
|
@ -8,14 +8,18 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/hashicorp/terraform/helper/validation"
|
||||
)
|
||||
|
||||
func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceAwsCloudWatchMetricAlarmCreate,
|
||||
Read: resourceAwsCloudWatchMetricAlarmRead,
|
||||
Update: resourceAwsCloudWatchMetricAlarmUpdate,
|
||||
Delete: resourceAwsCloudWatchMetricAlarmDelete,
|
||||
Create: resourceAwsCloudWatchMetricAlarmCreate,
|
||||
Read: resourceAwsCloudWatchMetricAlarmRead,
|
||||
Update: resourceAwsCloudWatchMetricAlarmUpdate,
|
||||
Delete: resourceAwsCloudWatchMetricAlarmDelete,
|
||||
SchemaVersion: 1,
|
||||
MigrateState: resourceAwsCloudWatchMetricAlarmMigrateState,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
@ -95,6 +99,18 @@ func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
|
|||
Optional: true,
|
||||
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("unit", a.Unit)
|
||||
d.Set("extended_statistic", a.ExtendedStatistic)
|
||||
d.Set("treat_missing_data", a.TreatMissingData)
|
||||
d.Set("evaluate_low_sample_count_percentiles", a.EvaluateLowSampleCountPercentile)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -214,6 +232,7 @@ func getAwsCloudWatchPutMetricAlarmInput(d *schema.ResourceData) cloudwatch.PutM
|
|||
Namespace: aws.String(d.Get("namespace").(string)),
|
||||
Period: aws.Int64(int64(d.Get("period").(int))),
|
||||
Threshold: aws.Float64(d.Get("threshold").(float64)),
|
||||
TreatMissingData: aws.String(d.Get("treat_missing_data").(string)),
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("evaluate_low_sample_count_percentiles"); ok {
|
||||
params.EvaluateLowSampleCountPercentile = aws.String(v.(string))
|
||||
}
|
||||
|
||||
var alarmActions []*string
|
||||
if v := d.Get("alarm_actions"); v != nil {
|
||||
for _, v := range v.(*schema.Set).List() {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,20 +7,21 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSCloudWatchMetricAlarm_basic(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: testAccAWSCloudWatchMetricAlarmConfig,
|
||||
Config: testAccAWSCloudWatchMetricAlarmConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
|
||||
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
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -42,7 +44,61 @@ func TestAccAWSCloudWatchMetricAlarm_extendedStatistic(t *testing.T) {
|
|||
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
|
||||
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(
|
||||
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
|
||||
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) {
|
||||
rInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
|
||||
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"),
|
||||
},
|
||||
},
|
||||
|
@ -133,9 +190,10 @@ func testAccCheckAWSCloudWatchMetricAlarmDestroy(s *terraform.State) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var testAccAWSCloudWatchMetricAlarmConfig = fmt.Sprintf(`
|
||||
func testAccAWSCloudWatchMetricAlarmConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_cloudwatch_metric_alarm" "foobar" {
|
||||
alarm_name = "terraform-test-foobar5"
|
||||
alarm_name = "terraform-test-foobar%d"
|
||||
comparison_operator = "GreaterThanOrEqualToThreshold"
|
||||
evaluation_periods = "2"
|
||||
metric_name = "CPUUtilization"
|
||||
|
@ -148,12 +206,93 @@ resource "aws_cloudwatch_metric_alarm" "foobar" {
|
|||
dimensions {
|
||||
InstanceId = "i-abc123"
|
||||
}
|
||||
}`, rInt)
|
||||
}
|
||||
`)
|
||||
|
||||
var testAccAWSCloudWatchMetricAlarmConfigExtendedStatistic = fmt.Sprintf(`
|
||||
func testAccAWSCloudWatchMetricAlarmConfigTreatMissingData(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
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"
|
||||
evaluation_periods = "2"
|
||||
metric_name = "CPUUtilization"
|
||||
|
@ -166,12 +305,13 @@ resource "aws_cloudwatch_metric_alarm" "foobar" {
|
|||
dimensions {
|
||||
InstanceId = "i-abc123"
|
||||
}
|
||||
}`, rInt)
|
||||
}
|
||||
`)
|
||||
|
||||
var testAccAWSCloudWatchMetricAlarmConfigMissingStatistic = fmt.Sprintf(`
|
||||
func testAccAWSCloudWatchMetricAlarmConfigMissingStatistic(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
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"
|
||||
|
@ -183,5 +323,5 @@ resource "aws_cloudwatch_metric_alarm" "foobar" {
|
|||
dimensions {
|
||||
InstanceId = "i-abc123"
|
||||
}
|
||||
}`, rInt)
|
||||
}
|
||||
`)
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/helper/validation"
|
||||
|
@ -168,6 +167,7 @@ func resourceAwsDmsEndpointRead(d *schema.ResourceData, meta interface{}) error
|
|||
})
|
||||
if err != nil {
|
||||
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
|
||||
log.Printf("[DEBUG] DMS Replication Endpoint %q Not Found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
@ -283,11 +283,6 @@ func resourceAwsDmsEndpointDelete(d *schema.ResourceData, meta interface{}) erro
|
|||
return err
|
||||
}
|
||||
|
||||
waitErr := waitForEndpointDelete(conn, d.Get("endpoint_id").(string), 30, 20)
|
||||
if waitErr != nil {
|
||||
return waitErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -310,36 +305,3 @@ func resourceAwsDmsEndpointSetState(d *schema.ResourceData, endpoint *dms.Endpoi
|
|||
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -67,11 +66,6 @@ func dmsEndpointDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
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 {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
@ -81,29 +75,26 @@ func checkDmsEndpointExistsWithProviders(n string, providers *[]*schema.Provider
|
|||
if rs.Primary.ID == "" {
|
||||
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
|
||||
_, err := conn.DescribeEndpoints(&dms.DescribeEndpointsInput{
|
||||
Filters: []*dms.Filter{
|
||||
{
|
||||
Name: aws.String("endpoint-id"),
|
||||
Values: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
conn := testAccProvider.Meta().(*AWSClient).dmsconn
|
||||
resp, err := conn.DescribeEndpoints(&dms.DescribeEndpointsInput{
|
||||
Filters: []*dms.Filter{
|
||||
{
|
||||
Name: aws.String("endpoint-id"),
|
||||
Values: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("DMS endpoint error: %v", err)
|
||||
}
|
||||
return nil
|
||||
if err != nil {
|
||||
return fmt.Errorf("DMS endpoint error: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("DMS endpoint not found")
|
||||
if resp.Endpoints == nil {
|
||||
return fmt.Errorf("DMS endpoint not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@ package aws
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
@ -174,12 +175,23 @@ func resourceAwsDmsReplicationInstanceCreate(d *schema.ResourceData, meta interf
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId(d.Get("replication_instance_id").(string))
|
||||
return resourceAwsDmsReplicationInstanceRead(d, meta)
|
||||
}
|
||||
|
||||
|
@ -196,6 +208,7 @@ func resourceAwsDmsReplicationInstanceRead(d *schema.ResourceData, meta interfac
|
|||
})
|
||||
if err != nil {
|
||||
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
|
||||
log.Printf("[DEBUG] DMS Replication Instance %q Not Found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
@ -287,6 +300,21 @@ func resourceAwsDmsReplicationInstanceUpdate(d *schema.ResourceData, meta interf
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -307,9 +335,19 @@ func resourceAwsDmsReplicationInstanceDelete(d *schema.ResourceData, meta interf
|
|||
return err
|
||||
}
|
||||
|
||||
waitErr := waitForInstanceDeleted(conn, d.Get("replication_instance_id").(string), 30, 20)
|
||||
if waitErr != nil {
|
||||
return waitErr
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"deleting"},
|
||||
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
|
||||
|
@ -355,68 +393,35 @@ func resourceAwsDmsReplicationInstanceSetState(d *schema.ResourceData, instance
|
|||
return nil
|
||||
}
|
||||
|
||||
func waitForInstanceCreated(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)},
|
||||
func resourceAwsDmsReplicationInstanceStateRefreshFunc(
|
||||
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
conn := meta.(*AWSClient).dmsconn
|
||||
|
||||
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{
|
||||
Operation: "DescribeReplicationInstances",
|
||||
Delay: delay,
|
||||
MaxAttempts: maxAttempts,
|
||||
Acceptors: []waiter.WaitAcceptor{
|
||||
{
|
||||
State: "success",
|
||||
Matcher: "pathAll",
|
||||
Argument: "ReplicationInstances[].ReplicationInstanceStatus",
|
||||
Expected: "available",
|
||||
},
|
||||
},
|
||||
}
|
||||
if v == nil {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
w := waiter.Waiter{
|
||||
Client: client,
|
||||
Input: input,
|
||||
Config: config,
|
||||
}
|
||||
if v.ReplicationInstances == nil {
|
||||
return nil, "", fmt.Errorf("Error on retrieving DMS Replication Instance when waiting for State")
|
||||
}
|
||||
|
||||
return w.Wait()
|
||||
}
|
||||
|
||||
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()
|
||||
return v, *v.ReplicationInstances[0].ReplicationInstanceStatus, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -47,11 +46,6 @@ func TestAccAwsDmsReplicationInstanceBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
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 {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
@ -61,29 +55,24 @@ func checkDmsReplicationInstanceExistsWithProviders(n string, providers *[]*sche
|
|||
if rs.Primary.ID == "" {
|
||||
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
|
||||
_, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
|
||||
Filters: []*dms.Filter{
|
||||
{
|
||||
Name: aws.String("replication-instance-id"),
|
||||
Values: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
conn := testAccProvider.Meta().(*AWSClient).dmsconn
|
||||
resp, err := conn.DescribeReplicationInstances(&dms.DescribeReplicationInstancesInput{
|
||||
Filters: []*dms.Filter{
|
||||
{
|
||||
Name: aws.String("replication-instance-id"),
|
||||
Values: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("DMS replication instance error: %v", err)
|
||||
}
|
||||
return nil
|
||||
if err != nil {
|
||||
return fmt.Errorf("DMS replication instance error: %v", err)
|
||||
}
|
||||
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 {
|
||||
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" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
tags {
|
||||
Name = "tf-test-dms-vpc-%[1]s"
|
||||
}
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
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_description = "terraform test for replication subnet group"
|
||||
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" {
|
||||
|
@ -168,22 +145,11 @@ resource "aws_dms_replication_instance" "dms_replication_instance" {
|
|||
|
||||
func dmsReplicationInstanceConfigUpdate(randId string) string {
|
||||
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" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
tags {
|
||||
Name = "tf-test-dms-vpc-%[1]s"
|
||||
}
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
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_description = "terraform test for replication subnet group"
|
||||
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" {
|
||||
|
|
|
@ -101,22 +101,11 @@ func dmsReplicationSubnetGroupDestroy(s *terraform.State) error {
|
|||
|
||||
func dmsReplicationSubnetGroupConfig(randId string) string {
|
||||
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" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
tags {
|
||||
Name = "tf-test-dms-vpc-%[1]s"
|
||||
}
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
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 {
|
||||
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" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
tags {
|
||||
Name = "tf-test-dms-vpc-%[1]s"
|
||||
}
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
resource "aws_subnet" "dms_subnet_1" {
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"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"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/helper/validation"
|
||||
)
|
||||
|
@ -121,13 +121,23 @@ func resourceAwsDmsReplicationTaskCreate(d *schema.ResourceData, meta interface{
|
|||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId(taskId)
|
||||
return resourceAwsDmsReplicationTaskRead(d, meta)
|
||||
}
|
||||
|
||||
|
@ -144,6 +154,7 @@ func resourceAwsDmsReplicationTaskRead(d *schema.ResourceData, meta interface{})
|
|||
})
|
||||
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
|
||||
}
|
||||
|
@ -213,7 +224,17 @@ func resourceAwsDmsReplicationTaskUpdate(d *schema.ResourceData, meta interface{
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -235,12 +256,27 @@ func resourceAwsDmsReplicationTaskDelete(d *schema.ResourceData, meta interface{
|
|||
|
||||
_, err := conn.DeleteReplicationTask(request)
|
||||
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
|
||||
}
|
||||
|
||||
waitErr := waitForTaskDeleted(conn, d.Get("replication_task_id").(string), 30, 10)
|
||||
if waitErr != nil {
|
||||
return waitErr
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"deleting"},
|
||||
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
|
||||
|
@ -261,119 +297,35 @@ func resourceAwsDmsReplicationTaskSetState(d *schema.ResourceData, task *dms.Rep
|
|||
return nil
|
||||
}
|
||||
|
||||
func waitForTaskCreated(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)},
|
||||
},
|
||||
},
|
||||
}
|
||||
func resourceAwsDmsReplicationTaskStateRefreshFunc(
|
||||
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
conn := meta.(*AWSClient).dmsconn
|
||||
|
||||
config := waiter.Config{
|
||||
Operation: "DescribeReplicationTasks",
|
||||
Delay: delay,
|
||||
MaxAttempts: maxAttempts,
|
||||
Acceptors: []waiter.WaitAcceptor{
|
||||
{
|
||||
State: "retry",
|
||||
Matcher: "pathAll",
|
||||
Argument: "ReplicationTasks[].Status",
|
||||
Expected: "creating",
|
||||
v, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{
|
||||
Filters: []*dms.Filter{
|
||||
{
|
||||
Name: aws.String("replication-task-id"),
|
||||
Values: []*string{aws.String(d.Id())}, // Must use d.Id() to work with import.
|
||||
},
|
||||
},
|
||||
{
|
||||
State: "success",
|
||||
Matcher: "pathAll",
|
||||
Argument: "ReplicationTasks[].Status",
|
||||
Expected: "ready",
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
if dmserr, ok := err.(awserr.Error); ok && dmserr.Code() == "ResourceNotFoundFault" {
|
||||
return nil, "", nil
|
||||
}
|
||||
log.Printf("Error on retrieving DMS Replication Task when waiting: %s", err)
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
w := waiter.Waiter{
|
||||
Client: client,
|
||||
Input: input,
|
||||
Config: config,
|
||||
}
|
||||
if v == nil {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
return w.Wait()
|
||||
}
|
||||
|
||||
func waitForTaskUpdated(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: "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()
|
||||
if v.ReplicationTasks != nil {
|
||||
log.Printf("[DEBUG] DMS Replication Task status for instance %s: %s", d.Id(), *v.ReplicationTasks[0].Status)
|
||||
}
|
||||
|
||||
return v, *v.ReplicationTasks[0].Status, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
dms "github.com/aws/aws-sdk-go/service/databasemigrationservice"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -44,11 +43,6 @@ func TestAccAwsDmsReplicationTaskBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
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 {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
@ -58,29 +52,25 @@ func checkDmsReplicationTaskExistsWithProviders(n string, providers *[]*schema.P
|
|||
if rs.Primary.ID == "" {
|
||||
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
|
||||
_, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{
|
||||
Filters: []*dms.Filter{
|
||||
{
|
||||
Name: aws.String("replication-task-id"),
|
||||
Values: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
conn := testAccProvider.Meta().(*AWSClient).dmsconn
|
||||
resp, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{
|
||||
Filters: []*dms.Filter{
|
||||
{
|
||||
Name: aws.String("replication-task-id"),
|
||||
Values: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("DMS replication subnet group error: %v", err)
|
||||
}
|
||||
return nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
err := checkDmsReplicationTaskExists(rs.Primary.ID)
|
||||
if err == nil {
|
||||
return fmt.Errorf("Found replication subnet group that was not destroyed: %s", rs.Primary.ID)
|
||||
conn := testAccProvider.Meta().(*AWSClient).dmsconn
|
||||
resp, err := conn.DescribeReplicationTasks(&dms.DescribeReplicationTasksInput{
|
||||
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 {
|
||||
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" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
tags {
|
||||
Name = "tf-test-dms-vpc-%[1]s"
|
||||
}
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
resource "aws_subnet" "dms_subnet_1" {
|
||||
|
@ -148,7 +140,6 @@ resource "aws_dms_endpoint" "dms_endpoint_source" {
|
|||
port = 3306
|
||||
username = "tftest"
|
||||
password = "tftest"
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
resource "aws_dms_endpoint" "dms_endpoint_target" {
|
||||
|
@ -160,14 +151,12 @@ resource "aws_dms_endpoint" "dms_endpoint_target" {
|
|||
port = 3306
|
||||
username = "tftest"
|
||||
password = "tftest"
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
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_description = "terraform test for replication subnet group"
|
||||
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" {
|
||||
|
@ -199,22 +188,11 @@ resource "aws_dms_replication_task" "dms_replication_task" {
|
|||
|
||||
func dmsReplicationTaskConfigUpdate(randId string) string {
|
||||
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" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
tags {
|
||||
Name = "tf-test-dms-vpc-%[1]s"
|
||||
}
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
resource "aws_subnet" "dms_subnet_1" {
|
||||
|
@ -246,7 +224,6 @@ resource "aws_dms_endpoint" "dms_endpoint_source" {
|
|||
port = 3306
|
||||
username = "tftest"
|
||||
password = "tftest"
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
resource "aws_dms_endpoint" "dms_endpoint_target" {
|
||||
|
@ -258,14 +235,12 @@ resource "aws_dms_endpoint" "dms_endpoint_target" {
|
|||
port = 3306
|
||||
username = "tftest"
|
||||
password = "tftest"
|
||||
depends_on = ["aws_iam_role_policy_attachment.dms_iam_role_policy"]
|
||||
}
|
||||
|
||||
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_description = "terraform test for replication subnet group"
|
||||
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" {
|
||||
|
|
|
@ -125,7 +125,7 @@ func resourceAwsElasticBeanstalkEnvironment() *schema.Resource {
|
|||
"wait_for_ready_timeout": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "10m",
|
||||
Default: "20m",
|
||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
duration, err := time.ParseDuration(value)
|
||||
|
|
|
@ -7,12 +7,14 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/elasticache"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
|
||||
var v elasticache.CacheParameterGroup
|
||||
rName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt())
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -20,12 +22,12 @@ func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
|
|||
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSElasticacheParameterGroupConfig,
|
||||
Config: testAccAWSElasticacheParameterGroupConfig(rName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
|
||||
testAccCheckAWSElasticacheParameterGroupAttributes(&v),
|
||||
testAccCheckAWSElasticacheParameterGroupAttributes(&v, rName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"),
|
||||
"aws_elasticache_parameter_group.bar", "name", rName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_elasticache_parameter_group.bar", "family", "redis2.8"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -37,12 +39,12 @@ func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
|
|||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccAWSElasticacheParameterGroupAddParametersConfig,
|
||||
Config: testAccAWSElasticacheParameterGroupAddParametersConfig(rName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
|
||||
testAccCheckAWSElasticacheParameterGroupAttributes(&v),
|
||||
testAccCheckAWSElasticacheParameterGroupAttributes(&v, rName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"),
|
||||
"aws_elasticache_parameter_group.bar", "name", rName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_elasticache_parameter_group.bar", "family", "redis2.8"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -63,6 +65,7 @@ func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
|
|||
|
||||
func TestAccAWSElasticacheParameterGroupOnly(t *testing.T) {
|
||||
var v elasticache.CacheParameterGroup
|
||||
rName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt())
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -70,12 +73,12 @@ func TestAccAWSElasticacheParameterGroupOnly(t *testing.T) {
|
|||
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSElasticacheParameterGroupOnlyConfig,
|
||||
Config: testAccAWSElasticacheParameterGroupOnlyConfig(rName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
|
||||
testAccCheckAWSElasticacheParameterGroupAttributes(&v),
|
||||
testAccCheckAWSElasticacheParameterGroupAttributes(&v, rName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"),
|
||||
"aws_elasticache_parameter_group.bar", "name", rName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_elasticache_parameter_group.bar", "family", "redis2.8"),
|
||||
),
|
||||
|
@ -118,10 +121,10 @@ func testAccCheckAWSElasticacheParameterGroupDestroy(s *terraform.State) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckAWSElasticacheParameterGroupAttributes(v *elasticache.CacheParameterGroup) resource.TestCheckFunc {
|
||||
func testAccCheckAWSElasticacheParameterGroupAttributes(v *elasticache.CacheParameterGroup, rName string) resource.TestCheckFunc {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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" {
|
||||
name = "parameter-group-test-terraform"
|
||||
name = "%s"
|
||||
family = "redis2.8"
|
||||
parameter {
|
||||
name = "appendonly"
|
||||
value = "yes"
|
||||
}
|
||||
}`, rName)
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSElasticacheParameterGroupAddParametersConfig = `
|
||||
func testAccAWSElasticacheParameterGroupAddParametersConfig(rName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_elasticache_parameter_group" "bar" {
|
||||
name = "parameter-group-test-terraform"
|
||||
name = "%s"
|
||||
family = "redis2.8"
|
||||
description = "Test parameter group for terraform"
|
||||
parameter {
|
||||
|
@ -191,13 +196,14 @@ resource "aws_elasticache_parameter_group" "bar" {
|
|||
name = "appendfsync"
|
||||
value = "always"
|
||||
}
|
||||
}`, rName)
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSElasticacheParameterGroupOnlyConfig = `
|
||||
func testAccAWSElasticacheParameterGroupOnlyConfig(rName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_elasticache_parameter_group" "bar" {
|
||||
name = "parameter-group-test-terraform"
|
||||
name = "%s"
|
||||
family = "redis2.8"
|
||||
description = "Test parameter group for terraform"
|
||||
}`, rName)
|
||||
}
|
||||
`
|
||||
|
|
|
@ -228,10 +228,6 @@ func testAccESDomainConfig_complex(randInt int) string {
|
|||
resource "aws_elasticsearch_domain" "example" {
|
||||
domain_name = "tf-test-%d"
|
||||
|
||||
cluster_config {
|
||||
instance_type = "r3.large.elasticsearch"
|
||||
}
|
||||
|
||||
advanced_options {
|
||||
"indices.fielddata.cache.size" = 80
|
||||
}
|
||||
|
@ -243,6 +239,7 @@ resource "aws_elasticsearch_domain" "example" {
|
|||
cluster_config {
|
||||
instance_count = 2
|
||||
zone_awareness_enabled = true
|
||||
instance_type = "r3.large.elasticsearch"
|
||||
}
|
||||
|
||||
snapshot_options {
|
||||
|
|
|
@ -487,6 +487,7 @@ resource "aws_redshift_cluster" "test_cluster" {
|
|||
master_password = "T3stPass"
|
||||
node_type = "dc1.large"
|
||||
cluster_type = "single-node"
|
||||
skip_final_snapshot = true
|
||||
}`
|
||||
|
||||
var testAccKinesisFirehoseDeliveryStreamConfig_RedshiftBasic = testAccKinesisFirehoseDeliveryStreamBaseRedshiftConfig + `
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"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/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
|
@ -18,6 +19,7 @@ func resourceAwsKmsKey() *schema.Resource {
|
|||
Read: resourceAwsKmsKeyRead,
|
||||
Update: resourceAwsKmsKeyUpdate,
|
||||
Delete: resourceAwsKmsKeyDelete,
|
||||
Exists: resourceAwsKmsKeyExists,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
|
@ -368,6 +370,30 @@ func updateKmsKeyRotationStatus(conn *kms.KMS, d *schema.ResourceData) error {
|
|||
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 {
|
||||
conn := meta.(*AWSClient).kmsconn
|
||||
keyId := d.Get("key_id").(string)
|
||||
|
|
|
@ -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) {
|
||||
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":"*"}]}`
|
||||
|
@ -238,6 +261,32 @@ resource "aws_kms_key" "foo" {
|
|||
POLICY
|
||||
}`, 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(`
|
||||
resource "aws_kms_key" "foo" {
|
||||
description = "Terraform acc test %s"
|
||||
|
|
|
@ -473,7 +473,7 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface
|
|||
|
||||
// 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
|
||||
err = resource.Retry(30*time.Second, func() *resource.RetryError {
|
||||
err = resource.Retry(90*time.Second, func() *resource.RetryError {
|
||||
_, err := autoscalingconn.CreateLaunchConfiguration(&createLaunchConfigurationOpts)
|
||||
if err != nil {
|
||||
if awsErr, ok := err.(awserr.Error); ok {
|
||||
|
|
|
@ -703,6 +703,7 @@ func testAccAWSRedshiftClusterConfig_loggingDisabled(rInt int) string {
|
|||
automated_snapshot_retention_period = 0
|
||||
allow_version_upgrade = false
|
||||
enable_logging = false
|
||||
skip_final_snapshot = true
|
||||
}`, rInt)
|
||||
}
|
||||
|
||||
|
|
|
@ -728,8 +728,8 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
log.Printf("[DEBUG] S3 Bucket: %s, logging: %v", d.Id(), logging)
|
||||
lcl := make([]map[string]interface{}, 0, 1)
|
||||
if v := logging.LoggingEnabled; v != nil {
|
||||
lcl := make([]map[string]interface{}, 0, 1)
|
||||
lc := make(map[string]interface{})
|
||||
if *v.TargetBucket != "" {
|
||||
lc["target_bucket"] = *v.TargetBucket
|
||||
|
@ -738,9 +738,9 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
|
|||
lc["target_prefix"] = *v.TargetPrefix
|
||||
}
|
||||
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
|
||||
|
|
|
@ -215,7 +215,7 @@ func resourceAwsSubnetDelete(d *schema.ResourceData, meta interface{}) error {
|
|||
wait := resource.StateChangeConf{
|
||||
Pending: []string{"pending"},
|
||||
Target: []string{"destroyed"},
|
||||
Timeout: 5 * time.Minute,
|
||||
Timeout: 10 * time.Minute,
|
||||
MinTimeout: 1 * time.Second,
|
||||
Refresh: func() (interface{}, string, error) {
|
||||
_, err := conn.DeleteSubnet(req)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"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/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/keyvault"
|
||||
"github.com/Azure/azure-sdk-for-go/arm/network"
|
||||
|
@ -47,6 +48,8 @@ type ArmClient struct {
|
|||
vmImageClient compute.VirtualMachineImagesClient
|
||||
vmClient compute.VirtualMachinesClient
|
||||
|
||||
diskClient disk.DisksClient
|
||||
|
||||
appGatewayClient network.ApplicationGatewaysClient
|
||||
ifaceClient network.InterfacesClient
|
||||
loadBalancerClient network.LoadBalancersClient
|
||||
|
@ -245,6 +248,12 @@ func (c *Config) getArmClient() (*ArmClient, error) {
|
|||
csc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
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)
|
||||
setUserAgent(&ehc.Client)
|
||||
ehc.Authorizer = spt
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -19,11 +19,39 @@ func TestAccAzureRMVirtualMachine_importBasic(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testCheckAzureRMVirtualMachineDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
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,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
|
|
|
@ -85,6 +85,8 @@ func Provider() terraform.ResourceProvider {
|
|||
"azurerm_lb_probe": resourceArmLoadBalancerProbe(),
|
||||
"azurerm_lb_rule": resourceArmLoadBalancerRule(),
|
||||
|
||||
"azurerm_managed_disk": resourceArmManagedDisk(),
|
||||
|
||||
"azurerm_key_vault": resourceArmKeyVault(),
|
||||
"azurerm_local_network_gateway": resourceArmLocalNetworkGateway(),
|
||||
"azurerm_network_interface": resourceArmNetworkInterface(),
|
||||
|
|
|
@ -101,6 +101,9 @@ func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{}
|
|||
return err
|
||||
}
|
||||
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
|
||||
|
||||
resp, err := lnetClient.Get(resGroup, name)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}`
|
|
@ -1,7 +1,6 @@
|
|||
package azurerm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
@ -188,10 +187,8 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e
|
|||
}
|
||||
|
||||
// Create
|
||||
cancelCtx, cancelFunc := context.WithTimeout(client.StopContext, 1*time.Hour)
|
||||
_, createErr := storageClient.Create(
|
||||
resourceGroupName, storageAccountName, opts, cancelCtx.Done())
|
||||
cancelFunc()
|
||||
resourceGroupName, storageAccountName, opts, make(chan struct{}))
|
||||
|
||||
// The only way to get the ID back apparently is to read the resource again
|
||||
read, err := storageClient.GetProperties(resourceGroupName, storageAccountName)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/arm/compute"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/helper/validation"
|
||||
riviera "github.com/jen20/riviera/azure"
|
||||
)
|
||||
|
||||
|
@ -141,10 +142,29 @@ func resourceArmVirtualMachine() *schema.Resource {
|
|||
|
||||
"vhd_uri": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Optional: 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": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
|
@ -189,7 +209,26 @@ func resourceArmVirtualMachine() *schema.Resource {
|
|||
|
||||
"vhd_uri": {
|
||||
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": {
|
||||
|
@ -204,9 +243,10 @@ func resourceArmVirtualMachine() *schema.Resource {
|
|||
},
|
||||
|
||||
"disk_size_gb": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ValidateFunc: validateDiskSizeGB,
|
||||
},
|
||||
|
||||
"lun": {
|
||||
|
@ -453,15 +493,6 @@ func validateLicenseType(v interface{}, k string) (ws []string, errors []error)
|
|||
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 {
|
||||
client := meta.(*ArmClient)
|
||||
vmClient := client.vmClient
|
||||
|
@ -685,21 +716,29 @@ func resourceArmVirtualMachineDelete(d *schema.ResourceData, meta interface{}) e
|
|||
|
||||
// delete OS Disk if opted in
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error expanding OS Disk: %s", err)
|
||||
}
|
||||
|
||||
if err = resourceArmVirtualMachineDeleteVhd(*osDisk.Vhd.URI, meta); err != nil {
|
||||
return fmt.Errorf("Error deleting OS Disk VHD: %s", err)
|
||||
if osDisk.Vhd != nil {
|
||||
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
|
||||
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)
|
||||
if err != nil {
|
||||
|
@ -707,8 +746,16 @@ func resourceArmVirtualMachineDelete(d *schema.ResourceData, meta interface{}) e
|
|||
}
|
||||
|
||||
for _, disk := range disks {
|
||||
if err = resourceArmVirtualMachineDeleteVhd(*disk.Vhd.URI, meta); err != nil {
|
||||
return fmt.Errorf("Error deleting Data Disk VHD: %s", err)
|
||||
if disk.Vhd != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
var buf bytes.Buffer
|
||||
m := v.(map[string]interface{})
|
||||
|
@ -784,8 +849,9 @@ func resourceArmVirtualMachineStorageOsDiskHash(v interface{}) int {
|
|||
var buf bytes.Buffer
|
||||
m := v.(map[string]interface{})
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -883,7 +949,13 @@ func flattenAzureRmVirtualMachineDataDisk(disks *[]compute.DataDisk) interface{}
|
|||
for i, disk := range *disks {
|
||||
l := make(map[string]interface{})
|
||||
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["caching"] = string(disk.Caching)
|
||||
if disk.DiskSizeGB != nil {
|
||||
|
@ -982,7 +1054,13 @@ func flattenAzureRmVirtualMachineOsProfileLinuxConfiguration(config *compute.Lin
|
|||
func flattenAzureRmVirtualMachineOsDisk(disk *compute.OSDisk) []interface{} {
|
||||
result := make(map[string]interface{})
|
||||
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["caching"] = disk.Caching
|
||||
if disk.DiskSizeGB != nil {
|
||||
|
@ -1157,22 +1235,22 @@ func expandAzureRmVirtualMachineOsProfileWindowsConfig(d *schema.ResourceData) (
|
|||
if v := osProfileConfig["winrm"]; v != nil {
|
||||
winRm := v.([]interface{})
|
||||
if len(winRm) > 0 {
|
||||
winRmListners := make([]compute.WinRMListener, 0, len(winRm))
|
||||
winRmListeners := make([]compute.WinRMListener, 0, len(winRm))
|
||||
for _, winRmConfig := range winRm {
|
||||
config := winRmConfig.(map[string]interface{})
|
||||
|
||||
protocol := config["protocol"].(string)
|
||||
winRmListner := compute.WinRMListener{
|
||||
winRmListener := compute.WinRMListener{
|
||||
Protocol: compute.ProtocolTypes(protocol),
|
||||
}
|
||||
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{
|
||||
Listeners: &winRmListners,
|
||||
Listeners: &winRmListeners,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1209,19 +1287,48 @@ func expandAzureRmVirtualMachineDataDisk(d *schema.ResourceData) ([]compute.Data
|
|||
config := disk_config.(map[string]interface{})
|
||||
|
||||
name := config["name"].(string)
|
||||
vhd := config["vhd_uri"].(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))
|
||||
|
||||
data_disk := compute.DataDisk{
|
||||
Name: &name,
|
||||
Vhd: &compute.VirtualHardDisk{
|
||||
URI: &vhd,
|
||||
},
|
||||
Name: &name,
|
||||
Lun: &lun,
|
||||
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 != "" {
|
||||
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) {
|
||||
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)
|
||||
vhdURI := disk["vhd_uri"].(string)
|
||||
imageURI := disk["image_uri"].(string)
|
||||
createOption := disk["create_option"].(string)
|
||||
name := config["name"].(string)
|
||||
imageURI := config["image_uri"].(string)
|
||||
createOption := config["create_option"].(string)
|
||||
vhdURI := config["vhd_uri"].(string)
|
||||
managedDiskType := config["managed_disk_type"].(string)
|
||||
managedDiskID := config["managed_disk_id"].(string)
|
||||
|
||||
osDisk := &compute.OSDisk{
|
||||
Name: &name,
|
||||
Vhd: &compute.VirtualHardDisk{
|
||||
URI: &vhdURI,
|
||||
},
|
||||
Name: &name,
|
||||
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{
|
||||
URI: &imageURI,
|
||||
}
|
||||
}
|
||||
|
||||
if v := disk["os_type"].(string); v != "" {
|
||||
if v := config["os_type"].(string); v != "" {
|
||||
if v == "linux" {
|
||||
osDisk.OsType = compute.Linux
|
||||
} 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)
|
||||
}
|
||||
|
||||
if v := disk["disk_size_gb"].(int); v != 0 {
|
||||
if v := config["disk_size_gb"].(int); v != 0 {
|
||||
diskSize := int32(v)
|
||||
osDisk.DiskSizeGB = &diskSize
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,64 +2,107 @@ package bitbucket
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"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 {
|
||||
Username string
|
||||
Password string
|
||||
Username 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) {
|
||||
client := &http.Client{}
|
||||
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)
|
||||
|
||||
return c.Do("GET", endpoint, nil)
|
||||
}
|
||||
|
||||
func (c *BitbucketClient) Post(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) {
|
||||
client := &http.Client{}
|
||||
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)
|
||||
return c.Do("POST", endpoint, jsonpayload)
|
||||
}
|
||||
|
||||
func (c *BitbucketClient) Put(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) {
|
||||
client := &http.Client{}
|
||||
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)
|
||||
return c.Do("PUT", endpoint, jsonpayload)
|
||||
}
|
||||
|
||||
func (c *BitbucketClient) PutOnly(endpoint string) (*http.Response, error) {
|
||||
client := &http.Client{}
|
||||
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)
|
||||
return c.Do("PUT", endpoint, nil)
|
||||
}
|
||||
|
||||
func (c *BitbucketClient) Delete(endpoint string) (*http.Response, error) {
|
||||
client := &http.Client{}
|
||||
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)
|
||||
return c.Do("DELETE", endpoint, nil)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package bitbucket
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
@ -30,8 +32,9 @@ func Provider() terraform.ResourceProvider {
|
|||
|
||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
client := &BitbucketClient{
|
||||
Username: d.Get("username").(string),
|
||||
Password: d.Get("password").(string),
|
||||
Username: d.Get("username").(string),
|
||||
Password: d.Get("password").(string),
|
||||
HTTPClient: &http.Client{},
|
||||
}
|
||||
|
||||
return client, nil
|
||||
|
|
|
@ -3,6 +3,7 @@ package bitbucket
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
@ -49,7 +50,7 @@ func resourceDefaultReviewersCreate(d *schema.ResourceData, m interface{}) error
|
|||
client := m.(*BitbucketClient)
|
||||
|
||||
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("repository").(string),
|
||||
user,
|
||||
|
@ -59,11 +60,11 @@ func resourceDefaultReviewersCreate(d *schema.ResourceData, m interface{}) error
|
|||
return err
|
||||
}
|
||||
|
||||
if reviewer_resp.StatusCode != 200 {
|
||||
return fmt.Errorf("Failed to create reviewer %s got code %d", user.(string), reviewer_resp.StatusCode)
|
||||
if reviewerResp.StatusCode != 200 {
|
||||
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)))
|
||||
|
@ -72,26 +73,26 @@ func resourceDefaultReviewersCreate(d *schema.ResourceData, m interface{}) error
|
|||
func resourceDefaultReviewersRead(d *schema.ResourceData, m interface{}) error {
|
||||
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("repository").(string),
|
||||
))
|
||||
|
||||
var reviewers PaginatedReviewers
|
||||
|
||||
decoder := json.NewDecoder(reviewers_response.Body)
|
||||
decoder := json.NewDecoder(reviewersResponse.Body)
|
||||
err = decoder.Decode(&reviewers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
terraform_reviewers := make([]string, 0, len(reviewers.Values))
|
||||
terraformReviewers := make([]string, 0, len(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
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
@ -81,86 +85,89 @@ func resourceHookCreate(d *schema.ResourceData, m interface{}) error {
|
|||
client := m.(*BitbucketClient)
|
||||
hook := createHook(d)
|
||||
|
||||
var jsonbuffer []byte
|
||||
|
||||
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)
|
||||
payload, err := json.Marshal(hook)
|
||||
if err != nil {
|
||||
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.Set("uuid", hook.Uuid)
|
||||
|
||||
return resourceHookRead(d, m)
|
||||
}
|
||||
func resourceHookRead(d *schema.ResourceData, m interface{}) error {
|
||||
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("repository").(string),
|
||||
d.Get("uuid").(string),
|
||||
url.PathEscape(d.Id()),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
log.Printf("ID: %s", url.PathEscape(d.Id()))
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func resourceHookUpdate(d *schema.ResourceData, m interface{}) error {
|
||||
client := m.(*BitbucketClient)
|
||||
hook := createHook(d)
|
||||
|
||||
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)
|
||||
|
||||
payload, err := json.Marshal(hook)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(hook_req.Body)
|
||||
err = decoder.Decode(&hook)
|
||||
_, err = client.Put(fmt.Sprintf("2.0/repositories/%s/%s/hooks/%s",
|
||||
d.Get("owner").(string),
|
||||
d.Get("repository").(string),
|
||||
url.PathEscape(d.Id()),
|
||||
), bytes.NewBuffer(payload))
|
||||
|
||||
if err != nil {
|
||||
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",
|
||||
d.Get("owner").(string),
|
||||
d.Get("repository").(string),
|
||||
d.Get("uuid").(string),
|
||||
url.PathEscape(d.Id()),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
|
@ -182,15 +189,14 @@ func resourceHookExists(d *schema.ResourceData, m interface{}) (bool, error) {
|
|||
}
|
||||
|
||||
if hook_req.StatusCode != 200 {
|
||||
d.SetId("")
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
|
||||
}
|
||||
|
||||
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",
|
||||
d.Get("owner").(string),
|
||||
d.Get("repository").(string),
|
||||
d.Get("uuid").(string),
|
||||
url.PathEscape(d.Id()),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package bitbucket
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
|
@ -16,7 +17,7 @@ func TestAccBitbucketHook_basic(t *testing.T) {
|
|||
testAccBitbucketHookConfig := fmt.Sprintf(`
|
||||
resource "bitbucket_repository" "test_repo" {
|
||||
owner = "%s"
|
||||
name = "test-repo"
|
||||
name = "test-repo-for-webhook-test"
|
||||
}
|
||||
resource "bitbucket_hook" "test_repo_hook" {
|
||||
owner = "%s"
|
||||
|
@ -51,10 +52,10 @@ func testAccCheckBitbucketHookDestroy(s *terraform.State) error {
|
|||
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 {
|
||||
return err
|
||||
if err == nil {
|
||||
return fmt.Errorf("The resource was found should have errored")
|
||||
}
|
||||
|
||||
if response.StatusCode != 404 {
|
||||
|
|
|
@ -4,8 +4,9 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"log"
|
||||
)
|
||||
|
||||
type CloneUrl struct {
|
||||
|
@ -131,7 +132,7 @@ func resourceRepositoryUpdate(d *schema.ResourceData, m interface{}) error {
|
|||
enc := json.NewEncoder(jsonpayload)
|
||||
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("name").(string),
|
||||
), jsonpayload)
|
||||
|
@ -140,16 +141,6 @@ func resourceRepositoryUpdate(d *schema.ResourceData, m interface{}) error {
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -157,29 +148,19 @@ func resourceRepositoryCreate(d *schema.ResourceData, m interface{}) error {
|
|||
client := m.(*BitbucketClient)
|
||||
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 {
|
||||
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 {
|
||||
return fmt.Errorf("Failed to create repository got status code %d", repo_req.StatusCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
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("name").(string),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if repo_req.StatusCode == 200 {
|
||||
|
||||
var repo Repository
|
||||
var repo Repository
|
||||
|
||||
decoder := json.NewDecoder(repo_req.Body)
|
||||
err = decoder.Decode(&repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body, readerr := ioutil.ReadAll(repo_req.Body)
|
||||
if readerr != nil {
|
||||
return readerr
|
||||
}
|
||||
|
||||
d.Set("scm", repo.SCM)
|
||||
d.Set("is_private", repo.IsPrivate)
|
||||
d.Set("has_wiki", repo.HasWiki)
|
||||
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)
|
||||
decodeerr := json.Unmarshal(body, &repo)
|
||||
if decodeerr != nil {
|
||||
return decodeerr
|
||||
}
|
||||
|
||||
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)
|
||||
d.Set("scm", repo.SCM)
|
||||
d.Set("is_private", repo.IsPrivate)
|
||||
d.Set("has_wiki", repo.HasWiki)
|
||||
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 {
|
||||
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 {
|
||||
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("name").(string),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if delete_response.StatusCode != 204 {
|
||||
return fmt.Errorf("Failed to delete the repository got status code %d", delete_response.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@ func TestAccBitbucketRepository_basic(t *testing.T) {
|
|||
testAccBitbucketRepositoryConfig := fmt.Sprintf(`
|
||||
resource "bitbucket_repository" "test_repo" {
|
||||
owner = "%s"
|
||||
name = "%s"
|
||||
name = "test-repo-for-repository-test"
|
||||
}
|
||||
`, testUser, testRepo)
|
||||
`, testUser)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -42,11 +42,7 @@ func testAccCheckBitbucketRepositoryDestroy(s *terraform.State) error {
|
|||
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"]))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, _ := client.Get(fmt.Sprintf("2.0/repositories/%s/%s", rs.Primary.Attributes["owner"], rs.Primary.Attributes["name"]))
|
||||
|
||||
if response.StatusCode != 404 {
|
||||
return fmt.Errorf("Repository still exists")
|
||||
|
|
|
@ -58,10 +58,11 @@ func resourceCloudStackLoadBalancerRule() *schema.Resource {
|
|||
},
|
||||
|
||||
"member_ids": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ForceNew: false,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
|
||||
"project": &schema.Schema{
|
||||
|
@ -124,7 +125,7 @@ func resourceCloudStackLoadBalancerRuleCreate(d *schema.ResourceData, meta inter
|
|||
ap := cs.LoadBalancer.NewAssignToLoadBalancerRuleParams(r.Id)
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -171,6 +172,18 @@ func resourceCloudStackLoadBalancerRuleRead(d *schema.ResourceData, meta interfa
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -215,6 +228,41 @@ func resourceCloudStackLoadBalancerRuleUpdate(d *schema.ResourceData, meta inter
|
|||
"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)
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ func resourceComputeVolumeAttachV2Read(d *schema.ResourceData, meta interface{})
|
|||
|
||||
attachment, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
|
||||
if err != nil {
|
||||
return err
|
||||
return CheckDeleted(d, err, "compute_volume_attach")
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment)
|
||||
|
|
|
@ -237,7 +237,6 @@ Options:
|
|||
-force-copy Suppress prompts about copying state data. This is
|
||||
equivalent to providing a "yes" to all confirmation
|
||||
prompts.
|
||||
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
|
|
@ -264,6 +264,10 @@ func (m *Meta) flagSet(n string) *flag.FlagSet {
|
|||
// Set the default Usage to empty
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package command
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -53,7 +54,7 @@ func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
|
|||
// Setup defaults
|
||||
opts.oneEnv = backend.DefaultStateName
|
||||
opts.twoEnv = backend.DefaultStateName
|
||||
opts.force = false
|
||||
opts.force = m.forceInitCopy
|
||||
|
||||
// Determine migration behavior based on whether the source/destionation
|
||||
// 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 {
|
||||
currentEnv := m.Env()
|
||||
|
||||
migrate := m.forceInitCopy
|
||||
migrate := opts.force
|
||||
if !migrate {
|
||||
var err error
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
|
||||
defer cancel()
|
||||
|
@ -241,10 +255,21 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
|
|||
return fmt.Errorf("Error locking destination state: %s", err)
|
||||
}
|
||||
defer clistate.Unlock(stateTwo, lockIDTwo, m.Ui, m.Colorize())
|
||||
}
|
||||
|
||||
one := stateOne.State()
|
||||
two := stateTwo.State()
|
||||
// We now own a lock, so double check that we have the version
|
||||
// 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
|
||||
// step then this won't be used anymore.
|
||||
|
@ -281,6 +306,11 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
|
|||
}
|
||||
|
||||
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, err := confirmFunc(stateOne, stateTwo, opts)
|
||||
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) {
|
||||
if m.forceInitCopy {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
inputOpts := &terraform.InputOpts{
|
||||
Id: "backend-migrate-copy-to-empty",
|
||||
Query: fmt.Sprintf(
|
||||
|
@ -372,10 +398,6 @@ func (m *Meta) backendMigrateNonEmptyConfirm(
|
|||
return false, fmt.Errorf("Error saving temporary state: %s", err)
|
||||
}
|
||||
|
||||
if m.forceInitCopy {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Ask for confirmation
|
||||
inputOpts := &terraform.InputOpts{
|
||||
Id: "backend-migrate-to-backend",
|
||||
|
|
|
@ -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,
|
||||
// but opting to not migrate.
|
||||
func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) {
|
||||
|
|
|
@ -62,10 +62,20 @@ type Schema struct {
|
|||
DiffSuppressFunc SchemaDiffSuppressFunc
|
||||
|
||||
// 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.
|
||||
// Only one of Default or DefaultFunc can be set.
|
||||
// DefaultFunc can be specified to compute a dynamic default.
|
||||
// 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
|
||||
// can be set with Required. If the DefaultFunc returns nil, then there
|
||||
|
|
|
@ -94,9 +94,9 @@ func LockWithContext(ctx context.Context, s State, info *LockInfo) (string, erro
|
|||
return "", err
|
||||
}
|
||||
|
||||
if le.Info.ID == "" {
|
||||
// the lock has no ID, something is wrong so don't keep trying
|
||||
return "", fmt.Errorf("lock error missing ID: %s", err)
|
||||
if le == nil || le.Info == nil || le.Info.ID == "" {
|
||||
// If we dont' have a complete LockError, there's something wrong with the lock
|
||||
return "", err
|
||||
}
|
||||
|
||||
if postLockHook != nil {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// EvalValidateError is the error structure returned if there were
|
||||
|
@ -85,12 +86,31 @@ func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
|
|||
type EvalValidateProvisioner struct {
|
||||
Provisioner *ResourceProvisioner
|
||||
Config **ResourceConfig
|
||||
ConnConfig **ResourceConfig
|
||||
}
|
||||
|
||||
func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
|
||||
provisioner := *n.Provisioner
|
||||
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 {
|
||||
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
|
||||
// the configuration of a resource.
|
||||
type EvalValidateResource struct {
|
||||
|
|
|
@ -178,3 +178,117 @@ func TestEvalValidateResource_ignoreWarnings(t *testing.T) {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,17 +129,29 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
|
|||
// Validate all the provisioners
|
||||
for _, p := range n.Config.Provisioners {
|
||||
var provisioner ResourceProvisioner
|
||||
seq.Nodes = append(seq.Nodes, &EvalGetProvisioner{
|
||||
Name: p.Type,
|
||||
Output: &provisioner,
|
||||
}, &EvalInterpolate{
|
||||
Config: p.RawConfig.Copy(),
|
||||
Resource: resource,
|
||||
Output: &config,
|
||||
}, &EvalValidateProvisioner{
|
||||
Provisioner: &provisioner,
|
||||
Config: &config,
|
||||
})
|
||||
var connConfig *ResourceConfig
|
||||
seq.Nodes = append(
|
||||
seq.Nodes,
|
||||
&EvalGetProvisioner{
|
||||
Name: p.Type,
|
||||
Output: &provisioner,
|
||||
},
|
||||
&EvalInterpolate{
|
||||
Config: p.RawConfig.Copy(),
|
||||
Resource: resource,
|
||||
Output: &config,
|
||||
},
|
||||
&EvalInterpolate{
|
||||
Config: p.ConnInfo.Copy(),
|
||||
Resource: resource,
|
||||
Output: &connConfig,
|
||||
},
|
||||
&EvalValidateProvisioner{
|
||||
Provisioner: &provisioner,
|
||||
Config: &config,
|
||||
ConnConfig: &connConfig,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return seq
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"`
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
===
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
### SDK Features
|
||||
|
||||
### SDK Enhancements
|
||||
|
||||
### SDK Bugs
|
|
@ -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
|
||||
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
|
||||
|
||||
To run the tests locally, running the `make unit` command will `go get` the
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
# AWS SDK for Go
|
||||
|
||||
<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 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)
|
||||
|
||||
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`)
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"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/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() {
|
||||
sess, err := session.NewSession()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
var bucket, key string
|
||||
var timeout time.Duration
|
||||
|
||||
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
|
||||
// Note that you can also configure your region globally by
|
||||
// exporting the AWS_REGION environment variable
|
||||
svc := ec2.New(sess, &aws.Config{Region: aws.String("us-west-2")})
|
||||
|
||||
// Call the DescribeInstances Operation
|
||||
resp, err := svc.DescribeInstances(nil)
|
||||
// Uploads the object to S3. The Context will interrupt the request if the
|
||||
// timeout expires.
|
||||
_, err := svc.PutObjectWithContext(ctx, &s3.PutObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
Body: os.Stdin,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// resp has all of the response data, pull out instance IDs:
|
||||
fmt.Println("> Number of reservation sets: ", len(resp.Reservations))
|
||||
for idx, res := range resp.Reservations {
|
||||
fmt.Println(" > Number of instances: ", len(res.Instances))
|
||||
for _, inst := range resp.Reservations[idx].Instances {
|
||||
fmt.Println(" - Instance ID: ", *inst.InstanceId)
|
||||
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.
|
||||
fmt.Fprintf(os.Stderr, "upload canceled due to timeout, %v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "failed to upload object, %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("successfully uploaded file to %s/%s\n", bucket, key)
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -187,6 +187,10 @@ type Config struct {
|
|||
// request delays. This value should only be used for testing. To adjust
|
||||
// the delay of a request see the aws/client.DefaultRetryer and
|
||||
// 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)
|
||||
|
||||
// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
)
|
|
@ -0,0 +1,9 @@
|
|||
// +build go1.7
|
||||
|
||||
package aws
|
||||
|
||||
import "context"
|
||||
|
||||
var (
|
||||
backgroundCtx = context.Background()
|
||||
)
|
|
@ -134,6 +134,16 @@ var SendHandler = request.NamedHandler{Name: "core.SendHandler", Fn: func(r *req
|
|||
// Catch all other request errors.
|
||||
r.Error = awserr.New("RequestError", "send request failed", err)
|
||||
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() {
|
||||
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
|
||||
// need to be expired locally so that the next request to
|
||||
|
|
|
@ -88,7 +88,7 @@ type Value struct {
|
|||
// The Provider should not need to implement its own mutexes, because
|
||||
// that will be managed by Credentials.
|
||||
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.
|
||||
Retrieve() (Value, error)
|
||||
|
||||
|
|
|
@ -56,7 +56,6 @@ func Config() *aws.Config {
|
|||
WithMaxRetries(aws.UseServiceDefaultRetries).
|
||||
WithLogger(aws.NewDefaultLogger()).
|
||||
WithLogLevel(aws.LogOff).
|
||||
WithSleepDelay(time.Sleep).
|
||||
WithEndpointResolver(endpoints.DefaultResolver())
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -131,6 +131,7 @@ const (
|
|||
StsServiceID = "sts" // Sts.
|
||||
SupportServiceID = "support" // Support.
|
||||
SwfServiceID = "swf" // Swf.
|
||||
TaggingServiceID = "tagging" // Tagging.
|
||||
WafServiceID = "waf" // Waf.
|
||||
WafRegionalServiceID = "waf-regional" // WafRegional.
|
||||
WorkdocsServiceID = "workdocs" // Workdocs.
|
||||
|
@ -249,6 +250,7 @@ var awsPartition = partition{
|
|||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
|
@ -435,10 +437,14 @@ var awsPartition = partition{
|
|||
"codebuild": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"codecommit": service{
|
||||
|
@ -755,10 +761,11 @@ var awsPartition = partition{
|
|||
"elasticfilesystem": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"elasticloadbalancing": service{
|
||||
|
@ -1022,6 +1029,7 @@ var awsPartition = partition{
|
|||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
|
@ -1380,7 +1388,6 @@ var awsPartition = partition{
|
|||
Endpoints: endpoints{
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
|
@ -1567,6 +1574,25 @@ var awsPartition = partition{
|
|||
"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{
|
||||
PartitionEndpoint: "aws-global",
|
||||
IsRegionalized: boxedFalse,
|
||||
|
@ -1678,6 +1704,12 @@ var awscnPartition = partition{
|
|||
"cn-north-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"codedeploy": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"cn-north-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"config": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -1855,6 +1887,12 @@ var awscnPartition = partition{
|
|||
},
|
||||
"swf": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"cn-north-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"tagging": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"cn-north-1": endpoint{},
|
||||
},
|
||||
|
|
|
@ -158,7 +158,7 @@ var funcMap = template.FuncMap{
|
|||
|
||||
const v3Tmpl = `
|
||||
{{ define "defaults" -}}
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
// Code generated by aws/endpoints/v3model_codegen.go. DO NOT EDIT.
|
||||
|
||||
package endpoints
|
||||
|
||||
|
|
|
@ -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{}
|
|
@ -18,6 +18,7 @@ type Handlers struct {
|
|||
UnmarshalError HandlerList
|
||||
Retry HandlerList
|
||||
AfterRetry HandlerList
|
||||
Complete HandlerList
|
||||
}
|
||||
|
||||
// Copy returns of this handler's lists.
|
||||
|
@ -33,6 +34,7 @@ func (h *Handlers) Copy() Handlers {
|
|||
UnmarshalMeta: h.UnmarshalMeta.copy(),
|
||||
Retry: h.Retry.copy(),
|
||||
AfterRetry: h.AfterRetry.copy(),
|
||||
Complete: h.Complete.copy(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +50,7 @@ func (h *Handlers) Clear() {
|
|||
h.ValidateResponse.Clear()
|
||||
h.Retry.Clear()
|
||||
h.AfterRetry.Clear()
|
||||
h.Complete.Clear()
|
||||
}
|
||||
|
||||
// A HandlerListRunItem represents an entry in the HandlerList which
|
||||
|
@ -85,13 +88,17 @@ func (l *HandlerList) copy() HandlerList {
|
|||
n := HandlerList{
|
||||
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
|
||||
}
|
||||
|
||||
// Clear clears the handler list.
|
||||
func (l *HandlerList) Clear() {
|
||||
l.list = []NamedHandler{}
|
||||
l.list = l.list[0:0]
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (l *HandlerList) PushBack(f func(*Request)) {
|
||||
l.list = append(l.list, 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...)
|
||||
l.PushBackNamed(NamedHandler{"__anonymous", f})
|
||||
}
|
||||
|
||||
// PushBackNamed pushes named handler f to the back of the handler list.
|
||||
func (l *HandlerList) PushBackNamed(n NamedHandler) {
|
||||
if cap(l.list) == 0 {
|
||||
l.list = make([]NamedHandler, 0, 5)
|
||||
}
|
||||
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.
|
||||
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
|
||||
func (l *HandlerList) Remove(n NamedHandler) {
|
||||
newlist := []NamedHandler{}
|
||||
for _, m := range l.list {
|
||||
if m.Name != n.Name {
|
||||
newlist = append(newlist, m)
|
||||
for i := 0; i < len(l.list); i++ {
|
||||
m := l.list[i]
|
||||
if m.Name == n.Name {
|
||||
// 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.
|
||||
|
@ -163,6 +186,16 @@ func HandlerListStopOnError(item HandlerListRunItem) bool {
|
|||
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
|
||||
// header. If the extra parameters are provided they will be added as metadata to the
|
||||
// name/version pair resulting in the following format.
|
||||
|
|
|
@ -16,6 +16,11 @@ import (
|
|||
"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.
|
||||
type Request struct {
|
||||
Config aws.Config
|
||||
|
@ -41,12 +46,14 @@ type Request struct {
|
|||
SignedHeaderVals http.Header
|
||||
LastSignedAt time.Time
|
||||
|
||||
context aws.Context
|
||||
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -60,14 +67,6 @@ type Operation struct {
|
|||
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
|
||||
// operation and parameters.
|
||||
//
|
||||
|
@ -111,6 +110,94 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
|
|||
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.
|
||||
func (r *Request) WillRetry() bool {
|
||||
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.
|
||||
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 {
|
||||
if aws.BoolValue(r.Retryable) {
|
||||
if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
|
||||
|
@ -446,6 +539,9 @@ func shouldRetryCancel(r *Request) bool {
|
|||
timeoutErr := false
|
||||
errStr := r.Error.Error()
|
||||
if ok {
|
||||
if awsErr.Code() == CanceledErrorCode {
|
||||
return false
|
||||
}
|
||||
err := awsErr.OrigErr()
|
||||
netErr, netOK := err.(net.Error)
|
||||
timeoutErr = netOK && netErr.Temporary()
|
||||
|
|
|
@ -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)
|
||||
}
|
14
vendor/github.com/aws/aws-sdk-go/aws/request/request_context_1_6.go
generated
vendored
Normal file
14
vendor/github.com/aws/aws-sdk-go/aws/request/request_context_1_6.go
generated
vendored
Normal 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()
|
||||
}
|
|
@ -2,29 +2,125 @@ package request
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
)
|
||||
|
||||
//type Paginater interface {
|
||||
// HasNextPage() bool
|
||||
// NextPage() *Request
|
||||
// EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error
|
||||
//}
|
||||
// A Pagination provides paginating of SDK API operations which are paginatable.
|
||||
// Generally you should not use this type directly, but use the "Pages" API
|
||||
// operations method to automatically perform pagination for you. Such as,
|
||||
// "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.
|
||||
func (r *Request) HasNextPage() bool {
|
||||
return len(r.nextPageTokens()) > 0
|
||||
started bool
|
||||
nextTokens []interface{}
|
||||
|
||||
err error
|
||||
curPage interface{}
|
||||
}
|
||||
|
||||
// nextPageTokens returns the tokens to use when asking for the next page of
|
||||
// data.
|
||||
// HasNextPage will return true if Pagination is able to determine that the API
|
||||
// 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{} {
|
||||
if r.Operation.Paginator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.Operation.TruncationToken != "" {
|
||||
tr, _ := awsutil.ValuesAtPath(r.Data, r.Operation.TruncationToken)
|
||||
if len(tr) == 0 {
|
||||
|
@ -61,9 +157,40 @@ func (r *Request) nextPageTokens() []interface{} {
|
|||
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
|
||||
// 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 {
|
||||
logDeprecatedf(r.Config.Logger, &logDeprecatedNextPage,
|
||||
"Request.NextPage deprecated. Use Pagination type for configurable pagination of API operations")
|
||||
|
||||
tokens := r.nextPageTokens()
|
||||
if len(tokens) == 0 {
|
||||
return nil
|
||||
|
@ -90,7 +217,12 @@ func (r *Request) NextPage() *Request {
|
|||
// 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
|
||||
// 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 {
|
||||
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() {
|
||||
if err := page.Send(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -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
|
||||
// without any further action.
|
||||
var retryableCodes = map[string]struct{}{
|
||||
"RequestError": {},
|
||||
"RequestTimeout": {},
|
||||
"RequestError": {},
|
||||
"RequestTimeout": {},
|
||||
"RequestTimeoutException": {}, // Glacier's flavor of RequestTimeout
|
||||
}
|
||||
|
||||
var throttleCodes = map[string]struct{}{
|
||||
|
|
|
@ -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...))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -194,6 +194,10 @@ type Signer struct {
|
|||
// This value should only be used for testing. If it is nil the default
|
||||
// time.Now will be used.
|
||||
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
|
||||
|
@ -227,6 +231,7 @@ type signingCtx struct {
|
|||
isPresign bool
|
||||
formattedTime string
|
||||
formattedShortTime string
|
||||
unsignedPayload bool
|
||||
|
||||
bodyDigest string
|
||||
signedHeaders string
|
||||
|
@ -317,6 +322,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
|
|||
ServiceName: service,
|
||||
Region: region,
|
||||
DisableURIPathEscaping: v4.DisableURIPathEscaping,
|
||||
unsignedPayload: v4.UnsignedPayload,
|
||||
}
|
||||
|
||||
for key := range ctx.Query {
|
||||
|
@ -409,7 +415,18 @@ var SignRequestHandler = request.NamedHandler{
|
|||
func SignSDKRequest(req *request.Request) {
|
||||
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
|
||||
// request if the AnonymousCredentials object is used.
|
||||
if req.Config.Credentials == credentials.AnonymousCredentials {
|
||||
|
@ -441,6 +458,10 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time
|
|||
v4.DisableRequestBodyOverwrite = true
|
||||
})
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(v4)
|
||||
}
|
||||
|
||||
signingTime := req.Time
|
||||
if !req.LastSignedAt.IsZero() {
|
||||
signingTime = req.LastSignedAt
|
||||
|
@ -634,14 +655,14 @@ func (ctx *signingCtx) buildSignature() {
|
|||
func (ctx *signingCtx) buildBodyDigest() {
|
||||
hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
|
||||
if hash == "" {
|
||||
if ctx.isPresign && ctx.ServiceName == "s3" {
|
||||
if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
|
||||
hash = "UNSIGNED-PAYLOAD"
|
||||
} else if ctx.Body == nil {
|
||||
hash = emptyStringSHA256
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ package aws
|
|||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.7.9"
|
||||
const SDKVersion = "1.8.8"
|
||||
|
|
|
@ -4,6 +4,7 @@ package rest
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -82,8 +83,12 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo
|
|||
if name == "" {
|
||||
name = field.Name
|
||||
}
|
||||
if m.Kind() == reflect.Ptr {
|
||||
if kind := m.Kind(); kind == reflect.Ptr {
|
||||
m = m.Elem()
|
||||
} else if kind == reflect.Interface {
|
||||
if !m.Elem().IsValid() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !m.IsValid() {
|
||||
continue
|
||||
|
@ -95,16 +100,16 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo
|
|||
var err error
|
||||
switch field.Tag.Get("location") {
|
||||
case "headers": // header maps
|
||||
err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag.Get("locationName"))
|
||||
err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag)
|
||||
case "header":
|
||||
err = buildHeader(&r.HTTPRequest.Header, m, name)
|
||||
err = buildHeader(&r.HTTPRequest.Header, m, name, field.Tag)
|
||||
case "uri":
|
||||
err = buildURI(r.HTTPRequest.URL, m, name)
|
||||
err = buildURI(r.HTTPRequest.URL, m, name, field.Tag)
|
||||
case "querystring":
|
||||
err = buildQueryString(query, m, name)
|
||||
err = buildQueryString(query, m, name, field.Tag)
|
||||
default:
|
||||
if buildGETQuery {
|
||||
err = buildQueryString(query, m, name)
|
||||
err = buildQueryString(query, m, name, field.Tag)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
str, err := convertType(v)
|
||||
func buildHeader(header *http.Header, v reflect.Value, name string, tag reflect.StructTag) error {
|
||||
str, err := convertType(v, tag)
|
||||
if err == errValueNotSet {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
|
@ -158,9 +163,10 @@ func buildHeader(header *http.Header, v reflect.Value, name string) error {
|
|||
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() {
|
||||
str, err := convertType(v.MapIndex(key))
|
||||
str, err := convertType(v.MapIndex(key), tag)
|
||||
if err == errValueNotSet {
|
||||
continue
|
||||
} else if err != nil {
|
||||
|
@ -173,8 +179,8 @@ func buildHeaderMap(header *http.Header, v reflect.Value, prefix string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func buildURI(u *url.URL, v reflect.Value, name string) error {
|
||||
value, err := convertType(v)
|
||||
func buildURI(u *url.URL, v reflect.Value, name string, tag reflect.StructTag) error {
|
||||
value, err := convertType(v, tag)
|
||||
if err == errValueNotSet {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
|
@ -190,7 +196,7 @@ func buildURI(u *url.URL, v reflect.Value, name string) error {
|
|||
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) {
|
||||
case []*string:
|
||||
for _, item := range value {
|
||||
|
@ -207,7 +213,7 @@ func buildQueryString(query url.Values, v reflect.Value, name string) error {
|
|||
}
|
||||
}
|
||||
default:
|
||||
str, err := convertType(v)
|
||||
str, err := convertType(v, tag)
|
||||
if err == errValueNotSet {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
|
@ -246,7 +252,7 @@ func EscapePath(path string, encodeSep bool) 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)
|
||||
if !v.IsValid() {
|
||||
return "", errValueNotSet
|
||||
|
@ -266,6 +272,16 @@ func convertType(v reflect.Value) (string, error) {
|
|||
str = strconv.FormatFloat(value, 'f', -1, 64)
|
||||
case time.Time:
|
||||
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:
|
||||
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
|
||||
return "", err
|
||||
|
|
|
@ -3,6 +3,7 @@ package rest
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -12,6 +13,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
@ -111,7 +113,7 @@ func unmarshalLocationElements(r *request.Request, v reflect.Value) {
|
|||
case "statusCode":
|
||||
unmarshalStatusCode(m, r.HTTPResponse.StatusCode)
|
||||
case "header":
|
||||
err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name))
|
||||
err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name), field.Tag)
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
|
||||
break
|
||||
|
@ -158,8 +160,13 @@ func unmarshalHeaderMap(r reflect.Value, headers http.Header, prefix string) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func unmarshalHeader(v reflect.Value, header string) error {
|
||||
if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) {
|
||||
func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) error {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -196,6 +203,22 @@ func unmarshalHeader(v reflect.Value, header string) error {
|
|||
return err
|
||||
}
|
||||
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:
|
||||
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
|
||||
return err
|
||||
|
|
|
@ -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...))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"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
|
||||
func (c *ACM) AddTagsToCertificate(input *AddTagsToCertificateInput) (*AddTagsToCertificateOutput, error) {
|
||||
req, out := c.AddTagsToCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) DeleteCertificate(input *DeleteCertificateInput) (*DeleteCertificateOutput, error) {
|
||||
req, out := c.DeleteCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) DescribeCertificate(input *DescribeCertificateInput) (*DescribeCertificateOutput, error) {
|
||||
req, out := c.DescribeCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) GetCertificate(input *GetCertificateInput) (*GetCertificateOutput, error) {
|
||||
req, out := c.GetCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) ImportCertificate(input *ImportCertificateInput) (*ImportCertificateOutput, error) {
|
||||
req, out := c.ImportCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) ListCertificates(input *ListCertificatesInput) (*ListCertificatesOutput, error) {
|
||||
req, out := c.ListCertificatesRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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,
|
||||
|
@ -523,12 +614,37 @@ func (c *ACM) ListCertificates(input *ListCertificatesInput) (*ListCertificatesO
|
|||
// return pageNum <= 3
|
||||
// })
|
||||
//
|
||||
func (c *ACM) ListCertificatesPages(input *ListCertificatesInput, fn func(p *ListCertificatesOutput, lastPage bool) (shouldContinue bool)) error {
|
||||
page, _ := c.ListCertificatesRequest(input)
|
||||
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
|
||||
return page.EachPage(func(p interface{}, lastPage bool) bool {
|
||||
return fn(p.(*ListCertificatesOutput), lastPage)
|
||||
})
|
||||
func (c *ACM) ListCertificatesPages(input *ListCertificatesInput, fn func(*ListCertificatesOutput, bool) bool) error {
|
||||
return c.ListCertificatesPagesWithContext(aws.BackgroundContext(), input, fn)
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) ListTagsForCertificate(input *ListTagsForCertificateInput) (*ListTagsForCertificateOutput, error) {
|
||||
req, out := c.ListTagsForCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) RemoveTagsFromCertificate(input *RemoveTagsFromCertificateInput) (*RemoveTagsFromCertificateOutput, error) {
|
||||
req, out := c.RemoveTagsFromCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) RequestCertificate(input *RequestCertificateInput) (*RequestCertificateOutput, error) {
|
||||
req, out := c.RequestCertificateRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
func (c *ACM) ResendValidationEmail(input *ResendValidationEmailInput) (*ResendValidationEmailOutput, error) {
|
||||
req, out := c.ResendValidationEmailRequest(input)
|
||||
err := req.Send()
|
||||
return out, err
|
||||
return out, req.Send()
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue