Skip to content

Commit dbb8573

Browse files
authoredApr 2, 2024··
Add support for apphosting:secrets:describe, alias for functions:secrets:get to align with gcloud (#6948)
* initial commit * initial commit 2 * fix * comments
1 parent ed46b01 commit dbb8573

8 files changed

+81
-19
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Command } from "../command";
2+
import { Options } from "../options";
3+
import { needProjectId } from "../projectUtils";
4+
import { logger } from "../logger";
5+
import { requireAuth } from "../requireAuth";
6+
import { listSecretVersions } from "../gcp/secretManager";
7+
import * as secretManager from "../gcp/secretManager";
8+
import { requirePermissions } from "../requirePermissions";
9+
10+
const Table = require("cli-table");
11+
12+
export const command = new Command("apphosting:secrets:describe <secretName>")
13+
.description("Get metadata for secret and its versions.")
14+
.before(requireAuth)
15+
.before(secretManager.ensureApi)
16+
.before(requirePermissions, ["secretmanager.secrets.get"])
17+
.action(async (secretName: string, options: Options) => {
18+
const projectId = needProjectId(options);
19+
const versions = await listSecretVersions(projectId, secretName);
20+
21+
const table = new Table({
22+
head: ["Name", "Version", "Status", "Create Time"],
23+
style: { head: ["yellow"] },
24+
});
25+
for (const version of versions) {
26+
table.push([secretName, version.versionId, version.state, version.createTime]);
27+
}
28+
logger.info(table.toString());
29+
return { secrets: versions };
30+
});
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { requireAuth } from "../requireAuth";
2+
import { Command } from "../command";
3+
import { requirePermissions } from "../requirePermissions";
4+
import * as secretManager from "../gcp/secretManager";
5+
import * as secrets from "../functions/secrets";
6+
7+
export const command = new Command("functions:secrets:describe <KEY>")
8+
.description(
9+
"Get metadata for secret and its versions. Alias for functions:secrets:get to align with gcloud",
10+
)
11+
.before(requireAuth)
12+
.before(secretManager.ensureApi)
13+
.before(requirePermissions, ["secretmanager.secrets.get"])
14+
.action(secrets.describeSecret);

‎src/commands/functions-secrets-get.ts

+2-19
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,12 @@
1-
const Table = require("cli-table");
2-
31
import { requireAuth } from "../requireAuth";
42
import { Command } from "../command";
5-
import { logger } from "../logger";
6-
import { Options } from "../options";
7-
import { needProjectId } from "../projectUtils";
8-
import { listSecretVersions } from "../gcp/secretManager";
93
import { requirePermissions } from "../requirePermissions";
104
import * as secretManager from "../gcp/secretManager";
5+
import * as secrets from "../functions/secrets";
116

127
export const command = new Command("functions:secrets:get <KEY>")
138
.description("Get metadata for secret and its versions")
149
.before(requireAuth)
1510
.before(secretManager.ensureApi)
1611
.before(requirePermissions, ["secretmanager.secrets.get"])
17-
.action(async (key: string, options: Options) => {
18-
const projectId = needProjectId(options);
19-
const versions = await listSecretVersions(projectId, key);
20-
21-
const table = new Table({
22-
head: ["Version", "State"],
23-
style: { head: ["yellow"] },
24-
});
25-
for (const version of versions) {
26-
table.push([version.versionId, version.state]);
27-
}
28-
logger.info(table.toString());
29-
});
12+
.action(secrets.describeSecret);

‎src/commands/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export function load(client: any): any {
135135
client.functions.secrets.access = loadCommand("functions-secrets-access");
136136
client.functions.secrets.destroy = loadCommand("functions-secrets-destroy");
137137
client.functions.secrets.get = loadCommand("functions-secrets-get");
138+
client.functions.secrets.describe = loadCommand("functions-secrets-describe");
138139
client.functions.secrets.prune = loadCommand("functions-secrets-prune");
139140
client.functions.secrets.set = loadCommand("functions-secrets-set");
140141
client.help = loadCommand("help");
@@ -172,6 +173,7 @@ export function load(client: any): any {
172173
client.apphosting.builds.create = loadCommand("apphosting-builds-create");
173174
client.apphosting.secrets = {};
174175
client.apphosting.secrets.grantaccess = loadCommand("apphosting-secrets-grantaccess");
176+
client.apphosting.secrets.describe = loadCommand("apphosting-secrets-describe");
175177
client.apphosting.rollouts = {};
176178
client.apphosting.rollouts.create = loadCommand("apphosting-rollouts-create");
177179
client.apphosting.rollouts.list = loadCommand("apphosting-rollouts-list");

‎src/functions/secrets.ts

+21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import { logger } from "../logger";
2626
import { assertExhaustive } from "../functional";
2727
import { isFunctionsManaged, FIREBASE_MANAGED } from "../gcp/secretManager";
2828
import { labels } from "../gcp/secretManager";
29+
import { needProjectId } from "../projectUtils";
30+
31+
const Table = require("cli-table");
2932

3033
// For mysterious reasons, importing the poller option in fabricator.ts leads to some
3134
// value of the poller option to be undefined at runtime. I can't figure out what's going on,
@@ -371,3 +374,21 @@ export async function updateEndpointSecret(
371374
assertExhaustive(endpoint.platform);
372375
}
373376
}
377+
378+
/**
379+
* Describe the given secret.
380+
*/
381+
export async function describeSecret(key: string, options: Options): Promise<any> {
382+
const projectId = needProjectId(options);
383+
const versions = await listSecretVersions(projectId, key);
384+
385+
const table = new Table({
386+
head: ["Version", "State"],
387+
style: { head: ["yellow"] },
388+
});
389+
for (const version of versions) {
390+
table.push([version.versionId, version.state]);
391+
}
392+
logger.info(table.toString());
393+
return { secrets: versions };
394+
}

‎src/gcp/secretManager.ts

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export interface SecretVersion {
5757

5858
// Output-only fields
5959
readonly state?: SecretVersionState;
60+
readonly createTime?: string;
6061
}
6162

6263
interface CreateSecretRequest {
@@ -72,6 +73,7 @@ interface AddVersionRequest {
7273
interface SecretVersionResponse {
7374
name: string;
7475
state: SecretVersionState;
76+
createTime: string;
7577
}
7678

7779
interface AccessSecretVersionResponse {
@@ -178,6 +180,7 @@ export async function listSecretVersions(
178180
secrets.push({
179181
...parseSecretVersionResourceName(s.name),
180182
state: s.state,
183+
createTime: s.createTime,
181184
});
182185
}
183186

@@ -203,6 +206,7 @@ export async function getSecretVersion(
203206
return {
204207
...parseSecretVersionResourceName(getRes.body.name),
205208
state: getRes.body.state,
209+
createTime: getRes.body.createTime,
206210
};
207211
}
208212

@@ -282,6 +286,7 @@ export function parseSecretVersionResourceName(resourceName: string): SecretVers
282286
replication: {},
283287
},
284288
versionId: match.groups.version,
289+
createTime: "",
285290
};
286291
}
287292

@@ -380,6 +385,7 @@ export async function addVersion(
380385
return {
381386
...parseSecretVersionResourceName(res.body.name),
382387
state: res.body.state,
388+
createTime: "",
383389
};
384390
}
385391

‎src/test/functions/secrets.spec.ts

+5
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,12 @@ describe("functions/secret", () => {
235235
const secretVersion11: secretManager.SecretVersion = {
236236
secret: secret1,
237237
versionId: "1",
238+
createTime: "2024-03-28T19:43:26",
238239
};
239240
const secretVersion12: secretManager.SecretVersion = {
240241
secret: secret1,
241242
versionId: "2",
243+
createTime: "2024-03-28T19:43:26",
242244
};
243245

244246
const secret2: secretManager.Secret = {
@@ -250,6 +252,7 @@ describe("functions/secret", () => {
250252
const secretVersion21: secretManager.SecretVersion = {
251253
secret: secret2,
252254
versionId: "1",
255+
createTime: "2024-03-28T19:43:26",
253256
};
254257

255258
function toSecretEnvVar(sv: secretManager.SecretVersion): backend.SecretEnvVar {
@@ -391,6 +394,7 @@ describe("functions/secret", () => {
391394
labels: {},
392395
replication: {},
393396
},
397+
createTime: "2024-03-28T19:43:26",
394398
};
395399

396400
it("returns true if secret version is in use", () => {
@@ -511,6 +515,7 @@ describe("functions/secret", () => {
511515
replication: {},
512516
},
513517
versionId: "2",
518+
createTime: "2024-03-28T19:43:26",
514519
};
515520

516521
let gcfMock: sinon.SinonMock;

‎src/test/gcp/secretManager.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe("secretManager", () => {
4040
).to.deep.equal({
4141
secret: { projectId: "my-project", name: "my-secret", labels: {}, replication: {} },
4242
versionId: "7",
43+
createTime: "",
4344
});
4445
});
4546

0 commit comments

Comments
 (0)
Please sign in to comment.