Skip to content

Commit 49dd4be

Browse files
committed
refactor app error messages
1 parent 3fa9be7 commit 49dd4be

File tree

3 files changed

+209
-123
lines changed

3 files changed

+209
-123
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<script module>
2+
import { defineMeta } from '@storybook/addon-svelte-csf';
3+
import ErrorMessage from './ErrorMessage.svelte';
4+
5+
const { Story } = defineMeta({
6+
component: ErrorMessage,
7+
tags: ['autodocs']
8+
});
9+
</script>
10+
11+
<Story name="Invalid Manifest">
12+
<ErrorMessage
13+
error={{
14+
__typename: 'WorkloadStatusInvalidNaisYaml',
15+
level: 'ERROR',
16+
detail:
17+
'the domain "roger.com" cannot be used in cluster "dev-gcp"; use one of dev.nav.cloud.nais.io, external.dev.nav.cloud.nais.io, authenticated.dev.nav.cloud.nais.io, .intern.dev.nav.no, .dev-gcp.nav.cloud.nais.io, .ekstern.dev.nav.no, .ansatt.dev.nav.no, .very.intern.dev.nav.no'
18+
}}
19+
/>
20+
</Story>
21+
22+
<Story name="Synchronization Error">
23+
<ErrorMessage
24+
error={{
25+
__typename: 'WorkloadStatusSynchronizationFailing',
26+
level: 'ERROR',
27+
detail:
28+
'persisting SQLInstance to Kubernetes: validation error: refusing to overwrite manually edited resource; please add the correct ownerReference in order to continue'
29+
}}
30+
/>
31+
</Story>
32+
33+
<Story name="Deprecated Image Registru">
34+
<ErrorMessage
35+
error={{
36+
__typename: 'WorkloadStatusDeprecatedRegistry',
37+
level: 'WARNING',
38+
registry: 'docker.pkg.github.com'
39+
}}
40+
/>
41+
</Story>
42+
43+
<Story name="No Running Instances">
44+
<ErrorMessage
45+
error={{
46+
__typename: 'WorkloadStatusNoRunningInstances',
47+
level: 'ERROR',
48+
instances: [
49+
{
50+
name: 'bidrag-sak-675cdddb5-vcffp',
51+
status: { message: 'ImagePullBackOff' }
52+
},
53+
{
54+
name: 'bidrag-sak-675cdddb5-vpc6m',
55+
status: { message: 'ImagePullBackOff' }
56+
}
57+
]
58+
}}
59+
/>
60+
</Story>
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<script lang="ts">
2+
import { WorkloadStatusErrorLevel, type ValueOf } from '$houdini';
3+
import { Alert, BodyLong, Heading } from '@nais/ds-svelte-community';
4+
5+
const {
6+
error,
7+
docURL = (p) => p
8+
}: {
9+
error:
10+
| ({
11+
level: ValueOf<typeof WorkloadStatusErrorLevel>;
12+
} & (
13+
| {
14+
__typename: 'WorkloadStatusInvalidNaisYaml' | 'WorkloadStatusSynchronizationFailing';
15+
detail: string;
16+
}
17+
| {
18+
__typename: 'WorkloadStatusDeprecatedRegistry';
19+
registry: string;
20+
}
21+
| {
22+
__typename: 'WorkloadStatusNoRunningInstances';
23+
instances: {
24+
name: string;
25+
status: { message: string };
26+
}[];
27+
}
28+
))
29+
| { __typename: "non-exhaustive; don't match this" };
30+
docURL?: (path: string) => string;
31+
} = $props();
32+
33+
const levelVariant = (level?: ValueOf<typeof WorkloadStatusErrorLevel>) => {
34+
switch (level) {
35+
case 'ERROR':
36+
return 'error';
37+
case 'WARNING':
38+
return 'warning';
39+
case 'TODO':
40+
default:
41+
return 'info';
42+
}
43+
};
44+
45+
const heading = {
46+
WorkloadStatusInvalidNaisYaml: 'Rollout Failed - Invalid Manifest',
47+
WorkloadStatusSynchronizationFailing: 'Rollout Failed - Synchronization Error',
48+
WorkloadStatusDeprecatedRegistry: 'Deprecated Image Registry',
49+
WorkloadStatusNoRunningInstances: 'No Running Instances'
50+
};
51+
</script>
52+
53+
{#if error.__typename !== "non-exhaustive; don't match this"}
54+
<Alert variant={levelVariant(error.level)}>
55+
<div class="content">
56+
<Heading level="2" size="small">{heading[error.__typename]}</Heading>
57+
{#if error.__typename === 'WorkloadStatusInvalidNaisYaml'}
58+
<BodyLong>
59+
The rollout of your application has failed due to an error in the application manifest.
60+
</BodyLong>
61+
62+
<Heading level="3" size="xsmall">Error details</Heading>
63+
<code>{error.detail}</code>
64+
65+
<BodyLong>
66+
To resolve this issue, review the application manifest and correct any errors. Consult the <a
67+
target="_blank"
68+
rel="noopener noreferrer"
69+
href={docURL('/workloads/application/reference/application-spec/')}
70+
>Nais application reference</a
71+
> for manifest requirements.
72+
</BodyLong>
73+
{:else if error.__typename === 'WorkloadStatusSynchronizationFailing'}
74+
<BodyLong>
75+
The rollout of the application is failing, meaning it is not in sync with the latest
76+
deployment. This may be due to a misconfiguration or a temporary issue, so try again in a
77+
few minutes. If the problem persists, contact the Nais team.
78+
</BodyLong>
79+
80+
<Heading level="3" size="xsmall">Error details</Heading>
81+
<code>{error.detail}</code>
82+
{:else if error.__typename === 'WorkloadStatusDeprecatedRegistry'}
83+
<BodyLong>
84+
This application is using a deprecated image registry ({error.registry}).
85+
</BodyLong>
86+
87+
<BodyLong
88+
>Starting April 1st, applications and jobs on Nais must use images from Google Artifact
89+
Registry (GAR). The easiest way to ensure that images are stored in GAR is to use Nais'
90+
GitHub Actions in the workflow. <a
91+
href="https://nais.io/log/#2025-02-24-image-policy"
92+
target="_blank"
93+
rel="noopener noreferrer">Read more in Nais announcement</a
94+
>.
95+
</BodyLong>
96+
{:else if error.__typename === 'WorkloadStatusNoRunningInstances'}
97+
<BodyLong>The application has no running instances.</BodyLong>
98+
99+
{#if error.instances.length}
100+
<Heading level="3" size="xsmall">Failing instances:</Heading>
101+
<ul style="margin: 0;">
102+
{#each error.instances as instance (instance.name)}
103+
<li>
104+
<code style="font-size: 1rem; line-height: 1.75;">{instance.name}</code>:
105+
<strong>{instance.status.message}</strong>
106+
</li>
107+
{/each}
108+
</ul>
109+
{/if}
110+
111+
<BodyLong>
112+
Check logs if available. If this is unexpected and you cannot resolve the issue, contact
113+
the Nais team.
114+
</BodyLong>
115+
{/if}
116+
</div>
117+
</Alert>
118+
{/if}
119+
120+
<style>
121+
.content {
122+
display: grid;
123+
gap: var(--a-spacing-3);
124+
}
125+
126+
code {
127+
font-size: 0.8rem;
128+
line-height: 1.75;
129+
}
130+
</style>

src/routes/team/[team]/[env]/app/[app]/+page.svelte

+19-123
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
<script lang="ts">
22
import { onNavigate } from '$app/navigation';
33
import { page } from '$app/state';
4-
import { graphql, type WorkloadStatusErrorLevel$options } from '$houdini';
4+
import { graphql } from '$houdini';
55
import AggregatedCostForWorkload from '$lib/components/AggregatedCostForWorkload.svelte';
66
import Confirm from '$lib/components/Confirm.svelte';
7+
import ErrorMessage from '$lib/components/ErrorMessage.svelte';
78
import Image from '$lib/components/Image.svelte';
89
import NetworkPolicy from '$lib/components/NetworkPolicy.svelte';
910
import Persistence from '$lib/components/persistence/Persistence.svelte';
1011
import Secrets from '$lib/components/Secrets.svelte';
1112
import WorkloadDeploy from '$lib/components/WorkloadDeploy.svelte';
12-
import { docURL } from '$lib/doc';
1313
import GraphErrors from '$lib/GraphErrors.svelte';
1414
import Time from '$lib/Time.svelte';
15-
import { Alert, BodyLong, BodyShort, Button, Heading } from '@nais/ds-svelte-community';
15+
import { Alert, Button, Heading } from '@nais/ds-svelte-community';
1616
import { ArrowCirclepathIcon } from '@nais/ds-svelte-community/icons';
1717
import type { PageProps } from './$houdini';
1818
import Ingresses from './Ingresses.svelte';
@@ -53,15 +53,18 @@
5353
});
5454
};
5555
56-
const levelVariant = (level?: WorkloadStatusErrorLevel$options) => {
57-
switch (level) {
58-
case 'ERROR':
59-
return 'error';
60-
case 'WARNING':
61-
return 'warning';
62-
case 'TODO':
56+
const getError = (type: string) => {
57+
const error = $App.data?.team.environment.application.status.errors.find(
58+
(error) => error.__typename === type
59+
);
60+
switch (error?.__typename) {
61+
case 'WorkloadStatusNoRunningInstances':
62+
return {
63+
...error,
64+
instances: $App.data?.team.environment.application.instances.nodes ?? []
65+
};
6366
default:
64-
return 'info';
67+
return error;
6568
}
6669
};
6770
</script>
@@ -74,118 +77,11 @@
7477
<div class="wrapper">
7578
<div class="app-content">
7679
<div class="main-section">
77-
{#if app.status.errors.some((error) => error.__typename === 'WorkloadStatusNoRunningInstances')}
78-
<Alert
79-
variant={levelVariant(
80-
app.status.errors.find(
81-
(error) => error.__typename === 'WorkloadStatusNoRunningInstances'
82-
)?.level
83-
)}
84-
>
85-
<div style="display: grid; gap: var(--a-spacing-3);">
86-
<Heading level="2" size="small">No Running Instances</Heading>
87-
<BodyShort>The application has no running instances.</BodyShort>
88-
89-
<Heading level="3" size="xsmall">Failing instances:</Heading>
90-
<ul style="margin: 0;">
91-
{#each app.instances.nodes as instance (instance.id)}
92-
<li>
93-
<code style="font-size: 1rem; line-height: 1.75;">{instance.name}</code>:
94-
<strong>{instance.status.message}</strong>
95-
</li>
96-
{/each}
97-
</ul>
98-
<BodyLong>
99-
Check logs if available. If this is unexpected and you cannot resolve the issue,
100-
contact the Nais team.
101-
</BodyLong>
102-
</div>
103-
</Alert>
104-
{/if}
105-
{#if app.status.errors.some((error) => error.__typename === 'WorkloadStatusInvalidNaisYaml')}
106-
<Alert
107-
variant={levelVariant(
108-
app.status.errors.find(
109-
(error) => error.__typename === 'WorkloadStatusInvalidNaisYaml'
110-
)?.level
111-
)}
112-
>
113-
<div style="display: grid; gap: var(--a-spacing-3);">
114-
<Heading level="2" size="small">Rollout Failed - Invalid Manifest</Heading>
115-
<BodyLong>
116-
The rollout of your application has failed due to an error in the application
117-
manifest.
118-
</BodyLong>
119-
120-
<Heading level="3" size="xsmall">Error details</Heading>
121-
122-
<code style="font-size: 0.8rem; line-height: 1.75;"
123-
>{app.status.errors.find(
124-
(error) => error.__typename === 'WorkloadStatusInvalidNaisYaml'
125-
)?.detail}</code
126-
>
127-
128-
<BodyLong>
129-
To resolve this issue, review the application manifest and correct any errors.
130-
Consult the <a
131-
target="_blank"
132-
rel="noopener noreferrer"
133-
href={docURL('/workloads/application/reference/application-spec/')}
134-
>Nais application reference</a
135-
> for manifest requirements.
136-
</BodyLong>
137-
</div>
138-
</Alert>
139-
{/if}
140-
{#if app.status.errors.some((error) => error.__typename === 'WorkloadStatusSynchronizationFailing')}
141-
<Alert
142-
variant={levelVariant(
143-
app.status.errors.find(
144-
(error) => error.__typename === 'WorkloadStatusSynchronizationFailing'
145-
)?.level
146-
)}
147-
>
148-
<Heading level="2" size="small" spacing>Rollout Failed - Synchronization Error</Heading>
149-
<BodyLong spacing>
150-
The rollout of the application is failing, meaning it is not in sync with the latest
151-
deployment. This may be due to a misconfiguration or a temporary issue, so try again
152-
in a few minutes. If the problem persists, contact the Nais team.
153-
</BodyLong>
154-
155-
<Heading level="3" size="xsmall" spacing>Error details</Heading>
156-
157-
<code style="font-size: 0.8rem; line-height: 1;"
158-
>{app.status.errors.find(
159-
(error) => error.__typename === 'WorkloadStatusSynchronizationFailing'
160-
)?.detail}</code
161-
>
162-
</Alert>
163-
{/if}
164-
{#if app.status.errors.some((error) => error.__typename === 'WorkloadStatusDeprecatedRegistry')}
165-
<Alert
166-
variant={levelVariant(
167-
app.status.errors.find(
168-
(error) => error.__typename === 'WorkloadStatusDeprecatedRegistry'
169-
)?.level
170-
)}
171-
>
172-
<BodyShort spacing
173-
>This application is using a deprecated image registry ({app.status.errors.find(
174-
(error) => error.__typename === 'WorkloadStatusDeprecatedRegistry'
175-
)?.registry}).</BodyShort
176-
>
177-
178-
<BodyLong
179-
>Starting April 1st, applications and jobs on Nais must use images from Google
180-
Artifact Registry (GAR). The easiest way to ensure that images are stored in GAR is to
181-
use Nais' GitHub Actions in the workflow. <a
182-
href="https://nais.io/log/#2025-02-24-image-policy"
183-
target="_blank"
184-
rel="noopener noreferrer">Read more in Nais announcement</a
185-
>.
186-
</BodyLong>
187-
</Alert>
188-
{/if}
80+
{#each ['WorkloadStatusInvalidNaisYaml', 'WorkloadStatusSynchronizationFailing', 'WorkloadStatusNoRunningInstances', 'WorkloadStatusDeprecatedRegistry'].map(getError) as error}
81+
{#if error}
82+
<ErrorMessage {error} />
83+
{/if}
84+
{/each}
18985

19086
{#if app.deletionStartedAt}
19187
<Alert variant="info" size="small" fullWidth={false}>

0 commit comments

Comments
 (0)