Skip to content

Commit dc66a99

Browse files
xingzhang-suseNickChungSUSE
authored andcommitted
Added helm chart repository wizard
1 parent 65f932e commit dc66a99

File tree

8 files changed

+663
-31
lines changed

8 files changed

+663
-31
lines changed
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
<script>
2+
import { mapGetters } from 'vuex';
3+
import debounce from 'lodash/debounce';
4+
5+
import { CATALOG } from '@shell/config/types';
6+
import { REPO_TYPE, REPO, CHART, VERSION } from '@shell/config/query-params';
7+
import ResourceFetch from '@shell/mixins/resource-fetch';
8+
9+
import { Banner } from '@components/Banner';
10+
import AsyncButton from '@shell/components/AsyncButton';
11+
import Loading from '@shell/components/Loading';
12+
import Markdown from '@shell/components/Markdown';
13+
14+
import { SBOMBASTIC, SBOMBASTIC_REPOS } from '@pkg/types';
15+
import { handleGrowl } from '@pkg/utils/handle-growl';
16+
import { refreshCharts, getLatestVersion } from '@pkg/utils/chart';
17+
import InstallWizard from '@pkg/components/InstallWizard';
18+
19+
export default {
20+
props: {
21+
hasSchema: {
22+
type: Object,
23+
default: null
24+
}
25+
},
26+
27+
components: {
28+
AsyncButton,
29+
Banner,
30+
InstallWizard,
31+
Loading,
32+
Markdown
33+
},
34+
35+
mixins: [ResourceFetch],
36+
37+
async fetch() {
38+
this.debouncedRefreshCharts = debounce((init = false) => {
39+
refreshCharts({
40+
store: this.$store,
41+
chartName: SBOMBASTIC.DEFAULTS,
42+
init
43+
});
44+
}, 500);
45+
46+
this.reloadReady = false;
47+
48+
if (!this.hasSchema) {
49+
if (this.$store.getters['cluster/canList'](CATALOG.CLUSTER_REPO)) {
50+
await this.$fetchType(CATALOG.CLUSTER_REPO);
51+
}
52+
53+
if (this.controllerChart) {
54+
this.initStepIndex = 1;
55+
this.installSteps[0].ready = true;
56+
}
57+
58+
if (!this.sbombasticRepo || !this.controllerChart) {
59+
this.debouncedRefreshCharts(true);
60+
}
61+
}
62+
},
63+
64+
data() {
65+
const installSteps = [
66+
{
67+
name: 'repository',
68+
label: 'Repository',
69+
ready: false,
70+
},
71+
{
72+
name: 'install',
73+
label: 'App Install',
74+
ready: false,
75+
},
76+
];
77+
78+
return {
79+
installSteps,
80+
debouncedRefreshCharts: null,
81+
reloadReady: false,
82+
install: false,
83+
initStepIndex: 0,
84+
docs: { airgap: '' },
85+
};
86+
},
87+
88+
async mounted() {
89+
// if (this.isAirgap) {
90+
// const docs = (await import(/* webpackChunkName: "airgap-docs" */ '../../assets/airgap-installation.md'));
91+
92+
// if (docs) {
93+
// this.docs.airgap = docs.body;
94+
// }
95+
// }
96+
},
97+
98+
watch: {
99+
controllerChart() {
100+
this.installSteps[0].ready = true;
101+
102+
// if (this.isAirgap) {
103+
// this.debouncedRefreshCharts();
104+
// }
105+
106+
this.$refs.wizard?.goToStep(2);
107+
}
108+
},
109+
110+
computed: {
111+
...mapGetters(['currentCluster']),
112+
...mapGetters({
113+
charts: 'catalog/charts',
114+
repos: 'catalog/repos',
115+
t: 'i18n/t'
116+
}),
117+
118+
// isAirgap() {
119+
// return this.$store.getters['kubewarden/airGapped'];
120+
// },
121+
122+
/**
123+
* [!IMPORTANT]
124+
* TODO:
125+
* THIS IS BROKEN
126+
* When installing, if you add the repo and leave the page
127+
* then come back, the controllerChart will be null, but so will
128+
* the sbombasticRepo. This is because the repo is not saved to the store?
129+
*/
130+
131+
controllerChart() {
132+
if (this.sbombasticRepo) {
133+
return this.$store.getters['catalog/chart']({
134+
repoName: this.sbombasticRepo.id,
135+
repoType: 'cluster',
136+
chartName: SBOMBASTIC.CONTROLLER
137+
});
138+
}
139+
140+
return null;
141+
},
142+
143+
sbombasticRepo() {
144+
const chart = this.charts?.find((chart) => chart.chartName === SBOMBASTIC.CONTROLLER);
145+
146+
return this.repos?.find((repo) => repo.id === chart?.repoName);
147+
},
148+
149+
shellEnabled() {
150+
return !!this.currentCluster?.links?.shell;
151+
}
152+
},
153+
154+
methods: {
155+
async addRepository(btnCb) {
156+
try {
157+
const repoObj = await this.$store.dispatch('cluster/create', {
158+
type: CATALOG.CLUSTER_REPO,
159+
metadata: { name: SBOMBASTIC_REPOS.CHARTS_REPO_NAME },
160+
spec: { url: SBOMBASTIC_REPOS.CHARTS },
161+
});
162+
163+
try {
164+
await repoObj.save();
165+
} catch (e) {
166+
handleGrowl({
167+
error: e,
168+
store: this.$store
169+
});
170+
btnCb(false);
171+
172+
return;
173+
}
174+
175+
if (!this.controllerChart) {
176+
this.debouncedRefreshCharts();
177+
}
178+
} catch (e) {
179+
handleGrowl({
180+
error: e,
181+
store: this.$store
182+
});
183+
btnCb(false);
184+
}
185+
},
186+
187+
chartRoute() {
188+
if (!this.controllerChart) {
189+
try {
190+
this.debouncedRefreshCharts();
191+
} catch (e) {
192+
handleGrowl({
193+
error: e,
194+
store: this.$store
195+
});
196+
197+
return;
198+
}
199+
}
200+
201+
const {
202+
repoType, repoName, chartName, versions
203+
} = this.controllerChart;
204+
205+
const latestChartVersion = getLatestVersion(this.$store, versions);
206+
207+
if (latestChartVersion) {
208+
const query = {
209+
[REPO_TYPE]: repoType,
210+
[REPO]: repoName,
211+
[CHART]: chartName,
212+
[VERSION]: latestChartVersion
213+
};
214+
215+
this.$router.push({
216+
name: 'c-cluster-apps-charts-install',
217+
params: { cluster: this.currentCluster?.id || '_' },
218+
query,
219+
});
220+
} else {
221+
const error = {
222+
_statusText: this.t('imageScanner.dashboard.appInstall.versionError.title'),
223+
message: this.t('imageScanner.dashboard.appInstall.versionError.message')
224+
};
225+
226+
handleGrowl({
227+
error,
228+
store: this.$store
229+
});
230+
}
231+
},
232+
233+
reload() {
234+
this.$router.go();
235+
}
236+
}
237+
};
238+
</script>
239+
240+
<template>
241+
<Loading v-if="$fetchState.pending" />
242+
<div v-else class="container">
243+
<div v-if="!install" class="title p-10">
244+
<div class="logo mt-20 mb-10">
245+
<img
246+
src="../assets/img/neuvector-logo.svg"
247+
height="64"
248+
/>
249+
</div>
250+
<h1 class="mb-20" data-testid="sb-install-title">
251+
{{ t("imageScanner.dashboard.appInstall.title") }}
252+
</h1>
253+
<div class="description">
254+
{{ t("imageScanner.dashboard.appInstall.description") }}
255+
</div>
256+
<button v-if="!hasSchema" class="btn role-primary mt-20" data-testid="sb-initial-install-button" @click="install = true">
257+
{{ t("imageScanner.dashboard.appInstall.button") }}
258+
</button>
259+
</div>
260+
261+
<template v-else style="display: flex">
262+
<!-- <template> -->
263+
<!-- Air-Gapped -->
264+
<!-- <template v-if="isAirgap">
265+
<Banner
266+
class="mb-20 mt-20"
267+
color="warning"
268+
>
269+
<span data-testid="sb-install-ag-warning">{{ t('kubewarden.dashboard.prerequisites.airGapped.warning') }}</span>
270+
</Banner>
271+
<Markdown v-model:value="docs.airgap" />
272+
</template> -->
273+
274+
<!-- Non Air-Gapped -->
275+
<!-- <template v-else> -->
276+
<template style="display: flex">
277+
<InstallWizard ref="wizard" :init-step-index="initStepIndex" :steps="installSteps" data-testid="sb-install-wizard">
278+
<template #repository>
279+
<h2 class="mt-20 mb-10" data-testid="sb-repo-title">
280+
{{ t("imageScanner.dashboard.prerequisites.repository.title") }}
281+
</h2>
282+
<p class="mb-20">
283+
{{ t("imageScanner.dashboard.prerequisites.repository.description") }}
284+
</p>
285+
286+
<AsyncButton mode="sbombasticRepository" data-testid="sb-repo-add-button" @click="addRepository" />
287+
</template>
288+
289+
<template #install>
290+
<h2 class="mt-20 mb-10" data-testid="sb-app-install-title">
291+
{{ t("imageScanner.dashboard.appInstall.title") }}
292+
</h2>
293+
<p class="mb-20">
294+
{{ t("imageScanner.dashboard.appInstall.description") }}
295+
</p>
296+
297+
<div class="chart-route">
298+
<Loading v-if="!controllerChart && !reloadReady" mode="relative" class="mt-20" />
299+
300+
<template v-else-if="!controllerChart && reloadReady">
301+
<Banner color="warning">
302+
<span class="mb-20">
303+
{{ t('imageScanner.dashboard.appInstall.reload' ) }}
304+
</span>
305+
<button data-testid="sb-app-install-reload" class="ml-10 btn btn-sm role-primary" @click="reload()">
306+
{{ t('generic.reload') }}
307+
</button>
308+
</Banner>
309+
</template>
310+
311+
<template v-else>
312+
<button
313+
data-testid="sb-app-install-button"
314+
class="btn role-primary mt-20"
315+
:disabled="!controllerChart"
316+
@click.prevent="chartRoute"
317+
>
318+
{{ t("imageScanner.dashboard.appInstall.button") }}
319+
</button>
320+
</template>
321+
</div>
322+
</template>
323+
</InstallWizard>
324+
</template>
325+
</template>
326+
</div>
327+
</template>
328+
329+
<style lang="scss" scoped>
330+
.container {
331+
& .title {
332+
display: flex;
333+
flex-wrap: wrap;
334+
flex-direction: column;
335+
justify-content: center;
336+
align-items: center;
337+
text-align: center;
338+
margin: 100px 0;
339+
}
340+
341+
& .description {
342+
line-height: 20px;
343+
}
344+
345+
& .chart-route {
346+
position: relative;
347+
}
348+
349+
& .airgap-align {
350+
justify-content: start;
351+
}
352+
}
353+
</style>

0 commit comments

Comments
 (0)