Skip to content

Commit 3b27353

Browse files
myftijaTrigger.dev RepoOps
authored andcommitted
fix(webapp): deployment log stream tokens headroom over their cache TTL
Mono-RevId: b622cd7f35e8a2de16f6b305192fe2bccb7d1de9
1 parent eb687fc commit 3b27353

4 files changed

Lines changed: 46 additions & 25 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Fixes an intermittent "Invalid access token" failure caused by the deployment log stream token expiring while a deploy was still in flight.

apps/webapp/app/env.server.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,37 @@ const S2EnvSchema = z.preprocess(
7373
}
7474
return obj;
7575
},
76-
z.discriminatedUnion("S2_ENABLED", [
77-
z.object({
78-
S2_ENABLED: z.literal("1"),
79-
S2_ACCESS_TOKEN: z.string(),
80-
S2_DEPLOYMENT_LOGS_BASIN_NAME: z.string(),
81-
S2_DEPLOYMENT_STREAMS_LOCAL: z.string().default("0"),
82-
}),
83-
z.object({
84-
S2_ENABLED: z.literal("0"),
85-
}),
86-
])
76+
z
77+
.discriminatedUnion("S2_ENABLED", [
78+
z.object({
79+
S2_ENABLED: z.literal("1"),
80+
S2_ACCESS_TOKEN: z.string(),
81+
S2_DEPLOYMENT_LOGS_BASIN_NAME: z.string(),
82+
S2_DEPLOYMENT_STREAMS_LOCAL: z.string().default("0"),
83+
S2_DEPLOYMENT_LOGS_TOKEN_VALIDITY_MS: z.coerce
84+
.number()
85+
.int()
86+
.min(60 * 1000)
87+
.default(60 * 60 * 1000),
88+
S2_DEPLOYMENT_LOGS_TOKEN_CACHE_TTL_MS: z.coerce
89+
.number()
90+
.int()
91+
.min(1000)
92+
.default(30 * 60 * 1000),
93+
}),
94+
z.object({
95+
S2_ENABLED: z.literal("0"),
96+
}),
97+
])
98+
.refine(
99+
(val) =>
100+
val.S2_ENABLED !== "1" ||
101+
val.S2_DEPLOYMENT_LOGS_TOKEN_CACHE_TTL_MS * 2 <= val.S2_DEPLOYMENT_LOGS_TOKEN_VALIDITY_MS,
102+
{
103+
path: ["S2_DEPLOYMENT_LOGS_TOKEN_CACHE_TTL_MS"],
104+
message: "must be at most half of S2_DEPLOYMENT_LOGS_TOKEN_VALIDITY_MS",
105+
}
106+
)
87107
);
88108

89109
// Previously published secret values must never be accepted, including when

apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ export class DeploymentPresenter {
295295

296296
const { accessToken } = await s2.accessTokens.issue({
297297
id: `${projectRef}-${new Date().getTime()}`,
298-
expiresAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour
298+
expiresAt: new Date(Date.now() + env.S2_DEPLOYMENT_LOGS_TOKEN_VALIDITY_MS),
299299
scope: {
300300
ops: ["read"],
301301
basins: {
@@ -309,7 +309,7 @@ export class DeploymentPresenter {
309309

310310
await s2TokenRedis.setex(
311311
redisKey,
312-
59 * 60, // slightly shorter than the token validity period
312+
Math.floor(env.S2_DEPLOYMENT_LOGS_TOKEN_CACHE_TTL_MS / 1000),
313313
accessToken
314314
);
315315

apps/webapp/app/v3/services/deployment.server.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,8 @@ export class DeploymentService extends BaseService {
522522
return errAsync({ type: "s2_is_disabled" as const });
523523
}
524524
const basinName = env.S2_DEPLOYMENT_LOGS_BASIN_NAME;
525+
const tokenValidityMs = env.S2_DEPLOYMENT_LOGS_TOKEN_VALIDITY_MS;
526+
const cacheTtlSeconds = Math.floor(env.S2_DEPLOYMENT_LOGS_TOKEN_CACHE_TTL_MS / 1000);
525527
const redisKey = `${S2_TOKEN_KEY_PREFIX}${project.externalRef}`;
526528

527529
const getTokenFromCache = () =>
@@ -539,7 +541,7 @@ export class DeploymentService extends BaseService {
539541
fromPromise(
540542
s2.accessTokens.issue({
541543
id: `${project.externalRef}-${new Date().getTime()}`,
542-
expiresAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour
544+
expiresAt: new Date(Date.now() + tokenValidityMs),
543545
scope: {
544546
ops: ["read"],
545547
basins: {
@@ -557,17 +559,10 @@ export class DeploymentService extends BaseService {
557559
).map(({ accessToken }) => accessToken);
558560

559561
const cacheToken = (token: string) =>
560-
fromPromise(
561-
s2TokenRedis.setex(
562-
redisKey,
563-
59 * 60, // slightly shorter than the token validity period
564-
token
565-
),
566-
(error) => ({
567-
type: "other" as const,
568-
cause: error,
569-
})
570-
);
562+
fromPromise(s2TokenRedis.setex(redisKey, cacheTtlSeconds, token), (error) => ({
563+
type: "other" as const,
564+
cause: error,
565+
}));
571566

572567
return getTokenFromCache().orElse(() =>
573568
issueS2Token().andThen((token) =>

0 commit comments

Comments
 (0)