@@ -17,18 +17,21 @@ package alloydbconn
1717import (
1818 "context"
1919 "crypto/rsa"
20+ "errors"
2021 "io"
2122 "net"
2223 "net/http"
2324 "os"
25+ "strings"
2426 "time"
2527
2628 "cloud.google.com/go/alloydbconn/debug"
2729 "cloud.google.com/go/alloydbconn/errtype"
2830 "cloud.google.com/go/alloydbconn/internal/alloydb"
31+ "golang.org/x/net/proxy"
2932 "golang.org/x/oauth2"
3033 "golang.org/x/oauth2/google"
31- apiopt "google.golang.org/api/option"
34+ "google.golang.org/api/option"
3235)
3336
3437// CloudPlatformScope is the default OAuth2 scope set on the API client.
@@ -37,23 +40,109 @@ const CloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform"
3740// An Option is an option for configuring a Dialer.
3841type Option func (d * dialerConfig )
3942
43+ func newDialerConfig (ctx context.Context , opts ... Option ) (* dialerConfig , error ) {
44+ d := & dialerConfig {
45+ refreshTimeout : alloydb .RefreshTimeout ,
46+ dialFunc : proxy .Dial ,
47+ logger : nullLogger {},
48+ userAgents : []string {userAgent },
49+ }
50+ for _ , opt := range opts {
51+ opt (d )
52+ }
53+
54+ incompatibleAuthOpts := d .disableMetadataExchange && d .useIAMAuthN
55+ if incompatibleAuthOpts {
56+ return nil , errors .New ("incompatible options: WithOptOutOfAdvancedConnectionCheck " +
57+ "cannot be used with WithIAMAuthN" )
58+ }
59+ incompatibleAuthOpts = d .credentialsFile != "" && d .credentialsJSON != nil
60+ if incompatibleAuthOpts {
61+ return nil , errors .New ("incompatible options: WithCredentialsFile " +
62+ "cannot be used with WithCredentialsJSON" )
63+ }
64+ incompatibleAuthOpts = d .credentialsFile != "" && d .tokenSource != nil
65+ if incompatibleAuthOpts {
66+ return nil , errors .New ("incompatible options: WithCredentialsFile " +
67+ "cannot be used with WithTokenSource" )
68+ }
69+ incompatibleAuthOpts = d .credentialsJSON != nil && d .tokenSource != nil
70+ if incompatibleAuthOpts {
71+ return nil , errors .New ("incompatible options: WithCredentialsJSON " +
72+ "cannot be used with WithTokenSource" )
73+ }
74+
75+ switch {
76+ case d .credentialsFile != "" :
77+ b , err := os .ReadFile (d .credentialsFile )
78+ if err != nil {
79+ return nil , errtype .NewConfigError (err .Error (), "n/a" )
80+ }
81+ // TODO: Use AlloyDB-specfic scope
82+ c , err := google .CredentialsFromJSON (context .Background (), b , CloudPlatformScope )
83+ if err != nil {
84+ return nil , errtype .NewConfigError (err .Error (), "n/a" )
85+ }
86+ d .tokenSource = c .TokenSource
87+ d .clientOpts = append (d .clientOpts , option .WithCredentials (c ))
88+ case d .credentialsJSON != nil :
89+ // TODO: Use AlloyDB-specfic scope
90+ c , err := google .CredentialsFromJSON (context .Background (), d .credentialsJSON , CloudPlatformScope )
91+ if err != nil {
92+ return nil , errtype .NewConfigError (err .Error (), "n/a" )
93+ }
94+ d .tokenSource = c .TokenSource
95+ d .clientOpts = append (d .clientOpts , option .WithCredentials (c ))
96+ case d .tokenSource != nil :
97+ d .clientOpts = append (d .clientOpts , option .WithTokenSource (d .tokenSource ))
98+ default :
99+ // If a credentials file, credentials JSON, or a token soruce was not provided,
100+ // default to Application Default Credentials.
101+ var err error
102+ ts , err := google .DefaultTokenSource (ctx , CloudPlatformScope )
103+ if err != nil {
104+ return nil , err
105+ }
106+ d .tokenSource = ts
107+ }
108+
109+ if d .httpClient != nil {
110+ d .clientOpts = append (d .clientOpts , option .WithHTTPClient (d .httpClient ))
111+ }
112+
113+ if d .adminAPIEndpoint != "" {
114+ d .alloydbClientOpts = append (d .alloydbClientOpts , option .WithEndpoint (d .adminAPIEndpoint ))
115+ }
116+
117+ userAgent := strings .Join (d .userAgents , " " )
118+ // Add user agent to the end to make sure it's not overridden.
119+ d .clientOpts = append (d .clientOpts , option .WithUserAgent (userAgent ))
120+
121+ return d , nil
122+ }
123+
40124type dialerConfig struct {
41125 rsaKey * rsa.PrivateKey
42126 // alloydbClientOpts are options to configure only the AlloyDB Rest API
43127 // client. Configuration that should apply to all Google Cloud API clients
44128 // should be included in clientOpts.
45- alloydbClientOpts []apiopt .ClientOption
129+ alloydbClientOpts []option .ClientOption
46130 // clientOpts are options to configure any Google Cloud API client. They
47131 // should not include any AlloyDB-specific configuration.
48- clientOpts []apiopt.ClientOption
49- dialOpts []DialOption
50- dialFunc func (ctx context.Context , network , addr string ) (net.Conn , error )
51- refreshTimeout time.Duration
52- tokenSource oauth2.TokenSource
53- userAgents []string
54- useIAMAuthN bool
55- logger debug.ContextLogger
56- lazyRefresh bool
132+ clientOpts []option.ClientOption
133+ dialOpts []DialOption
134+ dialFunc func (ctx context.Context , network , addr string ) (net.Conn , error )
135+ refreshTimeout time.Duration
136+ userAgents []string
137+ useIAMAuthN bool
138+ logger debug.ContextLogger
139+ lazyRefresh bool
140+ adminAPIEndpoint string
141+
142+ tokenSource oauth2.TokenSource
143+ credentialsFile string
144+ credentialsJSON []byte
145+ httpClient * http.Client
57146
58147 // disableMetadataExchange is a temporary addition and will be removed in
59148 // future versions.
@@ -62,8 +151,6 @@ type dialerConfig struct {
62151 disableBuiltInTelemetry bool
63152
64153 staticConnInfo io.Reader
65- // err tracks any dialer options that may have failed.
66- err error
67154}
68155
69156// WithOptions turns a list of Option's into a single Option.
@@ -80,28 +167,15 @@ func WithOptions(opts ...Option) Option {
80167// authentication.
81168func WithCredentialsFile (filename string ) Option {
82169 return func (d * dialerConfig ) {
83- b , err := os .ReadFile (filename )
84- if err != nil {
85- d .err = errtype .NewConfigError (err .Error (), "n/a" )
86- return
87- }
88- opt := WithCredentialsJSON (b )
89- opt (d )
170+ d .credentialsFile = filename
90171 }
91172}
92173
93174// WithCredentialsJSON returns an Option that specifies a service account
94175// or refresh token JSON credentials to be used as the basis for authentication.
95176func WithCredentialsJSON (b []byte ) Option {
96177 return func (d * dialerConfig ) {
97- // TODO: Use AlloyDB-specfic scope
98- c , err := google .CredentialsFromJSON (context .Background (), b , CloudPlatformScope )
99- if err != nil {
100- d .err = errtype .NewConfigError (err .Error (), "n/a" )
101- return
102- }
103- d .tokenSource = c .TokenSource
104- d .clientOpts = append (d .clientOpts , apiopt .WithCredentials (c ))
178+ d .credentialsJSON = b
105179 }
106180}
107181
@@ -125,7 +199,6 @@ func WithDefaultDialOptions(opts ...DialOption) Option {
125199func WithTokenSource (s oauth2.TokenSource ) Option {
126200 return func (d * dialerConfig ) {
127201 d .tokenSource = s
128- d .clientOpts = append (d .clientOpts , apiopt .WithTokenSource (s ))
129202 }
130203}
131204
@@ -150,15 +223,15 @@ func WithRefreshTimeout(t time.Duration) Option {
150223// advanced use-cases.
151224func WithHTTPClient (client * http.Client ) Option {
152225 return func (d * dialerConfig ) {
153- d .clientOpts = append ( d . clientOpts , apiopt . WithHTTPClient ( client ))
226+ d .httpClient = client
154227 }
155228}
156229
157230// WithAdminAPIEndpoint configures the underlying AlloyDB Admin API client to
158231// use the provided URL.
159232func WithAdminAPIEndpoint (url string ) Option {
160233 return func (d * dialerConfig ) {
161- d .alloydbClientOpts = append ( d . alloydbClientOpts , apiopt . WithEndpoint ( url ))
234+ d .adminAPIEndpoint = url
162235 }
163236}
164237
0 commit comments