Skip to content

Commit 07139c8

Browse files
Add support for service account in functions.runWith (#770)
* Add support for service account in `functions.runWith` * Add email validation and email generation by project id * Changing to serviceAccount, and adding default as an option * adds a test case for default * refactoring to checxk for @ at the end of service account, and throw erros earlier when service account is set to something invalid * gets rid of repeated @ for generated service account emails Co-authored-by: joehan <[email protected]>
1 parent 9055e0f commit 07139c8

File tree

4 files changed

+89
-3
lines changed

4 files changed

+89
-3
lines changed

spec/function-builder.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,52 @@ describe('FunctionBuilder', () => {
238238
)}`
239239
);
240240
});
241+
242+
it('should allow a serviceAccount to be set as-is', () => {
243+
const serviceAccount = '[email protected]';
244+
const fn = functions
245+
.runWith({
246+
serviceAccount,
247+
})
248+
.auth.user()
249+
.onCreate((user) => user);
250+
251+
expect(fn.__trigger.serviceAccountEmail).to.equal(serviceAccount);
252+
});
253+
254+
it('should allow a serviceAccount to be set with generated service account email', () => {
255+
const serviceAccount = 'test-service-account@';
256+
const projectId = process.env.GCLOUD_PROJECT;
257+
const fn = functions
258+
.runWith({
259+
serviceAccount,
260+
})
261+
.auth.user()
262+
.onCreate((user) => user);
263+
264+
expect(fn.__trigger.serviceAccountEmail).to.equal(
265+
`test-service-account@${projectId}.iam.gserviceaccount.com`
266+
);
267+
});
268+
269+
it('should not set a serviceAccountEmail if service account is set to `default`', () => {
270+
const serviceAccount = 'default';
271+
const fn = functions
272+
.runWith({
273+
serviceAccount,
274+
})
275+
.auth.user()
276+
.onCreate((user) => user);
277+
278+
expect(fn.__trigger.serviceAccountEmail).to.be.undefined;
279+
});
280+
281+
it('should throw an error if serviceAccount is set to an invalid value', () => {
282+
const serviceAccount = 'test-service-account';
283+
expect(() => {
284+
functions.runWith({
285+
serviceAccount,
286+
});
287+
}).to.throw();
288+
});
241289
});

src/cloud-functions.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ export interface TriggerAnnotated {
272272
timeout?: string;
273273
vpcConnector?: string;
274274
vpcConnectorEgressSettings?: string;
275+
serviceAccountEmail?: string;
275276
};
276277
}
277278

@@ -525,5 +526,24 @@ export function optionsToTrigger(options: DeploymentOptions) {
525526
trigger.vpcConnectorEgressSettings = options.vpcConnectorEgressSettings;
526527
}
527528

529+
if (options.serviceAccount) {
530+
if (options.serviceAccount === 'default') {
531+
// Do nothing, since this is equivalent to not setting serviceAccount.
532+
} else if (options.serviceAccount.endsWith('@')) {
533+
if (!process.env.GCLOUD_PROJECT) {
534+
throw new Error(
535+
`Unable to determine email for service account '${options.serviceAccount}' because process.env.GCLOUD_PROJECT is not set.`
536+
);
537+
}
538+
trigger.serviceAccountEmail = `${options.serviceAccount}${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`;
539+
} else if (options.serviceAccount.includes('@')) {
540+
trigger.serviceAccountEmail = options.serviceAccount;
541+
} else {
542+
throw new Error(
543+
`Invalid option for serviceAccount: '${options.serviceAccount}'. Valid options are 'default', a service account email, or '{serviceAccountName}@'`
544+
);
545+
}
546+
}
547+
528548
return trigger;
529549
}

src/function-builder.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean {
9999
}
100100
}
101101
}
102+
103+
if (
104+
runtimeOptions.serviceAccount &&
105+
runtimeOptions.serviceAccount !== 'default' &&
106+
!_.includes(runtimeOptions.serviceAccount, '@')
107+
) {
108+
throw new Error(
109+
`serviceAccount must be set to 'default', a service account email, or '{serviceAccountName}@'`
110+
);
111+
}
102112
return true;
103113
}
104114

@@ -139,9 +149,12 @@ export function region(
139149
* 0 to 540.
140150
* 3. `failurePolicy`: failure policy of the function, with boolean `true` being
141151
* equivalent to providing an empty retry object.
142-
* 4. `vpcConnector`: id of a VPC connector in the same project and region
143-
* 5. `vpcConnectorEgressSettings`: when a `vpcConnector` is set, control which
144-
* egress traffic is sent through the `vpcConnector`.
152+
* 4. `vpcConnector`: id of a VPC connector in same project and region.
153+
* 5. `vpcConnectorEgressSettings`: when a vpcConnector is set, control which
154+
* egress traffic is sent through the vpcConnector.
155+
* 6. `serviceAccount`: Specific service account for the function.
156+
* 7. `ingressSettings`: ingress settings for the function, which control where a HTTPS
157+
* function can be called from.
145158
*
146159
* Value must not be null.
147160
*/

src/function-configuration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ export interface RuntimeOptions {
122122
*/
123123
vpcConnectorEgressSettings?: typeof VPC_EGRESS_SETTINGS_OPTIONS[number];
124124

125+
/**
126+
* Specific service account for the function to run as
127+
*/
128+
serviceAccount?: 'default' | string;
129+
125130
/**
126131
* Ingress settings
127132
*/

0 commit comments

Comments
 (0)