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  )