instance/conn_name.go (70 lines of code) (raw):
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package instance
import (
"context"
"fmt"
"regexp"
"cloud.google.com/go/cloudsqlconn/errtype"
)
var (
// Instance connection name is the format <PROJECT>:<REGION>:<INSTANCE>
// Additionally, we have to support legacy "domain-scoped" projects
// (e.g. "google.com:PROJECT")
connNameRegex = regexp.MustCompile("([^:]+(:[^:]+)?):([^:]+):([^:]+)")
// The domain name pattern in accordance with RFC 1035, RFC 1123 and RFC 2181.
domainNameRegex = regexp.MustCompile(`^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$`)
)
// ConnName represents the "instance connection name", in the format
// "project:region:name".
type ConnName struct {
project string
region string
name string
domainName string
}
func (c *ConnName) String() string {
if c.domainName != "" {
return fmt.Sprintf("%s -> %s:%s:%s", c.domainName, c.project, c.region, c.name)
}
return fmt.Sprintf("%s:%s:%s", c.project, c.region, c.name)
}
// Project returns the project within which the Cloud SQL instance runs.
func (c *ConnName) Project() string {
return c.project
}
// Region returns the region where the Cloud SQL instance runs.
func (c *ConnName) Region() string {
return c.region
}
// Name returns the Cloud SQL instance name
func (c *ConnName) Name() string {
return c.name
}
// DomainName returns the domain name for this instance
func (c *ConnName) DomainName() string {
return c.domainName
}
// HasDomainName returns whether the Cloud SQL instance has a domain name
func (c *ConnName) HasDomainName() bool {
return c.domainName != ""
}
// IsValidDomain validates that a string is a well-formed domain name
func IsValidDomain(dn string) bool {
b := []byte(dn)
m := domainNameRegex.FindSubmatch(b)
if m == nil {
return false
}
return true
}
// ParseConnName initializes a new ConnName struct.
func ParseConnName(cn string) (ConnName, error) {
return ParseConnNameWithDomainName(cn, "")
}
// ParseConnNameWithDomainName initializes a new ConnName struct,
// also setting the domain name.
func ParseConnNameWithDomainName(cn string, dn string) (ConnName, error) {
b := []byte(cn)
m := connNameRegex.FindSubmatch(b)
if m == nil {
err := errtype.NewConfigError(
"invalid instance connection name, expected PROJECT:REGION:INSTANCE",
cn,
)
return ConnName{}, err
}
c := ConnName{
project: string(m[1]),
region: string(m[3]),
name: string(m[4]),
domainName: dn,
}
return c, nil
}
// ConnectionNameResolver resolves the connection name string into a valid
// instance name. This allows an application to replace the default
// resolver with a custom implementation.
type ConnectionNameResolver interface {
// Resolve accepts a name, and returns a ConnName with the instance
// connection string for the name. If the name cannot be resolved, returns
// an error.
Resolve(ctx context.Context, name string) (ConnName, error)
}