github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/mysql/azuremysql/azuremysql.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package azuremysql provides connections to Azure Database for MySQL. 16 // See https://docs.microsoft.com/en-us/azure/mysql. 17 // 18 // # URLs 19 // 20 // For mysql.Open, azuremysql registers for the scheme "azuremysql". 21 // To customize the URL opener, or for more details on the URL format, 22 // see URLOpener. 23 // 24 // See https://gocloud.dev/concepts/urls/ for background information. 25 package azuremysql // import "gocloud.dev/mysql/azuremysql" 26 27 import ( 28 "context" 29 "crypto/tls" 30 "database/sql" 31 "database/sql/driver" 32 "fmt" 33 "net/url" 34 "strings" 35 "sync" 36 37 "contrib.go.opencensus.io/integrations/ocsql" 38 "github.com/go-sql-driver/mysql" 39 "gocloud.dev/azure/azuredb" 40 cdkmysql "gocloud.dev/mysql" 41 ) 42 43 // URLOpener opens Azure MySQL URLs 44 // like "azuremysql://user:password@myinstance.mysql.database.azure.com/mydb". 45 type URLOpener struct { 46 // CertSource specifies how the opener will obtain the Azure Certificate 47 // Authority. If nil, it will use the default *azuredb.CertFetcher. 48 CertSource azuredb.CertPoolProvider 49 // TraceOpts contains options for OpenCensus. 50 TraceOpts []ocsql.TraceOption 51 } 52 53 // Scheme is the URL scheme azuremysql registers its URLOpener under on 54 // mysql.DefaultMux. 55 const Scheme = "azuremysql" 56 57 func init() { 58 cdkmysql.DefaultURLMux().RegisterMySQL(Scheme, &URLOpener{}) 59 } 60 61 // OpenMySQLURL opens an encrypted connection to an Azure MySQL database. 62 func (uo *URLOpener) OpenMySQLURL(ctx context.Context, u *url.URL) (*sql.DB, error) { 63 source := uo.CertSource 64 if source == nil { 65 source = new(azuredb.CertFetcher) 66 } 67 if u.Host == "" { 68 return nil, fmt.Errorf("open Azure database: empty endpoint") 69 } 70 password, _ := u.User.Password() 71 c := &connector{ 72 addr: u.Host, 73 user: u.User.Username(), 74 password: password, 75 dbName: strings.TrimPrefix(u.Path, "/"), 76 // Make a copy of TraceOpts to avoid caller modifying. 77 traceOpts: append([]ocsql.TraceOption(nil), uo.TraceOpts...), 78 provider: source, 79 80 sem: make(chan struct{}, 1), 81 ready: make(chan struct{}), 82 } 83 c.sem <- struct{}{} 84 return sql.OpenDB(c), nil 85 } 86 87 type connector struct { 88 addr string 89 user string 90 password string 91 dbName string 92 traceOpts []ocsql.TraceOption 93 94 sem chan struct{} // receive to acquire, send to release 95 provider CertPoolProvider // provides the CA certificate pool 96 97 ready chan struct{} // closed after writing dsn 98 dsn string 99 } 100 101 func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { 102 select { 103 case <-c.sem: 104 certPool, err := c.provider.AzureCertPool(ctx) 105 if err != nil { 106 c.sem <- struct{}{} // release 107 return nil, fmt.Errorf("connect Azure MySql: %v", err) 108 } 109 110 // TODO(light): Avoid global registry once https://github.com/go-sql-driver/mysql/issues/771 is fixed. 111 tlsConfigCounter.mu.Lock() 112 tlsConfigNum := tlsConfigCounter.n 113 tlsConfigCounter.n++ 114 tlsConfigCounter.mu.Unlock() 115 tlsConfigName := fmt.Sprintf("gocloud.dev/mysql/azuremysql/%d", tlsConfigNum) 116 err = mysql.RegisterTLSConfig(tlsConfigName, &tls.Config{ 117 RootCAs: certPool, 118 }) 119 if err != nil { 120 c.sem <- struct{}{} // release 121 return nil, fmt.Errorf("connect Azure MySql: register TLS: %v", err) 122 } 123 cfg := &mysql.Config{ 124 Net: "tcp", 125 Addr: c.addr, 126 User: c.user, 127 Passwd: c.password, 128 TLSConfig: tlsConfigName, 129 AllowCleartextPasswords: true, 130 AllowNativePasswords: true, 131 DBName: c.dbName, 132 } 133 c.dsn = cfg.FormatDSN() 134 close(c.ready) 135 // Don't release sem: make it block forever, so this case won't be run again. 136 case <-c.ready: 137 // Already succeeded. 138 case <-ctx.Done(): 139 return nil, fmt.Errorf("connect Azure MySql: waiting for certificates: %v", ctx.Err()) 140 } 141 return c.Driver().Open(c.dsn) 142 } 143 144 func (c *connector) Driver() driver.Driver { 145 return ocsql.Wrap(mysql.MySQLDriver{}, c.traceOpts...) 146 } 147 148 var tlsConfigCounter struct { 149 mu sync.Mutex 150 n int 151 } 152 153 // A CertPoolProvider obtains a certificate pool that contains the Azure CA certificate. 154 type CertPoolProvider = azuredb.CertPoolProvider 155 156 // CertFetcher is a default CertPoolProvider that can fetch CA certificates from 157 // any publicly accessible URI or File. 158 type CertFetcher = azuredb.CertFetcher