Merge branch 'master' into b-data-diff-lag
This commit is contained in:
commit
8b252cfd2b
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -11,46 +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/vsphere: Fix bug with `vsphere_virtual_machine` wait for ip [GH-6377]
|
||||
* provider/openstack: Enable DHCP By Default [GH-6838]
|
||||
* provider/openstack: Implement fixed_ip on Neutron floating ip allocations [GH-6837]
|
||||
* 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:
|
||||
|
@ -74,6 +76,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]
|
||||
|
||||
|
|
|
@ -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()
|
||||
},
|
||||
})
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package main
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
`
|
|
@ -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)
|
||||
}
|
|
@ -36,6 +36,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"
|
||||
|
@ -87,6 +88,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,
|
||||
|
|
|
@ -897,13 +897,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(),
|
||||
|
|
|
@ -36,6 +36,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,
|
||||
|
|
|
@ -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
|
||||
|
@ -301,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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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`.
|
||||
|
|
@ -278,6 +278,10 @@
|
|||
<a href="/docs/providers/powerdns/index.html">PowerDNS</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-random") %>>
|
||||
<a href="/docs/providers/random/index.html">Random</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-rundeck") %>>
|
||||
<a href="/docs/providers/rundeck/index.html">Rundeck</a>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<% wrap_layout :inner do %>
|
||||
<% content_for :sidebar do %>
|
||||
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||
<ul class="nav docs-sidenav">
|
||||
<li<%= sidebar_current("docs-home") %>>
|
||||
<a href="/docs/providers/index.html">« Documentation Home</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-random-index") %>>
|
||||
<a href="/docs/providers/random/index.html">Random Provider</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-random-resource/) %>>
|
||||
<a href="#">Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-random-resource-id") %>>
|
||||
<a href="/docs/providers/random/r/id.html">random_id</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-random-resource-shuffle") %>>
|
||||
<a href="/docs/providers/random/r/shuffle.html">random_shuffle</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= yield %>
|
||||
<% end %>
|
Loading…
Reference in New Issue