@@ -168,52 +168,56 @@ func dial(ctx context.Context, insecure bool, o *internal.DialSettings) (*grpc.C
168168 // when dialing an insecure connection?
169169 if ! o .NoAuth && ! insecure {
170170 if o .APIKey != "" {
171- log .Print ("API keys are not supported for gRPC APIs. Remove the WithAPIKey option from your client-creating call." )
172- }
173- creds , err := internal .Creds (ctx , o )
174- if err != nil {
175- return nil , err
176- }
177-
178- grpcOpts = append (grpcOpts ,
179- grpc .WithPerRPCCredentials (grpcTokenSource {
180- TokenSource : oauth.TokenSource {creds .TokenSource },
181- quotaProject : internal .GetQuotaProject (creds , o .QuotaProject ),
171+ grpcOpts = append (grpcOpts , grpc .WithPerRPCCredentials (grpcAPIKey {
172+ apiKey : o .APIKey ,
182173 requestReason : o .RequestReason ,
183- }),
184- )
185-
186- // Attempt Direct Path:
187- logRateLimiter .Do (func () {
188- logDirectPathMisconfig (endpoint , creds .TokenSource , o )
189- })
190- if isDirectPathEnabled (endpoint , o ) && isTokenSourceDirectPathCompatible (creds .TokenSource , o ) && metadata .OnGCE () {
191- // Overwrite all of the previously specific DialOptions, DirectPath uses its own set of credentials and certificates.
192- grpcOpts = []grpc.DialOption {
193- grpc .WithCredentialsBundle (grpcgoogle .NewDefaultCredentialsWithOptions (grpcgoogle.DefaultCredentialsOptions {oauth.TokenSource {creds .TokenSource }}))}
194- if timeoutDialerOption != nil {
195- grpcOpts = append (grpcOpts , timeoutDialerOption )
174+ }))
175+ } else {
176+ creds , err := internal .Creds (ctx , o )
177+ if err != nil {
178+ return nil , err
196179 }
197- // Check if google-c2p resolver is enabled for DirectPath
198- if isDirectPathXdsUsed (o ) {
199- // google-c2p resolver target must not have a port number
200- if addr , _ , err := net .SplitHostPort (endpoint ); err == nil {
201- endpoint = "google-c2p:///" + addr
202- } else {
203- endpoint = "google-c2p:///" + endpoint
180+ grpcOpts = append (grpcOpts , grpc .WithPerRPCCredentials (grpcTokenSource {
181+ TokenSource : oauth.TokenSource {TokenSource : creds .TokenSource },
182+ quotaProject : internal .GetQuotaProject (creds , o .QuotaProject ),
183+ requestReason : o .RequestReason ,
184+ }))
185+ // Attempt Direct Path:
186+ logRateLimiter .Do (func () {
187+ logDirectPathMisconfig (endpoint , creds .TokenSource , o )
188+ })
189+ if isDirectPathEnabled (endpoint , o ) && isTokenSourceDirectPathCompatible (creds .TokenSource , o ) && metadata .OnGCE () {
190+ // Overwrite all of the previously specific DialOptions, DirectPath uses its own set of credentials and certificates.
191+ grpcOpts = []grpc.DialOption {
192+ grpc .WithCredentialsBundle (grpcgoogle .NewDefaultCredentialsWithOptions (
193+ grpcgoogle.DefaultCredentialsOptions {
194+ PerRPCCreds : oauth.TokenSource {TokenSource : creds .TokenSource },
195+ })),
204196 }
205- } else {
206- if ! strings .HasPrefix (endpoint , "dns:///" ) {
207- endpoint = "dns:///" + endpoint
197+ if timeoutDialerOption != nil {
198+ grpcOpts = append (grpcOpts , timeoutDialerOption )
208199 }
209- grpcOpts = append (grpcOpts ,
210- // For now all DirectPath go clients will be using the following lb config, but in future
211- // when different services need different configs, then we should change this to a
212- // per-service config.
213- grpc .WithDisableServiceConfig (),
214- grpc .WithDefaultServiceConfig (`{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}` ))
200+ // Check if google-c2p resolver is enabled for DirectPath
201+ if isDirectPathXdsUsed (o ) {
202+ // google-c2p resolver target must not have a port number
203+ if addr , _ , err := net .SplitHostPort (endpoint ); err == nil {
204+ endpoint = "google-c2p:///" + addr
205+ } else {
206+ endpoint = "google-c2p:///" + endpoint
207+ }
208+ } else {
209+ if ! strings .HasPrefix (endpoint , "dns:///" ) {
210+ endpoint = "dns:///" + endpoint
211+ }
212+ grpcOpts = append (grpcOpts ,
213+ // For now all DirectPath go clients will be using the following lb config, but in future
214+ // when different services need different configs, then we should change this to a
215+ // per-service config.
216+ grpc .WithDisableServiceConfig (),
217+ grpc .WithDefaultServiceConfig (`{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}` ))
218+ }
219+ // TODO(cbro): add support for system parameters (quota project, request reason) via chained interceptor.
215220 }
216- // TODO(cbro): add support for system parameters (quota project, request reason) via chained interceptor.
217221 }
218222 }
219223
@@ -271,6 +275,31 @@ func (ts grpcTokenSource) GetRequestMetadata(ctx context.Context, uri ...string)
271275 return metadata , nil
272276}
273277
278+ // grpcAPIKey supplies PerRPCCredentials from an API Key.
279+ type grpcAPIKey struct {
280+ apiKey string
281+
282+ // Additional metadata attached as headers.
283+ requestReason string
284+ }
285+
286+ // GetRequestMetadata gets the request metadata as a map from a grpcAPIKey.
287+ func (ts grpcAPIKey ) GetRequestMetadata (ctx context.Context , uri ... string ) (
288+ map [string ]string , error ) {
289+ metadata := map [string ]string {
290+ "X-goog-api-key" : ts .apiKey ,
291+ }
292+ if ts .requestReason != "" {
293+ metadata ["X-goog-request-reason" ] = ts .requestReason
294+ }
295+ return metadata , nil
296+ }
297+
298+ // RequireTransportSecurity indicates whether the credentials requires transport security.
299+ func (ts grpcAPIKey ) RequireTransportSecurity () bool {
300+ return true
301+ }
302+
274303func isDirectPathEnabled (endpoint string , o * internal.DialSettings ) bool {
275304 if ! o .EnableDirectPath {
276305 return false
0 commit comments