From 3e34ddbf385d2dd4e8b216a9bd1647de9d62699e Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 14 May 2016 13:54:13 -0700 Subject: [PATCH 01/12] New "random" provider, representing randomness This provider will have logical resources that allow Terraform to "manage" randomness as a resource, producing random numbers on create and then retaining the outcome in the state so that it will remain consistent until something explicitly triggers generating new values. Managing randomness in this way allows configurations to do things like random distributions and ids without causing "perma-diffs". --- builtin/bins/provider-random/main.go | 15 +++++++++++ builtin/bins/provider-random/main_test.go | 1 + builtin/providers/random/provider.go | 31 +++++++++++++++++++++++ builtin/providers/random/provider_test.go | 31 +++++++++++++++++++++++ command/internal_plugin_list.go | 2 ++ 5 files changed, 80 insertions(+) create mode 100644 builtin/bins/provider-random/main.go create mode 100644 builtin/bins/provider-random/main_test.go create mode 100644 builtin/providers/random/provider.go create mode 100644 builtin/providers/random/provider_test.go diff --git a/builtin/bins/provider-random/main.go b/builtin/bins/provider-random/main.go new file mode 100644 index 000000000..f83de47dc --- /dev/null +++ b/builtin/bins/provider-random/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/hashicorp/terraform/builtin/providers/random" + "github.com/hashicorp/terraform/plugin" + "github.com/hashicorp/terraform/terraform" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: func() terraform.ResourceProvider { + return random.Provider() + }, + }) +} diff --git a/builtin/bins/provider-random/main_test.go b/builtin/bins/provider-random/main_test.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/builtin/bins/provider-random/main_test.go @@ -0,0 +1 @@ +package main diff --git a/builtin/providers/random/provider.go b/builtin/providers/random/provider.go new file mode 100644 index 000000000..999fe19ef --- /dev/null +++ b/builtin/providers/random/provider.go @@ -0,0 +1,31 @@ +package random + +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{}, + + ResourcesMap: map[string]*schema.Resource{ + //"random_id": resourceId(), + //"random_shuffle": resourceShuffle(), + }, + } +} + +// stubRead is a do-nothing Read implementation used for our resources, +// which don't actually need to do anything on read. +func stubRead(d *schema.ResourceData, meta interface{}) error { + return nil +} + +// stubDelete is a do-nothing Dete implementation used for our resources, +// which don't actually need to do anything unusual on delete. +func stubDelete(d *schema.ResourceData, meta interface{}) error { + d.SetId("") + return nil +} diff --git a/builtin/providers/random/provider_test.go b/builtin/providers/random/provider_test.go new file mode 100644 index 000000000..92d16c509 --- /dev/null +++ b/builtin/providers/random/provider_test.go @@ -0,0 +1,31 @@ +package random + +import ( + "testing" + + "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{ + "random": 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) { +} diff --git a/command/internal_plugin_list.go b/command/internal_plugin_list.go index bb565fcf9..bfef69b8d 100644 --- a/command/internal_plugin_list.go +++ b/command/internal_plugin_list.go @@ -35,6 +35,7 @@ import ( packetprovider "github.com/hashicorp/terraform/builtin/providers/packet" postgresqlprovider "github.com/hashicorp/terraform/builtin/providers/postgresql" powerdnsprovider "github.com/hashicorp/terraform/builtin/providers/powerdns" + randomprovider "github.com/hashicorp/terraform/builtin/providers/random" rundeckprovider "github.com/hashicorp/terraform/builtin/providers/rundeck" softlayerprovider "github.com/hashicorp/terraform/builtin/providers/softlayer" statuscakeprovider "github.com/hashicorp/terraform/builtin/providers/statuscake" @@ -85,6 +86,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{ "packet": packetprovider.Provider, "postgresql": postgresqlprovider.Provider, "powerdns": powerdnsprovider.Provider, + "random": randomprovider.Provider, "rundeck": rundeckprovider.Provider, "softlayer": softlayerprovider.Provider, "statuscake": statuscakeprovider.Provider, From b1de477691fa92c53dfaaadd0787a40cfa1ae142 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 14 May 2016 14:00:58 -0700 Subject: [PATCH 02/12] provider/random: random_id resource This resource generates a cryptographically-strong set of bytes and provides them as base64, hexadecimal and decimal string representations. It is intended to be used for generating unique ids for resources elsewhere in the configuration, and thus the "keepers" would be set to any ForceNew attributes of the target resources, so that a new id is generated each time a new resource is generated. --- builtin/providers/random/provider.go | 2 +- builtin/providers/random/resource_id.go | 76 ++++++++++++++++++++ builtin/providers/random/resource_id_test.go | 58 +++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 builtin/providers/random/resource_id.go create mode 100644 builtin/providers/random/resource_id_test.go diff --git a/builtin/providers/random/provider.go b/builtin/providers/random/provider.go index 999fe19ef..271482381 100644 --- a/builtin/providers/random/provider.go +++ b/builtin/providers/random/provider.go @@ -11,7 +11,7 @@ func Provider() terraform.ResourceProvider { Schema: map[string]*schema.Schema{}, ResourcesMap: map[string]*schema.Resource{ - //"random_id": resourceId(), + "random_id": resourceId(), //"random_shuffle": resourceShuffle(), }, } diff --git a/builtin/providers/random/resource_id.go b/builtin/providers/random/resource_id.go new file mode 100644 index 000000000..9bb36b9ef --- /dev/null +++ b/builtin/providers/random/resource_id.go @@ -0,0 +1,76 @@ +package random + +import ( + "crypto/rand" + "encoding/base64" + "encoding/hex" + "fmt" + "math/big" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceId() *schema.Resource { + return &schema.Resource{ + Create: CreateID, + Read: stubRead, + Delete: stubDelete, + + Schema: map[string]*schema.Schema{ + "keepers": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, + + "byte_length": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "b64": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "hex": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "dec": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func CreateID(d *schema.ResourceData, meta interface{}) error { + + byteLength := d.Get("byte_length").(int) + bytes := make([]byte, byteLength) + + n, err := rand.Reader.Read(bytes) + if n != byteLength { + return fmt.Errorf("generated insufficient random bytes") + } + if err != nil { + return fmt.Errorf("error generating random bytes: %s", err) + } + + b64Str := base64.RawURLEncoding.EncodeToString(bytes) + hexStr := hex.EncodeToString(bytes) + + int := big.Int{} + int.SetBytes(bytes) + decStr := int.String() + + d.SetId(b64Str) + d.Set("b64", b64Str) + d.Set("hex", hexStr) + d.Set("dec", decStr) + + return nil +} diff --git a/builtin/providers/random/resource_id_test.go b/builtin/providers/random/resource_id_test.go new file mode 100644 index 000000000..ed6b8af8d --- /dev/null +++ b/builtin/providers/random/resource_id_test.go @@ -0,0 +1,58 @@ +package random + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccResourceID(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccResourceIDConfig, + Check: resource.ComposeTestCheckFunc( + testAccResourceIDCheck("random_id.foo"), + ), + }, + }, + }) +} + +func testAccResourceIDCheck(id string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[id] + if !ok { + return fmt.Errorf("Not found: %s", id) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + b64Str := rs.Primary.Attributes["b64"] + hexStr := rs.Primary.Attributes["hex"] + decStr := rs.Primary.Attributes["dec"] + + if got, want := len(b64Str), 6; got != want { + return fmt.Errorf("base64 string length is %d; want %d", got, want) + } + if got, want := len(hexStr), 8; got != want { + return fmt.Errorf("hex string length is %d; want %d", got, want) + } + if len(decStr) < 1 { + return fmt.Errorf("decimal string is empty; want at least one digit") + } + + return nil + } +} + +const testAccResourceIDConfig = ` +resource "random_id" "foo" { + byte_length = 4 +} +` From eec6c88fd2ae6850161cc2f892504c28597c4f52 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 14 May 2016 16:48:45 -0700 Subject: [PATCH 03/12] provider/random: random_shuffle resource random_shuffle takes a list of strings and returns a new list with the same items in a random permutation. Optionally allows the result list to be a different length than the input list. A shorter result than input results in some items being excluded. A longer result than input results in some items being repeated, but never more often than the number of input items. --- builtin/providers/random/provider.go | 2 +- builtin/providers/random/resource_shuffle.go | 82 +++++++++++++++++ .../providers/random/resource_shuffle_test.go | 91 +++++++++++++++++++ builtin/providers/random/seed.go | 24 +++++ 4 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 builtin/providers/random/resource_shuffle.go create mode 100644 builtin/providers/random/resource_shuffle_test.go create mode 100644 builtin/providers/random/seed.go diff --git a/builtin/providers/random/provider.go b/builtin/providers/random/provider.go index 271482381..309c50d6c 100644 --- a/builtin/providers/random/provider.go +++ b/builtin/providers/random/provider.go @@ -12,7 +12,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "random_id": resourceId(), - //"random_shuffle": resourceShuffle(), + "random_shuffle": resourceShuffle(), }, } } diff --git a/builtin/providers/random/resource_shuffle.go b/builtin/providers/random/resource_shuffle.go new file mode 100644 index 000000000..31108aeee --- /dev/null +++ b/builtin/providers/random/resource_shuffle.go @@ -0,0 +1,82 @@ +package random + +import ( + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceShuffle() *schema.Resource { + return &schema.Resource{ + Create: CreateShuffle, + Read: stubRead, + Delete: stubDelete, + + Schema: map[string]*schema.Schema{ + "keepers": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, + + "seed": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "input": &schema.Schema{ + Type: schema.TypeList, + Required: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "result": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "result_count": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func CreateShuffle(d *schema.ResourceData, meta interface{}) error { + input := d.Get("input").([]interface{}) + seed := d.Get("seed").(string) + + resultCount := d.Get("result_count").(int) + if resultCount == 0 { + resultCount = len(input) + } + result := make([]interface{}, 0, resultCount) + + rand := NewRand(seed) + + // Keep producing permutations until we fill our result +Batches: + for { + perm := rand.Perm(len(input)) + + for _, i := range perm { + result = append(result, input[i]) + + if len(result) >= resultCount { + break Batches + } + } + } + + d.SetId("-") + d.Set("result", result) + + return nil +} diff --git a/builtin/providers/random/resource_shuffle_test.go b/builtin/providers/random/resource_shuffle_test.go new file mode 100644 index 000000000..5770e4105 --- /dev/null +++ b/builtin/providers/random/resource_shuffle_test.go @@ -0,0 +1,91 @@ +package random + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccResourceShuffle(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccResourceShuffleConfig, + Check: resource.ComposeTestCheckFunc( + // These results are current as of Go 1.6. The Go + // "rand" package does not guarantee that the random + // number generator will generate the same results + // forever, but the maintainers endeavor not to change + // it gratuitously. + // These tests allow us to detect such changes and + // document them when they arise, but the docs for this + // resource specifically warn that results are not + // guaranteed consistent across Terraform releases. + testAccResourceShuffleCheck( + "random_shuffle.default_length", + []string{"a", "c", "b", "e", "d"}, + ), + testAccResourceShuffleCheck( + "random_shuffle.shorter_length", + []string{"a", "c", "b"}, + ), + testAccResourceShuffleCheck( + "random_shuffle.longer_length", + []string{"a", "c", "b", "e", "d", "a", "e", "d", "c", "b", "a", "b"}, + ), + ), + }, + }, + }) +} + +func testAccResourceShuffleCheck(id string, wants []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[id] + if !ok { + return fmt.Errorf("Not found: %s", id) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + attrs := rs.Primary.Attributes + + gotLen := attrs["result.#"] + wantLen := strconv.Itoa(len(wants)) + if gotLen != wantLen { + return fmt.Errorf("got %s result items; want %s", gotLen, wantLen) + } + + for i, want := range wants { + key := fmt.Sprintf("result.%d", i) + if got := attrs[key]; got != want { + return fmt.Errorf("index %d is %q; want %q", i, got, want) + } + } + + return nil + } +} + +const testAccResourceShuffleConfig = ` +resource "random_shuffle" "default_length" { + input = ["a", "b", "c", "d", "e"] + seed = "-" +} +resource "random_shuffle" "shorter_length" { + input = ["a", "b", "c", "d", "e"] + seed = "-" + result_count = 3 +} +resource "random_shuffle" "longer_length" { + input = ["a", "b", "c", "d", "e"] + seed = "-" + result_count = 12 +} +` diff --git a/builtin/providers/random/seed.go b/builtin/providers/random/seed.go new file mode 100644 index 000000000..7d16322fd --- /dev/null +++ b/builtin/providers/random/seed.go @@ -0,0 +1,24 @@ +package random + +import ( + "hash/crc64" + "math/rand" + "time" +) + +// NewRand returns a seeded random number generator, using a seed derived +// from the provided string. +// +// If the seed string is empty, the current time is used as a seed. +func NewRand(seed string) *rand.Rand { + var seedInt int64 + if seed != "" { + crcTable := crc64.MakeTable(crc64.ISO) + seedInt = int64(crc64.Checksum([]byte(seed), crcTable)) + } else { + seedInt = time.Now().Unix() + } + + randSource := rand.NewSource(seedInt) + return rand.New(randSource) +} From feafc94dde8ce679aba6741d2e3ef0709da52aba Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 14 May 2016 16:49:10 -0700 Subject: [PATCH 04/12] website: docs for the "random" provider --- website/source/assets/stylesheets/_docs.scss | 1 + .../docs/providers/random/index.html.markdown | 73 +++++++++++++++++++ .../source/docs/providers/random/r/id.html.md | 69 ++++++++++++++++++ .../docs/providers/random/r/shuffle.html.md | 59 +++++++++++++++ website/source/layouts/docs.erb | 4 + website/source/layouts/random.erb | 29 ++++++++ 6 files changed, 235 insertions(+) create mode 100644 website/source/docs/providers/random/index.html.markdown create mode 100644 website/source/docs/providers/random/r/id.html.md create mode 100644 website/source/docs/providers/random/r/shuffle.html.md create mode 100644 website/source/layouts/random.erb diff --git a/website/source/assets/stylesheets/_docs.scss b/website/source/assets/stylesheets/_docs.scss index 9c7afec52..733206d80 100755 --- a/website/source/assets/stylesheets/_docs.scss +++ b/website/source/assets/stylesheets/_docs.scss @@ -35,6 +35,7 @@ body.layout-openstack, body.layout-packet, body.layout-postgresql, body.layout-powerdns, +body.layout-random, body.layout-rundeck, body.layout-statuscake, body.layout-softlayer, diff --git a/website/source/docs/providers/random/index.html.markdown b/website/source/docs/providers/random/index.html.markdown new file mode 100644 index 000000000..32bf038d8 --- /dev/null +++ b/website/source/docs/providers/random/index.html.markdown @@ -0,0 +1,73 @@ +--- +layout: "random" +page_title: "Provider: Random" +sidebar_current: "docs-random-index" +description: |- + The Random provider is used to generate randomness. +--- + +# Random Provider + +The "random" provider allows the use of randomness within Terraform +configurations. This is a *logical provider*, which means that it works +entirely within Terraform's logic, and doesn't interact with any other +services. + +Unconstrained randomness within a Terraform configuration would not be very +useful, since Terraform's goal is to converge on a fixed configuration by +applying a diff. Because of this, the "random" provider provides an idea of +*managed randomness*: it provides resources that generate random values during +their creation and then hold those values steady until the inputs are changed. + +Even with these resources, it is advisable to keep the use of randomness within +Terraform configuration to a minimum, and retain it for special cases only; +Terraform works best when the configuration is well-defined, since its behavior +can then be more readily predicted. + +Unless otherwise stated within the documentation of a specific resource, this +provider's results are **not** sufficiently random for cryptographic use. + +For more information on the specific resources available, see the links in the +navigation bar. Read on for information on the general patterns that apply +to this provider's resources. + +## Resource "Keepers" + +As noted above, the random resources generate randomness only when they are +created; the results produced are stored in the Terraform state and re-used +until the inputs change, prompting the resource to be recreated. + +The resources all provide a map argument called `keepers` that can be populated +with arbitrary key/value pairs that should be selected such that they remain +the same until new random values are desired. + +For example: + +``` +resource "random_id" "server" { + keepers = { + # Generate a new id each time we switch to a new AMI id + ami_id = "${var.ami_id}" + } + + byte_length = 8 +} + +resource "aws_instance" "server" { + tags = { + Name = "web-server ${random_id.server.hex}" + } + + # Read the AMI id "through" the random_id resource to ensure that + # both will change together. + ami = "${random_id.server.keepers.ami_id}" + + # ... (other aws_instance arguments) ... +} +``` + +Resource "keepers" are optional. The other arguments to each resource must +*also* remain constant in order to retain a random result. + +To force a random result to be replaced, the `taint` command can be used to +produce a new result on the next run. diff --git a/website/source/docs/providers/random/r/id.html.md b/website/source/docs/providers/random/r/id.html.md new file mode 100644 index 000000000..2fdcdd4a4 --- /dev/null +++ b/website/source/docs/providers/random/r/id.html.md @@ -0,0 +1,69 @@ +--- +layout: "random" +page_title: "Random: random_id" +sidebar_current: "docs-random-resource-id" +description: |- + Generates a random identifier. +--- + +# random\_id + +The resource `random_id` generates random numbers that are intended to be +used as unique identifiers for other resources. + +Unlike other resources in the "random" provider, this resource *does* use a +cryptographic random number generator in order to minimize the chance of +collisions, making the results of this resource when a 32-byte identifier +is requested of equivalent uniqueness to a type-4 UUID. + +This resource can be used in conjunction with resources that have, +the `create_before_destroy` lifecycle flag set, to avoid conflicts with +unique names during the brief period where both the old and new resources +exist concurrently. + +## Example Usage + +The following example shows how to generate a unique name for an AWS EC2 +instance that changes each time a new AMI id is selected. + +``` +resource "random_id" "server" { + keepers = { + # Generate a new id each time we switch to a new AMI id + ami_id = "${var.ami_id}" + } + + byte_length = 8 +} + +resource "aws_instance" "server" { + tags = { + Name = "web-server ${random_id.server.hex}" + } + + # Read the AMI id "through" the random_id resource to ensure that + # both will change together. + ami = "${random_id.server.keepers.ami_id}" + + # ... (other aws_instance arguments) ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `byte_length` - (Required) The number of random bytes to produce. The + minimum value is 1, which produces eight bits of randomness. + +* `keepers` - (Optional) Arbitrary map of values that, when changed, will + trigger a new id to be generated. See + [the main provider documentation](../index.html) for more information. + +## Attributes Reference + +The following attributes are exported: + +* `b64` - The generated id presented in base64, using the URL-friendly character set: case-sensitive letters, digits and the characters `_` and `-`. +* `hex` - The generated id presented in padded hexadecimal digits. This result will always be twice as long as the requested byte length. +* `decimal` - The generated id presented in non-padded decimal digits. diff --git a/website/source/docs/providers/random/r/shuffle.html.md b/website/source/docs/providers/random/r/shuffle.html.md new file mode 100644 index 000000000..bfae65bca --- /dev/null +++ b/website/source/docs/providers/random/r/shuffle.html.md @@ -0,0 +1,59 @@ +--- +layout: "random" +page_title: "Random: random_shuffle" +sidebar_current: "docs-random-resource-shuffle" +description: |- + Produces a random permutation of a given list. +--- + +# random\_shuffle + +The resource `random_shuffle` generates a random permutation of a list +of strings given as an argument. + +## Example Usage + +``` +resource "random_shuffle" "az" { + input = ["us-west-1a", "us-west-1c", "us-west-1d", "us-west-1e"] + result_count = 2 +} + +resource "aws_elb" "example" { + # Place the ELB in any two of the given availability zones, selected + # at random. + availability_zones = ["${random_shuffle.az.result}"] + + # ... and other aws_elb arguments ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `input` - (Required) The list of strings to shuffle. + +* `result_count` - (Optional) The number of results to return. Defaults to + the number of items in the `input` list. If fewer items are requested, + some elements will be excluded from the result. If more items are requested, + items will be repeated in the result but not more frequently than the number + of items in the input list. + +* `keepers` - (Optional) Arbitrary map of values that, when changed, will + trigger a new id to be generated. See + [the main provider documentation](../index.html) for more information. + +* `seed` - (Optional) Arbitrary string with which to seed the random number + generator, in order to produce less-volatile permutations of the list. + **Important:** Even with an identical seed, it is not guaranteed that the + same permutation will be produced across different versions of Terraform. + This argument causes the result to be *less volatile*, but not fixed for + all time. + +## Attributes Reference + +The following attributes are exported: + +* `result` - Random permutation of the list of strings given in `input`. + diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index bb94f405b..e73072791 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -249,6 +249,10 @@ PowerDNS + > + Random + + > Rundeck diff --git a/website/source/layouts/random.erb b/website/source/layouts/random.erb new file mode 100644 index 000000000..224559c85 --- /dev/null +++ b/website/source/layouts/random.erb @@ -0,0 +1,29 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> +<% end %> From 803522a7ce61dc279c89b44e3124b298f86aeb69 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 24 May 2016 03:43:50 +0000 Subject: [PATCH 05/12] provider/openstack: Rebuild On Network Changes This commit makes it so that openstack_compute_instance_v2 resources are recreated when any network setting (except Floating IPs) is changed. --- .../resource_openstack_compute_instance_v2.go | 5 ++ ...urce_openstack_compute_instance_v2_test.go | 58 ++++++++++++++++++- .../r/compute_instance_v2.html.markdown | 11 ++-- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 7d4bafaff..5fb2be254 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -114,26 +114,31 @@ func resourceComputeInstanceV2() *schema.Resource { "uuid": &schema.Schema{ Type: schema.TypeString, Optional: true, + ForceNew: true, Computed: true, }, "name": &schema.Schema{ Type: schema.TypeString, Optional: true, + ForceNew: true, Computed: true, }, "port": &schema.Schema{ Type: schema.TypeString, Optional: true, + ForceNew: true, Computed: true, }, "fixed_ip_v4": &schema.Schema{ Type: schema.TypeString, Optional: true, + ForceNew: true, Computed: true, }, "fixed_ip_v6": &schema.Schema{ Type: schema.TypeString, Optional: true, + ForceNew: true, Computed: true, }, "floating_ip": &schema.Schema{ diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go index b87e80757..0ba4817e5 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -557,6 +557,53 @@ func TestAccComputeV2Instance_accessIPv4(t *testing.T) { }) } +func TestAccComputeV2Instance_ChangeFixedIP(t *testing.T) { + var instance1_1 servers.Server + var instance1_2 servers.Server + var testAccComputeV2Instance_ChangeFixedIP_1 = fmt.Sprintf(` + resource "openstack_compute_instance_v2" "instance_1" { + name = "instance_1" + security_groups = ["default"] + network { + uuid = "%s" + fixed_ip_v4 = "10.0.0.24" + } + }`, + os.Getenv("OS_NETWORK_ID")) + + var testAccComputeV2Instance_ChangeFixedIP_2 = fmt.Sprintf(` + resource "openstack_compute_instance_v2" "instance_1" { + name = "instance_1" + security_groups = ["default"] + network { + uuid = "%s" + fixed_ip_v4 = "10.0.0.25" + } + }`, + os.Getenv("OS_NETWORK_ID")) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2InstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2Instance_ChangeFixedIP_1, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.instance_1", &instance1_1), + ), + }, + resource.TestStep{ + Config: testAccComputeV2Instance_ChangeFixedIP_2, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.instance_1", &instance1_2), + testAccCheckComputeV2InstanceInstanceIDsDoNotMatch(&instance1_1, &instance1_2), + ), + }, + }, + }) +} + func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) computeClient, err := config.computeV2Client(OS_REGION_NAME) @@ -726,6 +773,15 @@ func testAccCheckComputeV2InstanceFloatingIPAttach( } return fmt.Errorf("Floating IP %s was not attached to instance %s", fip.ID, instance.ID) - + } +} +func testAccCheckComputeV2InstanceInstanceIDsDoNotMatch( + instance1, instance2 *servers.Server) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance1.ID == instance2.ID { + return fmt.Errorf("Instance was not recreated.") + } + + return nil } } diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 8baf17a54..52e545f5f 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -267,16 +267,19 @@ The following arguments are supported: The `network` block supports: * `uuid` - (Required unless `port` or `name` is provided) The network UUID to - attach to the server. + attach to the server. Changing this creates a new server. * `name` - (Required unless `uuid` or `port` is provided) The human-readable - name of the network. + name of the network. Changing this creates a new server. * `port` - (Required unless `uuid` or `name` is provided) The port UUID of a - network to attach to the server. + network to attach to the server. Changing this creates a new server. * `fixed_ip_v4` - (Optional) Specifies a fixed IPv4 address to be used on this - network. + network. Changing this creates a new server. + +* `fixed_ip_v6` - (Optional) Specifies a fixed IPv6 address to be used on this + network. Changing this creates a new server. * `floating_ip` - (Optional) Specifies a floating IP address to be associated with this network. Cannot be combined with a top-level floating IP. See From 00be33eb40a3605fe6d9f4dda02646094750a501 Mon Sep 17 00:00:00 2001 From: Calle Pettersson Date: Mon, 23 May 2016 20:56:07 +0200 Subject: [PATCH 06/12] Implement fixed_ip on floating ip allocations --- builtin/providers/openstack/provider_test.go | 5 ++ ...urce_openstack_networking_floatingip_v2.go | 7 ++ ...openstack_networking_floatingip_v2_test.go | 71 +++++++++++++++++++ .../r/networking_floatingip_v2.html.markdown | 4 ++ 4 files changed, 87 insertions(+) diff --git a/builtin/providers/openstack/provider_test.go b/builtin/providers/openstack/provider_test.go index f1ed760ee..2964fd557 100644 --- a/builtin/providers/openstack/provider_test.go +++ b/builtin/providers/openstack/provider_test.go @@ -67,4 +67,9 @@ func testAccPreCheck(t *testing.T) { if v == "" { t.Fatal("OS_NETWORK_ID must be set for acceptance tests") } + + v = os.Getenv("OS_EXTGW_ID") + if v == "" { + t.Fatal("OS_EXTGW_ID must be set for acceptance tests") + } } diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go index 4dd6b1041..06af02e31 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go @@ -49,6 +49,11 @@ func resourceNetworkingFloatingIPV2() *schema.Resource { Computed: true, ForceNew: true, }, + "fixed_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, }, } } @@ -71,6 +76,7 @@ func resourceNetworkFloatingIPV2Create(d *schema.ResourceData, meta interface{}) FloatingNetworkID: poolID, PortID: d.Get("port_id").(string), TenantID: d.Get("tenant_id").(string), + FixedIP: d.Get("fixed_ip").(string), } log.Printf("[DEBUG] Create Options: %#v", createOpts) floatingIP, err := floatingips.Create(networkingClient, createOpts).Extract() @@ -109,6 +115,7 @@ func resourceNetworkFloatingIPV2Read(d *schema.ResourceData, meta interface{}) e d.Set("address", floatingIP.FloatingIP) d.Set("port_id", floatingIP.PortID) + d.Set("fixed_ip", floatingIP.FixedIP) poolName, err := getNetworkName(d, meta, floatingIP.FloatingNetworkID) if err != nil { return fmt.Errorf("Error retrieving floating IP pool name: %s", err) diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go index 9678305f8..015e64320 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go @@ -65,6 +65,67 @@ func TestAccNetworkingV2FloatingIP_attach(t *testing.T) { }) } +func TestAccNetworkingV2FloatingIP_fixedip_bind(t *testing.T) { + var fip floatingips.FloatingIP + var testAccNetworkingV2FloatingIP_fixedip_bind = fmt.Sprintf(` + resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } + + resource "openstack_networking_router_interface_v2" "router_interface_1" { + router_id = "${openstack_networking_router_v2.router_1.id}" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } + + resource "openstack_networking_router_v2" "router_1" { + name = "router_1" + external_gateway = "%s" + } + + resource "openstack_networking_port_v2" "port_1" { + network_id = "${openstack_networking_subnet_v2.subnet_1.network_id}" + admin_state_up = "true" + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.10" + } + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.20" + } + } + + resource "openstack_networking_floatingip_v2" "ip_1" { + pool = "%s" + port_id = "${openstack_networking_port_v2.port_1.id}" + fixed_ip = "${openstack_networking_port_v2.port_1.fixed_ip.1.ip_address}" + }`, + os.Getenv("OS_EXTGW_ID"), os.Getenv("OS_POOL_NAME")) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2FloatingIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2FloatingIP_fixedip_bind, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2FloatingIPExists(t, "openstack_networking_floatingip_v2.ip_1", &fip), + testAccCheckNetworkingV2FloatingIPBoundToCorrectIP(t, &fip, "192.168.199.20"), + ), + }, + }, + }) +} + func testAccCheckNetworkingV2FloatingIPDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -118,6 +179,16 @@ func testAccCheckNetworkingV2FloatingIPExists(t *testing.T, n string, kp *floati } } +func testAccCheckNetworkingV2FloatingIPBoundToCorrectIP(t *testing.T, fip *floatingips.FloatingIP, fixed_ip string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if fip.FixedIP != fixed_ip { + return fmt.Errorf("Floating ip associated with wrong fixed ip") + } + + return nil + } +} + func testAccCheckNetworkingV2InstanceFloatingIPAttach( instance *servers.Server, fip *floatingips.FloatingIP) resource.TestCheckFunc { diff --git a/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown b/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown index e33dc8d98..2813c815e 100644 --- a/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown @@ -43,6 +43,9 @@ The following arguments are supported: belongs to the same tenant. Changing this creates a new floating IP (which may or may not have a different address) +* `fixed_ip` - Fixed IP of the port to associate with this floating IP. Required if +the port has multiple fixed IPs. + ## Attributes Reference The following attributes are exported: @@ -52,3 +55,4 @@ The following attributes are exported: * `address` - The actual floating IP address itself. * `port_id` - ID of associated port. * `tenant_id` - the ID of the tenant in which to create the floating IP. +* `fixed_ip` - The fixed IP which the floating IP maps to. From 467ddab4a5ec6e31e682dfc7111d65300338638e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 28 May 2016 15:11:07 -0600 Subject: [PATCH 07/12] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecad61e89..52012e3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ BUG FIXES: * provider/google: Fix a bug causing an error attempting to delete an already-deleted `google_compute_disk` [GH-6689] * provider/openstack: Reassociate Floating IP on network changes [GH-6579] * provider/openstack: Ensure CIDRs Are Lower Case [GH-6864] + * provider/openstack: Rebuild Instances On Network Changes [GH-6844] * provider/vsphere: `gateway` and `ipv6_gateway` are now read from `vsphere_virtual_machine` resources [GH-6522] * provider/vsphere: `ipv*_gateway` parameters won't force a new `vsphere_virtual_machine` [GH-6635] From 8e52757fc3dec15d9a28308f1ab0cdb175f695a5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 28 May 2016 15:30:50 -0600 Subject: [PATCH 08/12] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52012e3ed..0edbc94df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ IMPROVEMENTS: * provider/openstack: Add support for client certificate authentication [GH-6279] * provider/openstack: Enable DHCP By Default [GH-6838] * provider/openstack: Allow Neutron-based Floating IP to target a specific tenant [GH-6454] + * provider/openstack: Implement fixed_ip on Neutron floating ip allocations [GH-6837] * provider/vsphere: Fix bug with `vsphere_virtual_machine` wait for ip [GH-6377] * provider/vsphere: Add support for `controller_type` to `vsphere_virtual_machine` [GH-6785] * provider/vsphere: Virtual machine update disk [GH-6619] From c7107e2228d7e168ecab3d6fd75727392e65d859 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 28 May 2016 21:32:49 +0000 Subject: [PATCH 09/12] provider/openstack: Devstack and fmt fixes This commit adds the newly required OS_EXT_GW environment variable to the devstack acceptance environment build suite. It also fixes some space formatting in a test. --- .../providers/openstack/devstack/deploy.sh | 2 + ...openstack_networking_floatingip_v2_test.go | 68 +++++++++---------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/builtin/providers/openstack/devstack/deploy.sh b/builtin/providers/openstack/devstack/deploy.sh index 6c85a4795..8e19bc1ae 100644 --- a/builtin/providers/openstack/devstack/deploy.sh +++ b/builtin/providers/openstack/devstack/deploy.sh @@ -116,10 +116,12 @@ wget http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img glance image-create --name CirrOS --disk-format qcow2 --container-format bare < cirros-0.3.4-x86_64-disk.img nova flavor-create m1.tform 99 512 5 1 --ephemeral 10 _NETWORK_ID=$(nova net-list | grep private | awk -F\| '{print $2}' | tr -d ' ') +_EXTGW_ID=$(nova net-list | grep public | awk -F\| '{print $2}' | tr -d ' ') _IMAGE_ID=$(nova image-list | grep CirrOS | awk -F\| '{print $2}' | tr -d ' ' | head -1) echo export OS_IMAGE_NAME="cirros-0.3.4-x86_64-uec" >> openrc echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc +echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc echo export OS_POOL_NAME="public" >> openrc echo export OS_FLAVOR_ID=99 >> openrc source openrc demo diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go index 015e64320..179596202 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go @@ -69,45 +69,45 @@ func TestAccNetworkingV2FloatingIP_fixedip_bind(t *testing.T) { var fip floatingips.FloatingIP var testAccNetworkingV2FloatingIP_fixedip_bind = fmt.Sprintf(` resource "openstack_networking_network_v2" "network_1" { - name = "network_1" - admin_state_up = "true" - } + name = "network_1" + admin_state_up = "true" + } - resource "openstack_networking_subnet_v2" "subnet_1" { - name = "subnet_1" - network_id = "${openstack_networking_network_v2.network_1.id}" - cidr = "192.168.199.0/24" - ip_version = 4 - } + resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } - resource "openstack_networking_router_interface_v2" "router_interface_1" { - router_id = "${openstack_networking_router_v2.router_1.id}" - subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" - } + resource "openstack_networking_router_interface_v2" "router_interface_1" { + router_id = "${openstack_networking_router_v2.router_1.id}" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } - resource "openstack_networking_router_v2" "router_1" { - name = "router_1" - external_gateway = "%s" - } + resource "openstack_networking_router_v2" "router_1" { + name = "router_1" + external_gateway = "%s" + } - resource "openstack_networking_port_v2" "port_1" { - network_id = "${openstack_networking_subnet_v2.subnet_1.network_id}" - admin_state_up = "true" - fixed_ip { - subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" - ip_address = "192.168.199.10" - } - fixed_ip { - subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" - ip_address = "192.168.199.20" - } - } + resource "openstack_networking_port_v2" "port_1" { + network_id = "${openstack_networking_subnet_v2.subnet_1.network_id}" + admin_state_up = "true" + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.10" + } + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.20" + } + } - resource "openstack_networking_floatingip_v2" "ip_1" { - pool = "%s" - port_id = "${openstack_networking_port_v2.port_1.id}" - fixed_ip = "${openstack_networking_port_v2.port_1.fixed_ip.1.ip_address}" - }`, + resource "openstack_networking_floatingip_v2" "ip_1" { + pool = "%s" + port_id = "${openstack_networking_port_v2.port_1.id}" + fixed_ip = "${openstack_networking_port_v2.port_1.fixed_ip.1.ip_address}" + }`, os.Getenv("OS_EXTGW_ID"), os.Getenv("OS_POOL_NAME")) resource.Test(t, resource.TestCase{ From c083a4f5dc140618f4e1d259416b54a4c38df343 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 28 May 2016 21:59:48 +0000 Subject: [PATCH 10/12] provider/openstack: Add delete_on_termination argument to docs This commit adds a description of the delete_on_termination argument to the openstack_compute_instance_v2 documentation. --- .../providers/openstack/r/compute_instance_v2.html.markdown | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 52e545f5f..9a25b75c8 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -304,6 +304,9 @@ The `block_device` block supports: * `destination_type` - (Optional) The type that gets created. Possible values are "volume" and "local". +* `delete_on_termination` - (Optional) Delete the volume / block device upon + termination of the instance. Defaults to false. + The `volume` block supports: * `volume_id` - (Required) The UUID of the volume to attach. From 559799599e6cb0bdf0eb4c2ae5eb50059bad111e Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Sat, 28 May 2016 22:48:28 -0700 Subject: [PATCH 11/12] core: Ensure EvalReadDataApply is called on expanded destroy nodes During accpeptance tests of some of the first data sources (see hashicorp/terraform#6881 and hashicorp/terraform#6911), "unknown resource type" errors have been coming up. Traced it down to the ResourceCountTransformer, which transforms destroy nodes to a graphNodeExpandedResourceDestroy node. This node's EvalTree() was still indiscriminately using EvalApply for all resource types, versus EvalReadDataApply. This accounts for both cases via EvalIf. --- terraform/transform_resource.go | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/terraform/transform_resource.go b/terraform/transform_resource.go index 0b2df921a..2100c107b 100644 --- a/terraform/transform_resource.go +++ b/terraform/transform_resource.go @@ -894,13 +894,30 @@ func (n *graphNodeExpandedResourceDestroy) EvalTree() EvalNode { &EvalRequireState{ State: &state, }, - &EvalApply{ - Info: info, - State: &state, - Diff: &diffApply, - Provider: &provider, - Output: &state, - Error: &err, + // Make sure we handle data sources properly. + &EvalIf{ + If: func(ctx EvalContext) (bool, error) { + if n.Resource.Mode == config.DataResourceMode { + return true, nil + } + + return false, nil + }, + + Then: &EvalReadDataApply{ + Info: info, + Diff: &diffApply, + Provider: &provider, + Output: &state, + }, + Else: &EvalApply{ + Info: info, + State: &state, + Diff: &diffApply, + Provider: &provider, + Output: &state, + Error: &err, + }, }, &EvalWriteState{ Name: n.stateId(), From 46429968f08b29b97efcd4b9346f2a45dbf7066d Mon Sep 17 00:00:00 2001 From: James Nugent Date: Sun, 29 May 2016 12:00:08 -0700 Subject: [PATCH 12/12] Update CHANGELOG.md --- CHANGELOG.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0edbc94df..0d1bebc9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,47 +11,48 @@ FEATURES: * **New Command:** `terraform state` to provide access to a variety of state manipulation functions [GH-5811] * **New Provider:** `grafana` [GH-6206] - * **New Resource:** `aws_rds_cluster_parameter_group` [GH-5269] - * **New Resource:** `openstack_blockstorage_volume_v2` [GH-6693] - * **New Resource:** `vsphere_virtual_disk` [GH-6273] + * **New Provider:** `random` - allows generation of random values without constantly generating diffs [GH6672] * **New Resource:** `aws_iam_group_policy_attachment` [GH-6858] * **New Resource:** `aws_iam_role_policy_attachment` [GH-6858] * **New Resource:** `aws_iam_user_policy_attachment` [GH-6858] + * **New Resource:** `aws_rds_cluster_parameter_group` [GH-5269] + * **New Resource:** `openstack_blockstorage_volume_v2` [GH-6693] + * **New Resource:** `vsphere_virtual_disk` [GH-6273] * core: Data Resources are now supported. Values are refreshed, and available during the planning stage [GH-6598] * core: Lists and maps can now be used as first class types for variables, and may be passed between modules [GH-6322] - * core: The `terraform plan` command no longer persists state. [GH-6811] * core: Tainted resources now show up in the plan and respect dependency ordering [GH-6600] + * core: The `terraform plan` command no longer persists state. [GH-6811] IMPROVEMENTS: * core: The `jsonencode` interpolation function now supports encoding lists and maps [GH-6749] * provider/aws: Add `option_settings` to `aws_db_option_group` [GH-6560] + * provider/aws: Add more explicit support for Skipping Final Snapshot in RDS Cluster [GH-6795] * provider/aws: Add support for S3 Bucket Acceleration [GH-6628] * provider/aws: Add support for `kms_key_id` to `aws_db_instance` [GH-6651] - * provider/aws: Support for Redshift Cluster encryption using a KMS key [GH-6712] - * provider/aws: Add more explicit support for Skipping Final Snapshot in RDS Cluster [GH-6795] - * provider/aws: Set default description to "Managed by Terraform" [GH-6104] - * provider/aws: SQS use raw policy string if compact fails [GH-6724] - * provider/aws: Support tags for AWS redshift cluster [GH-5356] * provider/aws: Add support to `aws_redshift_cluster` for `iam_roles` [GH-6647] - * provider/azurerm: Add support for exporting the `azurerm_storage_account` access keys [GH-6742] + * provider/aws: SQS use raw policy string if compact fails [GH-6724] + * provider/aws: Set default description to "Managed by Terraform" [GH-6104] + * provider/aws: Support for Redshift Cluster encryption using a KMS key [GH-6712] + * provider/aws: Support tags for AWS redshift cluster [GH-5356] * provider/azurerm: Add support for EnableIPForwarding to `azurerm_network_interface` [GH-6807] + * provider/azurerm: Add support for exporting the `azurerm_storage_account` access keys [GH-6742] * provider/clc: Add support for hyperscale and bareMetal server types and package installation * provider/clc: Fix optional server password [GH-6414] - * provider/cloudstack: Enable swapping of ACLs without having to rebuild the network tier [GH-6741] * provider/cloudstack: Add support for affinity groups to `cloudstack_instance` [GH-6898] + * provider/cloudstack: Enable swapping of ACLs without having to rebuild the network tier [GH-6741] * provider/datadog: Add support for 'require full window' and 'locked' [GH-6738] + * provider/fastly: Add support for Cache Settings [GH-6781] * provider/fastly: Add support for Service Request Settings on `fastly_service_v1` resources [GH-6622] * provider/fastly: Add support for custom VCL configuration [GH-6662] - * provider/fastly: Add support for Cache Settings [GH-6781] * provider/google: Support optional uuid naming for Instance Template [GH-6604] - * provider/openstack: Increase timeouts for image resize, subnets, and routers [GH-6764] * provider/openstack: Add support for client certificate authentication [GH-6279] - * provider/openstack: Enable DHCP By Default [GH-6838] * provider/openstack: Allow Neutron-based Floating IP to target a specific tenant [GH-6454] + * provider/openstack: Enable DHCP By Default [GH-6838] * provider/openstack: Implement fixed_ip on Neutron floating ip allocations [GH-6837] - * provider/vsphere: Fix bug with `vsphere_virtual_machine` wait for ip [GH-6377] + * provider/openstack: Increase timeouts for image resize, subnets, and routers [GH-6764] * provider/vsphere: Add support for `controller_type` to `vsphere_virtual_machine` [GH-6785] + * provider/vsphere: Fix bug with `vsphere_virtual_machine` wait for ip [GH-6377] * provider/vsphere: Virtual machine update disk [GH-6619] BUG FIXES: