2017-07-07 19:48:29 +00:00
package google
import (
"fmt"
2017-11-02 17:38:20 +00:00
"net"
2017-07-07 19:48:29 +00:00
"regexp"
2017-11-07 23:42:11 +00:00
"strconv"
2018-01-24 21:03:09 +00:00
"strings"
2018-06-01 00:31:45 +00:00
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
2017-07-07 19:48:29 +00:00
)
2017-08-04 18:00:45 +00:00
const (
// Copied from the official Google Cloud auto-generated client.
ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))"
RegionRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
SubnetworkRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
SubnetworkLinkRegex = "projects/(" + ProjectRegex + ")/regions/(" + RegionRegex + ")/subnetworks/(" + SubnetworkRegex + ")$"
2017-11-28 22:37:46 +00:00
RFC1035NameTemplate = "[a-z](?:[-a-z0-9]{%d,%d}[a-z0-9])"
2018-01-24 21:03:09 +00:00
CloudIoTIdRegex = "^[a-zA-Z][-a-zA-Z0-9._+~%]{2,254}$"
2018-04-26 22:46:56 +00:00
// Format of default Compute service accounts created by Google
// ${PROJECT_ID}-compute@developer.gserviceaccount.com where PROJECT_ID is an int64 (max 20 digits)
ComputeServiceAccountNameRegex = "[0-9]{1,20}-compute@developer.gserviceaccount.com"
2017-11-28 22:37:46 +00:00
)
var (
// Service account name must have a length between 6 and 30.
// The first and last characters have different restrictions, than
// the middle characters. The middle characters length must be between
// 4 and 28 since the first and last character are excluded.
ServiceAccountNameRegex = fmt . Sprintf ( RFC1035NameTemplate , 4 , 28 )
2018-04-26 22:46:56 +00:00
ServiceAccountLinkRegexPrefix = "projects/" + ProjectRegex + "/serviceAccounts/"
PossibleServiceAccountNames = [ ] string {
AppEngineServiceAccountNameRegex ,
ComputeServiceAccountNameRegex ,
CreatedServiceAccountNameRegex ,
}
ServiceAccountLinkRegex = ServiceAccountLinkRegexPrefix + "(" + strings . Join ( PossibleServiceAccountNames , "|" ) + ")"
// Format of service accounts created through the API
CreatedServiceAccountNameRegex = fmt . Sprintf ( RFC1035NameTemplate , 4 , 28 ) + "@" + ProjectNameInDNSFormRegex + "\\.iam\\.gserviceaccount\\.com$"
ProjectNameInDNSFormRegex = "[-a-z0-9\\.]{1,63}"
// Format of default App Engine service accounts created by Google
AppEngineServiceAccountNameRegex = ProjectRegex + "@appspot.gserviceaccount.com"
2018-05-22 19:45:28 +00:00
ProjectIDRegex = "^[a-z][a-z0-9-]{4,28}[a-z0-9]$"
ProjectNameRegex = "^[A-Za-z0-9-'\"\\s!]{4,30}$"
2017-08-04 18:00:45 +00:00
)
2017-07-28 16:51:29 +00:00
2017-11-02 17:38:20 +00:00
var rfc1918Networks = [ ] string {
"10.0.0.0/8" ,
"172.16.0.0/12" ,
"192.168.0.0/16" ,
}
2017-07-07 19:48:29 +00:00
func validateGCPName ( v interface { } , k string ) ( ws [ ] string , errors [ ] error ) {
re := ` ^(?:[a-z](?:[-a-z0-9] { 0,61}[a-z0-9])?)$ `
return validateRegexp ( re ) ( v , k )
}
func validateRegexp ( re string ) schema . SchemaValidateFunc {
return func ( v interface { } , k string ) ( ws [ ] string , errors [ ] error ) {
value := v . ( string )
if ! regexp . MustCompile ( re ) . MatchString ( value ) {
errors = append ( errors , fmt . Errorf (
"%q (%q) doesn't match regexp %q" , k , value , re ) )
}
return
}
}
2017-11-02 17:38:20 +00:00
func validateRFC1918Network ( min , max int ) schema . SchemaValidateFunc {
return func ( i interface { } , k string ) ( s [ ] string , es [ ] error ) {
s , es = validation . CIDRNetwork ( min , max ) ( i , k )
if len ( es ) > 0 {
return
}
v , _ := i . ( string )
ip , _ , _ := net . ParseCIDR ( v )
for _ , c := range rfc1918Networks {
if _ , ipnet , _ := net . ParseCIDR ( c ) ; ipnet . Contains ( ip ) {
return
}
}
es = append ( es , fmt . Errorf ( "expected %q to be an RFC1918-compliant CIDR, got: %s" , k , v ) )
return
}
}
2017-11-07 23:42:11 +00:00
func validateRFC3339Time ( v interface { } , k string ) ( warnings [ ] string , errors [ ] error ) {
time := v . ( string )
if len ( time ) != 5 || time [ 2 ] != ':' {
errors = append ( errors , fmt . Errorf ( "%q (%q) must be in the format HH:mm (RFC3399)" , k , time ) )
return
}
if hour , err := strconv . ParseUint ( time [ : 2 ] , 10 , 0 ) ; err != nil || hour > 23 {
errors = append ( errors , fmt . Errorf ( "%q (%q) does not contain a valid hour (00-23)" , k , time ) )
return
}
if min , err := strconv . ParseUint ( time [ 3 : ] , 10 , 0 ) ; err != nil || min > 59 {
errors = append ( errors , fmt . Errorf ( "%q (%q) does not contain a valid minute (00-59)" , k , time ) )
return
}
return
}
2017-11-28 01:58:11 +00:00
func validateRFC1035Name ( min , max int ) schema . SchemaValidateFunc {
if min < 2 || max < min {
return func ( i interface { } , k string ) ( s [ ] string , errors [ ] error ) {
if min < 2 {
errors = append ( errors , fmt . Errorf ( "min must be at least 2. Got: %d" , min ) )
}
if max < min {
errors = append ( errors , fmt . Errorf ( "max must greater than min. Got [%d, %d]" , min , max ) )
}
return
}
}
2017-11-28 22:37:46 +00:00
return validateRegexp ( fmt . Sprintf ( "^" + RFC1035NameTemplate + "$" , min - 2 , max - 2 ) )
2017-11-28 01:58:11 +00:00
}
2018-01-17 20:58:37 +00:00
func validateIpCidrRange ( v interface { } , k string ) ( warnings [ ] string , errors [ ] error ) {
_ , _ , err := net . ParseCIDR ( v . ( string ) )
if err != nil {
errors = append ( errors , fmt . Errorf ( "%q is not a valid IP CIDR range: %s" , k , err ) )
}
return
}
2018-01-24 21:03:09 +00:00
func validateCloudIoTID ( v interface { } , k string ) ( warnings [ ] string , errors [ ] error ) {
value := v . ( string )
if strings . HasPrefix ( value , "goog" ) {
errors = append ( errors , fmt . Errorf (
"%q (%q) can not start with \"goog\"" , k , value ) )
}
if ! regexp . MustCompile ( CloudIoTIdRegex ) . MatchString ( value ) {
errors = append ( errors , fmt . Errorf (
"%q (%q) doesn't match regexp %q" , k , value , CloudIoTIdRegex ) )
}
return
}
2018-05-07 22:34:56 +00:00
func orEmpty ( f schema . SchemaValidateFunc ) schema . SchemaValidateFunc {
return func ( i interface { } , k string ) ( [ ] string , [ ] error ) {
v , ok := i . ( string )
if ok && v == "" {
return nil , nil
}
return f ( i , k )
}
}
2018-05-22 19:45:28 +00:00
func validateProjectID ( ) schema . SchemaValidateFunc {
return func ( v interface { } , k string ) ( ws [ ] string , errors [ ] error ) {
value := v . ( string )
if ! regexp . MustCompile ( ProjectIDRegex ) . MatchString ( value ) {
errors = append ( errors , fmt . Errorf (
"%q project_id must be 6 to 30 with lowercase letters, digits, hyphens and start with a letter. Trailing hyphens are prohibited." , value ) )
}
return
}
}
func validateProjectName ( ) schema . SchemaValidateFunc {
return func ( v interface { } , k string ) ( ws [ ] string , errors [ ] error ) {
value := v . ( string )
if ! regexp . MustCompile ( ProjectNameRegex ) . MatchString ( value ) {
errors = append ( errors , fmt . Errorf (
"%q name must be 4 to 30 characters with lowercase and uppercase letters, numbers, hyphen, single-quote, double-quote, space, and exclamation point." , value ) )
}
return
}
}