From e97900aef28a4d3b2f3df30ee9558dfc8f730503 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Mon, 27 Mar 2017 09:32:31 -0400 Subject: [PATCH 01/91] provider/aws: Update calling_identity Updates `aws_caller_identity` data source to actually include the correct attributes from the `GetCallerIdentity` API function. ``` $ make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSCallerIdentity_basic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/27 09:26:13 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSCallerIdentity_basic -timeout 120m === RUN TestAccAWSCallerIdentity_basic --- PASS: TestAccAWSCallerIdentity_basic (12.74s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 12.767s ``` --- .../aws/data_source_aws_caller_identity.go | 27 +++++++++++++++---- .../data_source_aws_caller_identity_test.go | 8 ++++++ .../aws/d/caller_identity.html.markdown | 16 ++++++++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/builtin/providers/aws/data_source_aws_caller_identity.go b/builtin/providers/aws/data_source_aws_caller_identity.go index 05c03864d..95313cea8 100644 --- a/builtin/providers/aws/data_source_aws_caller_identity.go +++ b/builtin/providers/aws/data_source_aws_caller_identity.go @@ -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 } diff --git a/builtin/providers/aws/data_source_aws_caller_identity_test.go b/builtin/providers/aws/data_source_aws_caller_identity_test.go index f874d7da9..100bb4db8 100644 --- a/builtin/providers/aws/data_source_aws_caller_identity_test.go +++ b/builtin/providers/aws/data_source_aws_caller_identity_test.go @@ -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 } } diff --git a/website/source/docs/providers/aws/d/caller_identity.html.markdown b/website/source/docs/providers/aws/d/caller_identity.html.markdown index ea0b1afcc..8f4b9974a 100644 --- a/website/source/docs/providers/aws/d/caller_identity.html.markdown +++ b/website/source/docs/providers/aws/d/caller_identity.html.markdown @@ -9,8 +9,8 @@ description: |- # aws\_caller\_identity -Use this data source to get the access to the effective Account ID in -which Terraform is working. +Use this data source to get the access to the effective Account ID, User ID, and ARN in +which Terraform is authorized. ~> **NOTE on `aws_caller_identity`:** - an Account ID is only available if `skip_requesting_account_id` is not set on the AWS provider. In such @@ -24,6 +24,14 @@ data "aws_caller_identity" "current" {} output "account_id" { value = "${data.aws_caller_identity.current.account_id}" } + +output "caller_arn" { + value = "${data.aws_caller_identity.current.arn}" +} + +output "caller_user" { + value = "${data.aws_caller_identity.current.user_id}" +} ``` ## Argument Reference @@ -32,4 +40,6 @@ There are no arguments available for this data source. ## Attributes Reference -`account_id` is set to the ID of the AWS account. +`account_id` - The AWS Account ID number of the account that owns or contains the calling entity. +`arn` - The AWS ARN associated with the calling entity. +`user_id` - The unique identifier of the calling entity. From 1c40518e8035f2e73f1eae041e3ae1a1c058f014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serta=C3=A7=20=C3=96zercan?= Date: Mon, 27 Mar 2017 14:00:29 -0700 Subject: [PATCH 02/91] provider/azurerm: Update vault_certificates instructions for clarity --- .../azurerm/r/virtual_machine.html.markdown | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown b/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown index e6cc76524..9bfbff12e 100644 --- a/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown +++ b/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown @@ -304,11 +304,20 @@ For more information on the different example configurations, please check out t `os_profile_secrets` supports the following: * `source_vault_id` - (Required) Specifies the key vault to use. -* `vault_certificates` - (Required, on windows machines) A collection of Vault Certificates as documented below +* `vault_certificates` - (Required) A collection of Vault Certificates as documented below `vault_certificates` support the following: -* `certificate_url` - (Required) It is the Base64 encoding of a JSON Object that which is encoded in UTF-8 of which the contents need to be `data`, `dataType` and `password`. +* `certificate_url` - (Required) Specifies the URI of the key vault secrets in the format of `https:///secrets//`. Stored secret is the Base64 encoding of a JSON Object that which is encoded in UTF-8 of which the contents need to be + +``` +{ + "data":"", + "dataType":"pfx", + "password":"" +} +``` + * `certificate_store` - (Required, on windows machines) Specifies the certificate store on the Virtual Machine where the certificate should be added to. ## Attributes Reference From 4619897f920d80acc72802b26bc28ea61c195857 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 28 Mar 2017 17:10:34 -0500 Subject: [PATCH 03/91] provider/aws: Don't set DBName on `aws_db_instance` from snapshot It turns out if you're trying to write a config to conditionally restore an instance from a snapshot, you end up painted into a corner with the combination of `snapshot_identifier` and `name`. You need `name` to be specified for DBs you're creating, but when `snapshot_identifier` is populated you need it to be blank or else the AWS API throws an error. The logical next step is to drop a ternary in: ```tf resource "aws_db_instance" "foo" { # ... snapshot_identifier = "${var.snap}" name = "${var.snap != "" ? "" : var.dbname}" } ``` **BUT** the above config will _replace_ the DB on subsequent runs as the config basically has `name = ""` which will trigger a ForceNew diff once the `name` is populated from the snapshot restore. **SO** we can get a workable solution by actively avoiding populating DBName when we're using one of the engines the docs explicitly mention. It does not look like there are any tests covering `snapshot_identifer`, so I'll subject this to some manual tests and follow up with some results. --- builtin/providers/aws/resource_aws_db_instance.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index e944489ab..192ccab97 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -407,7 +407,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 { From 76dca009e0f048ea35e86f3737e7f63503378789 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 08:34:45 -0700 Subject: [PATCH 04/91] Allow escaped interpolation-like sequences in variable defaults The variable validator assumes that any AST node it gets from an interpolation walk is an indicator of an interpolation. Unfortunately, back in f223be15 we changed the interpolation walker to emit a LiteralNode as a way to signal that the result is a literal but not identical to the input due to escapes. The existence of this issue suggests a bit of a design smell in that the interpolation walker interface at first glance appears to skip over all literals, but it actually emits them in this one situation. In the long run we should perhaps think about whether the abstraction is right here, but this is a shallow, tactical change that fixes #13001. --- command/test-fixtures/validate-valid/main.tf | 5 +++++ config/config.go | 11 +++++++++-- config/config_test.go | 7 +++++++ .../validate-var-default-interpolate-escaped/main.tf | 5 +++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 config/test-fixtures/validate-var-default-interpolate-escaped/main.tf diff --git a/command/test-fixtures/validate-valid/main.tf b/command/test-fixtures/validate-valid/main.tf index fd9da13e0..2dcb1eccd 100644 --- a/command/test-fixtures/validate-valid/main.tf +++ b/command/test-fixtures/validate-valid/main.tf @@ -1,3 +1,8 @@ +variable "var_with_escaped_interp" { + # This is here because in the past it failed. See Github #13001 + default = "foo-$${bar.baz}" +} + resource "test_instance" "foo" { ami = "bar" diff --git a/config/config.go b/config/config.go index bf064e57a..f944cadd3 100644 --- a/config/config.go +++ b/config/config.go @@ -285,8 +285,15 @@ func (c *Config) Validate() error { } interp := false - fn := func(ast.Node) (interface{}, error) { - interp = true + fn := func(n ast.Node) (interface{}, error) { + // LiteralNode is a literal string (outside of a ${ ... } sequence). + // interpolationWalker skips most of these. but in particular it + // visits those that have escaped sequences (like $${foo}) as a + // signal that *some* processing is required on this string. For + // our purposes here though, this is fine and not an interpolation. + if _, ok := n.(*ast.LiteralNode); !ok { + interp = true + } return "", nil } diff --git a/config/config_test.go b/config/config_test.go index b391295c8..68a93d9b6 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -572,6 +572,13 @@ func TestConfigValidate_varDefaultInterpolate(t *testing.T) { } } +func TestConfigValidate_varDefaultInterpolateEscaped(t *testing.T) { + c := testConfig(t, "validate-var-default-interpolate-escaped") + if err := c.Validate(); err != nil { + t.Fatalf("should be valid, but got err: %s", err) + } +} + func TestConfigValidate_varDup(t *testing.T) { c := testConfig(t, "validate-var-dup") if err := c.Validate(); err == nil { diff --git a/config/test-fixtures/validate-var-default-interpolate-escaped/main.tf b/config/test-fixtures/validate-var-default-interpolate-escaped/main.tf new file mode 100644 index 000000000..da2758f6a --- /dev/null +++ b/config/test-fixtures/validate-var-default-interpolate-escaped/main.tf @@ -0,0 +1,5 @@ +variable "foo" { + # This should be considered valid since the sequence is escaped and is + # thus not actually an interpolation. + default = "foo bar $${aws_instance.foo.bar}" +} From 4ed0f769222b70c6b8458fe0800aa98d9ed4629b Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:28:12 -0700 Subject: [PATCH 05/91] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aed6fcf2..6dfc29f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ BUG FIXES: * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] - + * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] ## 0.9.2 (March 28, 2017) From 51081678a45fdb4276f0a914c848d6a661879ab8 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:35:09 -0700 Subject: [PATCH 06/91] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dfc29f03..3a7f57a38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ IMPROVEMENTS: 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] - * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] ## 0.9.2 (March 28, 2017) From 261f5f8a76ac6b912b44cc943f966c2ae2534ba6 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:06:17 +0100 Subject: [PATCH 07/91] provider/aws: Increase timeout for AMI registration (#13159) --- builtin/providers/aws/resource_aws_ami.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_ami.go b/builtin/providers/aws/resource_aws_ami.go index 6e4ee1522..95b29678c 100644 --- a/builtin/providers/aws/resource_aws_ami.go +++ b/builtin/providers/aws/resource_aws_ami.go @@ -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 From 4b8e235510eecc9ba6f143252b99aa2412d888fb Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:06:51 +0100 Subject: [PATCH 08/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a7f57a38..c3c1358cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 75979afcb729ec5d42e906f62930f352f7f1170c Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:07:11 +0100 Subject: [PATCH 09/91] provider/aws: Fix ES Domain acceptance tests (#13160) --- .../aws/resource_aws_elasticsearch_domain.go | 1 + ...rce_aws_elasticsearch_domain_policy_test.go | 7 +++++++ .../resource_aws_elasticsearch_domain_test.go | 18 +++++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_elasticsearch_domain.go b/builtin/providers/aws/resource_aws_elasticsearch_domain.go index 7a8753efd..c931b119e 100644 --- a/builtin/providers/aws/resource_aws_elasticsearch_domain.go +++ b/builtin/providers/aws/resource_aws_elasticsearch_domain.go @@ -83,6 +83,7 @@ func resourceAwsElasticSearchDomain() *schema.Resource { "volume_type": { Type: schema.TypeString, Optional: true, + Computed: true, }, }, }, diff --git a/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go b/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go index 76f650f6c..5efd3eb99 100644 --- a/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go +++ b/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go @@ -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" { diff --git a/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go b/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go index ed96dcd0d..e5bf282de 100644 --- a/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go +++ b/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go @@ -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) From 0c561535e9a4f4db354c99634e3e1680c46b212f Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:09:37 +0100 Subject: [PATCH 10/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3c1358cd..12f5d2573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ 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: `volume_type` of `aws_elasticsearch_domain.0.ebs_options` marked as `Computed` which prevents spurious diffs [GH-13160] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From dc5b1d936ba09152b5dfe237f0c258bdb10fa2f0 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:10:38 +0100 Subject: [PATCH 11/91] provider/aws: Increase timeouts for ELB (#13161) --- builtin/providers/aws/resource_aws_elb.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 2379ea49a..3878c9611 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -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 { From 6cae2a5eb1264534ef7843c05d6101838721e328 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:11:06 +0100 Subject: [PATCH 12/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f5d2573..1273e0423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] From 0d6d2f1cb489255080072af320e38041a3b21f8b Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:48:58 -0700 Subject: [PATCH 13/91] website: community people pics consistently spaced Most of the bios have five lines of text, so that's the driver for the layout except for @grubernaut's and @mbfrahry's where there's only three lines. This makes the pictures following the short bios look misaligned. This quick fix just always leaves enough room for five lines of bio text, ensuring that the photos appear at a consistent vertical rhythm. It's annoying to hard-code a particular value here, since this value won't survive e.g. a change to the typesetting, but a more involved fix here (using flexbox layout, or something else complicated) doesn't seem warranted. --- website/source/assets/stylesheets/_community.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/website/source/assets/stylesheets/_community.scss b/website/source/assets/stylesheets/_community.scss index de945fa38..ff8c3412e 100644 --- a/website/source/assets/stylesheets/_community.scss +++ b/website/source/assets/stylesheets/_community.scss @@ -3,6 +3,7 @@ .person { margin-bottom: 40px; + min-height: 165px; // enough space for five lines of bio text h3 { text-transform: none; From 80a1539bca01293740d5760ce2cb4dd56c43ca62 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:57:21 -0700 Subject: [PATCH 14/91] Add apparentlymart to the community page. --- website/source/community.html.erb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/website/source/community.html.erb b/website/source/community.html.erb index 556eed0d0..ca475cdd7 100644 --- a/website/source/community.html.erb +++ b/website/source/community.html.erb @@ -125,5 +125,16 @@ disappear from this list as contributors come and go. +
+ +
+

Martin Atkins (@apparentlymart)

+

+ Martin Atkins is a community contributor turned HashiCorp Engineer working + on Terraform with a focus on the core. +

+
+
+
From 3456f12f6a2b68216377fd276256b611a8b2923a Mon Sep 17 00:00:00 2001 From: joelcollin Date: Wed, 29 Mar 2017 15:06:26 -0400 Subject: [PATCH 15/91] Fixed typo in subscription (was subscripion) (#13172) --- .../docs/providers/google/r/pubsub_subscription.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/google/r/pubsub_subscription.html.markdown b/website/source/docs/providers/google/r/pubsub_subscription.html.markdown index 66715dab7..d5b7aed18 100644 --- a/website/source/docs/providers/google/r/pubsub_subscription.html.markdown +++ b/website/source/docs/providers/google/r/pubsub_subscription.html.markdown @@ -6,7 +6,7 @@ description: |- Creates a subscription in Google's pubsub queueing system --- -# google\_pubsub\_subscripion +# google\_pubsub\_subscription Creates a subscription in Google's pubsub queueing system. For more information see [the official documentation](https://cloud.google.com/pubsub/docs) and From b49e4035733121eaff6ef5cd44a374fc41358559 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 29 Mar 2017 14:18:12 -0500 Subject: [PATCH 16/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1273e0423..5fda0ede4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ BUG FIXES: * 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From ff2d753062b9a9d40dafbdb02839c2aff0701dfc Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 12:50:20 -0400 Subject: [PATCH 17/91] add Rehash to terraform.BackendState This method mirrors that of config.Backend, so we can compare the configration of a backend read from a config vs that of a backend read from a state. This will prevent init from reinitializing when using `-backend-config` options that match the existing state. --- command/init_test.go | 58 +++++++++++++++++++ command/meta_backend.go | 18 +++++- .../test-fixtures/init-backend-empty/main.tf | 4 ++ config/config_terraform.go | 2 +- terraform/state.go | 27 +++++++++ 5 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 command/test-fixtures/init-backend-empty/main.tf diff --git a/command/init_test.go b/command/init_test.go index dee54495d..ede29cfc3 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -406,6 +406,64 @@ func TestInit_copyBackendDst(t *testing.T) { } } +func TestInit_backendReinitWithExtra(t *testing.T) { + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend-empty"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + m := testMetaBackend(t, nil) + opts := &BackendOpts{ + ConfigExtra: map[string]interface{}{"path": "hello"}, + Init: true, + } + + b, err := m.backendConfig(opts) + if err != nil { + t.Fatal(err) + } + + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{"-backend-config", "path=hello"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + // Read our saved backend config and verify we have our settings + state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + if v := state.Backend.Config["path"]; v != "hello" { + t.Fatalf("bad: %#v", v) + } + + if state.Backend.Hash != b.Hash { + t.Fatal("mismatched state and config backend hashes") + } + + if state.Backend.Rehash() != b.Rehash() { + t.Fatal("mismatched state and config re-hashes") + } + + // init again and make sure nothing changes + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + state = testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + if v := state.Backend.Config["path"]; v != "hello" { + t.Fatalf("bad: %#v", v) + } + + if state.Backend.Hash != b.Hash { + t.Fatal("mismatched state and config backend hashes") + } +} + /* func TestInit_remoteState(t *testing.T) { tmp, cwd := testCwd(t) diff --git a/command/meta_backend.go b/command/meta_backend.go index b30659dc0..8cf639044 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -415,8 +415,16 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) { case c != nil && s.Remote.Empty() && !s.Backend.Empty(): // If our configuration is the same, then we're just initializing // a previously configured remote backend. - if !s.Backend.Empty() && s.Backend.Hash == cHash { - return m.backend_C_r_S_unchanged(c, sMgr) + if !s.Backend.Empty() { + hash := s.Backend.Hash + // on init we need an updated hash containing any extra options + // that were added after merging. + if opts.Init { + hash = s.Backend.Rehash() + } + if hash == cHash { + return m.backend_C_r_S_unchanged(c, sMgr) + } } if !opts.Init { @@ -451,7 +459,11 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) { case c != nil && !s.Remote.Empty() && !s.Backend.Empty(): // If the hashes are the same, we have a legacy remote state with // an unchanged stored backend state. - if s.Backend.Hash == cHash { + hash := s.Backend.Hash + if opts.Init { + hash = s.Backend.Rehash() + } + if hash == cHash { if !opts.Init { initReason := fmt.Sprintf( "Legacy remote state found with configured backend %q", diff --git a/command/test-fixtures/init-backend-empty/main.tf b/command/test-fixtures/init-backend-empty/main.tf new file mode 100644 index 000000000..7f62e0e19 --- /dev/null +++ b/command/test-fixtures/init-backend-empty/main.tf @@ -0,0 +1,4 @@ +terraform { + backend "local" { + } +} diff --git a/config/config_terraform.go b/config/config_terraform.go index a547cc798..8535c9648 100644 --- a/config/config_terraform.go +++ b/config/config_terraform.go @@ -72,7 +72,7 @@ type Backend struct { Hash uint64 } -// Hash returns a unique content hash for this backend's configuration +// Rehash returns a unique content hash for this backend's configuration // as a uint64 value. func (b *Backend) Rehash() uint64 { // If we have no backend, the value is zero diff --git a/terraform/state.go b/terraform/state.go index 4e5aa713f..d5adefca1 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/config" "github.com/mitchellh/copystructure" + "github.com/mitchellh/hashstructure" "github.com/satori/go.uuid" ) @@ -801,6 +802,32 @@ func (s *BackendState) Empty() bool { return s == nil || s.Type == "" } +// Rehash returns a unique content hash for this backend's configuration +// as a uint64 value. +// The Hash stored in the backend state needs to match the config itself, but +// we need to compare the backend config after it has been combined with all +// options. +// This function must match the implementation used by config.Backend. +func (s *BackendState) Rehash() uint64 { + if s == nil { + return 0 + } + + // Use hashstructure to hash only our type with the config. + code, err := hashstructure.Hash(map[string]interface{}{ + "type": s.Type, + "config": s.Config, + }, nil) + + // This should never happen since we have just some basic primitives + // so panic if there is an error. + if err != nil { + panic(err) + } + + return code +} + // RemoteState is used to track the information about a remote // state store that we push/pull state to. type RemoteState struct { From c891ab50b7bd1c2f282a97d324a6ef4ea63a0cd3 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 15:51:24 -0400 Subject: [PATCH 18/91] detect when backend.Hash needs update It's possible to not change the backend config, but require updating the stored backend state by moving init options from the config file to the `-backend-config` flag. If the config is the same, but the hash doesn't match, update the stored state. --- command/init_test.go | 44 +++++++++++++++++++++++++++++++++++++++++ command/meta_backend.go | 10 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/command/init_test.go b/command/init_test.go index ede29cfc3..a0a8c32a0 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -464,6 +464,50 @@ func TestInit_backendReinitWithExtra(t *testing.T) { } } +// move option from config to -backend-config args +func TestInit_backendReinitConfigToExtra(t *testing.T) { + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + if code := c.Run([]string{"-input=false"}); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + // Read our saved backend config and verify we have our settings + state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + if v := state.Backend.Config["path"]; v != "foo" { + t.Fatalf("bad: %#v", v) + } + + backendHash := state.Backend.Hash + + // init again but remove the path option from the config + cfg := "terraform {\n backend \"local\" {}\n}\n" + if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil { + t.Fatal(err) + } + + args := []string{"-input=false", "-backend-config=path=foo"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + state = testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + + if state.Backend.Hash == backendHash { + t.Fatal("state.Backend.Hash was not updated") + } +} + /* func TestInit_remoteState(t *testing.T) { tmp, cwd := testCwd(t) diff --git a/command/meta_backend.go b/command/meta_backend.go index 8cf639044..c50214116 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -1158,6 +1158,16 @@ func (m *Meta) backend_C_r_S_unchanged( c *config.Backend, sMgr state.State) (backend.Backend, error) { s := sMgr.State() + // it's possible for a backend to be unchanged, and the config itself to + // have changed by moving a paramter from the config to `-backend-config` + // In this case we only need to update the Hash. + if c != nil && s.Backend.Hash != c.Hash { + s.Backend.Hash = c.Hash + if err := sMgr.WriteState(s); err != nil { + return nil, fmt.Errorf(errBackendWriteSaved, err) + } + } + // Create the config. We do this from the backend state since this // has the complete configuration data whereas the config itself // may require input. From da1905d5e1a2c197f9ceceb47e69438709573362 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 13:51:01 -0700 Subject: [PATCH 19/91] Remove .gitattributes, treating all files as binary This was added earlier in an attempt to tolerate CRLF and convert CRLF line endings on Windows, but it causes issues where vendored files (which could be using either LF or CRLF depending on the original author's preference) get permanent diffs when inconsistent with the platform's preference. The goal of this change, therefore, is to treat all of the files as binary, with the standard that all of Terraform's own files will use Unix-style LF endings and the vendor stuff will just be verbatim, byte-for-byte copies of what's upstream. This will apparently cause some difficulty for people hacking on Terraform on Windows machines, because gofmt on Windows reportedly wants to convert all files to CRLF endings. Unfortunately we're forced to compromise here and treat development on Windows as an edge case in order to avoid the weirdness with inconsistent endings in the vendor tree. --- .gitattributes | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 6eb3a078e..000000000 --- a/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -# Set the default behavior, in case people don't have core.autocrlf set. -* text=auto - -*.go eol=lf From 7d23e1ef2090d1190ff514fdb0515111eded9cb5 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 17:50:55 -0400 Subject: [PATCH 20/91] add equivalent tests to meta_backend_test --- command/meta_backend_test.go | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index 8c1fe2d66..143759f98 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -3217,6 +3217,110 @@ func TestMetaBackend_planLegacy(t *testing.T) { } } +// init a backend using -backend-config options multiple times +func TestMetaBackend_configureWithExtra(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend-empty"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + extras := map[string]interface{}{"path": "hello"} + m := testMetaBackend(t, nil) + opts := &BackendOpts{ + ConfigExtra: extras, + Init: true, + } + + backendCfg, err := m.backendConfig(opts) + if err != nil { + t.Fatal(err) + } + + // init the backend + _, err = m.Backend(&BackendOpts{ + ConfigExtra: extras, + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // Check the state + s := testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + if s.Backend.Hash != backendCfg.Hash { + t.Fatal("mismatched state and config backend hashes") + } + if s.Backend.Rehash() == s.Backend.Hash { + t.Fatal("saved hash should not match actual hash") + } + if s.Backend.Rehash() != backendCfg.Rehash() { + t.Fatal("mismatched state and config re-hashes") + } + + // init the backend again with the same options + m = testMetaBackend(t, nil) + _, err = m.Backend(&BackendOpts{ + ConfigExtra: extras, + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // Check the state + s = testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + if s.Backend.Hash != backendCfg.Hash { + t.Fatal("mismatched state and config backend hashes") + } +} + +// move options from config to -backend-config +func TestMetaBackend_configToExtra(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // init the backend + m := testMetaBackend(t, nil) + _, err := m.Backend(&BackendOpts{ + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // Check the state + s := testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + backendHash := s.Backend.Hash + + // init again but remove the path option from the config + cfg := "terraform {\n backend \"local\" {}\n}\n" + if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil { + t.Fatal(err) + } + + // init the backend again with the options + extras := map[string]interface{}{"path": "hello"} + m = testMetaBackend(t, nil) + m.forceInitCopy = true + _, err = m.Backend(&BackendOpts{ + ConfigExtra: extras, + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + s = testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + + if s.Backend.Hash == backendHash { + t.Fatal("state.Backend.Hash was not updated") + } +} + func testMetaBackend(t *testing.T, args []string) *Meta { var m Meta m.Ui = new(cli.MockUi) From c55a5082f5117709d7fddf090fdd80ebc4e9f185 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 18:01:03 -0400 Subject: [PATCH 21/91] delegate BackendState.Rehash to config.Backend --- terraform/state.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/terraform/state.go b/terraform/state.go index d5adefca1..905ec3dcb 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/config" "github.com/mitchellh/copystructure" - "github.com/mitchellh/hashstructure" "github.com/satori/go.uuid" ) @@ -813,19 +812,14 @@ func (s *BackendState) Rehash() uint64 { return 0 } - // Use hashstructure to hash only our type with the config. - code, err := hashstructure.Hash(map[string]interface{}{ - "type": s.Type, - "config": s.Config, - }, nil) - - // This should never happen since we have just some basic primitives - // so panic if there is an error. - if err != nil { - panic(err) + cfg := config.Backend{ + Type: s.Type, + RawConfig: &config.RawConfig{ + Raw: s.Config, + }, } - return code + return cfg.Rehash() } // RemoteState is used to track the information about a remote From 50023e9a60aac7324f9e28fc1aa79a2415a4ca78 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 16:45:25 -0400 Subject: [PATCH 22/91] honor `input=false` in state migration return an error when confirming a copy if -input=false --- command/init_test.go | 26 ++++++++++++++++++++++++++ command/meta.go | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/command/init_test.go b/command/init_test.go index a0a8c32a0..eea5797c1 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -508,6 +508,32 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) { } } +// make sure inputFalse stops execution on migrate +func TestInit_inputFalse(t *testing.T) { + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{"-input=false", "-backend-config=path=foo"} + if code := c.Run([]string{"-input=false"}); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter) + } + + args = []string{"-input=false", "-backend-config=path=bar"} + if code := c.Run(args); code == 0 { + t.Fatal("init should have failed", ui.OutputWriter) + } +} + /* func TestInit_remoteState(t *testing.T) { tmp, cwd := testCwd(t) diff --git a/command/meta.go b/command/meta.go index 0dd4c7884..daf949a29 100644 --- a/command/meta.go +++ b/command/meta.go @@ -3,6 +3,7 @@ package command import ( "bufio" "bytes" + "errors" "flag" "fmt" "io" @@ -341,6 +342,9 @@ func (m *Meta) uiHook() *UiHook { // confirm asks a yes/no confirmation. func (m *Meta) confirm(opts *terraform.InputOpts) (bool, error) { + if !m.input { + return false, errors.New("input disabled") + } for { v, err := m.UIInput().Input(opts) if err != nil { From 11fa03cfb664e9b8667bd61aa80bfc40af96f3d6 Mon Sep 17 00:00:00 2001 From: Brian Hahn Date: Wed, 29 Mar 2017 23:03:08 -0700 Subject: [PATCH 23/91] fix docs typo (#13183) --- website/source/docs/backends/legacy-0-8.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/backends/legacy-0-8.html.md b/website/source/docs/backends/legacy-0-8.html.md index c3197d23c..a5feee405 100644 --- a/website/source/docs/backends/legacy-0-8.html.md +++ b/website/source/docs/backends/legacy-0-8.html.md @@ -124,7 +124,7 @@ You should be able to very easily migrate `terraform remote config` scripting to the new `terraform init` command. The new `terraform init` command takes a `-backend-config` flag which is -eitheran HCL file or a string in the format of `key=value`. This configuration +either an HCL file or a string in the format of `key=value`. This configuration is merged with the backend configuration in your Terraform files. This lets you keep secrets out of your actual configuration. We call this "partial configuration" and you can learn more in the From 1dca12201ab744259b49c78aa9caa02c0e898822 Mon Sep 17 00:00:00 2001 From: Ian Morgan Date: Thu, 30 Mar 2017 01:12:15 -0700 Subject: [PATCH 24/91] fix error message in route53 data source (#13174) --- builtin/providers/aws/data_source_aws_route53_zone.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/data_source_aws_route53_zone.go b/builtin/providers/aws/data_source_aws_route53_zone.go index a4df2c754..b3de4eed4 100644 --- a/builtin/providers/aws/data_source_aws_route53_zone.go +++ b/builtin/providers/aws/data_source_aws_route53_zone.go @@ -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 } From c2b657a03977287018aad1ccfe8b2b4b6ee6f9a8 Mon Sep 17 00:00:00 2001 From: Marc Rooding Date: Thu, 30 Mar 2017 10:24:40 +0200 Subject: [PATCH 25/91] kubernetes: Add secret resource (#12960) --- builtin/providers/kubernetes/provider.go | 1 + .../kubernetes/resource_kubernetes_secret.go | 159 +++++++++ .../resource_kubernetes_secret_test.go | 320 ++++++++++++++++++ builtin/providers/kubernetes/structures.go | 18 + .../kubernetes/r/secret.html.markdown | 69 ++++ website/source/layouts/kubernetes.erb | 3 + 6 files changed, 570 insertions(+) create mode 100644 builtin/providers/kubernetes/resource_kubernetes_secret.go create mode 100644 builtin/providers/kubernetes/resource_kubernetes_secret_test.go create mode 100644 website/source/docs/providers/kubernetes/r/secret.html.markdown diff --git a/builtin/providers/kubernetes/provider.go b/builtin/providers/kubernetes/provider.go index 9d0d23cc3..861519c6a 100644 --- a/builtin/providers/kubernetes/provider.go +++ b/builtin/providers/kubernetes/provider.go @@ -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, } diff --git a/builtin/providers/kubernetes/resource_kubernetes_secret.go b/builtin/providers/kubernetes/resource_kubernetes_secret.go new file mode 100644 index 000000000..0fe9a71ba --- /dev/null +++ b/builtin/providers/kubernetes/resource_kubernetes_secret.go @@ -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 +} diff --git a/builtin/providers/kubernetes/resource_kubernetes_secret_test.go b/builtin/providers/kubernetes/resource_kubernetes_secret_test.go new file mode 100644 index 000000000..0e9ef3123 --- /dev/null +++ b/builtin/providers/kubernetes/resource_kubernetes_secret_test.go @@ -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) +} diff --git a/builtin/providers/kubernetes/structures.go b/builtin/providers/kubernetes/structures.go index 58bc49030..878890d56 100644 --- a/builtin/providers/kubernetes/structures.go +++ b/builtin/providers/kubernetes/structures.go @@ -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 +} diff --git a/website/source/docs/providers/kubernetes/r/secret.html.markdown b/website/source/docs/providers/kubernetes/r/secret.html.markdown new file mode 100644 index 000000000..856b04d89 --- /dev/null +++ b/website/source/docs/providers/kubernetes/r/secret.html.markdown @@ -0,0 +1,69 @@ +--- +layout: "kubernetes" +page_title: "Kubernetes: kubernetes_secret" +sidebar_current: "docs-kubernetes-resource-secret" +description: |- + The resource provides mechanisms to inject containers with sensitive information while keeping containers agnostic of Kubernetes. +--- + +# kubernetes_secret + +The resource provides mechanisms to inject containers with sensitive information, such as passwords, while keeping containers agnostic of Kubernetes. +Secrets can be used to store sensitive information either as individual properties or coarse-grained entries like entire files or JSON blobs. +The resource will by default create a secret which is available to any pod in the specified (or default) namespace. + +~> Read more about security properties and risks involved with using Kubernetes secrets: https://kubernetes.io/docs/user-guide/secrets/#security-properties + +~> **Note:** All arguments including the secret data will be stored in the raw state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). + +## Example Usage + +``` +resource "kubernetes_secret" "example" { + metadata { + name = "basic-auth" + } + + data { + username = "admin" + password = "P4ssw0rd" + } + + type = "kubernetes.io/basic-auth" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `data` - (Optional) A map of the secret data. +* `metadata` - (Required) Standard secret's metadata. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata +* `type` - (Optional) The secret type. Defaults to `Opaque`. More info: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/secrets.md#proposed-design + +## Nested Blocks + +### `metadata` + +#### Arguments + +* `annotations` - (Optional) An unstructured key value map stored with the secret that may be used to store arbitrary metadata. More info: http://kubernetes.io/docs/user-guide/annotations +* `generate_name` - (Optional) Prefix, used by the server, to generate a unique name ONLY IF the `name` field has not been provided. This value will also be combined with a unique suffix. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#idempotency +* `labels` - (Optional) Map of string keys and values that can be used to organize and categorize (scope and select) the secret. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels +* `name` - (Optional) Name of the secret, must be unique. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names +* `namespace` - (Optional) Namespace defines the space within which name of the secret must be unique. + +#### Attributes + +* `generation` - A sequence number representing a specific generation of the desired state. +* `resource_version` - An opaque value that represents the internal version of this secret that can be used by clients to determine when secret has changed. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency +* `self_link` - A URL representing this secret. +* `uid` - The unique in time and space value for this secret. More info: http://kubernetes.io/docs/user-guide/identifiers#uids + +## Import + +Secret can be imported using its name, e.g. + +``` +$ terraform import kubernetes_secret.example my-secret +``` diff --git a/website/source/layouts/kubernetes.erb b/website/source/layouts/kubernetes.erb index 147bccbf4..4f80efec7 100644 --- a/website/source/layouts/kubernetes.erb +++ b/website/source/layouts/kubernetes.erb @@ -19,6 +19,9 @@ > kubernetes_namespace + > + kubernetes_secret + From b12e7782c98453cc6c2fbdac369a83521bc952b1 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 30 Mar 2017 09:26:50 +0100 Subject: [PATCH 26/91] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fda0ede4..2260e704b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.9.3 (unreleased) +FEATURES: + + * **New Resource:** `kubernetes_secret` [GH-12960] + IMPROVEMENTS: * config: New interpolation functions `basename` and `dirname`, for file path manipulation [GH-13080] From 204789f07c129026009a59d6ad32413175b3b441 Mon Sep 17 00:00:00 2001 From: Axel FAUVEL Date: Thu, 30 Mar 2017 10:34:55 +0200 Subject: [PATCH 27/91] fix cloudstack_disk documentation --- .../docs/providers/cloudstack/r/disk.html.markdown | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/source/docs/providers/cloudstack/r/disk.html.markdown b/website/source/docs/providers/cloudstack/r/disk.html.markdown index f9d5af30f..5232394dd 100644 --- a/website/source/docs/providers/cloudstack/r/disk.html.markdown +++ b/website/source/docs/providers/cloudstack/r/disk.html.markdown @@ -15,12 +15,12 @@ a virtual machine if the optional parameters are configured. ``` resource "cloudstack_disk" "default" { - name = "test-disk" - attach = "true" - disk_offering = "custom" - size = 50 - virtual_machine = "server-1" - zone = "zone-1" + name = "test-disk" + attach = "true" + disk_offering = "custom" + size = 50 + virtual_machine_id = "server-1" + zone = "zone-1" } ``` From d24dc532e54f7a2198623ac3f1ef9eae152815fb Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 30 Mar 2017 15:54:01 +0300 Subject: [PATCH 28/91] provider/aws: Documentation changes on ALB to remove ELB refs Fixes: #13179 --- website/source/docs/providers/aws/r/alb.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/aws/r/alb.html.markdown b/website/source/docs/providers/aws/r/alb.html.markdown index 18513be1c..d91706eca 100644 --- a/website/source/docs/providers/aws/r/alb.html.markdown +++ b/website/source/docs/providers/aws/r/alb.html.markdown @@ -46,9 +46,9 @@ must contain only alphanumeric characters or hyphens, and must not begin or end Terraform will autogenerate a name beginning with `tf-lb`. * `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `internal` - (Optional) If true, the ALB will be internal. -* `security_groups` - (Optional) A list of security group IDs to assign to the ELB. +* `security_groups` - (Optional) A list of security group IDs to assign to the ALB. * `access_logs` - (Optional) An Access Logs block. Access Logs documented below. -* `subnets` - (Required) A list of subnet IDs to attach to the ELB. +* `subnets` - (Required) A list of subnet IDs to attach to the ALB. * `idle_timeout` - (Optional) The time in seconds that the connection is allowed to be idle. Default: 60. * `enable_deletion_protection` - (Optional) If true, deletion of the load balancer will be disabled via the AWS API. This will prevent Terraform from deleting the load balancer. Defaults to `false`. From 9ed8bb2498186008101933af5ecab0627f0046d1 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Thu, 30 Mar 2017 16:20:42 +0300 Subject: [PATCH 29/91] provider/aws: Support the ability to enable / disable ipv6 support in (#12527) VPC ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSVpc_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/28 15:49:20 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSVpc_ -timeout 120m === RUN TestAccAWSVpc_importBasic --- PASS: TestAccAWSVpc_importBasic (102.01s) === RUN TestAccAWSVpc_basic --- PASS: TestAccAWSVpc_basic (63.75s) === RUN TestAccAWSVpc_enableIpv6 --- PASS: TestAccAWSVpc_enableIpv6 (231.41s) === RUN TestAccAWSVpc_dedicatedTenancy --- PASS: TestAccAWSVpc_dedicatedTenancy (66.65s) === RUN TestAccAWSVpc_tags --- PASS: TestAccAWSVpc_tags (130.26s) === RUN TestAccAWSVpc_update --- PASS: TestAccAWSVpc_update (120.21s) === RUN TestAccAWSVpc_bothDnsOptionsSet --- PASS: TestAccAWSVpc_bothDnsOptionsSet (50.10s) === RUN TestAccAWSVpc_DisabledDnsSupport --- PASS: TestAccAWSVpc_DisabledDnsSupport (67.47s) === RUN TestAccAWSVpc_classiclinkOptionSet --- PASS: TestAccAWSVpc_classiclinkOptionSet (64.57s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 896.464s ``` --- builtin/providers/aws/resource_aws_vpc.go | 100 +++++++++++++++++- .../providers/aws/resource_aws_vpc_test.go | 36 ++++++- 2 files changed, 133 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_vpc.go b/builtin/providers/aws/resource_aws_vpc.go index 6807706b6..6a8edca4b 100644 --- a/builtin/providers/aws/resource_aws_vpc.go +++ b/builtin/providers/aws/resource_aws_vpc.go @@ -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"), diff --git a/builtin/providers/aws/resource_aws_vpc_test.go b/builtin/providers/aws/resource_aws_vpc_test.go index 44f672268..ca68bdfe8 100644 --- a/builtin/providers/aws/resource_aws_vpc_test.go +++ b/builtin/providers/aws/resource_aws_vpc_test.go @@ -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" From 5ba7aa82966efeba6d3a768f6086a3e448668594 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Thu, 30 Mar 2017 16:22:30 +0300 Subject: [PATCH 30/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2260e704b..e151c9aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ 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/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] BUG FIXES: From 7d8a6f853311bcdf33fd164239aa291fd2554913 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 30 Mar 2017 14:59:28 +0100 Subject: [PATCH 31/91] provider/aws: Add support for aws_lightsail_static_ip (#13175) --- builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_lightsail_static_ip.go | 98 +++++++++++++ .../resource_aws_lightsail_static_ip_test.go | 138 ++++++++++++++++++ .../aws/r/lightsail_static_ip.html.markdown | 35 +++++ website/source/layouts/aws.erb | 4 + 5 files changed, 276 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_lightsail_static_ip.go create mode 100644 builtin/providers/aws/resource_aws_lightsail_static_ip_test.go create mode 100644 website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 744eb21ad..0a9de8acc 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -337,6 +337,7 @@ func Provider() terraform.ResourceProvider { "aws_lightsail_domain": resourceAwsLightsailDomain(), "aws_lightsail_instance": resourceAwsLightsailInstance(), "aws_lightsail_key_pair": resourceAwsLightsailKeyPair(), + "aws_lightsail_static_ip": resourceAwsLightsailStaticIp(), "aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(), "aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(), "aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(), diff --git a/builtin/providers/aws/resource_aws_lightsail_static_ip.go b/builtin/providers/aws/resource_aws_lightsail_static_ip.go new file mode 100644 index 000000000..1f593ad40 --- /dev/null +++ b/builtin/providers/aws/resource_aws_lightsail_static_ip.go @@ -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 +} diff --git a/builtin/providers/aws/resource_aws_lightsail_static_ip_test.go b/builtin/providers/aws/resource_aws_lightsail_static_ip_test.go new file mode 100644 index 000000000..275a29f20 --- /dev/null +++ b/builtin/providers/aws/resource_aws_lightsail_static_ip_test.go @@ -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) +} diff --git a/website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown b/website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown new file mode 100644 index 000000000..b3617a432 --- /dev/null +++ b/website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "aws" +page_title: "AWS: aws_lightsail_static_ip" +sidebar_current: "docs-aws-resource-lightsail-static-ip" +description: |- + Provides an Lightsail Static IP +--- + +# aws\_lightsail\_static\_ip + +Allocates a static IP address. + +~> **Note:** Lightsail is currently only supported in `us-east-1` region. + +## Example Usage + +``` +resource "aws_lightsail_static_ip" "test" { + name = "example" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name for the allocated static IP + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `arn` - The ARN of the Lightsail static IP +* `ip_address` - The allocated static IP address +* `support_code` - The support code. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 73656110a..312a3535d 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -881,6 +881,10 @@ aws_lightsail_key_pair + > + aws_lightsail_static_ip + + From 7bf9534b2ac961a2cc7b2f06922cb32c8432af86 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 30 Mar 2017 15:01:51 +0100 Subject: [PATCH 32/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e151c9aa6..bcc42c4f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ FEATURES: + * **New Resource:** `aws_lightsail_static_ip` [GH-13175] * **New Resource:** `kubernetes_secret` [GH-12960] IMPROVEMENTS: From 18513bcb8d1b011ef58e3c4ca144b448dd4b7257 Mon Sep 17 00:00:00 2001 From: lmorfitt Date: Thu, 30 Mar 2017 16:35:56 +0100 Subject: [PATCH 33/91] docs bug syntax change rev vs ref in docs. the default branch on hg is default, not master. --- website/source/docs/modules/sources.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/modules/sources.html.markdown b/website/source/docs/modules/sources.html.markdown index 0ab2978bc..67162347f 100644 --- a/website/source/docs/modules/sources.html.markdown +++ b/website/source/docs/modules/sources.html.markdown @@ -157,7 +157,7 @@ URLs for Mercurial repositories support the following query parameters: ``` module "consul" { - source = "hg::http://hashicorp.com/consul.hg?ref=master" + source = "hg::http://hashicorp.com/consul.hg?rev=default" } ``` From fc4cec3c40ac532c358526d8c6400ab1d0c5759f Mon Sep 17 00:00:00 2001 From: mathematician Date: Thu, 30 Mar 2017 11:09:11 -0500 Subject: [PATCH 34/91] Create AWS IAM Role data source, acceptance tests, documentation, and website link --- .../providers/aws/data_source_aws_iam_role.go | 67 +++++++++++++++++++ .../aws/data_source_aws_iam_role_test.go | 59 ++++++++++++++++ builtin/providers/aws/provider.go | 1 + .../providers/aws/d/iam_role.html.markdown | 35 ++++++++++ website/source/layouts/aws.erb | 3 + 5 files changed, 165 insertions(+) create mode 100644 builtin/providers/aws/data_source_aws_iam_role.go create mode 100644 builtin/providers/aws/data_source_aws_iam_role_test.go create mode 100644 website/source/docs/providers/aws/d/iam_role.html.markdown diff --git a/builtin/providers/aws/data_source_aws_iam_role.go b/builtin/providers/aws/data_source_aws_iam_role.go new file mode 100644 index 000000000..f681268b9 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_iam_role.go @@ -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 +} diff --git a/builtin/providers/aws/data_source_aws_iam_role_test.go b/builtin/providers/aws/data_source_aws_iam_role_test.go new file mode 100644 index 000000000..160e5d49b --- /dev/null +++ b/builtin/providers/aws/data_source_aws_iam_role_test.go @@ -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 = <> aws_iam_policy_document + > + aws_iam_role + > aws_iam_server_certificate From d17623891b2097d4e5a08bcd5737fc1434b66c11 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Thu, 30 Mar 2017 12:37:15 -0400 Subject: [PATCH 35/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcc42c4f4..66d578297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ FEATURES: * **New Resource:** `aws_lightsail_static_ip` [GH-13175] * **New Resource:** `kubernetes_secret` [GH-12960] + * **New Data Source:** `aws_iam_role` [GH-13213] IMPROVEMENTS: From 99c8c5302b5fd49f20e906ce26c5d50f88e600a6 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Thu, 30 Mar 2017 20:21:21 +0300 Subject: [PATCH 36/91] provider/aws: Document the AWS_IAM authorizer type for api_gateway_method (#13214) Fixes: #10497 --- .../docs/providers/aws/r/api_gateway_method.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/api_gateway_method.html.markdown b/website/source/docs/providers/aws/r/api_gateway_method.html.markdown index a5aa1fad2..3445f5ca0 100644 --- a/website/source/docs/providers/aws/r/api_gateway_method.html.markdown +++ b/website/source/docs/providers/aws/r/api_gateway_method.html.markdown @@ -39,7 +39,7 @@ The following arguments are supported: * `rest_api_id` - (Required) The ID of the associated REST API * `resource_id` - (Required) The API resource ID * `http_method` - (Required) The HTTP Method (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTION`, `ANY`) -* `authorization` - (Required) The type of authorization used for the method (`NONE`, `CUSTOM`) +* `authorization` - (Required) The type of authorization used for the method (`NONE`, `CUSTOM`, `AWS_IAM`) * `authorizer_id` - (Optional) The authorizer id to be used when the authorization is `CUSTOM` * `api_key_required` - (Optional) Specify if the method requires an API key * `request_models` - (Optional) A map of the API models used for the request's content type From 912bd2f9777e7b16fbb594beaeca72af0a04bcaf Mon Sep 17 00:00:00 2001 From: Phillip Shipley Date: Thu, 30 Mar 2017 14:01:13 -0400 Subject: [PATCH 37/91] Possible correction regarding remote state files Documentation has "... people have to remove state files..." when I believe it should say "...people have to use remote state files...". --- website/source/docs/state/purpose.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/state/purpose.html.md b/website/source/docs/state/purpose.html.md index d6e9beb58..7c2893abf 100644 --- a/website/source/docs/state/purpose.html.md +++ b/website/source/docs/state/purpose.html.md @@ -88,7 +88,7 @@ state is treated as the record of truth. ## Syncing -The primary motivation people have to remove state files is in an attempt +The primary motivation people have to remote state files is in an attempt to improve using Terraform with teams. State files can easily result in conflicts when two people modify infrastructure at the same time. From a21b599a791d6ca6849076e50fd0f1657949ed94 Mon Sep 17 00:00:00 2001 From: Devon Hubner Date: Thu, 30 Mar 2017 16:23:31 -0400 Subject: [PATCH 38/91] Expanded Joyent Triton documentation (#13205) * Added triton_vlan and triton_fabric documentation. Added Data Center information to the Triton provider documentation. Added an Ubuntu example to triton_machine. Cleaned up a copy-and-paste error in the sidebar_current of the Front Matter. * fixed the active resource sidebar highlight * expanded triton firewall ssh example to include authorization for multiple source IPs --- .../source/docs/import/importability.html.md | 5 +- .../docs/providers/triton/index.html.markdown | 4 +- .../triton/r/triton_fabric.html.markdown | 88 +++++++++++++++++++ .../r/triton_firewall_rule.html.markdown | 21 ++++- .../triton/r/triton_key.html.markdown | 2 +- .../triton/r/triton_machine.html.markdown | 27 +++++- .../triton/r/triton_vlan.html.markdown | 37 ++++++++ website/source/layouts/triton.erb | 10 ++- 8 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 website/source/docs/providers/triton/r/triton_fabric.html.markdown create mode 100644 website/source/docs/providers/triton/r/triton_vlan.html.markdown diff --git a/website/source/docs/import/importability.html.md b/website/source/docs/import/importability.html.md index e4c50e2ed..a9bac81e6 100644 --- a/website/source/docs/import/importability.html.md +++ b/website/source/docs/import/importability.html.md @@ -177,7 +177,8 @@ To make a resource importable, please see the ### Triton -* triton_firewall_rule * triton_key -* triton_machine +* triton_firewall_rule * triton_vlan +* triton_fabric +* triton_machine diff --git a/website/source/docs/providers/triton/index.html.markdown b/website/source/docs/providers/triton/index.html.markdown index 01ab521db..4954f1291 100644 --- a/website/source/docs/providers/triton/index.html.markdown +++ b/website/source/docs/providers/triton/index.html.markdown @@ -20,7 +20,7 @@ provider "triton" { key_material = "${file("~/.ssh/id_rsa")}" key_id = "25:d4:a9:fe:ef:e6:c0:bf:b4:4b:4b:d4:a8:8f:01:0f" - # If using a private installation of Triton, specify the URL + # Set the URL to specify the specific Triton Data Center: url = "https://us-west-1.api.joyentcloud.com" } ``` @@ -32,4 +32,4 @@ The following arguments are supported in the `provider` block: * `account` - (Required) This is the name of the Triton account. It can also be provided via the `SDC_ACCOUNT` environment variable. * `key_material` - (Required) This is the private key of an SSH key associated with the Triton account to be used. * `key_id` - (Required) This is the fingerprint of the public key matching the key specified in `key_path`. It can be obtained via the command `ssh-keygen -l -E md5 -f /path/to/key` -* `url` - (Optional) This is the URL to the Triton API endpoint. It is required if using a private installation of Triton. The default is to use the Joyent public cloud. +* `url` - (Optional) This is the URL to the Triton API endpoint. It is required if using a private installation of Triton. The default is to use the Joyent public cloud us-west-1 endpoint. Valid public cloud endpoints include: `us-east-1`, `us-east-2`, `us-east-3`, `us-sw-1`, `us-west-1`, `eu-ams-1` diff --git a/website/source/docs/providers/triton/r/triton_fabric.html.markdown b/website/source/docs/providers/triton/r/triton_fabric.html.markdown new file mode 100644 index 000000000..609690cd7 --- /dev/null +++ b/website/source/docs/providers/triton/r/triton_fabric.html.markdown @@ -0,0 +1,88 @@ +--- +layout: "triton" +page_title: "Triton: triton_fabric" +sidebar_current: "docs-triton-resource-fabric" +description: |- + The `triton_fabric` resource represents an SSH fabric for a Triton account. +--- + +# triton\_fabric + +The `triton_fabric` resource represents an fabric for a Triton account. The fabric is a logical set of interconnected switches. + +## Example Usages + +### Create a fabric + + +``` +resource "triton_fabric" "dmz" { + vlan_id = 100 + name = "dmz" + description = "DMZ Network" + subnet = "10.60.1.0/24" + provision_start_ip = "10.60.1.10" + provision_end_ip = "10.60.1.240" + gateway = "10.60.1.1" + resolvers = ["8.8.8.8", "8.8.4.4"] +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - (String, Required, Change forces new resource) + Network name. + +* `description` - (String, Optional, Change forces new resource) + Optional description of network. + +* `subnet` - (String, Required, Change forces new resource) + CIDR formatted string describing network. + +* `provision_start_ip` - (String, Required, Change forces new resource) + First IP on the network that can be assigned. + +* `provision_end_ip` - (String, Required, Change forces new resource) + Last assignable IP on the network. + +* `gateway` - (String, Optional, Change forces new resource) + Optional gateway IP. + +* `resolvers` - (List, Optional) + Array of IP addresses for resolvers. + +* `routes` - (Map, Optional, Change forces new resource) + Map of CIDR block to Gateway IP address. + +* `internet_nat` - (Bool, Optional, Change forces new resource) + If a NAT zone is provisioned at Gateway IP address. + +* `vlan_id` - (Int, Required, Change forces new resource) + VLAN id the network is on. Number between 0-4095 indicating VLAN ID. + + + + +## Attribute Reference + +The following attributes are exported: + +* `name` - (String) - Network name. +* `public` - (Bool) - Whether or not this is an RFC1918 network. +* `fabric` - (Bool) - Whether or not this network is on a fabric. +* `description` - (String) - Optional description of network. +* `subnet` - (String) - CIDR formatted string describing network. +* `provision_start_ip` - (String) - First IP on the network that can be assigned. +* `provision_end_ip` - (String) - Last assignable IP on the network. +* `gateway` - (String) - Optional gateway IP. +* `resolvers` - (List) - Array of IP addresses for resolvers. +* `routes` - (Map) - Map of CIDR block to Gateway IP address. +* `internet_nat` - (Bool) - If a NAT zone is provisioned at Gateway IP address. +* `vlan_id` - (Int) - VLAN id the network is on. Number between 0-4095 indicating VLAN ID. + + + + diff --git a/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown b/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown index ef31fd004..1bb815f33 100644 --- a/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown +++ b/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown @@ -1,7 +1,7 @@ --- layout: "triton" page_title: "Triton: triton_firewall_rule" -sidebar_current: "docs-triton-firewall" +sidebar_current: "docs-triton-resource-firewall-rule" description: |- The `triton_firewall_rule` resource represents a rule for the Triton cloud firewall. --- @@ -12,7 +12,7 @@ The `triton_firewall_rule` resource represents a rule for the Triton cloud firew ## Example Usages -Allow traffic on ports tcp/80 and tcp/443 to machines with the 'www' tag from any source +### Allow web traffic on ports tcp/80 and tcp/443 to machines with the 'www' tag from any source ``` @@ -21,9 +21,22 @@ resource "triton_firewall_rule" "www" { enabled = true } ``` -Block traffic on port tcp/143 to all machines +### Allow ssh traffic on port tcp/22 to all machines from known remote IPs + + +``` +resource "triton_firewall_rule" "22" { + rule = "FROM IP (IP w.x.y.z OR IP w.x.y.z) TO all vms ALLOW tcp port 22" + enabled = true +} +``` + + + +### Block IMAP traffic on port tcp/143 to all machines + ``` resource "triton_firewall_rule" "imap" { rule = "FROM any TO all vms BLOCK tcp port 143" @@ -31,6 +44,8 @@ resource "triton_firewall_rule" "imap" { } ``` + + ## Argument Reference The following arguments are supported: diff --git a/website/source/docs/providers/triton/r/triton_key.html.markdown b/website/source/docs/providers/triton/r/triton_key.html.markdown index e87ce3cad..4d18fdc64 100644 --- a/website/source/docs/providers/triton/r/triton_key.html.markdown +++ b/website/source/docs/providers/triton/r/triton_key.html.markdown @@ -1,7 +1,7 @@ --- layout: "triton" page_title: "Triton: triton_key" -sidebar_current: "docs-triton-firewall" +sidebar_current: "docs-triton-resource-key" description: |- The `triton_key` resource represents an SSH key for a Triton account. --- diff --git a/website/source/docs/providers/triton/r/triton_machine.html.markdown b/website/source/docs/providers/triton/r/triton_machine.html.markdown index f7a10f285..c5f4d851d 100644 --- a/website/source/docs/providers/triton/r/triton_machine.html.markdown +++ b/website/source/docs/providers/triton/r/triton_machine.html.markdown @@ -1,7 +1,7 @@ --- layout: "triton" page_title: "Triton: triton_machine" -sidebar_current: "docs-triton-firewall" +sidebar_current: "docs-triton-resource-machine" description: |- The `triton_machine` resource represents a virtual machine or infrastructure container running in Triton. --- @@ -12,12 +12,12 @@ The `triton_machine` resource represents a virtual machine or infrastructure con ## Example Usages -Run a SmartOS base-64 machine. +### Run a SmartOS base-64 machine. ``` -resource "triton_machine" "test" { - name = "example-machine" +resource "triton_machine" "test-smartos" { + name = "test-smartos" package = "g3-standard-0.25-smartos" image = "842e6fa6-6e9b-11e5-8402-1b490459e334" @@ -27,6 +27,25 @@ resource "triton_machine" "test" { } ``` +### Run an Ubuntu 14.04 LTS machine. + +``` +resource "triton_machine" "test-ubuntu" { + name = "test-ubuntu" + package = "g4-general-4G" + image = "1996a1d6-c0d9-11e6-8b80-4772e39dc920" + firewall_enabled = true + root_authorized_keys = "Example Key" + user_script = "#!/bin/bash\necho 'testing user-script' >> /tmp/test.out\nhostname $IMAGENAME" + + tags = { + purpose = "testing ubuntu" + } ## tags +} ## resource +``` + + + ## Argument Reference The following arguments are supported: diff --git a/website/source/docs/providers/triton/r/triton_vlan.html.markdown b/website/source/docs/providers/triton/r/triton_vlan.html.markdown new file mode 100644 index 000000000..838cc4393 --- /dev/null +++ b/website/source/docs/providers/triton/r/triton_vlan.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "triton" +page_title: "Triton: triton_vlan" +sidebar_current: "docs-triton-resource-vlan" +description: |- + The `triton_vlan` resource represents an VLAN for a Triton account. +--- + +# triton\_vlan + +The `triton_vlan` resource represents an Triton VLAN. A VLAN provides a low level way to segregate and subdivide the network. Traffic on one VLAN cannot, _on its own_, reach another VLAN. + +## Example Usages + +### Create a VLAN + + +``` +resource "triton_vlan" "dmz" { + vlan_id = 100 + name = "dmz" + description = "DMZ VLAN" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vlan_id` - (int, Required, Change forces new resource) + Number between 0-4095 indicating VLAN ID + +* `name` - (string, Required) + Unique name to identify VLAN + +* `description` - (string, Optional) + Description of the VLAN diff --git a/website/source/layouts/triton.erb b/website/source/layouts/triton.erb index 1482a8b52..3e048b3ea 100644 --- a/website/source/layouts/triton.erb +++ b/website/source/layouts/triton.erb @@ -14,11 +14,17 @@ Resources From 39b9e77d8a73a38d54f9b349665f33d46e751775 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 31 Mar 2017 07:31:30 +0100 Subject: [PATCH 45/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 748f22299..4807a6cc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: * **New Resource:** `aws_lightsail_static_ip` [GH-13175] + * **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207] * **New Resource:** `kubernetes_secret` [GH-12960] * **New Data Source:** `aws_iam_role` [GH-13213] From ee0a4c43fc307f61a8c9f8b9a32c0e3f50f247e5 Mon Sep 17 00:00:00 2001 From: Seigo Uchida Date: Fri, 31 Mar 2017 16:32:54 +0900 Subject: [PATCH 46/91] [docs] Fix wrong attributes in lambda_permission doc (#13191) * Fix wrong attributes in lambda_permission doc * Add a missing attribute in lambda_permission doc --- .../docs/providers/aws/r/lambda_permission.html.markdown | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/aws/r/lambda_permission.html.markdown b/website/source/docs/providers/aws/r/lambda_permission.html.markdown index 671108cd9..3360aef2d 100644 --- a/website/source/docs/providers/aws/r/lambda_permission.html.markdown +++ b/website/source/docs/providers/aws/r/lambda_permission.html.markdown @@ -27,7 +27,7 @@ resource "aws_lambda_permission" "allow_cloudwatch" { resource "aws_lambda_alias" "test_alias" { name = "testalias" description = "a sample description" - function_name = "${aws_lambda_function.test_lambda.arn}" + function_name = "${aws_lambda_function.test_lambda.function_name}" function_version = "$LATEST" } @@ -36,6 +36,7 @@ resource "aws_lambda_function" "test_lambda" { function_name = "lambda_function_name" role = "${aws_iam_role.iam_for_lambda.arn}" handler = "exports.handler" + runtime = "nodejs6.10" } resource "aws_iam_role" "iam_for_lambda" { @@ -65,7 +66,7 @@ EOF resource "aws_lambda_permission" "with_sns" { statement_id = "AllowExecutionFromSNS" action = "lambda:InvokeFunction" - function_name = "${aws_lambda_function.my-func.arn}" + function_name = "${aws_lambda_function.my-func.function_name}" principal = "sns.amazonaws.com" source_arn = "${aws_sns_topic.default.arn}" } @@ -85,6 +86,7 @@ resource "aws_lambda_function" "func" { function_name = "lambda_called_from_sns" role = "${aws_iam_role.default.arn}" handler = "exports.handler" + runtime = "python2.7" } resource "aws_iam_role" "default" { From 293922e5aef6ebc32f26c302ca06fe27021133e8 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:28:56 +0300 Subject: [PATCH 47/91] provider/aws: Refresh aws_alb_target_group stickiness on manual updates (#13199) Fixes: #13167 When changes to the target group were made via CLI or AWS Console, they were not being picked up by terraform. This is because we were not catching an error setting the `stickiness` parameters: ``` Error refreshing state: 1 error(s) occurred: * aws_alb_target_group.test: aws_alb_target_group.test: stickiness.0.enabled: '' expected type 'bool', got unconvertible type 'string' ``` This meant that changes were not picked up in the following plan. The changes mean the following now: ``` ~ aws_alb_target_group.test stickiness.0.cookie_duration: "10440" => "10000" stickiness.0.enabled: "false" => "true" Plan: 0 to add, 1 to change, 0 to destroy. ``` --- .../aws/resource_aws_alb_target_group.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_alb_target_group.go b/builtin/providers/aws/resource_aws_alb_target_group.go index 96ecd0d42..01f96b327 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group.go +++ b/builtin/providers/aws/resource_aws_alb_target_group.go @@ -258,11 +258,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 +279,10 @@ 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 + } return nil } From 5a37434bf198a2d53b18bc0556e505594fb1a12a Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:30:21 +0300 Subject: [PATCH 48/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4807a6cc9..5d29e3409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ BUG FIXES: * 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From e4e9d1e0730cb8031a58719579cc39985eda14eb Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:34:51 +0300 Subject: [PATCH 49/91] provider/aws: Preserve default retain_on_delete in cloudfront import (#13209) Fixes: #10969 --- .../providers/aws/import_aws_cloudfront_distribution.go | 4 ++++ .../aws/import_aws_cloudfront_distribution_test.go | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/builtin/providers/aws/import_aws_cloudfront_distribution.go b/builtin/providers/aws/import_aws_cloudfront_distribution.go index dcb8792a3..acfc836dc 100644 --- a/builtin/providers/aws/import_aws_cloudfront_distribution.go +++ b/builtin/providers/aws/import_aws_cloudfront_distribution.go @@ -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{ diff --git a/builtin/providers/aws/import_aws_cloudfront_distribution_test.go b/builtin/providers/aws/import_aws_cloudfront_distribution_test.go index 9fc195819..787d913a5 100644 --- a/builtin/providers/aws/import_aws_cloudfront_distribution_test.go +++ b/builtin/providers/aws/import_aws_cloudfront_distribution_test.go @@ -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"}, }, }, }) From 453325f3246cf3d41042145b68a04b3ed84cf45c Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:35:39 +0300 Subject: [PATCH 50/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d29e3409..f57fcd3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ BUG FIXES: * 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 46a5cd543c539a37f4770c2dc7040a112f992512 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:36:15 +0300 Subject: [PATCH 51/91] provider/aws: Refresh aws_alb_target_group tags (#13200) Fixes: #8847 We actually didn't get the list of tags from the API, therefore, any manual changes were not actually showing up in subsequent plans ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSALBTargetGroup_basic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/30 15:45:53 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSALBTargetGroup_basic -timeout 120m === RUN TestAccAWSALBTargetGroup_basic --- PASS: TestAccAWSALBTargetGroup_basic (62.76s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 62.787s ``` --- .../providers/aws/resource_aws_alb_target_group.go | 14 ++++++++++++++ .../aws/resource_aws_alb_target_group_test.go | 2 ++ 2 files changed, 16 insertions(+) diff --git a/builtin/providers/aws/resource_aws_alb_target_group.go b/builtin/providers/aws/resource_aws_alb_target_group.go index 01f96b327..df1a662be 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group.go +++ b/builtin/providers/aws/resource_aws_alb_target_group.go @@ -284,6 +284,20 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err 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 } diff --git a/builtin/providers/aws/resource_aws_alb_target_group_test.go b/builtin/providers/aws/resource_aws_alb_target_group_test.go index 67d453e6c..7a67978a8 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group_test.go +++ b/builtin/providers/aws/resource_aws_alb_target_group_test.go @@ -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"), ), }, }, From d139db87397260d29e791e64e568c0f95f406632 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:36:39 +0300 Subject: [PATCH 52/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57fcd3b4..ed00095b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ BUG FIXES: * 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From d06db231971de673efc76ec8c82d711443c77655 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:40:37 +0300 Subject: [PATCH 53/91] provider/aws: Set aws_vpn_connection to recreate when in deleted state (#13204) Fixes: #12440 ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSVpnConnection_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/30 16:16:13 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSVpnConnection_ -timeout 120m === RUN TestAccAWSVpnConnection_importBasic --- PASS: TestAccAWSVpnConnection_importBasic (208.68s) === RUN TestAccAWSVpnConnection_basic --- PASS: TestAccAWSVpnConnection_basic (391.02s) === RUN TestAccAWSVpnConnection_withoutStaticRoutes --- PASS: TestAccAWSVpnConnection_withoutStaticRoutes (316.99s) === RUN TestAccAWSVpnConnection_disappears --- PASS: TestAccAWSVpnConnection_disappears (202.84s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 1119.563s ``` --- .../aws/resource_aws_vpn_connection.go | 5 ++ .../aws/resource_aws_vpn_connection_test.go | 82 ++++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_vpn_connection.go b/builtin/providers/aws/resource_aws_vpn_connection.go index b38d903cf..1cdd83efd 100644 --- a/builtin/providers/aws/resource_aws_vpn_connection.go +++ b/builtin/providers/aws/resource_aws_vpn_connection.go @@ -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) diff --git a/builtin/providers/aws/resource_aws_vpn_connection_test.go b/builtin/providers/aws/resource_aws_vpn_connection_test.go index a07bdd10b..e5328ca9a 100644 --- a/builtin/providers/aws/resource_aws_vpn_connection_test.go +++ b/builtin/providers/aws/resource_aws_vpn_connection_test.go @@ -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 } } From b449b80af3333d5ac8b0976156d9ef5401d8fa5c Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:41:08 +0300 Subject: [PATCH 54/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed00095b5..93823fa3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ BUG FIXES: * 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 74c0353231d8f31921d28f90b146a838a58359c8 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:45:45 +0300 Subject: [PATCH 55/91] provider/aws: Wait for aws_opsworks_instance to be running when it's specified (#13218) Fixes: #9959 When we specify that we want an opsworks_instance state of running, we should wait for that the be the case. This will then allow us to use the Computed values (e.g. private_ip) etc and allow us to use provisioners as part of the terraform config ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSOpsworksInstance' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/30 20:55:21 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSOpsworksInstance -timeout 120m === RUN TestAccAWSOpsworksInstance_importBasic --- PASS: TestAccAWSOpsworksInstance_importBasic (72.28s) === RUN TestAccAWSOpsworksInstance --- PASS: TestAccAWSOpsworksInstance (110.17s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 182.479s ``` --- builtin/providers/aws/resource_aws_opsworks_instance.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_opsworks_instance.go b/builtin/providers/aws/resource_aws_opsworks_instance.go index 2b2b51c49..b195ac902 100644 --- a/builtin/providers/aws/resource_aws_opsworks_instance.go +++ b/builtin/providers/aws/resource_aws_opsworks_instance.go @@ -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 } From 835792018a119284573eb787df5c4ff14fc7c9b8 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:47:27 +0300 Subject: [PATCH 56/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93823fa3d..c453749ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ BUG FIXES: * 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 0f2331cf81d9430d92dc11c3a2937d918c594f05 Mon Sep 17 00:00:00 2001 From: Phillip Shipley Date: Fri, 31 Mar 2017 08:29:04 -0400 Subject: [PATCH 57/91] Improved sentence based on feedback --- website/source/docs/state/purpose.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/state/purpose.html.md b/website/source/docs/state/purpose.html.md index 7c2893abf..a8647006e 100644 --- a/website/source/docs/state/purpose.html.md +++ b/website/source/docs/state/purpose.html.md @@ -88,7 +88,7 @@ state is treated as the record of truth. ## Syncing -The primary motivation people have to remote state files is in an attempt +The primary motivation people have for using remote state files is in an attempt to improve using Terraform with teams. State files can easily result in conflicts when two people modify infrastructure at the same time. From 6ed873b72d85aaa896356d1c742c72bf3605b45e Mon Sep 17 00:00:00 2001 From: zimbatm Date: Fri, 31 Mar 2017 11:46:48 +0100 Subject: [PATCH 58/91] Make the external test work on more environments GOPATH is actually a list of path and doesn't necessarily have to be set. If unset it will default to $GOPATH/go in go 1.9+. Assume that go install will install to the first path in the list. --- builtin/providers/external/data_source_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin/providers/external/data_source_test.go b/builtin/providers/external/data_source_test.go index dbdf00386..b1ceabddf 100644 --- a/builtin/providers/external/data_source_test.go +++ b/builtin/providers/external/data_source_test.go @@ -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 } From c44487caee20051124d69d7e00ba4f08f3605f0c Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Wed, 29 Mar 2017 18:26:54 -0400 Subject: [PATCH 59/91] Handle the case when issue labels already exist This fixes GH-13163 --- .../github/resource_github_issue_label.go | 73 +++++++++++-------- .../github/r/issue_label.html.markdown | 16 +++- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/builtin/providers/github/resource_github_issue_label.go b/builtin/providers/github/resource_github_issue_label.go index 0d89c0343..5a1f6eea4 100644 --- a/builtin/providers/github/resource_github_issue_label.go +++ b/builtin/providers/github/resource_github_issue_label.go @@ -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 } diff --git a/website/source/docs/providers/github/r/issue_label.html.markdown b/website/source/docs/providers/github/r/issue_label.html.markdown index bb2a9aa04..216ed2bfe 100644 --- a/website/source/docs/providers/github/r/issue_label.html.markdown +++ b/website/source/docs/providers/github/r/issue_label.html.markdown @@ -6,16 +6,24 @@ description: |- Provides a GitHub issue label resource. --- -# github\_issue_label +# github_issue_label Provides a GitHub issue label resource. This resource allows you to create and manage issue labels within your Github organization. +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 resource will first check if the label exists, and then issue an update, +otherwise it will create. + ## Example Usage -``` +```hcl # Create a new, red colored label resource "github_issue_label" "test_repo" { repository = "test-repo" @@ -29,5 +37,7 @@ resource "github_issue_label" "test_repo" { The following arguments are supported: * `repository` - (Required) The GitHub repository + * `name` - (Required) The name of the label. -* `color` - (Required) A 6 character hex code, without the leading #, identifying the color of the label. + +* `color` - (Required) A 6 character hex code, **without the leading #**, identifying the color of the label. From 33dd50459362a087c88c12eaa2e7b2f4715f7269 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Fri, 31 Mar 2017 11:44:00 -0400 Subject: [PATCH 60/91] Add test --- .../resource_github_issue_label_test.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/builtin/providers/github/resource_github_issue_label_test.go b/builtin/providers/github/resource_github_issue_label_test.go index d3b3a0597..66461302d 100644 --- a/builtin/providers/github/resource_github_issue_label_test.go +++ b/builtin/providers/github/resource_github_issue_label_test.go @@ -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" +} +`) From 42557dae122e69cd69a6d7ab719946881f9059f5 Mon Sep 17 00:00:00 2001 From: Gauthier Wallet Date: Fri, 31 Mar 2017 18:45:06 +0200 Subject: [PATCH 61/91] provider/aws: Added API Gateway integration update (#13249) --- .../resource_aws_api_gateway_integration.go | 154 +++++++++++++-- ...source_aws_api_gateway_integration_test.go | 182 +++++++++++------- .../r/api_gateway_integration.html.markdown | 8 +- 3 files changed, 251 insertions(+), 93 deletions(-) diff --git a/builtin/providers/aws/resource_aws_api_gateway_integration.go b/builtin/providers/aws/resource_aws_api_gateway_integration.go index b06982880..f782e11ea 100644 --- a/builtin/providers/aws/resource_aws_api_gateway_integration.go +++ b/builtin/providers/aws/resource_aws_api_gateway_integration.go @@ -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()) diff --git a/builtin/providers/aws/resource_aws_api_gateway_integration_test.go b/builtin/providers/aws/resource_aws_api_gateway_integration_test.go index 153ed13b4..ff9c23387 100644 --- a/builtin/providers/aws/resource_aws_api_gateway_integration_test.go +++ b/builtin/providers/aws/resource_aws_api_gateway_integration_test.go @@ -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", "Foo"), + ), + }, + + { + 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" = "Foo" } - 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" } ` diff --git a/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown b/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown index 4281260ad..cbb2102bb 100644 --- a/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown +++ b/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown @@ -3,12 +3,12 @@ layout: "aws" page_title: "AWS: aws_api_gateway_integration" sidebar_current: "docs-aws-resource-api-gateway-integration" description: |- - Provides an HTTP Method Integration for an API Gateway Resource. + Provides an HTTP Method Integration for an API Gateway Integration. --- # aws\_api\_gateway\_integration -Provides an HTTP Method Integration for an API Gateway Resource. +Provides an HTTP Method Integration for an API Gateway Integration. ## Example Usage @@ -37,6 +37,10 @@ resource "aws_api_gateway_integration" "MyDemoIntegration" { http_method = "${aws_api_gateway_method.MyDemoMethod.http_method}" type = "MOCK" + request_parameters = { + "integration.request.header.X-Authorization" = "'static'" + } + # Transforms the incoming XML request to JSON request_templates { "application/xml" = < Date: Fri, 31 Mar 2017 19:45:32 +0300 Subject: [PATCH 62/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c453749ca..4a4a87171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS: * 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/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] From 4501be7e5c3d7f3c3b8af6297511072c243f188e Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 19:59:29 +0300 Subject: [PATCH 63/91] backend/remote-state: Add support for assume role extensions to s3 backend (#13236) Fixes: #13234 This now matches the AWS provider for the Assume Role support --- backend/remote-state/s3/backend.go | 64 ++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/backend/remote-state/s3/backend.go b/backend/remote-state/s3/backend.go index 8265d7f25..a1d9c1f9a 100644 --- a/backend/remote-state/s3/backend.go +++ b/backend/remote-state/s3/backend.go @@ -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 From 2f0cbda43559eef9395c28045d758503fdd84dd0 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 19:59:59 +0300 Subject: [PATCH 64/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4a87171..295ecb0b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ FEATURES: 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] From e7c3e8df68b9e3c724d3e4ccd851c6c3f66a821e Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 20:00:47 +0300 Subject: [PATCH 65/91] provider/aws: change kinesis_firehose_delivery_stream to point to correct destination (#13251) Fixes: #13244 --- .../aws/r/kinesis_firehose_delivery_stream.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown b/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown index 1c2cde2d4..57dca62e1 100644 --- a/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown +++ b/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown @@ -97,7 +97,7 @@ resource "aws_elasticsearch_domain" "test_cluster" { resource "aws_kinesis_firehose_delivery_stream" "test_stream" { name = "terraform-kinesis-firehose-test-stream" - destination = "redshift" + destination = "elasticsearch" s3_configuration { role_arn = "${aws_iam_role.firehose_role.arn}" From d25c3104681c18044895b47ed44a87dc71d1e34e Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Sat, 1 Apr 2017 04:22:57 +1100 Subject: [PATCH 66/91] Add `name_prefix` to RDS resources (#13232) Adds `name_prefix` (or, in some cases, `identifier_prefix`) support to all AWS RDS resources. --- .../providers/aws/resource_aws_db_instance.go | 24 +++- .../aws/resource_aws_db_instance_test.go | 79 ++++++++++- .../aws/resource_aws_db_option_group.go | 55 ++++---- .../aws/resource_aws_db_option_group_test.go | 124 ++++++++++++----- .../aws/resource_aws_db_parameter_group.go | 24 +++- .../resource_aws_db_parameter_group_test.go | 52 +++++++ .../aws/resource_aws_db_subnet_group.go | 42 +++--- .../aws/resource_aws_db_subnet_group_test.go | 116 +++++++++++----- .../providers/aws/resource_aws_rds_cluster.go | 26 +++- .../aws/resource_aws_rds_cluster_instance.go | 21 ++- .../resource_aws_rds_cluster_instance_test.go | 119 ++++++++++++++++ ...esource_aws_rds_cluster_parameter_group.go | 24 +++- ...ce_aws_rds_cluster_parameter_group_test.go | 51 +++++++ .../aws/resource_aws_rds_cluster_test.go | 105 ++++++++++++++ builtin/providers/aws/validators.go | 115 +++++++++++++++- builtin/providers/aws/validators_test.go | 128 ++++++++++++++++++ .../providers/aws/r/db_instance.html.markdown | 5 +- .../aws/r/db_option_group.html.markdown | 7 +- .../aws/r/db_parameter_group.html.markdown | 5 +- .../aws/r/db_subnet_group.html.markdown | 5 +- .../providers/aws/r/rds_cluster.html.markdown | 4 +- .../aws/r/rds_cluster_instance.html.markdown | 4 +- .../r/rds_cluster_parameter_group.markdown | 7 +- 23 files changed, 985 insertions(+), 157 deletions(-) diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index 192ccab97..00409230d 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -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") { diff --git a/builtin/providers/aws/resource_aws_db_instance_test.go b/builtin/providers/aws/resource_aws_db_instance_test.go index 56f890532..6c8d451ca 100644 --- a/builtin/providers/aws/resource_aws_db_instance_test.go +++ b/builtin/providers/aws/resource_aws_db_instance_test.go @@ -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,39 @@ 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" + security_group_names = ["default"] + 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" + security_group_names = ["default"] + publicly_accessible = true + skip_final_snapshot = true + + timeouts { + create = "30m" + } +}` + var testAccAWSDBInstanceConfigKmsKeyId = ` resource "aws_kms_key" "foo" { description = "Terraform acc test %s" @@ -720,7 +793,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 diff --git a/builtin/providers/aws/resource_aws_db_option_group.go b/builtin/providers/aws/resource_aws_db_option_group.go index 5c68e7bd3..e8ad1ac99 100644 --- a/builtin/providers/aws/resource_aws_db_option_group.go +++ b/builtin/providers/aws/resource_aws_db_option_group.go @@ -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 -} diff --git a/builtin/providers/aws/resource_aws_db_option_group_test.go b/builtin/providers/aws/resource_aws_db_option_group_test.go index 5a3215b04..3ee5f8197 100644 --- a/builtin/providers/aws/resource_aws_db_option_group_test.go +++ b/builtin/providers/aws/resource_aws_db_option_group_test.go @@ -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) +} diff --git a/builtin/providers/aws/resource_aws_db_parameter_group.go b/builtin/providers/aws/resource_aws_db_parameter_group.go index b18282712..d5e943fd6 100644 --- a/builtin/providers/aws/resource_aws_db_parameter_group.go +++ b/builtin/providers/aws/resource_aws_db_parameter_group.go @@ -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, diff --git a/builtin/providers/aws/resource_aws_db_parameter_group_test.go b/builtin/providers/aws/resource_aws_db_parameter_group_test.go index 75db4f77d..b8e4e56c4 100644 --- a/builtin/providers/aws/resource_aws_db_parameter_group_test.go +++ b/builtin/providers/aws/resource_aws_db_parameter_group_test.go @@ -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" +} +` diff --git a/builtin/providers/aws/resource_aws_db_subnet_group.go b/builtin/providers/aws/resource_aws_db_subnet_group.go index 9c1c56199..c4e437bee 100644 --- a/builtin/providers/aws/resource_aws_db_subnet_group.go +++ b/builtin/providers/aws/resource_aws_db_subnet_group.go @@ -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 -} diff --git a/builtin/providers/aws/resource_aws_db_subnet_group_test.go b/builtin/providers/aws/resource_aws_db_subnet_group_test.go index 434ae1728..70d27c5db 100644 --- a/builtin/providers/aws/resource_aws_db_subnet_group_test.go +++ b/builtin/providers/aws/resource_aws_db_subnet_group_test.go @@ -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" diff --git a/builtin/providers/aws/resource_aws_rds_cluster.go b/builtin/providers/aws/resource_aws_rds_cluster.go index 7461b880c..c33129654 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster.go +++ b/builtin/providers/aws/resource_aws_rds_cluster.go @@ -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)), diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance.go b/builtin/providers/aws/resource_aws_rds_cluster_instance.go index 36f111628..2caca4573 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance.go @@ -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 { diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go index b1e66ea7e..df1a5d644 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go @@ -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(` diff --git a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go index 62b0d497b..61cb20f01 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go @@ -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, diff --git a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go index f2a526d2e..231fdf44c 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go @@ -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" +} +` diff --git a/builtin/providers/aws/resource_aws_rds_cluster_test.go b/builtin/providers/aws/resource_aws_rds_cluster_test.go index c18a6431a..3774385a9 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_test.go @@ -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" { diff --git a/builtin/providers/aws/validators.go b/builtin/providers/aws/validators.go index a8f9c66cf..7ff0e6f38 100644 --- a/builtin/providers/aws/validators.go +++ b/builtin/providers/aws/validators.go @@ -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 +} diff --git a/builtin/providers/aws/validators_test.go b/builtin/providers/aws/validators_test.go index 0c37308fe..7fe451a87 100644 --- a/builtin/providers/aws/validators_test.go +++ b/builtin/providers/aws/validators_test.go @@ -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") + } + } +} diff --git a/website/source/docs/providers/aws/r/db_instance.html.markdown b/website/source/docs/providers/aws/r/db_instance.html.markdown index e491e3784..7e6044425 100644 --- a/website/source/docs/providers/aws/r/db_instance.html.markdown +++ b/website/source/docs/providers/aws/r/db_instance.html.markdown @@ -55,7 +55,8 @@ The following arguments are supported: * `allocated_storage` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The allocated storage in gigabytes. * `engine` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The database engine to use. * `engine_version` - (Optional) The engine version to use. -* `identifier` - (Optional) The name of the RDS instance, if omitted, Terraform will assign a random, unique name +* `identifier` - (Optional, Forces new resource) The name of the RDS instance, if omitted, Terraform will assign a random, unique identifier. +* `identifier_prefix` - (Optional, Forces new resource) Creates a unique identifier beginning with the specified prefix. Conflicts with `identifer`. * `instance_class` - (Required) The instance type of the RDS instance. * `storage_type` - (Optional) One of "standard" (magnetic), "gp2" (general purpose SSD), or "io1" (provisioned IOPS SSD). The default is "io1" if @@ -156,7 +157,7 @@ On Oracle instances the following is exported additionally: - `create` - (Default `40 minutes`) Used for Creating Instances, Replicas, and restoring from Snapshots -- `update` - (Default `80 minutes`) Used for Database modifications +- `update` - (Default `80 minutes`) Used for Database modifications - `delete` - (Default `40 minutes`) Used for destroying databases. This includes the time required to take snapshots diff --git a/website/source/docs/providers/aws/r/db_option_group.html.markdown b/website/source/docs/providers/aws/r/db_option_group.html.markdown index faf7b351c..ad4c4d5d4 100644 --- a/website/source/docs/providers/aws/r/db_option_group.html.markdown +++ b/website/source/docs/providers/aws/r/db_option_group.html.markdown @@ -38,8 +38,9 @@ resource "aws_db_option_group" "bar" { The following arguments are supported: -* `name` - (Required) The name of the Option group to be created. -* `option_group_description` - (Required) The description of the option group. +* `name` - (Optional, Forces new resource) The name of the option group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. +* `option_group_description` - (Optional) The description of the option group. Defaults to "Managed by Terraform". * `engine_name` - (Required) Specifies the name of the engine that this option group should be associated with.. * `major_engine_version` - (Required) Specifies the major version of the engine that this option group should be associated with. * `option` - (Optional) A list of Options to apply. @@ -70,4 +71,4 @@ DB Option groups can be imported using the `name`, e.g. ``` $ terraform import aws_db_option_group.bar mysql-option-group -``` \ No newline at end of file +``` diff --git a/website/source/docs/providers/aws/r/db_parameter_group.html.markdown b/website/source/docs/providers/aws/r/db_parameter_group.html.markdown index 271325034..2e4d362d2 100644 --- a/website/source/docs/providers/aws/r/db_parameter_group.html.markdown +++ b/website/source/docs/providers/aws/r/db_parameter_group.html.markdown @@ -31,7 +31,8 @@ resource "aws_db_parameter_group" "default" { The following arguments are supported: -* `name` - (Required) The name of the DB parameter group. +* `name` - (Optional, Forces new resource) The name of the DB parameter group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `family` - (Required) The family of the DB parameter group. * `description` - (Optional) The description of the DB parameter group. Defaults to "Managed by Terraform". * `parameter` - (Optional) A list of DB parameters to apply. @@ -58,4 +59,4 @@ DB Parameter groups can be imported using the `name`, e.g. ``` $ terraform import aws_db_parameter_group.rds_pg rds-pg -``` \ No newline at end of file +``` diff --git a/website/source/docs/providers/aws/r/db_subnet_group.html.markdown b/website/source/docs/providers/aws/r/db_subnet_group.html.markdown index a97f22c05..bee5eee52 100644 --- a/website/source/docs/providers/aws/r/db_subnet_group.html.markdown +++ b/website/source/docs/providers/aws/r/db_subnet_group.html.markdown @@ -27,7 +27,8 @@ resource "aws_db_subnet_group" "default" { The following arguments are supported: -* `name` - (Required) The name of the DB subnet group. +* `name` - (Optional, Forces new resource) The name of the DB subnet group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `description` - (Optional) The description of the DB subnet group. Defaults to "Managed by Terraform". * `subnet_ids` - (Required) A list of VPC subnet IDs. * `tags` - (Optional) A mapping of tags to assign to the resource. @@ -46,4 +47,4 @@ DB Subnet groups can be imported using the `name`, e.g. ``` $ terraform import aws_db_subnet_group.default production-subnet-group -``` \ No newline at end of file +``` diff --git a/website/source/docs/providers/aws/r/rds_cluster.html.markdown b/website/source/docs/providers/aws/r/rds_cluster.html.markdown index b40cbab40..6139824f1 100644 --- a/website/source/docs/providers/aws/r/rds_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster.html.markdown @@ -53,8 +53,8 @@ the [AWS official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/Co The following arguments are supported: -* `cluster_identifier` - (Required) The Cluster Identifier. Must be a lower case -string. +* `cluster_identifier` - (Optional, Forces new resources) The cluster identifier. If omitted, Terraform will assign a random, unique identifier. +* `cluster_identifier_prefix` - (Optional, Forces new resource) Creates a unique cluster identifier beginning with the specified prefix. Conflicts with `cluster_identifer`. * `database_name` - (Optional) The name for your database of up to 8 alpha-numeric characters. If you do not provide a name, Amazon RDS will not create a database in the DB cluster you are creating diff --git a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown index d5196d733..031972d4b 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown @@ -47,8 +47,8 @@ the [AWS official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/Co The following arguments are supported: -* `identifier` - (Optional) The Instance Identifier. Must be a lower case -string. If omitted, a unique identifier will be generated. +* `identifier` - (Optional, Forces new resource) The indentifier for the RDS instance, if omitted, Terraform will assign a random, unique identifier. +* `identifier_prefix` - (Optional, Forces new resource) Creates a unique identifier beginning with the specified prefix. Conflicts with `identifer`. * `cluster_identifier` - (Required) The identifier of the [`aws_rds_cluster`](/docs/providers/aws/r/rds_cluster.html) in which to launch this instance. * `instance_class` - (Required) The instance class to use. For details on CPU and memory, see [Scaling Aurora DB Instances][4]. Aurora currently diff --git a/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown b/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown index 8b465efad..c2fa63d78 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown @@ -32,9 +32,10 @@ resource "aws_rds_cluster_parameter_group" "default" { The following arguments are supported: -* `name` - (Required) The name of the DB cluster parameter group. +* `name` - (Optional, Forces new resource) The name of the DB cluster parameter group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `family` - (Required) The family of the DB cluster parameter group. -* `description` - (Required) The description of the DB cluster parameter group. +* `description` - (Optional) The description of the DB cluster parameter group. Defaults to "Managed by Terraform". * `parameter` - (Optional) A list of DB parameters to apply. * `tags` - (Optional) A mapping of tags to assign to the resource. @@ -60,4 +61,4 @@ RDS Cluster Parameter Groups can be imported using the `name`, e.g. ``` $ terraform import aws_rds_cluster_parameter_group.cluster_pg production-pg-1 -``` \ No newline at end of file +``` From d7a339eb4627b9a2f4b82f969bebbf0823a6e3d3 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 20:23:27 +0300 Subject: [PATCH 67/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 295ecb0b9..3c672c3d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ IMPROVEMENTS: * 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/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] From 50fb9aecbdf9bc637c1b3b2ae9eeb01ca06fe95e Mon Sep 17 00:00:00 2001 From: James Nugent Date: Fri, 31 Mar 2017 11:33:08 -0700 Subject: [PATCH 68/91] deps: Update github.com/joyent/triton-go/authentication (#13255) This commit allows private key material to be used with the Triton provider, which is necessary for running acceptance tests in the HashiCorp CI environment. --- .../authentication/private_key_signer.go | 15 +++++++++------ vendor/vendor.json | 10 +++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go b/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go index aadb6ee5f..20dc6bfed 100644 --- a/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go +++ b/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go @@ -7,10 +7,12 @@ import ( "crypto/x509" "encoding/base64" "encoding/pem" + "errors" "fmt" + "strings" + "github.com/hashicorp/errwrap" "golang.org/x/crypto/ssh" - "strings" ) type PrivateKeySigner struct { @@ -27,23 +29,23 @@ func NewPrivateKeySigner(keyFingerprint string, privateKeyMaterial []byte, accou block, _ := pem.Decode(privateKeyMaterial) if block == nil { - return nil, fmt.Errorf("Error PEM-decoding private key material: nil block received") + return nil, errors.New("Error PEM-decoding private key material: nil block received") } rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { - return nil, errwrap.Wrapf("Error parsing private key: %s", err) + return nil, errwrap.Wrapf("Error parsing private key: {{err}}", err) } sshPublicKey, err := ssh.NewPublicKey(rsakey.Public()) if err != nil { - return nil, errwrap.Wrapf("Error parsing SSH key from private key: %s", err) + return nil, errwrap.Wrapf("Error parsing SSH key from private key: {{err}}", err) } matchKeyFingerprint := formatPublicKeyFingerprint(sshPublicKey, false) displayKeyFingerprint := formatPublicKeyFingerprint(sshPublicKey, true) if matchKeyFingerprint != keyFingerprintMD5 { - return nil, fmt.Errorf("Private key file does not match public key fingerprint") + return nil, errors.New("Private key file does not match public key fingerprint") } return &PrivateKeySigner{ @@ -69,5 +71,6 @@ func (s *PrivateKeySigner) Sign(dateHeader string) (string, error) { } signedBase64 := base64.StdEncoding.EncodeToString(signed) - return fmt.Sprintf(authorizationHeaderFormat, s.formattedKeyFingerprint, "rsa-sha1", headerName, signedBase64), nil + keyID := fmt.Sprintf("/%s/keys/%s", s.accountName, s.formattedKeyFingerprint) + return fmt.Sprintf(authorizationHeaderFormat, keyID, "rsa-sha1", headerName, signedBase64), nil } diff --git a/vendor/vendor.json b/vendor/vendor.json index e8993495c..1d3e40282 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2296,14 +2296,14 @@ { "checksumSHA1": "fue8Al8kqw/Q6VFPsNzoky7NIgo=", "path": "github.com/joyent/triton-go", - "revision": "ed036af6d128e3c1ef76e92218810d3b298d1407", - "revisionTime": "2017-03-30T22:02:44Z" + "revision": "66b31a94af28a65e902423879a2820ea34b773fb", + "revisionTime": "2017-03-31T18:12:29Z" }, { - "checksumSHA1": "7sIV9LK625xVO9WsV1gaLQgfBeY=", + "checksumSHA1": "QzUqkCSn/ZHyIK346xb9V6EBw9U=", "path": "github.com/joyent/triton-go/authentication", - "revision": "ed036af6d128e3c1ef76e92218810d3b298d1407", - "revisionTime": "2017-03-30T22:02:44Z" + "revision": "66b31a94af28a65e902423879a2820ea34b773fb", + "revisionTime": "2017-03-31T18:12:29Z" }, { "checksumSHA1": "YhQcOsGx8r2S/jkJ0Qt4cZ5BLCU=", From 8050eda52dddf7a62fba602725ca6afdf637fdaf Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 31 Mar 2017 15:21:32 -0400 Subject: [PATCH 69/91] don't delete local state on a local backend Don't erase local state during backend migration if the new and old paths are the same. Skipping the confirmation and copy are handled in another patch, but the local state was always erased by default, even when it was our new state. --- command/meta_backend.go | 28 +++++++++++++++++----- command/meta_backend_test.go | 45 ++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/command/meta_backend.go b/command/meta_backend.go index c50214116..afcfe533a 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -1002,7 +1002,8 @@ func (m *Meta) backend_C_r_s( } // If the local state is not empty, we need to potentially do a - // state migration to the new backend (with user permission). + // state migration to the new backend (with user permission), unless the + // destination is also "local" if localS := localState.State(); !localS.Empty() { // Perform the migration err = m.backendMigrateState(&backendMigrateOpts{ @@ -1015,12 +1016,27 @@ func (m *Meta) backend_C_r_s( return nil, err } - // We always delete the local state - if err := localState.WriteState(nil); err != nil { - return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + // we usually remove the local state after migration to prevent + // confusion, but adding a default local backend block to the config + // can get us here too. Don't delete our state if the old and new paths + // are the same. + erase := true + if newLocalB, ok := b.(*backendlocal.Local); ok { + if localB, ok := localB.(*backendlocal.Local); ok { + if newLocalB.StatePath == localB.StatePath { + erase = false + } + } } - if err := localState.PersistState(); err != nil { - return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + + if erase { + // We always delete the local state, unless that was our new state too. + if err := localState.WriteState(nil); err != nil { + return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + } + if err := localState.PersistState(); err != nil { + return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + } } } diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index 143759f98..ea0085dba 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -3275,6 +3275,51 @@ func TestMetaBackend_configureWithExtra(t *testing.T) { } } +// when confniguring a default local state, don't delete local state +func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend-empty"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // create our local state + orig := &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{ + "foo": { + Value: "bar", + Type: "string", + }, + }, + }, + }, + } + + err := (&state.LocalState{Path: DefaultStateFilename}).WriteState(orig) + if err != nil { + t.Fatal(err) + } + + m := testMetaBackend(t, nil) + m.forceInitCopy = true + // init the backend + _, err = m.Backend(&BackendOpts{ + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // check that we can read the state + s := testStateRead(t, DefaultStateFilename) + if s.Empty() { + t.Fatal("our state was deleted") + } +} + // move options from config to -backend-config func TestMetaBackend_configToExtra(t *testing.T) { // Create a temporary working directory that is empty From 173bf10e7b2afcb64f127fc2033c1e91a60aea53 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 31 Mar 2017 22:22:12 +0100 Subject: [PATCH 70/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c672c3d3..3717de123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ IMPROVEMENTS: * 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/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] From 0c4c578552d4f4015da8adff6316b0bd13985245 Mon Sep 17 00:00:00 2001 From: Doug Neal Date: Sat, 1 Apr 2017 06:57:34 +0100 Subject: [PATCH 71/91] provider/aws: Implement aws_ses_domain_identity (#13098) * provider/aws: New resource: aws_ses_domain_identity Provide a resource to manage domain identities in SES. Exports the verification_code attribute which can be used to add the TXT record to the domain to complete the domain verification. * provider/aws: Acceptance tests for aws_ses_domain_identity * Resource aws_ses_domain_identity: Documentation update Provide documentation for the new resource type. --- builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_ses_domain_identity.go | 99 +++++++++++++++++ .../resource_aws_ses_domain_identity_test.go | 100 ++++++++++++++++++ .../source/docs/import/importability.html.md | 1 + .../aws/r/ses_domain_identity.html.markdown | 46 ++++++++ website/source/layouts/aws.erb | 4 + 6 files changed, 251 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_ses_domain_identity.go create mode 100644 builtin/providers/aws/resource_aws_ses_domain_identity_test.go create mode 100644 website/source/docs/providers/aws/r/ses_domain_identity.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 0ad581785..e1c7d24ac 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -386,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(), diff --git a/builtin/providers/aws/resource_aws_ses_domain_identity.go b/builtin/providers/aws/resource_aws_ses_domain_identity.go new file mode 100644 index 000000000..7eba7a187 --- /dev/null +++ b/builtin/providers/aws/resource_aws_ses_domain_identity.go @@ -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 +} diff --git a/builtin/providers/aws/resource_aws_ses_domain_identity_test.go b/builtin/providers/aws/resource_aws_ses_domain_identity_test.go new file mode 100644 index 000000000..6119fa123 --- /dev/null +++ b/builtin/providers/aws/resource_aws_ses_domain_identity_test.go @@ -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" +} +` diff --git a/website/source/docs/import/importability.html.md b/website/source/docs/import/importability.html.md index a9bac81e6..933c47a1d 100644 --- a/website/source/docs/import/importability.html.md +++ b/website/source/docs/import/importability.html.md @@ -92,6 +92,7 @@ To make a resource importable, please see the * aws_route_table * aws_s3_bucket * aws_security_group +* aws_ses_domain_identity * aws_ses_receipt_filter * aws_ses_receipt_rule_set * aws_simpledb_domain diff --git a/website/source/docs/providers/aws/r/ses_domain_identity.html.markdown b/website/source/docs/providers/aws/r/ses_domain_identity.html.markdown new file mode 100644 index 000000000..e005160bf --- /dev/null +++ b/website/source/docs/providers/aws/r/ses_domain_identity.html.markdown @@ -0,0 +1,46 @@ +--- +layout: "aws" +page_title: "AWS: ses_domain_identity" +sidebar_current: "docs-aws-resource-ses-domain-identity" +description: |- + Provides an SES domain identity resource +--- + +# aws\_ses\_domain_identity + +Provides an SES domain identity resource + +## Argument Reference + +The following arguments are supported: + +* `domain` - (Required) The domain name to assign to SES + +## Attributes Reference + +The following attributes are exported: + +* `verification_token` - A code which when added to the domain as a TXT record + will signal to SES that the owner of the domain has authorised SES to act on + their behalf. The domain identity will be in state "verification pending" + until this is done. See below for an example of how this might be achieved + when the domain is hosted in Route 53 and managed by Terraform. Find out + more about verifying domains in Amazon SES in the [AWS SES + docs](http://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-domains.html). + +## Example Usage + +``` +resource "aws_ses_domain_identity" "example" { + domain = "example.com" +} + +resource "aws_route53_record" "example_amazonses_verification_record" { + zone_id = "ABCDEFGHIJ123" + name = "_amazonses.example.com" + type = "TXT" + ttl = "600" + records = ["${aws_ses_domain_identity.example.verification_token}"] +} +``` + diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index d3e39f97d..75e5d546e 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -1127,6 +1127,10 @@ aws_ses_active_receipt_rule_set + > + aws_ses_domain_identity + + > aws_ses_receipt_filter From 6667a85cd9d1b764cd3b48a6e1c415fb4a45b02f Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sat, 1 Apr 2017 06:58:27 +0100 Subject: [PATCH 72/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3717de123..0c1e81f63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ FEATURES: * **New Resource:** `aws_lightsail_static_ip` [GH-13175] * **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207] + * **New Resource:** `aws_ses_domain_identity` [GH-13098] * **New Resource:** `kubernetes_secret` [GH-12960] * **New Data Source:** `aws_iam_role` [GH-13213] From b8f6e2a70ae0e741ddf8a3ef69334121bd62ae93 Mon Sep 17 00:00:00 2001 From: Jonathan Camp Date: Sat, 1 Apr 2017 16:39:46 +0200 Subject: [PATCH 73/91] provider/aws: handle aws_lambda_function missing s3 key error (#10960) * ensure NoSuchKey is not a retryable error * Simplify err handling + lower log msg severity --- .../providers/aws/resource_aws_lambda_function.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_lambda_function.go b/builtin/providers/aws/resource_aws_lambda_function.go index 211da109d..4eedd77a3 100644 --- a/builtin/providers/aws/resource_aws_lambda_function.go +++ b/builtin/providers/aws/resource_aws_lambda_function.go @@ -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 From 26c544547710eed3882d53d6d11b7d4c0625c47f Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sat, 1 Apr 2017 15:42:17 +0100 Subject: [PATCH 74/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1e81f63..6e1a57e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ BUG FIXES: * 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/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 4b9ec9c9200d4fe77101a62f94958dea678da3b5 Mon Sep 17 00:00:00 2001 From: Masayuki Morita Date: Sun, 2 Apr 2017 01:31:32 +0900 Subject: [PATCH 75/91] provider/aws: Validate aws_ecs_task_definition.container_definitions (#12161) --- .../aws/resource_aws_ecs_task_definition.go | 10 ++++ .../resource_aws_ecs_task_definition_test.go | 48 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/builtin/providers/aws/resource_aws_ecs_task_definition.go b/builtin/providers/aws/resource_aws_ecs_task_definition.go index 2734afba9..fa082472b 100644 --- a/builtin/providers/aws/resource_aws_ecs_task_definition.go +++ b/builtin/providers/aws/resource_aws_ecs_task_definition.go @@ -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 diff --git a/builtin/providers/aws/resource_aws_ecs_task_definition_test.go b/builtin/providers/aws/resource_aws_ecs_task_definition_test.go index a414130cd..afbf2955f 100644 --- a/builtin/providers/aws/resource_aws_ecs_task_definition_test.go +++ b/builtin/providers/aws/resource_aws_ecs_task_definition_test.go @@ -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 + } +] +` From 9b55ce5385fe0f7a5c0895c41843bb19432e0886 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sat, 1 Apr 2017 17:32:18 +0100 Subject: [PATCH 76/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1a57e87..5261d3ae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ IMPROVEMENTS: * 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/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] From d209dc1a32bb75cb072601c5d96f1e9080f485e7 Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 3 Apr 2017 01:48:45 +0300 Subject: [PATCH 77/91] provider/aws: Fixup AWS db instance acceptance tests with default security group --- builtin/providers/aws/resource_aws_db_instance_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_db_instance_test.go b/builtin/providers/aws/resource_aws_db_instance_test.go index 6c8d451ca..17d3bf6b8 100644 --- a/builtin/providers/aws/resource_aws_db_instance_test.go +++ b/builtin/providers/aws/resource_aws_db_instance_test.go @@ -676,7 +676,6 @@ resource "aws_db_instance" "test" { instance_class = "db.t1.micro" password = "password" username = "root" - security_group_names = ["default"] publicly_accessible = true skip_final_snapshot = true @@ -692,7 +691,6 @@ resource "aws_db_instance" "test" { instance_class = "db.t1.micro" password = "password" username = "root" - security_group_names = ["default"] publicly_accessible = true skip_final_snapshot = true From 2d8f3f257afbe301a5e5af12c7c1f30fc45f0e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20Schmidt?= Date: Mon, 3 Apr 2017 10:38:35 +0200 Subject: [PATCH 78/91] Fix small typo --- website/source/docs/configuration/interpolation.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index b101730a3..709261585 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -322,7 +322,7 @@ The supported built-in functions are: `a_resource_param = ["${split(",", var.CSV_STRING)}"]`. Example: `split(",", module.amod.server_ids)` - * `substr(string, offset, length)` - Extracts a substring from the input string. A negative offset is interpreted as being equivalent to a positive offset measured backwards from the end of the string. A length of `-1` is interpretted as meaning "until the end of the string". + * `substr(string, offset, length)` - Extracts a substring from the input string. A negative offset is interpreted as being equivalent to a positive offset measured backwards from the end of the string. A length of `-1` is interpreted as meaning "until the end of the string". * `timestamp()` - Returns a UTC timestamp string in RFC 3339 format. This string will change with every invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the From b80d3e5701f24e468c004363b7f5d3a9c97a6c82 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 3 Apr 2017 12:11:45 +0300 Subject: [PATCH 79/91] provider/aws: Set stickiness to computed in alb_target_group (#13278) The Default values set by AWS were breaking the AWS ALB Listener Rule tests. The stickiness parameter needed to be set to be Computed: true to remove this discrepancy ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSALBListenerRule_basic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/04/03 01:23:47 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSALBListenerRule_basic -timeout 120m === RUN TestAccAWSALBListenerRule_basic --- PASS: TestAccAWSALBListenerRule_basic (235.36s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 235.397s ``` --- builtin/providers/aws/resource_aws_alb_target_group.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/aws/resource_aws_alb_target_group.go b/builtin/providers/aws/resource_aws_alb_target_group.go index df1a662be..9412198d4 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group.go +++ b/builtin/providers/aws/resource_aws_alb_target_group.go @@ -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{ From ef2d2646a8a9e0c8ea02096d51016e64f080c817 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 3 Apr 2017 12:13:58 +0300 Subject: [PATCH 80/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5261d3ae3..74e010582 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ BUG FIXES: * 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) From fbbfe67bb5fb6584cd28338632d71b9e7174033b Mon Sep 17 00:00:00 2001 From: Adam Hoka Date: Mon, 3 Apr 2017 14:11:25 +0200 Subject: [PATCH 81/91] Update documentation to reflect reality From the code: "records": &schema.Schema{ Type: schema.TypeString, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, Removed: "Use `record` instead. This attribute will be removed in a future version", }, "record": &schema.Schema{ Type: schema.TypeString, Required: true, --- .../docs/providers/azurerm/r/dns_cname_record.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown b/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown index 38931897a..10832dff2 100644 --- a/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown +++ b/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown @@ -28,7 +28,7 @@ resource "azurerm_dns_cname_record" "test" { zone_name = "${azurerm_dns_zone.test.name}" resource_group_name = "${azurerm_resource_group.test.name}" ttl = "300" - records = ["contoso.com"] + record = "contoso.com" } ``` ## Argument Reference @@ -43,7 +43,7 @@ The following arguments are supported: * `TTL` - (Required) The Time To Live (TTL) of the DNS record. -* `records` - (Required) The target of the CNAME. Must be a single value. +* `record` - (Required) The target of the CNAME. * `tags` - (Optional) A mapping of tags to assign to the resource. From efab9e325e7f4510da7cb7735ed43074a4beb355 Mon Sep 17 00:00:00 2001 From: pradeepbhadani Date: Mon, 3 Apr 2017 17:51:20 +0100 Subject: [PATCH 82/91] Fix typo --- .../docs/providers/bitbucket/r/default_reviewers.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown b/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown index 29f4f0dc2..be2333dd7 100644 --- a/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown +++ b/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown @@ -13,7 +13,7 @@ Provides support for setting up default reviewers for your repository. ## Example Usage ``` -# Manage your respository +# Manage your repository resource "bitbucket_default_reviewers" "infrastructure" { owner = "myteam" repository = "terraform-code" From bf8497ed40463204254f4cb9c9c0f24f3803eeba Mon Sep 17 00:00:00 2001 From: pradeepbhadani Date: Mon, 3 Apr 2017 17:52:35 +0100 Subject: [PATCH 83/91] Fix typo --- website/source/docs/providers/bitbucket/r/hook.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/bitbucket/r/hook.html.markdown b/website/source/docs/providers/bitbucket/r/hook.html.markdown index 49e991c58..404759e18 100644 --- a/website/source/docs/providers/bitbucket/r/hook.html.markdown +++ b/website/source/docs/providers/bitbucket/r/hook.html.markdown @@ -15,7 +15,7 @@ This allows you to manage your webhooks on a repository. ## Example Usage ``` -# Manage your respositories hooks +# Manage your repositories hooks resource "bitbucket_hook" "deploy_on_push" { owner = "myteam" repository = "terraform-code" From 40d430ed6670df59544229a9aee3fb7ffdd99758 Mon Sep 17 00:00:00 2001 From: pradeepbhadani Date: Mon, 3 Apr 2017 17:52:50 +0100 Subject: [PATCH 84/91] Fix typo --- .../source/docs/providers/bitbucket/r/repository.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/bitbucket/r/repository.html.markdown b/website/source/docs/providers/bitbucket/r/repository.html.markdown index 7d1572a1a..cc584d9b1 100644 --- a/website/source/docs/providers/bitbucket/r/repository.html.markdown +++ b/website/source/docs/providers/bitbucket/r/repository.html.markdown @@ -16,7 +16,7 @@ private, how to fork the repository and other options. ## Example Usage ``` -# Manage your respository +# Manage your repository resource "bitbucket_repository" "infrastructure" { owner = "myteam" name = "terraform-code" From 75e61606bb74d40667d43292d60baaf30b366aa9 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Mon, 3 Apr 2017 14:10:57 -0400 Subject: [PATCH 85/91] dont dereference pointers on Set --- builtin/providers/aws/data_source_aws_caller_identity.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/data_source_aws_caller_identity.go b/builtin/providers/aws/data_source_aws_caller_identity.go index 95313cea8..756bc9b52 100644 --- a/builtin/providers/aws/data_source_aws_caller_identity.go +++ b/builtin/providers/aws/data_source_aws_caller_identity.go @@ -50,8 +50,8 @@ func dataSourceAwsCallerIdentityRead(d *schema.ResourceData, meta interface{}) e } 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) + d.Set("account_id", res.Account) + d.Set("arn", res.Arn) + d.Set("user_id", res.UserId) return nil } From 83afa7bf88c808d0a34686524a4dbc70d07d6cfa Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Mon, 3 Apr 2017 14:15:01 -0400 Subject: [PATCH 86/91] add bullets to website docs --- .../docs/providers/aws/d/caller_identity.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/docs/providers/aws/d/caller_identity.html.markdown b/website/source/docs/providers/aws/d/caller_identity.html.markdown index 8f4b9974a..fdcd41c65 100644 --- a/website/source/docs/providers/aws/d/caller_identity.html.markdown +++ b/website/source/docs/providers/aws/d/caller_identity.html.markdown @@ -40,6 +40,6 @@ There are no arguments available for this data source. ## Attributes Reference -`account_id` - The AWS Account ID number of the account that owns or contains the calling entity. -`arn` - The AWS ARN associated with the calling entity. -`user_id` - The unique identifier of the calling entity. +* `account_id` - The AWS Account ID number of the account that owns or contains the calling entity. +* `arn` - The AWS ARN associated with the calling entity. +* `user_id` - The unique identifier of the calling entity. From c68675bc4fc4424a6ce9f963a246dd509f6183d2 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Mon, 3 Apr 2017 14:30:11 -0400 Subject: [PATCH 87/91] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74e010582..bcfdf2ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ IMPROVEMENTS: * 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] From 403801cf6836d2b4897a2d6198705d88e1c06d08 Mon Sep 17 00:00:00 2001 From: Stephen Cross Date: Wed, 29 Mar 2017 16:30:08 +0000 Subject: [PATCH 88/91] Initial Oracle Compute Cloud provider --- builtin/providers/oracleopc/config.go | 47 +++ builtin/providers/oracleopc/provider.go | 75 +++++ builtin/providers/oracleopc/provider_test.go | 61 ++++ .../providers/oracleopc/resource_instance.go | 306 ++++++++++++++++++ .../oracleopc/resource_instance_test.go | 156 +++++++++ .../oracleopc/resource_ip_association.go | 103 ++++++ .../oracleopc/resource_ip_association_test.go | 74 +++++ .../oracleopc/resource_ip_reservation.go | 122 +++++++ .../resource_security_application.go | 124 +++++++ .../resource_security_association.go | 103 ++++++ .../resource_security_association_test.go | 75 +++++ .../oracleopc/resource_security_ip_list.go | 117 +++++++ .../oracleopc/resource_security_list.go | 119 +++++++ .../oracleopc/resource_security_rule.go | 143 ++++++++ .../oracleopc/resource_security_rule_test.go | 85 +++++ .../providers/oracleopc/resource_ssh_key.go | 117 +++++++ .../oracleopc/resource_storage_volume.go | 301 +++++++++++++++++ .../oracleopc/resource_storage_volume_test.go | 70 ++++ 18 files changed, 2198 insertions(+) create mode 100644 builtin/providers/oracleopc/config.go create mode 100644 builtin/providers/oracleopc/provider.go create mode 100644 builtin/providers/oracleopc/provider_test.go create mode 100644 builtin/providers/oracleopc/resource_instance.go create mode 100644 builtin/providers/oracleopc/resource_instance_test.go create mode 100644 builtin/providers/oracleopc/resource_ip_association.go create mode 100644 builtin/providers/oracleopc/resource_ip_association_test.go create mode 100644 builtin/providers/oracleopc/resource_ip_reservation.go create mode 100644 builtin/providers/oracleopc/resource_security_application.go create mode 100644 builtin/providers/oracleopc/resource_security_association.go create mode 100644 builtin/providers/oracleopc/resource_security_association_test.go create mode 100644 builtin/providers/oracleopc/resource_security_ip_list.go create mode 100644 builtin/providers/oracleopc/resource_security_list.go create mode 100644 builtin/providers/oracleopc/resource_security_rule.go create mode 100644 builtin/providers/oracleopc/resource_security_rule_test.go create mode 100644 builtin/providers/oracleopc/resource_ssh_key.go create mode 100644 builtin/providers/oracleopc/resource_storage_volume.go create mode 100644 builtin/providers/oracleopc/resource_storage_volume_test.go diff --git a/builtin/providers/oracleopc/config.go b/builtin/providers/oracleopc/config.go new file mode 100644 index 000000000..fbae3b5d5 --- /dev/null +++ b/builtin/providers/oracleopc/config.go @@ -0,0 +1,47 @@ +package opc + +import ( + "fmt" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "net/url" +) + +type Config struct { + User string + Password string + IdentityDomain string + Endpoint string + MaxRetryTimeout int +} + +type storageAttachment struct { + index int + instanceName *compute.InstanceName +} + +type OPCClient struct { + *compute.AuthenticatedClient + MaxRetryTimeout int + storageAttachmentsByVolumeCache map[string][]storageAttachment +} + +func (c *Config) Client() (*OPCClient, error) { + u, err := url.ParseRequestURI(c.Endpoint) + if err != nil { + return nil, fmt.Errorf("Invalid endpoint URI: %s", err) + } + + client := compute.NewComputeClient(c.IdentityDomain, c.User, c.Password, u) + authenticatedClient, err := client.Authenticate() + if err != nil { + return nil, fmt.Errorf("Authentication failed: %s", err) + } + + opcClient := &OPCClient{ + AuthenticatedClient: authenticatedClient, + MaxRetryTimeout: c.MaxRetryTimeout, + storageAttachmentsByVolumeCache: make(map[string][]storageAttachment), + } + + return opcClient, nil +} diff --git a/builtin/providers/oracleopc/provider.go b/builtin/providers/oracleopc/provider.go new file mode 100644 index 000000000..a6d0d3fb5 --- /dev/null +++ b/builtin/providers/oracleopc/provider.go @@ -0,0 +1,75 @@ +package opc + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +// Provider returns a terraform.ResourceProvider. +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "user": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OPC_USERNAME", nil), + Description: "The user name for OPC API operations.", + }, + + "password": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OPC_PASSWORD", nil), + Description: "The user password for OPC API operations.", + }, + + "identityDomain": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OPC_IDENTITY_DOMAIN", nil), + Description: "The OPC identity domain for API operations", + }, + + "endpoint": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OPC_ENDPOINT", nil), + Description: "The HTTP endpoint for OPC API operations.", + }, + + "maxRetryTimeout": &schema.Schema{ + 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)", + }, + }, + + ResourcesMap: map[string]*schema.Resource{ + "opc_compute_storage_volume": resourceStorageVolume(), + "opc_compute_instance": resourceInstance(), + "opc_compute_ssh_key": resourceSSHKey(), + "opc_compute_security_application": resourceSecurityApplication(), + "opc_compute_security_list": resourceSecurityList(), + "opc_compute_security_ip_list": resourceSecurityIPList(), + "opc_compute_ip_reservation": resourceIPReservation(), + "opc_compute_ip_association": resourceIPAssociation(), + "opc_compute_security_rule": resourceSecurityRule(), + "opc_compute_security_association": resourceSecurityAssociation(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := Config{ + User: d.Get("user").(string), + Password: d.Get("password").(string), + IdentityDomain: d.Get("identityDomain").(string), + Endpoint: d.Get("endpoint").(string), + MaxRetryTimeout: d.Get("maxRetryTimeout").(int), + } + + return config.Client() +} diff --git a/builtin/providers/oracleopc/provider_test.go b/builtin/providers/oracleopc/provider_test.go new file mode 100644 index 000000000..c60076b06 --- /dev/null +++ b/builtin/providers/oracleopc/provider_test.go @@ -0,0 +1,61 @@ +package opc + +import ( + "os" + "testing" + + "fmt" + "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 { + *OPCClient + *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{ + OPCClient: testAccProvider.Meta().(*OPCClient), + InstanceState: rs.Primary, + } + + return f(state) + } +} diff --git a/builtin/providers/oracleopc/resource_instance.go b/builtin/providers/oracleopc/resource_instance.go new file mode 100644 index 000000000..70f3b99c8 --- /dev/null +++ b/builtin/providers/oracleopc/resource_instance.go @@ -0,0 +1,306 @@ +package opc + +import ( + "encoding/json" + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceInstanceCreate, + Read: resourceInstanceRead, + Delete: resourceInstanceDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "shape": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "imageList": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "label": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "ip": { + Type: schema.TypeString, + Optional: false, + Computed: true, + }, + + "opcId": { + Type: schema.TypeString, + Optional: false, + Computed: true, + }, + + "sshKeys": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "attributes": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "vcable": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "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, + }, + }, + }, + }, + + "bootOrder": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeInt}, + }, + }, + } +} + +func getAttrs(d *schema.ResourceData) (*map[string]interface{}, error) { + var attrs map[string]interface{} + + attrString := d.Get("attributes").(string) + if attrString == "" { + return &attrs, nil + } + if err := json.Unmarshal([]byte(attrString), &attrs); err != nil { + return &attrs, fmt.Errorf("Cannot parse '%s' as json", attrString) + } + return &attrs, nil +} + +func resourceInstanceCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d.State()) + + client := meta.(*OPCClient).Instances() + name := d.Get("name").(string) + shape := d.Get("shape").(string) + imageList := d.Get("imageList").(string) + label := d.Get("label").(string) + storage := getStorageAttachments(d) + sshKeys := getSSHKeys(d) + bootOrder := getBootOrder(d) + + attrs, err := getAttrs(d) + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating instance with name %s, shape %s, imageList %s, storage %s, bootOrder %s, label %s, sshKeys %s, attrs %#v", + name, shape, imageList, storage, bootOrder, label, sshKeys, attrs) + + id, err := client.LaunchInstance(name, label, shape, imageList, storage, bootOrder, sshKeys, *attrs) + if err != nil { + return fmt.Errorf("Error creating instance %s: %s", name, err) + } + + log.Printf("[DEBUG] Waiting for instance %s to come online", id.String()) + info, err := client.WaitForInstanceRunning(id, meta.(*OPCClient).MaxRetryTimeout) + if err != nil { + return fmt.Errorf("Error waiting for instance %s to come online: %s", id, err) + } + + log.Printf("[DEBUG] Created instance %s: %#v", id, info) + + attachStorage( + &compute.InstanceName{ + Name: info.Name, + ID: info.ID, + }, + d, meta) + + d.SetId(info.Name) + updateInstanceResourceData(d, info) + return nil +} + +func attachStorage(name *compute.InstanceName, d *schema.ResourceData, meta interface{}) error { + storageClient := meta.(*OPCClient).StorageAttachments() + storage := d.Get("storage").(*schema.Set) + updatedStorage := schema.NewSet(storage.F, []interface{}{}) + + for _, i := range storage.List() { + attrs := i.(map[string]interface{}) + attachmentInfo, err := storageClient.CreateStorageAttachment( + attrs["index"].(int), + name, + attrs["volume"].(string)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Waiting for storage attachment %#v to come online", attachmentInfo) + storageClient.WaitForStorageAttachmentCreated(attachmentInfo.Name, meta.(*OPCClient).MaxRetryTimeout) + log.Printf("[DEBUG] Storage attachment %s: %s-%s created", + attachmentInfo.Name, attachmentInfo.InstanceName, attachmentInfo.StorageVolumeName) + attrs["name"] = attachmentInfo.Name + updatedStorage.Add(attrs) + } + + d.Set("storage", updatedStorage) + return nil +} + +func getSSHKeys(d *schema.ResourceData) []string { + sshKeys := []string{} + for _, i := range d.Get("sshKeys").([]interface{}) { + sshKeys = append(sshKeys, i.(string)) + } + return sshKeys +} + +func getBootOrder(d *schema.ResourceData) []int { + bootOrder := []int{} + for _, i := range d.Get("bootOrder").([]interface{}) { + bootOrder = append(bootOrder, i.(int)) + } + return bootOrder +} + +func getStorageAttachments(d *schema.ResourceData) []compute.LaunchPlanStorageAttachmentSpec { + storageAttachments := []compute.LaunchPlanStorageAttachmentSpec{} + storage := d.Get("storage").(*schema.Set) + for _, i := range storage.List() { + attrs := i.(map[string]interface{}) + storageAttachments = append(storageAttachments, compute.LaunchPlanStorageAttachmentSpec{ + Index: attrs["index"].(int), + Volume: attrs["volume"].(string), + }) + } + return storageAttachments +} + +func updateInstanceResourceData(d *schema.ResourceData, info *compute.InstanceInfo) error { + d.Set("name", info.Name) + d.Set("opcId", info.ID) + d.Set("imageList", info.ImageList) + d.Set("bootOrder", info.BootOrder) + d.Set("sshKeys", info.SSHKeys) + d.Set("label", info.Label) + d.Set("ip", info.IPAddress) + d.Set("vcable", info.VCableID) + + return nil +} + +func resourceInstanceRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d.State()) + client := meta.(*OPCClient).Instances() + name := d.Get("name").(string) + instanceName := &compute.InstanceName{ + Name: name, + ID: d.Get("opcId").(string), + } + + log.Printf("[DEBUG] Reading state of instance %s", instanceName) + result, err := client.GetInstance(instanceName) + if err != nil { + // Instance doesn't exist + if compute.WasNotFoundError(err) { + log.Printf("[DEBUG] Instance %s not found", instanceName) + d.SetId("") + return nil + } + return fmt.Errorf("Error reading instance %s: %s", instanceName, err) + } + + log.Printf("[DEBUG] Read state of instance %s: %#v", instanceName, result) + + attachments, err := meta.(*OPCClient).StorageAttachments().GetStorageAttachmentsForInstance(instanceName) + if err != nil { + return fmt.Errorf("Error reading storage attachments for instance %s: %s", instanceName, err) + } + updateInstanceResourceData(d, result) + updateAttachmentResourceData(d, attachments) + return nil +} + +func updateAttachmentResourceData(d *schema.ResourceData, attachments *[]compute.StorageAttachmentInfo) { + attachmentSet := schema.NewSet(d.Get("storage").(*schema.Set).F, []interface{}{}) + for _, attachment := range *attachments { + properties := map[string]interface{}{ + "index": attachment.Index, + "volume": attachment.StorageVolumeName, + "name": attachment.Name, + } + attachmentSet.Add(properties) + } + d.Set("storage", attachmentSet) +} + +func resourceInstanceDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d.State()) + client := meta.(*OPCClient).Instances() + name := d.Get("name").(string) + + instanceName := &compute.InstanceName{ + Name: name, + ID: d.Get("opcId").(string), + } + + log.Printf("[DEBUG] Deleting instance %s", instanceName) + if err := client.DeleteInstance(instanceName); err != nil { + return fmt.Errorf("Error deleting instance %s: %s", instanceName, err) + } + if err := client.WaitForInstanceDeleted(instanceName, meta.(*OPCClient).MaxRetryTimeout); err != nil { + return fmt.Errorf("Error deleting instance %s: %s", instanceName, err) + } + + for _, attachment := range d.Get("storage").(*schema.Set).List() { + name := attachment.(map[string]interface{})["name"].(string) + log.Printf("[DEBUG] Deleting storage attachment %s", name) + client.StorageAttachments().DeleteStorageAttachment(name) + client.StorageAttachments().WaitForStorageAttachmentDeleted(name, meta.(*OPCClient).MaxRetryTimeout) + } + + return nil +} diff --git a/builtin/providers/oracleopc/resource_instance_test.go b/builtin/providers/oracleopc/resource_instance_test.go new file mode 100644 index 000000000..6f386af84 --- /dev/null +++ b/builtin/providers/oracleopc/resource_instance_test.go @@ -0,0 +1,156 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "testing" +) + +func TestAccOPCInstance_Basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: opcResourceCheck( + instanceResourceName, + testAccCheckInstanceDestroyed), + Steps: []resource.TestStep{ + { + Config: testAccInstanceBasic, + Check: resource.ComposeTestCheckFunc( + opcResourceCheck( + instanceResourceName, + testAccCheckInstanceExists), + opcResourceCheck( + keyResourceName, + testAccCheckSSHKeyExists), + ), + }, + { + Config: modifySSHKey, + Check: resource.ComposeTestCheckFunc( + opcResourceCheck( + instanceResourceName, + testAccCheckInstanceExists), + opcResourceCheck( + keyResourceName, + testAccCheckSSHKeyUpdated), + ), + }, + }, + }) +} + +func testAccCheckInstanceExists(state *OPCResourceState) error { + instanceName := getInstanceName(state) + + if _, err := state.Instances().GetInstance(instanceName); err != nil { + return fmt.Errorf("Error retrieving state of instance %s: %s", instanceName, err) + } + + return nil +} + +func testAccCheckSSHKeyExists(state *OPCResourceState) error { + keyName := state.Attributes["name"] + + if _, err := state.SSHKeys().GetSSHKey(keyName); err != nil { + return fmt.Errorf("Error retrieving state of key %s: %s", keyName, err) + } + + return nil +} + +func testAccCheckSSHKeyUpdated(state *OPCResourceState) error { + keyName := state.Attributes["name"] + info, err := state.SSHKeys().GetSSHKey(keyName) + if err != nil { + return err + } + if info.Key != updatedKey { + return fmt.Errorf("Expected key\n\t%s\nbut was\n\t%s", updatedKey, info.Key) + } + return nil +} + +func getInstanceName(rs *OPCResourceState) *compute.InstanceName { + return &compute.InstanceName{ + Name: rs.Attributes["name"], + ID: rs.Attributes["opcId"], + } +} + +func testAccCheckInstanceDestroyed(state *OPCResourceState) error { + instanceName := getInstanceName(state) + if info, err := state.Instances().GetInstance(instanceName); err == nil { + return fmt.Errorf("Instance %s still exists: %#v", instanceName, info) + } + + return nil +} + +const instanceName = "test_instance" +const keyName = "test_key" + +var instanceResourceName = fmt.Sprintf("opc_compute_instance.%s", instanceName) +var keyResourceName = fmt.Sprintf("opc_compute_ssh_key.%s", keyName) + +const originalKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqw6JwbjIkZEr5UcMojtxhk6Zum39NOihHNXEvRWDt5WssX8TH/ghpv3D25K1pJkf+wfAi17HwEmYwPMEyEHENS443v6RZbXvzCkUWzkJzq7Zvbdqld038km31La2QUoMMp1KL5zk1nM65xCeQDVcR/h++03EScB2CuzTpAV6khMdfgOJgxm361kfrDVRwc1HQrAOpOnzkpPfwqBrYWqN1UnKvuO77Wk8z5LBe03EPNru3bLE3s3qHI9hjO0gXMiVUi0KyNxdWfDO8esqQlKavHAeePyrRA55YF8kBB5dEl4tVNOqpY/8TRnGN1mOe0LWxa8Ytz1wbyS49knsNVTel" +const updatedKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDHvb/2OSemgzUYLNW1/T3u33r7sZy1qbWtgVWiREH4gS5TVmDVPuvN1MFLdNqiWQA53gK8Gp24jtjNm9ftcPhicv81HVWJTB69C0sJGEfF0l4mgbemJLH3i37Mb6SdWJcGof9qHVDADPgiC8jIBVUhdiJSeq4fUJ3NQA2eUExBkRglQWairkNzPNA0mi3GL9KDGnoBnSCAXNGoKgDgIOqW0dYFP6oHyGWkF7V+/TME9aIQvmMpHjVzl7brZ/wED2t5vTJxxbgogHEmWnfs7p8EP5IsN6Vnjd0VNIt1tu3TduS8kH5npkPqZz8oIP93Ypxn0l7ZNEl9MahbhPj3gJ1YY7Cygrlt1VLC1ibBbOgIS2Lj6vGG/Yjkqs3Vw6qrmTRlsJ9c6bZO2xq0xzV11XQHvjPegBOClF6AztEe1jKU/RUFnzjIF8lUmM63fTaXuVkNERkTSE3E9XL3Uq6eqYdef7wHFFhCMSGotp3ANAb30kflysA9ID0b3o5QU2tB8OBxBicXQy11lh+u204YJuvIzeTXo+JAad5TWFlJcsUlbPFppLQdhUpoWaJouBGJV36DJb9R34i9T8Ze5tnJUQgPmMkERyPvb/+v5j3s2hs1A9WO6/MqmZd70gudsX/1bqWT898vCCOdM+CspNVY7nHVUtde7C6BrHzphr/C1YBXHw==" + +var testAccInstanceBasic = fmt.Sprintf(` +resource "opc_compute_instance" "%s" { + name = "test" + label = "test" + shape = "oc3" + imageList = "/oracle/public/oel_6.4_2GB_v1" + sshKeys = ["${opc_compute_ssh_key.test_key.name}"] + attributes = "{\"foo\": \"bar\"}" + storage = { + index = 1 + volume = "${opc_compute_storage_volume.test_volume.name}" + } +} + +resource "opc_compute_storage_volume" "test_volume" { + size = "3g" + description = "My volume" + name = "test_volume_b" + tags = ["foo", "bar", "baz"] +} + +resource "opc_compute_ssh_key" "%s" { + name = "test-key" + key = "%s" + enabled = true +} +`, instanceName, keyName, originalKey) + +var modifySSHKey = fmt.Sprintf(` +resource "opc_compute_instance" "%s" { + name = "test" + label = "test" + shape = "oc3" + imageList = "/oracle/public/oel_6.4_2GB_v1" + sshKeys = ["${opc_compute_ssh_key.test_key.name}"] + attributes = "{\"foo\": \"bar\"}" + storage = { + index = 1 + volume = "${opc_compute_storage_volume.test_volume.name}" + } +} + +resource "opc_compute_storage_volume" "test_volume" { + size = "3g" + description = "My volume" + name = "test_volume_b" + tags = ["foo", "bar", "baz"] +} + +resource "opc_compute_ssh_key" "%s" { + name = "test-key" + key = "%s" + enabled = true +} +`, instanceName, keyName, updatedKey) diff --git a/builtin/providers/oracleopc/resource_ip_association.go b/builtin/providers/oracleopc/resource_ip_association.go new file mode 100644 index 000000000..84df10ba8 --- /dev/null +++ b/builtin/providers/oracleopc/resource_ip_association.go @@ -0,0 +1,103 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceIPAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceIPAssociationCreate, + Read: resourceIPAssociationRead, + Delete: resourceIPAssociationDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "vcable": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "parentpool": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceIPAssociationCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + vcable, parentpool := getIPAssociationResourceData(d) + + log.Printf("[DEBUG] Creating ip association between vcable %s and parent pool %s", + vcable, parentpool) + + client := meta.(*OPCClient).IPAssociations() + info, err := client.CreateIPAssociation(vcable, parentpool) + 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) + updateIPAssociationResourceData(d, info) + return nil +} + +func updateIPAssociationResourceData(d *schema.ResourceData, info *compute.IPAssociationInfo) { + d.Set("name", info.Name) + d.Set("parentpool", info.ParentPool) + d.Set("vcable", info.VCable) +} + +func resourceIPAssociationRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).IPAssociations() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of ip association %s", name) + result, err := client.GetIPAssociation(name) + 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) + } + + log.Printf("[DEBUG] Read state of ip association %s: %#v", name, result) + updateIPAssociationResourceData(d, result) + return nil +} + +func getIPAssociationResourceData(d *schema.ResourceData) (string, string) { + return d.Get("vcable").(string), d.Get("parentpool").(string) +} + +func resourceIPAssociationDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).IPAssociations() + name := d.Get("name").(string) + + vcable, parentpool := getIPAssociationResourceData(d) + log.Printf("[DEBUG] Deleting ip association %s between vcable %s and parent pool %s", + name, vcable, parentpool) + + if err := client.DeleteIPAssociation(name); err != nil { + return fmt.Errorf("Error deleting ip association %s between vcable %s and parent pool %s: %s", + name, vcable, parentpool, err) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_ip_association_test.go b/builtin/providers/oracleopc/resource_ip_association_test.go new file mode 100644 index 000000000..44f48474f --- /dev/null +++ b/builtin/providers/oracleopc/resource_ip_association_test.go @@ -0,0 +1,74 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccOPCResourceIPAssociation_Basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: opcResourceCheck( + ipAssociationResourceName, + testAccCheckIPAssociationDestroyed), + Steps: []resource.TestStep{ + { + Config: testAccIPAssociationBasic, + Check: resource.ComposeTestCheckFunc( + opcResourceCheck( + ipAssociationResourceName, + testAccCheckIPAssociationExists), + ), + }, + }, + }) +} + +func testAccCheckIPAssociationExists(state *OPCResourceState) error { + associationName := getIPAssociationName(state) + + if _, err := state.IPAssociations().GetIPAssociation(associationName); err != nil { + return fmt.Errorf("Error retrieving state of ip assocation %s: %s", associationName, err) + } + + return nil +} + +func getIPAssociationName(rs *OPCResourceState) string { + return rs.Attributes["name"] +} + +func testAccCheckIPAssociationDestroyed(state *OPCResourceState) error { + associationName := getAssociationName(state) + if info, err := state.IPAssociations().GetIPAssociation(associationName); err == nil { + return fmt.Errorf("IP association %s still exists: %#v", associationName, info) + } + + return nil +} + +const ipAssociationName = "test_ip_association" + +var ipAssociationResourceName = fmt.Sprintf("opc_compute_ip_association.%s", ipAssociationName) + +var testAccIPAssociationBasic = fmt.Sprintf(` +resource "opc_compute_ip_reservation" "reservation1" { + parentpool = "/oracle/public/ippool" + permanent = true +} + +resource "opc_compute_ip_association" "%s" { + vcable = "${opc_compute_instance.test-instance1.vcable}" + parentpool = "ipreservation:${opc_compute_ip_reservation.reservation1.name}" +} + +resource "opc_compute_instance" "test-instance1" { + name = "test" + label = "test" + shape = "oc3" + imageList = "/oracle/public/oel_6.4_2GB_v1" +} +`, ipAssociationName) diff --git a/builtin/providers/oracleopc/resource_ip_reservation.go b/builtin/providers/oracleopc/resource_ip_reservation.go new file mode 100644 index 000000000..fa25679d2 --- /dev/null +++ b/builtin/providers/oracleopc/resource_ip_reservation.go @@ -0,0 +1,122 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceIPReservation() *schema.Resource { + return &schema.Resource{ + Create: resourceIPReservationCreate, + Read: resourceIPReservationRead, + Delete: resourceIPReservationDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "permanent": &schema.Schema{ + Type: schema.TypeBool, + Required: true, + ForceNew: true, + }, + + "parentpool": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "tags": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "ip": &schema.Schema{ + Type: schema.TypeString, + Optional: false, + Computed: true, + }, + }, + } +} + +func resourceIPReservationCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + parentpool, permanent, tags := getIPReservationResourceData(d) + + log.Printf("[DEBUG] Creating ip reservation from parentpool %s with tags=%s", + parentpool, tags) + + client := meta.(*OPCClient).IPReservations() + info, err := client.CreateIPReservation(parentpool, permanent, tags) + if err != nil { + return fmt.Errorf("Error creating ip reservation from parentpool %s with tags=%s: %s", + parentpool, tags, err) + } + + d.SetId(info.Name) + updateIPReservationResourceData(d, info) + return nil +} + +func updateIPReservationResourceData(d *schema.ResourceData, info *compute.IPReservationInfo) { + d.Set("name", info.Name) + d.Set("parentpool", info.ParentPool) + d.Set("permanent", info.Permanent) + d.Set("tags", info.Tags) + d.Set("ip", info.IP) +} + +func resourceIPReservationRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).IPReservations() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of ip reservation %s", name) + result, err := client.GetIPReservation(name) + if err != nil { + // IP Reservation does not exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading ip reservation %s: %s", name, err) + } + + log.Printf("[DEBUG] Read state of ip reservation %s: %#v", name, result) + updateIPReservationResourceData(d, result) + return nil +} + +func getIPReservationResourceData(d *schema.ResourceData) (string, bool, []string) { + tagdata := d.Get("tags").([]interface{}) + tags := make([]string, len(tagdata)) + for i, tag := range tagdata { + tags[i] = tag.(string) + } + return d.Get("parentpool").(string), + d.Get("permanent").(bool), + tags +} + +func resourceIPReservationDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).IPReservations() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting ip reservation %s", name) + + if err := client.DeleteIPReservation(name); err != nil { + return fmt.Errorf("Error deleting ip reservation %s", name) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_security_application.go b/builtin/providers/oracleopc/resource_security_application.go new file mode 100644 index 000000000..b7205754c --- /dev/null +++ b/builtin/providers/oracleopc/resource_security_application.go @@ -0,0 +1,124 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceSecurityApplication() *schema.Resource { + return &schema.Resource{ + Create: resourceSecurityApplicationCreate, + Read: resourceSecurityApplicationRead, + Delete: resourceSecurityApplicationDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "dport": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "icmptype": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "icmpcode": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceSecurityApplicationCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + name, protocol, dport, icmptype, icmpcode, description := getSecurityApplicationResourceData(d) + + log.Printf("[DEBUG] Creating security application %s", name) + + client := meta.(*OPCClient).SecurityApplications() + info, err := client.CreateSecurityApplication(name, protocol, dport, icmptype, icmpcode, description) + if err != nil { + return fmt.Errorf("Error creating security application %s: %s", name, err) + } + + d.SetId(info.Name) + updateSecurityApplicationResourceData(d, info) + return nil +} + +func updateSecurityApplicationResourceData(d *schema.ResourceData, info *compute.SecurityApplicationInfo) { + d.Set("name", info.Name) + d.Set("protocol", info.Protocol) + d.Set("dport", info.DPort) + d.Set("icmptype", info.ICMPType) + d.Set("icmpcode", info.ICMPCode) + d.Set("description", info.Description) +} + +func resourceSecurityApplicationRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityApplications() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of security application %s", name) + result, err := client.GetSecurityApplication(name) + if err != nil { + // Security Application does not exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading security application %s: %s", name, err) + } + + log.Printf("[DEBUG] Read state of security application %s: %#v", name, result) + updateSecurityApplicationResourceData(d, result) + return nil +} + +func getSecurityApplicationResourceData(d *schema.ResourceData) (string, string, string, string, string, string) { + return d.Get("name").(string), + d.Get("protocol").(string), + d.Get("dport").(string), + d.Get("icmptype").(string), + d.Get("icmpcode").(string), + d.Get("description").(string) +} + +func resourceSecurityApplicationDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityApplications() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting security application %s", name) + + if err := client.DeleteSecurityApplication(name); err != nil { + return fmt.Errorf("Error deleting security application %s: %s", name, err) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_security_association.go b/builtin/providers/oracleopc/resource_security_association.go new file mode 100644 index 000000000..15a912657 --- /dev/null +++ b/builtin/providers/oracleopc/resource_security_association.go @@ -0,0 +1,103 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceSecurityAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceSecurityAssociationCreate, + Read: resourceSecurityAssociationRead, + Delete: resourceSecurityAssociationDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "vcable": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "seclist": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceSecurityAssociationCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + vcable, seclist := getSecurityAssociationResourceData(d) + + log.Printf("[DEBUG] Creating security association between vcable %s and security list %s", + vcable, seclist) + + client := meta.(*OPCClient).SecurityAssociations() + info, err := client.CreateSecurityAssociation(vcable, seclist) + if err != nil { + return fmt.Errorf("Error creating security association between vcable %s and security list %s: %s", + vcable, seclist, err) + } + + d.SetId(info.Name) + updateSecurityAssociationResourceData(d, info) + return nil +} + +func updateSecurityAssociationResourceData(d *schema.ResourceData, info *compute.SecurityAssociationInfo) { + d.Set("name", info.Name) + d.Set("seclist", info.SecList) + d.Set("vcable", info.VCable) +} + +func resourceSecurityAssociationRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityAssociations() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of security association %s", name) + result, err := client.GetSecurityAssociation(name) + if err != nil { + // Security Association does not exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading security association %s: %s", name, err) + } + + log.Printf("[DEBUG] Read state of security association %s: %#v", name, result) + updateSecurityAssociationResourceData(d, result) + return nil +} + +func getSecurityAssociationResourceData(d *schema.ResourceData) (string, string) { + return d.Get("vcable").(string), d.Get("seclist").(string) +} + +func resourceSecurityAssociationDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityAssociations() + name := d.Get("name").(string) + + vcable, seclist := getSecurityAssociationResourceData(d) + log.Printf("[DEBUG] Deleting security association %s between vcable %s and security list %s", + name, vcable, seclist) + + if err := client.DeleteSecurityAssociation(name); err != nil { + return fmt.Errorf("Error deleting security association %s between vcable %s and security list %s: %s", + name, vcable, seclist, err) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_security_association_test.go b/builtin/providers/oracleopc/resource_security_association_test.go new file mode 100644 index 000000000..604ef64cb --- /dev/null +++ b/builtin/providers/oracleopc/resource_security_association_test.go @@ -0,0 +1,75 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccOPCResourceSecurityAssociation_Basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: opcResourceCheck( + associationResourceName, + testAccCheckAssociationDestroyed), + Steps: []resource.TestStep{ + { + Config: testAccSecurityAssociationBasic, + Check: resource.ComposeTestCheckFunc( + opcResourceCheck( + associationResourceName, + testAccCheckAssociationExists), + ), + }, + }, + }) +} + +func testAccCheckAssociationExists(state *OPCResourceState) error { + associationName := getAssociationName(state) + + if _, err := state.SecurityAssociations().GetSecurityAssociation(associationName); err != nil { + return fmt.Errorf("Error retrieving state of security assocation %s: %s", associationName, err) + } + + return nil +} + +func getAssociationName(rs *OPCResourceState) string { + return rs.Attributes["name"] +} + +func testAccCheckAssociationDestroyed(state *OPCResourceState) error { + associationName := getAssociationName(state) + if info, err := state.SecurityAssociations().GetSecurityAssociation(associationName); err == nil { + return fmt.Errorf("Association %s still exists: %#v", associationName, info) + } + + return nil +} + +const associationName = "test_rule" + +var associationResourceName = fmt.Sprintf("opc_compute_security_association.%s", associationName) + +var testAccSecurityAssociationBasic = fmt.Sprintf(` +resource "opc_compute_security_list" "sec-list1" { + name = "sec-list-1" + policy = "PERMIT" + outbound_cidr_policy = "DENY" +} + +resource "opc_compute_security_association" "%s" { + vcable = "${opc_compute_instance.test-instance1.vcable}" + seclist = "${opc_compute_security_list.sec-list1.name}" +} + +resource "opc_compute_instance" "test-instance1" { + name = "test" + label = "test" + shape = "oc3" + imageList = "/oracle/public/oel_6.4_2GB_v1" +} +`, ruleName) diff --git a/builtin/providers/oracleopc/resource_security_ip_list.go b/builtin/providers/oracleopc/resource_security_ip_list.go new file mode 100644 index 000000000..6a3e66b28 --- /dev/null +++ b/builtin/providers/oracleopc/resource_security_ip_list.go @@ -0,0 +1,117 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceSecurityIPList() *schema.Resource { + return &schema.Resource{ + Create: resourceSecurityIPListCreate, + Read: resourceSecurityIPListRead, + Update: resourceSecurityIPListUpdate, + Delete: resourceSecurityIPListDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "ip_entries": &schema.Schema{ + Type: schema.TypeList, + Required: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func resourceSecurityIPListCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + name, ipEntries := getSecurityIPListResourceData(d) + + log.Printf("[DEBUG] Creating security IP list with name %s, entries %s", + name, ipEntries) + + client := meta.(*OPCClient).SecurityIPLists() + info, err := client.CreateSecurityIPList(name, ipEntries) + if err != nil { + return fmt.Errorf("Error creating security IP list %s: %s", name, err) + } + + d.SetId(info.Name) + updateSecurityIPListResourceData(d, info) + return nil +} + +func updateSecurityIPListResourceData(d *schema.ResourceData, info *compute.SecurityIPListInfo) { + d.Set("name", info.Name) + d.Set("entries", info.SecIPEntries) +} + +func resourceSecurityIPListRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityIPLists() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of security IP list %s", name) + result, err := client.GetSecurityIPList(name) + if err != nil { + // Security IP List does not exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading security IP list %s: %s", name, err) + } + + log.Printf("[DEBUG] Read state of security IP list %s: %#v", name, result) + updateSecurityIPListResourceData(d, result) + return nil +} + +func getSecurityIPListResourceData(d *schema.ResourceData) (string, []string) { + name := d.Get("name").(string) + ipEntries := d.Get("ip_entries").([]interface{}) + ipEntryStrings := []string{} + for _, entry := range ipEntries { + ipEntryStrings = append(ipEntryStrings, entry.(string)) + } + return name, ipEntryStrings +} + +func resourceSecurityIPListUpdate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + client := meta.(*OPCClient).SecurityIPLists() + name, entries := getSecurityIPListResourceData(d) + + log.Printf("[DEBUG] Updating security IP list %s with ip entries %s", + name, entries) + + info, err := client.UpdateSecurityIPList(name, entries) + if err != nil { + return fmt.Errorf("Error updating security IP list %s: %s", name, err) + } + + updateSecurityIPListResourceData(d, info) + return nil +} + +func resourceSecurityIPListDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityIPLists() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting security IP list %s", name) + if err := client.DeleteSecurityIPList(name); err != nil { + return fmt.Errorf("Error deleting security IP list %s: %s", name, err) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_security_list.go b/builtin/providers/oracleopc/resource_security_list.go new file mode 100644 index 000000000..eea11bbb1 --- /dev/null +++ b/builtin/providers/oracleopc/resource_security_list.go @@ -0,0 +1,119 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceSecurityList() *schema.Resource { + return &schema.Resource{ + Create: resourceSecurityListCreate, + Read: resourceSecurityListRead, + Update: resourceSecurityListUpdate, + Delete: resourceSecurityListDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "outbound_cidr_policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceSecurityListCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + name, policy, outboundCIDRPolicy := getSecurityListResourceData(d) + + log.Printf("[DEBUG] Creating security list with name %s, policy %s, outbound CIDR policy %s", + name, policy, outboundCIDRPolicy) + + client := meta.(*OPCClient).SecurityLists() + info, err := client.CreateSecurityList(name, policy, outboundCIDRPolicy) + if err != nil { + return fmt.Errorf("Error creating security list %s: %s", name, err) + } + + d.SetId(info.Name) + updateSecurityListResourceData(d, info) + return nil +} + +func updateSecurityListResourceData(d *schema.ResourceData, info *compute.SecurityListInfo) { + d.Set("name", info.Name) + d.Set("policy", info.Policy) + d.Set("outbound_cidr_policy", info.OutboundCIDRPolicy) +} + +func resourceSecurityListRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityLists() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of security list %s", name) + result, err := client.GetSecurityList(name) + if err != nil { + // Security List does not exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading security list %s: %s", name, err) + } + + log.Printf("[DEBUG] Read state of ssh key %s: %#v", name, result) + updateSecurityListResourceData(d, result) + return nil +} + +func getSecurityListResourceData(d *schema.ResourceData) (string, string, string) { + return d.Get("name").(string), + d.Get("policy").(string), + d.Get("outbound_cidr_policy").(string) +} + +func resourceSecurityListUpdate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + client := meta.(*OPCClient).SecurityLists() + name, policy, outboundCIDRPolicy := getSecurityListResourceData(d) + + log.Printf("[DEBUG] Updating security list %s with policy %s, outbound_cidr_policy %s", + name, policy, outboundCIDRPolicy) + + info, err := client.UpdateSecurityList(name, policy, outboundCIDRPolicy) + if err != nil { + return fmt.Errorf("Error updating security list %s: %s", name, err) + } + + updateSecurityListResourceData(d, info) + return nil +} + +func resourceSecurityListDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityLists() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting ssh key volume %s", name) + if err := client.DeleteSecurityList(name); err != nil { + return fmt.Errorf("Error deleting security list %s: %s", name, err) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_security_rule.go b/builtin/providers/oracleopc/resource_security_rule.go new file mode 100644 index 000000000..0d9eb562c --- /dev/null +++ b/builtin/providers/oracleopc/resource_security_rule.go @@ -0,0 +1,143 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceSecurityRule() *schema.Resource { + return &schema.Resource{ + Create: resourceSecurityRuleCreate, + Read: resourceSecurityRuleRead, + Update: resourceSecurityRuleUpdate, + Delete: resourceSecurityRuleDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "source_list": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "destination_list": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "application": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "action": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "disabled": &schema.Schema{ + Type: schema.TypeBool, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceSecurityRuleCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + name, sourceList, destinationList, application, action, disabled := getSecurityRuleResourceData(d) + + log.Printf("[DEBUG] Creating security list with name %s, sourceList %s, destinationList %s, application %s, action %s, disabled %s", + name, sourceList, destinationList, application, action, disabled) + + client := meta.(*OPCClient).SecurityRules() + info, err := client.CreateSecurityRule(name, sourceList, destinationList, application, action, disabled) + if err != nil { + return fmt.Errorf("Error creating security rule %s: %s", name, err) + } + + d.SetId(info.Name) + updateSecurityRuleResourceData(d, info) + return nil +} + +func updateSecurityRuleResourceData(d *schema.ResourceData, info *compute.SecurityRuleInfo) { + d.Set("name", info.Name) + d.Set("source_list", info.SourceList) + d.Set("destination_list", info.DestinationList) + d.Set("application", info.Application) + d.Set("action", info.Action) + d.Set("disabled", info.Disabled) +} + +func resourceSecurityRuleRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityRules() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of security rule %s", name) + result, err := client.GetSecurityRule(name) + if err != nil { + // Security Rule does not exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading security list %s: %s", name, err) + } + + log.Printf("[DEBUG] Read state of ssh key %s: %#v", name, result) + updateSecurityRuleResourceData(d, result) + return nil +} + +func getSecurityRuleResourceData(d *schema.ResourceData) (string, string, string, string, string, bool) { + return d.Get("name").(string), + d.Get("source_list").(string), + d.Get("destination_list").(string), + d.Get("application").(string), + d.Get("action").(string), + d.Get("disabled").(bool) +} + +func resourceSecurityRuleUpdate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + + client := meta.(*OPCClient).SecurityRules() + name, sourceList, destinationList, application, action, disabled := getSecurityRuleResourceData(d) + + log.Printf("[DEBUG] Updating security list %s with sourceList %s, destinationList %s, application %s, action %s, disabled %s", + name, sourceList, destinationList, application, action, disabled) + + info, err := client.UpdateSecurityRule(name, sourceList, destinationList, application, action, disabled) + if err != nil { + return fmt.Errorf("Error updating security rule %s: %s", name, err) + } + + updateSecurityRuleResourceData(d, info) + return nil +} + +func resourceSecurityRuleDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource state: %#v", d.State()) + client := meta.(*OPCClient).SecurityRules() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting ssh key volume %s", name) + if err := client.DeleteSecurityRule(name); err != nil { + return fmt.Errorf("Error deleting security rule %s: %s", name, err) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_security_rule_test.go b/builtin/providers/oracleopc/resource_security_rule_test.go new file mode 100644 index 000000000..f09c2b879 --- /dev/null +++ b/builtin/providers/oracleopc/resource_security_rule_test.go @@ -0,0 +1,85 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccOPCResourceSecurityRule_Basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: opcResourceCheck( + ruleResourceName, + testAccCheckRuleDestroyed), + Steps: []resource.TestStep{ + { + Config: testAccSecurityRuleBasic, + Check: resource.ComposeTestCheckFunc( + opcResourceCheck( + ruleResourceName, + testAccCheckRuleExists), + ), + }, + }, + }) +} + +func testAccCheckRuleExists(state *OPCResourceState) error { + ruleName := getRuleName(state) + + if _, err := state.SecurityRules().GetSecurityRule(ruleName); err != nil { + return fmt.Errorf("Error retrieving state of security rule %s: %s", ruleName, err) + } + + return nil +} + +func getRuleName(rs *OPCResourceState) string { + return rs.Attributes["name"] +} + +func testAccCheckRuleDestroyed(state *OPCResourceState) error { + ruleName := getRuleName(state) + if info, err := state.SecurityRules().GetSecurityRule(ruleName); err == nil { + return fmt.Errorf("Rule %s still exists: %#v", ruleName, info) + } + + return nil +} + +const ruleName = "test_rule" +const secListName = "sec-list1" +const secIpListName = "sec-ip-list1" + +var ruleResourceName = fmt.Sprintf("opc_compute_security_rule.%s", ruleName) + +var testAccSecurityRuleBasic = fmt.Sprintf(` +resource "opc_compute_security_rule" "%s" { + name = "test" + source_list = "seclist:${opc_compute_security_list.sec-list1.name}" + destination_list = "seciplist:${opc_compute_security_ip_list.sec-ip-list1.name}" + action = "PERMIT" + application = "${opc_compute_security_application.spring-boot.name}" + disabled = false +} + +resource "opc_compute_security_list" "%s" { + name = "sec-list-1" + policy = "PERMIT" + outbound_cidr_policy = "DENY" +} + +resource "opc_compute_security_application" "spring-boot" { + name = "spring-boot" + protocol = "tcp" + dport = "8080" +} + +resource "opc_compute_security_ip_list" "%s" { + name = "sec-ip-list1" + ip_entries = ["217.138.34.4"] +} +`, ruleName, secListName, secIpListName) diff --git a/builtin/providers/oracleopc/resource_ssh_key.go b/builtin/providers/oracleopc/resource_ssh_key.go new file mode 100644 index 000000000..29f68b4aa --- /dev/null +++ b/builtin/providers/oracleopc/resource_ssh_key.go @@ -0,0 +1,117 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceSSHKey() *schema.Resource { + return &schema.Resource{ + Create: resourceSSHKeyCreate, + Read: resourceSSHKeyRead, + Update: resourceSSHKeyUpdate, + Delete: resourceSSHKeyDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceSSHKeyCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d) + + client := meta.(*OPCClient).SSHKeys() + name := d.Get("name").(string) + key := d.Get("key").(string) + enabled := d.Get("enabled").(bool) + + log.Printf("[DEBUG] Creating ssh key with name %s, key %s, enabled %s", + name, key, enabled) + + info, err := client.CreateSSHKey(name, key, enabled) + if err != nil { + return fmt.Errorf("Error creating ssh key %s: %s", name, err) + } + + d.SetId(info.Name) + updateSSHKeyResourceData(d, info) + return nil +} + +func updateSSHKeyResourceData(d *schema.ResourceData, info *compute.SSHKeyInfo) { + d.Set("name", info.Name) + d.Set("key", info.Key) + d.Set("enabled", info.Enabled) +} + +func resourceSSHKeyRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d) + client := meta.(*OPCClient).SSHKeys() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of ssh key %s", name) + result, err := client.GetSSHKey(name) + if err != nil { + // SSH Key does not exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading ssh key %s: %s", name, err) + } + + log.Printf("[DEBUG] Read state of ssh key %s: %#v", name, result) + updateSSHKeyResourceData(d, result) + return nil +} + +func resourceSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d) + + client := meta.(*OPCClient).SSHKeys() + name := d.Get("name").(string) + key := d.Get("key").(string) + enabled := d.Get("enabled").(bool) + + log.Printf("[DEBUG] Updating ssh key with name %s, key %s, enabled %s", + name, key, enabled) + + info, err := client.UpdateSSHKey(name, key, enabled) + if err != nil { + return fmt.Errorf("Error updating ssh key %s: %s", name, err) + } + + updateSSHKeyResourceData(d, info) + return nil +} + +func resourceSSHKeyDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d) + client := meta.(*OPCClient).SSHKeys() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting ssh key volume %s", name) + if err := client.DeleteSSHKey(name); err != nil { + return fmt.Errorf("Error deleting ssh key %s: %s", name, err) + } + return nil +} diff --git a/builtin/providers/oracleopc/resource_storage_volume.go b/builtin/providers/oracleopc/resource_storage_volume.go new file mode 100644 index 000000000..2d80d09f2 --- /dev/null +++ b/builtin/providers/oracleopc/resource_storage_volume.go @@ -0,0 +1,301 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/oracle/terraform-provider-compute/sdk/compute" + "log" +) + +func resourceStorageVolume() *schema.Resource { + return &schema.Resource{ + Create: resourceStorageVolumeCreate, + Read: resourceStorageVolumeRead, + Update: resourceStorageVolumeUpdate, + Delete: resourceStorageVolumeDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "size": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "sizeInBytes": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "storage": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "/oracle/public/storage/default", + }, + + "tags": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "bootableImage": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "bootableImageVersion": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: -1, + }, + + "snapshot": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "account": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, + }, + + "snapshotId": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceStorageVolumeCreate(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Resource data: %#v", d) + + sv := meta.(*OPCClient).StorageVolumes() + name := d.Get("name").(string) + properties := []string{d.Get("storage").(string)} + + spec := sv.NewStorageVolumeSpec( + d.Get("size").(string), + properties, + name) + + if d.Get("description").(string) != "" { + spec.SetDescription(d.Get("description").(string)) + } + + spec.SetTags(getTags(d)) + + if d.Get("bootableImage") != "" { + spec.SetBootableImage(d.Get("bootableImage").(string), d.Get("bootableImageVersion").(int)) + } + + if len(d.Get("snapshot").(*schema.Set).List()) > 0 { + snapshotDetails := d.Get("snapshot").(*schema.Set).List()[0].(map[string]interface{}) + spec.SetSnapshot( + snapshotDetails["name"].(string), + snapshotDetails["account"].(string), + ) + } + + if d.Get("snapshotId") != "" { + spec.SetSnapshotID(d.Get("snapshotId").(string)) + } + + log.Printf("[DEBUG] Creating storage volume %s with spec %#v", name, spec) + err := sv.CreateStorageVolume(spec) + if err != nil { + return fmt.Errorf("Error creating storage volume %s: %s", name, err) + } + + log.Printf("[DEBUG] Waiting for storage volume %s to come online", name) + info, err := sv.WaitForStorageVolumeOnline(name, meta.(*OPCClient).MaxRetryTimeout) + if err != nil { + return fmt.Errorf("Error waiting for storage volume %s to come online: %s", name, err) + } + + log.Printf("[DEBUG] Created storage volume %s: %#v", name, info) + + cachedAttachments, attachmentsFound := meta.(*OPCClient).storageAttachmentsByVolumeCache[name] + if attachmentsFound { + log.Printf("[DEBUG] Rebuilding storage attachments for volume %s", name) + for _, cachedAttachment := range cachedAttachments { + log.Printf("[DEBUG] Rebuilding storage attachments between volume %s and instance %s", + name, + cachedAttachment.instanceName) + + attachmentInfo, err := meta.(*OPCClient).StorageAttachments().CreateStorageAttachment( + cachedAttachment.index, + cachedAttachment.instanceName, + name, + ) + + if err != nil { + return fmt.Errorf( + "Error recreating storage attachment between volume %s and instance %s: %s", + name, + *cachedAttachment.instanceName, + err) + } + err = meta.(*OPCClient).StorageAttachments().WaitForStorageAttachmentCreated( + attachmentInfo.Name, + meta.(*OPCClient).MaxRetryTimeout) + if err != nil { + return fmt.Errorf( + "Error recreating storage attachment between volume %s and instance %s: %s", + name, + *cachedAttachment.instanceName, + err) + } + } + meta.(*OPCClient).storageAttachmentsByVolumeCache[name] = nil + } + + d.SetId(name) + updateResourceData(d, info) + return nil +} + +func getTags(d *schema.ResourceData) []string { + tags := []string{} + for _, i := range d.Get("tags").([]interface{}) { + tags = append(tags, i.(string)) + } + return tags +} + +func updateResourceData(d *schema.ResourceData, info *compute.StorageVolumeInfo) error { + d.Set("name", info.Name) + d.Set("description", info.Description) + d.Set("storage", info.Properties[0]) + d.Set("sizeInBytes", info.Size) + d.Set("tags", info.Tags) + d.Set("bootableImage", info.ImageList) + d.Set("bootableImageVersion", info.ImageListEntry) + if info.Snapshot != "" { + d.Set("snapshot", map[string]interface{}{ + "name": info.Snapshot, + "account": info.SnapshotAccount, + }) + } + d.Set("snapshotId", info.SnapshotID) + + return nil +} + +func resourceStorageVolumeRead(d *schema.ResourceData, meta interface{}) error { + sv := meta.(*OPCClient).StorageVolumes() + name := d.Get("name").(string) + + log.Printf("[DEBUG] Reading state of storage volume %s", name) + result, err := sv.GetStorageVolume(name) + if err != nil { + // Volume doesn't exist + if compute.WasNotFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading storage volume %s: %s", name, err) + } + + if len(result.Result) == 0 { + // Volume doesn't exist + d.SetId("") + return nil + } + + log.Printf("[DEBUG] Read state of storage volume %s: %#v", name, &result.Result[0]) + updateResourceData(d, &result.Result[0]) + + return nil +} + +func resourceStorageVolumeUpdate(d *schema.ResourceData, meta interface{}) error { + sv := meta.(*OPCClient).StorageVolumes() + name := d.Get("name").(string) + description := d.Get("description").(string) + size := d.Get("size").(string) + tags := getTags(d) + + log.Printf("[DEBUG] Updating storage volume %s with size %s, description %s, tags %#v", name, size, description, tags) + err := sv.UpdateStorageVolume(name, size, description, tags) + + if err != nil { + return fmt.Errorf("Error updating storage volume %s: %s", name, err) + } + + log.Printf("[DEBUG] Waiting for updated storage volume %s to come online", name) + info, err := sv.WaitForStorageVolumeOnline(name, meta.(*OPCClient).MaxRetryTimeout) + if err != nil { + return fmt.Errorf("Error waiting for updated storage volume %s to come online: %s", name, err) + } + + log.Printf("[DEBUG] Updated storage volume %s: %#v", name, info) + updateResourceData(d, info) + return nil +} + +func resourceStorageVolumeDelete(d *schema.ResourceData, meta interface{}) error { + sv := meta.(*OPCClient).StorageVolumes() + name := d.Get("name").(string) + + sva := meta.(*OPCClient).StorageAttachments() + attachments, err := sva.GetStorageAttachmentsForVolume(name) + if err != nil { + return fmt.Errorf("Error retrieving storage attachments for volume %s: %s", name, err) + } + + attachmentsToCache := make([]storageAttachment, len(*attachments)) + for index, attachment := range *attachments { + log.Printf("[DEBUG] Deleting storage attachment %s for volume %s", attachment.Name, name) + sva.DeleteStorageAttachment(attachment.Name) + sva.WaitForStorageAttachmentDeleted(attachment.Name, meta.(*OPCClient).MaxRetryTimeout) + attachmentsToCache[index] = storageAttachment{ + index: attachment.Index, + instanceName: compute.InstanceNameFromString(attachment.InstanceName), + } + } + meta.(*OPCClient).storageAttachmentsByVolumeCache[name] = attachmentsToCache + + log.Printf("[DEBUG] Deleting storage volume %s", name) + err = sv.DeleteStorageVolume(name) + if err != nil { + return fmt.Errorf("Error deleting storage volume %s: %s", name, err) + } + + log.Printf("[DEBUG] Waiting for storage volume %s to finish deleting", name) + err = sv.WaitForStorageVolumeDeleted(name, meta.(*OPCClient).MaxRetryTimeout) + if err != nil { + return fmt.Errorf("Error waiting for storage volume %s to finish deleting: %s", name, err) + } + + log.Printf("[DEBUG] Deleted storage volume %s", name) + return nil +} diff --git a/builtin/providers/oracleopc/resource_storage_volume_test.go b/builtin/providers/oracleopc/resource_storage_volume_test.go new file mode 100644 index 000000000..d168b5309 --- /dev/null +++ b/builtin/providers/oracleopc/resource_storage_volume_test.go @@ -0,0 +1,70 @@ +package opc + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccOPCStorageVolume_Basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: opcResourceCheck( + "opc_compute_storage_volume.test_volume", + testAccCheckStorageVolumeDestroyed), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageVolumeBasic, + Check: resource.ComposeTestCheckFunc( + opcResourceCheck( + "opc_compute_storage_volume.test_volume", + testAccCheckStorageVolumeExists), + ), + }, + }, + }) +} + +func testAccCheckStorageVolumeExists(state *OPCResourceState) error { + sv := state.StorageVolumes() + volumeName := state.Attributes["name"] + + info, err := sv.GetStorageVolume(volumeName) + if err != nil { + return fmt.Errorf("Error retrieving state of volume %s: %s", volumeName, err) + } + + if len(info.Result) == 0 { + return fmt.Errorf("No info found for volume %s", volumeName) + } + + return nil +} + +func testAccCheckStorageVolumeDestroyed(state *OPCResourceState) error { + sv := state.StorageVolumes() + + volumeName := state.Attributes["name"] + + info, err := sv.GetStorageVolume(volumeName) + if err != nil { + return fmt.Errorf("Error retrieving state of volume %s: %s", volumeName, err) + } + + if len(info.Result) != 0 { + return fmt.Errorf("Volume %s still exists", volumeName) + } + + return nil +} + +const testAccStorageVolumeBasic = ` +resource "opc_compute_storage_volume" "test_volume" { + size = "3g" + description = "My volume" + name = "test_volume_b" + tags = ["foo", "bar", "baz"] +} +` From c5fa0404d69df9bd117b7b42fe8fbe9107d5bf3c Mon Sep 17 00:00:00 2001 From: Stephen Cross Date: Wed, 29 Mar 2017 16:37:14 +0000 Subject: [PATCH 89/91] Initial docs for Oracle Compute Cloud provider --- .../r/opc_compute_instance.html.markdown | 68 +++++++++++++++++++ .../opc_compute_ip_association.html.markdown | 31 +++++++++ .../opc_compute_ip_reservation.html.markdown | 33 +++++++++ ...compute_security_application.html.markdown | 39 +++++++++++ ...compute_security_association.html.markdown | 29 ++++++++ ...opc_compute_security_ip_list.html.markdown | 28 ++++++++ .../r/opc_compute_security_list.html.markdown | 33 +++++++++ .../r/opc_compute_security_rule.html.markdown | 46 +++++++++++++ .../r/opc_compute_ssh_key.html.markdown | 32 +++++++++ .../opc_compute_storage_volume.html.markdown | 49 +++++++++++++ 10 files changed, 388 insertions(+) create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown create mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown new file mode 100644 index 000000000..04762b5c3 --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown @@ -0,0 +1,68 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_instance" +sidebar_current: "docs-opc-resource-instance" +description: |- + Creates and manages an instance in an OPC identity domain. +--- + +# opc\_compute\_instance + +The ``opc_compute_instance`` resource creates and manages an instance in an OPC identity domain. + +~> **Caution:** The ``opc_compute_instance`` resource can completely delete your +instance just as easily as it can create it. To avoid costly accidents, +consider setting +[``prevent_destroy``](/docs/configuration/resources.html#prevent_destroy) +on your instance resources as an extra safety measure. + +## Example Usage + +``` +resource "opc_compute_instance" "test_instance" { + name = "test" + label = "test" + shape = "oc3" + imageList = "/oracle/public/oel_6.4_2GB_v1" + sshKeys = ["${opc_compute_ssh_key.key1.name}"] + attributes = "{\"foo\":\"bar\"}" + storage = [{ + index = 1 + volume = "${opc_compute_storage_volume.test_volume.name}" + }, + { + index = 2 + volume = "${opc_compute_storage_volume.test_volume2.name}" + }] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the instance. This need not be unique, as each instance is assigned a separate +computed `opcId`. + +* `shape` - (Required) The shape of the instance, e.g. `oc4`. + +* `imageList` - (Optional) The imageList of the instance, e.g. `/oracle/public/oel_6.4_2GB_v1` + +* `label` - (Optional) The label to apply to the instance. + +* `ip` - (Computed) The internal IP address assigned to the instance. + +* `opcId` - (Computed) The interned ID assigned to the instance. + +* `sshKeys` - (Optional) The names of the SSH Keys that can be used to log into the instance. + +* `attributes` - (Optional) An arbitrary JSON-formatted collection of attributes which is made available to the instance. + +* `vcable` - (Computed) The ID of the instance's VCable, which is used to associate it with reserved IP addresses and +add it to Security Lists. + +* `storage` - (Optional) A set of zero or more storage volumes to attach to the instance. Each volume has two arguments: +`index`, which is the volume's index in the instance's list of mounted volumes, and `name`, which is the name of the +storage volume to mount. + +* `bootOrder` - (Optional) The index number of the bootable storage volume that should be used to boot the instance. e.g. `[ 1 ]`. If you specify both `bootOrder` and `imageList`, the imagelist attribute is ignored. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown new file mode 100644 index 000000000..deeed76c4 --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown @@ -0,0 +1,31 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_ip_association" +sidebar_current: "docs-opc-resource-ip-association" +description: |- + Creates and manages an IP association in an OPC identity domain. +--- + +# opc\_compute\_ip\_association + +The ``opc_compute_ip_association`` resource creates and manages an association between an IP address and an instance in +an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_ip_association" "instance1_reservation1" { + vcable = "${opc_compute_instance.test_instance.vcable}" + parentpool = "ipreservation:${opc_compute_ip_reservation.reservation1.name}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vcable` - (Required) The vcable of the instance to associate the IP address with. + +* `parentpool` - (Required) The pool from which to take an IP address. To associate a specific reserved IP address, use +the prefix `ipreservation:` followed by the name of the IP reservation. To allocate an IP address from a pool, use the +prefix `ippool:`, e.g. `ippool:/oracle/public/ippool`. \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown new file mode 100644 index 000000000..7c44c62ea --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown @@ -0,0 +1,33 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_ip_reservation" +sidebar_current: "docs-opc-resource-ip-assocation" +description: |- + Creates and manages an IP reservation in an OPC identity domain. +--- + +# opc\_compute\_ip\_reservation + +The ``opc_compute_ip_reservation`` resource creates and manages an IP reservation in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_ip_reservation" "reservation1" { + parentpool = "/oracle/public/ippool" + permanent = true + tags = [] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `parentpool` - (Required) The pool from which to allocate the IP address. + +* `permanent` - (Required) Whether the IP address remains reserved even when it is no longer associated with an instance +(if true), or may be returned to the pool and replaced with a different IP address when an instance is restarted, or +deleted and recreated (if false). + +* `tags` - (Optional) List of tags that may be applied to the IP reservation. \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown new file mode 100644 index 000000000..fe8c9ba3c --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_security_application" +sidebar_current: "docs-opc-resource-security-application" +description: |- + Creates and manages a security application in an OPC identity domain. +--- + +# opc\_compute\_ip\_reservation + +The ``opc_compute_security_application`` resource creates and manages a security application in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_security_application" "tomcat" { + name = "tomcat" + protocol = "tcp" + dport = "8080" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The unique (within the identity domain) name of the application + +* `protocol` - (Required) The protocol to enable for this application. Must be either one of +`tcp`, `udp`, `icmp`, `igmp`, `ipip`, `rdp`, `esp`, `ah`, `gre`, `icmpv6`, `ospf`, `pim`, `sctp`, `mplsip` or `all`, or +the corresponding integer in the range 0-254 from the list of [assigned protocol numbers](http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml) + +* `dport` - (Required) The port, or range of ports, to enable for this application, e.g `8080`, `6000-7000`. + +* `icmptype` - (Optional) The ICMP type to enable for this application, if the `protocol` is `icmp`. Must be one of +`echo`, `reply`, `ttl`, `traceroute`, `unreachable`. + +* `icmpcode` - (Optional) The ICMP code to enable for this application, if the `protocol` is `icmp`. Must be one of +`network`, `host`, `protocol`, `port`, `df`, `admin`. \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown new file mode 100644 index 000000000..170acc2ea --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown @@ -0,0 +1,29 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_security_association" +sidebar_current: "docs-opc-resource-security-association" +description: |- + Creates and manages a security association in an OPC identity domain. +--- + +# opc\_compute\_ip\_reservation + +The ``opc_compute_security_association`` resource creates and manages an association between an instance and a security +list in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_security_association" "test_instance_sec_list_1" { + vcable = "${opc_compute_instance.test_instance.vcable}" + seclist = "${opc_compute_security_list.sec_list1.name}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vcable` - (Required) The `vcable` of the instance to associate to the security list. + +* `seclist` - (Required) The name of the security list to associate the instance to. \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown new file mode 100644 index 000000000..bded4c30e --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown @@ -0,0 +1,28 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_security_ip_list" +sidebar_current: "docs-opc-resource-security-list" +description: |- + Creates and manages a security IP list in an OPC identity domain. +--- + +# opc\_compute\_ip\_reservation + +The ``opc_compute_security_ip_list`` resource creates and manages a security IP list in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_security_ip_list" "sec_ip_list1" { + name = "sec-ip-list1" + ip_entries = ["217.138.34.4"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The unique (within the identity domain) name of the security IP list. + +* `ip_entries` - (Required) The IP addresses to include in the list. \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown new file mode 100644 index 000000000..7da5e5668 --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown @@ -0,0 +1,33 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_security_list" +sidebar_current: "docs-opc-resource-security-list" +description: |- + Creates and manages a security list in an OPC identity domain. +--- + +# opc\_compute\_ip\_reservation + +The ``opc_compute_security_list`` resource creates and manages a security list in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_security_list" "sec_list1" { + name = "sec-list-1" + policy = "permit" + outbound_cidr_policy = "deny" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The unique (within the identity domain) name of the security list. + +* `policy` - (Required) The policy to apply to instances associated with this list. Must be one of `permit`, +`reject` (packets are dropped but a reply is sent) and `deny` (packets are dropped and no reply is sent). + +* `output_cidr_policy` - (Required) The policy for outbound traffic from the security list.Must be one of `permit`, +`reject` (packets are dropped but a reply is sent) and `deny` (packets are dropped and no reply is sent). \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown new file mode 100644 index 000000000..02c4b7533 --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown @@ -0,0 +1,46 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_security_rule" +sidebar_current: "docs-opc-resource-security-rule" +description: |- + Creates and manages a security rule in an OPC identity domain. +--- + +# opc\_compute\_ip\_reservation + +The ``opc_compute_security_rule`` resource creates and manages a security rule in an OPC identity domain, which joins +together a source security list (or security IP list), a destination security list (or security IP list), and a security +application. + +## Example Usage + +``` +resource "opc_compute_security_rule" "test_rule" { + name = "test" + source_list = "seclist:${opc_compute_security_list.sec-list1.name}" + destination_list = "seciplist:${opc_compute_security_ip_list.sec-ip-list1.name}" + action = "permit" + application = "${opc_compute_security_application.spring-boot.name}" + disabled = false +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The unique (within the identity domain) name of the security rule. + +* `source_list` - (Required) The source security list (prefixed with `seclist:`), or security IP list (prefixed with +`seciplist:`). + + * `destination_list` - (Required) The destination security list (prefixed with `seclist:`), or security IP list (prefixed with + `seciplist:`). + +* `application` - (Required) The name of the application to which the rule applies. + +* `action` - (Required) Whether to `permit`, `refuse` or `deny` packets to which this rule applies. This will ordinarily +be `permit`. + +* `disabled` - (Required) Whether to disable this security rule. This is useful if you want to temporarily disable a rule +without removing it outright from your Terraform resource definition. \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown new file mode 100644 index 000000000..9655653a9 --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown @@ -0,0 +1,32 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_ssh_key" +sidebar_current: "docs-opc-resource-instance" +description: |- + Creates and manages an SSH key in an OPC identity domain. +--- + +# opc\_compute\_ssh_key + +The ``opc_compute_ssh_key`` resource creates and manages an SSH key in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_ssh_key" "%s" { + name = "test-key" + key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqw6JwbjIk..." + enabled = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The unique (within this identity domain) name of the SSH key. + +* `key` - (Required) The SSH key itself + +* `enabled` - (Required) Whether or not the key is enabled. This is useful if you want to temporarily disable an SSH key, +without removing it entirely from your Terraform resource definition. \ No newline at end of file diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown new file mode 100644 index 000000000..0e91a8ad7 --- /dev/null +++ b/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown @@ -0,0 +1,49 @@ +--- +layout: "oracle" +page_title: "Oracle: opc_compute_storage_volume" +sidebar_current: "docs-opc-resource-storage_volume" +description: |- + Creates and manages a storage volume in an OPC identity domain. +--- + +# opc\_compute\_storage\_volume + +The ``opc_compute_storage_volume`` resource creates and manages a storage volume in an OPC identity domain. + +~> **Caution:** The ``opc_compute_storage_volume`` resource can completely delete your +storage volume just as easily as it can create it. To avoid costly accidents, +consider setting +[``prevent_destroy``](/docs/configuration/resources.html#prevent_destroy) +on your storage volume resources as an extra safety measure. + +## Example Usage + +``` +resource "opc_compute_storage_volume" "test_volume" { + size = "3g" + description = "My storage volume" + name = "test_volume_a" + tags = ["xyzzy", "quux"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The unique (within this identity domain) name of the storage volume. + +* `size` - (Required) The size of the storage instance. + +* `description` - (Optional) A description of the storage volume. + +* `tags` - (Optional) A list of tags to apply to the storage volume. + +* `bootableImage` - (Optional) The name of the bootable image the storage volume is loaded with. + +* `bootableImageVersion` - (Optional) The version of the bootable image specified in `bootableImage` to use. + +* `snapshot` - (Optional) The snapshot to initialise the storage volume with. This has two nested properties: `name`, +for the name of the snapshot to use, and `account` for the name of the snapshot account to use. + +* `snapshotId` - (Optional) The id of the snapshot to initialise the storage volume with. From 2d7be9bb9ea9fd9435aba168a8dd4cc30ab36154 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Wed, 29 Mar 2017 19:30:04 +0200 Subject: [PATCH 90/91] Hooking up the OracleOPC Docs --- website/source/assets/stylesheets/_docs.scss | 1 + .../d/opc_compute_vnic.html.markdown | 36 +++++++++++ .../providers/oracleopc/index.html.markdown | 55 +++++++++++++++++ .../r/opc_compute_instance.html.markdown | 4 +- .../opc_compute_ip_association.html.markdown | 6 +- .../opc_compute_ip_reservation.html.markdown | 6 +- ...compute_security_application.html.markdown | 8 +-- ...compute_security_association.html.markdown | 8 +-- ...opc_compute_security_ip_list.html.markdown | 8 +-- .../r/opc_compute_security_list.html.markdown | 8 +-- .../r/opc_compute_security_rule.html.markdown | 8 +-- .../r/opc_compute_ssh_key.html.markdown | 6 +- .../opc_compute_storage_volume.html.markdown | 4 +- website/source/layouts/docs.erb | 4 ++ website/source/layouts/oracleopc.erb | 59 +++++++++++++++++++ 15 files changed, 188 insertions(+), 33 deletions(-) create mode 100644 website/source/docs/providers/oracleopc/d/opc_compute_vnic.html.markdown create mode 100644 website/source/docs/providers/oracleopc/index.html.markdown create mode 100644 website/source/layouts/oracleopc.erb diff --git a/website/source/assets/stylesheets/_docs.scss b/website/source/assets/stylesheets/_docs.scss index 35f16eb60..9f2922c21 100755 --- a/website/source/assets/stylesheets/_docs.scss +++ b/website/source/assets/stylesheets/_docs.scss @@ -50,6 +50,7 @@ body.layout-nomad, body.layout-ns1, body.layout-openstack, body.layout-opsgenie, +body.layout-oracleopc, body.layout-packet, body.layout-pagerduty, body.layout-postgresql, diff --git a/website/source/docs/providers/oracleopc/d/opc_compute_vnic.html.markdown b/website/source/docs/providers/oracleopc/d/opc_compute_vnic.html.markdown new file mode 100644 index 000000000..78be49c4a --- /dev/null +++ b/website/source/docs/providers/oracleopc/d/opc_compute_vnic.html.markdown @@ -0,0 +1,36 @@ +--- +layout: "oracleopc" +page_title: "Oracle: opc_compute_vnic" +sidebar_current: "docs-oracleopc-datasource-vnic" +description: |- + Gets information about the configuration of a Virtual NIC. +--- + +# opc\_compute\_vnic + +Use this data source to access the configuration of a Virtual NIC. + +## Example Usage + +``` +data "opc_compute_vnic" "current" {} + +output "mac_address" { + value = "${data.opc_compute_vnic.current.mac_address}" +} +``` + +## Argument Reference +* `name` is the name of the Virtual NIC. + +## Attributes Reference + +* `description` is a description of the Virtual NIC. + +* `mac_address` is the MAC Address of the Virtual NIC. + +* `tags` is a list of Tags associated with the Virtual NIC. + +* `transit_flag` is `true` if the Virtual NIC is of the type `transit`. + +* `uri` is the Unique Resource Locator of the Virtual NIC. diff --git a/website/source/docs/providers/oracleopc/index.html.markdown b/website/source/docs/providers/oracleopc/index.html.markdown new file mode 100644 index 000000000..598346919 --- /dev/null +++ b/website/source/docs/providers/oracleopc/index.html.markdown @@ -0,0 +1,55 @@ +--- +layout: "oracleopc" +page_title: "Provider: Oracle Public Cloud" +sidebar_current: "docs-oracleopc-index" +description: |- + The Oracle Public Cloud provider is used to interact with the many resources supported by the Oracle Public Cloud. The provider needs to be configured with credentials for the Oracle Public Cloud API. +--- + +# Oracle Public Cloud Provider + +The Oracle Public Cloud provider is used to interact with the many resources supported by the Oracle Public Cloud. The provider needs to be configured with credentials for the Oracle Public Cloud API. + +Use the navigation to the left to read about the available resources. + +## Example Usage + +``` +# Configure the Oracle Public Cloud +provider "oracle" { + user = "..." + password = "..." + identity_domain = "..." + endpoint = "..." +} + +# Create an IP Reservation +resource "opc_compute_ip_reservation" "production" { + parent_pool = "/oracle/public/ippool" + permanent = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `user` - (Optional) The username to use, generally your email address. It can also + be sourced from the `OPC_USERNAME` environment variable. + +* `password` - (Optional) The password associated with the username to use. It can also be sourced from + the `OPC_PASSWORD` environment variable. + +* `identity_domain` - (Optional) The identity domain to use. It can also be sourced from + the `OPC_IDENTITY_DOMAIN` environment variable. + +* `endpoint` - (Optional) The API endpoint to use, associated with your Oracle Public Cloud account. This is known as the `REST Endpoint` within the Oracle portal. It can also be sourced from the `OPC_ENDPOINT` environment variable. + +Max num seconds to wait for successful response when operating on resources within OPC (defaults to 3000) +* `max_retry_timeout` - (Optional) The maximum number of seconds to wait for a successful response when operating on resources within Oracle Public Cloud. It can also be sourced from the `OPC_MAX_RETRY_TIMEOUT` environment variable. Defaults to 3000 seconds. + +## Testing + +Credentials must be provided via the `OPC_USERNAME`, `OPC_PASSWORD`, +`OPC_IDENTITY_DOMAIN` and `OPC_ENDPOINT` environment variables in order to run +acceptance tests. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown index 04762b5c3..faeb3ee7c 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_instance" -sidebar_current: "docs-opc-resource-instance" +sidebar_current: "docs-oracleopc-resource-instance" description: |- Creates and manages an instance in an OPC identity domain. --- diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown index deeed76c4..2518b2df1 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_ip_association" -sidebar_current: "docs-opc-resource-ip-association" +sidebar_current: "docs-oracleopc-resource-ip-association" description: |- Creates and manages an IP association in an OPC identity domain. --- @@ -28,4 +28,4 @@ The following arguments are supported: * `parentpool` - (Required) The pool from which to take an IP address. To associate a specific reserved IP address, use the prefix `ipreservation:` followed by the name of the IP reservation. To allocate an IP address from a pool, use the -prefix `ippool:`, e.g. `ippool:/oracle/public/ippool`. \ No newline at end of file +prefix `ippool:`, e.g. `ippool:/oracle/public/ippool`. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown index 7c44c62ea..44b70cc0f 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_ip_reservation" -sidebar_current: "docs-opc-resource-ip-assocation" +sidebar_current: "docs-oracleopc-resource-ip-reservation" description: |- Creates and manages an IP reservation in an OPC identity domain. --- @@ -30,4 +30,4 @@ The following arguments are supported: (if true), or may be returned to the pool and replaced with a different IP address when an instance is restarted, or deleted and recreated (if false). -* `tags` - (Optional) List of tags that may be applied to the IP reservation. \ No newline at end of file +* `tags` - (Optional) List of tags that may be applied to the IP reservation. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown index fe8c9ba3c..94760f082 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown @@ -1,12 +1,12 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_security_application" -sidebar_current: "docs-opc-resource-security-application" +sidebar_current: "docs-oracleopc-resource-security-application" description: |- Creates and manages a security application in an OPC identity domain. --- -# opc\_compute\_ip\_reservation +# opc\_compute\_security\_application The ``opc_compute_security_application`` resource creates and manages a security application in an OPC identity domain. @@ -36,4 +36,4 @@ the corresponding integer in the range 0-254 from the list of [assigned protocol `echo`, `reply`, `ttl`, `traceroute`, `unreachable`. * `icmpcode` - (Optional) The ICMP code to enable for this application, if the `protocol` is `icmp`. Must be one of -`network`, `host`, `protocol`, `port`, `df`, `admin`. \ No newline at end of file +`network`, `host`, `protocol`, `port`, `df`, `admin`. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown index 170acc2ea..49207c879 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown @@ -1,12 +1,12 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_security_association" -sidebar_current: "docs-opc-resource-security-association" +sidebar_current: "docs-oracleopc-resource-security-association" description: |- Creates and manages a security association in an OPC identity domain. --- -# opc\_compute\_ip\_reservation +# opc\_compute\_security\_association The ``opc_compute_security_association`` resource creates and manages an association between an instance and a security list in an OPC identity domain. @@ -26,4 +26,4 @@ The following arguments are supported: * `vcable` - (Required) The `vcable` of the instance to associate to the security list. -* `seclist` - (Required) The name of the security list to associate the instance to. \ No newline at end of file +* `seclist` - (Required) The name of the security list to associate the instance to. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown index bded4c30e..62f40d839 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown @@ -1,12 +1,12 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_security_ip_list" -sidebar_current: "docs-opc-resource-security-list" +sidebar_current: "docs-oracleopc-resource-security-ip-list" description: |- Creates and manages a security IP list in an OPC identity domain. --- -# opc\_compute\_ip\_reservation +# opc\_compute\_security\_ip\_list The ``opc_compute_security_ip_list`` resource creates and manages a security IP list in an OPC identity domain. @@ -25,4 +25,4 @@ The following arguments are supported: * `name` - (Required) The unique (within the identity domain) name of the security IP list. -* `ip_entries` - (Required) The IP addresses to include in the list. \ No newline at end of file +* `ip_entries` - (Required) The IP addresses to include in the list. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown index 7da5e5668..64547a41e 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown @@ -1,12 +1,12 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_security_list" -sidebar_current: "docs-opc-resource-security-list" +sidebar_current: "docs-oracleopc-resource-security-list" description: |- Creates and manages a security list in an OPC identity domain. --- -# opc\_compute\_ip\_reservation +# opc\_compute\_security\_list The ``opc_compute_security_list`` resource creates and manages a security list in an OPC identity domain. @@ -30,4 +30,4 @@ The following arguments are supported: `reject` (packets are dropped but a reply is sent) and `deny` (packets are dropped and no reply is sent). * `output_cidr_policy` - (Required) The policy for outbound traffic from the security list.Must be one of `permit`, -`reject` (packets are dropped but a reply is sent) and `deny` (packets are dropped and no reply is sent). \ No newline at end of file +`reject` (packets are dropped but a reply is sent) and `deny` (packets are dropped and no reply is sent). diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown index 02c4b7533..6497b0265 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_security_rule" -sidebar_current: "docs-opc-resource-security-rule" +sidebar_current: "docs-oracleopc-resource-security-rule" description: |- Creates and manages a security rule in an OPC identity domain. --- @@ -33,7 +33,7 @@ The following arguments are supported: * `source_list` - (Required) The source security list (prefixed with `seclist:`), or security IP list (prefixed with `seciplist:`). - + * `destination_list` - (Required) The destination security list (prefixed with `seclist:`), or security IP list (prefixed with `seciplist:`). @@ -43,4 +43,4 @@ The following arguments are supported: be `permit`. * `disabled` - (Required) Whether to disable this security rule. This is useful if you want to temporarily disable a rule -without removing it outright from your Terraform resource definition. \ No newline at end of file +without removing it outright from your Terraform resource definition. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown index 9655653a9..ff85467d8 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_ssh_key" -sidebar_current: "docs-opc-resource-instance" +sidebar_current: "docs-oracleopc-resource-ssh-key" description: |- Creates and manages an SSH key in an OPC identity domain. --- @@ -29,4 +29,4 @@ The following arguments are supported: * `key` - (Required) The SSH key itself * `enabled` - (Required) Whether or not the key is enabled. This is useful if you want to temporarily disable an SSH key, -without removing it entirely from your Terraform resource definition. \ No newline at end of file +without removing it entirely from your Terraform resource definition. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown index 0e91a8ad7..4b30b59ed 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown +++ b/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracle" +layout: "oracleopc" page_title: "Oracle: opc_compute_storage_volume" -sidebar_current: "docs-opc-resource-storage_volume" +sidebar_current: "docs-oracleopc-resource-storage-volume" description: |- Creates and manages a storage volume in an OPC identity domain. --- diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 1f42c1e32..77d5bf2c3 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -355,6 +355,10 @@ OpsGenie + > + Oracle OPC + + > Packet diff --git a/website/source/layouts/oracleopc.erb b/website/source/layouts/oracleopc.erb new file mode 100644 index 000000000..a9d9579f8 --- /dev/null +++ b/website/source/layouts/oracleopc.erb @@ -0,0 +1,59 @@ +<% wrap_layout :inner do %> +<% content_for :sidebar do %> + +<% end %> + +<%= yield %> +<% end %> From edc524df55965148e34ce21d6a15f5aeed4a5e26 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Mon, 3 Apr 2017 18:24:53 -0400 Subject: [PATCH 91/91] provider/opc: Update OPC Provider Updates the OPC provider to a fully working version. --- builtin/bins/provider-opc/main.go | 12 + builtin/providers/opc/config.go | 41 + .../opc/data_source_network_interface.go | 176 ++++ .../opc/data_source_network_interface_test.go | 103 ++ .../providers/opc/data_source_virtual_nic.go | 72 ++ .../opc/data_source_virtual_nic_test.go | 56 ++ builtin/providers/opc/helpers.go | 47 + builtin/providers/opc/import_acl_test.go | 58 ++ .../providers/opc/import_image_list_test.go | 57 ++ builtin/providers/opc/import_instance_test.go | 35 + .../opc/import_ip_address_prefix_set_test.go | 33 + .../opc/import_ip_address_reservation_test.go | 57 ++ .../opc/import_ip_association_test.go | 34 + .../opc/import_ip_network_exchange_test.go | 34 + .../opc/import_ip_reservation_test.go | 34 + builtin/providers/opc/import_route_test.go | 33 + builtin/providers/opc/import_sec_rule_test.go | 59 ++ .../opc/import_security_application_test.go | 59 ++ .../opc/import_security_association_test.go | 59 ++ .../opc/import_security_ip_list_test.go | 34 + .../opc/import_security_list_test.go | 59 ++ .../opc/import_security_protocol_test.go | 58 ++ .../opc/import_security_rule_test.go | 58 ++ builtin/providers/opc/import_ssh_key_test.go | 59 ++ builtin/providers/opc/provider.go | 89 ++ .../{oracleopc => opc}/provider_test.go | 7 +- builtin/providers/opc/resource_acl.go | 151 +++ builtin/providers/opc/resource_acl_test.go | 107 +++ builtin/providers/opc/resource_image_list.go | 107 +++ .../providers/opc/resource_image_list_test.go | 98 ++ builtin/providers/opc/resource_instance.go | 884 ++++++++++++++++++ .../providers/opc/resource_instance_test.go | 229 +++++ .../opc/resource_ip_address_prefix_set.go | 147 +++ .../resource_ip_address_prefix_set_test.go | 100 ++ .../opc/resource_ip_address_reservation.go | 137 +++ .../resource_ip_address_reservation_test.go | 77 ++ .../providers/opc/resource_ip_association.go | 96 ++ .../opc/resource_ip_association_test.go | 88 ++ builtin/providers/opc/resource_ip_network.go | 176 ++++ .../opc/resource_ip_network_exchange.go | 105 +++ .../opc/resource_ip_network_exchange_test.go | 73 ++ .../providers/opc/resource_ip_network_test.go | 91 ++ .../providers/opc/resource_ip_reservation.go | 117 +++ .../opc/resource_ip_reservation_test.go | 75 ++ builtin/providers/opc/resource_route.go | 176 ++++ builtin/providers/opc/resource_route_test.go | 115 +++ builtin/providers/opc/resource_sec_rule.go | 163 ++++ .../providers/opc/resource_sec_rule_test.go | 138 +++ .../opc/resource_security_application.go | 140 +++ .../opc/resource_security_application_test.go | 101 ++ .../opc/resource_security_association.go | 101 ++ .../opc/resource_security_association_test.go | 128 +++ .../opc/resource_security_ip_list.go | 135 +++ .../opc/resource_security_ip_list_test.go | 117 +++ .../providers/opc/resource_security_list.go | 141 +++ .../opc/resource_security_list_test.go | 100 ++ .../opc/resource_security_protocol.go | 159 ++++ .../opc/resource_security_protocol_test.go | 129 +++ .../providers/opc/resource_security_rule.go | 231 +++++ .../opc/resource_security_rule_test.go | 159 ++++ builtin/providers/opc/resource_ssh_key.go | 118 +++ .../providers/opc/resource_ssh_key_test.go | 152 +++ .../providers/opc/resource_storage_volume.go | 250 +++++ .../opc/resource_storage_volume_test.go | 206 ++++ builtin/providers/opc/resource_vnic_set.go | 169 ++++ .../providers/opc/resource_vnic_set_test.go | 113 +++ builtin/providers/opc/tags.go | 28 + builtin/providers/opc/validators.go | 67 ++ builtin/providers/opc/validators_test.go | 102 ++ builtin/providers/oracleopc/config.go | 47 - builtin/providers/oracleopc/provider.go | 75 -- .../providers/oracleopc/resource_instance.go | 306 ------ .../oracleopc/resource_instance_test.go | 156 ---- .../oracleopc/resource_ip_association.go | 103 -- .../oracleopc/resource_ip_association_test.go | 74 -- .../oracleopc/resource_ip_reservation.go | 122 --- .../resource_security_application.go | 124 --- .../resource_security_association.go | 103 -- .../resource_security_association_test.go | 75 -- .../oracleopc/resource_security_ip_list.go | 117 --- .../oracleopc/resource_security_list.go | 119 --- .../oracleopc/resource_security_rule.go | 143 --- .../oracleopc/resource_security_rule_test.go | 85 -- .../providers/oracleopc/resource_ssh_key.go | 117 --- .../oracleopc/resource_storage_volume.go | 301 ------ .../oracleopc/resource_storage_volume_test.go | 70 -- command/internal_plugin_list.go | 2 + .../hashicorp/go-oracle-terraform/LICENSE | 373 ++++++++ .../go-oracle-terraform/compute/acl.go | 138 +++ .../compute/authentication.go | 34 + .../go-oracle-terraform/compute/client.go | 238 +++++ .../go-oracle-terraform/compute/image_list.go | 154 +++ .../compute/image_list_entries.go | 122 +++ .../go-oracle-terraform/compute/instances.go | 540 +++++++++++ .../compute/ip_address_prefix_set.go | 135 +++ .../compute/ip_address_reservations.go | 190 ++++ .../compute/ip_associations.go | 118 +++ .../compute/ip_network_exchange.go | 99 ++ .../compute/ip_networks.go | 186 ++++ .../compute/ip_reservations.go | 147 +++ .../go-oracle-terraform/compute/logging.go | 28 + .../compute/resource_client.go | 94 ++ .../go-oracle-terraform/compute/routes.go | 153 +++ .../go-oracle-terraform/compute/sec_rules.go | 193 ++++ .../compute/security_applications.go | 150 +++ .../compute/security_associations.go | 95 ++ .../compute/security_ip_lists.go | 113 +++ .../compute/security_lists.go | 131 +++ .../compute/security_protocols.go | 187 ++++ .../compute/security_rules.go | 266 ++++++ .../go-oracle-terraform/compute/ssh_keys.go | 112 +++ .../compute/storage_volume_attachments.go | 158 ++++ .../compute/storage_volumes.go | 345 +++++++ .../go-oracle-terraform/compute/test_utils.go | 121 +++ .../compute/virtual_nic.go | 52 ++ .../compute/virtual_nic_sets.go | 154 +++ .../go-oracle-terraform/helper/testing.go | 44 + .../go-oracle-terraform/opc/config.go | 21 + .../go-oracle-terraform/opc/convert.go | 5 + .../go-oracle-terraform/opc/errors.go | 12 + .../go-oracle-terraform/opc/logger.go | 70 ++ vendor/vendor.json | 18 + website/source/assets/stylesheets/_docs.scss | 2 +- ...pc_compute_network_interface.html.markdown | 49 + .../d/opc_compute_vnic.html.markdown | 8 +- .../{oracleopc => opc}/index.html.markdown | 6 +- .../opc/r/opc_compute_acl.html.markdown | 45 + .../r/opc_compute_image_list.html.markdown | 39 + .../opc/r/opc_compute_instance.html.markdown | 169 ++++ ...ompute_ip_address_prefix_set.html.markdown | 45 + ...mpute_ip_address_reservation.html.markdown | 38 + .../opc_compute_ip_association.html.markdown | 19 +- .../r/opc_compute_ip_network.html.markdown | 54 ++ ..._compute_ip_network_exchange.html.markdown | 37 + .../opc_compute_ip_reservation.html.markdown | 16 +- .../opc/r/opc_compute_route.html.markdown | 60 ++ .../r/opc_compute_sec_rule.html.markdown} | 35 +- ...compute_security_application.html.markdown | 38 +- ...compute_security_association.html.markdown | 19 +- ...opc_compute_security_ip_list.html.markdown | 14 +- .../r/opc_compute_security_list.html.markdown | 22 +- ...pc_compute_security_protocol.html.markdown | 65 ++ .../r/opc_compute_security_rule.html.markdown | 62 ++ .../r/opc_compute_ssh_key.html.markdown | 16 +- .../opc_compute_storage_volume.html.markdown | 78 ++ .../opc/r/opc_compute_vnic_set.html.markdown | 45 + .../r/opc_compute_instance.html.markdown | 68 -- .../opc_compute_storage_volume.html.markdown | 49 - website/source/layouts/docs.erb | 4 +- website/source/layouts/opc.erb | 94 ++ website/source/layouts/oracleopc.erb | 59 -- 151 files changed, 13739 insertions(+), 2370 deletions(-) create mode 100644 builtin/bins/provider-opc/main.go create mode 100644 builtin/providers/opc/config.go create mode 100644 builtin/providers/opc/data_source_network_interface.go create mode 100644 builtin/providers/opc/data_source_network_interface_test.go create mode 100644 builtin/providers/opc/data_source_virtual_nic.go create mode 100644 builtin/providers/opc/data_source_virtual_nic_test.go create mode 100644 builtin/providers/opc/helpers.go create mode 100644 builtin/providers/opc/import_acl_test.go create mode 100644 builtin/providers/opc/import_image_list_test.go create mode 100644 builtin/providers/opc/import_instance_test.go create mode 100644 builtin/providers/opc/import_ip_address_prefix_set_test.go create mode 100644 builtin/providers/opc/import_ip_address_reservation_test.go create mode 100644 builtin/providers/opc/import_ip_association_test.go create mode 100644 builtin/providers/opc/import_ip_network_exchange_test.go create mode 100644 builtin/providers/opc/import_ip_reservation_test.go create mode 100644 builtin/providers/opc/import_route_test.go create mode 100644 builtin/providers/opc/import_sec_rule_test.go create mode 100644 builtin/providers/opc/import_security_application_test.go create mode 100644 builtin/providers/opc/import_security_association_test.go create mode 100644 builtin/providers/opc/import_security_ip_list_test.go create mode 100644 builtin/providers/opc/import_security_list_test.go create mode 100644 builtin/providers/opc/import_security_protocol_test.go create mode 100644 builtin/providers/opc/import_security_rule_test.go create mode 100644 builtin/providers/opc/import_ssh_key_test.go create mode 100644 builtin/providers/opc/provider.go rename builtin/providers/{oracleopc => opc}/provider_test.go (91%) create mode 100644 builtin/providers/opc/resource_acl.go create mode 100644 builtin/providers/opc/resource_acl_test.go create mode 100644 builtin/providers/opc/resource_image_list.go create mode 100644 builtin/providers/opc/resource_image_list_test.go create mode 100644 builtin/providers/opc/resource_instance.go create mode 100644 builtin/providers/opc/resource_instance_test.go create mode 100644 builtin/providers/opc/resource_ip_address_prefix_set.go create mode 100644 builtin/providers/opc/resource_ip_address_prefix_set_test.go create mode 100644 builtin/providers/opc/resource_ip_address_reservation.go create mode 100644 builtin/providers/opc/resource_ip_address_reservation_test.go create mode 100644 builtin/providers/opc/resource_ip_association.go create mode 100644 builtin/providers/opc/resource_ip_association_test.go create mode 100644 builtin/providers/opc/resource_ip_network.go create mode 100644 builtin/providers/opc/resource_ip_network_exchange.go create mode 100644 builtin/providers/opc/resource_ip_network_exchange_test.go create mode 100644 builtin/providers/opc/resource_ip_network_test.go create mode 100644 builtin/providers/opc/resource_ip_reservation.go create mode 100644 builtin/providers/opc/resource_ip_reservation_test.go create mode 100644 builtin/providers/opc/resource_route.go create mode 100644 builtin/providers/opc/resource_route_test.go create mode 100644 builtin/providers/opc/resource_sec_rule.go create mode 100644 builtin/providers/opc/resource_sec_rule_test.go create mode 100644 builtin/providers/opc/resource_security_application.go create mode 100644 builtin/providers/opc/resource_security_application_test.go create mode 100644 builtin/providers/opc/resource_security_association.go create mode 100644 builtin/providers/opc/resource_security_association_test.go create mode 100644 builtin/providers/opc/resource_security_ip_list.go create mode 100644 builtin/providers/opc/resource_security_ip_list_test.go create mode 100644 builtin/providers/opc/resource_security_list.go create mode 100644 builtin/providers/opc/resource_security_list_test.go create mode 100644 builtin/providers/opc/resource_security_protocol.go create mode 100644 builtin/providers/opc/resource_security_protocol_test.go create mode 100644 builtin/providers/opc/resource_security_rule.go create mode 100644 builtin/providers/opc/resource_security_rule_test.go create mode 100644 builtin/providers/opc/resource_ssh_key.go create mode 100644 builtin/providers/opc/resource_ssh_key_test.go create mode 100644 builtin/providers/opc/resource_storage_volume.go create mode 100644 builtin/providers/opc/resource_storage_volume_test.go create mode 100644 builtin/providers/opc/resource_vnic_set.go create mode 100644 builtin/providers/opc/resource_vnic_set_test.go create mode 100644 builtin/providers/opc/tags.go create mode 100644 builtin/providers/opc/validators.go create mode 100644 builtin/providers/opc/validators_test.go delete mode 100644 builtin/providers/oracleopc/config.go delete mode 100644 builtin/providers/oracleopc/provider.go delete mode 100644 builtin/providers/oracleopc/resource_instance.go delete mode 100644 builtin/providers/oracleopc/resource_instance_test.go delete mode 100644 builtin/providers/oracleopc/resource_ip_association.go delete mode 100644 builtin/providers/oracleopc/resource_ip_association_test.go delete mode 100644 builtin/providers/oracleopc/resource_ip_reservation.go delete mode 100644 builtin/providers/oracleopc/resource_security_application.go delete mode 100644 builtin/providers/oracleopc/resource_security_association.go delete mode 100644 builtin/providers/oracleopc/resource_security_association_test.go delete mode 100644 builtin/providers/oracleopc/resource_security_ip_list.go delete mode 100644 builtin/providers/oracleopc/resource_security_list.go delete mode 100644 builtin/providers/oracleopc/resource_security_rule.go delete mode 100644 builtin/providers/oracleopc/resource_security_rule_test.go delete mode 100644 builtin/providers/oracleopc/resource_ssh_key.go delete mode 100644 builtin/providers/oracleopc/resource_storage_volume.go delete mode 100644 builtin/providers/oracleopc/resource_storage_volume_test.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/LICENSE create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/client.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/logging.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/resource_client.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/helper/testing.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go create mode 100644 website/source/docs/providers/opc/d/opc_compute_network_interface.html.markdown rename website/source/docs/providers/{oracleopc => opc}/d/opc_compute_vnic.html.markdown (85%) rename website/source/docs/providers/{oracleopc => opc}/index.html.markdown (96%) create mode 100644 website/source/docs/providers/opc/r/opc_compute_acl.html.markdown create mode 100644 website/source/docs/providers/opc/r/opc_compute_image_list.html.markdown create mode 100644 website/source/docs/providers/opc/r/opc_compute_instance.html.markdown create mode 100644 website/source/docs/providers/opc/r/opc_compute_ip_address_prefix_set.html.markdown create mode 100644 website/source/docs/providers/opc/r/opc_compute_ip_address_reservation.html.markdown rename website/source/docs/providers/{oracleopc => opc}/r/opc_compute_ip_association.html.markdown (75%) create mode 100644 website/source/docs/providers/opc/r/opc_compute_ip_network.html.markdown create mode 100644 website/source/docs/providers/opc/r/opc_compute_ip_network_exchange.html.markdown rename website/source/docs/providers/{oracleopc => opc}/r/opc_compute_ip_reservation.html.markdown (70%) create mode 100644 website/source/docs/providers/opc/r/opc_compute_route.html.markdown rename website/source/docs/providers/{oracleopc/r/opc_compute_security_rule.html.markdown => opc/r/opc_compute_sec_rule.html.markdown} (54%) rename website/source/docs/providers/{oracleopc => opc}/r/opc_compute_security_application.html.markdown (55%) rename website/source/docs/providers/{oracleopc => opc}/r/opc_compute_security_association.html.markdown (55%) rename website/source/docs/providers/{oracleopc => opc}/r/opc_compute_security_ip_list.html.markdown (71%) rename website/source/docs/providers/{oracleopc => opc}/r/opc_compute_security_list.html.markdown (68%) create mode 100644 website/source/docs/providers/opc/r/opc_compute_security_protocol.html.markdown create mode 100644 website/source/docs/providers/opc/r/opc_compute_security_rule.html.markdown rename website/source/docs/providers/{oracleopc => opc}/r/opc_compute_ssh_key.html.markdown (72%) create mode 100644 website/source/docs/providers/opc/r/opc_compute_storage_volume.html.markdown create mode 100644 website/source/docs/providers/opc/r/opc_compute_vnic_set.html.markdown delete mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown delete mode 100644 website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown create mode 100644 website/source/layouts/opc.erb delete mode 100644 website/source/layouts/oracleopc.erb diff --git a/builtin/bins/provider-opc/main.go b/builtin/bins/provider-opc/main.go new file mode 100644 index 000000000..814a0b732 --- /dev/null +++ b/builtin/bins/provider-opc/main.go @@ -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, + }) +} diff --git a/builtin/providers/opc/config.go b/builtin/providers/opc/config.go new file mode 100644 index 000000000..5c473e591 --- /dev/null +++ b/builtin/providers/opc/config.go @@ -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) +} diff --git a/builtin/providers/opc/data_source_network_interface.go b/builtin/providers/opc/data_source_network_interface.go new file mode 100644 index 000000000..5a5b8fa78 --- /dev/null +++ b/builtin/providers/opc/data_source_network_interface.go @@ -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 +} diff --git a/builtin/providers/opc/data_source_network_interface_test.go b/builtin/providers/opc/data_source_network_interface_test.go new file mode 100644 index 000000000..392748980 --- /dev/null +++ b/builtin/providers/opc/data_source_network_interface_test.go @@ -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) +} diff --git a/builtin/providers/opc/data_source_virtual_nic.go b/builtin/providers/opc/data_source_virtual_nic.go new file mode 100644 index 000000000..2878cf489 --- /dev/null +++ b/builtin/providers/opc/data_source_virtual_nic.go @@ -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 +} diff --git a/builtin/providers/opc/data_source_virtual_nic_test.go b/builtin/providers/opc/data_source_virtual_nic_test.go new file mode 100644 index 000000000..6c52c06a3 --- /dev/null +++ b/builtin/providers/opc/data_source_virtual_nic_test.go @@ -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) +} diff --git a/builtin/providers/opc/helpers.go b/builtin/providers/opc/helpers.go new file mode 100644 index 000000000..ea0123c96 --- /dev/null +++ b/builtin/providers/opc/helpers.go @@ -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) +} diff --git a/builtin/providers/opc/import_acl_test.go b/builtin/providers/opc/import_acl_test.go new file mode 100644 index 000000000..16b49fa93 --- /dev/null +++ b/builtin/providers/opc/import_acl_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_image_list_test.go b/builtin/providers/opc/import_image_list_test.go new file mode 100644 index 000000000..19877f95a --- /dev/null +++ b/builtin/providers/opc/import_image_list_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_instance_test.go b/builtin/providers/opc/import_instance_test.go new file mode 100644 index 000000000..e3bbcc32c --- /dev/null +++ b/builtin/providers/opc/import_instance_test.go @@ -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 + "/", + }, + }, + }) +} diff --git a/builtin/providers/opc/import_ip_address_prefix_set_test.go b/builtin/providers/opc/import_ip_address_prefix_set_test.go new file mode 100644 index 000000000..09c53d845 --- /dev/null +++ b/builtin/providers/opc/import_ip_address_prefix_set_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_ip_address_reservation_test.go b/builtin/providers/opc/import_ip_address_reservation_test.go new file mode 100644 index 000000000..bc67afa16 --- /dev/null +++ b/builtin/providers/opc/import_ip_address_reservation_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_ip_association_test.go b/builtin/providers/opc/import_ip_association_test.go new file mode 100644 index 000000000..cd1be015c --- /dev/null +++ b/builtin/providers/opc/import_ip_association_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_ip_network_exchange_test.go b/builtin/providers/opc/import_ip_network_exchange_test.go new file mode 100644 index 000000000..c3abcb65e --- /dev/null +++ b/builtin/providers/opc/import_ip_network_exchange_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_ip_reservation_test.go b/builtin/providers/opc/import_ip_reservation_test.go new file mode 100644 index 000000000..045bfcf16 --- /dev/null +++ b/builtin/providers/opc/import_ip_reservation_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_route_test.go b/builtin/providers/opc/import_route_test.go new file mode 100644 index 000000000..8302c85ae --- /dev/null +++ b/builtin/providers/opc/import_route_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_sec_rule_test.go b/builtin/providers/opc/import_sec_rule_test.go new file mode 100644 index 000000000..1db5b6b16 --- /dev/null +++ b/builtin/providers/opc/import_sec_rule_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_security_application_test.go b/builtin/providers/opc/import_security_application_test.go new file mode 100644 index 000000000..35026c66a --- /dev/null +++ b/builtin/providers/opc/import_security_application_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_security_association_test.go b/builtin/providers/opc/import_security_association_test.go new file mode 100644 index 000000000..35c98503f --- /dev/null +++ b/builtin/providers/opc/import_security_association_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_security_ip_list_test.go b/builtin/providers/opc/import_security_ip_list_test.go new file mode 100644 index 000000000..b38c5a464 --- /dev/null +++ b/builtin/providers/opc/import_security_ip_list_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_security_list_test.go b/builtin/providers/opc/import_security_list_test.go new file mode 100644 index 000000000..0ac0d02d8 --- /dev/null +++ b/builtin/providers/opc/import_security_list_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_security_protocol_test.go b/builtin/providers/opc/import_security_protocol_test.go new file mode 100644 index 000000000..da1df1176 --- /dev/null +++ b/builtin/providers/opc/import_security_protocol_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_security_rule_test.go b/builtin/providers/opc/import_security_rule_test.go new file mode 100644 index 000000000..f3b98249e --- /dev/null +++ b/builtin/providers/opc/import_security_rule_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/import_ssh_key_test.go b/builtin/providers/opc/import_ssh_key_test.go new file mode 100644 index 000000000..a52987ed8 --- /dev/null +++ b/builtin/providers/opc/import_ssh_key_test.go @@ -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, + }, + }, + }) +} diff --git a/builtin/providers/opc/provider.go b/builtin/providers/opc/provider.go new file mode 100644 index 000000000..46b4fcc95 --- /dev/null +++ b/builtin/providers/opc/provider.go @@ -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() +} diff --git a/builtin/providers/oracleopc/provider_test.go b/builtin/providers/opc/provider_test.go similarity index 91% rename from builtin/providers/oracleopc/provider_test.go rename to builtin/providers/opc/provider_test.go index c60076b06..8c2c842d8 100644 --- a/builtin/providers/oracleopc/provider_test.go +++ b/builtin/providers/opc/provider_test.go @@ -1,10 +1,11 @@ package opc import ( + "fmt" "os" "testing" - "fmt" + "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" @@ -40,7 +41,7 @@ func testAccPreCheck(t *testing.T) { } type OPCResourceState struct { - *OPCClient + *compute.Client *terraform.InstanceState } @@ -52,7 +53,7 @@ func opcResourceCheck(resourceName string, f func(checker *OPCResourceState) err } state := &OPCResourceState{ - OPCClient: testAccProvider.Meta().(*OPCClient), + Client: testAccProvider.Meta().(*compute.Client), InstanceState: rs.Primary, } diff --git a/builtin/providers/opc/resource_acl.go b/builtin/providers/opc/resource_acl.go new file mode 100644 index 000000000..55558b7fa --- /dev/null +++ b/builtin/providers/opc/resource_acl.go @@ -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 +} diff --git a/builtin/providers/opc/resource_acl_test.go b/builtin/providers/opc/resource_acl_test.go new file mode 100644 index 000000000..9a0293e68 --- /dev/null +++ b/builtin/providers/opc/resource_acl_test.go @@ -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 +} +` diff --git a/builtin/providers/opc/resource_image_list.go b/builtin/providers/opc/resource_image_list.go new file mode 100644 index 000000000..79038f7bc --- /dev/null +++ b/builtin/providers/opc/resource_image_list.go @@ -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 +} diff --git a/builtin/providers/opc/resource_image_list_test.go b/builtin/providers/opc/resource_image_list_test.go new file mode 100644 index 000000000..072cf4225 --- /dev/null +++ b/builtin/providers/opc/resource_image_list_test.go @@ -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 +} +` diff --git a/builtin/providers/opc/resource_instance.go b/builtin/providers/opc/resource_instance.go new file mode 100644 index 000000000..8b682cf96 --- /dev/null +++ b/builtin/providers/opc/resource_instance.go @@ -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) +} diff --git a/builtin/providers/opc/resource_instance_test.go b/builtin/providers/opc/resource_instance_test.go new file mode 100644 index 000000000..00bb66e7e --- /dev/null +++ b/builtin/providers/opc/resource_instance_test.go @@ -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 = < 2 { + errors = append(errors, fmt.Errorf( + "%q can only be an interger between 0-2. Got: %d", k, value)) + } + return +} + +// Admin distance can either be a 0, 1, or a 2. Defaults to 0. +func validateIPProtocol(v interface{}, k string) (ws []string, errors []error) { + validProtocols := map[string]struct{}{ + string(compute.All): {}, + string(compute.AH): {}, + string(compute.ESP): {}, + string(compute.ICMP): {}, + string(compute.ICMPV6): {}, + string(compute.IGMP): {}, + string(compute.IPIP): {}, + string(compute.GRE): {}, + string(compute.MPLSIP): {}, + string(compute.OSPF): {}, + string(compute.PIM): {}, + string(compute.RDP): {}, + string(compute.SCTP): {}, + string(compute.TCP): {}, + string(compute.UDP): {}, + } + + value := v.(string) + if _, ok := validProtocols[value]; !ok { + errors = append(errors, fmt.Errorf( + `%q must contain a valid Image owner , expected ["all", "ah", "esp", "icmp", "icmpv6", "igmp", "ipip", "gre", "mplsip", "ospf", "pim", "rdp", "sctp", "tcp", "udp"] got %q`, + k, value)) + } + return +} diff --git a/builtin/providers/opc/validators_test.go b/builtin/providers/opc/validators_test.go new file mode 100644 index 000000000..96df22312 --- /dev/null +++ b/builtin/providers/opc/validators_test.go @@ -0,0 +1,102 @@ +package opc + +import "testing" + +func TestValidateIPPrefixCIDR(t *testing.T) { + validPrefixes := []string{ + "10.0.1.0/24", + "10.1.0.0/16", + "192.168.0.1/32", + "10.20.0.0/18", + "10.0.12.0/24", + } + + for _, v := range validPrefixes { + _, errors := validateIPPrefixCIDR(v, "prefix") + if len(errors) != 0 { + t.Fatalf("%q should be a valid IP Address Prefix: %q", v, errors) + } + } + + invalidPrefixes := []string{ + "10.0.0.1/35", + "192.168.1.256/16", + "256.0.1/16", + } + + for _, v := range invalidPrefixes { + _, errors := validateIPPrefixCIDR(v, "prefix") + if len(errors) == 0 { + t.Fatalf("%q should not be a valid IP Address", v) + } + } +} + +func TestValidateAdminDistance(t *testing.T) { + validDistances := []int{ + 0, + 1, + 2, + } + + for _, v := range validDistances { + _, errors := validateAdminDistance(v, "distance") + if len(errors) != 0 { + t.Fatalf("%q should be a valid Admin Distance: %q", v, errors) + } + } + + invalidDistances := []int{ + -1, + 4, + 3, + 42, + } + + for _, v := range invalidDistances { + _, errors := validateAdminDistance(v, "distance") + if len(errors) == 0 { + t.Fatalf("%q should not be a valid Admin Distance", v) + } + } +} + +func TestValidateIPProtocol(t *testing.T) { + validProtocols := []string{ + "all", + "ah", + "esp", + "icmp", + "icmpv6", + "igmp", + "ipip", + "gre", + "mplsip", + "ospf", + "pim", + "rdp", + "sctp", + "tcp", + "udp", + } + + for _, v := range validProtocols { + _, errors := validateIPProtocol(v, "ip_protocol") + if len(errors) != 0 { + t.Fatalf("%q should be a valid Admin Distance: %q", v, errors) + } + } + + invalidProtocols := []string{ + "bad", + "real bad", + "are you even trying at this point?", + } + for _, v := range invalidProtocols { + _, errors := validateIPProtocol(v, "ip_protocol") + if len(errors) == 0 { + t.Fatalf("%q should not be a valid IP Protocol", v) + } + } + +} diff --git a/builtin/providers/oracleopc/config.go b/builtin/providers/oracleopc/config.go deleted file mode 100644 index fbae3b5d5..000000000 --- a/builtin/providers/oracleopc/config.go +++ /dev/null @@ -1,47 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "net/url" -) - -type Config struct { - User string - Password string - IdentityDomain string - Endpoint string - MaxRetryTimeout int -} - -type storageAttachment struct { - index int - instanceName *compute.InstanceName -} - -type OPCClient struct { - *compute.AuthenticatedClient - MaxRetryTimeout int - storageAttachmentsByVolumeCache map[string][]storageAttachment -} - -func (c *Config) Client() (*OPCClient, error) { - u, err := url.ParseRequestURI(c.Endpoint) - if err != nil { - return nil, fmt.Errorf("Invalid endpoint URI: %s", err) - } - - client := compute.NewComputeClient(c.IdentityDomain, c.User, c.Password, u) - authenticatedClient, err := client.Authenticate() - if err != nil { - return nil, fmt.Errorf("Authentication failed: %s", err) - } - - opcClient := &OPCClient{ - AuthenticatedClient: authenticatedClient, - MaxRetryTimeout: c.MaxRetryTimeout, - storageAttachmentsByVolumeCache: make(map[string][]storageAttachment), - } - - return opcClient, nil -} diff --git a/builtin/providers/oracleopc/provider.go b/builtin/providers/oracleopc/provider.go deleted file mode 100644 index a6d0d3fb5..000000000 --- a/builtin/providers/oracleopc/provider.go +++ /dev/null @@ -1,75 +0,0 @@ -package opc - -import ( - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -// Provider returns a terraform.ResourceProvider. -func Provider() terraform.ResourceProvider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{ - "user": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("OPC_USERNAME", nil), - Description: "The user name for OPC API operations.", - }, - - "password": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("OPC_PASSWORD", nil), - Description: "The user password for OPC API operations.", - }, - - "identityDomain": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("OPC_IDENTITY_DOMAIN", nil), - Description: "The OPC identity domain for API operations", - }, - - "endpoint": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("OPC_ENDPOINT", nil), - Description: "The HTTP endpoint for OPC API operations.", - }, - - "maxRetryTimeout": &schema.Schema{ - 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)", - }, - }, - - ResourcesMap: map[string]*schema.Resource{ - "opc_compute_storage_volume": resourceStorageVolume(), - "opc_compute_instance": resourceInstance(), - "opc_compute_ssh_key": resourceSSHKey(), - "opc_compute_security_application": resourceSecurityApplication(), - "opc_compute_security_list": resourceSecurityList(), - "opc_compute_security_ip_list": resourceSecurityIPList(), - "opc_compute_ip_reservation": resourceIPReservation(), - "opc_compute_ip_association": resourceIPAssociation(), - "opc_compute_security_rule": resourceSecurityRule(), - "opc_compute_security_association": resourceSecurityAssociation(), - }, - - ConfigureFunc: providerConfigure, - } -} - -func providerConfigure(d *schema.ResourceData) (interface{}, error) { - config := Config{ - User: d.Get("user").(string), - Password: d.Get("password").(string), - IdentityDomain: d.Get("identityDomain").(string), - Endpoint: d.Get("endpoint").(string), - MaxRetryTimeout: d.Get("maxRetryTimeout").(int), - } - - return config.Client() -} diff --git a/builtin/providers/oracleopc/resource_instance.go b/builtin/providers/oracleopc/resource_instance.go deleted file mode 100644 index 70f3b99c8..000000000 --- a/builtin/providers/oracleopc/resource_instance.go +++ /dev/null @@ -1,306 +0,0 @@ -package opc - -import ( - "encoding/json" - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceInstance() *schema.Resource { - return &schema.Resource{ - Create: resourceInstanceCreate, - Read: resourceInstanceRead, - Delete: resourceInstanceDelete, - - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "shape": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "imageList": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "label": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "ip": { - Type: schema.TypeString, - Optional: false, - Computed: true, - }, - - "opcId": { - Type: schema.TypeString, - Optional: false, - Computed: true, - }, - - "sshKeys": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - - "attributes": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "vcable": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - - "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, - }, - }, - }, - }, - - "bootOrder": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeInt}, - }, - }, - } -} - -func getAttrs(d *schema.ResourceData) (*map[string]interface{}, error) { - var attrs map[string]interface{} - - attrString := d.Get("attributes").(string) - if attrString == "" { - return &attrs, nil - } - if err := json.Unmarshal([]byte(attrString), &attrs); err != nil { - return &attrs, fmt.Errorf("Cannot parse '%s' as json", attrString) - } - return &attrs, nil -} - -func resourceInstanceCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d.State()) - - client := meta.(*OPCClient).Instances() - name := d.Get("name").(string) - shape := d.Get("shape").(string) - imageList := d.Get("imageList").(string) - label := d.Get("label").(string) - storage := getStorageAttachments(d) - sshKeys := getSSHKeys(d) - bootOrder := getBootOrder(d) - - attrs, err := getAttrs(d) - if err != nil { - return err - } - - log.Printf("[DEBUG] Creating instance with name %s, shape %s, imageList %s, storage %s, bootOrder %s, label %s, sshKeys %s, attrs %#v", - name, shape, imageList, storage, bootOrder, label, sshKeys, attrs) - - id, err := client.LaunchInstance(name, label, shape, imageList, storage, bootOrder, sshKeys, *attrs) - if err != nil { - return fmt.Errorf("Error creating instance %s: %s", name, err) - } - - log.Printf("[DEBUG] Waiting for instance %s to come online", id.String()) - info, err := client.WaitForInstanceRunning(id, meta.(*OPCClient).MaxRetryTimeout) - if err != nil { - return fmt.Errorf("Error waiting for instance %s to come online: %s", id, err) - } - - log.Printf("[DEBUG] Created instance %s: %#v", id, info) - - attachStorage( - &compute.InstanceName{ - Name: info.Name, - ID: info.ID, - }, - d, meta) - - d.SetId(info.Name) - updateInstanceResourceData(d, info) - return nil -} - -func attachStorage(name *compute.InstanceName, d *schema.ResourceData, meta interface{}) error { - storageClient := meta.(*OPCClient).StorageAttachments() - storage := d.Get("storage").(*schema.Set) - updatedStorage := schema.NewSet(storage.F, []interface{}{}) - - for _, i := range storage.List() { - attrs := i.(map[string]interface{}) - attachmentInfo, err := storageClient.CreateStorageAttachment( - attrs["index"].(int), - name, - attrs["volume"].(string)) - - if err != nil { - return err - } - - log.Printf("[DEBUG] Waiting for storage attachment %#v to come online", attachmentInfo) - storageClient.WaitForStorageAttachmentCreated(attachmentInfo.Name, meta.(*OPCClient).MaxRetryTimeout) - log.Printf("[DEBUG] Storage attachment %s: %s-%s created", - attachmentInfo.Name, attachmentInfo.InstanceName, attachmentInfo.StorageVolumeName) - attrs["name"] = attachmentInfo.Name - updatedStorage.Add(attrs) - } - - d.Set("storage", updatedStorage) - return nil -} - -func getSSHKeys(d *schema.ResourceData) []string { - sshKeys := []string{} - for _, i := range d.Get("sshKeys").([]interface{}) { - sshKeys = append(sshKeys, i.(string)) - } - return sshKeys -} - -func getBootOrder(d *schema.ResourceData) []int { - bootOrder := []int{} - for _, i := range d.Get("bootOrder").([]interface{}) { - bootOrder = append(bootOrder, i.(int)) - } - return bootOrder -} - -func getStorageAttachments(d *schema.ResourceData) []compute.LaunchPlanStorageAttachmentSpec { - storageAttachments := []compute.LaunchPlanStorageAttachmentSpec{} - storage := d.Get("storage").(*schema.Set) - for _, i := range storage.List() { - attrs := i.(map[string]interface{}) - storageAttachments = append(storageAttachments, compute.LaunchPlanStorageAttachmentSpec{ - Index: attrs["index"].(int), - Volume: attrs["volume"].(string), - }) - } - return storageAttachments -} - -func updateInstanceResourceData(d *schema.ResourceData, info *compute.InstanceInfo) error { - d.Set("name", info.Name) - d.Set("opcId", info.ID) - d.Set("imageList", info.ImageList) - d.Set("bootOrder", info.BootOrder) - d.Set("sshKeys", info.SSHKeys) - d.Set("label", info.Label) - d.Set("ip", info.IPAddress) - d.Set("vcable", info.VCableID) - - return nil -} - -func resourceInstanceRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d.State()) - client := meta.(*OPCClient).Instances() - name := d.Get("name").(string) - instanceName := &compute.InstanceName{ - Name: name, - ID: d.Get("opcId").(string), - } - - log.Printf("[DEBUG] Reading state of instance %s", instanceName) - result, err := client.GetInstance(instanceName) - if err != nil { - // Instance doesn't exist - if compute.WasNotFoundError(err) { - log.Printf("[DEBUG] Instance %s not found", instanceName) - d.SetId("") - return nil - } - return fmt.Errorf("Error reading instance %s: %s", instanceName, err) - } - - log.Printf("[DEBUG] Read state of instance %s: %#v", instanceName, result) - - attachments, err := meta.(*OPCClient).StorageAttachments().GetStorageAttachmentsForInstance(instanceName) - if err != nil { - return fmt.Errorf("Error reading storage attachments for instance %s: %s", instanceName, err) - } - updateInstanceResourceData(d, result) - updateAttachmentResourceData(d, attachments) - return nil -} - -func updateAttachmentResourceData(d *schema.ResourceData, attachments *[]compute.StorageAttachmentInfo) { - attachmentSet := schema.NewSet(d.Get("storage").(*schema.Set).F, []interface{}{}) - for _, attachment := range *attachments { - properties := map[string]interface{}{ - "index": attachment.Index, - "volume": attachment.StorageVolumeName, - "name": attachment.Name, - } - attachmentSet.Add(properties) - } - d.Set("storage", attachmentSet) -} - -func resourceInstanceDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d.State()) - client := meta.(*OPCClient).Instances() - name := d.Get("name").(string) - - instanceName := &compute.InstanceName{ - Name: name, - ID: d.Get("opcId").(string), - } - - log.Printf("[DEBUG] Deleting instance %s", instanceName) - if err := client.DeleteInstance(instanceName); err != nil { - return fmt.Errorf("Error deleting instance %s: %s", instanceName, err) - } - if err := client.WaitForInstanceDeleted(instanceName, meta.(*OPCClient).MaxRetryTimeout); err != nil { - return fmt.Errorf("Error deleting instance %s: %s", instanceName, err) - } - - for _, attachment := range d.Get("storage").(*schema.Set).List() { - name := attachment.(map[string]interface{})["name"].(string) - log.Printf("[DEBUG] Deleting storage attachment %s", name) - client.StorageAttachments().DeleteStorageAttachment(name) - client.StorageAttachments().WaitForStorageAttachmentDeleted(name, meta.(*OPCClient).MaxRetryTimeout) - } - - return nil -} diff --git a/builtin/providers/oracleopc/resource_instance_test.go b/builtin/providers/oracleopc/resource_instance_test.go deleted file mode 100644 index 6f386af84..000000000 --- a/builtin/providers/oracleopc/resource_instance_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/resource" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "testing" -) - -func TestAccOPCInstance_Basic(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: opcResourceCheck( - instanceResourceName, - testAccCheckInstanceDestroyed), - Steps: []resource.TestStep{ - { - Config: testAccInstanceBasic, - Check: resource.ComposeTestCheckFunc( - opcResourceCheck( - instanceResourceName, - testAccCheckInstanceExists), - opcResourceCheck( - keyResourceName, - testAccCheckSSHKeyExists), - ), - }, - { - Config: modifySSHKey, - Check: resource.ComposeTestCheckFunc( - opcResourceCheck( - instanceResourceName, - testAccCheckInstanceExists), - opcResourceCheck( - keyResourceName, - testAccCheckSSHKeyUpdated), - ), - }, - }, - }) -} - -func testAccCheckInstanceExists(state *OPCResourceState) error { - instanceName := getInstanceName(state) - - if _, err := state.Instances().GetInstance(instanceName); err != nil { - return fmt.Errorf("Error retrieving state of instance %s: %s", instanceName, err) - } - - return nil -} - -func testAccCheckSSHKeyExists(state *OPCResourceState) error { - keyName := state.Attributes["name"] - - if _, err := state.SSHKeys().GetSSHKey(keyName); err != nil { - return fmt.Errorf("Error retrieving state of key %s: %s", keyName, err) - } - - return nil -} - -func testAccCheckSSHKeyUpdated(state *OPCResourceState) error { - keyName := state.Attributes["name"] - info, err := state.SSHKeys().GetSSHKey(keyName) - if err != nil { - return err - } - if info.Key != updatedKey { - return fmt.Errorf("Expected key\n\t%s\nbut was\n\t%s", updatedKey, info.Key) - } - return nil -} - -func getInstanceName(rs *OPCResourceState) *compute.InstanceName { - return &compute.InstanceName{ - Name: rs.Attributes["name"], - ID: rs.Attributes["opcId"], - } -} - -func testAccCheckInstanceDestroyed(state *OPCResourceState) error { - instanceName := getInstanceName(state) - if info, err := state.Instances().GetInstance(instanceName); err == nil { - return fmt.Errorf("Instance %s still exists: %#v", instanceName, info) - } - - return nil -} - -const instanceName = "test_instance" -const keyName = "test_key" - -var instanceResourceName = fmt.Sprintf("opc_compute_instance.%s", instanceName) -var keyResourceName = fmt.Sprintf("opc_compute_ssh_key.%s", keyName) - -const originalKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqw6JwbjIkZEr5UcMojtxhk6Zum39NOihHNXEvRWDt5WssX8TH/ghpv3D25K1pJkf+wfAi17HwEmYwPMEyEHENS443v6RZbXvzCkUWzkJzq7Zvbdqld038km31La2QUoMMp1KL5zk1nM65xCeQDVcR/h++03EScB2CuzTpAV6khMdfgOJgxm361kfrDVRwc1HQrAOpOnzkpPfwqBrYWqN1UnKvuO77Wk8z5LBe03EPNru3bLE3s3qHI9hjO0gXMiVUi0KyNxdWfDO8esqQlKavHAeePyrRA55YF8kBB5dEl4tVNOqpY/8TRnGN1mOe0LWxa8Ytz1wbyS49knsNVTel" -const updatedKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDHvb/2OSemgzUYLNW1/T3u33r7sZy1qbWtgVWiREH4gS5TVmDVPuvN1MFLdNqiWQA53gK8Gp24jtjNm9ftcPhicv81HVWJTB69C0sJGEfF0l4mgbemJLH3i37Mb6SdWJcGof9qHVDADPgiC8jIBVUhdiJSeq4fUJ3NQA2eUExBkRglQWairkNzPNA0mi3GL9KDGnoBnSCAXNGoKgDgIOqW0dYFP6oHyGWkF7V+/TME9aIQvmMpHjVzl7brZ/wED2t5vTJxxbgogHEmWnfs7p8EP5IsN6Vnjd0VNIt1tu3TduS8kH5npkPqZz8oIP93Ypxn0l7ZNEl9MahbhPj3gJ1YY7Cygrlt1VLC1ibBbOgIS2Lj6vGG/Yjkqs3Vw6qrmTRlsJ9c6bZO2xq0xzV11XQHvjPegBOClF6AztEe1jKU/RUFnzjIF8lUmM63fTaXuVkNERkTSE3E9XL3Uq6eqYdef7wHFFhCMSGotp3ANAb30kflysA9ID0b3o5QU2tB8OBxBicXQy11lh+u204YJuvIzeTXo+JAad5TWFlJcsUlbPFppLQdhUpoWaJouBGJV36DJb9R34i9T8Ze5tnJUQgPmMkERyPvb/+v5j3s2hs1A9WO6/MqmZd70gudsX/1bqWT898vCCOdM+CspNVY7nHVUtde7C6BrHzphr/C1YBXHw==" - -var testAccInstanceBasic = fmt.Sprintf(` -resource "opc_compute_instance" "%s" { - name = "test" - label = "test" - shape = "oc3" - imageList = "/oracle/public/oel_6.4_2GB_v1" - sshKeys = ["${opc_compute_ssh_key.test_key.name}"] - attributes = "{\"foo\": \"bar\"}" - storage = { - index = 1 - volume = "${opc_compute_storage_volume.test_volume.name}" - } -} - -resource "opc_compute_storage_volume" "test_volume" { - size = "3g" - description = "My volume" - name = "test_volume_b" - tags = ["foo", "bar", "baz"] -} - -resource "opc_compute_ssh_key" "%s" { - name = "test-key" - key = "%s" - enabled = true -} -`, instanceName, keyName, originalKey) - -var modifySSHKey = fmt.Sprintf(` -resource "opc_compute_instance" "%s" { - name = "test" - label = "test" - shape = "oc3" - imageList = "/oracle/public/oel_6.4_2GB_v1" - sshKeys = ["${opc_compute_ssh_key.test_key.name}"] - attributes = "{\"foo\": \"bar\"}" - storage = { - index = 1 - volume = "${opc_compute_storage_volume.test_volume.name}" - } -} - -resource "opc_compute_storage_volume" "test_volume" { - size = "3g" - description = "My volume" - name = "test_volume_b" - tags = ["foo", "bar", "baz"] -} - -resource "opc_compute_ssh_key" "%s" { - name = "test-key" - key = "%s" - enabled = true -} -`, instanceName, keyName, updatedKey) diff --git a/builtin/providers/oracleopc/resource_ip_association.go b/builtin/providers/oracleopc/resource_ip_association.go deleted file mode 100644 index 84df10ba8..000000000 --- a/builtin/providers/oracleopc/resource_ip_association.go +++ /dev/null @@ -1,103 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceIPAssociation() *schema.Resource { - return &schema.Resource{ - Create: resourceIPAssociationCreate, - Read: resourceIPAssociationRead, - Delete: resourceIPAssociationDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - - "vcable": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "parentpool": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - } -} - -func resourceIPAssociationCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - vcable, parentpool := getIPAssociationResourceData(d) - - log.Printf("[DEBUG] Creating ip association between vcable %s and parent pool %s", - vcable, parentpool) - - client := meta.(*OPCClient).IPAssociations() - info, err := client.CreateIPAssociation(vcable, parentpool) - 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) - updateIPAssociationResourceData(d, info) - return nil -} - -func updateIPAssociationResourceData(d *schema.ResourceData, info *compute.IPAssociationInfo) { - d.Set("name", info.Name) - d.Set("parentpool", info.ParentPool) - d.Set("vcable", info.VCable) -} - -func resourceIPAssociationRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).IPAssociations() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of ip association %s", name) - result, err := client.GetIPAssociation(name) - 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) - } - - log.Printf("[DEBUG] Read state of ip association %s: %#v", name, result) - updateIPAssociationResourceData(d, result) - return nil -} - -func getIPAssociationResourceData(d *schema.ResourceData) (string, string) { - return d.Get("vcable").(string), d.Get("parentpool").(string) -} - -func resourceIPAssociationDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).IPAssociations() - name := d.Get("name").(string) - - vcable, parentpool := getIPAssociationResourceData(d) - log.Printf("[DEBUG] Deleting ip association %s between vcable %s and parent pool %s", - name, vcable, parentpool) - - if err := client.DeleteIPAssociation(name); err != nil { - return fmt.Errorf("Error deleting ip association %s between vcable %s and parent pool %s: %s", - name, vcable, parentpool, err) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_ip_association_test.go b/builtin/providers/oracleopc/resource_ip_association_test.go deleted file mode 100644 index 44f48474f..000000000 --- a/builtin/providers/oracleopc/resource_ip_association_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/resource" - "testing" -) - -func TestAccOPCResourceIPAssociation_Basic(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: opcResourceCheck( - ipAssociationResourceName, - testAccCheckIPAssociationDestroyed), - Steps: []resource.TestStep{ - { - Config: testAccIPAssociationBasic, - Check: resource.ComposeTestCheckFunc( - opcResourceCheck( - ipAssociationResourceName, - testAccCheckIPAssociationExists), - ), - }, - }, - }) -} - -func testAccCheckIPAssociationExists(state *OPCResourceState) error { - associationName := getIPAssociationName(state) - - if _, err := state.IPAssociations().GetIPAssociation(associationName); err != nil { - return fmt.Errorf("Error retrieving state of ip assocation %s: %s", associationName, err) - } - - return nil -} - -func getIPAssociationName(rs *OPCResourceState) string { - return rs.Attributes["name"] -} - -func testAccCheckIPAssociationDestroyed(state *OPCResourceState) error { - associationName := getAssociationName(state) - if info, err := state.IPAssociations().GetIPAssociation(associationName); err == nil { - return fmt.Errorf("IP association %s still exists: %#v", associationName, info) - } - - return nil -} - -const ipAssociationName = "test_ip_association" - -var ipAssociationResourceName = fmt.Sprintf("opc_compute_ip_association.%s", ipAssociationName) - -var testAccIPAssociationBasic = fmt.Sprintf(` -resource "opc_compute_ip_reservation" "reservation1" { - parentpool = "/oracle/public/ippool" - permanent = true -} - -resource "opc_compute_ip_association" "%s" { - vcable = "${opc_compute_instance.test-instance1.vcable}" - parentpool = "ipreservation:${opc_compute_ip_reservation.reservation1.name}" -} - -resource "opc_compute_instance" "test-instance1" { - name = "test" - label = "test" - shape = "oc3" - imageList = "/oracle/public/oel_6.4_2GB_v1" -} -`, ipAssociationName) diff --git a/builtin/providers/oracleopc/resource_ip_reservation.go b/builtin/providers/oracleopc/resource_ip_reservation.go deleted file mode 100644 index fa25679d2..000000000 --- a/builtin/providers/oracleopc/resource_ip_reservation.go +++ /dev/null @@ -1,122 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceIPReservation() *schema.Resource { - return &schema.Resource{ - Create: resourceIPReservationCreate, - Read: resourceIPReservationRead, - Delete: resourceIPReservationDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - - "permanent": &schema.Schema{ - Type: schema.TypeBool, - Required: true, - ForceNew: true, - }, - - "parentpool": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "tags": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - - "ip": &schema.Schema{ - Type: schema.TypeString, - Optional: false, - Computed: true, - }, - }, - } -} - -func resourceIPReservationCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - parentpool, permanent, tags := getIPReservationResourceData(d) - - log.Printf("[DEBUG] Creating ip reservation from parentpool %s with tags=%s", - parentpool, tags) - - client := meta.(*OPCClient).IPReservations() - info, err := client.CreateIPReservation(parentpool, permanent, tags) - if err != nil { - return fmt.Errorf("Error creating ip reservation from parentpool %s with tags=%s: %s", - parentpool, tags, err) - } - - d.SetId(info.Name) - updateIPReservationResourceData(d, info) - return nil -} - -func updateIPReservationResourceData(d *schema.ResourceData, info *compute.IPReservationInfo) { - d.Set("name", info.Name) - d.Set("parentpool", info.ParentPool) - d.Set("permanent", info.Permanent) - d.Set("tags", info.Tags) - d.Set("ip", info.IP) -} - -func resourceIPReservationRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).IPReservations() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of ip reservation %s", name) - result, err := client.GetIPReservation(name) - if err != nil { - // IP Reservation does not exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading ip reservation %s: %s", name, err) - } - - log.Printf("[DEBUG] Read state of ip reservation %s: %#v", name, result) - updateIPReservationResourceData(d, result) - return nil -} - -func getIPReservationResourceData(d *schema.ResourceData) (string, bool, []string) { - tagdata := d.Get("tags").([]interface{}) - tags := make([]string, len(tagdata)) - for i, tag := range tagdata { - tags[i] = tag.(string) - } - return d.Get("parentpool").(string), - d.Get("permanent").(bool), - tags -} - -func resourceIPReservationDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).IPReservations() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Deleting ip reservation %s", name) - - if err := client.DeleteIPReservation(name); err != nil { - return fmt.Errorf("Error deleting ip reservation %s", name) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_security_application.go b/builtin/providers/oracleopc/resource_security_application.go deleted file mode 100644 index b7205754c..000000000 --- a/builtin/providers/oracleopc/resource_security_application.go +++ /dev/null @@ -1,124 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceSecurityApplication() *schema.Resource { - return &schema.Resource{ - Create: resourceSecurityApplicationCreate, - Read: resourceSecurityApplicationRead, - Delete: resourceSecurityApplicationDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "protocol": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "dport": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "icmptype": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "icmpcode": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "description": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - }, - } -} - -func resourceSecurityApplicationCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - name, protocol, dport, icmptype, icmpcode, description := getSecurityApplicationResourceData(d) - - log.Printf("[DEBUG] Creating security application %s", name) - - client := meta.(*OPCClient).SecurityApplications() - info, err := client.CreateSecurityApplication(name, protocol, dport, icmptype, icmpcode, description) - if err != nil { - return fmt.Errorf("Error creating security application %s: %s", name, err) - } - - d.SetId(info.Name) - updateSecurityApplicationResourceData(d, info) - return nil -} - -func updateSecurityApplicationResourceData(d *schema.ResourceData, info *compute.SecurityApplicationInfo) { - d.Set("name", info.Name) - d.Set("protocol", info.Protocol) - d.Set("dport", info.DPort) - d.Set("icmptype", info.ICMPType) - d.Set("icmpcode", info.ICMPCode) - d.Set("description", info.Description) -} - -func resourceSecurityApplicationRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityApplications() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of security application %s", name) - result, err := client.GetSecurityApplication(name) - if err != nil { - // Security Application does not exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading security application %s: %s", name, err) - } - - log.Printf("[DEBUG] Read state of security application %s: %#v", name, result) - updateSecurityApplicationResourceData(d, result) - return nil -} - -func getSecurityApplicationResourceData(d *schema.ResourceData) (string, string, string, string, string, string) { - return d.Get("name").(string), - d.Get("protocol").(string), - d.Get("dport").(string), - d.Get("icmptype").(string), - d.Get("icmpcode").(string), - d.Get("description").(string) -} - -func resourceSecurityApplicationDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityApplications() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Deleting security application %s", name) - - if err := client.DeleteSecurityApplication(name); err != nil { - return fmt.Errorf("Error deleting security application %s: %s", name, err) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_security_association.go b/builtin/providers/oracleopc/resource_security_association.go deleted file mode 100644 index 15a912657..000000000 --- a/builtin/providers/oracleopc/resource_security_association.go +++ /dev/null @@ -1,103 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceSecurityAssociation() *schema.Resource { - return &schema.Resource{ - Create: resourceSecurityAssociationCreate, - Read: resourceSecurityAssociationRead, - Delete: resourceSecurityAssociationDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - - "vcable": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "seclist": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - } -} - -func resourceSecurityAssociationCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - vcable, seclist := getSecurityAssociationResourceData(d) - - log.Printf("[DEBUG] Creating security association between vcable %s and security list %s", - vcable, seclist) - - client := meta.(*OPCClient).SecurityAssociations() - info, err := client.CreateSecurityAssociation(vcable, seclist) - if err != nil { - return fmt.Errorf("Error creating security association between vcable %s and security list %s: %s", - vcable, seclist, err) - } - - d.SetId(info.Name) - updateSecurityAssociationResourceData(d, info) - return nil -} - -func updateSecurityAssociationResourceData(d *schema.ResourceData, info *compute.SecurityAssociationInfo) { - d.Set("name", info.Name) - d.Set("seclist", info.SecList) - d.Set("vcable", info.VCable) -} - -func resourceSecurityAssociationRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityAssociations() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of security association %s", name) - result, err := client.GetSecurityAssociation(name) - if err != nil { - // Security Association does not exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading security association %s: %s", name, err) - } - - log.Printf("[DEBUG] Read state of security association %s: %#v", name, result) - updateSecurityAssociationResourceData(d, result) - return nil -} - -func getSecurityAssociationResourceData(d *schema.ResourceData) (string, string) { - return d.Get("vcable").(string), d.Get("seclist").(string) -} - -func resourceSecurityAssociationDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityAssociations() - name := d.Get("name").(string) - - vcable, seclist := getSecurityAssociationResourceData(d) - log.Printf("[DEBUG] Deleting security association %s between vcable %s and security list %s", - name, vcable, seclist) - - if err := client.DeleteSecurityAssociation(name); err != nil { - return fmt.Errorf("Error deleting security association %s between vcable %s and security list %s: %s", - name, vcable, seclist, err) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_security_association_test.go b/builtin/providers/oracleopc/resource_security_association_test.go deleted file mode 100644 index 604ef64cb..000000000 --- a/builtin/providers/oracleopc/resource_security_association_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/resource" - "testing" -) - -func TestAccOPCResourceSecurityAssociation_Basic(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: opcResourceCheck( - associationResourceName, - testAccCheckAssociationDestroyed), - Steps: []resource.TestStep{ - { - Config: testAccSecurityAssociationBasic, - Check: resource.ComposeTestCheckFunc( - opcResourceCheck( - associationResourceName, - testAccCheckAssociationExists), - ), - }, - }, - }) -} - -func testAccCheckAssociationExists(state *OPCResourceState) error { - associationName := getAssociationName(state) - - if _, err := state.SecurityAssociations().GetSecurityAssociation(associationName); err != nil { - return fmt.Errorf("Error retrieving state of security assocation %s: %s", associationName, err) - } - - return nil -} - -func getAssociationName(rs *OPCResourceState) string { - return rs.Attributes["name"] -} - -func testAccCheckAssociationDestroyed(state *OPCResourceState) error { - associationName := getAssociationName(state) - if info, err := state.SecurityAssociations().GetSecurityAssociation(associationName); err == nil { - return fmt.Errorf("Association %s still exists: %#v", associationName, info) - } - - return nil -} - -const associationName = "test_rule" - -var associationResourceName = fmt.Sprintf("opc_compute_security_association.%s", associationName) - -var testAccSecurityAssociationBasic = fmt.Sprintf(` -resource "opc_compute_security_list" "sec-list1" { - name = "sec-list-1" - policy = "PERMIT" - outbound_cidr_policy = "DENY" -} - -resource "opc_compute_security_association" "%s" { - vcable = "${opc_compute_instance.test-instance1.vcable}" - seclist = "${opc_compute_security_list.sec-list1.name}" -} - -resource "opc_compute_instance" "test-instance1" { - name = "test" - label = "test" - shape = "oc3" - imageList = "/oracle/public/oel_6.4_2GB_v1" -} -`, ruleName) diff --git a/builtin/providers/oracleopc/resource_security_ip_list.go b/builtin/providers/oracleopc/resource_security_ip_list.go deleted file mode 100644 index 6a3e66b28..000000000 --- a/builtin/providers/oracleopc/resource_security_ip_list.go +++ /dev/null @@ -1,117 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceSecurityIPList() *schema.Resource { - return &schema.Resource{ - Create: resourceSecurityIPListCreate, - Read: resourceSecurityIPListRead, - Update: resourceSecurityIPListUpdate, - Delete: resourceSecurityIPListDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "ip_entries": &schema.Schema{ - Type: schema.TypeList, - Required: true, - ForceNew: false, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - } -} - -func resourceSecurityIPListCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - name, ipEntries := getSecurityIPListResourceData(d) - - log.Printf("[DEBUG] Creating security IP list with name %s, entries %s", - name, ipEntries) - - client := meta.(*OPCClient).SecurityIPLists() - info, err := client.CreateSecurityIPList(name, ipEntries) - if err != nil { - return fmt.Errorf("Error creating security IP list %s: %s", name, err) - } - - d.SetId(info.Name) - updateSecurityIPListResourceData(d, info) - return nil -} - -func updateSecurityIPListResourceData(d *schema.ResourceData, info *compute.SecurityIPListInfo) { - d.Set("name", info.Name) - d.Set("entries", info.SecIPEntries) -} - -func resourceSecurityIPListRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityIPLists() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of security IP list %s", name) - result, err := client.GetSecurityIPList(name) - if err != nil { - // Security IP List does not exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading security IP list %s: %s", name, err) - } - - log.Printf("[DEBUG] Read state of security IP list %s: %#v", name, result) - updateSecurityIPListResourceData(d, result) - return nil -} - -func getSecurityIPListResourceData(d *schema.ResourceData) (string, []string) { - name := d.Get("name").(string) - ipEntries := d.Get("ip_entries").([]interface{}) - ipEntryStrings := []string{} - for _, entry := range ipEntries { - ipEntryStrings = append(ipEntryStrings, entry.(string)) - } - return name, ipEntryStrings -} - -func resourceSecurityIPListUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - client := meta.(*OPCClient).SecurityIPLists() - name, entries := getSecurityIPListResourceData(d) - - log.Printf("[DEBUG] Updating security IP list %s with ip entries %s", - name, entries) - - info, err := client.UpdateSecurityIPList(name, entries) - if err != nil { - return fmt.Errorf("Error updating security IP list %s: %s", name, err) - } - - updateSecurityIPListResourceData(d, info) - return nil -} - -func resourceSecurityIPListDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityIPLists() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Deleting security IP list %s", name) - if err := client.DeleteSecurityIPList(name); err != nil { - return fmt.Errorf("Error deleting security IP list %s: %s", name, err) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_security_list.go b/builtin/providers/oracleopc/resource_security_list.go deleted file mode 100644 index eea11bbb1..000000000 --- a/builtin/providers/oracleopc/resource_security_list.go +++ /dev/null @@ -1,119 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceSecurityList() *schema.Resource { - return &schema.Resource{ - Create: resourceSecurityListCreate, - Read: resourceSecurityListRead, - Update: resourceSecurityListUpdate, - Delete: resourceSecurityListDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "policy": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: false, - }, - - "outbound_cidr_policy": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: false, - }, - }, - } -} - -func resourceSecurityListCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - name, policy, outboundCIDRPolicy := getSecurityListResourceData(d) - - log.Printf("[DEBUG] Creating security list with name %s, policy %s, outbound CIDR policy %s", - name, policy, outboundCIDRPolicy) - - client := meta.(*OPCClient).SecurityLists() - info, err := client.CreateSecurityList(name, policy, outboundCIDRPolicy) - if err != nil { - return fmt.Errorf("Error creating security list %s: %s", name, err) - } - - d.SetId(info.Name) - updateSecurityListResourceData(d, info) - return nil -} - -func updateSecurityListResourceData(d *schema.ResourceData, info *compute.SecurityListInfo) { - d.Set("name", info.Name) - d.Set("policy", info.Policy) - d.Set("outbound_cidr_policy", info.OutboundCIDRPolicy) -} - -func resourceSecurityListRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityLists() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of security list %s", name) - result, err := client.GetSecurityList(name) - if err != nil { - // Security List does not exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading security list %s: %s", name, err) - } - - log.Printf("[DEBUG] Read state of ssh key %s: %#v", name, result) - updateSecurityListResourceData(d, result) - return nil -} - -func getSecurityListResourceData(d *schema.ResourceData) (string, string, string) { - return d.Get("name").(string), - d.Get("policy").(string), - d.Get("outbound_cidr_policy").(string) -} - -func resourceSecurityListUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - client := meta.(*OPCClient).SecurityLists() - name, policy, outboundCIDRPolicy := getSecurityListResourceData(d) - - log.Printf("[DEBUG] Updating security list %s with policy %s, outbound_cidr_policy %s", - name, policy, outboundCIDRPolicy) - - info, err := client.UpdateSecurityList(name, policy, outboundCIDRPolicy) - if err != nil { - return fmt.Errorf("Error updating security list %s: %s", name, err) - } - - updateSecurityListResourceData(d, info) - return nil -} - -func resourceSecurityListDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityLists() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Deleting ssh key volume %s", name) - if err := client.DeleteSecurityList(name); err != nil { - return fmt.Errorf("Error deleting security list %s: %s", name, err) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_security_rule.go b/builtin/providers/oracleopc/resource_security_rule.go deleted file mode 100644 index 0d9eb562c..000000000 --- a/builtin/providers/oracleopc/resource_security_rule.go +++ /dev/null @@ -1,143 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceSecurityRule() *schema.Resource { - return &schema.Resource{ - Create: resourceSecurityRuleCreate, - Read: resourceSecurityRuleRead, - Update: resourceSecurityRuleUpdate, - Delete: resourceSecurityRuleDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "source_list": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "destination_list": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "application": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "action": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: false, - }, - - "disabled": &schema.Schema{ - Type: schema.TypeBool, - Required: true, - ForceNew: false, - }, - }, - } -} - -func resourceSecurityRuleCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - name, sourceList, destinationList, application, action, disabled := getSecurityRuleResourceData(d) - - log.Printf("[DEBUG] Creating security list with name %s, sourceList %s, destinationList %s, application %s, action %s, disabled %s", - name, sourceList, destinationList, application, action, disabled) - - client := meta.(*OPCClient).SecurityRules() - info, err := client.CreateSecurityRule(name, sourceList, destinationList, application, action, disabled) - if err != nil { - return fmt.Errorf("Error creating security rule %s: %s", name, err) - } - - d.SetId(info.Name) - updateSecurityRuleResourceData(d, info) - return nil -} - -func updateSecurityRuleResourceData(d *schema.ResourceData, info *compute.SecurityRuleInfo) { - d.Set("name", info.Name) - d.Set("source_list", info.SourceList) - d.Set("destination_list", info.DestinationList) - d.Set("application", info.Application) - d.Set("action", info.Action) - d.Set("disabled", info.Disabled) -} - -func resourceSecurityRuleRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityRules() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of security rule %s", name) - result, err := client.GetSecurityRule(name) - if err != nil { - // Security Rule does not exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading security list %s: %s", name, err) - } - - log.Printf("[DEBUG] Read state of ssh key %s: %#v", name, result) - updateSecurityRuleResourceData(d, result) - return nil -} - -func getSecurityRuleResourceData(d *schema.ResourceData) (string, string, string, string, string, bool) { - return d.Get("name").(string), - d.Get("source_list").(string), - d.Get("destination_list").(string), - d.Get("application").(string), - d.Get("action").(string), - d.Get("disabled").(bool) -} - -func resourceSecurityRuleUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - - client := meta.(*OPCClient).SecurityRules() - name, sourceList, destinationList, application, action, disabled := getSecurityRuleResourceData(d) - - log.Printf("[DEBUG] Updating security list %s with sourceList %s, destinationList %s, application %s, action %s, disabled %s", - name, sourceList, destinationList, application, action, disabled) - - info, err := client.UpdateSecurityRule(name, sourceList, destinationList, application, action, disabled) - if err != nil { - return fmt.Errorf("Error updating security rule %s: %s", name, err) - } - - updateSecurityRuleResourceData(d, info) - return nil -} - -func resourceSecurityRuleDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource state: %#v", d.State()) - client := meta.(*OPCClient).SecurityRules() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Deleting ssh key volume %s", name) - if err := client.DeleteSecurityRule(name); err != nil { - return fmt.Errorf("Error deleting security rule %s: %s", name, err) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_security_rule_test.go b/builtin/providers/oracleopc/resource_security_rule_test.go deleted file mode 100644 index f09c2b879..000000000 --- a/builtin/providers/oracleopc/resource_security_rule_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/resource" - "testing" -) - -func TestAccOPCResourceSecurityRule_Basic(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: opcResourceCheck( - ruleResourceName, - testAccCheckRuleDestroyed), - Steps: []resource.TestStep{ - { - Config: testAccSecurityRuleBasic, - Check: resource.ComposeTestCheckFunc( - opcResourceCheck( - ruleResourceName, - testAccCheckRuleExists), - ), - }, - }, - }) -} - -func testAccCheckRuleExists(state *OPCResourceState) error { - ruleName := getRuleName(state) - - if _, err := state.SecurityRules().GetSecurityRule(ruleName); err != nil { - return fmt.Errorf("Error retrieving state of security rule %s: %s", ruleName, err) - } - - return nil -} - -func getRuleName(rs *OPCResourceState) string { - return rs.Attributes["name"] -} - -func testAccCheckRuleDestroyed(state *OPCResourceState) error { - ruleName := getRuleName(state) - if info, err := state.SecurityRules().GetSecurityRule(ruleName); err == nil { - return fmt.Errorf("Rule %s still exists: %#v", ruleName, info) - } - - return nil -} - -const ruleName = "test_rule" -const secListName = "sec-list1" -const secIpListName = "sec-ip-list1" - -var ruleResourceName = fmt.Sprintf("opc_compute_security_rule.%s", ruleName) - -var testAccSecurityRuleBasic = fmt.Sprintf(` -resource "opc_compute_security_rule" "%s" { - name = "test" - source_list = "seclist:${opc_compute_security_list.sec-list1.name}" - destination_list = "seciplist:${opc_compute_security_ip_list.sec-ip-list1.name}" - action = "PERMIT" - application = "${opc_compute_security_application.spring-boot.name}" - disabled = false -} - -resource "opc_compute_security_list" "%s" { - name = "sec-list-1" - policy = "PERMIT" - outbound_cidr_policy = "DENY" -} - -resource "opc_compute_security_application" "spring-boot" { - name = "spring-boot" - protocol = "tcp" - dport = "8080" -} - -resource "opc_compute_security_ip_list" "%s" { - name = "sec-ip-list1" - ip_entries = ["217.138.34.4"] -} -`, ruleName, secListName, secIpListName) diff --git a/builtin/providers/oracleopc/resource_ssh_key.go b/builtin/providers/oracleopc/resource_ssh_key.go deleted file mode 100644 index 29f68b4aa..000000000 --- a/builtin/providers/oracleopc/resource_ssh_key.go +++ /dev/null @@ -1,117 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceSSHKey() *schema.Resource { - return &schema.Resource{ - Create: resourceSSHKeyCreate, - Read: resourceSSHKeyRead, - Update: resourceSSHKeyUpdate, - Delete: resourceSSHKeyDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "key": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: false, - }, - - "enabled": &schema.Schema{ - Type: schema.TypeBool, - Required: true, - ForceNew: false, - }, - }, - } -} - -func resourceSSHKeyCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d) - - client := meta.(*OPCClient).SSHKeys() - name := d.Get("name").(string) - key := d.Get("key").(string) - enabled := d.Get("enabled").(bool) - - log.Printf("[DEBUG] Creating ssh key with name %s, key %s, enabled %s", - name, key, enabled) - - info, err := client.CreateSSHKey(name, key, enabled) - if err != nil { - return fmt.Errorf("Error creating ssh key %s: %s", name, err) - } - - d.SetId(info.Name) - updateSSHKeyResourceData(d, info) - return nil -} - -func updateSSHKeyResourceData(d *schema.ResourceData, info *compute.SSHKeyInfo) { - d.Set("name", info.Name) - d.Set("key", info.Key) - d.Set("enabled", info.Enabled) -} - -func resourceSSHKeyRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d) - client := meta.(*OPCClient).SSHKeys() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of ssh key %s", name) - result, err := client.GetSSHKey(name) - if err != nil { - // SSH Key does not exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading ssh key %s: %s", name, err) - } - - log.Printf("[DEBUG] Read state of ssh key %s: %#v", name, result) - updateSSHKeyResourceData(d, result) - return nil -} - -func resourceSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d) - - client := meta.(*OPCClient).SSHKeys() - name := d.Get("name").(string) - key := d.Get("key").(string) - enabled := d.Get("enabled").(bool) - - log.Printf("[DEBUG] Updating ssh key with name %s, key %s, enabled %s", - name, key, enabled) - - info, err := client.UpdateSSHKey(name, key, enabled) - if err != nil { - return fmt.Errorf("Error updating ssh key %s: %s", name, err) - } - - updateSSHKeyResourceData(d, info) - return nil -} - -func resourceSSHKeyDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d) - client := meta.(*OPCClient).SSHKeys() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Deleting ssh key volume %s", name) - if err := client.DeleteSSHKey(name); err != nil { - return fmt.Errorf("Error deleting ssh key %s: %s", name, err) - } - return nil -} diff --git a/builtin/providers/oracleopc/resource_storage_volume.go b/builtin/providers/oracleopc/resource_storage_volume.go deleted file mode 100644 index 2d80d09f2..000000000 --- a/builtin/providers/oracleopc/resource_storage_volume.go +++ /dev/null @@ -1,301 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/schema" - "github.com/oracle/terraform-provider-compute/sdk/compute" - "log" -) - -func resourceStorageVolume() *schema.Resource { - return &schema.Resource{ - Create: resourceStorageVolumeCreate, - Read: resourceStorageVolumeRead, - Update: resourceStorageVolumeUpdate, - Delete: resourceStorageVolumeDelete, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "size": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "sizeInBytes": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - - "description": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: false, - }, - - "storage": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: "/oracle/public/storage/default", - }, - - "tags": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - ForceNew: false, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - - "bootableImage": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "bootableImageVersion": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - Default: -1, - }, - - "snapshot": &schema.Schema{ - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "account": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - }, - }, - - "snapshotId": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - }, - } -} - -func resourceStorageVolumeCreate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] Resource data: %#v", d) - - sv := meta.(*OPCClient).StorageVolumes() - name := d.Get("name").(string) - properties := []string{d.Get("storage").(string)} - - spec := sv.NewStorageVolumeSpec( - d.Get("size").(string), - properties, - name) - - if d.Get("description").(string) != "" { - spec.SetDescription(d.Get("description").(string)) - } - - spec.SetTags(getTags(d)) - - if d.Get("bootableImage") != "" { - spec.SetBootableImage(d.Get("bootableImage").(string), d.Get("bootableImageVersion").(int)) - } - - if len(d.Get("snapshot").(*schema.Set).List()) > 0 { - snapshotDetails := d.Get("snapshot").(*schema.Set).List()[0].(map[string]interface{}) - spec.SetSnapshot( - snapshotDetails["name"].(string), - snapshotDetails["account"].(string), - ) - } - - if d.Get("snapshotId") != "" { - spec.SetSnapshotID(d.Get("snapshotId").(string)) - } - - log.Printf("[DEBUG] Creating storage volume %s with spec %#v", name, spec) - err := sv.CreateStorageVolume(spec) - if err != nil { - return fmt.Errorf("Error creating storage volume %s: %s", name, err) - } - - log.Printf("[DEBUG] Waiting for storage volume %s to come online", name) - info, err := sv.WaitForStorageVolumeOnline(name, meta.(*OPCClient).MaxRetryTimeout) - if err != nil { - return fmt.Errorf("Error waiting for storage volume %s to come online: %s", name, err) - } - - log.Printf("[DEBUG] Created storage volume %s: %#v", name, info) - - cachedAttachments, attachmentsFound := meta.(*OPCClient).storageAttachmentsByVolumeCache[name] - if attachmentsFound { - log.Printf("[DEBUG] Rebuilding storage attachments for volume %s", name) - for _, cachedAttachment := range cachedAttachments { - log.Printf("[DEBUG] Rebuilding storage attachments between volume %s and instance %s", - name, - cachedAttachment.instanceName) - - attachmentInfo, err := meta.(*OPCClient).StorageAttachments().CreateStorageAttachment( - cachedAttachment.index, - cachedAttachment.instanceName, - name, - ) - - if err != nil { - return fmt.Errorf( - "Error recreating storage attachment between volume %s and instance %s: %s", - name, - *cachedAttachment.instanceName, - err) - } - err = meta.(*OPCClient).StorageAttachments().WaitForStorageAttachmentCreated( - attachmentInfo.Name, - meta.(*OPCClient).MaxRetryTimeout) - if err != nil { - return fmt.Errorf( - "Error recreating storage attachment between volume %s and instance %s: %s", - name, - *cachedAttachment.instanceName, - err) - } - } - meta.(*OPCClient).storageAttachmentsByVolumeCache[name] = nil - } - - d.SetId(name) - updateResourceData(d, info) - return nil -} - -func getTags(d *schema.ResourceData) []string { - tags := []string{} - for _, i := range d.Get("tags").([]interface{}) { - tags = append(tags, i.(string)) - } - return tags -} - -func updateResourceData(d *schema.ResourceData, info *compute.StorageVolumeInfo) error { - d.Set("name", info.Name) - d.Set("description", info.Description) - d.Set("storage", info.Properties[0]) - d.Set("sizeInBytes", info.Size) - d.Set("tags", info.Tags) - d.Set("bootableImage", info.ImageList) - d.Set("bootableImageVersion", info.ImageListEntry) - if info.Snapshot != "" { - d.Set("snapshot", map[string]interface{}{ - "name": info.Snapshot, - "account": info.SnapshotAccount, - }) - } - d.Set("snapshotId", info.SnapshotID) - - return nil -} - -func resourceStorageVolumeRead(d *schema.ResourceData, meta interface{}) error { - sv := meta.(*OPCClient).StorageVolumes() - name := d.Get("name").(string) - - log.Printf("[DEBUG] Reading state of storage volume %s", name) - result, err := sv.GetStorageVolume(name) - if err != nil { - // Volume doesn't exist - if compute.WasNotFoundError(err) { - d.SetId("") - return nil - } - return fmt.Errorf("Error reading storage volume %s: %s", name, err) - } - - if len(result.Result) == 0 { - // Volume doesn't exist - d.SetId("") - return nil - } - - log.Printf("[DEBUG] Read state of storage volume %s: %#v", name, &result.Result[0]) - updateResourceData(d, &result.Result[0]) - - return nil -} - -func resourceStorageVolumeUpdate(d *schema.ResourceData, meta interface{}) error { - sv := meta.(*OPCClient).StorageVolumes() - name := d.Get("name").(string) - description := d.Get("description").(string) - size := d.Get("size").(string) - tags := getTags(d) - - log.Printf("[DEBUG] Updating storage volume %s with size %s, description %s, tags %#v", name, size, description, tags) - err := sv.UpdateStorageVolume(name, size, description, tags) - - if err != nil { - return fmt.Errorf("Error updating storage volume %s: %s", name, err) - } - - log.Printf("[DEBUG] Waiting for updated storage volume %s to come online", name) - info, err := sv.WaitForStorageVolumeOnline(name, meta.(*OPCClient).MaxRetryTimeout) - if err != nil { - return fmt.Errorf("Error waiting for updated storage volume %s to come online: %s", name, err) - } - - log.Printf("[DEBUG] Updated storage volume %s: %#v", name, info) - updateResourceData(d, info) - return nil -} - -func resourceStorageVolumeDelete(d *schema.ResourceData, meta interface{}) error { - sv := meta.(*OPCClient).StorageVolumes() - name := d.Get("name").(string) - - sva := meta.(*OPCClient).StorageAttachments() - attachments, err := sva.GetStorageAttachmentsForVolume(name) - if err != nil { - return fmt.Errorf("Error retrieving storage attachments for volume %s: %s", name, err) - } - - attachmentsToCache := make([]storageAttachment, len(*attachments)) - for index, attachment := range *attachments { - log.Printf("[DEBUG] Deleting storage attachment %s for volume %s", attachment.Name, name) - sva.DeleteStorageAttachment(attachment.Name) - sva.WaitForStorageAttachmentDeleted(attachment.Name, meta.(*OPCClient).MaxRetryTimeout) - attachmentsToCache[index] = storageAttachment{ - index: attachment.Index, - instanceName: compute.InstanceNameFromString(attachment.InstanceName), - } - } - meta.(*OPCClient).storageAttachmentsByVolumeCache[name] = attachmentsToCache - - log.Printf("[DEBUG] Deleting storage volume %s", name) - err = sv.DeleteStorageVolume(name) - if err != nil { - return fmt.Errorf("Error deleting storage volume %s: %s", name, err) - } - - log.Printf("[DEBUG] Waiting for storage volume %s to finish deleting", name) - err = sv.WaitForStorageVolumeDeleted(name, meta.(*OPCClient).MaxRetryTimeout) - if err != nil { - return fmt.Errorf("Error waiting for storage volume %s to finish deleting: %s", name, err) - } - - log.Printf("[DEBUG] Deleted storage volume %s", name) - return nil -} diff --git a/builtin/providers/oracleopc/resource_storage_volume_test.go b/builtin/providers/oracleopc/resource_storage_volume_test.go deleted file mode 100644 index d168b5309..000000000 --- a/builtin/providers/oracleopc/resource_storage_volume_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package opc - -import ( - "fmt" - "github.com/hashicorp/terraform/helper/resource" - "testing" -) - -func TestAccOPCStorageVolume_Basic(t *testing.T) { - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: opcResourceCheck( - "opc_compute_storage_volume.test_volume", - testAccCheckStorageVolumeDestroyed), - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccStorageVolumeBasic, - Check: resource.ComposeTestCheckFunc( - opcResourceCheck( - "opc_compute_storage_volume.test_volume", - testAccCheckStorageVolumeExists), - ), - }, - }, - }) -} - -func testAccCheckStorageVolumeExists(state *OPCResourceState) error { - sv := state.StorageVolumes() - volumeName := state.Attributes["name"] - - info, err := sv.GetStorageVolume(volumeName) - if err != nil { - return fmt.Errorf("Error retrieving state of volume %s: %s", volumeName, err) - } - - if len(info.Result) == 0 { - return fmt.Errorf("No info found for volume %s", volumeName) - } - - return nil -} - -func testAccCheckStorageVolumeDestroyed(state *OPCResourceState) error { - sv := state.StorageVolumes() - - volumeName := state.Attributes["name"] - - info, err := sv.GetStorageVolume(volumeName) - if err != nil { - return fmt.Errorf("Error retrieving state of volume %s: %s", volumeName, err) - } - - if len(info.Result) != 0 { - return fmt.Errorf("Volume %s still exists", volumeName) - } - - return nil -} - -const testAccStorageVolumeBasic = ` -resource "opc_compute_storage_volume" "test_volume" { - size = "3g" - description = "My volume" - name = "test_volume_b" - tags = ["foo", "bar", "baz"] -} -` diff --git a/command/internal_plugin_list.go b/command/internal_plugin_list.go index 2f48908c7..0d6f631b8 100644 --- a/command/internal_plugin_list.go +++ b/command/internal_plugin_list.go @@ -46,6 +46,7 @@ import ( nomadprovider "github.com/hashicorp/terraform/builtin/providers/nomad" ns1provider "github.com/hashicorp/terraform/builtin/providers/ns1" nullprovider "github.com/hashicorp/terraform/builtin/providers/null" + opcprovider "github.com/hashicorp/terraform/builtin/providers/opc" openstackprovider "github.com/hashicorp/terraform/builtin/providers/openstack" opsgenieprovider "github.com/hashicorp/terraform/builtin/providers/opsgenie" packetprovider "github.com/hashicorp/terraform/builtin/providers/packet" @@ -122,6 +123,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{ "nomad": nomadprovider.Provider, "ns1": ns1provider.Provider, "null": nullprovider.Provider, + "opc": opcprovider.Provider, "openstack": openstackprovider.Provider, "opsgenie": opsgenieprovider.Provider, "packet": packetprovider.Provider, diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/LICENSE b/vendor/github.com/hashicorp/go-oracle-terraform/LICENSE new file mode 100644 index 000000000..a612ad981 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go new file mode 100644 index 000000000..5543c4046 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go @@ -0,0 +1,138 @@ +package compute + +// ACLsClient is a client for the ACLs functions of the Compute API. +type ACLsClient struct { + ResourceClient +} + +const ( + ACLDescription = "acl" + ACLContainerPath = "/network/v1/acl/" + ACLResourcePath = "/network/v1/acl" +) + +// ACLs obtains a ACLsClient which can be used to access to the +// ACLs functions of the Compute API +func (c *Client) ACLs() *ACLsClient { + return &ACLsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: ACLDescription, + ContainerPath: ACLContainerPath, + ResourceRootPath: ACLResourcePath, + }} +} + +// ACLInfo describes an existing ACL. +type ACLInfo struct { + // Description of the ACL + Description string `json:"description"` + // Indicates whether the ACL is enabled + Enabled bool `json:"enabledFlag"` + // The name of the ACL + Name string `json:"name"` + // Tags associated with the ACL + Tags []string `json:"tags"` + // Uniform Resource Identifier for the ACL + URI string `json:"uri"` +} + +// CreateACLInput defines a ACL to be created. +type CreateACLInput struct { + // Description of the ACL + // Optional + Description string `json:"description"` + + // Enables or disables the ACL. Set to true by default. + //Set this to false to disable the ACL. + // Optional + Enabled bool `json:"enabledFlag"` + + // The name of the ACL to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Strings that you can use to tag the ACL. + // Optional + Tags []string `json:"tags"` +} + +// CreateACL creates a new ACL. +func (c *ACLsClient) CreateACL(createInput *CreateACLInput) (*ACLInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + + var aclInfo ACLInfo + if err := c.createResource(createInput, &aclInfo); err != nil { + return nil, err + } + + return c.success(&aclInfo) +} + +// GetACLInput describes the ACL to get +type GetACLInput struct { + // The name of the ACL to query for + // Required + Name string `json:"name"` +} + +// GetACL retrieves the ACL with the given name. +func (c *ACLsClient) GetACL(getInput *GetACLInput) (*ACLInfo, error) { + var aclInfo ACLInfo + if err := c.getResource(getInput.Name, &aclInfo); err != nil { + return nil, err + } + + return c.success(&aclInfo) +} + +// UpdateACLInput describes a secruity rule to update +type UpdateACLInput struct { + // Description of the ACL + // Optional + Description string `json:"description"` + + // Enables or disables the ACL. Set to true by default. + //Set this to false to disable the ACL. + // Optional + Enabled bool `json:"enabledFlag"` + + // The name of the ACL to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Strings that you can use to tag the ACL. + // Optional + Tags []string `json:"tags"` +} + +// UpdateACL modifies the properties of the ACL with the given name. +func (c *ACLsClient) UpdateACL(updateInput *UpdateACLInput) (*ACLInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + + var aclInfo ACLInfo + if err := c.updateResource(updateInput.Name, updateInput, &aclInfo); err != nil { + return nil, err + } + + return c.success(&aclInfo) +} + +// DeleteACLInput describes the ACL to delete +type DeleteACLInput struct { + // The name of the ACL to delete. + // Required + Name string `json:"name"` +} + +// DeleteACL deletes the ACL with the given name. +func (c *ACLsClient) DeleteACL(deleteInput *DeleteACLInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *ACLsClient) success(aclInfo *ACLInfo) (*ACLInfo, error) { + aclInfo.Name = c.getUnqualifiedName(aclInfo.Name) + return aclInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go new file mode 100644 index 000000000..d1b355087 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go @@ -0,0 +1,34 @@ +package compute + +import ( + "fmt" + "time" +) + +// AuthenticationReq represents the body of an authentication request. +type AuthenticationReq struct { + User string `json:"user"` + Password string `json:"password"` +} + +// Get a new auth cookie for the compute client +func (c *Client) getAuthenticationCookie() error { + req := AuthenticationReq{ + User: c.getUserName(), + Password: *c.password, + } + + rsp, err := c.executeRequest("POST", "/authenticate/", req) + if err != nil { + return err + } + + if len(rsp.Cookies()) == 0 { + return fmt.Errorf("No authentication cookie found in response %#v", rsp) + } + + c.debugLogString("Successfully authenticated to OPC") + c.authCookie = rsp.Cookies()[0] + c.cookieIssued = time.Now() + return nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/client.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/client.go new file mode 100644 index 000000000..015b87acd --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/client.go @@ -0,0 +1,238 @@ +package compute + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "regexp" + "strings" + "time" + + "github.com/hashicorp/go-oracle-terraform/opc" +) + +const CMP_USERNAME = "/Compute-%s/%s" +const CMP_QUALIFIED_NAME = "%s/%s" + +// Client represents an authenticated compute client, with compute credentials and an api client. +type Client struct { + identityDomain *string + userName *string + password *string + apiEndpoint *url.URL + httpClient *http.Client + authCookie *http.Cookie + cookieIssued time.Time + logger opc.Logger + loglevel opc.LogLevelType +} + +func NewComputeClient(c *opc.Config) (*Client, error) { + // First create a client + client := &Client{ + identityDomain: c.IdentityDomain, + userName: c.Username, + password: c.Password, + apiEndpoint: c.APIEndpoint, + httpClient: c.HTTPClient, + loglevel: c.LogLevel, + } + + // Setup logger; defaults to stdout + if c.Logger == nil { + client.logger = opc.NewDefaultLogger() + } + + // If LogLevel was not set to something different, + // double check for env var + if c.LogLevel == 0 { + client.loglevel = opc.LogLevel() + } + + if err := client.getAuthenticationCookie(); err != nil { + return nil, err + } + + return client, nil +} + +func (c *Client) executeRequest(method, path string, body interface{}) (*http.Response, error) { + // Parse URL Path + urlPath, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Marshall request body + var requestBody io.ReadSeeker + var marshaled []byte + if body != nil { + marshaled, err = json.Marshal(body) + if err != nil { + return nil, err + } + requestBody = bytes.NewReader(marshaled) + } + + // Create request + req, err := http.NewRequest(method, c.formatURL(urlPath), requestBody) + if err != nil { + return nil, err + } + + debugReqString := fmt.Sprintf("HTTP %s Req (%s)", method, path) + if body != nil { + req.Header.Set("Content-Type", "application/oracle-compute-v3+json") + // Don't leak creds in STDERR + if path != "/authenticate/" { + debugReqString = fmt.Sprintf("%s:\n %s", debugReqString, string(marshaled)) + } + } + + // Log the request before the authentication cookie, so as not to leak credentials + c.debugLogString(debugReqString) + + // If we have an authentication cookie, let's authenticate, refreshing cookie if need be + if c.authCookie != nil { + if time.Since(c.cookieIssued).Minutes() > 25 { + if err := c.getAuthenticationCookie(); err != nil { + return nil, err + } + } + req.AddCookie(c.authCookie) + } + + // Execute request with supplied client + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices { + return resp, nil + } + + oracleErr := &opc.OracleError{ + StatusCode: resp.StatusCode, + } + + // Even though the returned body will be in json form, it's undocumented what + // fields are actually returned. Once we get documentation of the actual + // error fields that are possible to be returned we can have stricter error types. + if resp.Body != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + oracleErr.Message = buf.String() + } + + return nil, oracleErr +} + +func (c *Client) formatURL(path *url.URL) string { + return c.apiEndpoint.ResolveReference(path).String() +} + +func (c *Client) getUserName() string { + return fmt.Sprintf(CMP_USERNAME, *c.identityDomain, *c.userName) +} + +// From compute_client +// GetObjectName returns the fully-qualified name of an OPC object, e.g. /identity-domain/user@email/{name} +func (c *Client) getQualifiedName(name string) string { + if name == "" { + return "" + } + if strings.HasPrefix(name, "/oracle") || strings.HasPrefix(name, "/Compute-") { + return name + } + return fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), name) +} + +func (c *Client) getObjectPath(root, name string) string { + return fmt.Sprintf("%s%s", root, c.getQualifiedName(name)) +} + +// GetUnqualifiedName returns the unqualified name of an OPC object, e.g. the {name} part of /identity-domain/user@email/{name} +func (c *Client) getUnqualifiedName(name string) string { + if name == "" { + return name + } + if strings.HasPrefix(name, "/oracle") { + return name + } + if !strings.Contains(name, "/") { + return name + } + + nameParts := strings.Split(name, "/") + return strings.Join(nameParts[3:], "/") +} + +func (c *Client) unqualify(names ...*string) { + for _, name := range names { + *name = c.getUnqualifiedName(*name) + } +} + +func (c *Client) unqualifyUrl(url *string) { + var validID = regexp.MustCompile(`(\/(Compute[^\/\s]+))(\/[^\/\s]+)(\/[^\/\s]+)`) + name := validID.FindString(*url) + *url = c.getUnqualifiedName(name) +} + +func (c *Client) getQualifiedList(list []string) []string { + for i, name := range list { + list[i] = c.getQualifiedName(name) + } + return list +} + +func (c *Client) getUnqualifiedList(list []string) []string { + for i, name := range list { + list[i] = c.getUnqualifiedName(name) + } + return list +} + +func (c *Client) getQualifiedListName(name string) string { + nameParts := strings.Split(name, ":") + listType := nameParts[0] + listName := nameParts[1] + return fmt.Sprintf("%s:%s", listType, c.getQualifiedName(listName)) +} + +func (c *Client) unqualifyListName(qualifiedName string) string { + nameParts := strings.Split(qualifiedName, ":") + listType := nameParts[0] + listName := nameParts[1] + return fmt.Sprintf("%s:%s", listType, c.getUnqualifiedName(listName)) +} + +// Retry function +func (c *Client) waitFor(description string, timeoutSeconds int, test func() (bool, error)) error { + tick := time.Tick(1 * time.Second) + + for i := 0; i < timeoutSeconds; i++ { + select { + case <-tick: + completed, err := test() + c.debugLogString(fmt.Sprintf("Waiting for %s (%d/%ds)", description, i, timeoutSeconds)) + if err != nil || completed { + return err + } + } + } + return fmt.Errorf("Timeout waiting for %s", description) +} + +// Used to determine if the checked resource was found or not. +func WasNotFoundError(e error) bool { + err, ok := e.(*opc.OracleError) + if ok { + return err.StatusCode == 404 + } + return false +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go new file mode 100644 index 000000000..229b75195 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go @@ -0,0 +1,154 @@ +package compute + +const ( + ImageListDescription = "Image List" + ImageListContainerPath = "/imagelist/" + ImageListResourcePath = "/imagelist" +) + +// ImageListClient is a client for the Image List functions of the Compute API. +type ImageListClient struct { + ResourceClient +} + +// ImageList obtains an ImageListClient which can be used to access to the +// Image List functions of the Compute API +func (c *Client) ImageList() *ImageListClient { + return &ImageListClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: ImageListDescription, + ContainerPath: ImageListContainerPath, + ResourceRootPath: ImageListResourcePath, + }} +} + +type ImageListEntry struct { + // User-defined parameters, in JSON format, that can be passed to an instance of this machine image when it is launched. + Attributes map[string]interface{} `json:"attributes"` + + // Name of the Image List. + ImageList string `json:"imagelist"` + + // A list of machine images. + MachineImages []string `json:"machineimages"` + + // Uniform Resource Identifier. + URI string `json:"uri"` + + // Version number of these Machine Images in the Image List. + Version int `json:"version"` +} + +// ImageList describes an existing Image List. +type ImageList struct { + // The image list entry to be used, by default, when launching instances using this image list + Default int `json:"default"` + + // A description of this image list. + Description string `json:"description"` + + // Each machine image in an image list is identified by an image list entry. + Entries []ImageListEntry `json:"entries"` + + // The name of the Image List + Name string `json:"name"` + + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateImageListInput defines an Image List to be created. +type CreateImageListInput struct { + // The image list entry to be used, by default, when launching instances using this image list. + // If you don't specify this value, it is set to 1. + // Optional + Default int `json:"default"` + + // A description of this image list. + // Required + Description string `json:"description"` + + // The name of the Image List + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` +} + +// CreateImageList creates a new Image List with the given name, key and enabled flag. +func (c *ImageListClient) CreateImageList(createInput *CreateImageListInput) (*ImageList, error) { + var imageList ImageList + createInput.Name = c.getQualifiedName(createInput.Name) + if err := c.createResource(&createInput, &imageList); err != nil { + return nil, err + } + + return c.success(&imageList) +} + +// DeleteKeyInput describes the image list to delete +type DeleteImageListInput struct { + // The name of the Image List + Name string `json:name` +} + +// DeleteImageList deletes the Image List with the given name. +func (c *ImageListClient) DeleteImageList(deleteInput *DeleteImageListInput) error { + deleteInput.Name = c.getQualifiedName(deleteInput.Name) + return c.deleteResource(deleteInput.Name) +} + +// GetImageListInput describes the image list to get +type GetImageListInput struct { + // The name of the Image List + Name string `json:name` +} + +// GetImageList retrieves the Image List with the given name. +func (c *ImageListClient) GetImageList(getInput *GetImageListInput) (*ImageList, error) { + getInput.Name = c.getQualifiedName(getInput.Name) + + var imageList ImageList + if err := c.getResource(getInput.Name, &imageList); err != nil { + return nil, err + } + + return c.success(&imageList) +} + +// UpdateImageListInput defines an Image List to be updated +type UpdateImageListInput struct { + // The image list entry to be used, by default, when launching instances using this image list. + // If you don't specify this value, it is set to 1. + // Optional + Default int `json:"default"` + + // A description of this image list. + // Required + Description string `json:"description"` + + // The name of the Image List + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` +} + +// UpdateImageList updates the key and enabled flag of the Image List with the given name. +func (c *ImageListClient) UpdateImageList(updateInput *UpdateImageListInput) (*ImageList, error) { + var imageList ImageList + updateInput.Name = c.getQualifiedName(updateInput.Name) + if err := c.updateResource(updateInput.Name, updateInput, &imageList); err != nil { + return nil, err + } + return c.success(&imageList) +} + +func (c *ImageListClient) success(imageList *ImageList) (*ImageList, error) { + c.unqualify(&imageList.Name) + + for _, v := range imageList.Entries { + v.MachineImages = c.getUnqualifiedList(v.MachineImages) + } + + return imageList, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go new file mode 100644 index 000000000..bcc45b8f4 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go @@ -0,0 +1,122 @@ +package compute + +import "fmt" + +const ( + ImageListEntryDescription = "image list entry" + ImageListEntryContainerPath = "/imagelist" + ImageListEntryResourcePath = "/imagelist" +) + +type ImageListEntriesClient struct { + ResourceClient +} + +// ImageListEntries() returns an ImageListEntriesClient that can be used to access the +// necessary CRUD functions for Image List Entry's. +func (c *Client) ImageListEntries() *ImageListEntriesClient { + return &ImageListEntriesClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: ImageListEntryDescription, + ContainerPath: ImageListEntryContainerPath, + ResourceRootPath: ImageListEntryResourcePath, + }, + } +} + +// ImageListEntryInfo contains the exported fields necessary to hold all the information about an +// Image List Entry +type ImageListEntryInfo struct { + // User-defined parameters, in JSON format, that can be passed to an instance of this machine + // image when it is launched. This field can be used, for example, to specify the location of + // a database server and login details. Instance metadata, including user-defined data is available + // at http://192.0.0.192/ within an instance. See Retrieving User-Defined Instance Attributes in Using + // Oracle Compute Cloud Service (IaaS). + Attributes map[string]interface{} `json:"attributes"` + // Name of the imagelist. + Name string `json:"imagelist"` + // A list of machine images. + MachineImages []string `json:"machineimages"` + // Uniform Resource Identifier for the Image List Entry + Uri string `json:"uri"` + // Version number of these machineImages in the imagelist. + Version int `json:"version"` +} + +type CreateImageListEntryInput struct { + // The name of the Image List + Name string + // User-defined parameters, in JSON format, that can be passed to an instance of this machine + // image when it is launched. This field can be used, for example, to specify the location of + // a database server and login details. Instance metadata, including user-defined data is + //available at http://192.0.0.192/ within an instance. See Retrieving User-Defined Instance + //Attributes in Using Oracle Compute Cloud Service (IaaS). + // Optional + Attributes map[string]interface{} `json:"attributes"` + // A list of machine images. + // Required + MachineImages []string `json:"machineimages"` + // The unique version of the entry in the image list. + // Required + Version int `json:"version"` +} + +// Create a new Image List Entry from an ImageListEntriesClient and an input struct. +// Returns a populated Info struct for the Image List Entry, and any errors +func (c *ImageListEntriesClient) CreateImageListEntry(input *CreateImageListEntryInput) (*ImageListEntryInfo, error) { + c.updateClientPaths(input.Name, -1) + var imageListEntryInfo ImageListEntryInfo + if err := c.createResource(&input, &imageListEntryInfo); err != nil { + return nil, err + } + return c.success(&imageListEntryInfo) +} + +type GetImageListEntryInput struct { + // The name of the Image List + Name string + // Version number of these machineImages in the imagelist. + Version int +} + +// Returns a populated ImageListEntryInfo struct from an input struct +func (c *ImageListEntriesClient) GetImageListEntry(input *GetImageListEntryInput) (*ImageListEntryInfo, error) { + c.updateClientPaths(input.Name, input.Version) + var imageListEntryInfo ImageListEntryInfo + if err := c.getResource("", &imageListEntryInfo); err != nil { + return nil, err + } + return c.success(&imageListEntryInfo) +} + +type DeleteImageListEntryInput struct { + // The name of the Image List + Name string + // Version number of these machineImages in the imagelist. + Version int +} + +func (c *ImageListEntriesClient) DeleteImageListEntry(input *DeleteImageListEntryInput) error { + c.updateClientPaths(input.Name, input.Version) + return c.deleteResource("") +} + +func (c *ImageListEntriesClient) updateClientPaths(name string, version int) { + var containerPath, resourcePath string + name = c.getQualifiedName(name) + containerPath = ImageListEntryContainerPath + name + "/entry/" + resourcePath = ImageListEntryContainerPath + name + "/entry" + if version != -1 { + containerPath = fmt.Sprintf("%s%d", containerPath, version) + resourcePath = fmt.Sprintf("%s/%d", resourcePath, version) + } + c.ContainerPath = containerPath + c.ResourceRootPath = resourcePath +} + +// Unqualifies any qualified fields in the IPNetworkInfo struct +func (c *ImageListEntriesClient) success(info *ImageListEntryInfo) (*ImageListEntryInfo, error) { + c.unqualifyUrl(&info.Uri) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go new file mode 100644 index 000000000..b697b36e3 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go @@ -0,0 +1,540 @@ +package compute + +import ( + "errors" + "fmt" + "strings" +) + +const WaitForInstanceReadyTimeout = 600 +const WaitForInstanceDeleteTimeout = 600 + +// InstancesClient is a client for the Instance functions of the Compute API. +type InstancesClient struct { + ResourceClient +} + +// Instances obtains an InstancesClient which can be used to access to the +// Instance functions of the Compute API +func (c *Client) Instances() *InstancesClient { + return &InstancesClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "instance", + ContainerPath: "/launchplan/", + ResourceRootPath: "/instance", + }} +} + +type InstanceState string + +const ( + InstanceRunning InstanceState = "running" + InstanceInitializing InstanceState = "initializing" + InstancePreparing InstanceState = "preparing" + InstanceStopping InstanceState = "stopping" + InstanceQueued InstanceState = "queued" + InstanceError InstanceState = "error" +) + +// InstanceInfo represents the Compute API's view of the state of an instance. +type InstanceInfo struct { + // The ID for the instance. Set by the SDK based on the request - not the API. + ID string + + // A dictionary of attributes to be made available to the instance. + // A value with the key "userdata" will be made available in an EC2-compatible manner. + Attributes map[string]interface{} `json:"attributes"` + + // The availability domain for the instance + AvailabilityDomain string `json:"availability_domain"` + + // Boot order list. + BootOrder []int `json:"boot_order"` + + // The default domain to use for the hostname and DNS lookups + Domain string `json:"domain"` + + // Optional ImageListEntry number. Default will be used if not specified + Entry int `json:"entry"` + + // The reason for the instance going to error state, if available. + ErrorReason string `json:"error_reason"` + + // SSH Server Fingerprint presented by the instance + Fingerprint string `json:"fingerprint"` + + // The hostname for the instance + Hostname string `json:"hostname"` + + // The format of the image + ImageFormat string `json:"image_format"` + + // Name of imagelist to be launched. + ImageList string `json:"imagelist"` + + // IP address of the instance. + IPAddress string `json:"ip"` + + // A label assigned by the user, specifically for defining inter-instance relationships. + Label string `json:"label"` + + // Name of this instance, generated by the server. + Name string `json:"name"` + + // Mapping of to network specifiers for virtual NICs to be attached to this instance. + Networking map[string]NetworkingInfo `json:"networking"` + + // A list of strings specifying arbitrary tags on nodes to be matched on placement. + PlacementRequirements []string `json:"placement_requirements"` + + // The OS platform for the instance. + Platform string `json:"platform"` + + // The priority at which this instance will be run + Priority string `json:"priority"` + + // Reference to the QuotaReservation, to be destroyed with the instance + QuotaReservation string `json:"quota_reservation"` + + // Array of relationship specifications to be satisfied on this instance's placement + Relationships []string `json:"relationships"` + + // Resolvers to use instead of the default resolvers + Resolvers []string `json:"resolvers"` + + // Add PTR records for the hostname + ReverseDNS bool `json:"reverse_dns"` + + // Type of instance, as defined on site configuration. + Shape string `json:"shape"` + + // Site to run on + Site string `json:"site"` + + // ID's of SSH keys that will be exposed to the instance. + SSHKeys []string `json:"sshkeys"` + + // The start time of the instance + StartTime string `json:"start_time"` + + // State of the instance. + State InstanceState `json:"state"` + + // The Storage Attachment information. + Storage []StorageAttachment `json:"storage_attachments"` + + // Array of tags associated with the instance. + Tags []string `json:"tags"` + + // vCable for this instance. + VCableID string `json:"vcable_id"` + + // Specify if the devices created for the instance are virtio devices. If not specified, the default + // will come from the cluster configuration file + Virtio bool `json:"virtio,omitempty"` + + // IP Address and port of the VNC console for the instance + VNC string `json:"vnc"` +} + +type StorageAttachment struct { + // The index number for the volume. + Index int `json:"index"` + + // The three-part name (/Compute-identity_domain/user/object) of the storage attachment. + Name string `json:"name"` + + // The three-part name (/Compute-identity_domain/user/object) of the storage volume attached to the instance. + StorageVolumeName string `json:"storage_volume_name"` +} + +func (i *InstanceInfo) getInstanceName() string { + return fmt.Sprintf(CMP_QUALIFIED_NAME, i.Name, i.ID) +} + +type CreateInstanceInput struct { + // A dictionary of user-defined attributes to be made available to the instance. + // Optional + Attributes map[string]interface{} `json:"attributes"` + // Boot order list + // Optional + BootOrder []int `json:"boot_order"` + // The host name assigned to the instance. On an Oracle Linux instance, + // this host name is displayed in response to the hostname command. + // Only relative DNS is supported. The domain name is suffixed to the host name + // that you specify. The host name must not end with a period. If you don't specify a + // host name, then a name is generated automatically. + // Optional + Hostname string `json:"hostname"` + // Name of imagelist to be launched. + // Optional + ImageList string `json:"imagelist"` + // A label assigned by the user, specifically for defining inter-instance relationships. + // Optional + Label string `json:"label"` + // Name of this instance, generated by the server. + // Optional + Name string `json:"name"` + // Networking information. + // Optional + Networking map[string]NetworkingInfo `json:"networking"` + // If set to true (default), then reverse DNS records are created. + // If set to false, no reverse DNS records are created. + // Optional + ReverseDNS bool `json:"reverse_dns,omitempty"` + // Type of instance, as defined on site configuration. + // Required + Shape string `json:"shape"` + // A list of the Storage Attachments you want to associate with the instance. + // Optional + Storage []StorageAttachmentInput `json:"storage_attachments"` + // A list of the SSH public keys that you want to associate with the instance. + // Optional + SSHKeys []string `json:"sshkeys"` + // A list of tags to be supplied to the instance + // Optional + Tags []string `json:"tags"` +} + +type StorageAttachmentInput struct { + // The index number for the volume. The allowed range is 1 to 10. + // If you want to use a storage volume as the boot disk for an instance, you must specify the index number for that volume as 1. + // The index determines the device name by which the volume is exposed to the instance. + Index int `json:"index"` + // The three-part name (/Compute-identity_domain/user/object) of the storage volume that you want to attach to the instance. + // Note that volumes attached to an instance at launch time can't be detached. + Volume string `json:"volume"` +} + +const ReservationPrefix = "ipreservation" +const ReservationIPPrefix = "network/v1/ipreservation" + +type NICModel string + +const ( + NICDefaultModel NICModel = "e1000" +) + +// Struct of Networking info from a populated instance, or to be used as input to create an instance +type NetworkingInfo struct { + // The DNS name for the Shared network (Required) + // DNS A Record for an IP Network (Optional) + DNS []string `json:"dns,omitempty"` + // IP Network only. + // If you want to associate a static private IP Address, + // specify that here within the range of the supplied IPNetwork attribute. + // Optional + IPAddress string `json:"ip,omitempty"` + // IP Network only. + // The name of the IP Network you want to add the instance to. + // Required + IPNetwork string `json:"ipnetwork,omitempty"` + // IP Network only. + // The hexadecimal MAC Address of the interface + // Optional + MACAddress string `json:"address,omitempty"` + // Shared Network only. + // The type of NIC used. Must be set to 'e1000' + // Required + Model NICModel `json:"model,omitempty"` + // IP Network and Shared Network + // The name servers that are sent through DHCP as option 6. + // You can specify a maximum of eight name server IP addresses per interface. + // Optional + NameServers []string `json:"name_servers,omitempty"` + // The names of an IP Reservation to associate in an IP Network (Optional) + // Indicates whether a temporary or permanent public IP Address should be assigned + // in a Shared Network (Required) + Nat []string `json:"nat,omitempty"` + // IP Network and Shared Network + // The search domains that should be sent through DHCP as option 119. + // You can enter a maximum of eight search domain zones per interface. + // Optional + SearchDomains []string `json:"search_domains,omitempty"` + // Shared Network only. + // The security lists that you want to add the instance to + // Required + SecLists []string `json:"seclists,omitempty"` + // IP Network Only + // The name of the vNIC + // Optional + Vnic string `json:"vnic,omitempty"` + // IP Network only. + // The names of the vNICSets you want to add the interface to. + // Optional + VnicSets []string `json:"vnicsets,omitempty"` +} + +// LaunchPlan defines a launch plan, used to launch instances with the supplied InstanceSpec(s) +type LaunchPlanInput struct { + // Describes an array of instances which should be launched + Instances []CreateInstanceInput `json:"instances"` +} + +type LaunchPlanResponse struct { + // An array of instances which have been launched + Instances []InstanceInfo `json:"instances"` +} + +// LaunchInstance creates and submits a LaunchPlan to launch a new instance. +func (c *InstancesClient) CreateInstance(input *CreateInstanceInput) (*InstanceInfo, error) { + qualifiedSSHKeys := []string{} + for _, key := range input.SSHKeys { + qualifiedSSHKeys = append(qualifiedSSHKeys, c.getQualifiedName(key)) + } + + input.SSHKeys = qualifiedSSHKeys + + qualifiedStorageAttachments := []StorageAttachmentInput{} + for _, attachment := range input.Storage { + qualifiedStorageAttachments = append(qualifiedStorageAttachments, StorageAttachmentInput{ + Index: attachment.Index, + Volume: c.getQualifiedName(attachment.Volume), + }) + } + input.Storage = qualifiedStorageAttachments + + input.Networking = c.qualifyNetworking(input.Networking) + + input.Name = fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), input.Name) + + plan := LaunchPlanInput{Instances: []CreateInstanceInput{*input}} + + var responseBody LaunchPlanResponse + if err := c.createResource(&plan, &responseBody); err != nil { + return nil, err + } + + if len(responseBody.Instances) == 0 { + return nil, fmt.Errorf("No instance information returned: %#v", responseBody) + } + + // Call wait for instance ready now, as creating the instance is an eventually consistent operation + getInput := &GetInstanceInput{ + Name: input.Name, + ID: responseBody.Instances[0].ID, + } + + // Wait for instance to be ready and return the result + // Don't have to unqualify any objects, as the GetInstance method will handle that + return c.WaitForInstanceRunning(getInput, WaitForInstanceReadyTimeout) +} + +// Both of these fields are required. If they're not provided, things go wrong in +// incredibly amazing ways. +type GetInstanceInput struct { + // The Unqualified Name of this Instance + Name string + // The Unqualified ID of this Instance + ID string +} + +func (g *GetInstanceInput) String() string { + return fmt.Sprintf(CMP_QUALIFIED_NAME, g.Name, g.ID) +} + +// GetInstance retrieves information about an instance. +func (c *InstancesClient) GetInstance(input *GetInstanceInput) (*InstanceInfo, error) { + if input.ID == "" || input.Name == "" { + return nil, errors.New("Both instance name and ID need to be specified") + } + + var responseBody InstanceInfo + if err := c.getResource(input.String(), &responseBody); err != nil { + return nil, err + } + + if responseBody.Name == "" { + return nil, fmt.Errorf("Empty response body when requesting instance %s", input.Name) + } + + // The returned 'Name' attribute is the fully qualified instance name + "/" + ID + // Split these out to accurately populate the fields + nID := strings.Split(c.getUnqualifiedName(responseBody.Name), "/") + responseBody.Name = nID[0] + responseBody.ID = nID[1] + + c.unqualify(&responseBody.VCableID) + + // Unqualify SSH Key names + sshKeyNames := []string{} + for _, sshKeyRef := range responseBody.SSHKeys { + sshKeyNames = append(sshKeyNames, c.getUnqualifiedName(sshKeyRef)) + } + responseBody.SSHKeys = sshKeyNames + + responseBody.Networking = c.unqualifyNetworking(responseBody.Networking) + + return &responseBody, nil +} + +type DeleteInstanceInput struct { + // The Unqualified Name of this Instance + Name string + // The Unqualified ID of this Instance + ID string +} + +func (d *DeleteInstanceInput) String() string { + return fmt.Sprintf(CMP_QUALIFIED_NAME, d.Name, d.ID) +} + +// DeleteInstance deletes an instance. +func (c *InstancesClient) DeleteInstance(input *DeleteInstanceInput) error { + // Call to delete the instance + if err := c.deleteResource(input.String()); err != nil { + return err + } + // Wait for instance to be deleted + return c.WaitForInstanceDeleted(input, WaitForInstanceDeleteTimeout) +} + +// WaitForInstanceRunning waits for an instance to be completely initialized and available. +func (c *InstancesClient) WaitForInstanceRunning(input *GetInstanceInput, timeoutSeconds int) (*InstanceInfo, error) { + var info *InstanceInfo + var getErr error + err := c.waitFor("instance to be ready", timeoutSeconds, func() (bool, error) { + info, getErr = c.GetInstance(input) + if getErr != nil { + return false, getErr + } + switch s := info.State; s { + case InstanceError: + return false, fmt.Errorf("Error initializing instance: %s", info.ErrorReason) + case InstanceRunning: + c.debugLogString("Instance Running") + return true, nil + case InstanceQueued: + c.debugLogString("Instance Queuing") + return false, nil + case InstanceInitializing: + c.debugLogString("Instance Initializing") + return false, nil + case InstancePreparing: + c.debugLogString("Instance Preparing") + return false, nil + default: + c.debugLogString(fmt.Sprintf("Unknown instance state: %s, waiting", s)) + return false, nil + } + }) + return info, err +} + +// WaitForInstanceDeleted waits for an instance to be fully deleted. +func (c *InstancesClient) WaitForInstanceDeleted(input *DeleteInstanceInput, timeoutSeconds int) error { + return c.waitFor("instance to be deleted", timeoutSeconds, func() (bool, error) { + var info InstanceInfo + if err := c.getResource(input.String(), &info); err != nil { + if WasNotFoundError(err) { + // Instance could not be found, thus deleted + return true, nil + } + // Some other error occurred trying to get instance, exit + return false, err + } + switch s := info.State; s { + case InstanceError: + return false, fmt.Errorf("Error stopping instance: %s", info.ErrorReason) + case InstanceStopping: + c.debugLogString("Instance stopping") + return false, nil + default: + c.debugLogString(fmt.Sprintf("Unknown instance state: %s, waiting", s)) + return false, nil + } + }) +} + +func (c *InstancesClient) qualifyNetworking(info map[string]NetworkingInfo) map[string]NetworkingInfo { + qualifiedNetworks := map[string]NetworkingInfo{} + for k, v := range info { + qfd := v + sharedNetwork := false + if v.IPNetwork != "" { + // Network interface is for an IP Network + qfd.IPNetwork = c.getQualifiedName(v.IPNetwork) + sharedNetwork = true + } + if v.Vnic != "" { + qfd.Vnic = c.getQualifiedName(v.Vnic) + } + if v.Nat != nil { + qfd.Nat = c.qualifyNat(v.Nat, sharedNetwork) + } + if v.VnicSets != nil { + qfd.VnicSets = c.getQualifiedList(v.VnicSets) + } + if v.SecLists != nil { + // Network interface is for the shared network + secLists := []string{} + for _, v := range v.SecLists { + secLists = append(secLists, c.getQualifiedName(v)) + } + qfd.SecLists = secLists + } + + qualifiedNetworks[k] = qfd + } + return qualifiedNetworks +} + +func (c *InstancesClient) unqualifyNetworking(info map[string]NetworkingInfo) map[string]NetworkingInfo { + // Unqualify ip network + unqualifiedNetworks := map[string]NetworkingInfo{} + for k, v := range info { + unq := v + if v.IPNetwork != "" { + unq.IPNetwork = c.getUnqualifiedName(v.IPNetwork) + } + if v.Vnic != "" { + unq.Vnic = c.getUnqualifiedName(v.Vnic) + } + if v.Nat != nil { + unq.Nat = c.unqualifyNat(v.Nat) + } + if v.VnicSets != nil { + unq.VnicSets = c.getUnqualifiedList(v.VnicSets) + } + if v.SecLists != nil { + secLists := []string{} + for _, v := range v.SecLists { + secLists = append(secLists, c.getUnqualifiedName(v)) + } + v.SecLists = secLists + } + unqualifiedNetworks[k] = unq + } + return unqualifiedNetworks +} + +func (c *InstancesClient) qualifyNat(nat []string, shared bool) []string { + qualifiedNats := []string{} + for _, v := range nat { + if strings.HasPrefix(v, "ippool:/oracle") { + qualifiedNats = append(qualifiedNats, v) + continue + } + prefix := ReservationPrefix + if shared { + prefix = ReservationIPPrefix + } + qualifiedNats = append(qualifiedNats, fmt.Sprintf("%s:%s", prefix, c.getQualifiedName(v))) + } + return qualifiedNats +} + +func (c *InstancesClient) unqualifyNat(nat []string) []string { + unQualifiedNats := []string{} + for _, v := range nat { + if strings.HasPrefix(v, "ippool:/oracle") { + unQualifiedNats = append(unQualifiedNats, v) + continue + } + n := strings.Split(v, ":") + u := n[1] + unQualifiedNats = append(unQualifiedNats, c.getUnqualifiedName(u)) + } + return unQualifiedNats +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go new file mode 100644 index 000000000..f685fa2d5 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go @@ -0,0 +1,135 @@ +package compute + +const ( + IPAddressPrefixSetDescription = "ip address prefix set" + IPAddressPrefixSetContainerPath = "/network/v1/ipaddressprefixset/" + IPAddressPrefixSetResourcePath = "/network/v1/ipaddressprefixset" +) + +type IPAddressPrefixSetsClient struct { + ResourceClient +} + +// IPAddressPrefixSets() returns an IPAddressPrefixSetsClient that can be used to access the +// necessary CRUD functions for IP Address Prefix Sets. +func (c *Client) IPAddressPrefixSets() *IPAddressPrefixSetsClient { + return &IPAddressPrefixSetsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: IPAddressPrefixSetDescription, + ContainerPath: IPAddressPrefixSetContainerPath, + ResourceRootPath: IPAddressPrefixSetResourcePath, + }, + } +} + +// IPAddressPrefixSetInfo contains the exported fields necessary to hold all the information about an +// IP Address Prefix Set +type IPAddressPrefixSetInfo struct { + // The name of the IP Address Prefix Set + Name string `json:"name"` + // Description of the IP Address Prefix Set + Description string `json:"description"` + // List of CIDR IPv4 prefixes assigned in the virtual network. + IPAddressPrefixes []string `json:"ipAddressPrefixes"` + // Slice of tags associated with the IP Address Prefix Set + Tags []string `json:"tags"` + // Uniform Resource Identifier for the IP Address Prefix Set + Uri string `json:"uri"` +} + +type CreateIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the IPAddressPrefixSet + // Optional + Description string `json:"description"` + + // List of CIDR IPv4 prefixes assigned in the virtual network. + // Optional + IPAddressPrefixes []string `json:"ipAddressPrefixes"` + + // String slice of tags to apply to the IP Address Prefix Set object + // Optional + Tags []string `json:"tags"` +} + +// Create a new IP Address Prefix Set from an IPAddressPrefixSetsClient and an input struct. +// Returns a populated Info struct for the IP Address Prefix Set, and any errors +func (c *IPAddressPrefixSetsClient) CreateIPAddressPrefixSet(input *CreateIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPAddressPrefixSetInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated IPAddressPrefixSetInfo struct from an input struct +func (c *IPAddressPrefixSetsClient) GetIPAddressPrefixSet(input *GetIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPAddressPrefixSetInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +// UpdateIPAddressPrefixSetInput defines what to update in a ip address prefix set +type UpdateIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the IPAddressPrefixSet + // Optional + Description string `json:"description"` + + // List of CIDR IPv4 prefixes assigned in the virtual network. + IPAddressPrefixes []string `json:"ipAddressPrefixes"` + + // String slice of tags to apply to the IP Address Prefix Set object + // Optional + Tags []string `json:"tags"` +} + +// UpdateIPAddressPrefixSet update the ip address prefix set +func (c *IPAddressPrefixSetsClient) UpdateIPAddressPrefixSet(updateInput *UpdateIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var ipInfo IPAddressPrefixSetInfo + if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *IPAddressPrefixSetsClient) DeleteIPAddressPrefixSet(input *DeleteIPAddressPrefixSetInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPAddressPrefixSetInfo struct +func (c *IPAddressPrefixSetsClient) success(info *IPAddressPrefixSetInfo) (*IPAddressPrefixSetInfo, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go new file mode 100644 index 000000000..67e92d4ed --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go @@ -0,0 +1,190 @@ +package compute + +import ( + "fmt" + "path/filepath" +) + +// IPAddressReservationsClient is a client to manage ip address reservation resources +type IPAddressReservationsClient struct { + *ResourceClient +} + +const ( + IPAddressReservationDescription = "IP Address Reservation" + IPAddressReservationContainerPath = "/network/v1/ipreservation/" + IPAddressReservationResourcePath = "/network/v1/ipreservation" + IPAddressReservationQualifier = "/oracle/public" +) + +// IPAddressReservations returns an IPAddressReservationsClient to manage IP address reservation +// resources +func (c *Client) IPAddressReservations() *IPAddressReservationsClient { + return &IPAddressReservationsClient{ + ResourceClient: &ResourceClient{ + Client: c, + ResourceDescription: IPAddressReservationDescription, + ContainerPath: IPAddressReservationContainerPath, + ResourceRootPath: IPAddressReservationResourcePath, + }, + } +} + +// IPAddressReservation describes an IP Address reservation +type IPAddressReservation struct { + // Description of the IP Address Reservation + Description string `json:"description"` + + // Reserved NAT IPv4 address from the IP Address Pool + IPAddress string `json:"ipAddress"` + + // Name of the IP Address pool to reserve the NAT IP from + IPAddressPool string `json:"ipAddressPool"` + + // Name of the reservation + Name string `json:"name"` + + // Tags associated with the object + Tags []string `json:"tags"` + + // Uniform Resource Identified for the reservation + Uri string `json:"uri"` +} + +const ( + PublicIPAddressPool = "public-ippool" + PrivateIPAddressPool = "cloud-ippool" +) + +// CreateIPAddressReservationInput defines input parameters to create an ip address reservation +type CreateIPAddressReservationInput struct { + // Description of the IP Address Reservation + // Optional + Description string `json:"description"` + + // IP Address pool from which to reserve an IP Address. + // Can be one of the following: + // + // 'public-ippool' - When you attach an IP Address from this pool to an instance, you enable + // access between the public Internet and the instance + // 'cloud-ippool' - When you attach an IP Address from this pool to an instance, the instance + // can communicate privately with other Oracle Cloud Services + // Optional + IPAddressPool string `json:"ipAddressPool"` + + // The name of the reservation to create + // Required + Name string `json:"name"` + + // Tags to associate with the IP Reservation + // Optional + Tags []string `json:"tags"` +} + +// Takes an input struct, creates an IP Address reservation, and returns the info struct and any errors +func (c *IPAddressReservationsClient) CreateIPAddressReservation(input *CreateIPAddressReservationInput) (*IPAddressReservation, error) { + var ipAddrRes IPAddressReservation + // Qualify supplied name + input.Name = c.getQualifiedName(input.Name) + // Qualify supplied address pool if not nil + if input.IPAddressPool != "" { + input.IPAddressPool = c.qualifyIPAddressPool(input.IPAddressPool) + } + + if err := c.createResource(input, &ipAddrRes); err != nil { + return nil, err + } + + return c.success(&ipAddrRes) +} + +// Parameters to retrieve information on an ip address reservation +type GetIPAddressReservationInput struct { + // Name of the IP Reservation + // Required + Name string `json:"name"` +} + +// Returns an IP Address Reservation and any errors +func (c *IPAddressReservationsClient) GetIPAddressReservation(input *GetIPAddressReservationInput) (*IPAddressReservation, error) { + var ipAddrRes IPAddressReservation + + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &ipAddrRes); err != nil { + return nil, err + } + + return c.success(&ipAddrRes) +} + +// Parameters to update an IP Address reservation +type UpdateIPAddressReservationInput struct { + // Description of the IP Address Reservation + // Optional + Description string `json:"description"` + + // IP Address pool from which to reserve an IP Address. + // Can be one of the following: + // + // 'public-ippool' - When you attach an IP Address from this pool to an instance, you enable + // access between the public Internet and the instance + // 'cloud-ippool' - When you attach an IP Address from this pool to an instance, the instance + // can communicate privately with other Oracle Cloud Services + // Optional + IPAddressPool string `json:"ipAddressPool"` + + // The name of the reservation to create + // Required + Name string `json:"name"` + + // Tags to associate with the IP Reservation + // Optional + Tags []string `json:"tags"` +} + +func (c *IPAddressReservationsClient) UpdateIPAddressReservation(input *UpdateIPAddressReservationInput) (*IPAddressReservation, error) { + var ipAddrRes IPAddressReservation + + // Qualify supplied name + input.Name = c.getQualifiedName(input.Name) + // Qualify supplied address pool if not nil + if input.IPAddressPool != "" { + input.IPAddressPool = c.qualifyIPAddressPool(input.IPAddressPool) + } + + if err := c.updateResource(input.Name, input, &ipAddrRes); err != nil { + return nil, err + } + + return c.success(&ipAddrRes) +} + +// Parameters to delete an IP Address Reservation +type DeleteIPAddressReservationInput struct { + // The name of the reservation to delete + Name string `json:"name"` +} + +func (c *IPAddressReservationsClient) DeleteIPAddressReservation(input *DeleteIPAddressReservationInput) error { + input.Name = c.getQualifiedName(input.Name) + return c.deleteResource(input.Name) +} + +func (c *IPAddressReservationsClient) success(result *IPAddressReservation) (*IPAddressReservation, error) { + c.unqualify(&result.Name) + if result.IPAddressPool != "" { + result.IPAddressPool = c.unqualifyIPAddressPool(result.IPAddressPool) + } + + return result, nil +} + +func (c *IPAddressReservationsClient) qualifyIPAddressPool(input string) string { + // Add '/oracle/public/' + return fmt.Sprintf("%s/%s", IPAddressReservationQualifier, input) +} + +func (c *IPAddressReservationsClient) unqualifyIPAddressPool(input string) string { + // Remove '/oracle/public/' + return filepath.Base(input) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go new file mode 100644 index 000000000..0bfdcbc9d --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go @@ -0,0 +1,118 @@ +package compute + +import ( + "fmt" + "strings" +) + +// IPAssociationsClient is a client for the IP Association functions of the Compute API. +type IPAssociationsClient struct { + *ResourceClient +} + +// IPAssociations obtains a IPAssociationsClient which can be used to access to the +// IP Association functions of the Compute API +func (c *Client) IPAssociations() *IPAssociationsClient { + return &IPAssociationsClient{ + ResourceClient: &ResourceClient{ + Client: c, + ResourceDescription: "ip association", + ContainerPath: "/ip/association/", + ResourceRootPath: "/ip/association", + }} +} + +// IPAssociationInfo describes an existing IP association. +type IPAssociationInfo struct { + // TODO: it'd probably make sense to expose the `ip` field here too? + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The three-part name of the IP reservation object in the format (/Compute-identity_domain/user/object). + // An IP reservation is a public IP address which is attached to an Oracle Compute Cloud Service instance that requires access to or from the Internet. + Reservation string `json:"reservation"` + + // The type of IP Address to associate with this instance + // for a Dynamic IP address specify `ippool:/oracle/public/ippool`. + // for a Static IP address specify the three part name of the existing IP reservation + ParentPool string `json:"parentpool"` + + // Uniform Resource Identifier for the IP Association + URI string `json:"uri"` + + // The three-part name of a vcable ID of an instance that is associated with the IP reservation. + VCable string `json:"vcable"` +} + +type CreateIPAssociationInput struct { + // The type of IP Address to associate with this instance + // for a Dynamic IP address specify `ippool:/oracle/public/ippool`. + // for a Static IP address specify the three part name of the existing IP reservation + // Required + ParentPool string `json:"parentpool"` + + // The three-part name of the vcable ID of the instance that you want to associate with an IP address. The three-part name is in the format: /Compute-identity_domain/user/object. + // Required + VCable string `json:"vcable"` +} + +// CreateIPAssociation creates a new IP association with the supplied vcable and parentpool. +func (c *IPAssociationsClient) CreateIPAssociation(input *CreateIPAssociationInput) (*IPAssociationInfo, error) { + input.VCable = c.getQualifiedName(input.VCable) + input.ParentPool = c.getQualifiedParentPoolName(input.ParentPool) + var assocInfo IPAssociationInfo + if err := c.createResource(input, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +type GetIPAssociationInput struct { + // The three-part name of the IP Association + // Required. + Name string `json:"name"` +} + +// GetIPAssociation retrieves the IP association with the given name. +func (c *IPAssociationsClient) GetIPAssociation(input *GetIPAssociationInput) (*IPAssociationInfo, error) { + var assocInfo IPAssociationInfo + if err := c.getResource(input.Name, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +type DeleteIPAssociationInput struct { + // The three-part name of the IP Association + // Required. + Name string `json:"name"` +} + +// DeleteIPAssociation deletes the IP association with the given name. +func (c *IPAssociationsClient) DeleteIPAssociation(input *DeleteIPAssociationInput) error { + return c.deleteResource(input.Name) +} + +func (c *IPAssociationsClient) getQualifiedParentPoolName(parentpool string) string { + parts := strings.Split(parentpool, ":") + pooltype := parts[0] + name := parts[1] + return fmt.Sprintf("%s:%s", pooltype, c.getQualifiedName(name)) +} + +func (c *IPAssociationsClient) unqualifyParentPoolName(parentpool *string) { + parts := strings.Split(*parentpool, ":") + pooltype := parts[0] + name := parts[1] + *parentpool = fmt.Sprintf("%s:%s", pooltype, c.getUnqualifiedName(name)) +} + +// Unqualifies identifiers +func (c *IPAssociationsClient) success(assocInfo *IPAssociationInfo) (*IPAssociationInfo, error) { + c.unqualify(&assocInfo.Name, &assocInfo.VCable) + c.unqualifyParentPoolName(&assocInfo.ParentPool) + return assocInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go new file mode 100644 index 000000000..9a20380f8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go @@ -0,0 +1,99 @@ +package compute + +const ( + IPNetworkExchangeDescription = "ip network exchange" + IPNetworkExchangeContainerPath = "/network/v1/ipnetworkexchange/" + IPNetworkExchangeResourcePath = "/network/v1/ipnetworkexchange" +) + +type IPNetworkExchangesClient struct { + ResourceClient +} + +// IPNetworkExchanges() returns an IPNetworkExchangesClient that can be used to access the +// necessary CRUD functions for IP Network Exchanges. +func (c *Client) IPNetworkExchanges() *IPNetworkExchangesClient { + return &IPNetworkExchangesClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: IPNetworkExchangeDescription, + ContainerPath: IPNetworkExchangeContainerPath, + ResourceRootPath: IPNetworkExchangeResourcePath, + }, + } +} + +// IPNetworkExchangeInfo contains the exported fields necessary to hold all the information about an +// IP Network Exchange +type IPNetworkExchangeInfo struct { + // The name of the IP Network Exchange + Name string `json:"name"` + // Description of the IP Network Exchange + Description string `json:"description"` + // Slice of tags associated with the IP Network Exchange + Tags []string `json:"tags"` + // Uniform Resource Identifier for the IP Network Exchange + Uri string `json:"uri"` +} + +type CreateIPNetworkExchangeInput struct { + // The name of the IP Network Exchange to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the IPNetworkExchange + // Optional + Description string `json:"description"` + + // String slice of tags to apply to the IP Network Exchange object + // Optional + Tags []string `json:"tags"` +} + +// Create a new IP Network Exchange from an IPNetworkExchangesClient and an input struct. +// Returns a populated Info struct for the IP Network Exchange, and any errors +func (c *IPNetworkExchangesClient) CreateIPNetworkExchange(input *CreateIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPNetworkExchangeInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetIPNetworkExchangeInput struct { + // The name of the IP Network Exchange to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated IPNetworkExchangeInfo struct from an input struct +func (c *IPNetworkExchangesClient) GetIPNetworkExchange(input *GetIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPNetworkExchangeInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteIPNetworkExchangeInput struct { + // The name of the IP Network Exchange to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *IPNetworkExchangesClient) DeleteIPNetworkExchange(input *DeleteIPNetworkExchangeInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPNetworkExchangeInfo struct +func (c *IPNetworkExchangesClient) success(info *IPNetworkExchangeInfo) (*IPNetworkExchangeInfo, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go new file mode 100644 index 000000000..3705eef29 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go @@ -0,0 +1,186 @@ +package compute + +const ( + IPNetworkDescription = "ip network" + IPNetworkContainerPath = "/network/v1/ipnetwork/" + IPNetworkResourcePath = "/network/v1/ipnetwork" +) + +type IPNetworksClient struct { + ResourceClient +} + +// IPNetworks() returns an IPNetworksClient that can be used to access the +// necessary CRUD functions for IP Networks. +func (c *Client) IPNetworks() *IPNetworksClient { + return &IPNetworksClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: IPNetworkDescription, + ContainerPath: IPNetworkContainerPath, + ResourceRootPath: IPNetworkResourcePath, + }, + } +} + +// IPNetworkInfo contains the exported fields necessary to hold all the information about an +// IP Network +type IPNetworkInfo struct { + // The name of the IP Network + Name string `json:"name"` + // The CIDR IPv4 prefix associated with the IP Network + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the IP Network Exchange associated with the IP Network + IPNetworkExchange string `json:"ipNetworkExchange,omitempty"` + // Description of the IP Network + Description string `json:"description"` + // Whether public internet access was enabled using NAPT for VNICs without any public IP reservation + PublicNaptEnabled bool `json:"publicNaptEnabledFlag"` + // Slice of tags associated with the IP Network + Tags []string `json:"tags"` + // Uniform Resource Identifier for the IP Network + Uri string `json:"uri"` +} + +type CreateIPNetworkInput struct { + // The name of the IP Network to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Specify the size of the IP Subnet. It is a range of IPv4 addresses assigned in the virtual + // network, in CIDR address prefix format. + // While specifying the IP address prefix take care of the following points: + // + //* These IP addresses aren't part of the common pool of Oracle-provided IP addresses used by the shared network. + // + //* There's no conflict with the range of IP addresses used in another IP network, the IP addresses used your on-premises network, or with the range of private IP addresses used in the shared network. If IP networks with overlapping IP subnets are linked to an IP exchange, packets going to and from those IP networks are dropped. + // + //* The upper limit of the CIDR block size for an IP network is /16. + // + //Note: The first IP address of any IP network is reserved for the default gateway, the DHCP server, and the DNS server of that IP network. + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + + //Specify the IP network exchange to which the IP network belongs. + //You can add an IP network to only one IP network exchange, but an IP network exchange + //can include multiple IP networks. An IP network exchange enables access between IP networks + //that have non-overlapping addresses, so that instances on these networks can exchange packets + //with each other without NAT. + // Optional + IPNetworkExchange string `json:"ipNetworkExchange,omitempty"` + + // Description of the IPNetwork + // Optional + Description string `json:"description"` + + // Enable public internet access using NAPT for VNICs without any public IP reservation + // Optional + PublicNaptEnabled bool `json:"publicNaptEnabledFlag"` + + // String slice of tags to apply to the IP Network object + // Optional + Tags []string `json:"tags"` +} + +// Create a new IP Network from an IPNetworksClient and an input struct. +// Returns a populated Info struct for the IP Network, and any errors +func (c *IPNetworksClient) CreateIPNetwork(input *CreateIPNetworkInput) (*IPNetworkInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.IPNetworkExchange = c.getQualifiedName(input.IPNetworkExchange) + + var ipInfo IPNetworkInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetIPNetworkInput struct { + // The name of the IP Network to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated IPNetworkInfo struct from an input struct +func (c *IPNetworksClient) GetIPNetwork(input *GetIPNetworkInput) (*IPNetworkInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPNetworkInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type UpdateIPNetworkInput struct { + // The name of the IP Network to update. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Specify the size of the IP Subnet. It is a range of IPv4 addresses assigned in the virtual + // network, in CIDR address prefix format. + // While specifying the IP address prefix take care of the following points: + // + //* These IP addresses aren't part of the common pool of Oracle-provided IP addresses used by the shared network. + // + //* There's no conflict with the range of IP addresses used in another IP network, the IP addresses used your on-premises network, or with the range of private IP addresses used in the shared network. If IP networks with overlapping IP subnets are linked to an IP exchange, packets going to and from those IP networks are dropped. + // + //* The upper limit of the CIDR block size for an IP network is /16. + // + //Note: The first IP address of any IP network is reserved for the default gateway, the DHCP server, and the DNS server of that IP network. + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + + //Specify the IP network exchange to which the IP network belongs. + //You can add an IP network to only one IP network exchange, but an IP network exchange + //can include multiple IP networks. An IP network exchange enables access between IP networks + //that have non-overlapping addresses, so that instances on these networks can exchange packets + //with each other without NAT. + // Optional + IPNetworkExchange string `json:"ipNetworkExchange,omitempty"` + + // Description of the IPNetwork + // Optional + Description string `json:"description"` + + // Enable public internet access using NAPT for VNICs without any public IP reservation + // Optional + PublicNaptEnabled bool `json:"publicNaptEnabledFlag"` + + // String slice of tags to apply to the IP Network object + // Optional + Tags []string `json:"tags"` +} + +func (c *IPNetworksClient) UpdateIPNetwork(input *UpdateIPNetworkInput) (*IPNetworkInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.IPNetworkExchange = c.getQualifiedName(input.IPNetworkExchange) + + var ipInfo IPNetworkInfo + if err := c.updateResource(input.Name, &input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteIPNetworkInput struct { + // The name of the IP Network to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *IPNetworksClient) DeleteIPNetwork(input *DeleteIPNetworkInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPNetworkInfo struct +func (c *IPNetworksClient) success(info *IPNetworkInfo) (*IPNetworkInfo, error) { + c.unqualify(&info.Name) + c.unqualify(&info.IPNetworkExchange) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go new file mode 100644 index 000000000..30cb47cd6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go @@ -0,0 +1,147 @@ +package compute + +// IPReservationsClient is a client for the IP Reservations functions of the Compute API. +type IPReservationsClient struct { + *ResourceClient +} + +const ( + IPReservationDesc = "ip reservation" + IPReservationContainerPath = "/ip/reservation/" + IPReservataionResourcePath = "/ip/reservation" +) + +// IPReservations obtains an IPReservationsClient which can be used to access to the +// IP Reservations functions of the Compute API +func (c *Client) IPReservations() *IPReservationsClient { + return &IPReservationsClient{ + ResourceClient: &ResourceClient{ + Client: c, + ResourceDescription: IPReservationDesc, + ContainerPath: IPReservationContainerPath, + ResourceRootPath: IPReservataionResourcePath, + }} +} + +type IPReservationPool string + +const ( + PublicReservationPool IPReservationPool = "/oracle/public/ippool" +) + +// IPReservationInput describes an existing IP reservation. +type IPReservation struct { + // Shows the default account for your identity domain. + Account string `json:"account"` + // Public IP address. + IP string `json:"ip"` + // The three-part name of the IP Reservation (/Compute-identity_domain/user/object). + Name string `json:"name"` + // Pool of public IP addresses + ParentPool IPReservationPool `json:"parentpool"` + // Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)? + Permanent bool `json:"permanent"` + // A comma-separated list of strings which helps you to identify IP reservation. + Tags []string `json:"tags"` + // Uniform Resource Identifier + Uri string `json:"uri"` + // Is the IP reservation associated with an instance? + Used bool `json:"used"` +} + +// CreateIPReservationInput defines an IP reservation to be created. +type CreateIPReservationInput struct { + // The name of the object + // If you don't specify a name for this object, then the name is generated automatically. + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. + // Optional + Name string `json:"name"` + // Pool of public IP addresses. This must be set to `ippool` + // Required + ParentPool IPReservationPool `json:"parentpool"` + // Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)? + // Required + Permanent bool `json:"permanent"` + // A comma-separated list of strings which helps you to identify IP reservations. + // Optional + Tags []string `json:"tags"` +} + +// CreateIPReservation creates a new IP reservation with the given parentpool, tags and permanent flag. +func (c *IPReservationsClient) CreateIPReservation(input *CreateIPReservationInput) (*IPReservation, error) { + var ipInput IPReservation + + input.Name = c.getQualifiedName(input.Name) + if err := c.createResource(input, &ipInput); err != nil { + return nil, err + } + + return c.success(&ipInput) +} + +// GetIPReservationInput defines an IP Reservation to get +type GetIPReservationInput struct { + // The name of the IP Reservation + // Required + Name string +} + +// GetIPReservation retrieves the IP reservation with the given name. +func (c *IPReservationsClient) GetIPReservation(input *GetIPReservationInput) (*IPReservation, error) { + var ipInput IPReservation + + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &ipInput); err != nil { + return nil, err + } + + return c.success(&ipInput) +} + +// UpdateIPReservationInput defines an IP Reservation to be updated +type UpdateIPReservationInput struct { + // The name of the object + // If you don't specify a name for this object, then the name is generated automatically. + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. + // Required + Name string `json:"name"` + // Pool of public IP addresses. + // Required + ParentPool IPReservationPool `json:"parentpool"` + // Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)? + // Required + Permanent bool `json:"permanent"` + // A comma-separated list of strings which helps you to identify IP reservations. + // Optional + Tags []string `json:"tags"` +} + +// UpdateIPReservation updates the IP reservation. +func (c *IPReservationsClient) UpdateIPReservation(input *UpdateIPReservationInput) (*IPReservation, error) { + var updateOutput IPReservation + input.Name = c.getQualifiedName(input.Name) + if err := c.updateResource(input.Name, input, &updateOutput); err != nil { + return nil, err + } + return c.success(&updateOutput) +} + +// DeleteIPReservationInput defines an IP Reservation to delete +type DeleteIPReservationInput struct { + // The name of the IP Reservation + // Required + Name string +} + +// DeleteIPReservation deletes the IP reservation with the given name. +func (c *IPReservationsClient) DeleteIPReservation(input *DeleteIPReservationInput) error { + input.Name = c.getQualifiedName(input.Name) + return c.deleteResource(input.Name) +} + +func (c *IPReservationsClient) success(result *IPReservation) (*IPReservation, error) { + c.unqualify(&result.Name) + return result, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/logging.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/logging.go new file mode 100644 index 000000000..8fde2e5f5 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/logging.go @@ -0,0 +1,28 @@ +package compute + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/hashicorp/go-oracle-terraform/opc" +) + +// Log a string if debug logs are on +func (c *Client) debugLogString(str string) { + if c.loglevel != opc.LogDebug { + return + } + c.logger.Log(fmt.Sprintf("[DEBUG]: %s", str)) +} + +func (c *Client) debugLogReq(req *http.Request) { + // Don't need to log this if not debugging + if c.loglevel != opc.LogDebug { + return + } + buf := new(bytes.Buffer) + buf.ReadFrom(req.Body) + c.logger.Log(fmt.Sprintf("DEBUG: HTTP %s Req %s: %s", + req.Method, req.URL.String(), buf.String())) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/resource_client.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/resource_client.go new file mode 100644 index 000000000..7b133c634 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/resource_client.go @@ -0,0 +1,94 @@ +package compute + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/mitchellh/mapstructure" +) + +// ResourceClient is an AuthenticatedClient with some additional information about the resources to be addressed. +type ResourceClient struct { + *Client + ResourceDescription string + ContainerPath string + ResourceRootPath string +} + +func (c *ResourceClient) createResource(requestBody interface{}, responseBody interface{}) error { + resp, err := c.executeRequest("POST", c.ContainerPath, requestBody) + if err != nil { + return err + } + + return c.unmarshalResponseBody(resp, responseBody) +} + +func (c *ResourceClient) updateResource(name string, requestBody interface{}, responseBody interface{}) error { + resp, err := c.executeRequest("PUT", c.getObjectPath(c.ResourceRootPath, name), requestBody) + if err != nil { + return err + } + + return c.unmarshalResponseBody(resp, responseBody) +} + +func (c *ResourceClient) getResource(name string, responseBody interface{}) error { + var objectPath string + if name != "" { + objectPath = c.getObjectPath(c.ResourceRootPath, name) + } else { + objectPath = c.ResourceRootPath + } + resp, err := c.executeRequest("GET", objectPath, nil) + if err != nil { + return err + } + + return c.unmarshalResponseBody(resp, responseBody) +} + +func (c *ResourceClient) deleteResource(name string) error { + var objectPath string + if name != "" { + objectPath = c.getObjectPath(c.ResourceRootPath, name) + } else { + objectPath = c.ResourceRootPath + } + _, err := c.executeRequest("DELETE", objectPath, nil) + if err != nil { + return err + } + + // No errors and no response body to write + return nil +} + +func (c *ResourceClient) unmarshalResponseBody(resp *http.Response, iface interface{}) error { + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + c.debugLogString(fmt.Sprintf("HTTP Resp (%d): %s", resp.StatusCode, buf.String())) + // JSON decode response into interface + var tmp interface{} + dcd := json.NewDecoder(buf) + if err := dcd.Decode(&tmp); err != nil { + return err + } + + // Use mapstructure to weakly decode into the resulting interface + msdcd, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + WeaklyTypedInput: true, + Result: iface, + TagName: "json", + }) + if err != nil { + return err + } + + if err := msdcd.Decode(tmp); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go new file mode 100644 index 000000000..c15303fd7 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go @@ -0,0 +1,153 @@ +package compute + +const ( + RoutesDescription = "IP Network Route" + RoutesContainerPath = "/network/v1/route/" + RoutesResourcePath = "/network/v1/route" +) + +type RoutesClient struct { + ResourceClient +} + +func (c *Client) Routes() *RoutesClient { + return &RoutesClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: RoutesDescription, + ContainerPath: RoutesContainerPath, + ResourceRootPath: RoutesResourcePath, + }, + } +} + +type RouteInfo struct { + // Admin distance associated with this route + AdminDistance int `json:"adminDistance"` + // Description of the route + Description string `json:"description"` + // CIDR IPv4 Prefix associated with this route + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the route + Name string `json:"name"` + // Name of the VNIC set associated with the route + NextHopVnicSet string `json:"nextHopVnicSet"` + // Slice of Tags associated with the route + Tags []string `json:"tags,omitempty"` + // Uniform resource identifier associated with the route + Uri string `json:"uri"` +} + +type CreateRouteInput struct { + // Specify 0,1, or 2 as the route's administrative distance. + // If you do not specify a value, the default value is 0. + // The same prefix can be used in multiple routes. In this case, packets are routed over all the matching + // routes with the lowest administrative distance. + // In the case multiple routes with the same lowest administrative distance match, + // routing occurs over all these routes using ECMP. + // Optional + AdminDistance int `json:"adminDistance"` + // Description of the route + // Optional + Description string `json:"description"` + // The IPv4 address prefix in CIDR format, of the external network (external to the vNIC set) + // from which you want to route traffic + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the route. + // Names can only contain alphanumeric, underscore, dash, and period characters. Case-sensitive + // Required + Name string `json:"name"` + // Name of the virtual NIC set to route matching packets to. + // Routed flows are load-balanced among all the virtual NICs in the virtual NIC set + // Required + NextHopVnicSet string `json:"nextHopVnicSet"` + // Slice of tags to be associated with the route + // Optional + Tags []string `json:"tags,omitempty"` +} + +func (c *RoutesClient) CreateRoute(input *CreateRouteInput) (*RouteInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet) + + var routeInfo RouteInfo + if err := c.createResource(&input, &routeInfo); err != nil { + return nil, err + } + + return c.success(&routeInfo) +} + +type GetRouteInput struct { + // Name of the Route to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *RoutesClient) GetRoute(input *GetRouteInput) (*RouteInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var routeInfo RouteInfo + if err := c.getResource(input.Name, &routeInfo); err != nil { + return nil, err + } + return c.success(&routeInfo) +} + +type UpdateRouteInput struct { + // Specify 0,1, or 2 as the route's administrative distance. + // If you do not specify a value, the default value is 0. + // The same prefix can be used in multiple routes. In this case, packets are routed over all the matching + // routes with the lowest administrative distance. + // In the case multiple routes with the same lowest administrative distance match, + // routing occurs over all these routes using ECMP. + // Optional + AdminDistance int `json:"adminDistance"` + // Description of the route + // Optional + Description string `json:"description"` + // The IPv4 address prefix in CIDR format, of the external network (external to the vNIC set) + // from which you want to route traffic + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the route. + // Names can only contain alphanumeric, underscore, dash, and period characters. Case-sensitive + // Required + Name string `json:"name"` + // Name of the virtual NIC set to route matching packets to. + // Routed flows are load-balanced among all the virtual NICs in the virtual NIC set + // Required + NextHopVnicSet string `json:"nextHopVnicSet"` + // Slice of tags to be associated with the route + // Optional + Tags []string `json:"tags"` +} + +func (c *RoutesClient) UpdateRoute(input *UpdateRouteInput) (*RouteInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet) + + var routeInfo RouteInfo + if err := c.updateResource(input.Name, &input, &routeInfo); err != nil { + return nil, err + } + + return c.success(&routeInfo) +} + +type DeleteRouteInput struct { + // Name of the Route to delete. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *RoutesClient) DeleteRoute(input *DeleteRouteInput) error { + return c.deleteResource(input.Name) +} + +func (c *RoutesClient) success(info *RouteInfo) (*RouteInfo, error) { + c.unqualify(&info.Name) + c.unqualify(&info.NextHopVnicSet) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go new file mode 100644 index 000000000..c16e9d78b --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go @@ -0,0 +1,193 @@ +package compute + +// SecRulesClient is a client for the Sec Rules functions of the Compute API. +type SecRulesClient struct { + ResourceClient +} + +// SecRules obtains a SecRulesClient which can be used to access to the +// Sec Rules functions of the Compute API +func (c *Client) SecRules() *SecRulesClient { + return &SecRulesClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "security ip list", + ContainerPath: "/secrule/", + ResourceRootPath: "/secrule", + }} +} + +// SecRuleInfo describes an existing sec rule. +type SecRuleInfo struct { + // Set this parameter to PERMIT. + Action string `json:"action"` + // The name of the security application + Application string `json:"application"` + // A description of the sec rule + Description string `json:"description"` + // Indicates whether the security rule is enabled + Disabled bool `json:"disabled"` + // The name of the destination security list or security IP list. + DestinationList string `json:"dst_list"` + // The name of the sec rule + Name string `json:"name"` + // The name of the source security list or security IP list. + SourceList string `json:"src_list"` + // Uniform Resource Identifier for the sec rule + URI string `json:"uri"` +} + +// CreateSecRuleInput defines a sec rule to be created. +type CreateSecRuleInput struct { + // Set this parameter to PERMIT. + // Required + Action string `json:"action"` + + // The name of the security application for user-defined or predefined security applications. + // Required + Application string `json:"application"` + + // Description of the IP Network + // Optional + Description string `json:"description"` + + // Indicates whether the sec rule is enabled (set to false) or disabled (true). + // The default setting is false. + // Optional + Disabled bool `json:"disabled"` + + // The name of the destination security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // You can specify a security IP list as the destination in a secrule, + // provided src_list is a security list that has DENY as its outbound policy. + // + // You cannot specify any of the security IP lists in the /oracle/public container + // as a destination in a secrule. + // Required + DestinationList string `json:"dst_list"` + + // The name of the Sec Rule to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // The name of the source security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // Required + SourceList string `json:"src_list"` +} + +// CreateSecRule creates a new sec rule. +func (c *SecRulesClient) CreateSecRule(createInput *CreateSecRuleInput) (*SecRuleInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + createInput.SourceList = c.getQualifiedListName(createInput.SourceList) + createInput.DestinationList = c.getQualifiedListName(createInput.DestinationList) + createInput.Application = c.getQualifiedName(createInput.Application) + + var ruleInfo SecRuleInfo + if err := c.createResource(createInput, &ruleInfo); err != nil { + return nil, err + } + + return c.success(&ruleInfo) +} + +// GetSecRuleInput describes the Sec Rule to get +type GetSecRuleInput struct { + // The name of the Sec Rule to query for + // Required + Name string `json:"name"` +} + +// GetSecRule retrieves the sec rule with the given name. +func (c *SecRulesClient) GetSecRule(getInput *GetSecRuleInput) (*SecRuleInfo, error) { + var ruleInfo SecRuleInfo + if err := c.getResource(getInput.Name, &ruleInfo); err != nil { + return nil, err + } + + return c.success(&ruleInfo) +} + +// UpdateSecRuleInput describes a secruity rule to update +type UpdateSecRuleInput struct { + // Set this parameter to PERMIT. + // Required + Action string `json:"action"` + + // The name of the security application for user-defined or predefined security applications. + // Required + Application string `json:"application"` + + // Description of the IP Network + // Optional + Description string `json:"description"` + + // Indicates whether the sec rule is enabled (set to false) or disabled (true). + // The default setting is false. + // Optional + Disabled bool `json:"disabled"` + + // The name of the destination security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // You can specify a security IP list as the destination in a secrule, + // provided src_list is a security list that has DENY as its outbound policy. + // + // You cannot specify any of the security IP lists in the /oracle/public container + // as a destination in a secrule. + // Required + DestinationList string `json:"dst_list"` + + // The name of the Sec Rule to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // The name of the source security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // Required + SourceList string `json:"src_list"` +} + +// UpdateSecRule modifies the properties of the sec rule with the given name. +func (c *SecRulesClient) UpdateSecRule(updateInput *UpdateSecRuleInput) (*SecRuleInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + updateInput.SourceList = c.getQualifiedListName(updateInput.SourceList) + updateInput.DestinationList = c.getQualifiedListName(updateInput.DestinationList) + updateInput.Application = c.getQualifiedName(updateInput.Application) + + var ruleInfo SecRuleInfo + if err := c.updateResource(updateInput.Name, updateInput, &ruleInfo); err != nil { + return nil, err + } + + return c.success(&ruleInfo) +} + +// DeleteSecRuleInput describes the sec rule to delete +type DeleteSecRuleInput struct { + // The name of the Sec Rule to delete. + // Required + Name string `json:"name"` +} + +// DeleteSecRule deletes the sec rule with the given name. +func (c *SecRulesClient) DeleteSecRule(deleteInput *DeleteSecRuleInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecRulesClient) success(ruleInfo *SecRuleInfo) (*SecRuleInfo, error) { + ruleInfo.Name = c.getUnqualifiedName(ruleInfo.Name) + ruleInfo.SourceList = c.unqualifyListName(ruleInfo.SourceList) + ruleInfo.DestinationList = c.unqualifyListName(ruleInfo.DestinationList) + ruleInfo.Application = c.getUnqualifiedName(ruleInfo.Application) + return ruleInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go new file mode 100644 index 000000000..007e234da --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go @@ -0,0 +1,150 @@ +package compute + +// SecurityApplicationsClient is a client for the Security Application functions of the Compute API. +type SecurityApplicationsClient struct { + ResourceClient +} + +// SecurityApplications obtains a SecurityApplicationsClient which can be used to access to the +// Security Application functions of the Compute API +func (c *Client) SecurityApplications() *SecurityApplicationsClient { + return &SecurityApplicationsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "security application", + ContainerPath: "/secapplication/", + ResourceRootPath: "/secapplication", + }} +} + +// SecurityApplicationInfo describes an existing security application. +type SecurityApplicationInfo struct { + // A description of the security application. + Description string `json:"description"` + // The TCP or UDP destination port number. This can be a port range, such as 5900-5999 for TCP. + DPort string `json:"dport"` + // The ICMP code. + ICMPCode SecurityApplicationICMPCode `json:"icmpcode"` + // The ICMP type. + ICMPType SecurityApplicationICMPType `json:"icmptype"` + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The protocol to use. + Protocol SecurityApplicationProtocol `json:"protocol"` + // The Uniform Resource Identifier + URI string `json:"uri"` +} + +type SecurityApplicationProtocol string + +const ( + All SecurityApplicationProtocol = "all" + AH SecurityApplicationProtocol = "ah" + ESP SecurityApplicationProtocol = "esp" + ICMP SecurityApplicationProtocol = "icmp" + ICMPV6 SecurityApplicationProtocol = "icmpv6" + IGMP SecurityApplicationProtocol = "igmp" + IPIP SecurityApplicationProtocol = "ipip" + GRE SecurityApplicationProtocol = "gre" + MPLSIP SecurityApplicationProtocol = "mplsip" + OSPF SecurityApplicationProtocol = "ospf" + PIM SecurityApplicationProtocol = "pim" + RDP SecurityApplicationProtocol = "rdp" + SCTP SecurityApplicationProtocol = "sctp" + TCP SecurityApplicationProtocol = "tcp" + UDP SecurityApplicationProtocol = "udp" +) + +type SecurityApplicationICMPCode string + +const ( + Admin SecurityApplicationICMPCode = "admin" + Df SecurityApplicationICMPCode = "df" + Host SecurityApplicationICMPCode = "host" + Network SecurityApplicationICMPCode = "network" + Port SecurityApplicationICMPCode = "port" + Protocol SecurityApplicationICMPCode = "protocol" +) + +type SecurityApplicationICMPType string + +const ( + Echo SecurityApplicationICMPType = "echo" + Reply SecurityApplicationICMPType = "reply" + TTL SecurityApplicationICMPType = "ttl" + TraceRoute SecurityApplicationICMPType = "traceroute" + Unreachable SecurityApplicationICMPType = "unreachable" +) + +func (c *SecurityApplicationsClient) success(result *SecurityApplicationInfo) (*SecurityApplicationInfo, error) { + c.unqualify(&result.Name) + return result, nil +} + +// CreateSecurityApplicationInput describes the Security Application to create +type CreateSecurityApplicationInput struct { + // A description of the security application. + // Optional + Description string `json:"description"` + // The TCP or UDP destination port number. + // You can also specify a port range, such as 5900-5999 for TCP. + // This parameter isn't relevant to the icmp protocol. + // Required if the Protocol is TCP or UDP + DPort string `json:"dport"` + // The ICMP code. This parameter is relevant only if you specify ICMP as the protocol. + // If you specify icmp as the protocol and don't specify icmptype or icmpcode, then all ICMP packets are matched. + // Optional + ICMPCode SecurityApplicationICMPCode `json:"icmpcode,omitempty"` + // This parameter is relevant only if you specify ICMP as the protocol. + // If you specify icmp as the protocol and don't specify icmptype or icmpcode, then all ICMP packets are matched. + // Optional + ICMPType SecurityApplicationICMPType `json:"icmptype,omitempty"` + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The protocol to use. + // Required + Protocol SecurityApplicationProtocol `json:"protocol"` +} + +// CreateSecurityApplication creates a new security application. +func (c *SecurityApplicationsClient) CreateSecurityApplication(input *CreateSecurityApplicationInput) (*SecurityApplicationInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var appInfo SecurityApplicationInfo + if err := c.createResource(&input, &appInfo); err != nil { + return nil, err + } + + return c.success(&appInfo) +} + +// GetSecurityApplicationInput describes the Security Application to obtain +type GetSecurityApplicationInput struct { + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetSecurityApplication retrieves the security application with the given name. +func (c *SecurityApplicationsClient) GetSecurityApplication(input *GetSecurityApplicationInput) (*SecurityApplicationInfo, error) { + var appInfo SecurityApplicationInfo + if err := c.getResource(input.Name, &appInfo); err != nil { + return nil, err + } + + return c.success(&appInfo) +} + +// DeleteSecurityApplicationInput describes the Security Application to delete +type DeleteSecurityApplicationInput struct { + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// DeleteSecurityApplication deletes the security application with the given name. +func (c *SecurityApplicationsClient) DeleteSecurityApplication(input *DeleteSecurityApplicationInput) error { + return c.deleteResource(input.Name) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go new file mode 100644 index 000000000..ea30d7813 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go @@ -0,0 +1,95 @@ +package compute + +// SecurityAssociationsClient is a client for the Security Association functions of the Compute API. +type SecurityAssociationsClient struct { + ResourceClient +} + +// SecurityAssociations obtains a SecurityAssociationsClient which can be used to access to the +// Security Association functions of the Compute API +func (c *Client) SecurityAssociations() *SecurityAssociationsClient { + return &SecurityAssociationsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "security association", + ContainerPath: "/secassociation/", + ResourceRootPath: "/secassociation", + }} +} + +// SecurityAssociationInfo describes an existing security association. +type SecurityAssociationInfo struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The name of the Security List that you want to associate with the instance. + SecList string `json:"seclist"` + // vCable of the instance that you want to associate with the security list. + VCable string `json:"vcable"` + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateSecurityAssociationInput defines a security association to be created. +type CreateSecurityAssociationInput struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + // If you don't specify a name for this object, then the name is generated automatically. + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Optional + Name string `json:"name"` + // The name of the Security list that you want to associate with the instance. + // Required + SecList string `json:"seclist"` + // The name of the vCable of the instance that you want to associate with the security list. + // Required + VCable string `json:"vcable"` +} + +// CreateSecurityAssociation creates a security association between the given VCable and security list. +func (c *SecurityAssociationsClient) CreateSecurityAssociation(createInput *CreateSecurityAssociationInput) (*SecurityAssociationInfo, error) { + if createInput.Name != "" { + createInput.Name = c.getQualifiedName(createInput.Name) + } + createInput.VCable = c.getQualifiedName(createInput.VCable) + createInput.SecList = c.getQualifiedName(createInput.SecList) + + var assocInfo SecurityAssociationInfo + if err := c.createResource(&createInput, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +// GetSecurityAssociationInput describes the security association to get +type GetSecurityAssociationInput struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetSecurityAssociation retrieves the security association with the given name. +func (c *SecurityAssociationsClient) GetSecurityAssociation(getInput *GetSecurityAssociationInput) (*SecurityAssociationInfo, error) { + var assocInfo SecurityAssociationInfo + if err := c.getResource(getInput.Name, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +// DeleteSecurityAssociationInput describes the security association to delete +type DeleteSecurityAssociationInput struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// DeleteSecurityAssociation deletes the security association with the given name. +func (c *SecurityAssociationsClient) DeleteSecurityAssociation(deleteInput *DeleteSecurityAssociationInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecurityAssociationsClient) success(assocInfo *SecurityAssociationInfo) (*SecurityAssociationInfo, error) { + c.unqualify(&assocInfo.Name, &assocInfo.SecList, &assocInfo.VCable) + return assocInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go new file mode 100644 index 000000000..08314cd12 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go @@ -0,0 +1,113 @@ +package compute + +// SecurityIPListsClient is a client for the Security IP List functions of the Compute API. +type SecurityIPListsClient struct { + ResourceClient +} + +// SecurityIPLists obtains a SecurityIPListsClient which can be used to access to the +// Security IP List functions of the Compute API +func (c *Client) SecurityIPLists() *SecurityIPListsClient { + return &SecurityIPListsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "security ip list", + ContainerPath: "/seciplist/", + ResourceRootPath: "/seciplist", + }} +} + +// SecurityIPListInfo describes an existing security IP list. +type SecurityIPListInfo struct { + // A description of the security IP list. + Description string `json:"description"` + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + // A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list. + SecIPEntries []string `json:"secipentries"` + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateSecurityIPListInput defines a security IP list to be created. +type CreateSecurityIPListInput struct { + // A description of the security IP list. + // Optional + Description string `json:"description"` + // The three-part name of the object (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list. + // Required + SecIPEntries []string `json:"secipentries"` +} + +// CreateSecurityIPList creates a security IP list with the given name and entries. +func (c *SecurityIPListsClient) CreateSecurityIPList(createInput *CreateSecurityIPListInput) (*SecurityIPListInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + var listInfo SecurityIPListInfo + if err := c.createResource(createInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// GetSecurityIPListInput describes the Security IP List to obtain +type GetSecurityIPListInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetSecurityIPList gets the security IP list with the given name. +func (c *SecurityIPListsClient) GetSecurityIPList(getInput *GetSecurityIPListInput) (*SecurityIPListInfo, error) { + var listInfo SecurityIPListInfo + if err := c.getResource(getInput.Name, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// UpdateSecurityIPListInput describes the security ip list to update +type UpdateSecurityIPListInput struct { + // A description of the security IP list. + // Optional + Description string `json:"description"` + // The three-part name of the object (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` + // A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list. + // Required + SecIPEntries []string `json:"secipentries"` +} + +// UpdateSecurityIPList modifies the entries in the security IP list with the given name. +func (c *SecurityIPListsClient) UpdateSecurityIPList(updateInput *UpdateSecurityIPListInput) (*SecurityIPListInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var listInfo SecurityIPListInfo + if err := c.updateResource(updateInput.Name, updateInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// DeleteSecurityIPListInput describes the security ip list to delete. +type DeleteSecurityIPListInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// DeleteSecurityIPList deletes the security IP list with the given name. +func (c *SecurityIPListsClient) DeleteSecurityIPList(deleteInput *DeleteSecurityIPListInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecurityIPListsClient) success(listInfo *SecurityIPListInfo) (*SecurityIPListInfo, error) { + c.unqualify(&listInfo.Name) + return listInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go new file mode 100644 index 000000000..fd654fac6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go @@ -0,0 +1,131 @@ +package compute + +// SecurityListsClient is a client for the Security List functions of the Compute API. +type SecurityListsClient struct { + ResourceClient +} + +// SecurityLists obtains a SecurityListsClient which can be used to access to the +// Security List functions of the Compute API +func (c *Client) SecurityLists() *SecurityListsClient { + return &SecurityListsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "security list", + ContainerPath: "/seclist/", + ResourceRootPath: "/seclist", + }} +} + +type SecurityListPolicy string + +const ( + SecurityListPolicyDeny SecurityListPolicy = "deny" + SecurityListPolicyReject SecurityListPolicy = "reject" + SecurityListPolicyPermit SecurityListPolicy = "permit" +) + +// SecurityListInfo describes an existing security list. +type SecurityListInfo struct { + // Shows the default account for your identity domain. + Account string `json:"account"` + // A description of the security list. + Description string `json:description` + // The three-part name of the security list (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The policy for outbound traffic from the security list. + OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"` + // The policy for inbound traffic to the security list + Policy SecurityListPolicy `json:"policy"` + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateSecurityListInput defines a security list to be created. +type CreateSecurityListInput struct { + // A description of the security list. + // Optional + Description string `json:"description"` + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The policy for outbound traffic from the security list. + // Optional (defaults to `permit`) + OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"` + // The policy for inbound traffic to the security list. + // Optional (defaults to `deny`) + Policy SecurityListPolicy `json:"policy"` +} + +// CreateSecurityList creates a new security list with the given name, policy and outbound CIDR policy. +func (c *SecurityListsClient) CreateSecurityList(createInput *CreateSecurityListInput) (*SecurityListInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + var listInfo SecurityListInfo + if err := c.createResource(createInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// GetSecurityListInput describes the security list you want to get +type GetSecurityListInput struct { + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Required + Name string `json:name` +} + +// GetSecurityList retrieves the security list with the given name. +func (c *SecurityListsClient) GetSecurityList(getInput *GetSecurityListInput) (*SecurityListInfo, error) { + var listInfo SecurityListInfo + if err := c.getResource(getInput.Name, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// UpdateSecurityListInput defines what to update in a security list +type UpdateSecurityListInput struct { + // A description of the security list. + // Optional + Description string `json:description` + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` + // The policy for outbound traffic from the security list. + // Optional (defaults to `permit`) + OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"` + // The policy for inbound traffic to the security list. + // Optional (defaults to `deny`) + Policy SecurityListPolicy `json:"policy"` +} + +// UpdateSecurityList updates the policy and outbound CIDR pol +func (c *SecurityListsClient) UpdateSecurityList(updateInput *UpdateSecurityListInput) (*SecurityListInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var listInfo SecurityListInfo + if err := c.updateResource(updateInput.Name, updateInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// DeleteSecurityListInput describes the security list to destroy +type DeleteSecurityListInput struct { + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Required + Name string `json:name` +} + +// DeleteSecurityList deletes the security list with the given name. +func (c *SecurityListsClient) DeleteSecurityList(deleteInput *DeleteSecurityListInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecurityListsClient) success(listInfo *SecurityListInfo) (*SecurityListInfo, error) { + c.unqualify(&listInfo.Name) + return listInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go new file mode 100644 index 000000000..e54aeef89 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go @@ -0,0 +1,187 @@ +package compute + +const ( + SecurityProtocolDescription = "security protocol" + SecurityProtocolContainerPath = "/network/v1/secprotocol/" + SecurityProtocolResourcePath = "/network/v1/secprotocol" +) + +type SecurityProtocolsClient struct { + ResourceClient +} + +// SecurityProtocols() returns an SecurityProtocolsClient that can be used to access the +// necessary CRUD functions for Security Protocols. +func (c *Client) SecurityProtocols() *SecurityProtocolsClient { + return &SecurityProtocolsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: SecurityProtocolDescription, + ContainerPath: SecurityProtocolContainerPath, + ResourceRootPath: SecurityProtocolResourcePath, + }, + } +} + +// SecurityProtocolInfo contains the exported fields necessary to hold all the information about an +// Security Protocol +type SecurityProtocolInfo struct { + // List of port numbers or port range strings to match the packet's destination port. + DstPortSet []string `json:"dstPortSet"` + // Protocol used in the data portion of the IP datagram. + IPProtocol string `json:"ipProtocol"` + // List of port numbers or port range strings to match the packet's source port. + SrcPortSet []string `json:"srcPortSet"` + // The name of the Security Protocol + Name string `json:"name"` + // Description of the Security Protocol + Description string `json:"description"` + // Slice of tags associated with the Security Protocol + Tags []string `json:"tags"` + // Uniform Resource Identifier for the Security Protocol + Uri string `json:"uri"` +} + +type CreateSecurityProtocolInput struct { + // The name of the Security Protocol to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the SecurityProtocol + // Optional + Description string `json:"description"` + + // Enter a list of port numbers or port range strings. + //Traffic is enabled by a security rule when a packet's destination port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a destination transport port, between 0 and 65535, + // inclusive. For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no destination ports are specified, all destination ports or ICMP types are allowed. + // Optional + DstPortSet []string `json:"dstPortSet"` + + // The protocol used in the data portion of the IP datagram. + // Specify one of the permitted values or enter a number in the range 0–254 to + // represent the protocol that you want to specify. See Assigned Internet Protocol Numbers. + // Permitted values are: tcp, udp, icmp, igmp, ipip, rdp, esp, ah, gre, icmpv6, ospf, pim, sctp, + // mplsip, all. + // Traffic is enabled by a security rule when the protocol in the packet matches the + // protocol specified here. If no protocol is specified, all protocols are allowed. + // Optional + IPProtocol string `json:"ipProtocol"` + + // Enter a list of port numbers or port range strings. + // Traffic is enabled by a security rule when a packet's source port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a source transport port, + // between 0 and 65535, inclusive. + // For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no source ports are specified, all source ports or ICMP types are allowed. + // Optional + SrcPortSet []string `json:"srcPortSet"` + + // String slice of tags to apply to the Security Protocol object + // Optional + Tags []string `json:"tags"` +} + +// Create a new Security Protocol from an SecurityProtocolsClient and an input struct. +// Returns a populated Info struct for the Security Protocol, and any errors +func (c *SecurityProtocolsClient) CreateSecurityProtocol(input *CreateSecurityProtocolInput) (*SecurityProtocolInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo SecurityProtocolInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetSecurityProtocolInput struct { + // The name of the Security Protocol to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated SecurityProtocolInfo struct from an input struct +func (c *SecurityProtocolsClient) GetSecurityProtocol(input *GetSecurityProtocolInput) (*SecurityProtocolInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo SecurityProtocolInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +// UpdateSecurityProtocolInput defines what to update in a security protocol +type UpdateSecurityProtocolInput struct { + // The name of the Security Protocol to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the SecurityProtocol + // Optional + Description string `json:"description"` + + // Enter a list of port numbers or port range strings. + //Traffic is enabled by a security rule when a packet's destination port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a destination transport port, between 0 and 65535, + // inclusive. For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no destination ports are specified, all destination ports or ICMP types are allowed. + DstPortSet []string `json:"dstPortSet"` + + // The protocol used in the data portion of the IP datagram. + // Specify one of the permitted values or enter a number in the range 0–254 to + // represent the protocol that you want to specify. See Assigned Internet Protocol Numbers. + // Permitted values are: tcp, udp, icmp, igmp, ipip, rdp, esp, ah, gre, icmpv6, ospf, pim, sctp, + // mplsip, all. + // Traffic is enabled by a security rule when the protocol in the packet matches the + // protocol specified here. If no protocol is specified, all protocols are allowed. + IPProtocol string `json:"ipProtocol"` + + // Enter a list of port numbers or port range strings. + // Traffic is enabled by a security rule when a packet's source port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a source transport port, + // between 0 and 65535, inclusive. + // For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no source ports are specified, all source ports or ICMP types are allowed. + SrcPortSet []string `json:"srcPortSet"` + + // String slice of tags to apply to the Security Protocol object + // Optional + Tags []string `json:"tags"` +} + +// UpdateSecurityProtocol update the security protocol +func (c *SecurityProtocolsClient) UpdateSecurityProtocol(updateInput *UpdateSecurityProtocolInput) (*SecurityProtocolInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var ipInfo SecurityProtocolInfo + if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteSecurityProtocolInput struct { + // The name of the Security Protocol to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *SecurityProtocolsClient) DeleteSecurityProtocol(input *DeleteSecurityProtocolInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the SecurityProtocolInfo struct +func (c *SecurityProtocolsClient) success(info *SecurityProtocolInfo) (*SecurityProtocolInfo, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go new file mode 100644 index 000000000..025a3465a --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go @@ -0,0 +1,266 @@ +package compute + +const ( + SecurityRuleDescription = "security rules" + SecurityRuleContainerPath = "/network/v1/secrule/" + SecurityRuleResourcePath = "/network/v1/secrule" +) + +type SecurityRuleClient struct { + ResourceClient +} + +// SecurityRules() returns an SecurityRulesClient that can be used to access the +// necessary CRUD functions for Security Rules. +func (c *Client) SecurityRules() *SecurityRuleClient { + return &SecurityRuleClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: SecurityRuleDescription, + ContainerPath: SecurityRuleContainerPath, + ResourceRootPath: SecurityRuleResourcePath, + }, + } +} + +// SecurityRuleInfo contains the exported fields necessary to hold all the information about a +// Security Rule +type SecurityRuleInfo struct { + // Name of the ACL that contains this rule. + ACL string `json:"acl"` + // Description of the Security Rule + Description string `json:"description"` + // List of IP address prefix set names to match the packet's destination IP address. + DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` + // Name of virtual NIC set containing the packet's destination virtual NIC. + DstVnicSet string `json:"dstVnicSet"` + // Allows the security rule to be disabled. + Enabled bool `json:"enabledFlag"` + // Direction of the flow; Can be "egress" or "ingress". + FlowDirection string `json:"FlowDirection"` + // The name of the Security Rule + Name string `json:"name"` + // List of security protocol names to match the packet's protocol and port. + SecProtocols []string `json:"secProtocols"` + // List of multipart names of IP address prefix set to match the packet's source IP address. + SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` + // Name of virtual NIC set containing the packet's source virtual NIC. + SrcVnicSet string `json:"srcVnicSet"` + // Slice of tags associated with the Security Rule + Tags []string `json:"tags"` + // Uniform Resource Identifier for the Security Rule + Uri string `json:"uri"` +} + +type CreateSecurityRuleInput struct { + //Select the name of the access control list (ACL) that you want to add this + // security rule to. Security rules are applied to vNIC sets by using ACLs. + // Optional + ACL string `json:"acl,omitempty"` + + // Description of the Security Rule + // Optional + Description string `json:"description"` + + // A list of IP address prefix sets to which you want to permit traffic. + // Only packets to IP addresses in the specified IP address prefix sets are permitted. + // When no destination IP address prefix sets are specified, traffic to any + // IP address is permitted. + // Optional + DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` + + // The vNICset to which you want to permit traffic. Only packets to vNICs in the + // specified vNICset are permitted. When no destination vNICset is specified, traffic + // to any vNIC is permitted. + // Optional + DstVnicSet string `json:"dstVnicSet,omitempty"` + + // Allows the security rule to be enabled or disabled. This parameter is set to + // true by default. Specify false to disable the security rule. + // Optional + Enabled bool `json:"enabledFlag"` + + // Specify the direction of flow of traffic, which is relative to the instances, + // for this security rule. Allowed values are ingress or egress. + // An ingress packet is a packet received by a virtual NIC, for example from + // another virtual NIC or from the public Internet. + // An egress packet is a packet sent by a virtual NIC, for example to another + // virtual NIC or to the public Internet. + // Required + FlowDirection string `json:"flowDirection"` + + // The name of the Security Rule + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. When you specify the object name, ensure that an object + // of the same type and with the same name doesn't already exist. + // If such an object already exists, another object of the same type and with the same name won't + // be created and the existing object won't be updated. + // Required + Name string `json:"name"` + + // A list of security protocols for which you want to permit traffic. Only packets that + // match the specified protocols and ports are permitted. When no security protocols are + // specified, traffic using any protocol over any port is permitted. + // Optional + SecProtocols []string `json:"secProtocols"` + + // A list of IP address prefix sets from which you want to permit traffic. Only packets + // from IP addresses in the specified IP address prefix sets are permitted. When no source + // IP address prefix sets are specified, traffic from any IP address is permitted. + // Optional + SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` + + // The vNICset from which you want to permit traffic. Only packets from vNICs in the + // specified vNICset are permitted. When no source vNICset is specified, traffic from any + // vNIC is permitted. + // Optional + SrcVnicSet string `json:"srcVnicSet,omitempty"` + + // Strings that you can use to tag the security rule. + // Optional + Tags []string `json:"tags"` +} + +// Create a new Security Rule from an SecurityRuleClient and an input struct. +// Returns a populated Info struct for the Security Rule, and any errors +func (c *SecurityRuleClient) CreateSecurityRule(input *CreateSecurityRuleInput) (*SecurityRuleInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.ACL = c.getQualifiedName(input.ACL) + input.SrcVnicSet = c.getQualifiedName(input.SrcVnicSet) + input.DstVnicSet = c.getQualifiedName(input.DstVnicSet) + input.SrcIpAddressPrefixSets = c.getQualifiedList(input.SrcIpAddressPrefixSets) + input.DstIpAddressPrefixSets = c.getQualifiedList(input.DstIpAddressPrefixSets) + input.SecProtocols = c.getQualifiedList(input.SecProtocols) + + var securityRuleInfo SecurityRuleInfo + if err := c.createResource(&input, &securityRuleInfo); err != nil { + return nil, err + } + + return c.success(&securityRuleInfo) +} + +type GetSecurityRuleInput struct { + // The name of the Security Rule to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated SecurityRuleInfo struct from an input struct +func (c *SecurityRuleClient) GetSecurityRule(input *GetSecurityRuleInput) (*SecurityRuleInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var securityRuleInfo SecurityRuleInfo + if err := c.getResource(input.Name, &securityRuleInfo); err != nil { + return nil, err + } + + return c.success(&securityRuleInfo) +} + +// UpdateSecurityRuleInput describes a secruity rule to update +type UpdateSecurityRuleInput struct { + //Select the name of the access control list (ACL) that you want to add this + // security rule to. Security rules are applied to vNIC sets by using ACLs. + // Optional + ACL string `json:"acl,omitempty"` + + // Description of the Security Rule + // Optional + Description string `json:"description"` + + // A list of IP address prefix sets to which you want to permit traffic. + // Only packets to IP addresses in the specified IP address prefix sets are permitted. + // When no destination IP address prefix sets are specified, traffic to any + // IP address is permitted. + // Optional + DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` + + // The vNICset to which you want to permit traffic. Only packets to vNICs in the + // specified vNICset are permitted. When no destination vNICset is specified, traffic + // to any vNIC is permitted. + // Optional + DstVnicSet string `json:"dstVnicSet,omitempty"` + + // Allows the security rule to be enabled or disabled. This parameter is set to + // true by default. Specify false to disable the security rule. + // Optional + Enabled bool `json:"enabledFlag"` + + // Specify the direction of flow of traffic, which is relative to the instances, + // for this security rule. Allowed values are ingress or egress. + // An ingress packet is a packet received by a virtual NIC, for example from + // another virtual NIC or from the public Internet. + // An egress packet is a packet sent by a virtual NIC, for example to another + // virtual NIC or to the public Internet. + // Required + FlowDirection string `json:"flowDirection"` + + // The name of the Security Rule + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. When you specify the object name, ensure that an object + // of the same type and with the same name doesn't already exist. + // If such an object already exists, another object of the same type and with the same name won't + // be created and the existing object won't be updated. + // Required + Name string `json:"name"` + + // A list of security protocols for which you want to permit traffic. Only packets that + // match the specified protocols and ports are permitted. When no security protocols are + // specified, traffic using any protocol over any port is permitted. + // Optional + SecProtocols []string `json:"secProtocols"` + + // A list of IP address prefix sets from which you want to permit traffic. Only packets + // from IP addresses in the specified IP address prefix sets are permitted. When no source + // IP address prefix sets are specified, traffic from any IP address is permitted. + // Optional + SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` + + // The vNICset from which you want to permit traffic. Only packets from vNICs in the + // specified vNICset are permitted. When no source vNICset is specified, traffic from any + // vNIC is permitted. + // Optional + SrcVnicSet string `json:"srcVnicSet,omitempty"` + + // Strings that you can use to tag the security rule. + // Optional + Tags []string `json:"tags"` +} + +// UpdateSecRule modifies the properties of the sec rule with the given name. +func (c *SecurityRuleClient) UpdateSecurityRule(updateInput *UpdateSecurityRuleInput) (*SecurityRuleInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + updateInput.ACL = c.getQualifiedName(updateInput.ACL) + updateInput.SrcVnicSet = c.getQualifiedName(updateInput.SrcVnicSet) + updateInput.DstVnicSet = c.getQualifiedName(updateInput.DstVnicSet) + updateInput.SrcIpAddressPrefixSets = c.getQualifiedList(updateInput.SrcIpAddressPrefixSets) + updateInput.DstIpAddressPrefixSets = c.getQualifiedList(updateInput.DstIpAddressPrefixSets) + updateInput.SecProtocols = c.getQualifiedList(updateInput.SecProtocols) + + var securityRuleInfo SecurityRuleInfo + if err := c.updateResource(updateInput.Name, updateInput, &securityRuleInfo); err != nil { + return nil, err + } + + return c.success(&securityRuleInfo) +} + +type DeleteSecurityRuleInput struct { + // The name of the Security Rule to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *SecurityRuleClient) DeleteSecurityRule(input *DeleteSecurityRuleInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPNetworkExchangeInfo struct +func (c *SecurityRuleClient) success(info *SecurityRuleInfo) (*SecurityRuleInfo, error) { + c.unqualify(&info.Name, &info.ACL, &info.SrcVnicSet, &info.DstVnicSet) + info.SrcIpAddressPrefixSets = c.getUnqualifiedList(info.SrcIpAddressPrefixSets) + info.DstIpAddressPrefixSets = c.getUnqualifiedList(info.DstIpAddressPrefixSets) + info.SecProtocols = c.getUnqualifiedList(info.SecProtocols) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go new file mode 100644 index 000000000..8ae2b03c2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go @@ -0,0 +1,112 @@ +package compute + +// SSHKeysClient is a client for the SSH key functions of the Compute API. +type SSHKeysClient struct { + ResourceClient +} + +// SSHKeys obtains an SSHKeysClient which can be used to access to the +// SSH key functions of the Compute API +func (c *Client) SSHKeys() *SSHKeysClient { + return &SSHKeysClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "SSH key", + ContainerPath: "/sshkey/", + ResourceRootPath: "/sshkey", + }} +} + +// SSHKeyInfo describes an existing SSH key. +type SSHKey struct { + // Indicates whether the key is enabled (true) or disabled. + Enabled bool `json:"enabled"` + // The SSH public key value. + Key string `json:"key"` + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + Name string `json:"name"` + // Unique Resource Identifier + URI string `json:"uri"` +} + +// CreateSSHKeyInput defines an SSH key to be created. +type CreateSSHKeyInput struct { + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The SSH public key value. + // Required + Key string `json:"key"` + // Indicates whether the key must be enabled (default) or disabled. Note that disabled keys cannot be associated with instances. + // To explicitly enable the key, specify true. To disable the key, specify false. + // Optional + Enabled bool `json:"enabled"` +} + +// CreateSSHKey creates a new SSH key with the given name, key and enabled flag. +func (c *SSHKeysClient) CreateSSHKey(createInput *CreateSSHKeyInput) (*SSHKey, error) { + var keyInfo SSHKey + createInput.Name = c.getQualifiedName(createInput.Name) + if err := c.createResource(&createInput, &keyInfo); err != nil { + return nil, err + } + + return c.success(&keyInfo) +} + +// GetSSHKeyInput describes the ssh key to get +type GetSSHKeyInput struct { + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + Name string `json:name` +} + +// GetSSHKey retrieves the SSH key with the given name. +func (c *SSHKeysClient) GetSSHKey(getInput *GetSSHKeyInput) (*SSHKey, error) { + var keyInfo SSHKey + if err := c.getResource(getInput.Name, &keyInfo); err != nil { + return nil, err + } + + return c.success(&keyInfo) +} + +// UpdateSSHKeyInput defines an SSH key to be updated +type UpdateSSHKeyInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The SSH public key value. + // Required + Key string `json:"key"` + // Indicates whether the key must be enabled (default) or disabled. Note that disabled keys cannot be associated with instances. + // To explicitly enable the key, specify true. To disable the key, specify false. + // Optional + // TODO/NOTE: isn't this required? + Enabled bool `json:"enabled"` +} + +// UpdateSSHKey updates the key and enabled flag of the SSH key with the given name. +func (c *SSHKeysClient) UpdateSSHKey(updateInput *UpdateSSHKeyInput) (*SSHKey, error) { + var keyInfo SSHKey + updateInput.Name = c.getQualifiedName(updateInput.Name) + if err := c.updateResource(updateInput.Name, updateInput, &keyInfo); err != nil { + return nil, err + } + return c.success(&keyInfo) +} + +// DeleteKeyInput describes the ssh key to delete +type DeleteSSHKeyInput struct { + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + Name string `json:name` +} + +// DeleteSSHKey deletes the SSH key with the given name. +func (c *SSHKeysClient) DeleteSSHKey(deleteInput *DeleteSSHKeyInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SSHKeysClient) success(keyInfo *SSHKey) (*SSHKey, error) { + c.unqualify(&keyInfo.Name) + return keyInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go new file mode 100644 index 000000000..c24cf0cc9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go @@ -0,0 +1,158 @@ +package compute + +const WaitForVolumeAttachmentDeleteTimeout = 30 +const WaitForVolumeAttachmentReadyTimeout = 30 + +// StorageAttachmentsClient is a client for the Storage Attachment functions of the Compute API. +type StorageAttachmentsClient struct { + ResourceClient +} + +// StorageAttachments obtains a StorageAttachmentsClient which can be used to access to the +// Storage Attachment functions of the Compute API +func (c *Client) StorageAttachments() *StorageAttachmentsClient { + return &StorageAttachmentsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "storage volume attachment", + ContainerPath: "/storage/attachment/", + ResourceRootPath: "/storage/attachment", + }} +} + +type StorageAttachmentState string + +const ( + Attaching StorageAttachmentState = "attaching" + Attached StorageAttachmentState = "attached" + Detaching StorageAttachmentState = "detaching" + Unavailable StorageAttachmentState = "unavailable" + Unknown StorageAttachmentState = "unknown" +) + +// StorageAttachmentInfo describes an existing storage attachment. +type StorageAttachmentInfo struct { + // Name of this attachment, generated by the server. + Name string `json:"name"` + + // Index number for the volume. The allowed range is 1-10 + // An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on. + Index int `json:"index"` + + // Multipart name of the instance attached to the storage volume. + InstanceName string `json:"instance_name"` + + // Multipart name of the volume attached to the instance. + StorageVolumeName string `json:"storage_volume_name"` + + // The State of the Storage Attachment + State StorageAttachmentState `json:"state"` +} + +func (c *StorageAttachmentsClient) success(attachmentInfo *StorageAttachmentInfo) (*StorageAttachmentInfo, error) { + c.unqualify(&attachmentInfo.Name, &attachmentInfo.InstanceName, &attachmentInfo.StorageVolumeName) + return attachmentInfo, nil +} + +type CreateStorageAttachmentInput struct { + // Index number for the volume. The allowed range is 1-10 + // An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on. + // Required. + Index int `json:"index"` + + // Multipart name of the instance to which you want to attach the volume. + // Required. + InstanceName string `json:"instance_name"` + + // Multipart name of the volume that you want to attach. + // Required. + StorageVolumeName string `json:"storage_volume_name"` +} + +// CreateStorageAttachment creates a storage attachment attaching the given volume to the given instance at the given index. +func (c *StorageAttachmentsClient) CreateStorageAttachment(input *CreateStorageAttachmentInput) (*StorageAttachmentInfo, error) { + input.InstanceName = c.getQualifiedName(input.InstanceName) + + var attachmentInfo *StorageAttachmentInfo + if err := c.createResource(&input, &attachmentInfo); err != nil { + return nil, err + } + + return c.waitForStorageAttachmentToFullyAttach(attachmentInfo.Name, WaitForVolumeAttachmentReadyTimeout) +} + +// DeleteStorageAttachmentInput represents the body of an API request to delete a Storage Attachment. +type DeleteStorageAttachmentInput struct { + // The three-part name of the Storage Attachment (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// DeleteStorageAttachment deletes the storage attachment with the given name. +func (c *StorageAttachmentsClient) DeleteStorageAttachment(input *DeleteStorageAttachmentInput) error { + if err := c.deleteResource(input.Name); err != nil { + return err + } + + return c.waitForStorageAttachmentToBeDeleted(input.Name, WaitForVolumeAttachmentDeleteTimeout) +} + +// GetStorageAttachmentInput represents the body of an API request to obtain a Storage Attachment. +type GetStorageAttachmentInput struct { + // The three-part name of the Storage Attachment (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetStorageAttachment retrieves the storage attachment with the given name. +func (c *StorageAttachmentsClient) GetStorageAttachment(input *GetStorageAttachmentInput) (*StorageAttachmentInfo, error) { + var attachmentInfo *StorageAttachmentInfo + if err := c.getResource(input.Name, &attachmentInfo); err != nil { + return nil, err + } + + return c.success(attachmentInfo) +} + +// waitForStorageAttachmentToFullyAttach waits for the storage attachment with the given name to be fully attached, or times out. +func (c *StorageAttachmentsClient) waitForStorageAttachmentToFullyAttach(name string, timeoutSeconds int) (*StorageAttachmentInfo, error) { + var waitResult *StorageAttachmentInfo + + err := c.waitFor("storage attachment to be attached", timeoutSeconds, func() (bool, error) { + input := &GetStorageAttachmentInput{ + Name: name, + } + info, err := c.GetStorageAttachment(input) + if err != nil { + return false, err + } + + if info != nil { + if info.State == Attached { + waitResult = info + return true, nil + } + } + + return false, nil + }) + + return waitResult, err +} + +// waitForStorageAttachmentToBeDeleted waits for the storage attachment with the given name to be fully deleted, or times out. +func (c *StorageAttachmentsClient) waitForStorageAttachmentToBeDeleted(name string, timeoutSeconds int) error { + return c.waitFor("storage attachment to be deleted", timeoutSeconds, func() (bool, error) { + input := &GetStorageAttachmentInput{ + Name: name, + } + _, err := c.GetStorageAttachment(input) + if err != nil { + if WasNotFoundError(err) { + return true, nil + } + return false, err + } + return false, nil + }) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go new file mode 100644 index 000000000..c40c354a9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go @@ -0,0 +1,345 @@ +package compute + +import ( + "fmt" + "strconv" + "strings" +) + +const WaitForVolumeReadyTimeout = 30 +const WaitForVolumeDeleteTimeout = 30 + +// StorageVolumeClient is a client for the Storage Volume functions of the Compute API. +type StorageVolumeClient struct { + ResourceClient +} + +// StorageVolumes obtains a StorageVolumeClient which can be used to access to the +// Storage Volume functions of the Compute API +func (c *Client) StorageVolumes() *StorageVolumeClient { + return &StorageVolumeClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "storage volume", + ContainerPath: "/storage/volume/", + ResourceRootPath: "/storage/volume", + }} + +} + +type StorageVolumeKind string + +const ( + StorageVolumeKindDefault StorageVolumeKind = "/oracle/public/storage/default" + StorageVolumeKindLatency StorageVolumeKind = "/oracle/public/storage/latency" +) + +// StorageVolumeInfo represents information retrieved from the service about a Storage Volume. +type StorageVolumeInfo struct { + // Shows the default account for your identity domain. + Account string `json:"account,omitempty"` + + // true indicates that the storage volume can also be used as a boot disk for an instance. + // If you set the value to true, then you must specify values for the `ImageList` and `ImageListEntry` fields. + Bootable bool `json:"bootable,omitempty"` + + // The description of the storage volume. + Description string `json:"description,omitempty"` + + // The hypervisor that this volume is compatible with. + Hypervisor string `json:"hypervisor,omitempty"` + + // Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes. + ImageList string `json:"imagelist,omitempty"` + + // Specific imagelist entry version to extract. + ImageListEntry int `json:"imagelist_entry,omitempty"` + + // Three-part name of the machine image. This information is available if the volume is a bootable storage volume. + MachineImage string `json:"machineimage_name,omitempty"` + + // All volumes are managed volumes. Default value is true. + Managed bool `json:"managed,omitempty"` + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The OS platform this volume is compatible with. + Platform string `json:"platform,omitempty` + + // The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default. + Properties []string `json:"properties,omitempty"` + + // Boolean field indicating whether this volume can be attached as readonly. If set to False the volume will be attached as read-write. + ReadOnly bool `json:"readonly,omitempty"` + + // The size of this storage volume in GB. + Size string `json:"size"` + + // Name of the parent snapshot from which the storage volume is restored or cloned. + Snapshot string `json:"snapshot,omitempty"` + + // Account of the parent snapshot from which the storage volume is restored. + SnapshotAccount string `json:"snapshot_account,omitempty"` + + // Id of the parent snapshot from which the storage volume is restored or cloned. + SnapshotID string `json:"snapshot_id,omitempty"` + + // TODO: this should become a Constant, if/when we have the values + // The current state of the storage volume. + Status string `json:"status,omitempty"` + + // Details about the latest state of the storage volume. + StatusDetail string `json:"status_detail,omitempty"` + + // It indicates the time that the current view of the storage volume was generated. + StatusTimestamp string `json:"status_timestamp,omitempty"` + + // The storage pool from which this volume is allocated. + StoragePool string `json:"storage_pool,omitempty"` + + // Comma-separated strings that tag the storage volume. + Tags []string `json:"tags,omitempty"` + + // Uniform Resource Identifier + URI string `json:"uri,omitempty"` +} + +func (c *StorageVolumeClient) getStorageVolumePath(name string) string { + return c.getObjectPath("/storage/volume", name) +} + +// CreateStorageVolumeInput represents the body of an API request to create a new Storage Volume. +type CreateStorageVolumeInput struct { + // true indicates that the storage volume can also be used as a boot disk for an instance. + // If you set the value to true, then you must specify values for the `ImageList` and `ImageListEntry` fields. + Bootable bool `json:"bootable,omitempty"` + + // The description of the storage volume. + Description string `json:"description,omitempty"` + + // Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes. + ImageList string `json:"imagelist,omitempty"` + + // Specific imagelist entry version to extract. + ImageListEntry int `json:"imagelist_entry,omitempty"` + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default. + Properties []string `json:"properties,omitempty"` + + // The size of this storage volume in GB. + Size string `json:"size"` + + // Name of the parent snapshot from which the storage volume is restored or cloned. + Snapshot string `json:"snapshot,omitempty"` + + // Account of the parent snapshot from which the storage volume is restored. + SnapshotAccount string `json:"snapshot_account,omitempty"` + + // Id of the parent snapshot from which the storage volume is restored or cloned. + SnapshotID string `json:"snapshot_id,omitempty"` + + // Comma-separated strings that tag the storage volume. + Tags []string `json:"tags,omitempty"` +} + +// CreateStorageVolume uses the given CreateStorageVolumeInput to create a new Storage Volume. +func (c *StorageVolumeClient) CreateStorageVolume(input *CreateStorageVolumeInput) (*StorageVolumeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.ImageList = c.getQualifiedName(input.ImageList) + + sizeInBytes, err := sizeInBytes(input.Size) + if err != nil { + return nil, err + } + input.Size = sizeInBytes + + var storageInfo StorageVolumeInfo + if err := c.createResource(&input, &storageInfo); err != nil { + return nil, err + } + + return c.waitForStorageVolumeToBecomeAvailable(input.Name, WaitForVolumeReadyTimeout) +} + +// DeleteStorageVolumeInput represents the body of an API request to delete a Storage Volume. +type DeleteStorageVolumeInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` +} + +// DeleteStorageVolume deletes the specified storage volume. +func (c *StorageVolumeClient) DeleteStorageVolume(input *DeleteStorageVolumeInput) error { + if err := c.deleteResource(input.Name); err != nil { + return err + } + + return c.waitForStorageVolumeToBeDeleted(input.Name, WaitForVolumeDeleteTimeout) +} + +// GetStorageVolumeInput represents the body of an API request to obtain a Storage Volume. +type GetStorageVolumeInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` +} + +func (c *StorageVolumeClient) success(result *StorageVolumeInfo) (*StorageVolumeInfo, error) { + c.unqualify(&result.Name) + + sizeInMegaBytes, err := sizeInGigaBytes(result.Size) + if err != nil { + return nil, err + } + result.Size = sizeInMegaBytes + + return result, nil +} + +// GetStorageVolume gets Storage Volume information for the specified storage volume. +func (c *StorageVolumeClient) GetStorageVolume(input *GetStorageVolumeInput) (*StorageVolumeInfo, error) { + var storageVolume StorageVolumeInfo + if err := c.getResource(input.Name, &storageVolume); err != nil { + if WasNotFoundError(err) { + return nil, nil + } + + return nil, err + } + + return c.success(&storageVolume) +} + +// UpdateStorageVolumeInput represents the body of an API request to update a Storage Volume. +type UpdateStorageVolumeInput struct { + // The description of the storage volume. + Description string `json:"description,omitempty"` + + // Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes. + ImageList string `json:"imagelist,omitempty"` + + // Specific imagelist entry version to extract. + ImageListEntry int `json:"imagelist_entry,omitempty"` + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default. + Properties []string `json:"properties,omitempty"` + + // The size of this storage volume in GB. + Size string `json:"size"` + + // Name of the parent snapshot from which the storage volume is restored or cloned. + Snapshot string `json:"snapshot,omitempty"` + + // Account of the parent snapshot from which the storage volume is restored. + SnapshotAccount string `json:"snapshot_account,omitempty"` + + // Id of the parent snapshot from which the storage volume is restored or cloned. + SnapshotID string `json:"snapshot_id,omitempty"` + + // Comma-separated strings that tag the storage volume. + Tags []string `json:"tags,omitempty"` +} + +// UpdateStorageVolume updates the specified storage volume, optionally modifying size, description and tags. +func (c *StorageVolumeClient) UpdateStorageVolume(input *UpdateStorageVolumeInput) (*StorageVolumeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.ImageList = c.getQualifiedName(input.ImageList) + + sizeInBytes, err := sizeInBytes(input.Size) + if err != nil { + return nil, err + } + input.Size = sizeInBytes + + path := c.getStorageVolumePath(input.Name) + _, err = c.executeRequest("PUT", path, input) + if err != nil { + return nil, err + } + + instanceInfo, err := c.waitForStorageVolumeToBecomeAvailable(input.Name, WaitForVolumeReadyTimeout) + if err != nil { + return nil, err + } + + return instanceInfo, nil +} + +// waitForStorageVolumeToBecomeAvailable waits until a new Storage Volume is available (i.e. has finished initialising or updating). +func (c *StorageVolumeClient) waitForStorageVolumeToBecomeAvailable(name string, timeoutInSeconds int) (*StorageVolumeInfo, error) { + var waitResult *StorageVolumeInfo + + err := c.waitFor( + fmt.Sprintf("storage volume %s to become available", c.getQualifiedName(name)), + timeoutInSeconds, + func() (bool, error) { + getRequest := &GetStorageVolumeInput{ + Name: name, + } + result, err := c.GetStorageVolume(getRequest) + + if err != nil { + return false, err + } + + if result != nil { + waitResult = result + if strings.ToLower(waitResult.Status) == "online" { + return true, nil + } + } + + return false, nil + }) + + return waitResult, err +} + +// waitForStorageVolumeToBeDeleted waits until the specified storage volume has been deleted. +func (c *StorageVolumeClient) waitForStorageVolumeToBeDeleted(name string, timeoutInSeconds int) error { + return c.waitFor( + fmt.Sprintf("storage volume %s to be deleted", c.getQualifiedName(name)), + timeoutInSeconds, + func() (bool, error) { + getRequest := &GetStorageVolumeInput{ + Name: name, + } + result, err := c.GetStorageVolume(getRequest) + if result == nil { + return true, nil + } + + if err != nil { + return false, err + } + + return result == nil, nil + }) +} + +func sizeInGigaBytes(input string) (string, error) { + sizeInBytes, err := strconv.Atoi(input) + if err != nil { + return "", err + } + sizeInKB := sizeInBytes / 1024 + sizeInMB := sizeInKB / 1024 + sizeInGb := sizeInMB / 1024 + return strconv.Itoa(sizeInGb), nil +} + +func sizeInBytes(input string) (string, error) { + sizeInGB, err := strconv.Atoi(input) + if err != nil { + return "", err + } + sizeInMB := sizeInGB * 1024 + sizeInKB := sizeInMB * 1024 + sizeInBytes := sizeInKB * 1024 + return strconv.Itoa(sizeInBytes), nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go new file mode 100644 index 000000000..82b6047ff --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go @@ -0,0 +1,121 @@ +package compute + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "os" + "testing" + + "time" + + "log" + + "github.com/hashicorp/go-oracle-terraform/opc" +) + +const ( + _ClientTestUser = "test-user" + _ClientTestDomain = "test-domain" +) + +func newAuthenticatingServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if os.Getenv("ORACLE_LOG") != "" { + log.Printf("[DEBUG] Received request: %s, %s\n", r.Method, r.URL) + } + + if r.URL.Path == "/authenticate/" { + http.SetCookie(w, &http.Cookie{Name: "testAuthCookie", Value: "cookie value"}) + // w.WriteHeader(200) + } else { + handler(w, r) + } + })) +} + +func getTestClient(c *opc.Config) (*Client, error) { + // Build up config with default values if omitted + if c.APIEndpoint == nil { + if os.Getenv("OPC_ENDPOINT") == "" { + panic("OPC_ENDPOINT not set in environment") + } + endpoint, err := url.Parse(os.Getenv("OPC_ENDPOINT")) + if err != nil { + return nil, err + } + c.APIEndpoint = endpoint + } + + if c.IdentityDomain == nil { + domain := os.Getenv("OPC_IDENTITY_DOMAIN") + c.IdentityDomain = &domain + } + + if c.Username == nil { + username := os.Getenv("OPC_USERNAME") + c.Username = &username + } + + if c.Password == nil { + password := os.Getenv("OPC_PASSWORD") + c.Password = &password + } + + if c.HTTPClient == nil { + c.HTTPClient = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSHandshakeTimeout: 120 * time.Second}, + } + } + + return NewComputeClient(c) +} + +func getBlankTestClient() (*Client, *httptest.Server, error) { + server := newAuthenticatingServer(func(w http.ResponseWriter, r *http.Request) { + }) + + endpoint, err := url.Parse(server.URL) + if err != nil { + server.Close() + return nil, nil, err + } + + client, err := getTestClient(&opc.Config{ + IdentityDomain: opc.String(_ClientTestDomain), + Username: opc.String(_ClientTestUser), + APIEndpoint: endpoint, + }) + if err != nil { + server.Close() + return nil, nil, err + } + return client, server, nil +} + +// Returns a stub client with default values, and a custom API Endpoint +func getStubClient(endpoint *url.URL) (*Client, error) { + domain := "test" + username := "test" + password := "test" + config := &opc.Config{ + IdentityDomain: &domain, + Username: &username, + Password: &password, + APIEndpoint: endpoint, + } + return getTestClient(config) +} + +func unmarshalRequestBody(t *testing.T, r *http.Request, target interface{}) { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + err := json.Unmarshal(buf.Bytes(), target) + if err != nil { + t.Fatalf("Error marshalling request: %s", err) + } +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go new file mode 100644 index 000000000..825bb8fe8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go @@ -0,0 +1,52 @@ +package compute + +type VirtNICsClient struct { + ResourceClient +} + +func (c *Client) VirtNICs() *VirtNICsClient { + return &VirtNICsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "Virtual NIC", + ContainerPath: "/network/v1/vnic/", + ResourceRootPath: "/network/v1/vnic", + }, + } +} + +type VirtualNIC struct { + // Description of the object. + Description string `json:"description"` + // MAC address of this VNIC. + MACAddress string `json:"macAddress"` + // The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC. + Name string `json:"name"` + // Tags associated with the object. + Tags []string `json:"tags"` + // True if the VNIC is of type "transit". + TransitFlag bool `json:"transitFlag"` + // Uniform Resource Identifier + Uri string `json:"uri"` +} + +// Can only GET a virtual NIC, not update, create, or delete +type GetVirtualNICInput struct { + // The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC. + // Required + Name string `json:"name"` +} + +func (c *VirtNICsClient) GetVirtualNIC(input *GetVirtualNICInput) (*VirtualNIC, error) { + var virtNIC VirtualNIC + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &virtNIC); err != nil { + return nil, err + } + return c.success(&virtNIC) +} + +func (c *VirtNICsClient) success(info *VirtualNIC) (*VirtualNIC, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go new file mode 100644 index 000000000..ef9035f26 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go @@ -0,0 +1,154 @@ +package compute + +type VirtNICSetsClient struct { + ResourceClient +} + +func (c *Client) VirtNICSets() *VirtNICSetsClient { + return &VirtNICSetsClient{ + ResourceClient: ResourceClient{ + Client: c, + ResourceDescription: "Virtual NIC Set", + ContainerPath: "/network/v1/vnicset/", + ResourceRootPath: "/network/v1/vnicset", + }, + } +} + +// Describes an existing virtual nic set +type VirtualNICSet struct { + // List of ACLs applied to the VNICs in the set. + AppliedACLs []string `json:"appliedAcls"` + // Description of the VNIC Set. + Description string `json:"description"` + // Name of the VNIC set. + Name string `json:"name"` + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + Tags []string `json:"tags"` + // Uniform Resource Identifier + Uri string `json:"uri"` + // List of VNICs associated with this VNIC set. + VirtualNICs []string `json:"vnics"` +} + +type CreateVirtualNICSetInput struct { + // List of ACLs applied to the VNICs in the set. + // Optional + AppliedACLs []string `json:"appliedAcls"` + // Description of the object. + // Optional + Description string `json:"description"` + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + // Object names can contain only alphanumeric, underscore (_), dash (-), and period (.) characters. Object names are case-sensitive. + // Required + Name string `json:"name"` + // Tags associated with this VNIC set. + // Optional + Tags []string `json:"tags"` + // List of VNICs associated with this VNIC set. + // Optional + VirtualNICs []string `json:"vnics"` +} + +func (c *VirtNICSetsClient) CreateVirtualNICSet(input *CreateVirtualNICSetInput) (*VirtualNICSet, error) { + input.Name = c.getQualifiedName(input.Name) + input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs) + qualifiedNics := c.getQualifiedList(input.VirtualNICs) + if len(qualifiedNics) != 0 { + input.VirtualNICs = qualifiedNics + } + + var virtNicSet VirtualNICSet + if err := c.createResource(input, &virtNicSet); err != nil { + return nil, err + } + + return c.success(&virtNicSet) +} + +type GetVirtualNICSetInput struct { + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + // Required + Name string `json:"name"` +} + +func (c *VirtNICSetsClient) GetVirtualNICSet(input *GetVirtualNICSetInput) (*VirtualNICSet, error) { + var virtNicSet VirtualNICSet + // Qualify Name + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &virtNicSet); err != nil { + return nil, err + } + + return c.success(&virtNicSet) +} + +type UpdateVirtualNICSetInput struct { + // List of ACLs applied to the VNICs in the set. + // Optional + AppliedACLs []string `json:"appliedAcls"` + // Description of the object. + // Optional + Description string `json:"description"` + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + // Object names can contain only alphanumeric, underscore (_), dash (-), and period (.) characters. Object names are case-sensitive. + // Required + Name string `json:"name"` + // Tags associated with this VNIC set. + // Optional + Tags []string `json:"tags"` + // List of VNICs associated with this VNIC set. + // Optional + VirtualNICs []string `json:"vnics"` +} + +func (c *VirtNICSetsClient) UpdateVirtualNICSet(input *UpdateVirtualNICSetInput) (*VirtualNICSet, error) { + input.Name = c.getQualifiedName(input.Name) + input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs) + // Qualify VirtualNICs + qualifiedVNICs := c.getQualifiedList(input.VirtualNICs) + if len(qualifiedVNICs) != 0 { + input.VirtualNICs = qualifiedVNICs + } + + var virtNICSet VirtualNICSet + if err := c.updateResource(input.Name, input, &virtNICSet); err != nil { + return nil, err + } + + return c.success(&virtNICSet) +} + +type DeleteVirtualNICSetInput struct { + // The name of the virtual NIC set. + // Required + Name string `json:"name"` +} + +func (c *VirtNICSetsClient) DeleteVirtualNICSet(input *DeleteVirtualNICSetInput) error { + input.Name = c.getQualifiedName(input.Name) + return c.deleteResource(input.Name) +} + +func (c *VirtNICSetsClient) getQualifiedAcls(acls []string) []string { + qualifiedAcls := []string{} + for _, acl := range acls { + qualifiedAcls = append(qualifiedAcls, c.getQualifiedName(acl)) + } + return qualifiedAcls +} + +func (c *VirtNICSetsClient) unqualifyAcls(acls []string) []string { + unqualifiedAcls := []string{} + for _, acl := range acls { + unqualifiedAcls = append(unqualifiedAcls, c.getUnqualifiedName(acl)) + } + return unqualifiedAcls +} + +func (c *VirtNICSetsClient) success(info *VirtualNICSet) (*VirtualNICSet, error) { + c.unqualify(&info.Name) + info.AppliedACLs = c.unqualifyAcls(info.AppliedACLs) + info.VirtualNICs = c.getUnqualifiedList(info.VirtualNICs) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/helper/testing.go b/vendor/github.com/hashicorp/go-oracle-terraform/helper/testing.go new file mode 100644 index 000000000..eccbdeeb2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/helper/testing.go @@ -0,0 +1,44 @@ +package helper + +import ( + "fmt" + "log" + "math/rand" + "os" + "time" + + "github.com/hashicorp/go-oracle-terraform/opc" +) + +const TestEnvVar = "ORACLE_ACC" + +// Test suite helpers + +type TestCase struct { + // Fields to test stuff with +} + +func Test(t TestT, c TestCase) { + if os.Getenv(TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' is set", TestEnvVar)) + return + } + + // Setup logging Output + logWriter, err := opc.LogOutput() + if err != nil { + t.Error(fmt.Sprintf("Error setting up log writer: %s", err)) + } + log.SetOutput(logWriter) +} + +type TestT interface { + Error(args ...interface{}) + Fatal(args ...interface{}) + Skip(args ...interface{}) +} + +func RInt() int { + rand.Seed(time.Now().UTC().UnixNano()) + return rand.Int() +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go new file mode 100644 index 000000000..0ac83ec88 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go @@ -0,0 +1,21 @@ +package opc + +import ( + "net/http" + "net/url" +) + +type Config struct { + Username *string + Password *string + IdentityDomain *string + APIEndpoint *url.URL + MaxRetries *int + LogLevel LogLevelType + Logger Logger + HTTPClient *http.Client +} + +func NewConfig() *Config { + return &Config{} +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go new file mode 100644 index 000000000..3fa365c1c --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go @@ -0,0 +1,5 @@ +package opc + +func String(v string) *string { + return &v +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go new file mode 100644 index 000000000..6b12c10d9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go @@ -0,0 +1,12 @@ +package opc + +import "fmt" + +type OracleError struct { + StatusCode int + Message string +} + +func (e OracleError) Error() string { + return fmt.Sprintf("%d: %s", e.StatusCode, e.Message) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go new file mode 100644 index 000000000..f9714a7a8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go @@ -0,0 +1,70 @@ +package opc + +import ( + "io" + "io/ioutil" + "log" + "os" +) + +const ( + LogOff LogLevelType = 0 + LogDebug LogLevelType = 1 +) + +type LogLevelType uint + +// Logger interface. Should be satisfied by Terraform's logger as well as the Default logger +type Logger interface { + Log(...interface{}) +} + +type LoggerFunc func(...interface{}) + +func (f LoggerFunc) Log(args ...interface{}) { + f(args...) +} + +// Returns a default logger if one isn't specified during configuration +func NewDefaultLogger() Logger { + logWriter, err := LogOutput() + if err != nil { + log.Fatalf("Error setting up log writer: %s", err) + } + return &defaultLogger{ + logger: log.New(logWriter, "", log.LstdFlags), + } +} + +// Default logger to satisfy the logger interface +type defaultLogger struct { + logger *log.Logger +} + +func (l defaultLogger) Log(args ...interface{}) { + l.logger.Println(args...) +} + +func LogOutput() (logOutput io.Writer, err error) { + // Default to nil + logOutput = ioutil.Discard + + logLevel := LogLevel() + if logLevel == LogOff { + return + } + + // Logging is on, set output to STDERR + logOutput = os.Stderr + return +} + +// Gets current Log Level from the ORACLE_LOG env var +func LogLevel() LogLevelType { + envLevel := os.Getenv("ORACLE_LOG") + if envLevel == "" { + return LogOff + } else { + return LogDebug + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 1d3e40282..df4eca514 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1969,6 +1969,24 @@ "path": "github.com/hashicorp/go-multierror", "revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5" }, + { + "checksumSHA1": "Sqz9+8frdOIkyK/v4IjjInZAp4Y=", + "path": "github.com/hashicorp/go-oracle-terraform/compute", + "revision": "98fdaf3c4bde245e21947487ba722c3d0abaccb2", + "revisionTime": "2017-03-29T21:19:34Z" + }, + { + "checksumSHA1": "DzK7lYwHt5Isq5Zf73cnQqBO2LI=", + "path": "github.com/hashicorp/go-oracle-terraform/helper", + "revision": "98fdaf3c4bde245e21947487ba722c3d0abaccb2", + "revisionTime": "2017-03-29T21:19:34Z" + }, + { + "checksumSHA1": "AyNRs19Es9pDw2VMxVKWuLx3Afg=", + "path": "github.com/hashicorp/go-oracle-terraform/opc", + "revision": "98fdaf3c4bde245e21947487ba722c3d0abaccb2", + "revisionTime": "2017-03-29T21:19:34Z" + }, { "checksumSHA1": "b0nQutPMJHeUmz4SjpreotAo6Yk=", "path": "github.com/hashicorp/go-plugin", diff --git a/website/source/assets/stylesheets/_docs.scss b/website/source/assets/stylesheets/_docs.scss index 9f2922c21..16c6ae042 100755 --- a/website/source/assets/stylesheets/_docs.scss +++ b/website/source/assets/stylesheets/_docs.scss @@ -50,7 +50,7 @@ body.layout-nomad, body.layout-ns1, body.layout-openstack, body.layout-opsgenie, -body.layout-oracleopc, +body.layout-opc, body.layout-packet, body.layout-pagerduty, body.layout-postgresql, diff --git a/website/source/docs/providers/opc/d/opc_compute_network_interface.html.markdown b/website/source/docs/providers/opc/d/opc_compute_network_interface.html.markdown new file mode 100644 index 000000000..b479baeba --- /dev/null +++ b/website/source/docs/providers/opc/d/opc_compute_network_interface.html.markdown @@ -0,0 +1,49 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_network_interface" +sidebar_current: "docs-opc-datasource-network-interface" +description: |- + Gets information about the configuration of an instance's network interface +--- + +# opc\_compute\_network\_interface + +Use this data source to access the configuration of an instance's network interface + +## Example Usage + +``` +data "opc_compute_network_interface" "foo" { + instance_id = "${opc_compute_instance.my_instance.id}" + instance_name = "${opc_compute_instance.my_instance.name}" + interface = "eth0" +} + +output "mac_address" { + value = "${data.opc_compute_network_interface.foo.mac_address}" +} + +output "vnic" { + value = "${data.opc_compute_network_interface.foo.vnic}" +} +``` + +## Argument Reference +* `instance_name` is the name of the instance. +* `instance_id` is the id of the instance. +* `interface` is the name of the attached interface. `eth0`, `eth1`, ... `eth9`. + +## Attributes Reference + +* `dns` - Array of DNS servers for the interface. +* `ip_address` - IP Address assigned to the interface. +* `ip_network` - The IP Network assigned to the interface. +* `mac_address` - The MAC address of the interface. +* `model` - The model of the NIC card used. +* `name_servers` - Array of name servers for the interface. +* `nat` - The IP Reservation (in IP Networks) associated with the interface. +* `search_domains` - The search domains that are sent through DHCP as option 119. +* `sec_lists` - The security lists the interface is added to. +* `shared_network` - Whether or not the interface is inside the Shared Network or an IP Network. +* `vnic` - The name of the vNIC created for the IP Network. +* `vnic_sets` - The array of vNIC Sets the interface was added to. diff --git a/website/source/docs/providers/oracleopc/d/opc_compute_vnic.html.markdown b/website/source/docs/providers/opc/d/opc_compute_vnic.html.markdown similarity index 85% rename from website/source/docs/providers/oracleopc/d/opc_compute_vnic.html.markdown rename to website/source/docs/providers/opc/d/opc_compute_vnic.html.markdown index 78be49c4a..8656c85c2 100644 --- a/website/source/docs/providers/oracleopc/d/opc_compute_vnic.html.markdown +++ b/website/source/docs/providers/opc/d/opc_compute_vnic.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_vnic" -sidebar_current: "docs-oracleopc-datasource-vnic" +sidebar_current: "docs-opc-datasource-vnic" description: |- Gets information about the configuration of a Virtual NIC. --- @@ -13,7 +13,9 @@ Use this data source to access the configuration of a Virtual NIC. ## Example Usage ``` -data "opc_compute_vnic" "current" {} +data "opc_compute_vnic" "current" { + name = "my_vnic_name" +} output "mac_address" { value = "${data.opc_compute_vnic.current.mac_address}" diff --git a/website/source/docs/providers/oracleopc/index.html.markdown b/website/source/docs/providers/opc/index.html.markdown similarity index 96% rename from website/source/docs/providers/oracleopc/index.html.markdown rename to website/source/docs/providers/opc/index.html.markdown index 598346919..1086f310a 100644 --- a/website/source/docs/providers/oracleopc/index.html.markdown +++ b/website/source/docs/providers/opc/index.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Provider: Oracle Public Cloud" -sidebar_current: "docs-oracleopc-index" +sidebar_current: "docs-opc-index" description: |- The Oracle Public Cloud provider is used to interact with the many resources supported by the Oracle Public Cloud. The provider needs to be configured with credentials for the Oracle Public Cloud API. --- @@ -16,7 +16,7 @@ Use the navigation to the left to read about the available resources. ``` # Configure the Oracle Public Cloud -provider "oracle" { +provider "opc" { user = "..." password = "..." identity_domain = "..." diff --git a/website/source/docs/providers/opc/r/opc_compute_acl.html.markdown b/website/source/docs/providers/opc/r/opc_compute_acl.html.markdown new file mode 100644 index 000000000..6f26c777b --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_acl.html.markdown @@ -0,0 +1,45 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_acl" +sidebar_current: "docs-opc-resource-acl" +description: |- + Creates and manages an ACL in an OPC identity domain. +--- + +# opc\_compute\_acl + +The ``opc_compute_acl`` resource creates and manages an ACL in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_acl" "default" { + name = "ACL1" + description = "This is a description for an acl" + tags = ["tag1", "tag2"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the ACL. + +* `enabled` - (Optional) Enables or disables the ACL. Set to true by default. + +* `description` - (Optional) A description of the ACL. + +* `tags` - (Optional) List of tags that may be applied to the ACL. + +In addition to the above, the following values are exported: + +* `uri` - The Uniform Resource Identifier for the ACL + +## Import + +ACL's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_acl.acl1 example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_image_list.html.markdown b/website/source/docs/providers/opc/r/opc_compute_image_list.html.markdown new file mode 100644 index 000000000..f5899520b --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_image_list.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_image_list" +sidebar_current: "docs-opc-resource-image-list" +description: |- + Creates and manages an Image List in an OPC identity domain. +--- + +# opc\_compute\_image\_list + +The ``opc_compute_image_list`` resource creates and manages an Image List in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_image_list" "test" { + name = "imagelist1" + description = "This is a description of the Image List" + default = 21 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Image List. + +* `description` - (Required) A description of the Image List. + +* `default` - (Required) The image list entry to be used, by default, when launching instances using this image list. Defaults to `1`. + +## Import + +Image List's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_image_list.imagelist1 example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_instance.html.markdown b/website/source/docs/providers/opc/r/opc_compute_instance.html.markdown new file mode 100644 index 000000000..56b6240c4 --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_instance.html.markdown @@ -0,0 +1,169 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_instance" +sidebar_current: "docs-opc-resource-instance" +description: |- + Creates and manages an instance in an OPC identity domain. +--- + +# opc\_compute\_instance + +The ``opc_compute_instance`` resource creates and manages an instance in an OPC identity domain. + +~> **Caution:** The ``opc_compute_instance`` resource can completely delete your +instance just as easily as it can create it. To avoid costly accidents, +consider setting +[``prevent_destroy``](/docs/configuration/resources.html#prevent_destroy) +on your instance resources as an extra safety measure. + +## Example Usage + +``` +resource "opc_compute_instance" "test_instance" { + name = "test" + label = "test" + shape = "oc3" + imageList = "/oracle/public/oel_6.4_2GB_v1" + sshKeys = ["${opc_compute_ssh_key.key1.name}"] + networking_info { + index = 0 + model = "e1000" + nat = ["ippool:/oracle/public/ippool"] + shared_network = true + } + networking_info { + index = 1 + ip_network = "${opc_compute_ip_network.foo.id}" + vnic = "testing-vnic-name" + shared_network = false + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the instance. + +* `shape` - (Required) The shape of the instance, e.g. `oc4`. + +* `instance_attributes` - (Optional) A JSON string of custom attributes. See [Attributes](#attributes) below for more information. + +* `boot_order` - (Optional) The index number of the bootable storage volume, presented as a list, that should be used to boot the instance. The only valid value is `[1]`. If you set this attribute, you must also specify a bootable storage volume with index number 1 in the volume sub-parameter of storage_attachments. When you specify boot_order, you don't need to specify the imagelist attribute, because the instance is booted using the image on the specified bootable storage volume. If you specify both boot_order and imagelist, the imagelist attribute is ignored. + +* `hostname` - (Optional) The host name assigned to the instance. On an Oracle Linux instance, this host name is displayed in response to the hostname command. Only relative DNS is supported. The domain name is suffixed to the host name that you specify. The host name must not end with a period. If you don't specify a host name, then a name is generated automatically. + +* `image_list` - (Optional) The imageList of the instance, e.g. `/oracle/public/oel_6.4_2GB_v1`. + +* `label` - (Optional) The label to apply to the instance. + +* `networking_info` - (Optional) Information pertaining to an individual network interface to be created and attached to the instance. See [Networking Info](#networking-info) below for more information. + +* `reverse_dns` - (Optional) If set to `true` (default), then reverse DNS records are created. If set to `false`, no reverse DNS records are created. + +* `ssh_keys` - (Optional) A list of the names of the SSH Keys that can be used to log into the instance. + +* `tags` - (Optional) A list of strings that should be supplied to the instance as tags. + +## Attributes + +During instance creation, there are several custom attributes that a user may wish to make available to the instance during instance creation. +These attributes can be specified via the `instance_attributes` field, and must be presented as a string in JSON format. +The easiest way to populate this field is with a HEREDOC: + +```hcl +resource "opc_compute_instance" "foo" { + name = "test" + label = "test" + shape = "oc3" + imageList = "/oracle/public/oel_6.4_2GB_v1" + instance_attributes = </@// +``` + +The instance can be imported as such: + +``` +terraform import opc_compute_instance.instance1 instance_name/instance_id +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_ip_address_prefix_set.html.markdown b/website/source/docs/providers/opc/r/opc_compute_ip_address_prefix_set.html.markdown new file mode 100644 index 000000000..3220644d5 --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_ip_address_prefix_set.html.markdown @@ -0,0 +1,45 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_ip_address_prefix_set" +sidebar_current: "docs-opc-resource-ip-address-prefix-set" +description: |- + Creates and manages an IP address prefix set in an OPC identity domain. +--- + +# opc\_compute\_ip\_address\_prefix\_set + +The ``opc_compute_ip_address_prefix_set`` resource creates and manages an IP address prefix set in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_ip_address_prefix_set" "default" { + name = "PrefixSet1" + prefixes = ["192.168.0.0/16", "172.120.0.0/24"] + tags = ["tags1", "tags2"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the ip address prefix set. + +* `prefixes` - (Optional) List of CIDR IPv4 prefixes assigned in the virtual network. + +* `description` - (Optional) A description of the ip address prefix set. + +* `tags` - (Optional) List of tags that may be applied to the ip address prefix set. + +In addition to the above, the following variables are exported: + +* `uri` - (Computed) The Uniform Resource Identifier of the ip address prefix set. + +## Import + +IP Address Prefix Set can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_ip_address_prefix_set.default example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_ip_address_reservation.html.markdown b/website/source/docs/providers/opc/r/opc_compute_ip_address_reservation.html.markdown new file mode 100644 index 000000000..00f0befd9 --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_ip_address_reservation.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_ip_address_reservation" +sidebar_current: "docs-opc-resource-ip-address-reservation" +description: |- + Creates and manages an IP address reservation in an OPC identity domain. +--- + +# opc\_compute\_ip\_address\_reservation + +The ``opc_compute_ip_address_reservation`` resource creates and manages an IP address reservation in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_ip_address_reservation" "default" { + name = "IPAddressReservation1" + ip_address_pool = "public-ippool" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the ip address reservation. + +* `ip_address_pool` - (Required) The IP address pool from which you want to reserve an IP address. + +* `description` - (Optional) A description of the ip address reservation. + +* `tags` - (Optional) List of tags that may be applied to the IP address reservation. + +In addition to the above, the following attributes are exported: + +* `ip_address` - Reserved NAT IPv4 address from the IP address pool. + +* `uri` - The Uniform Resource Identifier of the ip address reservation diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown b/website/source/docs/providers/opc/r/opc_compute_ip_association.html.markdown similarity index 75% rename from website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_ip_association.html.markdown index 2518b2df1..a148875ff 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_ip_association.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_ip_association.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_ip_association" -sidebar_current: "docs-oracleopc-resource-ip-association" +sidebar_current: "docs-opc-resource-ip-association" description: |- Creates and manages an IP association in an OPC identity domain. --- @@ -29,3 +29,18 @@ The following arguments are supported: * `parentpool` - (Required) The pool from which to take an IP address. To associate a specific reserved IP address, use the prefix `ipreservation:` followed by the name of the IP reservation. To allocate an IP address from a pool, use the prefix `ippool:`, e.g. `ippool:/oracle/public/ippool`. + + +## Attributes Reference + +The following attributes are exported: + +* `name` The name of the IP Association + +## Import + +IP Associations can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_ip_association.association1 example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_ip_network.html.markdown b/website/source/docs/providers/opc/r/opc_compute_ip_network.html.markdown new file mode 100644 index 000000000..6ce1977ba --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_ip_network.html.markdown @@ -0,0 +1,54 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_ip_network" +sidebar_current: "docs-opc-resource-ip-network" +description: |- + Creates and manages an IP Network +--- + +# opc\_compute\_ip_network + +The ``opc_compute_ip_network`` resource creates and manages an IP Network. + +## Example Usage + +``` +resource "opc_compute_ip_network" "foo" { + name = "my-ip-network" + description = "my IP Network" + ip_address_prefix = "10.0.1.0/24" + ip_network_exchange = "${opc_compute_ip_exchange.foo.name}" + public_napt_enabled = false + tags = ["tag1", "tag2"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the IP Network. + +* `ip_address_prefix` - (Required) The IPv4 address prefix, in CIDR format. + +* `description` - (Optional) The description of the IP Network. + +* `ip_network_exchange` - (Optional) Specify the IP Network exchange to which the IP Network belongs to. + +* `public_napt_enabled` - (Optional) If true, enable public internet access using NAPT for VNICs without any public IP Reservation. Defaults to `false`. + +## Attributes Reference + +The following attributes are exported: + +* `name` - The name of the IP Network + +* `ip_address_prefix` - The IPv4 address prefix, in CIDR format. + +* `description` - The description of the IP Network. + +* `ip_network_exchange` - The IP Network Exchange for the IP Network + +* `public_napt_enabled` - Whether public internet access using NAPT for VNICs without any public IP Reservation or not. + +* `uri` - Uniform Resource Identifier for the IP Network diff --git a/website/source/docs/providers/opc/r/opc_compute_ip_network_exchange.html.markdown b/website/source/docs/providers/opc/r/opc_compute_ip_network_exchange.html.markdown new file mode 100644 index 000000000..604d95c37 --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_ip_network_exchange.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_ip_network_exchange" +sidebar_current: "docs-opc-resource-ip-network-exchange" +description: |- + Creates and manages an IP network exchange in an OPC identity domain. +--- + +# opc\_compute\_ip\_network\_exchange + +The ``opc_compute_ip_network_exchange`` resource creates and manages an IP network exchange in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_ip_network_exchange" "default" { + name = "NetworkExchange1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the ip network exchange. + +* `description` - (Optional) A description of the ip network exchange. + +* `tags` - (Optional) List of tags that may be applied to the IP network exchange. + +## Import + +IP Network Exchange's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_ip_network_exchange.exchange1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown b/website/source/docs/providers/opc/r/opc_compute_ip_reservation.html.markdown similarity index 70% rename from website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_ip_reservation.html.markdown index 44b70cc0f..f937e03f9 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_ip_reservation.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_ip_reservation.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_ip_reservation" -sidebar_current: "docs-oracleopc-resource-ip-reservation" +sidebar_current: "docs-opc-resource-ip-reservation" description: |- Creates and manages an IP reservation in an OPC identity domain. --- @@ -14,7 +14,7 @@ The ``opc_compute_ip_reservation`` resource creates and manages an IP reservatio ``` resource "opc_compute_ip_reservation" "reservation1" { - parentpool = "/oracle/public/ippool" + parent_pool = "/oracle/public/ippool" permanent = true tags = [] } @@ -24,10 +24,18 @@ resource "opc_compute_ip_reservation" "reservation1" { The following arguments are supported: -* `parentpool` - (Required) The pool from which to allocate the IP address. +* `parent_pool` - (Required) The pool from which to allocate the IP address. * `permanent` - (Required) Whether the IP address remains reserved even when it is no longer associated with an instance (if true), or may be returned to the pool and replaced with a different IP address when an instance is restarted, or deleted and recreated (if false). * `tags` - (Optional) List of tags that may be applied to the IP reservation. + +## Import + +IP Reservations can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_ip_reservations.reservation1 example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_route.html.markdown b/website/source/docs/providers/opc/r/opc_compute_route.html.markdown new file mode 100644 index 000000000..6fb86c860 --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_route.html.markdown @@ -0,0 +1,60 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_route" +sidebar_current: "docs-opc-resource-route" +description: |- + Creates and manages a Route resource for an IP Network +--- + +# opc\_compute\_route + +The ``opc_compute_route`` resource creates and manages a route for an IP Network. + +## Example Usage + +``` +resource "opc_compute_route" "foo" { + name = "my-route" + description = "my IP Network route" + admin_distance = 1 + ip_address_prefix = "10.0.1.0/24" + next_hop_vnic_set = "${opc_compute_vnic_set.bar.name}" + tags = ["tag1", "tag2"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the route. + +* `description` - (Optional) The description of the route. + +* `admin_distance` - (Optional) The route's administrative distance. Defaults to `0`. + +* `ip_address_prefix` - (Required) The IPv4 address prefix, in CIDR format, of the external network from which to route traffic. + +* `next_hop_vnic_set` - (Required) Name of the virtual NIC set to route matching packets to. Routed flows are load-balanced among all the virtual NICs in the virtual NIC set. + +## Attributes Reference + +The following attributes are exported: + +* `name` The name of the route + +* `description` - The description of the route. + +* `admin_distance` - The route's administrative distance. Defaults to `0`. + +* `ip_address_prefix` - The IPv4 address prefix, in CIDR format, of the external network from which to route traffic. + +* `next_hop_vnic_set` - Name of the virtual NIC set to route matching packets to. Routed flows are load-balanced among all the virtual NICs in the virtual NIC set. + +## Import + +Route's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_route.route1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown b/website/source/docs/providers/opc/r/opc_compute_sec_rule.html.markdown similarity index 54% rename from website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_sec_rule.html.markdown index 6497b0265..df222ee49 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_rule.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_sec_rule.html.markdown @@ -1,27 +1,24 @@ --- -layout: "oracleopc" -page_title: "Oracle: opc_compute_security_rule" -sidebar_current: "docs-oracleopc-resource-security-rule" +layout: "opc" +page_title: "Oracle: opc_compute_sec_rule" +sidebar_current: "docs-opc-resource-sec-rule" description: |- - Creates and manages a security rule in an OPC identity domain. + Creates and manages a sec rule in an OPC identity domain. --- -# opc\_compute\_ip\_reservation +# opc\_compute\_sec\_rule -The ``opc_compute_security_rule`` resource creates and manages a security rule in an OPC identity domain, which joins -together a source security list (or security IP list), a destination security list (or security IP list), and a security -application. +The ``opc_compute_sec_rule`` resource creates and manages a sec rule in an OPC identity domain, which joinstogether a source security list (or security IP list), a destination security list (or security IP list), and a security application. ## Example Usage ``` -resource "opc_compute_security_rule" "test_rule" { +resource "opc_compute_sec_rule" "test_rule" { name = "test" source_list = "seclist:${opc_compute_security_list.sec-list1.name}" destination_list = "seciplist:${opc_compute_security_ip_list.sec-ip-list1.name}" action = "permit" application = "${opc_compute_security_application.spring-boot.name}" - disabled = false } ``` @@ -31,6 +28,8 @@ The following arguments are supported: * `name` - (Required) The unique (within the identity domain) name of the security rule. +* `description` - (Optional) A description for this security rule. + * `source_list` - (Required) The source security list (prefixed with `seclist:`), or security IP list (prefixed with `seciplist:`). @@ -42,5 +41,17 @@ The following arguments are supported: * `action` - (Required) Whether to `permit`, `refuse` or `deny` packets to which this rule applies. This will ordinarily be `permit`. -* `disabled` - (Required) Whether to disable this security rule. This is useful if you want to temporarily disable a rule -without removing it outright from your Terraform resource definition. +* `disabled` - (Optional) Whether to disable this security rule. This is useful if you want to temporarily disable a rule +without removing it outright from your Terraform resource definition. Defaults to `false`. + +In addition to the above, the following values are exported: + +* `uri` - The Uniform Resource Identifier of the sec rule. + +## Import + +Sec Rule's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_sec_rule.rule1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown b/website/source/docs/providers/opc/r/opc_compute_security_application.html.markdown similarity index 55% rename from website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_security_application.html.markdown index 94760f082..29e3af03d 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_application.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_security_application.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_security_application" -sidebar_current: "docs-oracleopc-resource-security-application" +sidebar_current: "docs-opc-resource-security-application" description: |- Creates and manages a security application in an OPC identity domain. --- @@ -10,13 +10,24 @@ description: |- The ``opc_compute_security_application`` resource creates and manages a security application in an OPC identity domain. -## Example Usage +## Example Usage (TCP) ``` resource "opc_compute_security_application" "tomcat" { - name = "tomcat" + name = "tomcat" protocol = "tcp" - dport = "8080" + dport = "8080" +} +``` + +## Example Usage (ICMP) + +``` +resource "opc_compute_security_application" "tomcat" { + name = "tomcat" + protocol = "icmp" + icmptype = "echo" + icmpcode = "protocol" } ``` @@ -26,14 +37,21 @@ The following arguments are supported: * `name` - (Required) The unique (within the identity domain) name of the application -* `protocol` - (Required) The protocol to enable for this application. Must be either one of -`tcp`, `udp`, `icmp`, `igmp`, `ipip`, `rdp`, `esp`, `ah`, `gre`, `icmpv6`, `ospf`, `pim`, `sctp`, `mplsip` or `all`, or -the corresponding integer in the range 0-254 from the list of [assigned protocol numbers](http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml) +* `protocol` - (Required) The protocol to enable for this application. Must be one of +`tcp`, `udp`, `ah`, `esp`, `icmp`, `icmpv6`, `igmp`, `ipip`, `gre`, `mplsip`, `ospf`, `pim`, `rdp`, `sctp` or `all`. -* `dport` - (Required) The port, or range of ports, to enable for this application, e.g `8080`, `6000-7000`. +* `dport` - (Required) The port, or range of ports, to enable for this application, e.g `8080`, `6000-7000`. This must be set if the `protocol` is set to `tcp` or `udp`. * `icmptype` - (Optional) The ICMP type to enable for this application, if the `protocol` is `icmp`. Must be one of `echo`, `reply`, `ttl`, `traceroute`, `unreachable`. * `icmpcode` - (Optional) The ICMP code to enable for this application, if the `protocol` is `icmp`. Must be one of -`network`, `host`, `protocol`, `port`, `df`, `admin`. +`admin`, `df`, `host`, `network`, `port` or `protocol`. + +## Import + +Security Application's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_security_application.application1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown b/website/source/docs/providers/opc/r/opc_compute_security_association.html.markdown similarity index 55% rename from website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_security_association.html.markdown index 49207c879..0cbe442ef 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_association.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_security_association.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_security_association" -sidebar_current: "docs-oracleopc-resource-security-association" +sidebar_current: "docs-opc-resource-security-association" description: |- Creates and manages a security association in an OPC identity domain. --- @@ -15,8 +15,9 @@ list in an OPC identity domain. ``` resource "opc_compute_security_association" "test_instance_sec_list_1" { - vcable = "${opc_compute_instance.test_instance.vcable}" - seclist = "${opc_compute_security_list.sec_list1.name}" + name = "association1" + vcable = "${opc_compute_instance.test_instance.vcable}" + seclist = "${opc_compute_security_list.sec_list1.name}" } ``` @@ -24,6 +25,16 @@ resource "opc_compute_security_association" "test_instance_sec_list_1" { The following arguments are supported: +* `name` - (Optional) The Name for the Security Association. If not specified, one is created automatically. Changing this forces a new resource to be created. + * `vcable` - (Required) The `vcable` of the instance to associate to the security list. * `seclist` - (Required) The name of the security list to associate the instance to. + +## Import + +Security Association's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_security_association.association1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown b/website/source/docs/providers/opc/r/opc_compute_security_ip_list.html.markdown similarity index 71% rename from website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_security_ip_list.html.markdown index 62f40d839..8873d4d49 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_ip_list.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_security_ip_list.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_security_ip_list" -sidebar_current: "docs-oracleopc-resource-security-ip-list" +sidebar_current: "docs-opc-resource-security-list" description: |- Creates and manages a security IP list in an OPC identity domain. --- @@ -26,3 +26,13 @@ The following arguments are supported: * `name` - (Required) The unique (within the identity domain) name of the security IP list. * `ip_entries` - (Required) The IP addresses to include in the list. + +* `description` - (Optional) The description of the security ip list. + +## Import + +IP List's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_ip_list.list1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown b/website/source/docs/providers/opc/r/opc_compute_security_list.html.markdown similarity index 68% rename from website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_security_list.html.markdown index 64547a41e..ea92fc8c3 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_security_list.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_security_list.html.markdown @@ -1,12 +1,12 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_security_list" -sidebar_current: "docs-oracleopc-resource-security-list" +sidebar_current: "docs-opc-resource-security-list" description: |- Creates and manages a security list in an OPC identity domain. --- -# opc\_compute\_security\_list +# opc\_compute\_ip\_reservation The ``opc_compute_security_list`` resource creates and manages a security list in an OPC identity domain. @@ -14,9 +14,9 @@ The ``opc_compute_security_list`` resource creates and manages a security list i ``` resource "opc_compute_security_list" "sec_list1" { - name = "sec-list-1" - policy = "permit" - outbound_cidr_policy = "deny" + name = "sec-list-1" + policy = "permit" + outbound_cidr_policy = "deny" } ``` @@ -29,5 +29,13 @@ The following arguments are supported: * `policy` - (Required) The policy to apply to instances associated with this list. Must be one of `permit`, `reject` (packets are dropped but a reply is sent) and `deny` (packets are dropped and no reply is sent). -* `output_cidr_policy` - (Required) The policy for outbound traffic from the security list.Must be one of `permit`, +* `output_cidr_policy` - (Required) The policy for outbound traffic from the security list. Must be one of `permit`, `reject` (packets are dropped but a reply is sent) and `deny` (packets are dropped and no reply is sent). + +## Import + +Security List's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_security_list.list1 example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_security_protocol.html.markdown b/website/source/docs/providers/opc/r/opc_compute_security_protocol.html.markdown new file mode 100644 index 000000000..612ab88b9 --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_security_protocol.html.markdown @@ -0,0 +1,65 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_security_protocol" +sidebar_current: "docs-opc-resource-security-protocol" +description: |- + Creates and manages an security protocol in an OPC identity domain. +--- + +# opc\_compute\_security\_protocol + +The ``opc_compute_security_protocol`` resource creates and manages a security protocol in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_security_protocol" "default" { + name = "security-protocol-1" + dst_ports = ["2045-2050"] + src_ports = ["3045-3060"] + ip_protocol = "tcp" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the security protocol. + +* `dst_ports` (Optional) Enter a list of port numbers or port range strings. + Traffic is enabled by a security rule when a packet's destination port matches the + ports specified here. + For TCP, SCTP, and UDP, each port is a destination transport port, between 0 and 65535, + inclusive. For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + If no destination ports are specified, all destination ports or ICMP types are allowed. + +* `src_ports` (Optional) Enter a list of port numbers or port range strings. + Traffic is enabled by a security rule when a packet's source port matches the + ports specified here. + For TCP, SCTP, and UDP, each port is a source transport port, + between 0 and 65535, inclusive. + For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + If no source ports are specified, all source ports or ICMP types are allowed. + +* `ip_protocol` (Optional) The protocol used in the data portion of the IP datagram. + Permitted values are: tcp, udp, icmp, igmp, ipip, rdp, esp, ah, gre, icmpv6, ospf, pim, sctp, + mplsip, all. + Traffic is enabled by a security rule when the protocol in the packet matches the + protocol specified here. If no protocol is specified, all protocols are allowed. + +* `description` - (Optional) A description of the security protocol. + +* `tags` - (Optional) List of tags that may be applied to the security protocol. + +In addition to the above, the following values are exported: + +* `uri` - The Uniform Resource Identifier for the Security Protocol + +## Import + +ACL's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_security_protocol.default example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_security_rule.html.markdown b/website/source/docs/providers/opc/r/opc_compute_security_rule.html.markdown new file mode 100644 index 000000000..c501517ad --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_security_rule.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_security_rule" +sidebar_current: "docs-opc-resource-security-rule" +description: |- + Creates and manages a security rule in an OPC identity domain. +--- + +# opc\_compute\_security\_rule + +The ``opc_compute_security_rule`` resource creates and manages a security rule in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_security_rule" "default" { + name = "SecurityRule1" + flow_direction = "ingress" + acl = "${opc_compute_acl.default.name}" + security_protocols = ["${opc_compute_security_protocol.default.name}"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the security rule. + +* `flow_direction` - (Required) Specify the direction of flow of traffic, which is relative to the instances, for this security rule. Allowed values are ingress or egress. + +* `disabled` - (Optional) Whether to disable this security rule. This is useful if you want to temporarily disable a rule without removing it outright from your Terraform resource definition. Defaults to `false`. + +* `acl` - (Optional) Name of the ACL that contains this security rule. + +* `dst_ip_address_prefixes` - (Optional) List of IP address prefix set names to match the packet's destination IP address. + +* `src_ip_address_prefixes` - (Optional) List of names of IP address prefix set to match the packet's source IP address. + +* `dst_vnic_set` - (Optional) Name of virtual NIC set containing the packet's destination virtual NIC. + +* `src_vnic_set` - (Optional) Name of virtual NIC set containing the packet's source virtual NIC. + +* `security_protocols` - (Optional) List of security protocol object names to match the packet's protocol and port. + +* `description` - (Optional) A description of the security rule. + +* `tags` - (Optional) List of tags that may be applied to the security rule. + +## Attributes Reference + +In addition to the above, the following attributes are exported: + +* `uri` - The Uniform Resource Identifier of the security rule. + +## Import + +Security Rule's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_security_rule.rule1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown b/website/source/docs/providers/opc/r/opc_compute_ssh_key.html.markdown similarity index 72% rename from website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown rename to website/source/docs/providers/opc/r/opc_compute_ssh_key.html.markdown index ff85467d8..fd1dcbd9d 100644 --- a/website/source/docs/providers/oracleopc/r/opc_compute_ssh_key.html.markdown +++ b/website/source/docs/providers/opc/r/opc_compute_ssh_key.html.markdown @@ -1,7 +1,7 @@ --- -layout: "oracleopc" +layout: "opc" page_title: "Oracle: opc_compute_ssh_key" -sidebar_current: "docs-oracleopc-resource-ssh-key" +sidebar_current: "docs-opc-resource-ssh-key" description: |- Creates and manages an SSH key in an OPC identity domain. --- @@ -28,5 +28,13 @@ The following arguments are supported: * `key` - (Required) The SSH key itself -* `enabled` - (Required) Whether or not the key is enabled. This is useful if you want to temporarily disable an SSH key, -without removing it entirely from your Terraform resource definition. +* `enabled` - (Optional) Whether or not the key is enabled. This is useful if you want to temporarily disable an SSH key, +without removing it entirely from your Terraform resource definition. Defaults to `true` + +## Import + +SSH Key's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_ssh_key.key1 example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_storage_volume.html.markdown b/website/source/docs/providers/opc/r/opc_compute_storage_volume.html.markdown new file mode 100644 index 000000000..103f48eea --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_storage_volume.html.markdown @@ -0,0 +1,78 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_storage_volume" +sidebar_current: "docs-opc-resource-storage-volume" +description: |- + Creates and manages a storage volume in an OPC identity domain. +--- + +# opc\_compute\_storage\_volume + +The ``opc_compute_storage_volume`` resource creates and manages a storage volume in an OPC identity domain. + +~> **Caution:** The ``opc_compute_storage_volume`` resource can completely delete your storage volume just as easily as it can create it. To avoid costly accidents, consider setting [``prevent_destroy``](/docs/configuration/resources.html#prevent_destroy) on your storage volume resources as an extra safety measure. + +## Example Usage + +``` +resource "opc_compute_storage_volume" "test" { + name = "storageVolume1" + description = "Description for the Storage Volume" + size = 10 + tags = ["bar", "foo"] +} +``` + +## Example Usage (Bootable Volume) +``` +resource "opc_compute_image_list" "test" { + name = "imageList1" + description = "Description for the Image List" +} + +resource "opc_compute_storage_volume" "test" { + name = "storageVolume1" + description = "Description for the Bootable Storage Volume" + size = 30 + tags = ["first", "second"] + bootable { + image_list = "${opc_compute_image_list.test.name}" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` (Required) The name for the Storage Account. +* `description` (Optional) The description of the storage volume. +* `size` (Required) The size of this storage volume in GB. The allowed range is from 1 GB to 2 TB (2048 GB). +* `storage_type` - (Optional) - The Type of Storage to provision. Possible values are `/oracle/public/storage/latency` or `/oracle/public/storage/default`. Defaults to `/oracle/public/storage/default`. +* `bootable` - (Optional) A `bootable` block as defined below. +* `tags` - (Optional) Comma-separated strings that tag the storage volume. + +`bootable` supports the following: +* `image_list` - (Optional) Defines an image list. +* `image_list_entry` - (Optional) Defines an image list entry. + +## Attributes Reference + +The following attributes are exported: + +* `hypervisor` - The hypervisor that this volume is compatible with. +* `machine_image` - Name of the Machine Image - available if the volume is a bootable storage volume. +* `managed` - Is this a Managed Volume? +* `platform` - The OS platform this volume is compatible with. +* `readonly` - Can this Volume be attached as readonly? +* `status` - The current state of the storage volume. +* `storage_pool` - The storage pool from which this volume is allocated. +* `uri` - Unique Resource Identifier of the Storage Volume. + +## Import + +Storage Volume's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_storage_volume.volume1 example +``` diff --git a/website/source/docs/providers/opc/r/opc_compute_vnic_set.html.markdown b/website/source/docs/providers/opc/r/opc_compute_vnic_set.html.markdown new file mode 100644 index 000000000..a0531b948 --- /dev/null +++ b/website/source/docs/providers/opc/r/opc_compute_vnic_set.html.markdown @@ -0,0 +1,45 @@ +--- +layout: "opc" +page_title: "Oracle: opc_compute_vnic_set" +sidebar_current: "docs-opc-resource-vnic-set" +description: |- + Creates and manages a virtual NIC set in an OPC identity domain +--- + +# opc\_compute\_vnic\_set + +The ``opc_compute_vnic_set`` resource creates and manages a virtual NIC set in an OPC identity domain. + +## Example Usage + +``` +resource "opc_compute_vnic_set" "test_set" { + name = "test_vnic_set" + description = "My vnic set" + applied_acls = ["acl1", "acl2"] + virtual_nics = ["nic1", "nic2", "nic3"] + tags = ["xyzzy", "quux"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The unique (within this identity domain) name of the virtual nic set. + +* `description` - (Optional) A description of the virtual nic set. + +* `applied_acls` - (Optional) A list of the ACLs to apply to the virtual nics in the set. + +* `virtual_nics` - (Optional) List of virtual NICs associated with this virtual NIC set. + +* `tags` - (Optional) A list of tags to apply to the storage volume. + +## Import + +VNIC Set's can be imported using the `resource name`, e.g. + +``` +terraform import opc_compute_vnic_set.set1 example +``` diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown deleted file mode 100644 index faeb3ee7c..000000000 --- a/website/source/docs/providers/oracleopc/r/opc_compute_instance.html.markdown +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: "oracleopc" -page_title: "Oracle: opc_compute_instance" -sidebar_current: "docs-oracleopc-resource-instance" -description: |- - Creates and manages an instance in an OPC identity domain. ---- - -# opc\_compute\_instance - -The ``opc_compute_instance`` resource creates and manages an instance in an OPC identity domain. - -~> **Caution:** The ``opc_compute_instance`` resource can completely delete your -instance just as easily as it can create it. To avoid costly accidents, -consider setting -[``prevent_destroy``](/docs/configuration/resources.html#prevent_destroy) -on your instance resources as an extra safety measure. - -## Example Usage - -``` -resource "opc_compute_instance" "test_instance" { - name = "test" - label = "test" - shape = "oc3" - imageList = "/oracle/public/oel_6.4_2GB_v1" - sshKeys = ["${opc_compute_ssh_key.key1.name}"] - attributes = "{\"foo\":\"bar\"}" - storage = [{ - index = 1 - volume = "${opc_compute_storage_volume.test_volume.name}" - }, - { - index = 2 - volume = "${opc_compute_storage_volume.test_volume2.name}" - }] -} -``` - -## Argument Reference - -The following arguments are supported: - -* `name` - (Required) The name of the instance. This need not be unique, as each instance is assigned a separate -computed `opcId`. - -* `shape` - (Required) The shape of the instance, e.g. `oc4`. - -* `imageList` - (Optional) The imageList of the instance, e.g. `/oracle/public/oel_6.4_2GB_v1` - -* `label` - (Optional) The label to apply to the instance. - -* `ip` - (Computed) The internal IP address assigned to the instance. - -* `opcId` - (Computed) The interned ID assigned to the instance. - -* `sshKeys` - (Optional) The names of the SSH Keys that can be used to log into the instance. - -* `attributes` - (Optional) An arbitrary JSON-formatted collection of attributes which is made available to the instance. - -* `vcable` - (Computed) The ID of the instance's VCable, which is used to associate it with reserved IP addresses and -add it to Security Lists. - -* `storage` - (Optional) A set of zero or more storage volumes to attach to the instance. Each volume has two arguments: -`index`, which is the volume's index in the instance's list of mounted volumes, and `name`, which is the name of the -storage volume to mount. - -* `bootOrder` - (Optional) The index number of the bootable storage volume that should be used to boot the instance. e.g. `[ 1 ]`. If you specify both `bootOrder` and `imageList`, the imagelist attribute is ignored. diff --git a/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown b/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown deleted file mode 100644 index 4b30b59ed..000000000 --- a/website/source/docs/providers/oracleopc/r/opc_compute_storage_volume.html.markdown +++ /dev/null @@ -1,49 +0,0 @@ ---- -layout: "oracleopc" -page_title: "Oracle: opc_compute_storage_volume" -sidebar_current: "docs-oracleopc-resource-storage-volume" -description: |- - Creates and manages a storage volume in an OPC identity domain. ---- - -# opc\_compute\_storage\_volume - -The ``opc_compute_storage_volume`` resource creates and manages a storage volume in an OPC identity domain. - -~> **Caution:** The ``opc_compute_storage_volume`` resource can completely delete your -storage volume just as easily as it can create it. To avoid costly accidents, -consider setting -[``prevent_destroy``](/docs/configuration/resources.html#prevent_destroy) -on your storage volume resources as an extra safety measure. - -## Example Usage - -``` -resource "opc_compute_storage_volume" "test_volume" { - size = "3g" - description = "My storage volume" - name = "test_volume_a" - tags = ["xyzzy", "quux"] -} -``` - -## Argument Reference - -The following arguments are supported: - -* `name` - (Required) The unique (within this identity domain) name of the storage volume. - -* `size` - (Required) The size of the storage instance. - -* `description` - (Optional) A description of the storage volume. - -* `tags` - (Optional) A list of tags to apply to the storage volume. - -* `bootableImage` - (Optional) The name of the bootable image the storage volume is loaded with. - -* `bootableImageVersion` - (Optional) The version of the bootable image specified in `bootableImage` to use. - -* `snapshot` - (Optional) The snapshot to initialise the storage volume with. This has two nested properties: `name`, -for the name of the snapshot to use, and `account` for the name of the snapshot account to use. - -* `snapshotId` - (Optional) The id of the snapshot to initialise the storage volume with. diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 77d5bf2c3..a0cd152d8 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -355,8 +355,8 @@ OpsGenie - > - Oracle OPC + > + Oracle OPC > diff --git a/website/source/layouts/opc.erb b/website/source/layouts/opc.erb new file mode 100644 index 000000000..8c9bfd793 --- /dev/null +++ b/website/source/layouts/opc.erb @@ -0,0 +1,94 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + +<% end %> +<%= yield %> +<% end %> diff --git a/website/source/layouts/oracleopc.erb b/website/source/layouts/oracleopc.erb deleted file mode 100644 index a9d9579f8..000000000 --- a/website/source/layouts/oracleopc.erb +++ /dev/null @@ -1,59 +0,0 @@ -<% wrap_layout :inner do %> -<% content_for :sidebar do %> - -<% end %> - -<%= yield %> -<% end %>