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" )