pkg/firebase/apphostingschema/vpcaccess.go (85 lines of code) (raw):
package apphostingschema
import (
"fmt"
"regexp"
"slices"
)
var (
// fullyQualifiedConnector is the regular expression for a full VPC Connector name.
fullyQualifiedConnector = regexp.MustCompile(`^projects/[^/]+/locations/[^/]+/connectors/[^/]+$`)
// resourceID is the regular expression for a resource ID.
resourceID = regexp.MustCompile(`^[^/]+$`)
// fullyQualifiedNetwork is the regular expression for a fully qualified network name.
fullyQualifiedNetwork = regexp.MustCompile(`^projects/[^/]+/global/networks/[^/]+$`)
// fullyQualifiedSubnetwork is the regular expression for a fully qualified subnetwork name.
fullyQualifiedSubnetwork = regexp.MustCompile(`^projects/[^/]+/regions/[^/]+/subnetworks/[^/]+$`)
// validEgress is the list of valid egress settings.
validEgress = []string{"ALL_TRAFFIC", "PRIVATE_RANGES_ONLY"}
)
// ValidateVpcAccess validates the form of a vpcAccess struct.
func ValidateVpcAccess(vpcAccess *VpcAccess) error {
if vpcAccess == nil {
return nil
}
if vpcAccess.Egress != "" && !slices.Contains(validEgress, vpcAccess.Egress) {
return fmt.Errorf("egress must be one of %v, got: %q", validEgress, vpcAccess.Egress)
}
if vpcAccess.Connector != "" && !fullyQualifiedConnector.MatchString(vpcAccess.Connector) && !resourceID.MatchString(vpcAccess.Connector) {
return fmt.Errorf("connector must be fully qualified or an ID, got: %q", vpcAccess.Connector)
}
if vpcAccess.Connector == "" && len(vpcAccess.NetworkInterfaces) == 0 {
return fmt.Errorf("one of connector or networkInterfaces must be set")
}
if vpcAccess.Connector != "" && len(vpcAccess.NetworkInterfaces) > 0 {
return fmt.Errorf("connector and networkInterfaces cannot be set at the same time")
}
for _, ni := range vpcAccess.NetworkInterfaces {
if ni.Network == "" && ni.Subnetwork == "" {
return fmt.Errorf("at least one of network or subnetwork is required")
}
if ni.Network != "" && !fullyQualifiedNetwork.MatchString(ni.Network) && !resourceID.MatchString(ni.Network) {
return fmt.Errorf("network must be fully qualified or an ID, got: %q", ni.Network)
}
if ni.Subnetwork != "" && !fullyQualifiedSubnetwork.MatchString(ni.Subnetwork) && !resourceID.MatchString(ni.Subnetwork) {
return fmt.Errorf("subnetwork must be fully qualified or an ID, got: %q", ni.Subnetwork)
}
}
return nil
}
// MergeVpcAccess merges the access from the YAML settings and the output bundle (though the output
// bundle is not expected to have any VPC access settings at the moment).
func MergeVpcAccess(yamlAccess, envAccess *VpcAccess) *VpcAccess {
if yamlAccess == nil {
return envAccess
}
if envAccess == nil {
return yamlAccess
}
ret := &VpcAccess{}
if envAccess.Egress != "" {
ret.Egress = envAccess.Egress
} else if yamlAccess.Egress != "" {
ret.Egress = yamlAccess.Egress
}
// Note: connector and network interfaces are mutually exclusive, so we only copy one field from
// one source.
if envAccess.Connector != "" {
ret.Connector = envAccess.Connector
} else if envAccess.NetworkInterfaces != nil {
ret.NetworkInterfaces = make([]NetworkInterface, len(envAccess.NetworkInterfaces))
copy(ret.NetworkInterfaces, envAccess.NetworkInterfaces)
} else if yamlAccess.Connector != "" {
ret.Connector = yamlAccess.Connector
} else if yamlAccess.NetworkInterfaces != nil {
ret.NetworkInterfaces = make([]NetworkInterface, len(yamlAccess.NetworkInterfaces))
copy(ret.NetworkInterfaces, yamlAccess.NetworkInterfaces)
}
return ret
}
// NormalizeVpcAccess ensures that any connector is a fully qualified resource name.
func NormalizeVpcAccess(vpcAccess *VpcAccess, project, region string) {
if vpcAccess == nil {
return
}
if resourceID.MatchString(vpcAccess.Connector) {
vpcAccess.Connector = fmt.Sprintf("projects/%s/locations/%s/connectors/%s", project, region, vpcAccess.Connector)
}
// N.B. range returns copies, so editing the value directly would not affect the original.
ni := vpcAccess.NetworkInterfaces
for x := range ni {
if resourceID.MatchString(ni[x].Network) {
ni[x].Network = fmt.Sprintf("projects/%s/global/networks/%s", project, ni[x].Network)
}
if resourceID.MatchString(ni[x].Subnetwork) {
ni[x].Subnetwork = fmt.Sprintf("projects/%s/regions/%s/subnetworks/%s", project, region, ni[x].Subnetwork)
}
}
}