blob: bbbc48d103954a7df5e86a8fb36db13e4d3d0a8d [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package acctest
import (
"bytes"
crand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"math/rand"
"net"
"strings"
"time"
"golang.org/x/crypto/ssh"
"github.com/apparentlymart/go-cidr/cidr"
)
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
// Helpers for generating random tidbits for use in identifiers to prevent
// collisions in acceptance tests.
// RandInt generates a random integer
func RandInt() int {
return rand.New(rand.NewSource(time.Now().UnixNano())).Int()
}
// RandomWithPrefix is used to generate a unique name with a prefix, for
// randomizing names in acceptance tests
func RandomWithPrefix(name string) string {
return fmt.Sprintf("%s-%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
}
func RandIntRange(min int, max int) int {
source := rand.New(rand.NewSource(time.Now().UnixNano()))
rangeMax := max - min
return int(source.Int31n(int32(rangeMax)))
}
// RandString generates a random alphanumeric string of the length specified
func RandString(strlen int) string {
return RandStringFromCharSet(strlen, CharSetAlphaNum)
}
// RandStringFromCharSet generates a random string by selecting characters from
// the charset provided
func RandStringFromCharSet(strlen int, charSet string) string {
result := make([]byte, strlen)
for i := 0; i < strlen; i++ {
result[i] = charSet[rand.Intn(len(charSet))]
}
return string(result)
}
// RandSSHKeyPair generates a public and private SSH key pair. The public key is
// returned in OpenSSH format, and the private key is PEM encoded.
func RandSSHKeyPair(comment string) (string, string, error) {
privateKey, privateKeyPEM, err := genPrivateKey()
if err != nil {
return "", "", err
}
publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return "", "", err
}
keyMaterial := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey)))
return fmt.Sprintf("%s %s", keyMaterial, comment), privateKeyPEM, nil
}
// RandTLSCert generates a self-signed TLS certificate with a newly created
// private key, and returns both the cert and the private key PEM encoded.
func RandTLSCert(orgName string) (string, string, error) {
template := &x509.Certificate{
SerialNumber: big.NewInt(int64(RandInt())),
Subject: pkix.Name{
Organization: []string{orgName},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
privateKey, privateKeyPEM, err := genPrivateKey()
if err != nil {
return "", "", err
}
cert, err := x509.CreateCertificate(crand.Reader, template, template, &privateKey.PublicKey, privateKey)
if err != nil {
return "", "", err
}
certPEM, err := pemEncode(cert, "CERTIFICATE")
if err != nil {
return "", "", err
}
return certPEM, privateKeyPEM, nil
}
// RandIpAddress returns a random IP address in the specified CIDR block.
// The prefix length must be less than 31.
func RandIpAddress(s string) (string, error) {
_, network, err := net.ParseCIDR(s)
if err != nil {
return "", err
}
firstIp, lastIp := cidr.AddressRange(network)
first := &big.Int{}
first.SetBytes([]byte(firstIp))
last := &big.Int{}
last.SetBytes([]byte(lastIp))
r := &big.Int{}
r.Sub(last, first)
if len := r.BitLen(); len > 31 {
return "", fmt.Errorf("CIDR range is too large: %d", len)
}
max := int(r.Int64())
if max == 0 {
// panic: invalid argument to Int31n
return firstIp.String(), nil
}
host, err := cidr.Host(network, RandIntRange(0, max))
if err != nil {
return "", err
}
return host.String(), nil
}
func genPrivateKey() (*rsa.PrivateKey, string, error) {
privateKey, err := rsa.GenerateKey(crand.Reader, 1024)
if err != nil {
return nil, "", err
}
privateKeyPEM, err := pemEncode(x509.MarshalPKCS1PrivateKey(privateKey), "RSA PRIVATE KEY")
if err != nil {
return nil, "", err
}
return privateKey, privateKeyPEM, nil
}
func pemEncode(b []byte, block string) (string, error) {
var buf bytes.Buffer
pb := &pem.Block{Type: block, Bytes: b}
if err := pem.Encode(&buf, pb); err != nil {
return "", err
}
return buf.String(), nil
}
const (
// CharSetAlphaNum is the alphanumeric character set for use with
// RandStringFromCharSet
CharSetAlphaNum = "abcdefghijklmnopqrstuvwxyz012346789"
// CharSetAlpha is the alphabetical character set for use with
// RandStringFromCharSet
CharSetAlpha = "abcdefghijklmnopqrstuvwxyz"
)