github.com/opentofu/opentofu@v1.7.1/internal/legacy/helper/acctest/random.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package acctest 7 8 import ( 9 "bytes" 10 crand "crypto/rand" 11 "crypto/rsa" 12 "crypto/x509" 13 "crypto/x509/pkix" 14 "encoding/pem" 15 "fmt" 16 "math/big" 17 "math/rand" 18 "net" 19 "strings" 20 "time" 21 22 "golang.org/x/crypto/ssh" 23 24 "github.com/apparentlymart/go-cidr/cidr" 25 ) 26 27 func init() { 28 rand.Seed(time.Now().UTC().UnixNano()) 29 } 30 31 // Helpers for generating random tidbits for use in identifiers to prevent 32 // collisions in acceptance tests. 33 34 // RandInt generates a random integer 35 func RandInt() int { 36 return rand.New(rand.NewSource(time.Now().UnixNano())).Int() 37 } 38 39 // RandomWithPrefix is used to generate a unique name with a prefix, for 40 // randomizing names in acceptance tests 41 func RandomWithPrefix(name string) string { 42 return fmt.Sprintf("%s-%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) 43 } 44 45 func RandIntRange(min int, max int) int { 46 source := rand.New(rand.NewSource(time.Now().UnixNano())) 47 rangeMax := max - min 48 49 return int(source.Int31n(int32(rangeMax))) 50 } 51 52 // RandString generates a random alphanumeric string of the length specified 53 func RandString(strlen int) string { 54 return RandStringFromCharSet(strlen, CharSetAlphaNum) 55 } 56 57 // RandStringFromCharSet generates a random string by selecting characters from 58 // the charset provided 59 func RandStringFromCharSet(strlen int, charSet string) string { 60 result := make([]byte, strlen) 61 for i := 0; i < strlen; i++ { 62 result[i] = charSet[rand.Intn(len(charSet))] 63 } 64 return string(result) 65 } 66 67 // RandSSHKeyPair generates a public and private SSH key pair. The public key is 68 // returned in OpenSSH format, and the private key is PEM encoded. 69 func RandSSHKeyPair(comment string) (string, string, error) { 70 privateKey, privateKeyPEM, err := genPrivateKey() 71 if err != nil { 72 return "", "", err 73 } 74 75 publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) 76 if err != nil { 77 return "", "", err 78 } 79 keyMaterial := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey))) 80 return fmt.Sprintf("%s %s", keyMaterial, comment), privateKeyPEM, nil 81 } 82 83 // RandTLSCert generates a self-signed TLS certificate with a newly created 84 // private key, and returns both the cert and the private key PEM encoded. 85 func RandTLSCert(orgName string) (string, string, error) { 86 template := &x509.Certificate{ 87 SerialNumber: big.NewInt(int64(RandInt())), 88 Subject: pkix.Name{ 89 Organization: []string{orgName}, 90 }, 91 NotBefore: time.Now(), 92 NotAfter: time.Now().Add(24 * time.Hour), 93 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 94 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 95 BasicConstraintsValid: true, 96 } 97 98 privateKey, privateKeyPEM, err := genPrivateKey() 99 if err != nil { 100 return "", "", err 101 } 102 103 cert, err := x509.CreateCertificate(crand.Reader, template, template, &privateKey.PublicKey, privateKey) 104 if err != nil { 105 return "", "", err 106 } 107 108 certPEM, err := pemEncode(cert, "CERTIFICATE") 109 if err != nil { 110 return "", "", err 111 } 112 113 return certPEM, privateKeyPEM, nil 114 } 115 116 // RandIpAddress returns a random IP address in the specified CIDR block. 117 // The prefix length must be less than 31. 118 func RandIpAddress(s string) (string, error) { 119 _, network, err := net.ParseCIDR(s) 120 if err != nil { 121 return "", err 122 } 123 124 firstIp, lastIp := cidr.AddressRange(network) 125 first := &big.Int{} 126 first.SetBytes([]byte(firstIp)) 127 last := &big.Int{} 128 last.SetBytes([]byte(lastIp)) 129 r := &big.Int{} 130 r.Sub(last, first) 131 if len := r.BitLen(); len > 31 { 132 return "", fmt.Errorf("CIDR range is too large: %d", len) 133 } 134 135 max := int(r.Int64()) 136 if max == 0 { 137 // panic: invalid argument to Int31n 138 return firstIp.String(), nil 139 } 140 141 host, err := cidr.Host(network, RandIntRange(0, max)) 142 if err != nil { 143 return "", err 144 } 145 146 return host.String(), nil 147 } 148 149 func genPrivateKey() (*rsa.PrivateKey, string, error) { 150 privateKey, err := rsa.GenerateKey(crand.Reader, 1024) 151 if err != nil { 152 return nil, "", err 153 } 154 155 privateKeyPEM, err := pemEncode(x509.MarshalPKCS1PrivateKey(privateKey), "RSA PRIVATE KEY") 156 if err != nil { 157 return nil, "", err 158 } 159 160 return privateKey, privateKeyPEM, nil 161 } 162 163 func pemEncode(b []byte, block string) (string, error) { 164 var buf bytes.Buffer 165 pb := &pem.Block{Type: block, Bytes: b} 166 if err := pem.Encode(&buf, pb); err != nil { 167 return "", err 168 } 169 170 return buf.String(), nil 171 } 172 173 const ( 174 // CharSetAlphaNum is the alphanumeric character set for use with 175 // RandStringFromCharSet 176 CharSetAlphaNum = "abcdefghijklmnopqrstuvwxyz012346789" 177 178 // CharSetAlpha is the alphabetical character set for use with 179 // RandStringFromCharSet 180 CharSetAlpha = "abcdefghijklmnopqrstuvwxyz" 181 )