Merge pull request #13308 from hashicorp/f-update-oracle-provider

provider/opc: Update Oracle provider
This commit is contained in:
Jake Champlin 2017-04-03 18:37:54 -04:00 committed by GitHub
commit 8ba93fdd8b
297 changed files with 21675 additions and 2784 deletions

4
.gitattributes vendored
View File

@ -1,4 +0,0 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
*.go eol=lf

View File

@ -1,7 +1,16 @@
## 0.9.3 (unreleased)
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:** `kubernetes_secret` [GH-12960]
* **New Data Source:** `aws_iam_role` [GH-13213]
IMPROVEMENTS:
* backend/remote-state: Add support for assume role extensions to s3 backend [GH-13236]
* config: New interpolation functions `basename` and `dirname`, for file path manipulation [GH-13080]
* helper/resource: Allow unknown "pending" states [GH-13099]
* provider/aws: Add support to set iam_role_arn on cloudformation Stack [GH-12547]
@ -13,13 +22,32 @@ IMPROVEMENTS:
* provider/aws: `aws_kinesis_firehose_delivery_stream` `password` field marked as sensitive [GH-13147]
* provider/aws: `aws_opsworks_application` `app_source.0.password` & `ssl_configuration.0.private_key` fields marked as sensitive [GH-13147]
* provider/aws: `aws_opsworks_stack` `custom_cookbooks_source.0.password` field marked as sensitive [GH-13147]
* provider/aws: Support the ability to enable / disable ipv6 support in VPC [GH-12527]
* provider/aws: Added API Gateway integration update [GH-13249]
* provider/aws: Add `identifier` | `name_prefix` to RDS resources [GH-13232]
* provider/aws: Validate `aws_ecs_task_definition.container_definitions` [GH-12161]
* provider/aws: Update caller_identity data source [GH-13092]
* 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]
* 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]
* provider/aws: `volume_type` of `aws_elasticsearch_domain.0.ebs_options` marked as `Computed` which prevents spurious diffs [GH-13160]
* provider/aws: Don't set DBName on `aws_db_instance` from snapshot [GH-13140]
* provider/aws: Add DiffSuppression to aws_ecs_service placement_strategies [GH-13220]
* provider/aws: Refresh aws_alb_target_group stickiness on manual updates [GH-13199]
* provider/aws: Preserve default retain_on_delete in cloudfront import [GH-13209]
* provider/aws: Refresh aws_alb_target_group tags [GH-13200]
* provider/aws: Set aws_vpn_connection to recreate when in deleted state [GH-13204]
* provider/aws: Wait for aws_opsworks_instance to be running when it's specified [GH-13218]
* provider/aws: Handle `aws_lambda_function` missing s3 key error [GH-10960]
* provider/aws: Set stickiness to computed in alb_target_group [GH-13278]
* provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153]
## 0.9.2 (March 28, 2017)

View File

@ -21,101 +21,122 @@ import (
func New() backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"bucket": &schema.Schema{
"bucket": {
Type: schema.TypeString,
Required: true,
Description: "The name of the S3 bucket",
},
"key": &schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
Description: "The path to the state file inside the bucket",
},
"region": &schema.Schema{
"region": {
Type: schema.TypeString,
Required: true,
Description: "The region of the S3 bucket.",
DefaultFunc: schema.EnvDefaultFunc("AWS_DEFAULT_REGION", nil),
},
"endpoint": &schema.Schema{
"endpoint": {
Type: schema.TypeString,
Optional: true,
Description: "A custom endpoint for the S3 API",
DefaultFunc: schema.EnvDefaultFunc("AWS_S3_ENDPOINT", ""),
},
"encrypt": &schema.Schema{
"encrypt": {
Type: schema.TypeBool,
Optional: true,
Description: "Whether to enable server side encryption of the state file",
Default: false,
},
"acl": &schema.Schema{
"acl": {
Type: schema.TypeString,
Optional: true,
Description: "Canned ACL to be applied to the state file",
Default: "",
},
"access_key": &schema.Schema{
"access_key": {
Type: schema.TypeString,
Optional: true,
Description: "AWS access key",
Default: "",
},
"secret_key": &schema.Schema{
"secret_key": {
Type: schema.TypeString,
Optional: true,
Description: "AWS secret key",
Default: "",
},
"kms_key_id": &schema.Schema{
"kms_key_id": {
Type: schema.TypeString,
Optional: true,
Description: "The ARN of a KMS Key to use for encrypting the state",
Default: "",
},
"lock_table": &schema.Schema{
"lock_table": {
Type: schema.TypeString,
Optional: true,
Description: "DynamoDB table for state locking",
Default: "",
},
"profile": &schema.Schema{
"profile": {
Type: schema.TypeString,
Optional: true,
Description: "AWS profile name",
Default: "",
},
"shared_credentials_file": &schema.Schema{
"shared_credentials_file": {
Type: schema.TypeString,
Optional: true,
Description: "Path to a shared credentials file",
Default: "",
},
"token": &schema.Schema{
"token": {
Type: schema.TypeString,
Optional: true,
Description: "MFA token",
Default: "",
},
"role_arn": &schema.Schema{
"role_arn": {
Type: schema.TypeString,
Optional: true,
Description: "The role to be assumed",
Default: "",
},
"session_name": {
Type: schema.TypeString,
Optional: true,
Description: "The session name to use when assuming the role.",
Default: "",
},
"external_id": {
Type: schema.TypeString,
Optional: true,
Description: "The external ID to use when assuming the role",
Default: "",
},
"assume_role_policy": {
Type: schema.TypeString,
Optional: true,
Description: "The permissions applied when assuming a role.",
Default: "",
},
},
}
@ -156,12 +177,15 @@ func (b *Backend) configure(ctx context.Context) error {
var errs []error
creds, err := terraformAWS.GetCredentials(&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),
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),
})
if err != nil {
return err

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/opc"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: opc.Provider,
})
}

View File

@ -5,6 +5,7 @@ import (
"log"
"time"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/hashicorp/terraform/helper/schema"
)
@ -17,24 +18,40 @@ func dataSourceAwsCallerIdentity() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"user_id": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func dataSourceAwsCallerIdentityRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient)
client := meta.(*AWSClient).stsconn
res, err := client.GetCallerIdentity(&sts.GetCallerIdentityInput{})
if err != nil {
return fmt.Errorf("Error getting Caller Identity: %v", err)
}
log.Printf("[DEBUG] Reading Caller Identity.")
d.SetId(time.Now().UTC().String())
if client.accountid == "" {
if *res.Account == "" {
log.Println("[DEBUG] No Account ID available, failing")
return fmt.Errorf("No AWS Account ID is available to the provider. Please ensure that\n" +
"skip_requesting_account_id is not set on the AWS provider.")
}
log.Printf("[DEBUG] Setting AWS Account ID to %s.", client.accountid)
d.Set("account_id", meta.(*AWSClient).accountid)
log.Printf("[DEBUG] Setting AWS Account ID to %s.", *res.Account)
d.Set("account_id", res.Account)
d.Set("arn", res.Arn)
d.Set("user_id", res.UserId)
return nil
}

View File

@ -39,6 +39,14 @@ func testAccCheckAwsCallerIdentityAccountId(n string) resource.TestCheckFunc {
return fmt.Errorf("Incorrect Account ID: expected %q, got %q", expected, rs.Primary.Attributes["account_id"])
}
if rs.Primary.Attributes["user_id"] == "" {
return fmt.Errorf("UserID expected to not be nil")
}
if rs.Primary.Attributes["arn"] == "" {
return fmt.Errorf("ARN expected to not be nil")
}
return nil
}
}

View File

@ -0,0 +1,67 @@
package aws
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/schema"
)
func dataSourceAwsIAMRole() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsIAMRoleRead,
Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"assume_role_policy_document": {
Type: schema.TypeString,
Computed: true,
},
"path": {
Type: schema.TypeString,
Computed: true,
},
"role_id": {
Type: schema.TypeString,
Computed: true,
},
"role_name": {
Type: schema.TypeString,
Required: true,
},
},
}
}
func dataSourceAwsIAMRoleRead(d *schema.ResourceData, meta interface{}) error {
iamconn := meta.(*AWSClient).iamconn
roleName := d.Get("role_name").(string)
req := &iam.GetRoleInput{
RoleName: aws.String(roleName),
}
resp, err := iamconn.GetRole(req)
if err != nil {
return errwrap.Wrapf("Error getting roles: {{err}}", err)
}
if resp == nil {
return fmt.Errorf("no IAM role found")
}
role := resp.Role
d.SetId(*role.RoleId)
d.Set("arn", role.Arn)
d.Set("assume_role_policy_document", role.AssumeRolePolicyDocument)
d.Set("path", role.Path)
d.Set("role_id", role.RoleId)
return nil
}

View File

@ -0,0 +1,59 @@
package aws
import (
"regexp"
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccAWSDataSourceIAMRole_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAwsIAMRoleConfig,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("data.aws_iam_role.test", "role_id"),
resource.TestCheckResourceAttr("data.aws_iam_role.test", "assume_role_policy_document", "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D"),
resource.TestCheckResourceAttr("data.aws_iam_role.test", "path", "/testpath/"),
resource.TestCheckResourceAttr("data.aws_iam_role.test", "role_name", "TestRole"),
resource.TestMatchResourceAttr("data.aws_iam_role.test", "arn", regexp.MustCompile("^arn:aws:iam::[0-9]{12}:role/testpath/TestRole$")),
),
},
},
})
}
const testAccAwsIAMRoleConfig = `
provider "aws" {
region = "us-east-1"
}
resource "aws_iam_role" "test_role" {
name = "TestRole"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
path = "/testpath/"
}
data "aws_iam_role" "test" {
role_name = "${aws_iam_role.test_role.name}"
}
`

View File

@ -136,7 +136,7 @@ func dataSourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) erro
if matchingTags && matchingVPC {
if hostedZoneFound != nil {
return fmt.Errorf("multplie Route53Zone found please use vpc_id option to filter")
return fmt.Errorf("multiple Route53Zone found please use vpc_id option to filter")
} else {
hostedZoneFound = hostedZone
}

View File

@ -7,6 +7,10 @@ import (
)
func resourceAwsCloudFrontDistributionImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
// This is a non API attribute
// We are merely setting this to the same value as the Default setting in the schema
d.Set("retain_on_delete", false)
conn := meta.(*AWSClient).cloudfrontconn
id := d.Id()
resp, err := conn.GetDistributionConfig(&cloudfront.GetDistributionConfigInput{

View File

@ -19,16 +19,13 @@ func TestAccAWSCloudFrontDistribution_importBasic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudFrontDistributionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testConfig,
},
resource.TestStep{
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
// Ignore retain_on_delete since it doesn't come from the AWS
// API.
ImportStateVerifyIgnore: []string{"retain_on_delete"},
},
},
})

View File

@ -174,6 +174,7 @@ func Provider() terraform.ResourceProvider {
"aws_elb_service_account": dataSourceAwsElbServiceAccount(),
"aws_iam_account_alias": dataSourceAwsIamAccountAlias(),
"aws_iam_policy_document": dataSourceAwsIamPolicyDocument(),
"aws_iam_role": dataSourceAwsIAMRole(),
"aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(),
"aws_instance": dataSourceAwsInstance(),
"aws_ip_ranges": dataSourceAwsIPRanges(),
@ -337,6 +338,8 @@ func Provider() terraform.ResourceProvider {
"aws_lightsail_domain": resourceAwsLightsailDomain(),
"aws_lightsail_instance": resourceAwsLightsailInstance(),
"aws_lightsail_key_pair": resourceAwsLightsailKeyPair(),
"aws_lightsail_static_ip": resourceAwsLightsailStaticIp(),
"aws_lightsail_static_ip_attachment": resourceAwsLightsailStaticIpAttachment(),
"aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(),
"aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(),
"aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(),
@ -383,6 +386,7 @@ func Provider() terraform.ResourceProvider {
"aws_route_table": resourceAwsRouteTable(),
"aws_route_table_association": resourceAwsRouteTableAssociation(),
"aws_ses_active_receipt_rule_set": resourceAwsSesActiveReceiptRuleSet(),
"aws_ses_domain_identity": resourceAwsSesDomainIdentity(),
"aws_ses_receipt_filter": resourceAwsSesReceiptFilter(),
"aws_ses_receipt_rule": resourceAwsSesReceiptRule(),
"aws_ses_receipt_rule_set": resourceAwsSesReceiptRuleSet(),

View File

@ -73,6 +73,7 @@ func resourceAwsAlbTargetGroup() *schema.Resource {
"stickiness": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@ -258,11 +259,19 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err
for _, attr := range attrResp.Attributes {
switch *attr.Key {
case "stickiness.enabled":
stickinessMap["enabled"] = *attr.Value
enabled, err := strconv.ParseBool(*attr.Value)
if err != nil {
return fmt.Errorf("Error converting stickiness.enabled to bool: %s", *attr.Value)
}
stickinessMap["enabled"] = enabled
case "stickiness.type":
stickinessMap["type"] = *attr.Value
case "stickiness.lb_cookie.duration_seconds":
stickinessMap["cookie_duration"] = *attr.Value
duration, err := strconv.Atoi(*attr.Value)
if err != nil {
return fmt.Errorf("Error converting stickiness.lb_cookie.duration_seconds to int: %s", *attr.Value)
}
stickinessMap["cookie_duration"] = duration
case "deregistration_delay.timeout_seconds":
timeout, err := strconv.Atoi(*attr.Value)
if err != nil {
@ -271,7 +280,24 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err
d.Set("deregistration_delay", timeout)
}
}
d.Set("stickiness", []interface{}{stickinessMap})
if err := d.Set("stickiness", []interface{}{stickinessMap}); err != nil {
return err
}
tagsResp, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{
ResourceArns: []*string{aws.String(d.Id())},
})
if err != nil {
return errwrap.Wrapf("Error retrieving Target Group Tags: {{err}}", err)
}
for _, t := range tagsResp.TagDescriptions {
if *t.ResourceArn == d.Id() {
if err := d.Set("tags", tagsToMapELBv2(t.Tags)); err != nil {
return err
}
}
}
return nil
}

View File

@ -77,6 +77,8 @@ func TestAccAWSALBTargetGroup_basic(t *testing.T) {
resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.healthy_threshold", "3"),
resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.unhealthy_threshold", "3"),
resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.matcher", "200-299"),
resource.TestCheckResourceAttr("aws_alb_target_group.test", "tags.%", "1"),
resource.TestCheckResourceAttr("aws_alb_target_group.test", "tags.TestName", "TestAccAWSALBTargetGroup_basic"),
),
},
},

View File

@ -18,7 +18,7 @@ import (
)
const (
AWSAMIRetryTimeout = 10 * time.Minute
AWSAMIRetryTimeout = 20 * time.Minute
AWSAMIDeleteRetryTimeout = 20 * time.Minute
AWSAMIRetryDelay = 5 * time.Second
AWSAMIRetryMinTimeout = 3 * time.Second

View File

@ -11,87 +11,94 @@ import (
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"strings"
)
func resourceAwsApiGatewayIntegration() *schema.Resource {
return &schema.Resource{
Create: resourceAwsApiGatewayIntegrationCreate,
Read: resourceAwsApiGatewayIntegrationRead,
Update: resourceAwsApiGatewayIntegrationCreate,
Update: resourceAwsApiGatewayIntegrationUpdate,
Delete: resourceAwsApiGatewayIntegrationDelete,
Schema: map[string]*schema.Schema{
"rest_api_id": &schema.Schema{
"rest_api_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"resource_id": &schema.Schema{
"resource_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"http_method": &schema.Schema{
"http_method": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateHTTPMethod,
},
"type": &schema.Schema{
"type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateApiGatewayIntegrationType,
},
"uri": &schema.Schema{
"uri": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"credentials": &schema.Schema{
"credentials": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"integration_http_method": &schema.Schema{
"integration_http_method": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateHTTPMethod,
},
"request_templates": &schema.Schema{
"request_templates": {
Type: schema.TypeMap,
Optional: true,
Elem: schema.TypeString,
},
"request_parameters": &schema.Schema{
"request_parameters": {
Type: schema.TypeMap,
Elem: schema.TypeString,
Optional: true,
ConflictsWith: []string{"request_parameters_in_json"},
},
"request_parameters_in_json": &schema.Schema{
"request_parameters_in_json": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"request_parameters"},
Deprecated: "Use field request_parameters instead",
},
"content_handling": &schema.Schema{
"content_handling": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateApiGatewayIntegrationContentHandling,
},
"passthrough_behavior": &schema.Schema{
"passthrough_behavior": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateApiGatewayIntegrationPassthroughBehavior,
},
},
@ -101,6 +108,7 @@ func resourceAwsApiGatewayIntegration() *schema.Resource {
func resourceAwsApiGatewayIntegrationCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Print("[DEBUG] Creating API Gateway Integration")
var integrationHttpMethod *string
if v, ok := d.GetOk("integration_http_method"); ok {
integrationHttpMethod = aws.String(v.(string))
@ -163,13 +171,13 @@ func resourceAwsApiGatewayIntegrationCreate(d *schema.ResourceData, meta interfa
d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string)))
return nil
return resourceAwsApiGatewayIntegrationRead(d, meta)
}
func resourceAwsApiGatewayIntegrationRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Reading API Gateway Integration %s", d.Id())
log.Printf("[DEBUG] Reading API Gateway Integration: %s", d.Id())
integration, err := conn.GetIntegration(&apigateway.GetIntegrationInput{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
@ -191,17 +199,127 @@ func resourceAwsApiGatewayIntegrationRead(d *schema.ResourceData, meta interface
}
d.Set("request_templates", aws.StringValueMap(integration.RequestTemplates))
d.Set("credentials", integration.Credentials)
d.Set("type", integration.Type)
d.Set("uri", integration.Uri)
d.Set("request_parameters", aws.StringValueMap(integration.RequestParameters))
d.Set("request_parameters_in_json", aws.StringValueMap(integration.RequestParameters))
d.Set("passthrough_behavior", integration.PassthroughBehavior)
d.Set("content_handling", integration.ContentHandling)
if integration.Uri != nil {
d.Set("uri", integration.Uri)
}
if integration.Credentials != nil {
d.Set("credentials", integration.Credentials)
}
if integration.ContentHandling != nil {
d.Set("content_handling", integration.ContentHandling)
}
return nil
}
func resourceAwsApiGatewayIntegrationUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Updating API Gateway Integration: %s", d.Id())
operations := make([]*apigateway.PatchOperation, 0)
// https://docs.aws.amazon.com/apigateway/api-reference/link-relation/integration-update/#remarks
// According to the above documentation, only a few parts are addable / removable.
if d.HasChange("request_templates") {
o, n := d.GetChange("request_templates")
prefix := "requestTemplates"
os := o.(map[string]interface{})
ns := n.(map[string]interface{})
// Handle Removal
for k := range os {
if _, ok := ns[k]; !ok {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
})
}
}
for k, v := range ns {
// Handle replaces
if _, ok := os[k]; ok {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
Value: aws.String(v.(string)),
})
}
// Handle additions
if _, ok := os[k]; !ok {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
Value: aws.String(v.(string)),
})
}
}
}
if d.HasChange("request_parameters") {
o, n := d.GetChange("request_parameters")
prefix := "requestParameters"
os := o.(map[string]interface{})
ns := n.(map[string]interface{})
// Handle Removal
for k := range os {
if _, ok := ns[k]; !ok {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
})
}
}
for k, v := range ns {
// Handle replaces
if _, ok := os[k]; ok {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("replace"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
Value: aws.String(v.(string)),
})
}
// Handle additions
if _, ok := os[k]; !ok {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
Value: aws.String(v.(string)),
})
}
}
}
params := &apigateway.UpdateIntegrationInput{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
RestApiId: aws.String(d.Get("rest_api_id").(string)),
PatchOperations: operations,
}
_, err := conn.UpdateIntegration(params)
if err != nil {
return fmt.Errorf("Error updating API Gateway Integration: %s", err)
}
d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string)))
return resourceAwsApiGatewayIntegrationRead(d, meta)
}
func resourceAwsApiGatewayIntegrationDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Integration: %s", d.Id())

View File

@ -19,88 +19,80 @@ func TestAccAWSAPIGatewayIntegration_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayIntegrationDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSAPIGatewayIntegrationConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
testAccCheckAWSAPIGatewayIntegrationAttributes(&conf),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "type", "HTTP"),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "integration_http_method", "GET"),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "uri", "https://www.google.de"),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "request_templates.application/json", ""),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
resource.TestCheckNoResourceAttr(
"aws_api_gateway_integration.test", "content_handling"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'static'"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Foo", "'Bar'"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", ""),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
),
},
resource.TestStep{
{
Config: testAccAWSAPIGatewayIntegrationConfigUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
testAccCheckAWSAPIGatewayMockIntegrationAttributes(&conf),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "type", "MOCK"),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "integration_http_method", ""),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "uri", ""),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "passthrough_behavior", "NEVER"),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_BINARY"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'updated'"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-FooBar", "'Baz'"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", "{'foobar': 'bar}"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.text/html", "<html>Foo</html>"),
),
},
{
Config: testAccAWSAPIGatewayIntegrationConfigUpdateNoTemplates,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "0"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "0"),
),
},
{
Config: testAccAWSAPIGatewayIntegrationConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'static'"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", ""),
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
),
},
},
})
}
func testAccCheckAWSAPIGatewayMockIntegrationAttributes(conf *apigateway.Integration) resource.TestCheckFunc {
return func(s *terraform.State) error {
if *conf.Type != "MOCK" {
return fmt.Errorf("Wrong Type: %q", *conf.Type)
}
if *conf.RequestParameters["integration.request.header.X-Authorization"] != "'updated'" {
return fmt.Errorf("wrong updated RequestParameters for header.X-Authorization")
}
if *conf.ContentHandling != "CONVERT_TO_BINARY" {
return fmt.Errorf("wrong ContentHandling: %q", *conf.ContentHandling)
}
return nil
}
}
func testAccCheckAWSAPIGatewayIntegrationAttributes(conf *apigateway.Integration) resource.TestCheckFunc {
return func(s *terraform.State) error {
if *conf.HttpMethod == "" {
return fmt.Errorf("empty HttpMethod")
}
if *conf.Uri != "https://www.google.de" {
return fmt.Errorf("wrong Uri")
}
if *conf.Type != "HTTP" {
return fmt.Errorf("wrong Type")
}
if conf.RequestTemplates["application/json"] != nil {
return fmt.Errorf("wrong RequestTemplate for application/json")
}
if *conf.RequestTemplates["application/xml"] != "#set($inputRoot = $input.path('$'))\n{ }" {
return fmt.Errorf("wrong RequestTemplate for application/xml")
}
if *conf.RequestParameters["integration.request.header.X-Authorization"] != "'static'" {
return fmt.Errorf("wrong RequestParameters for header.X-Authorization")
}
return nil
}
}
func testAccCheckAWSAPIGatewayIntegrationExists(n string, res *apigateway.Integration) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
@ -196,13 +188,15 @@ resource "aws_api_gateway_integration" "test" {
}
request_parameters = {
"integration.request.header.X-Authorization" = "'static'"
"integration.request.header.X-Authorization" = "'static'"
"integration.request.header.X-Foo" = "'Bar'"
}
type = "HTTP"
uri = "https://www.google.de"
integration_http_method = "GET"
passthrough_behavior = "WHEN_NO_MATCH"
content_handling = "CONVERT_TO_TEXT"
}
`
@ -233,13 +227,55 @@ resource "aws_api_gateway_integration" "test" {
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
request_parameters = {
"integration.request.header.X-Authorization" = "'updated'"
request_templates = {
"application/json" = "{'foobar': 'bar}"
"text/html" = "<html>Foo</html>"
}
type = "MOCK"
passthrough_behavior = "NEVER"
content_handling = "CONVERT_TO_BINARY"
request_parameters = {
"integration.request.header.X-Authorization" = "'updated'"
"integration.request.header.X-FooBar" = "'Baz'"
}
type = "HTTP"
uri = "https://www.google.de"
integration_http_method = "GET"
passthrough_behavior = "WHEN_NO_MATCH"
content_handling = "CONVERT_TO_TEXT"
}
`
const testAccAWSAPIGatewayIntegrationConfigUpdateNoTemplates = `
resource "aws_api_gateway_rest_api" "test" {
name = "test"
}
resource "aws_api_gateway_resource" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
path_part = "test"
}
resource "aws_api_gateway_method" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "GET"
authorization = "NONE"
request_models = {
"application/json" = "Error"
}
}
resource "aws_api_gateway_integration" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
type = "HTTP"
uri = "https://www.google.de"
integration_http_method = "GET"
passthrough_behavior = "WHEN_NO_MATCH"
content_handling = "CONVERT_TO_TEXT"
}
`

View File

@ -101,11 +101,19 @@ func resourceAwsDbInstance() *schema.Resource {
},
"identifier": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"identifier_prefix"},
ValidateFunc: validateRdsIdentifier,
},
"identifier_prefix": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsId,
ValidateFunc: validateRdsIdentifierPrefix,
},
"instance_class": {
@ -336,10 +344,16 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error
conn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
identifier := d.Get("identifier").(string)
// Generate a unique ID for the user
if identifier == "" {
identifier = resource.PrefixedUniqueId("tf-")
var identifier string
if v, ok := d.GetOk("identifier"); ok {
identifier = v.(string)
} else {
if v, ok := d.GetOk("identifier_prefix"); ok {
identifier = resource.PrefixedUniqueId(v.(string))
} else {
identifier = resource.UniqueId()
}
// SQL Server identifier size is max 15 chars, so truncate
if engine := d.Get("engine").(string); engine != "" {
if strings.Contains(strings.ToLower(engine), "sqlserver") {
@ -407,7 +421,14 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error
}
if attr, ok := d.GetOk("name"); ok {
opts.DBName = aws.String(attr.(string))
// "Note: This parameter [DBName] doesn't apply to the MySQL, PostgreSQL, or MariaDB engines."
// https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_RestoreDBInstanceFromDBSnapshot.html
switch strings.ToLower(d.Get("engine").(string)) {
case "mysql", "postgres", "mariadb":
// skip
default:
opts.DBName = aws.String(attr.(string))
}
}
if attr, ok := d.GetOk("availability_zone"); ok {

View File

@ -53,6 +53,46 @@ func TestAccAWSDBInstance_basic(t *testing.T) {
})
}
func TestAccAWSDBInstance_namePrefix(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBInstanceConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBInstanceExists("aws_db_instance.test", &v),
testAccCheckAWSDBInstanceAttributes(&v),
resource.TestMatchResourceAttr(
"aws_db_instance.test", "identifier", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBInstance_generatedName(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBInstanceConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBInstanceExists("aws_db_instance.test", &v),
testAccCheckAWSDBInstanceAttributes(&v),
),
},
},
})
}
func TestAccAWSDBInstance_kmsKey(t *testing.T) {
var v rds.DBInstance
keyRegex := regexp.MustCompile("^arn:aws:kms:")
@ -613,8 +653,8 @@ resource "aws_db_instance" "bar" {
username = "foo"
# Maintenance Window is stored in lower case in the API, though not strictly
# documented. Terraform will downcase this to match (as opposed to throw a
# Maintenance Window is stored in lower case in the API, though not strictly
# documented. Terraform will downcase this to match (as opposed to throw a
# validation error).
maintenance_window = "Fri:09:00-Fri:09:30"
skip_final_snapshot = true
@ -628,6 +668,37 @@ resource "aws_db_instance" "bar" {
}
}`
const testAccAWSDBInstanceConfig_namePrefix = `
resource "aws_db_instance" "test" {
allocated_storage = 10
engine = "MySQL"
identifier_prefix = "tf-test-"
instance_class = "db.t1.micro"
password = "password"
username = "root"
publicly_accessible = true
skip_final_snapshot = true
timeouts {
create = "30m"
}
}`
const testAccAWSDBInstanceConfig_generatedName = `
resource "aws_db_instance" "test" {
allocated_storage = 10
engine = "MySQL"
instance_class = "db.t1.micro"
password = "password"
username = "root"
publicly_accessible = true
skip_final_snapshot = true
timeouts {
create = "30m"
}
}`
var testAccAWSDBInstanceConfigKmsKeyId = `
resource "aws_kms_key" "foo" {
description = "Terraform acc test %s"
@ -720,7 +791,7 @@ func testAccReplicaInstanceConfig(val int) string {
parameter_group_name = "default.mysql5.6"
}
resource "aws_db_instance" "replica" {
identifier = "tf-replica-db-%d"
backup_retention_period = 0

View File

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"log"
"regexp"
"time"
"github.com/aws/aws-sdk-go/aws"
@ -31,10 +30,19 @@ func resourceAwsDbOptionGroup() *schema.Resource {
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbOptionGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ValidateFunc: validateDbOptionGroupName,
ValidateFunc: validateDbOptionGroupNamePrefix,
},
"engine_name": &schema.Schema{
Type: schema.TypeString,
@ -48,8 +56,9 @@ func resourceAwsDbOptionGroup() *schema.Resource {
},
"option_group_description": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
ForceNew: true,
Default: "Managed by Terraform",
},
"option": &schema.Schema{
@ -107,11 +116,20 @@ func resourceAwsDbOptionGroupCreate(d *schema.ResourceData, meta interface{}) er
rdsconn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := &rds.CreateOptionGroupInput{
EngineName: aws.String(d.Get("engine_name").(string)),
MajorEngineVersion: aws.String(d.Get("major_engine_version").(string)),
OptionGroupDescription: aws.String(d.Get("option_group_description").(string)),
OptionGroupName: aws.String(d.Get("name").(string)),
OptionGroupName: aws.String(groupName),
Tags: tags,
}
@ -121,7 +139,7 @@ func resourceAwsDbOptionGroupCreate(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error creating DB Option Group: %s", err)
}
d.SetId(d.Get("name").(string))
d.SetId(groupName)
log.Printf("[INFO] DB Option Group ID: %s", d.Id())
return resourceAwsDbOptionGroupUpdate(d, meta)
@ -343,28 +361,3 @@ func buildRDSOptionGroupARN(identifier, partition, accountid, region string) (st
arn := fmt.Sprintf("arn:%s:rds:%s:%s:og:%s", partition, region, accountid, identifier)
return arn, nil
}
func validateDbOptionGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters and hyphens allowed in %q", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if regexp.MustCompile(`-$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot end with a hyphen", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 255 characters", k))
}
return
}

View File

@ -2,6 +2,7 @@ package aws
import (
"fmt"
"regexp"
"testing"
"github.com/aws/aws-sdk-go/aws"
@ -34,6 +35,66 @@ func TestAccAWSDBOptionGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBOptionGroup_namePrefix(t *testing.T) {
var v rds.OptionGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBOptionGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDBOptionGroup_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v),
testAccCheckAWSDBOptionGroupAttributes(&v),
resource.TestMatchResourceAttr(
"aws_db_option_group.test", "name", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBOptionGroup_generatedName(t *testing.T) {
var v rds.OptionGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBOptionGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDBOptionGroup_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v),
testAccCheckAWSDBOptionGroupAttributes(&v),
),
},
},
})
}
func TestAccAWSDBOptionGroup_defaultDescription(t *testing.T) {
var v rds.OptionGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBOptionGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDBOptionGroup_defaultDescription(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v),
resource.TestCheckResourceAttr(
"aws_db_option_group.test", "option_group_description", "Managed by Terraform"),
),
},
},
})
}
func TestAccAWSDBOptionGroup_basicDestroyWithInstance(t *testing.T) {
rName := fmt.Sprintf("option-group-test-terraform-%s", acctest.RandString(5))
@ -160,42 +221,6 @@ func testAccCheckAWSDBOptionGroupAttributes(v *rds.OptionGroup) resource.TestChe
}
}
func TestResourceAWSDBOptionGroupName_validation(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "testing123!",
ErrCount: 1,
},
{
Value: "1testing123",
ErrCount: 1,
},
{
Value: "testing--123",
ErrCount: 1,
},
{
Value: "testing123-",
ErrCount: 1,
},
{
Value: randomString(256),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbOptionGroupName(tc.Value, "aws_db_option_group_name")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Option Group Name to trigger a validation error")
}
}
}
func testAccCheckAWSDBOptionGroupExists(n string, v *rds.OptionGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
@ -387,3 +412,30 @@ resource "aws_db_option_group" "bar" {
}
`, r)
}
const testAccAWSDBOptionGroup_namePrefix = `
resource "aws_db_option_group" "test" {
name_prefix = "tf-test-"
option_group_description = "Test option group for terraform"
engine_name = "mysql"
major_engine_version = "5.6"
}
`
const testAccAWSDBOptionGroup_generatedName = `
resource "aws_db_option_group" "test" {
option_group_description = "Test option group for terraform"
engine_name = "mysql"
major_engine_version = "5.6"
}
`
func testAccAWSDBOptionGroup_defaultDescription(n int) string {
return fmt.Sprintf(`
resource "aws_db_option_group" "test" {
name = "tf-test-%d"
engine_name = "mysql"
major_engine_version = "5.6"
}
`, n)
}

View File

@ -32,10 +32,19 @@ func resourceAwsDbParameterGroup() *schema.Resource {
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbParamGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ValidateFunc: validateDbParamGroupName,
ValidateFunc: validateDbParamGroupNamePrefix,
},
"family": &schema.Schema{
Type: schema.TypeString,
@ -81,8 +90,17 @@ func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{})
rdsconn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := rds.CreateDBParameterGroupInput{
DBParameterGroupName: aws.String(d.Get("name").(string)),
DBParameterGroupName: aws.String(groupName),
DBParameterGroupFamily: aws.String(d.Get("family").(string)),
Description: aws.String(d.Get("description").(string)),
Tags: tags,

View File

@ -3,6 +3,7 @@ package aws
import (
"fmt"
"math/rand"
"regexp"
"testing"
"time"
@ -290,6 +291,44 @@ func TestAccAWSDBParameterGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBParameterGroup_namePrefix(t *testing.T) {
var v rds.DBParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBParameterGroupConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBParameterGroupExists("aws_db_parameter_group.test", &v),
resource.TestMatchResourceAttr(
"aws_db_parameter_group.test", "name", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBParameterGroup_generatedName(t *testing.T) {
var v rds.DBParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBParameterGroupConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBParameterGroupExists("aws_db_parameter_group.test", &v),
),
},
},
})
}
func TestAccAWSDBParameterGroup_withApplyMethod(t *testing.T) {
var v rds.DBParameterGroup
@ -671,3 +710,16 @@ resource "aws_db_parameter_group" "large" {
parameter { name = "tx_isolation" value = "REPEATABLE-READ" }
}`, n)
}
const testAccDBParameterGroupConfig_namePrefix = `
resource "aws_db_parameter_group" "test" {
name_prefix = "tf-test-"
family = "mysql5.6"
}
`
const testAccDBParameterGroupConfig_generatedName = `
resource "aws_db_parameter_group" "test" {
family = "mysql5.6"
}
`

View File

@ -3,7 +3,6 @@ package aws
import (
"fmt"
"log"
"regexp"
"strings"
"time"
@ -31,10 +30,19 @@ func resourceAwsDbSubnetGroup() *schema.Resource {
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbSubnetGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ValidateFunc: validateSubnetGroupName,
ValidateFunc: validateDbSubnetGroupNamePrefix,
},
"description": &schema.Schema{
@ -65,8 +73,17 @@ func resourceAwsDbSubnetGroupCreate(d *schema.ResourceData, meta interface{}) er
subnetIds[i] = aws.String(subnetId.(string))
}
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := rds.CreateDBSubnetGroupInput{
DBSubnetGroupName: aws.String(d.Get("name").(string)),
DBSubnetGroupName: aws.String(groupName),
DBSubnetGroupDescription: aws.String(d.Get("description").(string)),
SubnetIds: subnetIds,
Tags: tags,
@ -238,20 +255,3 @@ func buildRDSsubgrpARN(identifier, partition, accountid, region string) (string,
return arn, nil
}
func validateSubnetGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 255 characters", k))
}
if regexp.MustCompile(`(?i)^default$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q is not allowed as %q", "Default", k))
}
return
}

View File

@ -2,6 +2,7 @@ package aws
import (
"fmt"
"regexp"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
@ -43,6 +44,46 @@ func TestAccAWSDBSubnetGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBSubnetGroup_namePrefix(t *testing.T) {
var v rds.DBSubnetGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDBSubnetGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBSubnetGroupConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckDBSubnetGroupExists(
"aws_db_subnet_group.test", &v),
resource.TestMatchResourceAttr(
"aws_db_subnet_group.test", "name", regexp.MustCompile("^tf_test-")),
),
},
},
})
}
func TestAccAWSDBSubnetGroup_generatedName(t *testing.T) {
var v rds.DBSubnetGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDBSubnetGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDBSubnetGroupConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckDBSubnetGroupExists(
"aws_db_subnet_group.test", &v),
),
},
},
})
}
// Regression test for https://github.com/hashicorp/terraform/issues/2603 and
// https://github.com/hashicorp/terraform/issues/2664
func TestAccAWSDBSubnetGroup_withUndocumentedCharacters(t *testing.T) {
@ -105,38 +146,6 @@ func TestAccAWSDBSubnetGroup_updateDescription(t *testing.T) {
})
}
func TestResourceAWSDBSubnetGroupNameValidation(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "tEsting",
ErrCount: 1,
},
{
Value: "testing?",
ErrCount: 1,
},
{
Value: "default",
ErrCount: 1,
},
{
Value: randomString(300),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateSubnetGroupName(tc.Value, "aws_db_subnet_group")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Subnet Group name to trigger a validation error")
}
}
}
func testAccCheckDBSubnetGroupDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).rdsconn
@ -263,6 +272,49 @@ resource "aws_db_subnet_group" "foo" {
}`, rName)
}
const testAccDBSubnetGroupConfig_namePrefix = `
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.1.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.2.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name_prefix = "tf_test-"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}`
const testAccDBSubnetGroupConfig_generatedName = `
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.1.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.2.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}`
const testAccDBSubnetGroupConfig_withUnderscoresAndPeriodsAndSpaces = `
resource "aws_vpc" "main" {
cidr_block = "192.168.0.0/16"

View File

@ -118,6 +118,12 @@ func resourceAwsEcsService() *schema.Resource {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if strings.ToLower(old) == strings.ToLower(new) {
return true
}
return false
},
},
},
},

View File

@ -45,6 +45,7 @@ func resourceAwsEcsTaskDefinition() *schema.Resource {
hash := sha1.Sum([]byte(v.(string)))
return hex.EncodeToString(hash[:])
},
ValidateFunc: validateAwsEcsTaskDefinitionContainerDefinitions,
},
"task_role_arn": {
@ -121,6 +122,15 @@ func validateAwsEcsTaskDefinitionNetworkMode(v interface{}, k string) (ws []stri
return
}
func validateAwsEcsTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
_, err := expandEcsContainerDefinitions(value)
if err != nil {
errors = append(errors, fmt.Errorf("ECS Task Definition container_definitions is invalid: %s", err))
}
return
}
func resourceAwsEcsTaskDefinitionCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ecsconn

View File

@ -203,6 +203,28 @@ func TestValidateAwsEcsTaskDefinitionNetworkMode(t *testing.T) {
}
}
func TestValidateAwsEcsTaskDefinitionContainerDefinitions(t *testing.T) {
validDefinitions := []string{
testValidateAwsEcsTaskDefinitionValidContainerDefinitions,
}
for _, v := range validDefinitions {
_, errors := validateAwsEcsTaskDefinitionContainerDefinitions(v, "container_definitions")
if len(errors) != 0 {
t.Fatalf("%q should be a valid AWS ECS Task Definition Container Definitions: %q", v, errors)
}
}
invalidDefinitions := []string{
testValidateAwsEcsTaskDefinitionInvalidCommandContainerDefinitions,
}
for _, v := range invalidDefinitions {
_, errors := validateAwsEcsTaskDefinitionContainerDefinitions(v, "container_definitions")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid AWS ECS Task Definition Container Definitions", v)
}
}
}
func testAccCheckAWSEcsTaskDefinitionDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ecsconn
@ -666,3 +688,29 @@ TASK_DEFINITION
}
}
`
var testValidateAwsEcsTaskDefinitionValidContainerDefinitions = `
[
{
"name": "sleep",
"image": "busybox",
"cpu": 10,
"command": ["sleep","360"],
"memory": 10,
"essential": true
}
]
`
var testValidateAwsEcsTaskDefinitionInvalidCommandContainerDefinitions = `
[
{
"name": "sleep",
"image": "busybox",
"cpu": 10,
"command": "sleep 360",
"memory": 10,
"essential": true
}
]
`

View File

@ -83,6 +83,7 @@ func resourceAwsElasticSearchDomain() *schema.Resource {
"volume_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
},

View File

@ -85,6 +85,13 @@ func testAccESDomainPolicyConfig(randInt int, policy string) string {
resource "aws_elasticsearch_domain" "example" {
domain_name = "tf-test-%d"
elasticsearch_version = "2.3"
cluster_config {
instance_type = "t2.micro.elasticsearch"
}
ebs_options {
ebs_enabled = true
volume_size = 10
}
}
resource "aws_elasticsearch_domain_policy" "main" {

View File

@ -96,7 +96,7 @@ func TestAccAWSElasticSearchDomain_complex(t *testing.T) {
})
}
func TestAccAWSElasticSearch_tags(t *testing.T) {
func TestAccAWSElasticSearchDomain_tags(t *testing.T) {
var domain elasticsearch.ElasticsearchDomainStatus
var td elasticsearch.ListTagsOutput
ri := acctest.RandInt()
@ -198,6 +198,10 @@ func testAccESDomainConfig(randInt int) string {
return fmt.Sprintf(`
resource "aws_elasticsearch_domain" "example" {
domain_name = "tf-test-%d"
ebs_options {
ebs_enabled = true
volume_size = 10
}
}
`, randInt)
}
@ -206,6 +210,10 @@ func testAccESDomainConfig_TagUpdate(randInt int) string {
return fmt.Sprintf(`
resource "aws_elasticsearch_domain" "example" {
domain_name = "tf-test-%d"
ebs_options {
ebs_enabled = true
volume_size = 10
}
tags {
foo = "bar"
@ -220,6 +228,10 @@ 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
}
@ -248,6 +260,10 @@ func testAccESDomainConfigV23(randInt int) string {
return fmt.Sprintf(`
resource "aws_elasticsearch_domain" "example" {
domain_name = "tf-test-%d"
ebs_options {
ebs_enabled = true
volume_size = 10
}
elasticsearch_version = "2.3"
}
`, randInt)

View File

@ -287,7 +287,7 @@ func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
}
log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := elbconn.CreateLoadBalancer(elbOpts)
if err != nil {
@ -488,7 +488,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
// Occasionally AWS will error with a 'duplicate listener', without any
// other listeners on the ELB. Retry here to eliminate that.
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] ELB Create Listeners opts: %s", createListenersOpts)
if _, err := elbconn.CreateLoadBalancerListeners(createListenersOpts); err != nil {
if awsErr, ok := err.(awserr.Error); ok {
@ -746,7 +746,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
}
log.Printf("[DEBUG] ELB attach subnets opts: %s", attachOpts)
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := elbconn.AttachLoadBalancerToSubnets(attachOpts)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {

View File

@ -297,14 +297,13 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
err := resource.Retry(10*time.Minute, func() *resource.RetryError {
_, err := conn.CreateFunction(params)
if err != nil {
log.Printf("[ERROR] Received %q, retrying CreateFunction", err)
if awserr, ok := err.(awserr.Error); ok {
if awserr.Code() == "InvalidParameterValueException" {
log.Printf("[DEBUG] InvalidParameterValueException creating Lambda Function: %s", awserr)
return resource.RetryableError(awserr)
}
}
log.Printf("[DEBUG] Error creating Lambda Function: %s", err)
if isAWSErr(err, "InvalidParameterValueException", "The role defined for the function cannot be assumed by Lambda") {
log.Printf("[DEBUG] Received %s, retrying CreateFunction", err)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil

View File

@ -0,0 +1,98 @@
package aws
import (
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsLightsailStaticIp() *schema.Resource {
return &schema.Resource{
Create: resourceAwsLightsailStaticIpCreate,
Read: resourceAwsLightsailStaticIpRead,
Delete: resourceAwsLightsailStaticIpDelete,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"support_code": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceAwsLightsailStaticIpCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
name := d.Get("name").(string)
log.Printf("[INFO] Allocating Lightsail Static IP: %q", name)
out, err := conn.AllocateStaticIp(&lightsail.AllocateStaticIpInput{
StaticIpName: aws.String(name),
})
if err != nil {
return err
}
log.Printf("[INFO] Lightsail Static IP allocated: %s", *out)
d.SetId(name)
return resourceAwsLightsailStaticIpRead(d, meta)
}
func resourceAwsLightsailStaticIpRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
name := d.Get("name").(string)
log.Printf("[INFO] Reading Lightsail Static IP: %q", name)
out, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{
StaticIpName: aws.String(name),
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
log.Printf("[WARN] Lightsail Static IP (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
}
return err
}
log.Printf("[INFO] Received Lightsail Static IP: %s", *out)
d.Set("arn", out.StaticIp.Arn)
d.Set("ip_address", out.StaticIp.IpAddress)
d.Set("support_code", out.StaticIp.SupportCode)
return nil
}
func resourceAwsLightsailStaticIpDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
name := d.Get("name").(string)
log.Printf("[INFO] Deleting Lightsail Static IP: %q", name)
out, err := conn.ReleaseStaticIp(&lightsail.ReleaseStaticIpInput{
StaticIpName: aws.String(name),
})
if err != nil {
return err
}
log.Printf("[INFO] Deleted Lightsail Static IP: %s", *out)
return nil
}

View File

@ -0,0 +1,96 @@
package aws
import (
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsLightsailStaticIpAttachment() *schema.Resource {
return &schema.Resource{
Create: resourceAwsLightsailStaticIpAttachmentCreate,
Read: resourceAwsLightsailStaticIpAttachmentRead,
Delete: resourceAwsLightsailStaticIpAttachmentDelete,
Schema: map[string]*schema.Schema{
"static_ip_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"instance_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}
func resourceAwsLightsailStaticIpAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
staticIpName := d.Get("static_ip_name").(string)
log.Printf("[INFO] Attaching Lightsail Static IP: %q", staticIpName)
out, err := conn.AttachStaticIp(&lightsail.AttachStaticIpInput{
StaticIpName: aws.String(staticIpName),
InstanceName: aws.String(d.Get("instance_name").(string)),
})
if err != nil {
return err
}
log.Printf("[INFO] Lightsail Static IP attached: %s", *out)
d.SetId(staticIpName)
return resourceAwsLightsailStaticIpAttachmentRead(d, meta)
}
func resourceAwsLightsailStaticIpAttachmentRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
staticIpName := d.Get("static_ip_name").(string)
log.Printf("[INFO] Reading Lightsail Static IP: %q", staticIpName)
out, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{
StaticIpName: aws.String(staticIpName),
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
log.Printf("[WARN] Lightsail Static IP (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
}
return err
}
if !*out.StaticIp.IsAttached {
log.Printf("[WARN] Lightsail Static IP (%s) is not attached, removing from state", d.Id())
d.SetId("")
return nil
}
log.Printf("[INFO] Received Lightsail Static IP: %s", *out)
d.Set("instance_name", out.StaticIp.AttachedTo)
return nil
}
func resourceAwsLightsailStaticIpAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
name := d.Get("static_ip_name").(string)
log.Printf("[INFO] Detaching Lightsail Static IP: %q", name)
out, err := conn.DetachStaticIp(&lightsail.DetachStaticIpInput{
StaticIpName: aws.String(name),
})
if err != nil {
return err
}
log.Printf("[INFO] Detached Lightsail Static IP: %s", *out)
return nil
}

View File

@ -0,0 +1,163 @@
package aws
import (
"errors"
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSLightsailStaticIpAttachment_basic(t *testing.T) {
var staticIp lightsail.StaticIp
staticIpName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
instanceName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
keypairName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLightsailStaticIpAttachmentDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLightsailStaticIpAttachmentConfig_basic(staticIpName, instanceName, keypairName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLightsailStaticIpAttachmentExists("aws_lightsail_static_ip_attachment.test", &staticIp),
),
},
},
})
}
func TestAccAWSLightsailStaticIpAttachment_disappears(t *testing.T) {
var staticIp lightsail.StaticIp
staticIpName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
instanceName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
keypairName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
staticIpDestroy := func(*terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
_, err := conn.DetachStaticIp(&lightsail.DetachStaticIpInput{
StaticIpName: aws.String(staticIpName),
})
if err != nil {
return fmt.Errorf("Error deleting Lightsail Static IP in disappear test")
}
return nil
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLightsailStaticIpAttachmentDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLightsailStaticIpAttachmentConfig_basic(staticIpName, instanceName, keypairName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLightsailStaticIpAttachmentExists("aws_lightsail_static_ip_attachment.test", &staticIp),
staticIpDestroy,
),
ExpectNonEmptyPlan: true,
},
},
})
}
func testAccCheckAWSLightsailStaticIpAttachmentExists(n string, staticIp *lightsail.StaticIp) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return errors.New("No Lightsail Static IP Attachment ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
resp, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{
StaticIpName: aws.String(rs.Primary.ID),
})
if err != nil {
return err
}
if resp == nil || resp.StaticIp == nil {
return fmt.Errorf("Static IP (%s) not found", rs.Primary.ID)
}
if !*resp.StaticIp.IsAttached {
return fmt.Errorf("Static IP (%s) not attached", rs.Primary.ID)
}
*staticIp = *resp.StaticIp
return nil
}
}
func testAccCheckAWSLightsailStaticIpAttachmentDestroy(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_lightsail_static_ip_attachment" {
continue
}
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
resp, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{
StaticIpName: aws.String(rs.Primary.ID),
})
if err == nil {
if *resp.StaticIp.IsAttached {
return fmt.Errorf("Lightsail Static IP %q is still attached (to %q)", rs.Primary.ID, *resp.StaticIp.AttachedTo)
}
}
// Verify the error
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
return nil
}
}
return err
}
return nil
}
func testAccAWSLightsailStaticIpAttachmentConfig_basic(staticIpName, instanceName, keypairName string) string {
return fmt.Sprintf(`
provider "aws" {
region = "us-east-1"
}
resource "aws_lightsail_static_ip_attachment" "test" {
static_ip_name = "${aws_lightsail_static_ip.test.name}"
instance_name = "${aws_lightsail_instance.test.name}"
}
resource "aws_lightsail_static_ip" "test" {
name = "%s"
}
resource "aws_lightsail_instance" "test" {
name = "%s"
availability_zone = "us-east-1b"
blueprint_id = "wordpress_4_6_1"
bundle_id = "micro_1_0"
key_pair_name = "${aws_lightsail_key_pair.test.name}"
}
resource "aws_lightsail_key_pair" "test" {
name = "%s"
}
`, staticIpName, instanceName, keypairName)
}

View File

@ -0,0 +1,138 @@
package aws
import (
"errors"
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSLightsailStaticIp_basic(t *testing.T) {
var staticIp lightsail.StaticIp
staticIpName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLightsailStaticIpDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLightsailStaticIpConfig_basic(staticIpName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLightsailStaticIpExists("aws_lightsail_static_ip.test", &staticIp),
),
},
},
})
}
func TestAccAWSLightsailStaticIp_disappears(t *testing.T) {
var staticIp lightsail.StaticIp
staticIpName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5))
staticIpDestroy := func(*terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
_, err := conn.ReleaseStaticIp(&lightsail.ReleaseStaticIpInput{
StaticIpName: aws.String(staticIpName),
})
if err != nil {
return fmt.Errorf("Error deleting Lightsail Static IP in disapear test")
}
return nil
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLightsailStaticIpDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLightsailStaticIpConfig_basic(staticIpName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLightsailStaticIpExists("aws_lightsail_static_ip.test", &staticIp),
staticIpDestroy,
),
ExpectNonEmptyPlan: true,
},
},
})
}
func testAccCheckAWSLightsailStaticIpExists(n string, staticIp *lightsail.StaticIp) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return errors.New("No Lightsail Static IP ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
resp, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{
StaticIpName: aws.String(rs.Primary.ID),
})
if err != nil {
return err
}
if resp == nil || resp.StaticIp == nil {
return fmt.Errorf("Static IP (%s) not found", rs.Primary.ID)
}
*staticIp = *resp.StaticIp
return nil
}
}
func testAccCheckAWSLightsailStaticIpDestroy(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_lightsail_static_ip" {
continue
}
conn := testAccProvider.Meta().(*AWSClient).lightsailconn
resp, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{
StaticIpName: aws.String(rs.Primary.ID),
})
if err == nil {
if resp.StaticIp != nil {
return fmt.Errorf("Lightsail Static IP %q still exists", rs.Primary.ID)
}
}
// Verify the error
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
return nil
}
}
return err
}
return nil
}
func testAccAWSLightsailStaticIpConfig_basic(staticIpName string) string {
return fmt.Sprintf(`
provider "aws" {
region = "us-east-1"
}
resource "aws_lightsail_static_ip" "test" {
name = "%s"
}
`, staticIpName)
}

View File

@ -781,7 +781,7 @@ func resourceAwsOpsworksInstanceCreate(d *schema.ResourceData, meta interface{})
d.Set("id", instanceId)
if v, ok := d.GetOk("state"); ok && v.(string) == "running" {
err := startOpsworksInstance(d, meta, false)
err := startOpsworksInstance(d, meta, true)
if err != nil {
return err
}
@ -860,7 +860,7 @@ func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{})
}
} else {
if status != "stopped" && status != "stopping" && status != "shutting_down" {
err := stopOpsworksInstance(d, meta, false)
err := stopOpsworksInstance(d, meta, true)
if err != nil {
return err
}

View File

@ -36,10 +36,19 @@ func resourceAwsRDSCluster() *schema.Resource {
},
"cluster_identifier": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"cluster_identifier_prefix"},
ValidateFunc: validateRdsIdentifier,
},
"cluster_identifier_prefix": {
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsId,
ValidateFunc: validateRdsIdentifierPrefix,
},
"cluster_members": {
@ -225,6 +234,19 @@ func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error
conn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var identifier string
if v, ok := d.GetOk("cluster_identifier"); ok {
identifier = v.(string)
} else {
if v, ok := d.GetOk("cluster_identifier_prefix"); ok {
identifier = resource.PrefixedUniqueId(v.(string))
} else {
identifier = resource.PrefixedUniqueId("tf-")
}
d.Set("cluster_identifier", identifier)
}
if _, ok := d.GetOk("snapshot_identifier"); ok {
opts := rds.RestoreDBClusterFromSnapshotInput{
DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),

View File

@ -24,10 +24,19 @@ func resourceAwsRDSClusterInstance() *schema.Resource {
Schema: map[string]*schema.Schema{
"identifier": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"identifier_prefix"},
ValidateFunc: validateRdsIdentifier,
},
"identifier_prefix": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateRdsId,
ValidateFunc: validateRdsIdentifierPrefix,
},
"db_subnet_group_name": {
@ -162,10 +171,14 @@ func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{
createOpts.DBParameterGroupName = aws.String(attr.(string))
}
if v := d.Get("identifier").(string); v != "" {
createOpts.DBInstanceIdentifier = aws.String(v)
if v, ok := d.GetOk("identifier"); ok {
createOpts.DBInstanceIdentifier = aws.String(v.(string))
} else {
createOpts.DBInstanceIdentifier = aws.String(resource.UniqueId())
if v, ok := d.GetOk("identifier_prefix"); ok {
createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId(v.(string)))
} else {
createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId("tf-"))
}
}
if attr, ok := d.GetOk("db_subnet_group_name"); ok {

View File

@ -46,6 +46,48 @@ func TestAccAWSRDSClusterInstance_basic(t *testing.T) {
})
}
func TestAccAWSRDSClusterInstance_namePrefix(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterInstanceConfig_namePrefix(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.test", &v),
testAccCheckAWSDBClusterInstanceAttributes(&v),
resource.TestMatchResourceAttr(
"aws_rds_cluster_instance.test", "identifier", regexp.MustCompile("^tf-cluster-instance-")),
),
},
},
})
}
func TestAccAWSRDSClusterInstance_generatedName(t *testing.T) {
var v rds.DBInstance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterInstanceConfig_generatedName(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.test", &v),
testAccCheckAWSDBClusterInstanceAttributes(&v),
resource.TestMatchResourceAttr(
"aws_rds_cluster_instance.test", "identifier", regexp.MustCompile("^tf-")),
),
},
},
})
}
func TestAccAWSRDSClusterInstance_kmsKey(t *testing.T) {
var v rds.DBInstance
keyRegex := regexp.MustCompile("^arn:aws:kms:")
@ -256,6 +298,83 @@ resource "aws_db_parameter_group" "bar" {
`, n, n, n)
}
func testAccAWSClusterInstanceConfig_namePrefix(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster_instance" "test" {
identifier_prefix = "tf-cluster-instance-"
cluster_identifier = "${aws_rds_cluster.test.id}"
instance_class = "db.r3.large"
}
resource "aws_rds_cluster" "test" {
cluster_identifier = "tf-aurora-cluster-%d"
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n, n)
}
func testAccAWSClusterInstanceConfig_generatedName(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster_instance" "test" {
cluster_identifier = "${aws_rds_cluster.test.id}"
instance_class = "db.r3.large"
}
resource "aws_rds_cluster" "test" {
cluster_identifier = "tf-aurora-cluster-%d"
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n, n)
}
func testAccAWSClusterInstanceConfigKmsKey(n int) string {
return fmt.Sprintf(`

View File

@ -29,10 +29,19 @@ func resourceAwsRDSClusterParameterGroup() *schema.Resource {
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"name_prefix"},
ValidateFunc: validateDbParamGroupName,
},
"name_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Required: true,
ValidateFunc: validateDbParamGroupName,
ValidateFunc: validateDbParamGroupNamePrefix,
},
"family": &schema.Schema{
Type: schema.TypeString,
@ -86,8 +95,17 @@ func resourceAwsRDSClusterParameterGroupCreate(d *schema.ResourceData, meta inte
rdsconn := meta.(*AWSClient).rdsconn
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
var groupName string
if v, ok := d.GetOk("name"); ok {
groupName = v.(string)
} else if v, ok := d.GetOk("name_prefix"); ok {
groupName = resource.PrefixedUniqueId(v.(string))
} else {
groupName = resource.UniqueId()
}
createOpts := rds.CreateDBClusterParameterGroupInput{
DBClusterParameterGroupName: aws.String(d.Get("name").(string)),
DBClusterParameterGroupName: aws.String(groupName),
DBParameterGroupFamily: aws.String(d.Get("family").(string)),
Description: aws.String(d.Get("description").(string)),
Tags: tags,

View File

@ -3,6 +3,7 @@ package aws
import (
"errors"
"fmt"
"regexp"
"testing"
"time"
@ -90,6 +91,44 @@ func TestAccAWSDBClusterParameterGroup_basic(t *testing.T) {
})
}
func TestAccAWSDBClusterParameterGroup_namePrefix(t *testing.T) {
var v rds.DBClusterParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBClusterParameterGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBClusterParameterGroupConfig_namePrefix,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBClusterParameterGroupExists("aws_rds_cluster_parameter_group.test", &v),
resource.TestMatchResourceAttr(
"aws_rds_cluster_parameter_group.test", "name", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSDBClusterParameterGroup_generatedName(t *testing.T) {
var v rds.DBClusterParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDBClusterParameterGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDBClusterParameterGroupConfig_generatedName,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSDBClusterParameterGroupExists("aws_rds_cluster_parameter_group.test", &v),
),
},
},
})
}
func TestAccAWSDBClusterParameterGroup_disappears(t *testing.T) {
var v rds.DBClusterParameterGroup
@ -365,3 +404,15 @@ func testAccAWSDBClusterParameterGroupOnlyConfig(name string) string {
family = "aurora5.6"
}`, name)
}
const testAccAWSDBClusterParameterGroupConfig_namePrefix = `
resource "aws_rds_cluster_parameter_group" "test" {
name_prefix = "tf-test-"
family = "aurora5.6"
}
`
const testAccAWSDBClusterParameterGroupConfig_generatedName = `
resource "aws_rds_cluster_parameter_group" "test" {
family = "aurora5.6"
}
`

View File

@ -40,6 +40,46 @@ func TestAccAWSRDSCluster_basic(t *testing.T) {
})
}
func TestAccAWSRDSCluster_namePrefix(t *testing.T) {
var v rds.DBCluster
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterConfig_namePrefix(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterExists("aws_rds_cluster.test", &v),
resource.TestMatchResourceAttr(
"aws_rds_cluster.test", "cluster_identifier", regexp.MustCompile("^tf-test-")),
),
},
},
})
}
func TestAccAWSRDSCluster_generatedName(t *testing.T) {
var v rds.DBCluster
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSClusterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSClusterConfig_generatedName(acctest.RandInt()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSClusterExists("aws_rds_cluster.test", &v),
resource.TestMatchResourceAttr(
"aws_rds_cluster.test", "cluster_identifier", regexp.MustCompile("^tf-")),
),
},
},
})
}
func TestAccAWSRDSCluster_takeFinalSnapshot(t *testing.T) {
var v rds.DBCluster
rInt := acctest.RandInt()
@ -322,6 +362,71 @@ resource "aws_rds_cluster" "default" {
}`, n)
}
func testAccAWSClusterConfig_namePrefix(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "test" {
cluster_identifier_prefix = "tf-test-"
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n)
}
func testAccAWSClusterConfig_generatedName(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "test" {
master_username = "root"
master_password = "password"
db_subnet_group_name = "${aws_db_subnet_group.test.name}"
skip_final_snapshot = true
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "a" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"
availability_zone = "us-west-2a"
}
resource "aws_subnet" "b" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
}
resource "aws_db_subnet_group" "test" {
name = "tf-test-%d"
subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"]
}
`, n)
}
func testAccAWSClusterConfigWithFinalSnapshot(n int) string {
return fmt.Sprintf(`
resource "aws_rds_cluster" "default" {

View File

@ -0,0 +1,99 @@
package aws
import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ses"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsSesDomainIdentity() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSesDomainIdentityCreate,
Read: resourceAwsSesDomainIdentityRead,
Delete: resourceAwsSesDomainIdentityDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"domain": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"verification_token": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceAwsSesDomainIdentityCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesConn
domainName := d.Get("domain").(string)
createOpts := &ses.VerifyDomainIdentityInput{
Domain: aws.String(domainName),
}
_, err := conn.VerifyDomainIdentity(createOpts)
if err != nil {
return fmt.Errorf("Error requesting SES domain identity verification: %s", err)
}
d.SetId(domainName)
return resourceAwsSesDomainIdentityRead(d, meta)
}
func resourceAwsSesDomainIdentityRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesConn
domainName := d.Id()
d.Set("domain", domainName)
readOpts := &ses.GetIdentityVerificationAttributesInput{
Identities: []*string{
aws.String(domainName),
},
}
response, err := conn.GetIdentityVerificationAttributes(readOpts)
if err != nil {
log.Printf("[WARN] Error fetching identity verification attributes for %s: %s", d.Id(), err)
return err
}
verificationAttrs, ok := response.VerificationAttributes[domainName]
if !ok {
log.Printf("[WARN] Domain not listed in response when fetching verification attributes for %s", d.Id())
d.SetId("")
return nil
}
d.Set("verification_token", verificationAttrs.VerificationToken)
return nil
}
func resourceAwsSesDomainIdentityDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesConn
domainName := d.Get("domain").(string)
deleteOpts := &ses.DeleteIdentityInput{
Identity: aws.String(domainName),
}
_, err := conn.DeleteIdentity(deleteOpts)
if err != nil {
return fmt.Errorf("Error deleting SES domain identity: %s", err)
}
return nil
}

View File

@ -0,0 +1,100 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ses"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAwsSESDomainIdentity_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsSESDomainIdentityDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(
testAccAwsSESDomainIdentityConfig,
acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsSESDomainIdentityExists("aws_ses_domain_identity.test"),
),
},
},
})
}
func testAccCheckAwsSESDomainIdentityDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).sesConn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_ses_domain_identity" {
continue
}
domain := rs.Primary.ID
params := &ses.GetIdentityVerificationAttributesInput{
Identities: []*string{
aws.String(domain),
},
}
response, err := conn.GetIdentityVerificationAttributes(params)
if err != nil {
return err
}
if response.VerificationAttributes[domain] != nil {
return fmt.Errorf("SES Domain Identity %s still exists. Failing!", domain)
}
}
return nil
}
func testAccCheckAwsSESDomainIdentityExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("SES Domain Identity not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("SES Domain Identity name not set")
}
domain := rs.Primary.ID
conn := testAccProvider.Meta().(*AWSClient).sesConn
params := &ses.GetIdentityVerificationAttributesInput{
Identities: []*string{
aws.String(domain),
},
}
response, err := conn.GetIdentityVerificationAttributes(params)
if err != nil {
return err
}
if response.VerificationAttributes[domain] == nil {
return fmt.Errorf("SES Domain Identity %s not found in AWS", domain)
}
return nil
}
}
const testAccAwsSESDomainIdentityConfig = `
resource "aws_ses_domain_identity" "test" {
domain = "%s.terraformtesting.com"
}
`

View File

@ -60,7 +60,6 @@ func resourceAwsVpc() *schema.Resource {
"assign_generated_ipv6_cidr_block": {
Type: schema.TypeBool,
ForceNew: true,
Optional: true,
Default: false,
},
@ -178,7 +177,7 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error {
d.Set("tags", tagsToMap(vpc.Tags))
for _, a := range vpc.Ipv6CidrBlockAssociationSet {
if *a.Ipv6CidrBlockState.State == "associated" {
if *a.Ipv6CidrBlockState.State == "associated" { //we can only ever have 1 IPv6 block associated at once
d.Set("assign_generated_ipv6_cidr_block", true)
d.Set("ipv6_association_id", a.AssociationId)
d.Set("ipv6_cidr_block", a.Ipv6CidrBlock)
@ -344,6 +343,68 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error {
d.SetPartial("enable_classiclink")
}
if d.HasChange("assign_generated_ipv6_cidr_block") && !d.IsNewResource() {
toAssign := d.Get("assign_generated_ipv6_cidr_block").(bool)
log.Printf("[INFO] Modifying assign_generated_ipv6_cidr_block to %#v", toAssign)
if toAssign {
modifyOpts := &ec2.AssociateVpcCidrBlockInput{
VpcId: &vpcid,
AmazonProvidedIpv6CidrBlock: aws.Bool(toAssign),
}
log.Printf("[INFO] Enabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v",
d.Id(), modifyOpts)
resp, err := conn.AssociateVpcCidrBlock(modifyOpts)
if err != nil {
return err
}
// Wait for the CIDR to become available
log.Printf(
"[DEBUG] Waiting for IPv6 CIDR (%s) to become associated",
d.Id())
stateConf := &resource.StateChangeConf{
Pending: []string{"associating", "disassociated"},
Target: []string{"associated"},
Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), *resp.Ipv6CidrBlockAssociation.AssociationId),
Timeout: 1 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf(
"Error waiting for IPv6 CIDR (%s) to become associated: %s",
d.Id(), err)
}
} else {
modifyOpts := &ec2.DisassociateVpcCidrBlockInput{
AssociationId: aws.String(d.Get("ipv6_association_id").(string)),
}
log.Printf("[INFO] Disabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v",
d.Id(), modifyOpts)
if _, err := conn.DisassociateVpcCidrBlock(modifyOpts); err != nil {
return err
}
// Wait for the CIDR to become available
log.Printf(
"[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated",
d.Id())
stateConf := &resource.StateChangeConf{
Pending: []string{"disassociating", "associated"},
Target: []string{"disassociated"},
Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_association_id").(string)),
Timeout: 1 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf(
"Error waiting for IPv6 CIDR (%s) to become disassociated: %s",
d.Id(), err)
}
}
d.SetPartial("assign_generated_ipv6_cidr_block")
}
if err := setTags(conn, d); err != nil {
return err
} else {
@ -412,6 +473,41 @@ func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
}
}
func Ipv6CidrStateRefreshFunc(conn *ec2.EC2, id string, associationId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
describeVpcOpts := &ec2.DescribeVpcsInput{
VpcIds: []*string{aws.String(id)},
}
resp, err := conn.DescribeVpcs(describeVpcOpts)
if err != nil {
if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" {
resp = nil
} else {
log.Printf("Error on VPCStateRefresh: %s", err)
return nil, "", err
}
}
if resp == nil {
// Sometimes AWS just has consistency issues and doesn't see
// our instance yet. Return an empty state.
return nil, "", nil
}
if resp.Vpcs[0].Ipv6CidrBlockAssociationSet == nil {
return nil, "", nil
}
for _, association := range resp.Vpcs[0].Ipv6CidrBlockAssociationSet {
if *association.AssociationId == associationId {
return association, *association.Ipv6CidrBlockState.State, nil
}
}
return nil, "", nil
}
}
func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error {
filter1 := &ec2.Filter{
Name: aws.String("default"),

View File

@ -46,7 +46,7 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) {
Steps: []resource.TestStep{
{
Config: testAccVpcConfigIpv6Enabled,
Check: resource.ComposeTestCheckFunc(
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckVpcExists("aws_vpc.foo", &vpc),
testAccCheckVpcCidr(&vpc, "10.1.0.0/16"),
resource.TestCheckResourceAttr(
@ -55,6 +55,34 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) {
"aws_vpc.foo", "ipv6_association_id"),
resource.TestCheckResourceAttrSet(
"aws_vpc.foo", "ipv6_cidr_block"),
resource.TestCheckResourceAttr(
"aws_vpc.foo", "assign_generated_ipv6_cidr_block", "true"),
),
},
{
Config: testAccVpcConfigIpv6Disabled,
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckVpcExists("aws_vpc.foo", &vpc),
testAccCheckVpcCidr(&vpc, "10.1.0.0/16"),
resource.TestCheckResourceAttr(
"aws_vpc.foo", "cidr_block", "10.1.0.0/16"),
resource.TestCheckResourceAttr(
"aws_vpc.foo", "assign_generated_ipv6_cidr_block", "false"),
),
},
{
Config: testAccVpcConfigIpv6Enabled,
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckVpcExists("aws_vpc.foo", &vpc),
testAccCheckVpcCidr(&vpc, "10.1.0.0/16"),
resource.TestCheckResourceAttr(
"aws_vpc.foo", "cidr_block", "10.1.0.0/16"),
resource.TestCheckResourceAttrSet(
"aws_vpc.foo", "ipv6_association_id"),
resource.TestCheckResourceAttrSet(
"aws_vpc.foo", "ipv6_cidr_block"),
resource.TestCheckResourceAttr(
"aws_vpc.foo", "assign_generated_ipv6_cidr_block", "true"),
),
},
},
@ -283,6 +311,12 @@ resource "aws_vpc" "foo" {
}
`
const testAccVpcConfigIpv6Disabled = `
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
}
`
const testAccVpcConfigUpdate = `
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"

View File

@ -294,6 +294,11 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro
}
vpnConnection := resp.VpnConnections[0]
if vpnConnection == nil || *vpnConnection.State == "deleted" {
// Seems we have lost our VPN Connection
d.SetId("")
return nil
}
// Set attributes under the user's control.
d.Set("vpn_gateway_id", vpnConnection.VpnGatewayId)

View File

@ -3,6 +3,7 @@ package aws
import (
"fmt"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
@ -13,6 +14,8 @@ import (
)
func TestAccAWSVpnConnection_basic(t *testing.T) {
var vpn ec2.VpnConnection
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "aws_vpn_connection.foo",
@ -27,6 +30,7 @@ func TestAccAWSVpnConnection_basic(t *testing.T) {
"aws_vpn_gateway.vpn_gateway",
"aws_customer_gateway.customer_gateway",
"aws_vpn_connection.foo",
&vpn,
),
),
},
@ -38,6 +42,7 @@ func TestAccAWSVpnConnection_basic(t *testing.T) {
"aws_vpn_gateway.vpn_gateway",
"aws_customer_gateway.customer_gateway",
"aws_vpn_connection.foo",
&vpn,
),
),
},
@ -46,6 +51,7 @@ func TestAccAWSVpnConnection_basic(t *testing.T) {
}
func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) {
var vpn ec2.VpnConnection
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "aws_vpn_connection.foo",
@ -60,6 +66,7 @@ func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) {
"aws_vpn_gateway.vpn_gateway",
"aws_customer_gateway.customer_gateway",
"aws_vpn_connection.foo",
&vpn,
),
resource.TestCheckResourceAttr("aws_vpn_connection.foo", "static_routes_only", "false"),
),
@ -68,6 +75,74 @@ func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) {
})
}
func TestAccAWSVpnConnection_disappears(t *testing.T) {
var vpn ec2.VpnConnection
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccAwsVpnConnectionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsVpnConnectionConfig,
Check: resource.ComposeTestCheckFunc(
testAccAwsVpnConnection(
"aws_vpc.vpc",
"aws_vpn_gateway.vpn_gateway",
"aws_customer_gateway.customer_gateway",
"aws_vpn_connection.foo",
&vpn,
),
testAccAWSVpnConnectionDisappears(&vpn),
),
ExpectNonEmptyPlan: true,
},
},
})
}
func testAccAWSVpnConnectionDisappears(connection *ec2.VpnConnection) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
_, err := conn.DeleteVpnConnection(&ec2.DeleteVpnConnectionInput{
VpnConnectionId: connection.VpnConnectionId,
})
if err != nil {
if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnConnectionID.NotFound" {
return nil
}
if err != nil {
return err
}
}
return resource.Retry(40*time.Minute, func() *resource.RetryError {
opts := &ec2.DescribeVpnConnectionsInput{
VpnConnectionIds: []*string{connection.VpnConnectionId},
}
resp, err := conn.DescribeVpnConnections(opts)
if err != nil {
cgw, ok := err.(awserr.Error)
if ok && cgw.Code() == "InvalidVpnConnectionID.NotFound" {
return nil
}
if ok && cgw.Code() == "IncorrectState" {
return resource.RetryableError(fmt.Errorf(
"Waiting for VPN Connection to be in the correct state: %v", connection.VpnConnectionId))
}
return resource.NonRetryableError(
fmt.Errorf("Error retrieving VPN Connection: %s", err))
}
if *resp.VpnConnections[0].State == "deleted" {
return nil
}
return resource.RetryableError(fmt.Errorf(
"Waiting for VPN Connection: %v", connection.VpnConnectionId))
})
}
}
func testAccAwsVpnConnectionDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
for _, rs := range s.RootModule().Resources {
@ -112,7 +187,8 @@ func testAccAwsVpnConnection(
vpcResource string,
vpnGatewayResource string,
customerGatewayResource string,
vpnConnectionResource string) resource.TestCheckFunc {
vpnConnectionResource string,
vpnConnection *ec2.VpnConnection) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[vpnConnectionResource]
if !ok {
@ -129,7 +205,7 @@ func testAccAwsVpnConnection(
ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn
_, err := ec2conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{
resp, err := ec2conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{
VpnConnectionIds: []*string{aws.String(connection.Primary.ID)},
})
@ -137,6 +213,8 @@ func testAccAwsVpnConnection(
return err
}
*vpnConnection = *resp.VpnConnections[0]
return nil
}
}

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform/helper/schema"
)
func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
func validateRdsIdentifier(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
@ -33,6 +33,23 @@ func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
return
}
func validateRdsIdentifierPrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
}
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
return
}
func validateElastiCacheClusterId(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if (len(value) < 1) || (len(value) > 20) {
@ -103,7 +120,27 @@ func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []er
"%q cannot be greater than 255 characters", k))
}
return
}
func validateDbParamGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
}
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 226 characters", k))
}
return
}
func validateStreamViewType(v interface{}, k string) (ws []string, errors []error) {
@ -1041,3 +1078,79 @@ func validateApiGatewayUsagePlanQuotaSettings(v map[string]interface{}) (errors
return
}
func validateDbSubnetGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 255 characters", k))
}
if regexp.MustCompile(`(?i)^default$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q is not allowed as %q", "Default", k))
}
return
}
func validateDbSubnetGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k))
}
if len(value) > 229 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 229 characters", k))
}
return
}
func validateDbOptionGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters and hyphens allowed in %q", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if regexp.MustCompile(`-$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot end with a hyphen", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 255 characters", k))
}
return
}
func validateDbOptionGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters and hyphens allowed in %q", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if len(value) > 229 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 229 characters", k))
}
return
}

View File

@ -1785,3 +1785,131 @@ func TestValidateElbNamePrefix(t *testing.T) {
}
}
}
func TestValidateDbSubnetGroupName(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "tEsting",
ErrCount: 1,
},
{
Value: "testing?",
ErrCount: 1,
},
{
Value: "default",
ErrCount: 1,
},
{
Value: randomString(300),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbSubnetGroupName(tc.Value, "aws_db_subnet_group")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Subnet Group name to trigger a validation error")
}
}
}
func TestValidateDbSubnetGroupNamePrefix(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "tEsting",
ErrCount: 1,
},
{
Value: "testing?",
ErrCount: 1,
},
{
Value: randomString(230),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbSubnetGroupNamePrefix(tc.Value, "aws_db_subnet_group")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Subnet Group name prefix to trigger a validation error")
}
}
}
func TestValidateDbOptionGroupName(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "testing123!",
ErrCount: 1,
},
{
Value: "1testing123",
ErrCount: 1,
},
{
Value: "testing--123",
ErrCount: 1,
},
{
Value: "testing123-",
ErrCount: 1,
},
{
Value: randomString(256),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbOptionGroupName(tc.Value, "aws_db_option_group_name")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Option Group Name to trigger a validation error")
}
}
}
func TestValidateDbOptionGroupNamePrefix(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "testing123!",
ErrCount: 1,
},
{
Value: "1testing123",
ErrCount: 1,
},
{
Value: "testing--123",
ErrCount: 1,
},
{
Value: randomString(230),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateDbOptionGroupNamePrefix(tc.Value, "aws_db_option_group_name")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the DB Option Group name prefix to trigger a validation error")
}
}
}

View File

@ -5,6 +5,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"testing"
@ -117,8 +118,13 @@ func buildDataSourceTestProgram() (string, error) {
return "", fmt.Errorf("failed to build test stub program: %s", err)
}
gopath := os.Getenv("GOPATH")
if gopath == "" {
gopath = filepath.Join(os.Getenv("HOME") + "/go")
}
programPath := path.Join(
os.Getenv("GOPATH"), "bin", "tf-acc-external-data-source",
filepath.SplitList(gopath)[0], "bin", "tf-acc-external-data-source",
)
return programPath, nil
}

View File

@ -10,9 +10,9 @@ import (
func resourceGithubIssueLabel() *schema.Resource {
return &schema.Resource{
Create: resourceGithubIssueLabelCreate,
Create: resourceGithubIssueLabelCreateOrUpdate,
Read: resourceGithubIssueLabelRead,
Update: resourceGithubIssueLabelUpdate,
Update: resourceGithubIssueLabelCreateOrUpdate,
Delete: resourceGithubIssueLabelDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
@ -40,21 +40,54 @@ func resourceGithubIssueLabel() *schema.Resource {
}
}
func resourceGithubIssueLabelCreate(d *schema.ResourceData, meta interface{}) error {
// resourceGithubIssueLabelCreateOrUpdate idempotently creates or updates an
// issue label. Issue labels are keyed off of their "name", so pre-existing
// issue labels result in a 422 HTTP error if they exist outside of Terraform.
// Normally this would not be an issue, except new repositories are created with
// a "default" set of labels, and those labels easily conflict with custom ones.
//
// This function will first check if the label exists, and then issue an update,
// otherwise it will create. This is also advantageous in that we get to use the
// same function for two schema funcs.
func resourceGithubIssueLabelCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
o := meta.(*Organization).name
r := d.Get("repository").(string)
n := d.Get("name").(string)
c := d.Get("color").(string)
label := github.Label{
label := &github.Label{
Name: &n,
Color: &c,
}
log.Printf("[DEBUG] Creating label: %#v", label)
_, resp, err := client.Issues.CreateLabel(context.TODO(), meta.(*Organization).name, r, &label)
log.Printf("[DEBUG] Response from creating label: %s", *resp)
if err != nil {
return err
log.Printf("[DEBUG] Querying label existence %s/%s (%s)", o, r, n)
existing, _, _ := client.Issues.GetLabel(context.TODO(), o, r, n)
if existing != nil {
log.Printf("[DEBUG] Updating label: %s/%s (%s: %s)", o, r, n, c)
// Pull out the original name. If we already have a resource, this is the
// parsed ID. If not, it's the value given to the resource.
var oname string
if d.Id() == "" {
oname = n
} else {
_, oname = parseTwoPartID(d.Id())
}
_, _, err := client.Issues.EditLabel(context.TODO(), o, r, oname, label)
if err != nil {
return err
}
} else {
log.Printf("[DEBUG] Creating label: %s/%s (%s: %s)", o, r, n, c)
_, resp, err := client.Issues.CreateLabel(context.TODO(), o, r, label)
log.Printf("[DEBUG] Response from creating label: %s", *resp)
if err != nil {
return err
}
}
d.SetId(buildTwoPartID(&r, &n))
@ -66,6 +99,7 @@ func resourceGithubIssueLabelRead(d *schema.ResourceData, meta interface{}) erro
client := meta.(*Organization).client
r, n := parseTwoPartID(d.Id())
log.Printf("[DEBUG] Reading label: %s/%s", r, n)
githubLabel, _, err := client.Issues.GetLabel(context.TODO(), meta.(*Organization).name, r, n)
if err != nil {
d.SetId("")
@ -80,31 +114,12 @@ func resourceGithubIssueLabelRead(d *schema.ResourceData, meta interface{}) erro
return nil
}
func resourceGithubIssueLabelUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
r := d.Get("repository").(string)
n := d.Get("name").(string)
c := d.Get("color").(string)
_, originalName := parseTwoPartID(d.Id())
_, _, err := client.Issues.EditLabel(context.TODO(), meta.(*Organization).name, r, originalName, &github.Label{
Name: &n,
Color: &c,
})
if err != nil {
return err
}
d.SetId(buildTwoPartID(&r, &n))
return resourceGithubIssueLabelRead(d, meta)
}
func resourceGithubIssueLabelDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
r := d.Get("repository").(string)
n := d.Get("name").(string)
log.Printf("[DEBUG] Deleting label: %s/%s", r, n)
_, err := client.Issues.DeleteLabel(context.TODO(), meta.(*Organization).name, r, n)
return err
}

View File

@ -32,6 +32,13 @@ func TestAccGithubIssueLabel_basic(t *testing.T) {
testAccCheckGithubIssueLabelAttributes(&label, "bar", "FFFFFF"),
),
},
{
Config: testAccGitHubIssueLabelExistsConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubIssueLabelExists("github_issue_label.test", &label),
testAccCheckGithubIssueLabelAttributes(&label, "enhancement", "FF00FF"),
),
},
},
})
}
@ -134,3 +141,16 @@ resource "github_issue_label" "test" {
color = "FFFFFF"
}
`, testRepo)
var testAccGitHubIssueLabelExistsConfig string = fmt.Sprintf(`
// Create a repository which has the default labels
resource "github_repository" "test" {
name = "tf-acc-repo-label-abc1234"
}
resource "github_issue_label" "test" {
repository = "${github_repository.test.name}"
name = "enhancement" // Important! This is a pre-created label
color = "FF00FF"
}
`)

View File

@ -83,6 +83,7 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"kubernetes_config_map": resourceKubernetesConfigMap(),
"kubernetes_namespace": resourceKubernetesNamespace(),
"kubernetes_secret": resourceKubernetesSecret(),
},
ConfigureFunc: providerConfigure,
}

View File

@ -0,0 +1,159 @@
package kubernetes
import (
"log"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
pkgApi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
api "k8s.io/kubernetes/pkg/api/v1"
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
)
func resourceKubernetesSecret() *schema.Resource {
return &schema.Resource{
Create: resourceKubernetesSecretCreate,
Read: resourceKubernetesSecretRead,
Exists: resourceKubernetesSecretExists,
Update: resourceKubernetesSecretUpdate,
Delete: resourceKubernetesSecretDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"metadata": namespacedMetadataSchema("secret", true),
"data": {
Type: schema.TypeMap,
Description: "A map of the secret data.",
Optional: true,
Sensitive: true,
},
"type": {
Type: schema.TypeString,
Description: "Type of secret",
Default: "Opaque",
Optional: true,
ForceNew: true,
},
},
}
}
func resourceKubernetesSecretCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
metadata := expandMetadata(d.Get("metadata").([]interface{}))
secret := api.Secret{
ObjectMeta: metadata,
StringData: expandStringMap(d.Get("data").(map[string]interface{})),
}
if v, ok := d.GetOk("type"); ok {
secret.Type = api.SecretType(v.(string))
}
log.Printf("[INFO] Creating new secret: %#v", secret)
out, err := conn.CoreV1().Secrets(metadata.Namespace).Create(&secret)
if err != nil {
return err
}
log.Printf("[INFO] Submitting new secret: %#v", out)
d.SetId(buildId(out.ObjectMeta))
return resourceKubernetesSecretRead(d, meta)
}
func resourceKubernetesSecretRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
log.Printf("[INFO] Reading secret %s", name)
secret, err := conn.CoreV1().Secrets(namespace).Get(name)
if err != nil {
return err
}
log.Printf("[INFO] Received secret: %#v", secret)
err = d.Set("metadata", flattenMetadata(secret.ObjectMeta))
if err != nil {
return err
}
d.Set("data", byteMapToStringMap(secret.Data))
d.Set("type", secret.Type)
return nil
}
func resourceKubernetesSecretUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
ops := patchMetadata("metadata.0.", "/metadata/", d)
if d.HasChange("data") {
oldV, newV := d.GetChange("data")
oldV = base64EncodeStringMap(oldV.(map[string]interface{}))
newV = base64EncodeStringMap(newV.(map[string]interface{}))
diffOps := diffStringMap("/data/", oldV.(map[string]interface{}), newV.(map[string]interface{}))
ops = append(ops, diffOps...)
}
data, err := ops.MarshalJSON()
if err != nil {
return fmt.Errorf("Failed to marshal update operations: %s", err)
}
log.Printf("[INFO] Updating secret %q: %v", name, data)
out, err := conn.CoreV1().Secrets(namespace).Patch(name, pkgApi.JSONPatchType, data)
if err != nil {
return fmt.Errorf("Failed to update secret: %s", err)
}
log.Printf("[INFO] Submitting updated secret: %#v", out)
d.SetId(buildId(out.ObjectMeta))
return resourceKubernetesSecretRead(d, meta)
}
func resourceKubernetesSecretDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
log.Printf("[INFO] Deleting secret: %q", name)
err := conn.CoreV1().Secrets(namespace).Delete(name, &api.DeleteOptions{})
if err != nil {
return err
}
log.Printf("[INFO] Secret %s deleted", name)
d.SetId("")
return nil
}
func resourceKubernetesSecretExists(d *schema.ResourceData, meta interface{}) (bool, error) {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
log.Printf("[INFO] Checking secret %s", name)
_, err := conn.CoreV1().Secrets(namespace).Get(name)
if err != nil {
if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
return false, nil
}
log.Printf("[DEBUG] Received error: %#v", err)
}
return true, err
}

View File

@ -0,0 +1,320 @@
package kubernetes
import (
"fmt"
"reflect"
"regexp"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
api "k8s.io/kubernetes/pkg/api/v1"
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
)
func TestAccKubernetesSecret_basic(t *testing.T) {
var conf api.Secret
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_secret.test",
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesSecretConfig_basic(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "2"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.TestAnnotationOne", "one"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.TestAnnotationTwo", "two"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one", "TestAnnotationTwo": "two"}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "3"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelOne", "one"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelTwo", "two"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelThree", "three"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelTwo": "two", "TestLabelThree": "three"}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "2"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.one", "first"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.two", "second"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "type", "Opaque"),
testAccCheckSecretData(&conf, map[string]string{"one": "first", "two": "second"}),
),
},
{
Config: testAccKubernetesSecretConfig_modified(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "2"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.TestAnnotationOne", "one"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.Different", "1234"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one", "Different": "1234"}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "2"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelOne", "one"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelThree", "three"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelThree": "three"}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "3"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.one", "first"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.two", "second"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.nine", "ninth"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "type", "Opaque"),
testAccCheckSecretData(&conf, map[string]string{"one": "first", "two": "second", "nine": "ninth"}),
),
},
{
Config: testAccKubernetesSecretConfig_noData(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "0"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "0"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "0"),
testAccCheckSecretData(&conf, map[string]string{}),
),
},
{
Config: testAccKubernetesSecretConfig_typeSpecified(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "0"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "0"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "2"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.username", "admin"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "data.password", "password"),
resource.TestCheckResourceAttr("kubernetes_secret.test", "type", "kubernetes.io/basic-auth"),
testAccCheckSecretData(&conf, map[string]string{"username": "admin", "password": "password"}),
),
},
},
})
}
func TestAccKubernetesSecret_importBasic(t *testing.T) {
resourceName := "kubernetes_secret.test"
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesSecretConfig_basic(name),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccKubernetesSecret_generatedName(t *testing.T) {
var conf api.Secret
prefix := "tf-acc-test-gen-"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_secret.test",
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesSecretConfig_generatedName(prefix),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "0"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "0"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.generate_name", prefix),
resource.TestMatchResourceAttr("kubernetes_secret.test", "metadata.0.name", regexp.MustCompile("^"+prefix)),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"),
),
},
},
})
}
func TestAccKubernetesSecret_importGeneratedName(t *testing.T) {
resourceName := "kubernetes_secret.test"
prefix := "tf-acc-test-gen-import-"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesSecretConfig_generatedName(prefix),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func testAccCheckSecretData(m *api.Secret, expected map[string]string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if len(expected) == 0 && len(m.Data) == 0 {
return nil
}
if !reflect.DeepEqual(byteMapToStringMap(m.Data), expected) {
return fmt.Errorf("%s data don't match.\nExpected: %q\nGiven: %q",
m.Name, expected, m.Data)
}
return nil
}
}
func testAccCheckKubernetesSecretDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*kubernetes.Clientset)
for _, rs := range s.RootModule().Resources {
if rs.Type != "kubernetes_secret" {
continue
}
namespace, name := idParts(rs.Primary.ID)
resp, err := conn.CoreV1().Secrets(namespace).Get(name)
if err == nil {
if resp.Name == rs.Primary.ID {
return fmt.Errorf("Secret still exists: %s", rs.Primary.ID)
}
}
}
return nil
}
func testAccCheckKubernetesSecretExists(n string, obj *api.Secret) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
conn := testAccProvider.Meta().(*kubernetes.Clientset)
namespace, name := idParts(rs.Primary.ID)
out, err := conn.CoreV1().Secrets(namespace).Get(name)
if err != nil {
return err
}
*obj = *out
return nil
}
}
func testAccKubernetesSecretConfig_basic(name string) string {
return fmt.Sprintf(`
resource "kubernetes_secret" "test" {
metadata {
annotations {
TestAnnotationOne = "one"
TestAnnotationTwo = "two"
}
labels {
TestLabelOne = "one"
TestLabelTwo = "two"
TestLabelThree = "three"
}
name = "%s"
}
data {
one = "first"
two = "second"
}
}`, name)
}
func testAccKubernetesSecretConfig_modified(name string) string {
return fmt.Sprintf(`
resource "kubernetes_secret" "test" {
metadata {
annotations {
TestAnnotationOne = "one"
Different = "1234"
}
labels {
TestLabelOne = "one"
TestLabelThree = "three"
}
name = "%s"
}
data {
one = "first"
two = "second"
nine = "ninth"
}
}`, name)
}
func testAccKubernetesSecretConfig_noData(name string) string {
return fmt.Sprintf(`
resource "kubernetes_secret" "test" {
metadata {
name = "%s"
}
}`, name)
}
func testAccKubernetesSecretConfig_typeSpecified(name string) string {
return fmt.Sprintf(`
resource "kubernetes_secret" "test" {
metadata {
name = "%s"
}
data {
username = "admin"
password = "password"
}
type = "kubernetes.io/basic-auth"
}`, name)
}
func testAccKubernetesSecretConfig_generatedName(prefix string) string {
return fmt.Sprintf(`
resource "kubernetes_secret" "test" {
metadata {
generate_name = "%s"
}
data {
one = "first"
two = "second"
}
}`, prefix)
}

View File

@ -5,6 +5,7 @@ import (
"net/url"
"strings"
"encoding/base64"
"github.com/hashicorp/terraform/helper/schema"
api "k8s.io/kubernetes/pkg/api/v1"
)
@ -99,3 +100,20 @@ func isInternalAnnotationKey(annotationKey string) bool {
return false
}
func byteMapToStringMap(m map[string][]byte) map[string]string {
result := make(map[string]string)
for k, v := range m {
result[k] = string(v)
}
return result
}
func base64EncodeStringMap(m map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for k, v := range m {
value := v.(string)
result[k] = (base64.StdEncoding.EncodeToString([]byte(value)))
}
return result
}

View File

@ -0,0 +1,41 @@
package opc
import (
"fmt"
"net/http"
"net/url"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/go-oracle-terraform/opc"
)
type Config struct {
User string
Password string
IdentityDomain string
Endpoint string
MaxRetryTimeout int
}
type OPCClient struct {
Client *compute.Client
MaxRetryTimeout int
}
func (c *Config) Client() (*compute.Client, error) {
u, err := url.ParseRequestURI(c.Endpoint)
if err != nil {
return nil, fmt.Errorf("Invalid endpoint URI: %s", err)
}
config := opc.Config{
IdentityDomain: &c.IdentityDomain,
Username: &c.User,
Password: &c.Password,
APIEndpoint: u,
HTTPClient: http.DefaultClient,
}
// TODO: http client wrapping / log level
return compute.NewComputeClient(&config)
}

View File

@ -0,0 +1,176 @@
package opc
import (
"fmt"
"log"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func dataSourceNetworkInterface() *schema.Resource {
return &schema.Resource{
Read: dataSourceNetworkInterfaceRead,
Schema: map[string]*schema.Schema{
"instance_id": {
Type: schema.TypeString,
Required: true,
},
"instance_name": {
Type: schema.TypeString,
Required: true,
},
"interface": {
Type: schema.TypeString,
Required: true,
},
// Computed Values returned from the data source lookup
"dns": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
},
"ip_network": {
Type: schema.TypeString,
Computed: true,
},
"mac_address": {
Type: schema.TypeString,
Computed: true,
},
"model": {
Type: schema.TypeString,
Computed: true,
},
"name_servers": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"nat": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"search_domains": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"sec_lists": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"shared_network": {
Type: schema.TypeBool,
Computed: true,
},
"vnic": {
Type: schema.TypeString,
Computed: true,
},
"vnic_sets": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}
func dataSourceNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).Instances()
// Get required attributes
instance_name := d.Get("instance_name").(string)
instance_id := d.Get("instance_id").(string)
targetInterface := d.Get("interface").(string)
// Get instance
input := &compute.GetInstanceInput{
Name: instance_name,
ID: instance_id,
}
instance, err := client.GetInstance(input)
if err != nil {
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading instance %q: %v", instance_name, err)
}
result := compute.NetworkingInfo{}
// If the target instance has no network interfaces, return
if instance.Networking == nil {
d.SetId("")
return nil
}
// Set the computed fields
result = instance.Networking[targetInterface]
// Check if the target interface exists or not
if &result == nil {
log.Printf("[WARN] %q networking interface not found on instance %q", targetInterface, instance_name)
}
d.SetId(fmt.Sprintf("%s-%s", instance_name, targetInterface))
// vNIC is a required field for an IP Network interface, and can only be set if the network
// interface is inside an IP Network. Use this key to determine shared_network status
if result.Vnic != "" {
d.Set("shared_network", false)
} else {
d.Set("shared_network", true)
}
d.Set("ip_address", result.IPAddress)
d.Set("ip_network", result.IPNetwork)
d.Set("mac_address", result.MACAddress)
d.Set("model", result.Model)
d.Set("vnic", result.Vnic)
if err := setStringList(d, "dns", result.DNS); err != nil {
return err
}
if err := setStringList(d, "name_servers", result.NameServers); err != nil {
return err
}
if err := setStringList(d, "nat", result.Nat); err != nil {
return err
}
if err := setStringList(d, "search_domains", result.SearchDomains); err != nil {
return err
}
if err := setStringList(d, "sec_lists", result.SecLists); err != nil {
return err
}
if err := setStringList(d, "vnic_sets", result.VnicSets); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,103 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCDataSourceNetworkInterface_basic(t *testing.T) {
rInt := acctest.RandInt()
resName := "data.opc_compute_network_interface.test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceNetworkInterfaceBasic(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resName, "ip_network", fmt.Sprintf("testing-ip-network-%d", rInt)),
resource.TestCheckResourceAttr(resName, "vnic", fmt.Sprintf("ip-network-test-%d", rInt)),
resource.TestCheckResourceAttr(resName, "shared_network", "false"),
),
},
},
})
}
func TestAccOPCDataSourceNetworkInterface_sharedNetwork(t *testing.T) {
rInt := acctest.RandInt()
resName := "data.opc_compute_network_interface.test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceNetworkInterfaceShared(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resName, "model", "e1000"),
resource.TestCheckResourceAttr(resName, "nat.#", "1"),
resource.TestCheckResourceAttr(resName, "shared_network", "true"),
resource.TestCheckResourceAttr(resName, "sec_lists.#", "1"),
resource.TestCheckResourceAttr(resName, "name_servers.#", "0"),
resource.TestCheckResourceAttr(resName, "vnic_sets.#", "0"),
),
},
},
})
}
func testAccDataSourceNetworkInterfaceBasic(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_network" "foo" {
name = "testing-ip-network-%d"
description = "testing-ip-network-instance"
ip_address_prefix = "10.1.12.0/24"
}
resource "opc_compute_instance" "test" {
name = "test-%d"
label = "test"
shape = "oc3"
image_list = "/oracle/public/oel_6.7_apaas_16.4.5_1610211300"
networking_info {
index = 0
ip_network = "${opc_compute_ip_network.foo.id}"
vnic = "ip-network-test-%d"
shared_network = false
}
}
data "opc_compute_network_interface" "test" {
instance_name = "${opc_compute_instance.test.name}"
instance_id = "${opc_compute_instance.test.id}"
interface = "eth0"
}`, rInt, rInt, rInt)
}
func testAccDataSourceNetworkInterfaceShared(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_instance" "test" {
name = "test-%d"
label = "test"
shape = "oc3"
image_list = "/oracle/public/oel_6.7_apaas_16.4.5_1610211300"
tags = ["tag1", "tag2"]
networking_info {
index = 0
model = "e1000"
nat = ["ippool:/oracle/public/ippool"]
shared_network = true
}
}
data "opc_compute_network_interface" "test" {
instance_name = "${opc_compute_instance.test.name}"
instance_id = "${opc_compute_instance.test.id}"
interface = "eth0"
}`, rInt)
}

View File

@ -0,0 +1,72 @@
package opc
import (
"fmt"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func dataSourceVNIC() *schema.Resource {
return &schema.Resource{
Read: dataSourceVNICRead,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"mac_address": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsComputedSchema(),
"transit_flag": {
Type: schema.TypeBool,
Computed: true,
},
"uri": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func dataSourceVNICRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).VirtNICs()
name := d.Get("name").(string)
input := &compute.GetVirtualNICInput{
Name: name,
}
vnic, err := client.GetVirtualNIC(input)
if err != nil {
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading vnic %s: %s", name, err)
}
d.SetId(name)
d.Set("description", vnic.Description)
d.Set("mac_address", vnic.MACAddress)
d.Set("transit_flag", vnic.TransitFlag)
d.Set("uri", vnic.Uri)
if err := setStringList(d, "tags", vnic.Tags); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,56 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCVNIC_Basic(t *testing.T) {
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccVnicBasic(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"data.opc_compute_vnic.foo", "mac_address", "02:5a:cd:ec:2e:4c"),
resource.TestCheckResourceAttr(
"data.opc_compute_vnic.foo", "transit_flag", "false"),
),
},
},
})
}
func testAccVnicBasic(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_network" "foo" {
name = "testing-vnic-data-%d"
description = "testing-vnic-data"
ip_address_prefix = "10.1.13.0/24"
}
resource "opc_compute_instance" "test" {
name = "test-%d"
label = "test"
shape = "oc3"
image_list = "/oracle/public/oel_6.7_apaas_16.4.5_1610211300"
networking_info {
index = 0
ip_network = "${opc_compute_ip_network.foo.id}"
vnic = "test-vnic-data-%d"
shared_network = false
mac_address = "02:5a:cd:ec:2e:4c"
}
}
data "opc_compute_vnic" "foo" {
name = "test-vnic-data-%d"
}`, rInt, rInt, rInt, rInt)
}

View File

@ -0,0 +1,47 @@
package opc
import (
"sort"
"github.com/hashicorp/terraform/helper/schema"
)
// Helper function to get a string list from the schema, and alpha-sort it
func getStringList(d *schema.ResourceData, key string) []string {
if _, ok := d.GetOk(key); !ok {
return nil
}
l := d.Get(key).([]interface{})
res := make([]string, len(l))
for i, v := range l {
res[i] = v.(string)
}
sort.Strings(res)
return res
}
// Helper function to set a string list in the schema, in an alpha-sorted order.
func setStringList(d *schema.ResourceData, key string, value []string) error {
sort.Strings(value)
return d.Set(key, value)
}
// Helper function to get an int list from the schema, and numerically sort it
func getIntList(d *schema.ResourceData, key string) []int {
if _, ok := d.GetOk(key); !ok {
return nil
}
l := d.Get(key).([]interface{})
res := make([]int, len(l))
for i, v := range l {
res[i] = v.(int)
}
sort.Ints(res)
return res
}
func setIntList(d *schema.ResourceData, key string, value []int) error {
sort.Ints(value)
return d.Set(key, value)
}

View File

@ -0,0 +1,58 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCACL_importBasic(t *testing.T) {
resourceName := "opc_compute_acl.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccACLBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckACLDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCACL_importDisabled(t *testing.T) {
resourceName := "opc_compute_acl.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccACLDisabled, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckACLDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,57 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCImageList_importBasic(t *testing.T) {
resourceName := "opc_compute_image_list.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccImageList_basic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckImageListDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCImageList_importComplete(t *testing.T) {
resourceName := "opc_compute_image_list.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccImageList_complete, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckImageListDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,35 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCInstance_importBasic(t *testing.T) {
rInt := acctest.RandInt()
resourceName := "opc_compute_instance.test"
instanceName := fmt.Sprintf("acc-test-instance-%d", rInt)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccInstanceBasic(rInt),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateIdPrefix: instanceName + "/",
},
},
})
}

View File

@ -0,0 +1,33 @@
package opc
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCIPAddressPrefixSet_importBasic(t *testing.T) {
resourceName := "opc_compute_ip_address_prefix_set.test"
ri := acctest.RandInt()
config := testAccIPAddressPrefixSetBasic(ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckIPAddressPrefixSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,57 @@
package opc
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCIPAddressReservation_importBasic(t *testing.T) {
resourceName := "opc_compute_ip_address_reservation.test"
ri := acctest.RandInt()
config := testAccOPCIPAddressReservationConfig_Basic(ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckIPAddressReservationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCIPAddressReservation_importDisabled(t *testing.T) {
resourceName := "opc_compute_ip_address_reservation.test"
ri := acctest.RandInt()
config := testAccOPCIPAddressReservationConfig_Basic(ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckIPAddressReservationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,34 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCIPAssociation_importBasic(t *testing.T) {
resourceName := "opc_compute_ip_association.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccIPAssociationBasic, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckIPAssociationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,34 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCIPNetworkExchange_importBasic(t *testing.T) {
resourceName := "opc_compute_ip_network_exchange.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccIPNetworkExchangeBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckIPNetworkExchangeDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,34 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCIPReservation_importBasic(t *testing.T) {
resourceName := "opc_compute_ip_reservation.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccIPReservationBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckIPReservationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,33 @@
package opc
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCRoute_importBasic(t *testing.T) {
resourceName := "opc_compute_route.test"
ri := acctest.RandInt()
config := testAccOPCRouteConfig_Basic(ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckRouteDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,59 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSecRule_importBasic(t *testing.T) {
resourceName := "opc_compute_sec_rule.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecRuleBasic, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecRuleDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCSecRule_importComplete(t *testing.T) {
resourceName := "opc_compute_sec_rule.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecRuleComplete, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecRuleDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,59 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSecurityApplication_importICMP(t *testing.T) {
resourceName := "opc_compute_security_application.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecurityApplicationICMP, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckSecurityApplicationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCSecurityApplication_importTCP(t *testing.T) {
resourceName := "opc_compute_security_application.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecurityApplicationTCP, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckSecurityApplicationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,59 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSecurityAssociation_importBasic(t *testing.T) {
resourceName := "opc_compute_security_association.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccSecurityAssociationBasic, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckSecurityAssociationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCSecurityAssociation_importComplete(t *testing.T) {
resourceName := "opc_compute_security_association.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccSecurityAssociationComplete, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckSecurityAssociationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,34 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSecurityIPList_importBasic(t *testing.T) {
resourceName := "opc_compute_security_ip_list.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecurityIPListBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecurityIPListDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,59 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSecurityList_importBasic(t *testing.T) {
resourceName := "opc_compute_security_list.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecurityListBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecurityListDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCSecurityList_importComplete(t *testing.T) {
resourceName := "opc_compute_security_list.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecurityListComplete, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecurityListDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,58 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSecurityProtocol_importBasic(t *testing.T) {
resourceName := "opc_compute_security_protocol.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecurityProtocolBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecurityProtocolDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCSecurityProtocol_importDisabled(t *testing.T) {
resourceName := "opc_compute_security_protocol.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSecurityProtocolFull, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecurityProtocolDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,58 @@
package opc
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSecurityRule_importBasic(t *testing.T) {
resourceName := "opc_compute_security_rule.test"
ri := acctest.RandInt()
config := testAccOPCSecurityRuleConfig_Basic(ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecurityRuleDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCSecurityRule_importFull(t *testing.T) {
resourceName := "opc_compute_security_rule.test"
ri := acctest.RandInt()
config := testAccOPCSecurityRuleConfig_Full(ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckSecurityRuleDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,59 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCSSHKey_importBasic(t *testing.T) {
resourceName := "opc_compute_ssh_key.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSSHKeyBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckSSHKeyDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccOPCSSHKey_importDisabled(t *testing.T) {
resourceName := "opc_compute_ssh_key.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccOPCSSHKeyDisabled, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckSSHKeyDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -0,0 +1,89 @@
package opc
import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"user": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("OPC_USERNAME", nil),
Description: "The user name for OPC API operations.",
},
"password": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("OPC_PASSWORD", nil),
Description: "The user password for OPC API operations.",
},
"identity_domain": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("OPC_IDENTITY_DOMAIN", nil),
Description: "The OPC identity domain for API operations",
},
"endpoint": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("OPC_ENDPOINT", nil),
Description: "The HTTP endpoint for OPC API operations.",
},
"max_retry_timeout": {
Type: schema.TypeInt,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("OPC_MAX_RETRY_TIMEOUT", 3000),
Description: "Max num seconds to wait for successful response when operating on resources within OPC (defaults to 3000)",
},
},
DataSourcesMap: map[string]*schema.Resource{
"opc_compute_network_interface": dataSourceNetworkInterface(),
"opc_compute_vnic": dataSourceVNIC(),
},
ResourcesMap: map[string]*schema.Resource{
"opc_compute_ip_network": resourceOPCIPNetwork(),
"opc_compute_acl": resourceOPCACL(),
"opc_compute_image_list": resourceOPCImageList(),
"opc_compute_instance": resourceInstance(),
"opc_compute_ip_address_reservation": resourceOPCIPAddressReservation(),
"opc_compute_ip_association": resourceOPCIPAssociation(),
"opc_compute_ip_network_exchange": resourceOPCIPNetworkExchange(),
"opc_compute_ip_reservation": resourceOPCIPReservation(),
"opc_compute_route": resourceOPCRoute(),
"opc_compute_security_application": resourceOPCSecurityApplication(),
"opc_compute_security_association": resourceOPCSecurityAssociation(),
"opc_compute_security_ip_list": resourceOPCSecurityIPList(),
"opc_compute_security_list": resourceOPCSecurityList(),
"opc_compute_security_rule": resourceOPCSecurityRule(),
"opc_compute_sec_rule": resourceOPCSecRule(),
"opc_compute_ssh_key": resourceOPCSSHKey(),
"opc_compute_storage_volume": resourceOPCStorageVolume(),
"opc_compute_vnic_set": resourceOPCVNICSet(),
"opc_compute_security_protocol": resourceOPCSecurityProtocol(),
"opc_compute_ip_address_prefix_set": resourceOPCIPAddressPrefixSet(),
},
ConfigureFunc: providerConfigure,
}
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{
User: d.Get("user").(string),
Password: d.Get("password").(string),
IdentityDomain: d.Get("identity_domain").(string),
Endpoint: d.Get("endpoint").(string),
MaxRetryTimeout: d.Get("max_retry_timeout").(int),
}
return config.Client()
}

View File

@ -0,0 +1,62 @@
package opc
import (
"fmt"
"os"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider
func init() {
testAccProvider = Provider().(*schema.Provider)
testAccProviders = map[string]terraform.ResourceProvider{
"opc": testAccProvider,
}
}
func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvider_impl(t *testing.T) {
var _ terraform.ResourceProvider = Provider()
}
func testAccPreCheck(t *testing.T) {
required := []string{"OPC_USERNAME", "OPC_PASSWORD", "OPC_IDENTITY_DOMAIN", "OPC_ENDPOINT"}
for _, prop := range required {
if os.Getenv(prop) == "" {
t.Fatalf("%s must be set for acceptance test", prop)
}
}
}
type OPCResourceState struct {
*compute.Client
*terraform.InstanceState
}
func opcResourceCheck(resourceName string, f func(checker *OPCResourceState) error) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Resource not found: %s", resourceName)
}
state := &OPCResourceState{
Client: testAccProvider.Meta().(*compute.Client),
InstanceState: rs.Primary,
}
return f(state)
}
}

View File

@ -0,0 +1,151 @@
package opc
import (
"fmt"
"log"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceOPCACL() *schema.Resource {
return &schema.Resource{
Create: resourceOPCACLCreate,
Read: resourceOPCACLRead,
Update: resourceOPCACLUpdate,
Delete: resourceOPCACLDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"enabled": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"tags": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
ForceNew: true,
},
"uri": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceOPCACLCreate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Resource state: %#v", d.State())
log.Print("[DEBUG] Creating acl")
client := meta.(*compute.Client).ACLs()
input := compute.CreateACLInput{
Name: d.Get("name").(string),
Enabled: d.Get("enabled").(bool),
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
if description, ok := d.GetOk("description"); ok {
input.Description = description.(string)
}
info, err := client.CreateACL(&input)
if err != nil {
return fmt.Errorf("Error creating ACL: %s", err)
}
d.SetId(info.Name)
return resourceOPCACLRead(d, meta)
}
func resourceOPCACLRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Resource state: %#v", d.State())
client := meta.(*compute.Client).ACLs()
log.Printf("[DEBUG] Reading state of ip reservation %s", d.Id())
getInput := compute.GetACLInput{
Name: d.Id(),
}
result, err := client.GetACL(&getInput)
if err != nil {
// ACL does not exist
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading acl %s: %s", d.Id(), err)
}
log.Printf("[DEBUG] Read state of acl %s: %#v", d.Id(), result)
d.Set("name", result.Name)
d.Set("enabled", result.Enabled)
d.Set("description", result.Description)
d.Set("uri", result.URI)
if err := setStringList(d, "tags", result.Tags); err != nil {
return err
}
return nil
}
func resourceOPCACLUpdate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Resource state: %#v", d.State())
log.Print("[DEBUG] Updating acl")
client := meta.(*compute.Client).ACLs()
input := compute.UpdateACLInput{
Name: d.Get("name").(string),
Enabled: d.Get("enabled").(bool),
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
if description, ok := d.GetOk("description"); ok {
input.Description = description.(string)
}
info, err := client.UpdateACL(&input)
if err != nil {
return fmt.Errorf("Error updating ACL: %s", err)
}
d.SetId(info.Name)
return resourceOPCACLRead(d, meta)
}
func resourceOPCACLDelete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Resource state: %#v", d.State())
client := meta.(*compute.Client).ACLs()
name := d.Id()
log.Printf("[DEBUG] Deleting ACL: %v", name)
input := compute.DeleteACLInput{
Name: name,
}
if err := client.DeleteACL(&input); err != nil {
return fmt.Errorf("Error deleting ACL")
}
return nil
}

View File

@ -0,0 +1,107 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccOPCACL_Basic(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccACLBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckACLDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testAccCheckACLExists,
),
},
},
})
}
func TestAccOPCACL_Update(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccACLBasic, ri)
updatedConfig := fmt.Sprintf(testAccACLDisabled, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckACLDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: testAccCheckACLExists,
},
{
Config: updatedConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckACLExists,
resource.TestCheckResourceAttr("opc_compute_acl.test", "enabled", "false"),
),
},
},
})
}
func testAccCheckACLExists(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).ACLs()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_acl" {
continue
}
input := compute.GetACLInput{
Name: rs.Primary.Attributes["name"],
}
if _, err := client.GetACL(&input); err != nil {
return fmt.Errorf("Error retrieving state of ACL %s: %s", input.Name, err)
}
}
return nil
}
func testAccCheckACLDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).ACLs()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_acl" {
continue
}
input := compute.GetACLInput{
Name: rs.Primary.Attributes["name"],
}
if info, err := client.GetACL(&input); err == nil {
return fmt.Errorf("ACL %s still exists: %#v", input.Name, info)
}
}
return nil
}
var testAccACLBasic = `
resource "opc_compute_acl" "test" {
name = "test_acl-%d"
description = "test acl"
}
`
var testAccACLDisabled = `
resource "opc_compute_acl" "test" {
name = "test_acl-%d"
description = "test acl"
enabled = false
}
`

View File

@ -0,0 +1,107 @@
package opc
import (
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceOPCImageList() *schema.Resource {
return &schema.Resource{
Create: resourceOPCImageListCreate,
Read: resourceOPCImageListRead,
Update: resourceOPCImageListUpdate,
Delete: resourceOPCImageListDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"description": {
Type: schema.TypeString,
Required: true,
},
"default": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
},
},
}
}
func resourceOPCImageListCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).ImageList()
name := d.Get("name").(string)
createInput := &compute.CreateImageListInput{
Name: name,
Description: d.Get("description").(string),
Default: d.Get("default").(int),
}
createResult, err := client.CreateImageList(createInput)
if err != nil {
return err
}
d.SetId(createResult.Name)
return resourceOPCImageListRead(d, meta)
}
func resourceOPCImageListUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).ImageList()
name := d.Id()
updateInput := &compute.UpdateImageListInput{
Name: name,
Description: d.Get("description").(string),
Default: d.Get("default").(int),
}
_, err := client.UpdateImageList(updateInput)
if err != nil {
return err
}
return resourceOPCImageListRead(d, meta)
}
func resourceOPCImageListRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).ImageList()
getInput := &compute.GetImageListInput{
Name: d.Id(),
}
getResult, err := client.GetImageList(getInput)
if err != nil {
return err
}
d.Set("name", getResult.Name)
d.Set("description", getResult.Description)
d.Set("default", getResult.Default)
return nil
}
func resourceOPCImageListDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).ImageList()
deleteInput := &compute.DeleteImageListInput{
Name: d.Id(),
}
err := client.DeleteImageList(deleteInput)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,98 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccOPCImageList_Basic(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccImageList_basic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckImageListDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: testAccCheckImageListExists,
},
},
})
}
func TestAccOPCImageList_Complete(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccImageList_complete, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckImageListDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: testAccCheckImageListExists,
},
},
})
}
func testAccCheckImageListExists(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).ImageList()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_image_list" {
continue
}
input := compute.GetImageListInput{
Name: rs.Primary.Attributes["name"],
}
if _, err := client.GetImageList(&input); err != nil {
return fmt.Errorf("Error retrieving state of Image List %s: %s", input.Name, err)
}
}
return nil
}
func testAccCheckImageListDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).ImageList()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_image_list" {
continue
}
input := compute.GetImageListInput{
Name: rs.Primary.Attributes["name"],
}
if info, err := client.GetImageList(&input); err == nil {
return fmt.Errorf("Image List %s still exists: %#v", input.Name, info)
}
}
return nil
}
var testAccImageList_basic = `
resource "opc_compute_image_list" "test" {
name = "test-acc-image-list-basic-%d"
description = "Image List (Basic)"
}
`
var testAccImageList_complete = `
resource "opc_compute_image_list" "test" {
name = "test-acc-image-list-complete-%d"
description = "Image List (Complete)"
default = 2
}
`

View File

@ -0,0 +1,884 @@
package opc
import (
"bytes"
"encoding/json"
"fmt"
"log"
"strings"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceInstance() *schema.Resource {
return &schema.Resource{
Create: resourceInstanceCreate,
Read: resourceInstanceRead,
Delete: resourceInstanceDelete,
Importer: &schema.ResourceImporter{
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
combined := strings.Split(d.Id(), "/")
if len(combined) != 2 {
return nil, fmt.Errorf("Invalid ID specified. Must be in the form of instance_name/instance_id. Got: %s", d.Id())
}
d.Set("name", combined[0])
d.SetId(combined[1])
return []*schema.ResourceData{d}, nil
},
},
Schema: map[string]*schema.Schema{
/////////////////////////
// Required Attributes //
/////////////////////////
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"shape": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
/////////////////////////
// Optional Attributes //
/////////////////////////
"instance_attributes": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return true
},
},
"boot_order": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeInt},
},
"hostname": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"image_list": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"label": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"networking_info": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"dns": {
// Required for Shared Network Interface, will default if unspecified, however
// Optional for IP Network Interface
Type: schema.TypeList,
Optional: true,
Computed: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"index": {
Type: schema.TypeInt,
ForceNew: true,
Required: true,
},
"ip_address": {
// Optional, IP Network only
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
"ip_network": {
// Required for an IP Network Interface
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
"mac_address": {
// Optional, IP Network Only
Type: schema.TypeString,
ForceNew: true,
Computed: true,
Optional: true,
},
"model": {
// Required, Shared Network only.
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "e1000" {
errors = append(errors, fmt.Errorf("Model needs to be set to 'e1000', got: %s", value))
}
return
},
},
"name_servers": {
// Optional, IP Network + Shared Network
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"nat": {
// Optional for IP Network
// Required for Shared Network
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"search_domains": {
// Optional, IP Network + Shared Network
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"sec_lists": {
// Required, Shared Network only. Will default if unspecified however
Type: schema.TypeList,
Optional: true,
Computed: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"shared_network": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Default: false,
},
"vnic": {
// Optional, IP Network only.
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
"vnic_sets": {
// Optional, IP Network only.
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
Set: func(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%d-", m["index"].(int)))
buf.WriteString(fmt.Sprintf("%s-", m["vnic"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["nat"]))
buf.WriteString(fmt.Sprintf("%s-", m["model"].(string)))
return hashcode.String(buf.String())
},
},
"reverse_dns": {
Type: schema.TypeBool,
Optional: true,
Default: true,
ForceNew: true,
},
"ssh_keys": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"storage": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"index": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"volume": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"tags": tagsForceNewSchema(),
/////////////////////////
// Computed Attributes //
/////////////////////////
"attributes": {
Type: schema.TypeString,
Computed: true,
},
"availability_domain": {
Type: schema.TypeString,
Computed: true,
},
"domain": {
Type: schema.TypeString,
Computed: true,
},
"entry": {
Type: schema.TypeInt,
Computed: true,
},
"fingerprint": {
Type: schema.TypeString,
Computed: true,
},
"image_format": {
Type: schema.TypeString,
Computed: true,
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
},
"placement_requirements": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"platform": {
Type: schema.TypeString,
Computed: true,
},
"priority": {
Type: schema.TypeString,
Computed: true,
},
"quota_reservation": {
Type: schema.TypeString,
Computed: true,
},
"relationships": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"resolvers": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"site": {
Type: schema.TypeString,
Computed: true,
},
"start_time": {
Type: schema.TypeString,
Computed: true,
},
"state": {
Type: schema.TypeString,
Computed: true,
},
"vcable_id": {
Type: schema.TypeString,
Computed: true,
},
"virtio": {
Type: schema.TypeBool,
Computed: true,
},
"vnc_address": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceInstanceCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).Instances()
// Get Required Attributes
input := &compute.CreateInstanceInput{
Name: d.Get("name").(string),
Shape: d.Get("shape").(string),
}
// Get optional instance attributes
if attributes, err := getInstanceAttributes(d); err != nil && attributes != nil {
input.Attributes = attributes
}
if bootOrder := getIntList(d, "boot_order"); len(bootOrder) > 0 {
input.BootOrder = bootOrder
}
if v, ok := d.GetOk("hostname"); ok {
input.Hostname = v.(string)
}
if v, ok := d.GetOk("image_list"); ok {
input.ImageList = v.(string)
}
if v, ok := d.GetOk("label"); ok {
input.Label = v.(string)
}
interfaces, err := readNetworkInterfacesFromConfig(d)
if err != nil {
return err
}
if interfaces != nil {
input.Networking = interfaces
}
if v, ok := d.GetOk("reverse_dns"); ok {
input.ReverseDNS = v.(bool)
}
if sshKeys := getStringList(d, "ssh_keys"); len(sshKeys) > 0 {
input.SSHKeys = sshKeys
}
// TODO Add storage things
//storage := getStorageAttachments(d)
if tags := getStringList(d, "tags"); len(tags) > 0 {
input.Tags = tags
}
result, err := client.CreateInstance(input)
if err != nil {
return fmt.Errorf("Error creating instance %s: %s", input.Name, err)
}
log.Printf("[DEBUG] Created instance %s: %#v", result.ID)
d.SetId(result.ID)
return resourceInstanceRead(d, meta)
}
func resourceInstanceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).Instances()
name := d.Get("name").(string)
input := &compute.GetInstanceInput{
ID: d.Id(),
Name: name,
}
log.Printf("[DEBUG] Reading state of instance %s", name)
result, err := client.GetInstance(input)
if err != nil {
// Instance doesn't exist
if compute.WasNotFoundError(err) {
log.Printf("[DEBUG] Instance %s not found", name)
d.SetId("")
return nil
}
return fmt.Errorf("Error reading instance %s: %s", name, err)
}
log.Printf("[DEBUG] Instance '%s' found", name)
// Update attributes
return updateInstanceAttributes(d, result)
}
func updateInstanceAttributes(d *schema.ResourceData, instance *compute.InstanceInfo) error {
d.Set("name", instance.Name)
d.Set("shape", instance.Shape)
if err := setInstanceAttributes(d, instance.Attributes); err != nil {
return err
}
if attrs, ok := d.GetOk("instance_attributes"); ok && attrs != nil {
d.Set("instance_attributes", attrs.(string))
}
if err := setIntList(d, "boot_order", instance.BootOrder); err != nil {
return err
}
d.Set("hostname", instance.Hostname)
d.Set("image_list", instance.ImageList)
d.Set("label", instance.Label)
if err := readNetworkInterfaces(d, instance.Networking); err != nil {
return err
}
d.Set("reverse_dns", instance.ReverseDNS)
if err := setStringList(d, "ssh_keys", instance.SSHKeys); err != nil {
return err
}
// TODO Set Storage
if err := setStringList(d, "tags", instance.Tags); err != nil {
return err
}
d.Set("availability_domain", instance.AvailabilityDomain)
d.Set("domain", instance.Domain)
d.Set("entry", instance.Entry)
d.Set("fingerprint", instance.Fingerprint)
d.Set("image_format", instance.ImageFormat)
d.Set("ip_address", instance.IPAddress)
if err := setStringList(d, "placement_requirements", instance.PlacementRequirements); err != nil {
return err
}
d.Set("platform", instance.Platform)
d.Set("priority", instance.Priority)
d.Set("quota_reservation", instance.QuotaReservation)
if err := setStringList(d, "relationships", instance.Relationships); err != nil {
return err
}
if err := setStringList(d, "resolvers", instance.Resolvers); err != nil {
return err
}
d.Set("site", instance.Site)
d.Set("start_time", instance.StartTime)
d.Set("state", instance.State)
if err := setStringList(d, "tags", instance.Tags); err != nil {
return err
}
d.Set("vcable_id", instance.VCableID)
d.Set("virtio", instance.Virtio)
d.Set("vnc_address", instance.VNC)
return nil
}
func resourceInstanceDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).Instances()
name := d.Get("name").(string)
input := &compute.DeleteInstanceInput{
ID: d.Id(),
Name: name,
}
log.Printf("[DEBUG] Deleting instance %s", name)
if err := client.DeleteInstance(input); err != nil {
return fmt.Errorf("Error deleting instance %s: %s", name, err)
}
return nil
}
// TODO Uncomment this when working on storage
/*
func getStorageAttachments(d *schema.ResourceData) []compute.StorageAttachment {
storageAttachments := []compute.StorageAttachment{}
storage := d.Get("storage").(*schema.Set)
for _, i := range storage.List() {
attrs := i.(map[string]interface{})
storageAttachments = append(storageAttachments, compute.StorageAttachmentInput{
Index: attrs["index"].(int),
Volume: attrs["volume"].(string),
})
}
return storageAttachments
}*/
// Parses instance_attributes from a string to a map[string]interface and returns any errors.
func getInstanceAttributes(d *schema.ResourceData) (map[string]interface{}, error) {
var attrs map[string]interface{}
// Empty instance attributes
attributes, ok := d.GetOk("instance_attributes")
if !ok {
return attrs, nil
}
if err := json.Unmarshal([]byte(attributes.(string)), &attrs); err != nil {
return attrs, fmt.Errorf("Cannot parse attributes as json: %s", err)
}
return attrs, nil
}
// Reads attributes from the returned instance object, and sets the computed attributes string
// as JSON
func setInstanceAttributes(d *schema.ResourceData, attributes map[string]interface{}) error {
// Shouldn't ever get nil attributes on an instance, but protect against the case either way
if attributes == nil {
return nil
}
b, err := json.Marshal(attributes)
if err != nil {
return fmt.Errorf("Error marshalling returned attributes: %s", err)
}
return d.Set("attributes", string(b))
}
// Populates and validates shared network and ip network interfaces to return the of map
// objects needed to create/update an instance's networking_info
func readNetworkInterfacesFromConfig(d *schema.ResourceData) (map[string]compute.NetworkingInfo, error) {
interfaces := make(map[string]compute.NetworkingInfo)
if v, ok := d.GetOk("networking_info"); ok {
vL := v.(*schema.Set).List()
for _, v := range vL {
ni := v.(map[string]interface{})
index, ok := ni["index"].(int)
if !ok {
return nil, fmt.Errorf("Index not specified for network interface: %v", ni)
}
deviceIndex := fmt.Sprintf("eth%d", index)
// Verify that the network interface doesn't already exist
if _, ok := interfaces[deviceIndex]; ok {
return nil, fmt.Errorf("Duplicate Network interface at eth%d already specified", index)
}
// Determine if we're creating a shared network interface or an IP Network interface
info := compute.NetworkingInfo{}
var err error
if ni["shared_network"].(bool) {
// Populate shared network parameters
info, err = readSharedNetworkFromConfig(ni)
} else {
// Populate IP Network Parameters
info, err = readIPNetworkFromConfig(ni)
}
if err != nil {
return nil, err
}
// And you may find yourself in a beautiful house, with a beautiful wife
// And you may ask yourself, well, how did I get here?
interfaces[deviceIndex] = info
}
}
return interfaces, nil
}
// Reads a networking_info config block as a shared network interface
func readSharedNetworkFromConfig(ni map[string]interface{}) (compute.NetworkingInfo, error) {
info := compute.NetworkingInfo{}
// Validate the shared network
if err := validateSharedNetwork(ni); err != nil {
return info, err
}
// Populate shared network fields; checking type casting
dns := []string{}
if v, ok := ni["dns"]; ok && v != nil {
for _, d := range v.([]interface{}) {
dns = append(dns, d.(string))
}
if len(dns) > 0 {
info.DNS = dns
}
}
if v, ok := ni["model"].(string); ok && v != "" {
info.Model = compute.NICModel(v)
}
nats := []string{}
if v, ok := ni["nat"]; ok && v != nil {
for _, nat := range v.([]interface{}) {
nats = append(nats, nat.(string))
}
if len(nats) > 0 {
info.Nat = nats
}
}
slists := []string{}
if v, ok := ni["sec_lists"]; ok && v != nil {
for _, slist := range v.([]interface{}) {
slists = append(slists, slist.(string))
}
if len(slists) > 0 {
info.SecLists = slists
}
}
nservers := []string{}
if v, ok := ni["name_servers"]; ok && v != nil {
for _, nserver := range v.([]interface{}) {
nservers = append(nservers, nserver.(string))
}
if len(nservers) > 0 {
info.NameServers = nservers
}
}
sdomains := []string{}
if v, ok := ni["search_domains"]; ok && v != nil {
for _, sdomain := range v.([]interface{}) {
sdomains = append(sdomains, sdomain.(string))
}
if len(sdomains) > 0 {
info.SearchDomains = sdomains
}
}
return info, nil
}
// Unfortunately this cannot take place during plan-phase, because we currently cannot have a validation
// function based off of multiple fields in the supplied schema.
func validateSharedNetwork(ni map[string]interface{}) error {
// A Shared Networking Interface MUST have the following attributes set:
// - "model"
// - "nat"
// The following attributes _cannot_ be set for a shared network:
// - "ip_address"
// - "ip_network"
// - "mac_address"
// - "vnic"
// - "vnic_sets"
if d, ok := ni["model"]; !ok || d.(string) == "" {
return fmt.Errorf("'model' field needs to be set for a Shared Networking Interface")
}
if _, ok := ni["nat"]; !ok {
return fmt.Errorf("'nat' field needs to be set for a Shared Networking Interface")
}
// Strings only
nilAttrs := []string{
"ip_address",
"ip_network",
"mac_address",
"vnic",
}
for _, v := range nilAttrs {
if d, ok := ni[v]; ok && d.(string) != "" {
return fmt.Errorf("%q field cannot be set in a Shared Networking Interface", v)
}
}
if _, ok := ni["vnic_sets"].([]string); ok {
return fmt.Errorf("%q field cannot be set in a Shared Networking Interface", "vnic_sets")
}
return nil
}
// Populates fields for an IP Network
func readIPNetworkFromConfig(ni map[string]interface{}) (compute.NetworkingInfo, error) {
info := compute.NetworkingInfo{}
// Validate the IP Network
if err := validateIPNetwork(ni); err != nil {
return info, err
}
// Populate fields
if v, ok := ni["ip_network"].(string); ok && v != "" {
info.IPNetwork = v
}
dns := []string{}
if v, ok := ni["dns"]; ok && v != nil {
for _, d := range v.([]interface{}) {
dns = append(dns, d.(string))
}
if len(dns) > 0 {
info.DNS = dns
}
}
if v, ok := ni["ip_address"].(string); ok && v != "" {
info.IPAddress = v
}
if v, ok := ni["mac_address"].(string); ok && v != "" {
info.MACAddress = v
}
nservers := []string{}
if v, ok := ni["name_servers"]; ok && v != nil {
for _, nserver := range v.([]interface{}) {
nservers = append(nservers, nserver.(string))
}
if len(nservers) > 0 {
info.NameServers = nservers
}
}
nats := []string{}
if v, ok := ni["nat"]; ok && v != nil {
for _, nat := range v.([]interface{}) {
nats = append(nats, nat.(string))
}
if len(nats) > 0 {
info.Nat = nats
}
}
sdomains := []string{}
if v, ok := ni["search_domains"]; ok && v != nil {
for _, sdomain := range v.([]interface{}) {
sdomains = append(sdomains, sdomain.(string))
}
if len(sdomains) > 0 {
info.SearchDomains = sdomains
}
}
if v, ok := ni["vnic"].(string); ok && v != "" {
info.Vnic = v
}
vnicSets := []string{}
if v, ok := ni["vnic_sets"]; ok && v != nil {
for _, vnic := range v.([]interface{}) {
vnicSets = append(vnicSets, vnic.(string))
}
if len(vnicSets) > 0 {
info.VnicSets = vnicSets
}
}
return info, nil
}
// Validates an IP Network config block
func validateIPNetwork(ni map[string]interface{}) error {
// An IP Networking Interface MUST have the following attributes set:
// - "ip_network"
// The following attributes _cannot_ be set for an IP Network:
// - "model"
// Required to be set
if d, ok := ni["ip_network"]; !ok || d.(string) == "" {
return fmt.Errorf("'ip_network' field is required for an IP Network interface")
}
// Requird to be unset
if d, ok := ni["model"]; ok && d.(string) != "" {
return fmt.Errorf("'model' cannot be set in an IP Network Interface")
}
return nil
}
// Reads network interfaces from the config
func readNetworkInterfaces(d *schema.ResourceData, ifaces map[string]compute.NetworkingInfo) error {
result := make([]map[string]interface{}, 0)
// Nil check for import case
if ifaces == nil {
return d.Set("networking_info", result)
}
for _, iface := range ifaces {
res := make(map[string]interface{})
if iface.DNS != nil {
res["dns"] = iface.DNS
}
if iface.IPAddress != "" {
res["ip_address"] = iface.IPAddress
}
if iface.IPNetwork != "" {
res["ip_network"] = iface.IPNetwork
}
if iface.MACAddress != "" {
res["mac_address"] = iface.MACAddress
}
if iface.Model != "" {
res["model"] = iface.Model
// Model can only be set on Shared networks
res["shared_network"] = true
}
if iface.NameServers != nil {
res["name_servers"] = iface.NameServers
}
if iface.Nat != nil {
res["nat"] = iface.Nat
}
if iface.SearchDomains != nil {
res["search_domains"] = iface.SearchDomains
}
if iface.SecLists != nil {
res["sec_lists"] = iface.SecLists
}
if iface.Vnic != "" {
res["vnic"] = iface.Vnic
// VNIC can only be set on an IP Network
res["shared_network"] = false
}
if iface.VnicSets != nil {
res["vnic_sets"] = iface.VnicSets
}
result = append(result, res)
}
return d.Set("networking_info", result)
}

View File

@ -0,0 +1,229 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccOPCInstance_basic(t *testing.T) {
resName := "opc_compute_instance.test"
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccInstanceBasic(rInt),
Check: resource.ComposeTestCheckFunc(
testAccOPCCheckInstanceExists,
resource.TestCheckResourceAttr(resName, "name", fmt.Sprintf("acc-test-instance-%d", rInt)),
resource.TestCheckResourceAttr(resName, "label", "TestAccOPCInstance_basic"),
),
},
},
})
}
func TestAccOPCInstance_sharedNetworking(t *testing.T) {
rInt := acctest.RandInt()
resName := "opc_compute_instance.test"
dataName := "data.opc_compute_network_interface.test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccInstanceSharedNetworking(rInt),
Check: resource.ComposeTestCheckFunc(
testAccOPCCheckInstanceExists,
resource.TestCheckResourceAttrSet(resName, "id"),
resource.TestCheckResourceAttrSet(resName, "availability_domain"),
resource.TestCheckResourceAttrSet(resName, "domain"),
resource.TestCheckResourceAttrSet(resName, "hostname"),
resource.TestCheckResourceAttrSet(resName, "ip_address"),
resource.TestCheckResourceAttr(resName, "name", fmt.Sprintf("acc-test-instance-%d", rInt)),
resource.TestCheckResourceAttr(resName, "networking_info.#", "1"),
// Default Placement Reqs
resource.TestCheckResourceAttr(resName, "placement_requirements.#", "2"),
resource.TestCheckResourceAttr(resName, "placement_requirements.0", "/system/compute/allow_instances"),
resource.TestCheckResourceAttr(resName, "placement_requirements.1", "/system/compute/placement/default"),
resource.TestCheckResourceAttr(resName, "platform", "linux"),
resource.TestCheckResourceAttr(resName, "priority", "/oracle/public/default"),
resource.TestCheckResourceAttr(resName, "reverse_dns", "true"),
resource.TestCheckResourceAttr(resName, "state", "running"),
resource.TestCheckResourceAttr(resName, "tags.#", "2"),
resource.TestCheckResourceAttrSet(resName, "vcable_id"),
resource.TestCheckResourceAttr(resName, "virtio", "false"),
// Check Data Source to validate networking attributes
resource.TestCheckResourceAttr(dataName, "shared_network", "true"),
resource.TestCheckResourceAttr(dataName, "nat.#", "1"),
resource.TestCheckResourceAttr(dataName, "model", "e1000"),
resource.TestCheckResourceAttr(dataName, "sec_lists.#", "1"),
resource.TestCheckResourceAttr(dataName, "name_servers.#", "0"),
resource.TestCheckResourceAttr(dataName, "vnic_sets.#", "0"),
),
},
},
})
}
func TestAccOPCInstance_ipNetwork(t *testing.T) {
rInt := acctest.RandInt()
resName := "opc_compute_instance.test"
dataName := "data.opc_compute_network_interface.test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccInstanceIPNetworking(rInt),
Check: resource.ComposeTestCheckFunc(
testAccOPCCheckInstanceExists,
resource.TestCheckResourceAttrSet(resName, "id"),
resource.TestCheckResourceAttrSet(resName, "availability_domain"),
resource.TestCheckResourceAttrSet(resName, "domain"),
resource.TestCheckResourceAttrSet(resName, "ip_address"),
resource.TestCheckResourceAttr(resName, "name", fmt.Sprintf("acc-test-instance-%d", rInt)),
resource.TestCheckResourceAttr(resName, "networking_info.#", "1"),
// Default Placement Reqs
resource.TestCheckResourceAttr(resName, "placement_requirements.#", "2"),
resource.TestCheckResourceAttr(resName, "placement_requirements.0", "/system/compute/allow_instances"),
resource.TestCheckResourceAttr(resName, "placement_requirements.1", "/system/compute/placement/default"),
resource.TestCheckResourceAttr(resName, "platform", "linux"),
resource.TestCheckResourceAttr(resName, "priority", "/oracle/public/default"),
resource.TestCheckResourceAttr(resName, "reverse_dns", "true"),
resource.TestCheckResourceAttr(resName, "state", "running"),
resource.TestCheckResourceAttr(resName, "virtio", "false"),
// Check Data Source to validate networking attributes
resource.TestCheckResourceAttr(dataName, "ip_network", fmt.Sprintf("testing-ip-network-%d", rInt)),
resource.TestCheckResourceAttr(dataName, "vnic", fmt.Sprintf("ip-network-test-%d", rInt)),
resource.TestCheckResourceAttr(dataName, "shared_network", "false"),
),
},
},
})
}
func testAccOPCCheckInstanceExists(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).Instances()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_instance" {
continue
}
input := &compute.GetInstanceInput{
ID: rs.Primary.ID,
Name: rs.Primary.Attributes["name"],
}
_, err := client.GetInstance(input)
if err != nil {
return fmt.Errorf("Error retrieving state of Instance %s: %s", input.Name, err)
}
}
return nil
}
func testAccOPCCheckInstanceDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).Instances()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_instance" {
continue
}
input := &compute.GetInstanceInput{
ID: rs.Primary.ID,
Name: rs.Primary.Attributes["name"],
}
if info, err := client.GetInstance(input); err == nil {
return fmt.Errorf("Instance %s still exists: %#v", input.Name, info)
}
}
return nil
}
const validSSHKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqw6JwbjIkZEr5UcMojtxhk6Zum39NOihHNXEvRWDt5WssX8TH/ghpv3D25K1pJkf+wfAi17HwEmYwPMEyEHENS443v6RZbXvzCkUWzkJzq7Zvbdqld038km31La2QUoMMp1KL5zk1nM65xCeQDVcR/h++03EScB2CuzTpAV6khMdfgOJgxm361kfrDVRwc1HQrAOpOnzkpPfwqBrYWqN1UnKvuO77Wk8z5LBe03EPNru3bLE3s3qHI9hjO0gXMiVUi0KyNxdWfDO8esqQlKavHAeePyrRA55YF8kBB5dEl4tVNOqpY/8TRnGN1mOe0LWxa8Ytz1wbyS49knsNVTel"
func testAccInstanceBasic(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_instance" "test" {
name = "acc-test-instance-%d"
label = "TestAccOPCInstance_basic"
shape = "oc3"
image_list = "/oracle/public/oel_6.7_apaas_16.4.5_1610211300"
instance_attributes = <<JSON
{
"foo" = "bar"
}
JSON
}`, rInt)
}
func testAccInstanceSharedNetworking(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_instance" "test" {
name = "acc-test-instance-%d"
label = "TestAccOPCInstance_sharedNetworking"
shape = "oc3"
image_list = "/oracle/public/oel_6.7_apaas_16.4.5_1610211300"
tags = ["tag1", "tag2"]
networking_info {
index = 0
model = "e1000"
nat = ["ippool:/oracle/public/ippool"]
shared_network = true
}
}
data "opc_compute_network_interface" "test" {
instance_name = "${opc_compute_instance.test.name}"
instance_id = "${opc_compute_instance.test.id}"
interface = "eth0"
}
`, rInt)
}
func testAccInstanceIPNetworking(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_network" "foo" {
name = "testing-ip-network-%d"
description = "testing-ip-network-instance"
ip_address_prefix = "10.1.12.0/24"
}
resource "opc_compute_instance" "test" {
name = "acc-test-instance-%d"
label = "TestAccOPCInstance_ipNetwork"
shape = "oc3"
image_list = "/oracle/public/oel_6.7_apaas_16.4.5_1610211300"
networking_info {
index = 0
ip_network = "${opc_compute_ip_network.foo.id}"
vnic = "ip-network-test-%d"
shared_network = false
}
}
data "opc_compute_network_interface" "test" {
instance_id = "${opc_compute_instance.test.id}"
instance_name = "${opc_compute_instance.test.name}"
interface = "eth0"
}
`, rInt, rInt, rInt)
}

View File

@ -0,0 +1,147 @@
package opc
import (
"fmt"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceOPCIPAddressPrefixSet() *schema.Resource {
return &schema.Resource{
Create: resourceOPCIPAddressPrefixSetCreate,
Read: resourceOPCIPAddressPrefixSetRead,
Update: resourceOPCIPAddressPrefixSetUpdate,
Delete: resourceOPCIPAddressPrefixSetDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"prefixes": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateIPPrefixCIDR,
},
},
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"tags": tagsOptionalSchema(),
"uri": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceOPCIPAddressPrefixSetCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressPrefixSets()
input := compute.CreateIPAddressPrefixSetInput{
Name: d.Get("name").(string),
}
prefixes := getStringList(d, "prefixes")
if len(prefixes) != 0 {
input.IPAddressPrefixes = prefixes
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
if description, ok := d.GetOk("description"); ok {
input.Description = description.(string)
}
info, err := client.CreateIPAddressPrefixSet(&input)
if err != nil {
return fmt.Errorf("Error creating IP Address Prefix Set: %s", err)
}
d.SetId(info.Name)
return resourceOPCIPAddressPrefixSetRead(d, meta)
}
func resourceOPCIPAddressPrefixSetRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressPrefixSets()
getInput := compute.GetIPAddressPrefixSetInput{
Name: d.Id(),
}
result, err := client.GetIPAddressPrefixSet(&getInput)
if err != nil {
// IP Address Prefix Set does not exist
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading IP Address Prefix Set %s: %s", d.Id(), err)
}
d.Set("name", result.Name)
d.Set("description", result.Description)
d.Set("uri", result.Uri)
if err := setStringList(d, "prefixes", result.IPAddressPrefixes); err != nil {
return err
}
if err := setStringList(d, "tags", result.Tags); err != nil {
return err
}
return nil
}
func resourceOPCIPAddressPrefixSetUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressPrefixSets()
input := compute.UpdateIPAddressPrefixSetInput{
Name: d.Get("name").(string),
}
prefixes := getStringList(d, "prefixes")
if len(prefixes) != 0 {
input.IPAddressPrefixes = prefixes
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
if description, ok := d.GetOk("description"); ok {
input.Description = description.(string)
}
info, err := client.UpdateIPAddressPrefixSet(&input)
if err != nil {
return fmt.Errorf("Error updating IP Address Prefix Set: %s", err)
}
d.SetId(info.Name)
return resourceOPCIPAddressPrefixSetRead(d, meta)
}
func resourceOPCIPAddressPrefixSetDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressPrefixSets()
name := d.Id()
input := compute.DeleteIPAddressPrefixSetInput{
Name: name,
}
if err := client.DeleteIPAddressPrefixSet(&input); err != nil {
return fmt.Errorf("Error deleting IP Address Prefix Set: %s", err)
}
return nil
}

View File

@ -0,0 +1,100 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccOPCIPAddressPrefixSet_Basic(t *testing.T) {
rInt := acctest.RandInt()
resourceName := "opc_compute_ip_address_prefix_set.test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckIPAddressPrefixSetDestroy,
Steps: []resource.TestStep{
{
Config: testAccIPAddressPrefixSetBasic(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckIPAddressPrefixSetExists,
resource.TestCheckResourceAttr(
resourceName, "tags.#", "2"),
resource.TestCheckResourceAttr(
resourceName, "prefixes.#", "2"),
),
},
{
Config: testAccIPAddressPrefixSetBasic_Update(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
resourceName, "tags.#", "1"),
resource.TestCheckResourceAttr(
resourceName, "prefixes.0", "171.120.0.0/24"),
),
},
},
})
}
func testAccCheckIPAddressPrefixSetExists(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPAddressPrefixSets()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_address_prefix_set" {
continue
}
input := compute.GetIPAddressPrefixSetInput{
Name: rs.Primary.Attributes["name"],
}
if _, err := client.GetIPAddressPrefixSet(&input); err != nil {
return fmt.Errorf("Error retrieving state of IP Address Prefix Set %s: %s", input.Name, err)
}
}
return nil
}
func testAccCheckIPAddressPrefixSetDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPAddressPrefixSets()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_address_prefix_set" {
continue
}
input := compute.GetIPAddressPrefixSetInput{
Name: rs.Primary.Attributes["name"],
}
if info, err := client.GetIPAddressPrefixSet(&input); err == nil {
return fmt.Errorf("IP Address Prefix Set %s still exists: %#v", input.Name, info)
}
}
return nil
}
func testAccIPAddressPrefixSetBasic(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_address_prefix_set" "test" {
name = "testing-acc-%d"
prefixes = ["172.120.0.0/24", "192.168.0.0/16"]
description = "acctesting ip address prefix test %d"
tags = ["tag1", "tag2"]
}`, rInt, rInt)
}
func testAccIPAddressPrefixSetBasic_Update(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_address_prefix_set" "test" {
name = "testing-acc-%d"
description = "acctesting ip address prefix test updated %d"
prefixes = ["171.120.0.0/24", "192.168.0.0/16"]
tags = ["tag1"]
}`, rInt, rInt)
}

View File

@ -0,0 +1,137 @@
package opc
import (
"fmt"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)
func resourceOPCIPAddressReservation() *schema.Resource {
return &schema.Resource{
Create: resourceOPCIPAddressReservationCreate,
Read: resourceOPCIPAddressReservationRead,
Update: resourceOPCIPAddressReservationUpdate,
Delete: resourceOPCIPAddressReservationDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"ip_address_pool": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(compute.PublicIPAddressPool),
string(compute.PrivateIPAddressPool),
}, true),
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"tags": tagsOptionalSchema(),
"ip_address": {
Type: schema.TypeString,
Computed: true,
},
"uri": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceOPCIPAddressReservationCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressReservations()
input := compute.CreateIPAddressReservationInput{
Name: d.Get("name").(string),
IPAddressPool: d.Get("ip_address_pool").(string),
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
if description, ok := d.GetOk("description"); ok {
input.Description = description.(string)
}
info, err := client.CreateIPAddressReservation(&input)
if err != nil {
return fmt.Errorf("Error creating IP Address Reservation: %s", err)
}
d.SetId(info.Name)
return resourceOPCIPAddressReservationRead(d, meta)
}
func resourceOPCIPAddressReservationRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressReservations()
getInput := compute.GetIPAddressReservationInput{
Name: d.Id(),
}
result, err := client.GetIPAddressReservation(&getInput)
if err != nil {
// IP Address Reservation does not exist
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading ip address reservation %s: %s", d.Id(), err)
}
d.Set("name", result.Name)
d.Set("description", result.Description)
d.Set("ip_address_pool", result.IPAddressPool)
d.Set("uri", result.Uri)
if err := setStringList(d, "tags", result.Tags); err != nil {
return err
}
return nil
}
func resourceOPCIPAddressReservationUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressReservations()
input := compute.UpdateIPAddressReservationInput{
Name: d.Get("name").(string),
IPAddressPool: d.Get("ip_address_pool").(string),
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
if description, ok := d.GetOk("description"); ok {
input.Description = description.(string)
}
info, err := client.UpdateIPAddressReservation(&input)
if err != nil {
return fmt.Errorf("Error updating IP Address Reservation: %s", err)
}
d.SetId(info.Name)
return resourceOPCIPAddressReservationRead(d, meta)
}
func resourceOPCIPAddressReservationDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAddressReservations()
name := d.Id()
input := compute.DeleteIPAddressReservationInput{
Name: name,
}
if err := client.DeleteIPAddressReservation(&input); err != nil {
return fmt.Errorf("Error deleting IP Address Reservation: %+v", err)
}
return nil
}

View File

@ -0,0 +1,77 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccOPCIPAddressReservation_Basic(t *testing.T) {
rInt := acctest.RandInt()
resName := "opc_compute_ip_address_reservation.test"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckIPAddressReservationDestroy,
Steps: []resource.TestStep{
{
Config: testAccOPCIPAddressReservationConfig_Basic(rInt),
Check: resource.ComposeTestCheckFunc(
testAccOPCCheckIPAddressReservationExists,
resource.TestCheckResourceAttr(resName, "name", fmt.Sprintf("testing-ip-address-reservation-%d", rInt)),
),
},
},
})
}
func testAccOPCCheckIPAddressReservationExists(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPAddressReservations()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_address_reservation" {
continue
}
input := compute.GetIPAddressReservationInput{
Name: rs.Primary.Attributes["name"],
}
if _, err := client.GetIPAddressReservation(&input); err != nil {
return fmt.Errorf("Error retrieving state of IP Address Reservation %s: %s", input.Name, err)
}
}
return nil
}
func testAccOPCCheckIPAddressReservationDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPAddressReservations()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_address_reservation" {
continue
}
input := compute.GetIPAddressReservationInput{
Name: rs.Primary.Attributes["name"],
}
if info, err := client.GetIPAddressReservation(&input); err == nil {
return fmt.Errorf("IP Address Reservation %s still exists: %#v", input.Name, info)
}
}
return nil
}
func testAccOPCIPAddressReservationConfig_Basic(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_address_reservation" "test" {
name = "testing-ip-address-reservation-%d"
description = "testing-desc-%d"
ip_address_pool = "public-ippool"
}`, rInt, rInt)
}

View File

@ -0,0 +1,96 @@
package opc
import (
"fmt"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceOPCIPAssociation() *schema.Resource {
return &schema.Resource{
Create: resourceOPCIPAssociationCreate,
Read: resourceOPCIPAssociationRead,
Delete: resourceOPCIPAssociationDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"vcable": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"parent_pool": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}
func resourceOPCIPAssociationCreate(d *schema.ResourceData, meta interface{}) error {
vCable := d.Get("vcable").(string)
parentPool := d.Get("parent_pool").(string)
client := meta.(*compute.Client).IPAssociations()
input := compute.CreateIPAssociationInput{
ParentPool: parentPool,
VCable: vCable,
}
info, err := client.CreateIPAssociation(&input)
if err != nil {
return fmt.Errorf("Error creating ip association between vcable %s and parent pool %s: %s", vCable, parentPool, err)
}
d.SetId(info.Name)
return resourceOPCIPAssociationRead(d, meta)
}
func resourceOPCIPAssociationRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAssociations()
name := d.Id()
input := compute.GetIPAssociationInput{
Name: name,
}
result, err := client.GetIPAssociation(&input)
if err != nil {
// IP Association does not exist
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading ip association '%s': %s", name, err)
}
d.Set("name", result.Name)
d.Set("parent_pool", result.ParentPool)
d.Set("vcable", result.VCable)
return nil
}
func resourceOPCIPAssociationDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPAssociations()
name := d.Id()
input := compute.DeleteIPAssociationInput{
Name: name,
}
if err := client.DeleteIPAssociation(&input); err != nil {
return fmt.Errorf("Error deleting ip association '%s': %s", name, err)
}
return nil
}

View File

@ -0,0 +1,88 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccOPCIPAssociation_Basic(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccIPAssociationBasic, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccOPCCheckIPAssociationDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testAccOPCCheckIPAssociationExists,
),
},
},
})
}
func testAccOPCCheckIPAssociationExists(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPAssociations()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_association" {
continue
}
input := compute.GetIPAssociationInput{
Name: rs.Primary.Attributes["name"],
}
if _, err := client.GetIPAssociation(&input); err != nil {
return fmt.Errorf("Error retrieving state of IP Association %s: %s", input.Name, err)
}
}
return nil
}
func testAccOPCCheckIPAssociationDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPAssociations()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_association" {
continue
}
input := compute.GetIPAssociationInput{
Name: rs.Primary.Attributes["name"],
}
if info, err := client.GetIPAssociation(&input); err == nil {
return fmt.Errorf("IP Association %s still exists: %#v", input.Name, info)
}
}
return nil
}
var testAccIPAssociationBasic = `
resource "opc_compute_instance" "test" {
name = "test-acc-ip-ass-instance-%d"
label = "testAccIPAssociationBasic"
shape = "oc3"
image_list = "/oracle/public/oel_6.7_apaas_16.4.5_1610211300"
}
resource "opc_compute_ip_reservation" "test" {
name = "test-acc-ip-ass-reservation-%d"
parent_pool = "/oracle/public/ippool"
permanent = true
}
resource "opc_compute_ip_association" "test" {
vcable = "${opc_compute_instance.test.vcable_id}"
parent_pool = "ipreservation:${opc_compute_ip_reservation.test.name}"
}
`

View File

@ -0,0 +1,176 @@
package opc
import (
"fmt"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceOPCIPNetwork() *schema.Resource {
return &schema.Resource{
Create: resourceOPCIPNetworkCreate,
Read: resourceOPCIPNetworkRead,
Update: resourceOPCIPNetworkUpdate,
Delete: resourceOPCIPNetworkDelete,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"ip_address_prefix": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateIPPrefixCIDR,
},
"ip_network_exchange": {
Type: schema.TypeString,
Optional: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"public_napt_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"uri": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsOptionalSchema(),
},
}
}
func resourceOPCIPNetworkCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPNetworks()
// Get required attributes
name := d.Get("name").(string)
ipPrefix := d.Get("ip_address_prefix").(string)
// public_napt_enabled is not required, but bool type allows it to be unspecified
naptEnabled := d.Get("public_napt_enabled").(bool)
input := &compute.CreateIPNetworkInput{
Name: name,
IPAddressPrefix: ipPrefix,
PublicNaptEnabled: naptEnabled,
}
// Get Optional attributes
if desc, ok := d.GetOk("description"); ok && desc != nil {
input.Description = desc.(string)
}
if ipEx, ok := d.GetOk("ip_network_exchange"); ok && ipEx != nil {
input.IPNetworkExchange = ipEx.(string)
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
info, err := client.CreateIPNetwork(input)
if err != nil {
return fmt.Errorf("Error creating IP Network '%s': %v", name, err)
}
d.SetId(info.Name)
return resourceOPCIPNetworkRead(d, meta)
}
func resourceOPCIPNetworkRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPNetworks()
name := d.Id()
input := &compute.GetIPNetworkInput{
Name: name,
}
res, err := client.GetIPNetwork(input)
if err != nil {
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading IP Network '%s': %v", name, err)
}
d.Set("name", res.Name)
d.Set("ip_address_prefix", res.IPAddressPrefix)
d.Set("ip_network_exchanged", res.IPNetworkExchange)
d.Set("description", res.Description)
d.Set("public_napt_enabled", res.PublicNaptEnabled)
d.Set("uri", res.Uri)
if err := setStringList(d, "tags", res.Tags); err != nil {
return err
}
return nil
}
func resourceOPCIPNetworkUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPNetworks()
// Get required attributes
name := d.Get("name").(string)
ipPrefix := d.Get("ip_address_prefix").(string)
// public_napt_enabled is not required, but bool type allows it to be unspecified
naptEnabled := d.Get("public_napt_enabled").(bool)
input := &compute.UpdateIPNetworkInput{
Name: name,
IPAddressPrefix: ipPrefix,
PublicNaptEnabled: naptEnabled,
}
// Get Optional attributes
desc, descOk := d.GetOk("description")
if descOk {
input.Description = desc.(string)
}
ipEx, ipExOk := d.GetOk("ip_network_exchange")
if ipExOk {
input.IPNetworkExchange = ipEx.(string)
}
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
info, err := client.UpdateIPNetwork(input)
if err != nil {
return fmt.Errorf("Error updating IP Network '%s': %v", name, err)
}
d.SetId(info.Name)
return resourceOPCIPNetworkRead(d, meta)
}
func resourceOPCIPNetworkDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPNetworks()
name := d.Id()
input := &compute.DeleteIPNetworkInput{
Name: name,
}
if err := client.DeleteIPNetwork(input); err != nil {
return fmt.Errorf("Error deleting IP Network '%s': %v", name, err)
}
return nil
}

View File

@ -0,0 +1,105 @@
package opc
import (
"fmt"
"log"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceOPCIPNetworkExchange() *schema.Resource {
return &schema.Resource{
Create: resourceOPCIPNetworkExchangeCreate,
Read: resourceOPCIPNetworkExchangeRead,
Delete: resourceOPCIPNetworkExchangeDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"tags": tagsForceNewSchema(),
"uri": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceOPCIPNetworkExchangeCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPNetworkExchanges()
input := compute.CreateIPNetworkExchangeInput{
Name: d.Get("name").(string),
}
log.Printf("[DEBUG] Creating ip network exchange '%s'", input.Name)
tags := getStringList(d, "tags")
if len(tags) != 0 {
input.Tags = tags
}
if description, ok := d.GetOk("description"); ok {
input.Description = description.(string)
}
info, err := client.CreateIPNetworkExchange(&input)
if err != nil {
return fmt.Errorf("Error creating IP Network Exchange: %s", err)
}
d.SetId(info.Name)
return resourceOPCIPNetworkExchangeRead(d, meta)
}
func resourceOPCIPNetworkExchangeRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPNetworkExchanges()
log.Printf("[DEBUG] Reading state of IP Network Exchange %s", d.Id())
getInput := compute.GetIPNetworkExchangeInput{
Name: d.Id(),
}
result, err := client.GetIPNetworkExchange(&getInput)
if err != nil {
// IP NetworkExchange does not exist
if compute.WasNotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error reading ip network exchange %s: %s", d.Id(), err)
}
d.Set("name", result.Name)
d.Set("description", result.Description)
d.Set("uri", result.Uri)
if err := setStringList(d, "tags", result.Tags); err != nil {
return err
}
return nil
}
func resourceOPCIPNetworkExchangeDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*compute.Client).IPNetworkExchanges()
name := d.Id()
log.Printf("[DEBUG] Deleting IP Network Exchange '%s'", name)
input := compute.DeleteIPNetworkExchangeInput{
Name: name,
}
if err := client.DeleteIPNetworkExchange(&input); err != nil {
return fmt.Errorf("Error deleting IP Network Exchange '%s': %+v", name, err)
}
return nil
}

View File

@ -0,0 +1,73 @@
package opc
import (
"fmt"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccOPCIPNetworkExchange_Basic(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccIPNetworkExchangeBasic, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckIPNetworkExchangeDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: testAccCheckIPNetworkExchangeExists,
},
},
})
}
func testAccCheckIPNetworkExchangeExists(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPNetworkExchanges()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_network_exchange" {
continue
}
input := compute.GetIPNetworkExchangeInput{
Name: rs.Primary.Attributes["name"],
}
if _, err := client.GetIPNetworkExchange(&input); err != nil {
return fmt.Errorf("Error retrieving state of ip network exchange %s: %s", input.Name, err)
}
}
return nil
}
func testAccCheckIPNetworkExchangeDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*compute.Client).IPNetworkExchanges()
for _, rs := range s.RootModule().Resources {
if rs.Type != "opc_compute_ip_network_exchange" {
continue
}
input := compute.GetIPNetworkExchangeInput{
Name: rs.Primary.Attributes["name"],
}
if info, err := client.GetIPNetworkExchange(&input); err == nil {
return fmt.Errorf("IPNetworkExchange %s still exists: %#v", input.Name, info)
}
}
return nil
}
var testAccIPNetworkExchangeBasic = `
resource "opc_compute_ip_network_exchange" "test" {
name = "test_ip_network_exchange-%d"
description = "test ip network exchange"
}
`

View File

@ -0,0 +1,91 @@
package opc
import (
"fmt"
"regexp"
"testing"
"github.com/hashicorp/go-oracle-terraform/compute"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccOPCIPNetwork_Basic(t *testing.T) {
rInt := acctest.RandInt()
resName := "opc_compute_ip_network.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: opcResourceCheck(resName, testAccOPCCheckIPNetworkDestroyed),
Steps: []resource.TestStep{
{
Config: testAccOPCIPNetworkConfig_Basic(rInt),
Check: resource.ComposeTestCheckFunc(
opcResourceCheck(resName, testAccOPCCheckIPNetworkExists),
resource.TestCheckResourceAttr(resName, "ip_address_prefix", "10.0.12.0/24"),
resource.TestCheckResourceAttr(resName, "public_napt_enabled", "false"),
resource.TestCheckResourceAttr(resName, "description", fmt.Sprintf("testing-desc-%d", rInt)),
resource.TestCheckResourceAttr(resName, "name", fmt.Sprintf("testing-ip-network-%d", rInt)),
resource.TestMatchResourceAttr(resName, "uri", regexp.MustCompile("testing-ip-network")),
),
},
{
Config: testAccOPCIPNetworkConfig_BasicUpdate(rInt),
Check: resource.ComposeTestCheckFunc(
opcResourceCheck(resName, testAccOPCCheckIPNetworkExists),
resource.TestCheckResourceAttr(resName, "ip_address_prefix", "10.0.12.0/24"),
resource.TestCheckResourceAttr(resName, "public_napt_enabled", "true"),
resource.TestCheckResourceAttr(resName, "description", fmt.Sprintf("testing-desc-%d", rInt)),
resource.TestCheckResourceAttr(resName, "name", fmt.Sprintf("testing-ip-network-%d", rInt)),
),
},
},
})
}
func testAccOPCIPNetworkConfig_Basic(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_network" "foo" {
name = "testing-ip-network-%d"
description = "testing-desc-%d"
ip_address_prefix = "10.0.12.0/24"
}`, rInt, rInt)
}
func testAccOPCIPNetworkConfig_BasicUpdate(rInt int) string {
return fmt.Sprintf(`
resource "opc_compute_ip_network" "foo" {
name = "testing-ip-network-%d"
description = "testing-desc-%d"
ip_address_prefix = "10.0.12.0/24"
public_napt_enabled = true
}`, rInt, rInt)
}
func testAccOPCCheckIPNetworkExists(state *OPCResourceState) error {
name := state.Attributes["name"]
input := &compute.GetIPNetworkInput{
Name: name,
}
if _, err := state.Client.IPNetworks().GetIPNetwork(input); err != nil {
return fmt.Errorf("Error retrieving state of IP Network '%s': %v", name, err)
}
return nil
}
func testAccOPCCheckIPNetworkDestroyed(state *OPCResourceState) error {
name := state.Attributes["name"]
input := &compute.GetIPNetworkInput{
Name: name,
}
if info, _ := state.Client.IPNetworks().GetIPNetwork(input); info != nil {
return fmt.Errorf("IP Network '%s' still exists: %+v", name, info)
}
return nil
}

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