From b1de477691fa92c53dfaaadd0787a40cfa1ae142 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 14 May 2016 14:00:58 -0700 Subject: [PATCH] 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 +} +`