Skip to content

Commit e30a646

Browse files
Making onCall Dart functions Public when deployed (#10390)
* Making Http and Callable Dart functions Public when deployed
1 parent 747b829 commit e30a646

2 files changed

Lines changed: 140 additions & 20 deletions

File tree

src/deploy/functions/release/fabricator.spec.ts

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,16 @@ describe("Fabricator", () => {
910910
expect(run.setInvokerUpdate).to.have.been.calledWith(ep.project, "service", ["custom@"]);
911911
});
912912

913+
it("sets invoker to private on Node updates when explicitly configured as private", async () => {
914+
gcfv2.updateFunction.resolves({ name: "op", done: false });
915+
poller.pollOperation.resolves({ serviceConfig: { service: "service" } });
916+
run.setInvokerUpdate.resolves();
917+
const ep = endpoint({ httpsTrigger: { invoker: ["private"] } }, { platform: "gcfv2" });
918+
919+
await fab.updateV2Function(ep, new scraper.SourceTokenScraper());
920+
expect(run.setInvokerUpdate).to.have.been.calledWith(ep.project, "service", ["private"]);
921+
});
922+
913923
it("sets explicit invoker on dataConnectGraphqlTrigger", async () => {
914924
gcfv2.updateFunction.resolves({ name: "op", done: false });
915925
poller.pollOperation.resolves({ serviceConfig: { service: "service" } });
@@ -974,6 +984,16 @@ describe("Fabricator", () => {
974984
expect(run.setInvokerUpdate).to.not.have.been.called;
975985
});
976986

987+
it("updates invoker to public on Node updates when explicitly null", async () => {
988+
gcfv2.updateFunction.resolves({ name: "op", done: false });
989+
poller.pollOperation.resolves({ serviceConfig: { service: "service" } });
990+
run.setInvokerUpdate.resolves();
991+
const ep = endpoint({ httpsTrigger: { invoker: null } }, { platform: "gcfv2" });
992+
993+
await fab.updateV2Function(ep, new scraper.SourceTokenScraper());
994+
expect(run.setInvokerUpdate).to.have.been.calledWith(ep.project, "service", ["public"]);
995+
});
996+
977997
it("doesn't set invoker on non-http functions", async () => {
978998
gcfv2.updateFunction.resolves({ name: "op", done: false });
979999
poller.pollOperation.resolves({ serviceConfig: { service: "service" } });
@@ -1767,7 +1787,7 @@ describe("Fabricator", () => {
17671787
describe("createRunFunction", () => {
17681788
it("creates a Cloud Run service with correct configuration", async () => {
17691789
runv2.createService.resolves({ uri: "https://service", name: "service" } as any);
1770-
run.setInvokerUpdate.resolves();
1790+
run.setInvokerCreate.resolves();
17711791

17721792
const ep = endpoint(
17731793
{ httpsTrigger: {} },
@@ -1805,6 +1825,44 @@ describe("Fabricator", () => {
18051825
}),
18061826
);
18071827
});
1828+
1829+
it("always sets callable triggers to public on creation", async () => {
1830+
runv2.createService.resolves({ uri: "https://service", name: "service" } as any);
1831+
run.setInvokerCreate.resolves();
1832+
1833+
const ep = endpoint(
1834+
{ callableTrigger: {} },
1835+
{
1836+
platform: "run",
1837+
baseImageUri: "gcr.io/base",
1838+
command: ["cmd"],
1839+
args: ["arg"],
1840+
},
1841+
);
1842+
await fab.createRunFunction(ep);
1843+
1844+
expect(run.setInvokerCreate).to.have.been.calledWith(ep.project, sinon.match.string, [
1845+
"public",
1846+
]);
1847+
});
1848+
1849+
it("does not set invoker on creation when HTTPS configuration is private", async () => {
1850+
runv2.createService.resolves({ uri: "https://service", name: "service" } as any);
1851+
run.setInvokerCreate.resolves();
1852+
1853+
const ep = endpoint(
1854+
{ httpsTrigger: { invoker: ["private"] } },
1855+
{
1856+
platform: "run",
1857+
baseImageUri: "gcr.io/base",
1858+
command: ["cmd"],
1859+
args: ["arg"],
1860+
},
1861+
);
1862+
await fab.createRunFunction(ep);
1863+
1864+
expect(run.setInvokerCreate).to.not.have.been.called;
1865+
});
18081866
});
18091867

18101868
describe("updateRunFunction", () => {
@@ -1839,6 +1897,58 @@ describe("Fabricator", () => {
18391897
}),
18401898
);
18411899
});
1900+
1901+
it("does not update invoker for callable functions", async () => {
1902+
runv2.updateService.resolves({ uri: "https://service", name: "service" } as any);
1903+
run.setInvokerUpdate.resolves();
1904+
1905+
const ep = endpoint({ callableTrigger: {} }, { platform: "run" });
1906+
const update = { endpoint: ep };
1907+
1908+
await fab.updateRunFunction(update);
1909+
1910+
expect(run.setInvokerUpdate).to.not.have.been.called;
1911+
});
1912+
1913+
it("updates invoker to public for HTTPS functions when explicitly null", async () => {
1914+
runv2.updateService.resolves({ uri: "https://service", name: "service" } as any);
1915+
run.setInvokerUpdate.resolves();
1916+
1917+
const ep = endpoint({ httpsTrigger: { invoker: null } }, { platform: "run" });
1918+
const update = { endpoint: ep };
1919+
1920+
await fab.updateRunFunction(update);
1921+
1922+
expect(run.setInvokerUpdate).to.have.been.calledWith(ep.project, sinon.match.string, [
1923+
"public",
1924+
]);
1925+
});
1926+
1927+
it("does not update invoker for HTTPS functions when invoker is omitted (undefined)", async () => {
1928+
runv2.updateService.resolves({ uri: "https://service", name: "service" } as any);
1929+
run.setInvokerUpdate.resolves();
1930+
1931+
const ep = endpoint({ httpsTrigger: {} }, { platform: "run" });
1932+
const update = { endpoint: ep };
1933+
1934+
await fab.updateRunFunction(update);
1935+
1936+
expect(run.setInvokerUpdate).to.not.have.been.called;
1937+
});
1938+
1939+
it("updates invoker for HTTPS functions to private when explicitly configured as private", async () => {
1940+
runv2.updateService.resolves({ uri: "https://service", name: "service" } as any);
1941+
run.setInvokerUpdate.resolves();
1942+
1943+
const ep = endpoint({ httpsTrigger: { invoker: ["private"] } }, { platform: "run" });
1944+
const update = { endpoint: ep };
1945+
1946+
await fab.updateRunFunction(update);
1947+
1948+
expect(run.setInvokerUpdate).to.have.been.calledWith(ep.project, sinon.match.string, [
1949+
"private",
1950+
]);
1951+
});
18421952
});
18431953

18441954
describe("deleteRunFunction", () => {

src/deploy/functions/release/fabricator.ts

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,8 @@ export class Fabricator {
662662
.catch(rethrowAs(endpoint, "delete"));
663663
}
664664

665+
// We use createRunFunction and updateRunFunction specifically to deploy Dart functions
666+
// directly over to Cloud Run! This avoids cluttering normal Gen 2 Node function hooks.
665667
async createRunFunction(endpoint: backend.Endpoint): Promise<void> {
666668
const storageSource = this.sources[endpoint.codebase!]?.storage;
667669
if (!storageSource) {
@@ -692,7 +694,20 @@ export class Fabricator {
692694
})
693695
.catch(rethrowAs(endpoint, "create"));
694696

695-
await this.setInvoker(endpoint);
697+
const serviceName = `projects/${endpoint.project}/locations/${endpoint.region}/services/${endpoint.runServiceId}`;
698+
if (backend.isHttpsTriggered(endpoint)) {
699+
const invoker = endpoint.httpsTrigger.invoker || ["public"];
700+
if (!invoker.includes("private")) {
701+
await this.executor
702+
.run(() => run.setInvokerCreate(endpoint.project, serviceName, invoker))
703+
.catch(rethrowAs(endpoint, "set invoker"));
704+
}
705+
} else if (backend.isCallableTriggered(endpoint)) {
706+
// Callable functions should always be public
707+
await this.executor
708+
.run(() => run.setInvokerCreate(endpoint.project, serviceName, ["public"]))
709+
.catch(rethrowAs(endpoint, "set invoker"));
710+
}
696711
}
697712

698713
async updateRunFunction(update: planner.EndpointUpdate): Promise<void> {
@@ -722,7 +737,19 @@ export class Fabricator {
722737
})
723738
.catch(rethrowAs(endpoint, "update"));
724739

725-
await this.setInvoker(endpoint);
740+
const serviceName = `projects/${endpoint.project}/locations/${endpoint.region}/services/${endpoint.runServiceId}`;
741+
// We check for null vs undefined to respect settings people make on the Google Console.
742+
// If it's omitted (undefined), we don't touch policies. If it is explicitly null, we make it public.
743+
let invoker: string[] | undefined;
744+
if (backend.isHttpsTriggered(endpoint)) {
745+
invoker = endpoint.httpsTrigger.invoker === null ? ["public"] : endpoint.httpsTrigger.invoker;
746+
}
747+
748+
if (invoker) {
749+
await this.executor
750+
.run(() => run.setInvokerUpdate(endpoint.project, serviceName, invoker!))
751+
.catch(rethrowAs(endpoint, "set invoker"));
752+
}
726753
}
727754

728755
async deleteRunFunction(endpoint: backend.Endpoint): Promise<void> {
@@ -740,23 +767,6 @@ export class Fabricator {
740767
.catch(rethrowAs(endpoint, "delete"));
741768
}
742769

743-
async setInvoker(endpoint: backend.Endpoint): Promise<void> {
744-
if (backend.isHttpsTriggered(endpoint)) {
745-
const invoker = endpoint.httpsTrigger.invoker || ["public"];
746-
if (!invoker.includes("private")) {
747-
await this.executor
748-
.run(() =>
749-
run.setInvokerUpdate(
750-
endpoint.project,
751-
`projects/${endpoint.project}/locations/${endpoint.region}/services/${endpoint.runServiceId}`,
752-
invoker,
753-
),
754-
)
755-
.catch(rethrowAs(endpoint, "set invoker"));
756-
}
757-
}
758-
}
759-
760770
async setRunTraits(serviceName: string, endpoint: backend.Endpoint): Promise<void> {
761771
await this.functionExecutor
762772
.run(async () => {

0 commit comments

Comments
 (0)