@google-cloud/cloud-run-mcp 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +10 -6
- package/README.md +26 -0
- package/SECURITY.md +7 -0
- package/cloudbuild.yaml +37 -0
- package/example-sources-to-deploy/java/pom.xml +41 -0
- package/example-sources-to-deploy/java/src/main/java/com/example/helloworld/HelloworldApplication.java +21 -0
- package/example-sources-to-deploy/java/src/main/resources/application.properties +1 -0
- package/example-sources-to-deploy/nodejs/index.js +15 -0
- package/example-sources-to-deploy/nodejs/package.json +12 -0
- package/example-sources-to-deploy/python/pip-project/main.py +11 -0
- package/example-sources-to-deploy/python/pip-project/requirements.txt +2 -0
- package/example-sources-to-deploy/python/pyproject-project/main.py +11 -0
- package/example-sources-to-deploy/python/pyproject-project/pyproject.toml +10 -0
- package/lib/clients.js +142 -12
- package/lib/cloud-api/build.js +192 -28
- package/lib/cloud-api/run.js +62 -0
- package/lib/cloud-api/secrets.js +196 -0
- package/lib/cloud-api/storage.js +164 -16
- package/lib/deployment/compose.js +517 -0
- package/lib/deployment/constants.js +8 -0
- package/lib/deployment/deployer.js +283 -28
- package/lib/deployment/helpers.js +58 -1
- package/lib/deployment/universal-maker.js +21 -148
- package/lib/middleware/oauth.js +26 -15
- package/lib/util/archive.js +3 -3
- package/lib/util/artifacts.js +189 -0
- package/lib/util/helpers.js +109 -0
- package/mcp-server.js +83 -20
- package/package.json +7 -4
- package/skills/cloud-run/SKILL.md +109 -0
- package/test/local/archive.test.js +88 -0
- package/test/local/clients.test.js +307 -2
- package/test/local/cloud-api/build.test.js +330 -25
- package/test/local/cloud-api/run.test.js +129 -0
- package/test/local/cloud-api/secrets.test.js +291 -0
- package/test/local/cloud-api/storage.test.js +405 -0
- package/test/local/compose.test.js +618 -0
- package/test/local/deploy.test.js +408 -0
- package/test/local/deployment-helpers.test.js +100 -1
- package/test/local/mcp-server-streamable-http.test.js +132 -12
- package/test/local/middleware/oauth.test.js +106 -5
- package/test/local/test-utils.js +2 -18
- package/test/local/tools.test.js +43 -1
- package/test/local/universal-maker.test.js +23 -114
- package/test/local/util.test.js +361 -0
- package/test/need-gcp/deployer.test.js +1 -1
- package/test/need-gcp/test-helpers.js +5 -1
- package/test/need-gcp/workflows/deployment-workflows.test.js +172 -6
- package/tools/register-tools.js +98 -78
- /package/example-sources-to-deploy/{Dockerfile → golang/Dockerfile} +0 -0
- /package/example-sources-to-deploy/{go.mod → golang/go.mod} +0 -0
- /package/example-sources-to-deploy/{main.go → golang/main.go} +0 -0
|
@@ -4,19 +4,23 @@ on:
|
|
|
4
4
|
release:
|
|
5
5
|
types: [published]
|
|
6
6
|
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
|
|
7
10
|
jobs:
|
|
8
11
|
publish-npm:
|
|
9
12
|
runs-on: ubuntu-latest
|
|
10
13
|
steps:
|
|
11
|
-
- uses: actions/checkout@
|
|
12
|
-
|
|
14
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09
|
|
15
|
+
with:
|
|
16
|
+
persist-credentials: false
|
|
17
|
+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444
|
|
13
18
|
with:
|
|
14
|
-
node-version:
|
|
19
|
+
node-version: 24
|
|
15
20
|
- run: npm ci
|
|
16
|
-
|
|
17
|
-
- uses: actions/setup-node@v4
|
|
21
|
+
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444
|
|
18
22
|
with:
|
|
19
|
-
node-version:
|
|
23
|
+
node-version: 24
|
|
20
24
|
registry-url: https://wombat-dressing-room.appspot.com/
|
|
21
25
|
- run: npm publish
|
|
22
26
|
env:
|
package/README.md
CHANGED
|
@@ -60,6 +60,8 @@ The Cloud Run MCP server can be configured using the following environment varia
|
|
|
60
60
|
| `SKIP_IAM_CHECK` | Controls whether to check for IAM permissions for a Cloud Run service. Set to `false` to enable checks. This is `true` by default which is a recommended way to make the service public. |
|
|
61
61
|
| `ENABLE_HOST_VALIDATION` | Prevents [DNS Rebinding](https://en.wikipedia.org/wiki/DNS_rebinding) attacks by validating the Host header. This is disabled by default. |
|
|
62
62
|
| `ALLOWED_HOSTS` | Comma-separated list of allowed Host headers (if host validation is enabled). The default value is `localhost,127.0.0.1,::1`. |
|
|
63
|
+
| `OAUTH_ENABLED` | Enables OAuth 2.0 Bearer token verification for incoming HTTP/SSE requests when set to `true`. Disabled by default. |
|
|
64
|
+
| `GOOGLE_OAUTH_AUDIENCE` | Expected OAuth 2.0 token audience (typically your OAuth Client ID). Required when `OAUTH_ENABLED=true`. |
|
|
63
65
|
|
|
64
66
|
## Use as a Gemini CLI extension
|
|
65
67
|
|
|
@@ -274,3 +276,27 @@ Post the configuration changes as shown above, start the Gemini CLI. You should
|
|
|
274
276
|
This will take you to the authentication page on your browser, wherein you need to sign in using the appropriate gmail id, and accept the terms and conditions. Once the authentication is succcessful, you can come back to the Gemini CLI, and the Cloud Run MCP server will be ready to use.
|
|
275
277
|
|
|
276
278
|
The Google Cloud Platform Terms of Service (available at https://cloud.google.com/terms/) and the Data Processing and Security Terms (available at https://cloud.google.com/terms/data-processing-terms) do not apply to any component of the Cloud Run MCP Server software.
|
|
279
|
+
|
|
280
|
+
# Cloud Run Skills
|
|
281
|
+
|
|
282
|
+
We introduce Cloud Run skills to enable AI agents to perform actions on Cloud Run. You can use these skills with Gemini CLI and other agentic AI tools. The skills are available at [Cloud Run Skills](https://github.com/GoogleCloudPlatform/cloud-run-mcp/blob/main/skills/cloud-run/SKILL.md).
|
|
283
|
+
|
|
284
|
+
The Cloud Run skills are based on top of gcloud cli for Cloud Run empowering agents to perform all the actions on the Cloud Run using gcloud, as can be performed by the GCP user using gcloud cli.
|
|
285
|
+
|
|
286
|
+
In order to use Cloud Run skills:
|
|
287
|
+
|
|
288
|
+
1. Ensure you have the `gcloud` CLI installed and authenticated with `gcloud auth login` and `gcloud auth application-default login`.
|
|
289
|
+
2. Set your project with `gcloud config set project [PROJECT_ID]`.
|
|
290
|
+
3. Enable the skills on your agentic AI tool. For example, you can enable the skills for Gemini CLI using the following command on your terminal:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
gemini skills install https://github.com/GoogleCloudPlatform/cloud-run-mcp.git --path skills/cloud-run
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
4. Once the skills are enabled, you can use them to perform actions on Cloud Run. Here are some of the prompts for you to get started:
|
|
297
|
+
|
|
298
|
+
- List the Cloud Run services in the project test-gcp-project in the region us-west1.
|
|
299
|
+
|
|
300
|
+
- Deploy the folder /home/username/workspace/hello-world as Cloud Run service hello-world to the project test-gcp-project in the region us-west1.
|
|
301
|
+
|
|
302
|
+
- Describe the Cloud Run job hello-job in the project test-gcp-project in the region europe-west1.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
To report a security issue, please use [g.co/vulnz](https://g.co/vulnz).
|
|
4
|
+
|
|
5
|
+
The Google Security Team will respond within 5 working days of your report on [g.co/vulnz](https://g.co/vulnz).
|
|
6
|
+
|
|
7
|
+
We use [g.co/vulnz](https://g.co/vulnz) for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue.
|
package/cloudbuild.yaml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright 2026 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# Default cloudbuild.yaml for Cloud Shell custom images.
|
|
16
|
+
# This file is used by the Cloud Build trigger that builds this image.
|
|
17
|
+
|
|
18
|
+
substitutions:
|
|
19
|
+
_IMAGE_TAG: '${COMMIT_SHA}'
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
23
|
+
args:
|
|
24
|
+
[
|
|
25
|
+
'build',
|
|
26
|
+
'-t',
|
|
27
|
+
'us-docker.pkg.dev/${PROJECT_ID}/container/mcp:latest',
|
|
28
|
+
'-t',
|
|
29
|
+
'us-docker.pkg.dev/${PROJECT_ID}/container/mcp:public-image-${_IMAGE_TAG}',
|
|
30
|
+
'.',
|
|
31
|
+
]
|
|
32
|
+
images:
|
|
33
|
+
- 'us-docker.pkg.dev/${PROJECT_ID}/container/mcp:latest'
|
|
34
|
+
- 'us-docker.pkg.dev/${PROJECT_ID}/container/mcp:public-image-${_IMAGE_TAG}'
|
|
35
|
+
|
|
36
|
+
options:
|
|
37
|
+
dynamicSubstitutions: true
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
3
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
4
|
+
<modelVersion>4.0.0</modelVersion>
|
|
5
|
+
<parent>
|
|
6
|
+
<groupId>org.springframework.boot</groupId>
|
|
7
|
+
<artifactId>spring-boot-starter-parent</artifactId>
|
|
8
|
+
<version>3.2.2</version>
|
|
9
|
+
<relativePath/> <!-- lookup parent from repository -->
|
|
10
|
+
</parent>
|
|
11
|
+
<groupId>com.example</groupId>
|
|
12
|
+
<artifactId>helloworld</artifactId>
|
|
13
|
+
<version>0.0.1-SNAPSHOT</version>
|
|
14
|
+
<name>helloworld</name>
|
|
15
|
+
<description>Demo project for Spring Boot</description>
|
|
16
|
+
<properties>
|
|
17
|
+
<java.version>17</java.version>
|
|
18
|
+
</properties>
|
|
19
|
+
<dependencies>
|
|
20
|
+
<dependency>
|
|
21
|
+
<groupId>org.springframework.boot</groupId>
|
|
22
|
+
<artifactId>spring-boot-starter-web</artifactId>
|
|
23
|
+
</dependency>
|
|
24
|
+
|
|
25
|
+
<dependency>
|
|
26
|
+
<groupId>org.springframework.boot</groupId>
|
|
27
|
+
<artifactId>spring-boot-starter-test</artifactId>
|
|
28
|
+
<scope>test</scope>
|
|
29
|
+
</dependency>
|
|
30
|
+
</dependencies>
|
|
31
|
+
|
|
32
|
+
<build>
|
|
33
|
+
<plugins>
|
|
34
|
+
<plugin>
|
|
35
|
+
<groupId>org.springframework.boot</groupId>
|
|
36
|
+
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
37
|
+
</plugin>
|
|
38
|
+
</plugins>
|
|
39
|
+
</build>
|
|
40
|
+
|
|
41
|
+
</project>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package com.example.helloworld;
|
|
2
|
+
|
|
3
|
+
import org.springframework.boot.SpringApplication;
|
|
4
|
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
5
|
+
import org.springframework.web.bind.annotation.GetMapping;
|
|
6
|
+
import org.springframework.web.bind.annotation.RestController;
|
|
7
|
+
|
|
8
|
+
@SpringBootApplication
|
|
9
|
+
@RestController
|
|
10
|
+
public class HelloworldApplication {
|
|
11
|
+
|
|
12
|
+
public static void main(String[] args) {
|
|
13
|
+
SpringApplication.run(HelloworldApplication.class, args);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@GetMapping("/")
|
|
17
|
+
public String hello() {
|
|
18
|
+
return "Hello from Spring Boot on Cloud Run!";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
server.port=${PORT:8080}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const app = express();
|
|
3
|
+
const port = process.env.PORT || 8080;
|
|
4
|
+
|
|
5
|
+
app.get('/', (req, res) => {
|
|
6
|
+
res.send('Hello from Node.js on Cloud Run!');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
app.get('/health', (req, res) => {
|
|
10
|
+
res.status(200).send('OK');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
app.listen(port, () => {
|
|
14
|
+
console.log(`Nodejs sample app listening at http://localhost:${port}`);
|
|
15
|
+
});
|
package/lib/clients.js
CHANGED
|
@@ -22,12 +22,30 @@ const CLIENT_SECRET = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
|
|
|
22
22
|
const REDIRECT_URI = process.env.GOOGLE_OAUTH_REDIRECT_URI;
|
|
23
23
|
const AUTHORIZATION_HEADER = 'Authorization';
|
|
24
24
|
const BEARER_PREFIX = 'Bearer';
|
|
25
|
+
const CLOUD_PLATFORM_SCOPE = 'https://www.googleapis.com/auth/cloud-platform';
|
|
25
26
|
|
|
26
|
-
function
|
|
27
|
-
return
|
|
27
|
+
export function getDefaultAccessToken() {
|
|
28
|
+
return process.env.OAUTH_ENABLED === 'true' ? undefined : GCLOUD_AUTH;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
export function validateAccessToken(accessToken) {
|
|
32
|
+
if (
|
|
33
|
+
process.env.OAUTH_ENABLED === 'true' &&
|
|
34
|
+
(!accessToken || accessToken === GCLOUD_AUTH)
|
|
35
|
+
) {
|
|
36
|
+
throw new Error('Access token is required when OAuth is enabled.');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function keyGenerator(projectId, accessToken, region = '') {
|
|
41
|
+
const baseKey =
|
|
42
|
+
accessToken && accessToken !== GCLOUD_AUTH
|
|
43
|
+
? projectId + accessToken
|
|
44
|
+
: projectId;
|
|
45
|
+
return baseKey + (region || '');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const clients = {
|
|
31
49
|
run: new Map(),
|
|
32
50
|
builds: new Map(),
|
|
33
51
|
serviceUsage: new Map(),
|
|
@@ -37,7 +55,9 @@ const clients = {
|
|
|
37
55
|
logging: new Map(),
|
|
38
56
|
billing: new Map(),
|
|
39
57
|
projects: new Map(),
|
|
58
|
+
runV1: new Map(),
|
|
40
59
|
oauth: new Map(),
|
|
60
|
+
secretManager: new Map(),
|
|
41
61
|
};
|
|
42
62
|
|
|
43
63
|
function getAuthClient(accessToken) {
|
|
@@ -98,6 +118,7 @@ export async function getClient(
|
|
|
98
118
|
options = {},
|
|
99
119
|
accessToken = null
|
|
100
120
|
) {
|
|
121
|
+
validateAccessToken(accessToken);
|
|
101
122
|
if (!clients[service].has(key)) {
|
|
102
123
|
const ClientClass = await loadClient();
|
|
103
124
|
const finalOptions = { ...options };
|
|
@@ -121,7 +142,10 @@ export async function getClient(
|
|
|
121
142
|
* @param {string} projectId - The Google Cloud project ID.
|
|
122
143
|
* @returns {Promise<import('@google-cloud/run').v2.ServicesClient>}
|
|
123
144
|
*/
|
|
124
|
-
export async function getRunClient(
|
|
145
|
+
export async function getRunClient(
|
|
146
|
+
projectId,
|
|
147
|
+
accessToken = getDefaultAccessToken()
|
|
148
|
+
) {
|
|
125
149
|
const key = keyGenerator(projectId, accessToken);
|
|
126
150
|
return getClient(
|
|
127
151
|
'run',
|
|
@@ -137,7 +161,10 @@ export async function getRunClient(projectId, accessToken = GCLOUD_AUTH) {
|
|
|
137
161
|
* @param {string} projectId - The Google Cloud project ID.
|
|
138
162
|
* @returns {Promise<import('@google-cloud/run').v2.BuildsClient>}
|
|
139
163
|
*/
|
|
140
|
-
export async function getBuildsClient(
|
|
164
|
+
export async function getBuildsClient(
|
|
165
|
+
projectId,
|
|
166
|
+
accessToken = getDefaultAccessToken()
|
|
167
|
+
) {
|
|
141
168
|
const key = keyGenerator(projectId, accessToken);
|
|
142
169
|
return getClient(
|
|
143
170
|
'builds',
|
|
@@ -155,7 +182,7 @@ export async function getBuildsClient(projectId, accessToken = GCLOUD_AUTH) {
|
|
|
155
182
|
*/
|
|
156
183
|
export async function getServiceUsageClient(
|
|
157
184
|
projectId,
|
|
158
|
-
accessToken =
|
|
185
|
+
accessToken = getDefaultAccessToken()
|
|
159
186
|
) {
|
|
160
187
|
const key = keyGenerator(projectId, accessToken);
|
|
161
188
|
return getClient(
|
|
@@ -173,7 +200,10 @@ export async function getServiceUsageClient(
|
|
|
173
200
|
* @param {string} projectId - The Google Cloud project ID.
|
|
174
201
|
* @returns {Promise<import('@google-cloud/storage').Storage>}
|
|
175
202
|
*/
|
|
176
|
-
export async function getStorageClient(
|
|
203
|
+
export async function getStorageClient(
|
|
204
|
+
projectId,
|
|
205
|
+
accessToken = getDefaultAccessToken()
|
|
206
|
+
) {
|
|
177
207
|
const key = keyGenerator(projectId, accessToken);
|
|
178
208
|
return getClient(
|
|
179
209
|
'storage',
|
|
@@ -191,7 +221,7 @@ export async function getStorageClient(projectId, accessToken = GCLOUD_AUTH) {
|
|
|
191
221
|
*/
|
|
192
222
|
export async function getCloudBuildClient(
|
|
193
223
|
projectId,
|
|
194
|
-
accessToken =
|
|
224
|
+
accessToken = getDefaultAccessToken()
|
|
195
225
|
) {
|
|
196
226
|
const key = keyGenerator(projectId, accessToken);
|
|
197
227
|
return getClient(
|
|
@@ -210,7 +240,7 @@ export async function getCloudBuildClient(
|
|
|
210
240
|
*/
|
|
211
241
|
export async function getArtifactRegistryClient(
|
|
212
242
|
projectId,
|
|
213
|
-
accessToken =
|
|
243
|
+
accessToken = getDefaultAccessToken()
|
|
214
244
|
) {
|
|
215
245
|
const key = keyGenerator(projectId, accessToken);
|
|
216
246
|
return getClient(
|
|
@@ -228,7 +258,10 @@ export async function getArtifactRegistryClient(
|
|
|
228
258
|
* @param {string} projectId - The Google Cloud project ID.
|
|
229
259
|
* @returns {Promise<import('@google-cloud/logging').Logging>}
|
|
230
260
|
*/
|
|
231
|
-
export async function getLoggingClient(
|
|
261
|
+
export async function getLoggingClient(
|
|
262
|
+
projectId,
|
|
263
|
+
accessToken = getDefaultAccessToken()
|
|
264
|
+
) {
|
|
232
265
|
const key = keyGenerator(projectId, accessToken);
|
|
233
266
|
return getClient(
|
|
234
267
|
'logging',
|
|
@@ -248,7 +281,7 @@ export async function getLoggingClient(projectId, accessToken = GCLOUD_AUTH) {
|
|
|
248
281
|
*/
|
|
249
282
|
export async function getBillingClient(
|
|
250
283
|
projectId = 'global',
|
|
251
|
-
accessToken =
|
|
284
|
+
accessToken = getDefaultAccessToken()
|
|
252
285
|
) {
|
|
253
286
|
const key = keyGenerator(projectId, accessToken);
|
|
254
287
|
return getClient(
|
|
@@ -264,7 +297,7 @@ export async function getBillingClient(
|
|
|
264
297
|
* Gets a Projects Client (Resource Manager).
|
|
265
298
|
* @returns {Promise<import('@google-cloud/resource-manager').ProjectsClient>}
|
|
266
299
|
*/
|
|
267
|
-
export async function getProjectsClient(accessToken =
|
|
300
|
+
export async function getProjectsClient(accessToken = getDefaultAccessToken()) {
|
|
268
301
|
const key = keyGenerator('global', accessToken);
|
|
269
302
|
return getClient(
|
|
270
303
|
'projects',
|
|
@@ -275,6 +308,26 @@ export async function getProjectsClient(accessToken = GCLOUD_AUTH) {
|
|
|
275
308
|
);
|
|
276
309
|
}
|
|
277
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Gets a Secret Manager Client for the specified project.
|
|
313
|
+
* @param {string} projectId - The Google Cloud project ID.
|
|
314
|
+
* @returns {Promise<import('@google-cloud/secret-manager').SecretManagerServiceClient>}
|
|
315
|
+
*/
|
|
316
|
+
export async function getSecretManagerClient(
|
|
317
|
+
projectId,
|
|
318
|
+
accessToken = getDefaultAccessToken()
|
|
319
|
+
) {
|
|
320
|
+
const key = keyGenerator(projectId, accessToken);
|
|
321
|
+
return getClient(
|
|
322
|
+
'secretManager',
|
|
323
|
+
key,
|
|
324
|
+
async () =>
|
|
325
|
+
(await import('@google-cloud/secret-manager')).SecretManagerServiceClient,
|
|
326
|
+
{ projectId },
|
|
327
|
+
accessToken
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
278
331
|
/**
|
|
279
332
|
* Gets an OAuth2 Client for the specified access token.
|
|
280
333
|
* @param {string} accessToken - The access token.
|
|
@@ -290,3 +343,80 @@ export async function getOAuthClient(accessToken) {
|
|
|
290
343
|
|
|
291
344
|
return clients.oauth.get(accessToken);
|
|
292
345
|
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Gets a Cloud Run v1 Client (from googleapis).
|
|
349
|
+
* @param {string} projectId - The Google Cloud project ID.
|
|
350
|
+
* @param {string} accessToken - Access token for authentication.
|
|
351
|
+
* @param {string} [region] - The Google Cloud region (optional).
|
|
352
|
+
* @returns {Promise<import('googleapis').run_v1.Run>}
|
|
353
|
+
*/
|
|
354
|
+
export async function getRunV1Client(
|
|
355
|
+
projectId,
|
|
356
|
+
accessToken = getDefaultAccessToken(),
|
|
357
|
+
region = null
|
|
358
|
+
) {
|
|
359
|
+
validateAccessToken(accessToken);
|
|
360
|
+
const key = keyGenerator(projectId, accessToken, region);
|
|
361
|
+
if (!clients.runV1.has(key)) {
|
|
362
|
+
const { google } = await import('googleapis');
|
|
363
|
+
const options = { version: 'v1' };
|
|
364
|
+
|
|
365
|
+
if (accessToken && accessToken !== GCLOUD_AUTH) {
|
|
366
|
+
options.auth = await getOAuthClient(accessToken);
|
|
367
|
+
} else {
|
|
368
|
+
// Use Application Default Credentials for GCLOUD_AUTH
|
|
369
|
+
const auth = new google.auth.GoogleAuth({
|
|
370
|
+
scopes: [CLOUD_PLATFORM_SCOPE],
|
|
371
|
+
projectId: projectId,
|
|
372
|
+
});
|
|
373
|
+
options.auth = auth;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (region) {
|
|
377
|
+
const availableRegions = await getCloudRunRegions(projectId, accessToken);
|
|
378
|
+
if (!availableRegions.includes(region)) {
|
|
379
|
+
throw new Error(`Invalid region: ${region}.`);
|
|
380
|
+
}
|
|
381
|
+
options.rootUrl = `https://${region}-run.googleapis.com/`;
|
|
382
|
+
}
|
|
383
|
+
clients.runV1.set(key, google.run(options));
|
|
384
|
+
}
|
|
385
|
+
return clients.runV1.get(key);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
let cachedRegions = null;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Resets the cached regions. Used for testing.
|
|
392
|
+
*/
|
|
393
|
+
export function resetCachedRegions() {
|
|
394
|
+
cachedRegions = null;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Returns a list of supported Cloud Run regions by fetching them from Compute Engine API.
|
|
399
|
+
* This is the SDK equivalent of `gcloud compute regions list`.
|
|
400
|
+
* Note: This is not moved to utils as it is required by getRunV1Client. Moving it to utils
|
|
401
|
+
* would lead to circular dependency.
|
|
402
|
+
* @param {string} projectId - The project ID to fetch regions for.
|
|
403
|
+
* @param {string} accessToken - The access token for authentication.
|
|
404
|
+
* @returns {Promise<string[]>}
|
|
405
|
+
*/
|
|
406
|
+
export async function getCloudRunRegions(projectId, accessToken) {
|
|
407
|
+
if (cachedRegions) {
|
|
408
|
+
return cachedRegions;
|
|
409
|
+
}
|
|
410
|
+
const runClient = await getRunClient(projectId, accessToken);
|
|
411
|
+
const iterable = runClient.listLocationsAsync({
|
|
412
|
+
name: `projects/${projectId}`,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const regions = [];
|
|
416
|
+
for await (const region of iterable) {
|
|
417
|
+
regions.push(region.locationId);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
cachedRegions = regions;
|
|
421
|
+
return cachedRegions;
|
|
422
|
+
}
|