1616
1717import { presence } from '@google-github-actions/actions-utils' ;
1818import { GoogleAuth } from 'google-auth-library' ;
19- import { Headers } from 'gaxios' ;
19+ import { Headers , GaxiosOptions } from 'gaxios' ;
2020import YAML from 'yaml' ;
2121
2222// Do not listen to the linter - this can NOT be rewritten as an ES6 import statement.
@@ -45,6 +45,16 @@ type ClientOptions = {
4545 projectID ?: string ;
4646 quotaProjectID ?: string ;
4747 location ?: string ;
48+ logger ?: Logger ;
49+ } ;
50+
51+ /**
52+ * Logger is the passed in logger on the client.
53+ */
54+ type Logger = {
55+ debug : ( message : string ) => void ; // eslint-disable-line no-unused-vars
56+ info : ( message : string ) => void ; // eslint-disable-line no-unused-vars
57+ warn : ( message : string ) => void ; // eslint-disable-line no-unused-vars
4858} ;
4959
5060/**
@@ -130,9 +140,11 @@ export class ClusterClient {
130140 * name is given (e.g. `c`), these values will be used to construct the full
131141 * resource name.
132142 */
143+ readonly #logger?: Logger ;
133144 readonly #projectID?: string ;
134145 readonly #quotaProjectID?: string ;
135146 readonly #location?: string ;
147+ readonly #headers?: Headers ;
136148
137149 readonly defaultEndpoint = 'https://container.googleapis.com/v1' ;
138150 readonly hubEndpoint = 'https://gkehub.googleapis.com/v1' ;
@@ -141,6 +153,8 @@ export class ClusterClient {
141153 readonly auth : GoogleAuth ;
142154
143155 constructor ( opts ?: ClientOptions ) {
156+ this . #logger = opts ?. logger ;
157+
144158 this . auth = new GoogleAuth ( {
145159 scopes : [
146160 'https://www.googleapis.com/auth/cloud-platform' ,
@@ -152,6 +166,13 @@ export class ClusterClient {
152166 this . #projectID = opts ?. projectID ;
153167 this . #quotaProjectID = opts ?. quotaProjectID ;
154168 this . #location = opts ?. location ;
169+
170+ this . #headers = {
171+ 'User-Agent' : userAgent ,
172+ } ;
173+ if ( this . #quotaProjectID) {
174+ this . #headers[ 'X-Goog-User-Project' ] = this . #quotaProjectID;
175+ }
155176 }
156177
157178 /**
@@ -160,13 +181,38 @@ export class ClusterClient {
160181 * @returns string
161182 */
162183 async getToken ( ) : Promise < string > {
184+ this . #logger?. debug ( `Getting token` ) ;
185+
163186 const token = await this . auth . getAccessToken ( ) ;
164187 if ( ! token ) {
165188 throw new Error ( 'Failed to generate token.' ) ;
166189 }
167190 return token ;
168191 }
169192
193+ /**
194+ * request is a wrapper around an authenticated request.
195+ *
196+ * @returns T
197+ */
198+ async request < T > ( opts : GaxiosOptions ) : Promise < T > {
199+ this . #logger?. debug ( `Initiating request with options: ${ JSON . stringify ( opts ) } ` ) ;
200+
201+ const mergedOpts : GaxiosOptions = {
202+ ...{
203+ retry : true ,
204+ headers : this . #headers,
205+ errorRedactor : false ,
206+ } ,
207+ ...opts ,
208+ } ;
209+
210+ this . #logger?. debug ( ` Request options: ${ JSON . stringify ( mergedOpts ) } ` ) ;
211+
212+ const resp = await this . auth . request < T > ( mergedOpts ) ;
213+ return resp . data ;
214+ }
215+
170216 /**
171217 * Generates full resource name.
172218 *
@@ -209,14 +255,15 @@ export class ClusterClient {
209255 * @returns project number.
210256 */
211257 async projectIDtoNum ( projectID : string ) : Promise < string > {
258+ this . #logger?. debug ( `Converting project ID '${ projectID } ' to a project number` ) ;
259+
212260 const url = `${ this . cloudResourceManagerEndpoint } /projects/${ projectID } ` ;
213- const resp = ( await this . auth . request ( {
261+ const resp = await this . request < { name : string } > ( {
214262 url : url ,
215- headers : this . #defaultHeaders( ) ,
216- } ) ) as { data : { name : string } } ;
263+ } ) ;
217264
218265 // projectRef of form projects/<project-num>"
219- const projectRef = resp . data ?. name ;
266+ const projectRef = resp . name ;
220267 const projectNum = projectRef . replace ( 'projects/' , '' ) ;
221268 if ( ! projectRef . includes ( 'projects/' ) || ! projectNum ) {
222269 throw new Error (
@@ -234,15 +281,15 @@ export class ClusterClient {
234281 * @returns endpoint.
235282 */
236283 async getConnectGWEndpoint ( name : string ) : Promise < string > {
284+ this . #logger?. debug ( `Getting connect gateway endpoint for '${ name } '` ) ;
285+
237286 const membershipURL = `${ this . hubEndpoint } /${ name } ` ;
238- const resp = ( await this . auth . request ( {
287+ const membership = await this . request < HubMembershipResponse > ( {
239288 url : membershipURL ,
240- headers : this . #defaultHeaders( ) ,
241- } ) ) as HubMembershipResponse ;
289+ } ) ;
242290
243- const membership = resp . data ;
244291 if ( ! membership ) {
245- throw new Error ( `Failed to lookup membership: ${ resp } ` ) ;
292+ throw new Error ( `Failed to lookup membership: ${ name } ` ) ;
246293 }
247294
248295 // For GKE clusters, the configuration path is gkeMemberships, not
@@ -269,6 +316,8 @@ export class ClusterClient {
269316 * @returns Fleet membership name.
270317 */
271318 async discoverClusterMembership ( clusterName : string ) : Promise < string > {
319+ this . #logger?. debug ( `Discovering cluster membership for '${ clusterName } '` ) ;
320+
272321 const clusterResourceLink = `//container.googleapis.com/${ this . getResource ( clusterName ) } ` ;
273322 const projectID = this . #projectID;
274323 if ( ! projectID ) {
@@ -278,12 +327,11 @@ export class ClusterClient {
278327 }
279328
280329 const url = `${ this . hubEndpoint } /projects/${ projectID } /locations/global/memberships?filter=endpoint.gkeCluster.resourceLink="${ clusterResourceLink } "` ;
281- const resp = ( await this . auth . request ( {
330+ const resp = await this . request < HubMembershipsResponse > ( {
282331 url : url ,
283- headers : this . #defaultHeaders( ) ,
284- } ) ) as HubMembershipsResponse ;
332+ } ) ;
285333
286- const memberships = resp . data . resources ;
334+ const memberships = resp . resources ;
287335 if ( ! memberships || memberships . length < 1 ) {
288336 throw new Error (
289337 `Expected one membership for ${ clusterName } in ${ projectID } . ` +
@@ -309,11 +357,12 @@ export class ClusterClient {
309357 * @returns a Cluster object.
310358 */
311359 async getCluster ( clusterName : string ) : Promise < ClusterResponse > {
360+ this . #logger?. debug ( `Getting information about cluster '${ clusterName } '` ) ;
361+
312362 const url = `${ this . defaultEndpoint } /${ this . getResource ( clusterName ) } ` ;
313- const resp = ( await this . auth . request ( {
363+ const resp = await this . request < ClusterResponse > ( {
314364 url : url ,
315- headers : this . #defaultHeaders( ) ,
316- } ) ) as ClusterResponse ;
365+ } ) ;
317366 return resp ;
318367 }
319368
@@ -323,17 +372,19 @@ export class ClusterClient {
323372 * @param opts Input options. See CreateKubeConfigOptions.
324373 */
325374 async createKubeConfig ( opts : CreateKubeConfigOptions ) : Promise < string > {
375+ this . #logger?. debug ( `Creating kubeconfig with options: ${ JSON . stringify ( opts ) } ` ) ;
376+
326377 const cluster = opts . clusterData ;
327- let endpoint = cluster . data . endpoint ;
378+ let endpoint = cluster . endpoint ;
328379 const connectGatewayEndpoint = presence ( opts . connectGWEndpoint ) ;
329380 if ( connectGatewayEndpoint ) {
330381 endpoint = connectGatewayEndpoint ;
331382 }
332383 if ( opts . useInternalIP ) {
333- endpoint = cluster . data . privateClusterConfig . privateEndpoint ;
384+ endpoint = cluster . privateClusterConfig . privateEndpoint ;
334385 }
335386 if ( opts . useDNSBasedEndpoint ) {
336- endpoint = cluster . data . controlPlaneEndpointsConfig . dnsEndpointConfig . endpoint ;
387+ endpoint = cluster . controlPlaneEndpointsConfig . dnsEndpointConfig . endpoint ;
337388 }
338389
339390 // By default, use the CA cert. Even if user doesn't specify
@@ -356,41 +407,30 @@ export class ClusterClient {
356407 {
357408 cluster : {
358409 ...( useCACert && {
359- 'certificate-authority-data' : cluster . data . masterAuth ?. clusterCaCertificate ,
410+ 'certificate-authority-data' : cluster . masterAuth ?. clusterCaCertificate ,
360411 } ) ,
361412 server : `https://${ endpoint } ` ,
362413 } ,
363- name : cluster . data . name ,
414+ name : cluster . name ,
364415 } ,
365416 ] ,
366417 'contexts' : [
367418 {
368419 context : {
369- cluster : cluster . data . name ,
370- user : cluster . data . name ,
420+ cluster : cluster . name ,
421+ user : cluster . name ,
371422 namespace : opts . namespace ,
372423 } ,
373424 name : contextName ,
374425 } ,
375426 ] ,
376427 'kind' : 'Config' ,
377428 'current-context' : contextName ,
378- 'users' : [ { ...{ name : cluster . data . name } , ...auth } ] ,
429+ 'users' : [ { ...{ name : cluster . name } , ...auth } ] ,
379430 } ;
380431
381432 return YAML . stringify ( kubeConfig ) ;
382433 }
383-
384- #defaultHeaders( ) : Headers {
385- const h : Headers = {
386- 'User-Agent' : userAgent ,
387- } ;
388-
389- if ( this . #quotaProjectID) {
390- h [ 'X-Goog-User-Project' ] = this . #quotaProjectID;
391- }
392- return h ;
393- }
394434}
395435
396436type cluster = {
@@ -454,19 +494,17 @@ export type KubeConfig = {
454494} ;
455495
456496export type ClusterResponse = {
457- data : {
458- name : string ;
459- endpoint : string ;
460- masterAuth : {
461- clusterCaCertificate : string ;
462- } ;
463- privateClusterConfig : {
464- privateEndpoint : string ;
465- } ;
466- controlPlaneEndpointsConfig : {
467- dnsEndpointConfig : {
468- endpoint : string ;
469- } ;
497+ name : string ;
498+ endpoint : string ;
499+ masterAuth : {
500+ clusterCaCertificate : string ;
501+ } ;
502+ privateClusterConfig : {
503+ privateEndpoint : string ;
504+ } ;
505+ controlPlaneEndpointsConfig : {
506+ dnsEndpointConfig : {
507+ endpoint : string ;
470508 } ;
471509 } ;
472510} ;
@@ -475,17 +513,13 @@ export type ClusterResponse = {
475513 * HubMembershipsResponse is the response from listing GKE Hub memberships.
476514 */
477515type HubMembershipsResponse = {
478- data : {
479- resources : HubMembership [ ] ;
480- } ;
516+ resources : HubMembership [ ] ;
481517} ;
482518
483519/**
484520 * HubMembershipResponse is the response from getting a GKE Hub membership.
485521 */
486- type HubMembershipResponse = {
487- data : HubMembership ;
488- } ;
522+ type HubMembershipResponse = HubMembership ;
489523
490524/**
491525 * HubMembership is a single HubMembership.
0 commit comments