Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add minimum character constraints to random_string #22

Merged
merged 1 commit into from
May 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 75 additions & 8 deletions random/resource_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package random

import (
"crypto/rand"
"math/big"
"sort"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/schema"
)

Expand Down Expand Up @@ -53,6 +56,34 @@ func resourceString() *schema.Resource {
ForceNew: true,
},

"min_numeric": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"min_upper": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"min_lower": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"min_special": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ForceNew: true,
},

"override_special": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -75,9 +106,13 @@ func CreateString(d *schema.ResourceData, meta interface{}) error {

length := d.Get("length").(int)
upper := d.Get("upper").(bool)
minUpper := d.Get("min_upper").(int)
lower := d.Get("lower").(bool)
minLower := d.Get("min_lower").(int)
number := d.Get("number").(bool)
minNumeric := d.Get("min_numeric").(int)
special := d.Get("special").(bool)
minSpecial := d.Get("min_special").(int)
overrideSpecial := d.Get("override_special").(string)

if overrideSpecial != "" {
Expand All @@ -98,17 +133,49 @@ func CreateString(d *schema.ResourceData, meta interface{}) error {
chars += specialChars
}

var bytes = make([]byte, length)
var l = byte(len(chars))
minMapping := map[string]int{
numChars: minNumeric,
lowerChars: minLower,
upperChars: minUpper,
specialChars: minSpecial,
}
var result = make([]byte, 0, length)
for k, v := range minMapping {
s, err := generateRandomBytes(&k, v)
if err != nil {
return errwrap.Wrapf("error generating random bytes: {{err}}", err)
}
result = append(result, s...)
}
s, err := generateRandomBytes(&chars, length-len(result))
if err != nil {
return errwrap.Wrapf("error generating random bytes: {{err}}", err)
}
result = append(result, s...)
order := make([]byte, len(result))
if _, err := rand.Read(order); err != nil {
return errwrap.Wrapf("error generating random bytes: {{err}}", err)
}
sort.Slice(result, func(i, j int) bool {
return order[i] < order[j]
})

rand.Read(bytes)
d.Set("result", string(result))
d.SetId(string(result))
return nil
}

for i, b := range bytes {
bytes[i] = chars[b%l]
func generateRandomBytes(charSet *string, length int) ([]byte, error) {
bytes := make([]byte, length)
setLen := big.NewInt(int64(len(*charSet)))
for i := range bytes {
idx, err := rand.Int(rand.Reader, setLen)
if err != nil {
return nil, err
}
bytes[i] = (*charSet)[idx.Int64()]
}
d.Set("result", string(bytes))
d.SetId(string(bytes))
return nil
return bytes, nil
}

func ReadString(d *schema.ResourceData, meta interface{}) error {
Expand Down
36 changes: 36 additions & 0 deletions random/resource_string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package random

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform/helper/resource"
Expand Down Expand Up @@ -30,6 +31,13 @@ func TestAccResourceString(t *testing.T) {
customLen: 4,
}),
patternMatch("random_string.three", "!!!!"),
testAccResourceStringCheck("random_string.min", &customLens{
customLen: 12,
}),
regexMatch("random_string.min", regexp.MustCompile(`([a-z])`), 2),
regexMatch("random_string.min", regexp.MustCompile(`([A-Z])`), 3),
regexMatch("random_string.min", regexp.MustCompile(`([0-9])`), 4),
regexMatch("random_string.min", regexp.MustCompile(`([!#@])`), 1),
),
},
},
Expand All @@ -56,6 +64,25 @@ func testAccResourceStringCheck(id string, want *customLens) resource.TestCheckF
}
}

func regexMatch(id string, exp *regexp.Regexp, requiredMatches int) 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")
}

customStr := rs.Primary.Attributes["result"]

if matches := exp.FindAllStringSubmatchIndex(customStr, -1); len(matches) < requiredMatches {
return fmt.Errorf("custom string is %s; did not match %s", customStr, exp)
}

return nil
}
}
func patternMatch(id string, want string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[id]
Expand Down Expand Up @@ -93,5 +120,14 @@ resource "random_string" "three" {
number = false
}

resource "random_string" "min" {
length = 12
override_special = "!#@"
min_lower = 2
min_upper = 3
min_special = 1
min_numeric = 4
}

`
)
17 changes: 13 additions & 4 deletions website/docs/r/string.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ description: |-
# random\_string

The resource `random_string` generates a random permutation of alphanumeric
characters and optionally special characters. This resource does not provide
any guarantee that the random string will contain specific characters.
ie. if length = 4 and special = true, output could be 'Aa0#' or '1111'

characters and optionally special characters.

## Example Usage

Expand All @@ -37,15 +34,27 @@ The following arguments are supported:
* `upper` - (Optional) (default true) Include uppercase alphabet characters
in random string.

* `min_upper` - (Optional) (default 0) Minimum number of uppercase alphabet
characters in random string.

* `lower` - (Optional) (default true) Include lowercase alphabet characters
in random string.

* `min_lower` - (Optional) (default 0) Minimum number of lowercase alphabet
characters in random string.

* `number` - (Optional) (default true) Include numeric characters in random
string.

* `min_numeric` - (Optional) (default 0) Minimum number of numeric characters
in random string.

* `special` - (Optional) (default true) Include special characters in random
string. These are '!@#$%&*()-_=+[]{}<>:?'

* `min_special` - (Optional) (default 0) Minimum number of special characters
in random string.

* `override_special` - (Optional) Supply your own list of special characters to
use for string generation. This overrides characters list in the special
argument. The special argument must still be set to true for any overwritten
Expand Down