Skip to content

Commit 51164cb

Browse files
authored
[COM-193] Lock compute provider for new deployment versions (#3674)
* fix(deployments): lock provider for new versions * feat(deployments): allow hiding unsupported create action
1 parent eee96c6 commit 51164cb

6 files changed

Lines changed: 355 additions & 83 deletions

File tree

src/lib/components/deployments/deployments-empty-state.svelte

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88
interface Props {
99
createHref: string;
1010
error?: string;
11+
canCreateServerlessDeployment?: boolean;
1112
}
1213
13-
let { createHref, error = '' }: Props = $props();
14+
let {
15+
createHref,
16+
error = '',
17+
canCreateServerlessDeployment = true,
18+
}: Props = $props();
1419
</script>
1520

1621
<div class="flex flex-col items-center gap-4 py-16">
@@ -24,11 +29,13 @@
2429
</p>
2530
</div>
2631
<div class="flex flex-wrap items-center justify-center gap-4">
27-
<CapabilityGuard capability="serverScaledDeployments">
28-
<Button variant="secondary" href={createHref}>
29-
{translate('deployments.create-serverless-deployment')}
30-
</Button>
31-
</CapabilityGuard>
32+
{#if canCreateServerlessDeployment}
33+
<CapabilityGuard capability="serverScaledDeployments">
34+
<Button variant="secondary" href={createHref}>
35+
{translate('deployments.create-serverless-deployment')}
36+
</Button>
37+
</CapabilityGuard>
38+
{/if}
3239
<Button
3340
variant="ghost"
3441
href="https://docs.temporal.io/worker-deployments"

src/lib/components/workers/serverless-worker-form/create-version-form.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
error?: string;
2828
versions?: VersionSummary[];
2929
computeProviders?: readonly ComputeProviderOption[];
30+
initialProvider?: ComputeProviderOption['value'];
3031
gcpRegions?: string[];
3132
}
3233
@@ -36,13 +37,15 @@
3637
error,
3738
versions = [],
3839
computeProviders,
40+
initialProvider,
3941
gcpRegions,
4042
}: Props = $props();
4143
4244
const superform = superForm(
4345
{
4446
buildId: '',
4547
provider: getInitialComputeProvider({
48+
provider: untrack(() => initialProvider),
4649
providers: untrack(() => computeProviders),
4750
}),
4851
lambdaArn: '',

src/lib/pages/deployments.svelte

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
import { has } from '$lib/utilities/has';
2121
import { routeForWorkerDeploymentCreate } from '$lib/utilities/route-for';
2222
23+
interface Props {
24+
canCreateServerlessDeployment?: boolean;
25+
}
26+
27+
let { canCreateServerlessDeployment = true }: Props = $props();
28+
2329
let error = $state('');
2430
2531
const namespace = $derived(page.params.namespace);
@@ -102,7 +108,11 @@
102108
{/each}
103109

104110
<svelte:fragment slot="empty">
105-
<DeploymentsEmptyState {createHref} {error} />
111+
<DeploymentsEmptyState
112+
{createHref}
113+
{error}
114+
{canCreateServerlessDeployment}
115+
/>
106116
</svelte:fragment>
107117
<svelte:fragment slot="actions-end-additional">
108118
<Tooltip text="Configure Columns" top>
Lines changed: 103 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script lang="ts">
22
import CreateVersionForm from '$lib/components/workers/serverless-worker-form/create-version-form.svelte';
33
import type { ComputeProviderOption } from '$lib/components/workers/serverless-worker-form/shared';
4+
import Alert from '$lib/holocene/alert.svelte';
45
import Link from '$lib/holocene/link.svelte';
56
import { translate } from '$lib/i18n/translate';
67
import {
@@ -12,6 +13,7 @@
1213
validateWorkerDeploymentVersionComputeConfig,
1314
} from '$lib/services/deployments-service';
1415
import type { VersionSummary } from '$lib/types/deployments';
16+
import { lockComputeProvider } from '$lib/utilities/lock-compute-provider';
1517
import { routeForWorkerDeployment } from '$lib/utilities/route-for';
1618
1719
interface Props {
@@ -32,12 +34,20 @@
3234
3335
let error = $state<string | undefined>();
3436
let versions = $state<VersionSummary[]>();
37+
let lockedProvider = $state<ReturnType<typeof lockComputeProvider>>();
38+
let loading = $state(true);
39+
let loadError = $state(false);
3540
3641
const backHref = $derived(
3742
routeForWorkerDeployment({ namespace, deployment }),
3843
);
3944
4045
$effect(() => {
46+
loading = true;
47+
loadError = false;
48+
versions = undefined;
49+
lockedProvider = undefined;
50+
4151
const controller = new AbortController();
4252
fetchDeployment(
4353
{ namespace, deploymentName: deployment },
@@ -48,11 +58,21 @@
4858
)
4959
.then((response) => {
5060
if (controller.signal.aborted) return;
51-
versions = response?.workerDeploymentInfo?.versionSummaries ?? [];
61+
const info = response?.workerDeploymentInfo;
62+
if (!info) {
63+
loadError = true;
64+
return;
65+
}
66+
versions = info.versionSummaries ?? [];
67+
lockedProvider = lockComputeProvider(info, computeProviders);
68+
loadError = versions.length > 0 && !lockedProvider;
5269
})
5370
.catch(() => {
5471
if (controller.signal.aborted) return;
55-
versions = [];
72+
loadError = true;
73+
})
74+
.finally(() => {
75+
if (!controller.signal.aborted) loading = false;
5676
});
5777
return () => controller.abort();
5878
});
@@ -65,85 +85,92 @@
6585
<h1>
6686
{translate('workers.create-version-title')}
6787
</h1>
68-
<CreateVersionForm
69-
{error}
70-
{versions}
71-
{computeProviders}
72-
{gcpRegions}
73-
cancelHref={backHref}
74-
onSubmit={async (data) => {
75-
error = undefined;
76-
const computeConfig =
77-
data.provider === 'cloud-run'
78-
? buildGcpCloudRunComputeConfig(
79-
data.gcpProject,
80-
data.gcpRegion,
81-
data.gcpWorkerPool,
82-
data.gcpServiceAccount,
83-
)
84-
: buildLambdaComputeConfig(data.lambdaArn, data.iamRoleArn, {
85-
roleExternalId: data.roleExternalId,
86-
scaleUpCooloffMs: data.scaleUpCooloffMs,
87-
scaleUpBacklogThreshold: data.scaleUpBacklogThreshold,
88-
maxWorkerLifetimeMs: data.maxWorkerLifetimeMs,
89-
metricsPollIntervalMs: data.metricsPollIntervalMs,
90-
});
91-
await createWorkerDeploymentVersion(
92-
{
93-
namespace,
94-
deploymentVersion: {
88+
{#if loadError}
89+
<Alert intent="error" title={translate('common.error-occurred')}>
90+
{translate('common.unknown-error')}
91+
</Alert>
92+
{:else if !loading}
93+
<CreateVersionForm
94+
{error}
95+
{versions}
96+
computeProviders={lockedProvider?.providers ?? computeProviders}
97+
initialProvider={lockedProvider?.provider}
98+
{gcpRegions}
99+
cancelHref={backHref}
100+
onSubmit={async (data) => {
101+
error = undefined;
102+
const computeConfig =
103+
data.provider === 'cloud-run'
104+
? buildGcpCloudRunComputeConfig(
105+
data.gcpProject,
106+
data.gcpRegion,
107+
data.gcpWorkerPool,
108+
data.gcpServiceAccount,
109+
)
110+
: buildLambdaComputeConfig(data.lambdaArn, data.iamRoleArn, {
111+
roleExternalId: data.roleExternalId,
112+
scaleUpCooloffMs: data.scaleUpCooloffMs,
113+
scaleUpBacklogThreshold: data.scaleUpBacklogThreshold,
114+
maxWorkerLifetimeMs: data.maxWorkerLifetimeMs,
115+
metricsPollIntervalMs: data.metricsPollIntervalMs,
116+
});
117+
await createWorkerDeploymentVersion(
118+
{
119+
namespace,
120+
deploymentVersion: {
121+
deploymentName: deployment,
122+
buildId: data.buildId,
123+
},
124+
computeConfig,
125+
},
126+
(err) => {
127+
error =
128+
err.body?.message ||
129+
err.statusText ||
130+
translate('workers.create-version-error');
131+
},
132+
);
133+
if (error) return;
134+
135+
let validateError: string | undefined;
136+
const validation = await validateWorkerDeploymentVersionComputeConfig(
137+
{
138+
namespace,
95139
deploymentName: deployment,
96140
buildId: data.buildId,
141+
computeConfig,
97142
},
98-
computeConfig,
99-
},
100-
(err) => {
101-
error =
102-
err.body?.message ||
103-
err.statusText ||
104-
translate('workers.create-version-error');
105-
},
106-
);
107-
if (error) return;
108-
109-
let validateError: string | undefined;
110-
const validation = await validateWorkerDeploymentVersionComputeConfig(
111-
{
112-
namespace,
113-
deploymentName: deployment,
114-
buildId: data.buildId,
115-
computeConfig,
116-
},
117-
(err) => {
118-
if (err.status === 501) return;
119-
validateError =
120-
err.body?.message ||
121-
err.statusText ||
122-
translate('workers.create-version-error');
123-
},
124-
);
143+
(err) => {
144+
if (err.status === 501) return;
145+
validateError =
146+
err.body?.message ||
147+
err.statusText ||
148+
translate('workers.create-version-error');
149+
},
150+
);
125151

126-
if (!validateError && (!validation || validation.valid !== false)) {
127-
await onSuccess();
128-
return;
129-
}
152+
if (!validateError && (!validation || validation.valid !== false)) {
153+
await onSuccess();
154+
return;
155+
}
130156

131-
const message =
132-
validation?.message ??
133-
validateError ??
134-
translate('workers.create-version-error');
157+
const message =
158+
validation?.message ??
159+
validateError ??
160+
translate('workers.create-version-error');
135161

136-
let rollbackFailed = false;
137-
await deleteWorkerDeploymentVersion(
138-
{ namespace, deploymentName: deployment, buildId: data.buildId },
139-
() => {
140-
rollbackFailed = true;
141-
},
142-
);
162+
let rollbackFailed = false;
163+
await deleteWorkerDeploymentVersion(
164+
{ namespace, deploymentName: deployment, buildId: data.buildId },
165+
() => {
166+
rollbackFailed = true;
167+
},
168+
);
143169

144-
error = rollbackFailed
145-
? translate('workers.create-version-rollback-failed', { message })
146-
: message;
147-
}}
148-
/>
170+
error = rollbackFailed
171+
? translate('workers.create-version-rollback-failed', { message })
172+
: message;
173+
}}
174+
/>
175+
{/if}
149176
</div>

0 commit comments

Comments
 (0)