@rancher/shell 3.0.5-rc.6 → 3.0.5-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/brand/classic/metadata.json +3 -0
- package/assets/styles/app.scss +1 -0
- package/assets/styles/base/_color.scss +16 -0
- package/assets/styles/base/_helpers.scss +10 -0
- package/assets/styles/base/_variables.scss +18 -12
- package/assets/styles/fonts/_icons.scss +1 -32
- package/assets/styles/global/_layout.scss +1 -1
- package/assets/styles/themes/_dark.scss +262 -258
- package/assets/styles/themes/_light.scss +538 -509
- package/assets/styles/themes/_modern.scss +914 -0
- package/assets/translations/en-us.yaml +110 -29
- package/chart/__tests__/S3.test.ts +2 -1
- package/cloud-credential/generic.vue +18 -10
- package/cloud-credential/harvester.vue +1 -9
- package/components/AdvancedSection.vue +8 -0
- package/components/ChartReadme.vue +17 -7
- package/components/CodeMirror.vue +1 -1
- package/components/Drawer/Chrome.vue +0 -1
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +27 -28
- package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -24
- package/components/Drawer/ResourceDetailDrawer/index.vue +18 -4
- package/components/InstallHelmCharts.vue +656 -0
- package/components/LazyImage.vue +60 -4
- package/components/Loading.vue +1 -1
- package/components/LocaleSelector.vue +7 -2
- package/components/Markdown.vue +4 -0
- package/components/PaginatedResourceTable.vue +46 -1
- package/components/PromptRestore.vue +22 -44
- package/components/Resource/Detail/Masthead/composable.ts +16 -0
- package/components/Resource/Detail/Masthead/index.vue +37 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +26 -7
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +8 -1
- package/components/Resource/Detail/Metadata/KeyValue.vue +12 -10
- package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
- package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -17
- package/components/Resource/Detail/Metadata/composables.ts +9 -7
- package/components/Resource/Detail/Metadata/index.vue +17 -2
- package/components/Resource/Detail/Page.vue +35 -21
- package/components/Resource/Detail/SpacedRow.vue +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -9
- package/components/Resource/Detail/TitleBar/composables.ts +5 -5
- package/components/Resource/Detail/TitleBar/index.vue +12 -3
- package/components/ResourceDetail/Masthead/legacy.vue +1 -1
- package/components/ResourceDetail/index.vue +569 -72
- package/components/ResourceList/index.vue +1 -0
- package/components/ResourceTable.vue +6 -1
- package/components/ResourceYaml.vue +1 -1
- package/components/RichTranslation.vue +106 -0
- package/components/SlideInPanelManager.vue +13 -10
- package/components/SortableTable/index.vue +5 -5
- package/components/SortableTable/selection.js +0 -1
- package/components/Tabbed/index.vue +35 -4
- package/components/__tests__/LazyImage.spec.ts +121 -0
- package/components/__tests__/PromptRestore.test.ts +1 -65
- package/components/__tests__/RichTranslation.test.ts +115 -0
- package/components/fleet/FleetStatus.vue +4 -0
- package/components/fleet/dashboard/ResourcePanel.vue +2 -1
- package/components/form/ClusterAppearance.vue +5 -0
- package/components/form/FileImageSelector.vue +1 -1
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/NameNsDescription.vue +1 -0
- package/components/form/Networking.vue +24 -19
- package/components/form/ProjectMemberEditor.vue +1 -1
- package/components/form/ResourceLabeledSelect.vue +22 -8
- package/components/form/ResourceTabs/index.vue +20 -0
- package/components/form/SecretSelector.vue +9 -0
- package/components/form/SelectOrCreateAuthSecret.vue +6 -3
- package/components/form/__tests__/Networking.test.ts +116 -0
- package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
- package/components/formatter/FleetApplicationSource.vue +25 -17
- package/components/formatter/PodImages.vue +1 -1
- package/components/formatter/__tests__/LiveDate.test.ts +10 -2
- package/components/google/AccountAccess.vue +44 -46
- package/components/nav/Favorite.vue +4 -0
- package/components/nav/Group.vue +4 -1
- package/components/nav/NotificationCenter/Notification.vue +1 -27
- package/components/nav/WindowManager/index.vue +3 -3
- package/composables/resources.ts +2 -2
- package/config/labels-annotations.js +3 -2
- package/config/pagination-table-headers.js +8 -1
- package/config/product/explorer.js +27 -2
- package/config/product/manager.js +0 -1
- package/config/query-params.js +10 -0
- package/config/router/routes.js +21 -1
- package/config/system-namespaces.js +1 -1
- package/config/table-headers.js +30 -1
- package/config/types.js +1 -1
- package/config/version.js +1 -1
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
- package/detail/__tests__/workload.test.ts +164 -0
- package/detail/configmap.vue +33 -75
- package/detail/projectsecret.vue +11 -0
- package/detail/provisioning.cattle.io.cluster.vue +351 -369
- package/detail/secret.vue +49 -308
- package/detail/workload/index.vue +38 -21
- package/dialog/InstallExtensionDialog.vue +8 -5
- package/dialog/RotateEncryptionKeyDialog.vue +10 -30
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
- package/edit/auth/ldap/__tests__/config.test.ts +14 -0
- package/edit/auth/ldap/config.vue +24 -0
- package/edit/compliance.cattle.io.clusterscan.vue +1 -1
- package/edit/configmap.vue +4 -1
- package/edit/fleet.cattle.io.gitrepo.vue +5 -6
- package/edit/fleet.cattle.io.helmop.vue +78 -56
- package/edit/logging.banzaicloud.io.output/index.vue +1 -1
- package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
- package/edit/networking.k8s.io.ingress/Certificate.vue +20 -22
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
- package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
- package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
- package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
- package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
- package/edit/provisioning.cattle.io.cluster/rke2.vue +123 -61
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +32 -33
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
- package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
- package/edit/secret/basic.vue +1 -0
- package/edit/secret/index.vue +126 -15
- package/edit/workload/index.vue +5 -14
- package/list/projectsecret.vue +345 -0
- package/list/provisioning.cattle.io.cluster.vue +1 -69
- package/list/secret.vue +109 -0
- package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
- package/machine-config/google.vue +9 -1
- package/machine-config/vmwarevsphere.vue +7 -17
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/chart.js +0 -2
- package/mixins/create-edit-view/impl.js +10 -1
- package/mixins/resource-fetch-api-pagination.js +11 -12
- package/mixins/resource-fetch.js +3 -1
- package/models/__tests__/chart.test.ts +111 -80
- package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
- package/models/__tests__/node.test.ts +7 -63
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/catalog.cattle.io.operation.js +1 -1
- package/models/chart.js +36 -20
- package/models/cloudcredential.js +2 -163
- package/models/cluster/node.js +7 -7
- package/models/cluster.x-k8s.io.machine.js +3 -3
- package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
- package/models/compliance.cattle.io.clusterscan.js +2 -2
- package/models/configmap.js +4 -0
- package/models/constraints.gatekeeper.sh.constraint.js +1 -1
- package/models/fleet-application.js +0 -17
- package/models/fleet.cattle.io.cluster.js +2 -2
- package/models/fleet.cattle.io.gitrepo.js +15 -1
- package/models/fleet.cattle.io.helmop.js +26 -22
- package/models/management.cattle.io.setting.js +4 -0
- package/models/persistentvolumeclaim.js +1 -1
- package/models/pod.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +39 -67
- package/models/rke.cattle.io.etcdsnapshot.js +1 -1
- package/models/secret.js +161 -2
- package/models/storage.k8s.io.storageclass.js +2 -2
- package/models/workload.js +3 -3
- package/package.json +11 -10
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +1 -0
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
- package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
- package/pages/c/_cluster/apps/charts/chart.vue +422 -174
- package/pages/c/_cluster/apps/charts/index.vue +46 -35
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/projectsecret.vue +24 -0
- package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
- package/pages/c/_cluster/fleet/index.vue +103 -45
- package/pages/c/_cluster/manager/cloudCredential/index.vue +2 -59
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +10 -3
- package/pages/c/_cluster/uiplugins/index.vue +36 -25
- package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
- package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
- package/plugins/dashboard-store/actions.js +42 -22
- package/plugins/dashboard-store/normalize.js +29 -17
- package/plugins/dashboard-store/resource-class.js +83 -17
- package/plugins/steve/__tests__/getters.test.ts +1 -1
- package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
- package/plugins/steve/getters.js +8 -2
- package/plugins/steve/resourceWatcher.js +10 -3
- package/plugins/steve/steve-pagination-utils.ts +14 -3
- package/plugins/steve/subscribe.js +192 -19
- package/plugins/steve/worker/web-worker.advanced.js +2 -0
- package/rancher-components/Card/Card.vue +0 -18
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
- package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
- package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
- package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
- package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
- package/rancher-components/Pill/types.ts +2 -0
- package/rancher-components/RcButton/RcButton.vue +1 -1
- package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
- package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
- package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
- package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
- package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
- package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +40 -6
- package/store/__tests__/catalog.test.ts +93 -1
- package/store/aws.js +19 -8
- package/store/catalog.js +8 -3
- package/types/kube/kube-api.ts +12 -0
- package/types/resources/settings.d.ts +1 -1
- package/types/shell/index.d.ts +643 -585
- package/types/store/pagination.types.ts +16 -6
- package/types/uiplugins.ts +73 -0
- package/utils/__tests__/back-off.test.ts +354 -0
- package/utils/__tests__/create-yaml.test.ts +235 -0
- package/utils/__tests__/kontainer.test.ts +19 -0
- package/utils/__tests__/uiplugins.test.ts +84 -0
- package/utils/back-off.ts +176 -0
- package/utils/create-yaml.js +103 -9
- package/utils/dynamic-importer.js +8 -0
- package/utils/kontainer.ts +3 -5
- package/utils/pagination-utils.ts +18 -0
- package/utils/style.ts +3 -0
- package/utils/uiplugins.ts +29 -2
- package/utils/validators/__tests__/setting.test.js +92 -0
- package/utils/validators/formRules/__tests__/index.test.ts +88 -7
- package/utils/validators/formRules/index.ts +83 -8
- package/utils/validators/setting.js +17 -0
- package/cloud-credential/__tests__/harvester.test.ts +0 -18
- package/components/ResourceDetail/__tests__/index.test.ts +0 -135
- package/components/ResourceDetail/legacy.vue +0 -562
- package/components/formatter/CloudCredExpired.vue +0 -69
- package/models/etcdbackup.js +0 -45
- package/pages/explorer/resource/detail/configmap.vue +0 -42
- package/pages/explorer/resource/detail/secret.vue +0 -50
- package/utils/aws.js +0 -0
|
@@ -17,6 +17,8 @@ export interface Props {
|
|
|
17
17
|
}
|
|
18
18
|
</script>
|
|
19
19
|
<script setup lang="ts">
|
|
20
|
+
const editBttnDataTestId = 'save-configuration-bttn';
|
|
21
|
+
const componentTestid = 'configuration-drawer-tabbed';
|
|
20
22
|
const props = defineProps<Props>();
|
|
21
23
|
const emit = defineEmits(['close']);
|
|
22
24
|
const store = useStore();
|
|
@@ -38,11 +40,14 @@ const title = computed(() => {
|
|
|
38
40
|
|
|
39
41
|
const activeTab = ref<string>(configTabProps ? 'config-tab' : 'yaml-tab');
|
|
40
42
|
|
|
43
|
+
const isConfig = computed(() => {
|
|
44
|
+
return activeTab.value === 'config-tab';
|
|
45
|
+
});
|
|
46
|
+
|
|
41
47
|
const action = computed(() => {
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const action = isConfig ? () => props.resource.goToEdit() : () => props.resource.goToEditYaml();
|
|
48
|
+
const ariaLabel = isConfig.value ? i18n.t('component.drawer.resourceDetailDrawer.ariaLabel.editConfig') : i18n.t('component.drawer.resourceDetailDrawer.ariaLabel.editYaml');
|
|
49
|
+
const label = isConfig.value ? i18n.t('component.drawer.resourceDetailDrawer.ariaLabel.editConfig') : i18n.t('component.drawer.resourceDetailDrawer.ariaLabel.editYaml');
|
|
50
|
+
const action = isConfig.value ? () => props.resource.goToEdit() : () => props.resource.goToEditYaml();
|
|
46
51
|
|
|
47
52
|
return {
|
|
48
53
|
ariaLabel,
|
|
@@ -50,6 +55,11 @@ const action = computed(() => {
|
|
|
50
55
|
action
|
|
51
56
|
};
|
|
52
57
|
});
|
|
58
|
+
|
|
59
|
+
const canEdit = computed(() => {
|
|
60
|
+
return isConfig.value ? props.resource.canEdit : props.resource.canEditYaml;
|
|
61
|
+
});
|
|
62
|
+
|
|
53
63
|
</script>
|
|
54
64
|
<template>
|
|
55
65
|
<Drawer
|
|
@@ -68,6 +78,8 @@ const action = computed(() => {
|
|
|
68
78
|
<Tabbed
|
|
69
79
|
class="tabbed"
|
|
70
80
|
:useHash="false"
|
|
81
|
+
:showExtensionTabs="false"
|
|
82
|
+
:componentTestid="componentTestid"
|
|
71
83
|
@changed="({selectedName}) => {activeTab = selectedName;}"
|
|
72
84
|
>
|
|
73
85
|
<ConfigTab
|
|
@@ -82,8 +94,10 @@ const action = computed(() => {
|
|
|
82
94
|
</template>
|
|
83
95
|
<template #additional-actions>
|
|
84
96
|
<RcButton
|
|
97
|
+
v-if="canEdit"
|
|
85
98
|
:primary="true"
|
|
86
99
|
:aria-label="action.ariaLabel"
|
|
100
|
+
:data-testid="editBttnDataTestId"
|
|
87
101
|
@click="action.action"
|
|
88
102
|
>
|
|
89
103
|
{{ action.label }}
|
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { mapGetters } from 'vuex';
|
|
3
|
+
import throttle from 'lodash/throttle';
|
|
4
|
+
|
|
5
|
+
import { CATALOG, MANAGEMENT } from '@shell/config/types';
|
|
6
|
+
import { SETTING } from '@shell/config/settings';
|
|
7
|
+
import { WINDOWS } from '@shell/store/catalog';
|
|
8
|
+
import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
9
|
+
|
|
10
|
+
import AsyncButton from '@shell/components/AsyncButton';
|
|
11
|
+
import Loading from '@shell/components/Loading';
|
|
12
|
+
|
|
13
|
+
import { isPrerelease } from '@shell/utils/version';
|
|
14
|
+
import { set } from '@shell/utils/object';
|
|
15
|
+
import { defaultCmdOpts as defaultOpts } from '@shell/pages/c/_cluster/apps/charts/install.vue';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Assumptions made by this component
|
|
19
|
+
* If not in a cluster context, you want to install things in the mgmt cluster
|
|
20
|
+
* You want to use the latest non-pre-release version of the chart
|
|
21
|
+
* You want global values (rancher values) configured just as they would be when using the full install experience
|
|
22
|
+
* Slots:
|
|
23
|
+
* errors - this component doesn't render errors but it tracks them and provides a slot for the parent component to render errors if needed
|
|
24
|
+
* cant install - rendered when the user can't create repos or cant see app resources or cant do the install action on the target repo
|
|
25
|
+
* ready to install - rendered when a chart is available and the user appears to have permission to install it; this slot lets parent components expose some values customization
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const defaultCmdOpts = { ...defaultOpts, timeout: '600s' };
|
|
29
|
+
|
|
30
|
+
const MAX_TRIES = 5;
|
|
31
|
+
const RETRY_WAIT = 500;
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
name: 'InstallHelmCharts',
|
|
35
|
+
|
|
36
|
+
components: { AsyncButton, Loading },
|
|
37
|
+
|
|
38
|
+
emits: ['done'],
|
|
39
|
+
|
|
40
|
+
props: {
|
|
41
|
+
// vuex store management or cluster
|
|
42
|
+
store: {
|
|
43
|
+
type: String,
|
|
44
|
+
default: 'cluster'
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
chartName: {
|
|
48
|
+
type: String,
|
|
49
|
+
required: true
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
repoUrl: {
|
|
53
|
+
type: String,
|
|
54
|
+
required: true
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
repoName: {
|
|
58
|
+
type: String,
|
|
59
|
+
required: true
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
repoType: {
|
|
63
|
+
type: String,
|
|
64
|
+
default: 'cluster'
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// if not set will use chart targetNamespace. If neither this prop nor chart's targetNamespace are defined, will use default
|
|
68
|
+
targetNamespace: {
|
|
69
|
+
type: String,
|
|
70
|
+
default: null
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// consuming component can supply some values to merge with chart values
|
|
74
|
+
extraValues: {
|
|
75
|
+
type: Object,
|
|
76
|
+
default: () => {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// how long in ms to show a done/success label between each step
|
|
82
|
+
delay: {
|
|
83
|
+
type: Number,
|
|
84
|
+
default: 1000
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
chartDisplayName: {
|
|
88
|
+
type: String,
|
|
89
|
+
default: null
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
repoDisplayName: {
|
|
93
|
+
type: String,
|
|
94
|
+
default: null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async fetch() {
|
|
100
|
+
// check if the user can even load repos
|
|
101
|
+
const repoSchema = this.$store.getters[`${ this.store }/schemaFor`](CATALOG.CLUSTER_REPO);
|
|
102
|
+
|
|
103
|
+
if (repoSchema && repoSchema?.resourceActions?.install) {
|
|
104
|
+
this.canCreateRepos = true;
|
|
105
|
+
|
|
106
|
+
this.throttledRefreshCharts = throttle(() => {
|
|
107
|
+
try {
|
|
108
|
+
this.$store.dispatch('catalog/load', { force: true });
|
|
109
|
+
} catch (e) {
|
|
110
|
+
this.$store.dispatch('growl/fromError', { err: e });
|
|
111
|
+
}
|
|
112
|
+
}, 500, { trailing: false });
|
|
113
|
+
|
|
114
|
+
if (!this.targetRepo || !this.chart) {
|
|
115
|
+
this.throttledRefreshCharts();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// server url and project ids are used in global values
|
|
119
|
+
try {
|
|
120
|
+
this.serverUrlSetting = await this.$store.dispatch(`${ this.store }/find`, {
|
|
121
|
+
type: MANAGEMENT.SETTING,
|
|
122
|
+
id: SETTING.SERVER_URL,
|
|
123
|
+
});
|
|
124
|
+
} catch (e) {
|
|
125
|
+
this.$store.dispatch('growl/fromError', { err: e });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await this.$store.dispatch(`${ this.store }/findAll`, { type: MANAGEMENT.PROJECT });
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
data() {
|
|
133
|
+
const repo = this.repoDisplayName || this.repoName;
|
|
134
|
+
const chart = this.chartDisplayName || this.chartName;
|
|
135
|
+
const stageNames = {
|
|
136
|
+
ADD_REPO: 'addRepo',
|
|
137
|
+
LOAD_CHARTS: 'loadCharts',
|
|
138
|
+
INSTALL: 'install',
|
|
139
|
+
WAIT: 'waitForLogs'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
throttledRefreshCharts: null,
|
|
144
|
+
|
|
145
|
+
// check the cluster repo schema
|
|
146
|
+
canCreateRepos: false,
|
|
147
|
+
|
|
148
|
+
serverUrlSetting: null,
|
|
149
|
+
versionInfo: null,
|
|
150
|
+
userValues: {},
|
|
151
|
+
// payload for 'install' action on clusterrepo resource
|
|
152
|
+
// contains array of chart install info as well as some helm install opts that are set by default in the regular chart install ui
|
|
153
|
+
installCmd: { charts: [], ...defaultCmdOpts },
|
|
154
|
+
|
|
155
|
+
// disable the button and show a loading spinner while
|
|
156
|
+
// creating repo
|
|
157
|
+
// fetching charts from the repo
|
|
158
|
+
// making the 'install' api call
|
|
159
|
+
// waiting for logs
|
|
160
|
+
doingButtonAction: false,
|
|
161
|
+
|
|
162
|
+
loadingCharts: false,
|
|
163
|
+
waitingForLogs: false,
|
|
164
|
+
|
|
165
|
+
// app has been installed, either by this component or elsewhere
|
|
166
|
+
isInstalled: false,
|
|
167
|
+
// app installation has been initiated by this component
|
|
168
|
+
didInstall: false,
|
|
169
|
+
installedVersion: '',
|
|
170
|
+
|
|
171
|
+
installOperationName: '',
|
|
172
|
+
installOperationNamespace: '',
|
|
173
|
+
operation: {}, // crd used to view helm install logs
|
|
174
|
+
logsReady: false,
|
|
175
|
+
stageNames,
|
|
176
|
+
stages: {
|
|
177
|
+
[stageNames.ADD_REPO]: {
|
|
178
|
+
actionLabel: this.t('catalog.install.button.stages.addRepo.action', { repo }),
|
|
179
|
+
waitingLabel: this.t('catalog.install.button.stages.addRepo.waiting', { repo }),
|
|
180
|
+
successLabel: this.t('catalog.install.button.stages.addRepo.success', { repo }),
|
|
181
|
+
errorLabel: this.t('catalog.install.button.stages.addRepo.error', { repo }),
|
|
182
|
+
action: this.addRepository
|
|
183
|
+
},
|
|
184
|
+
[stageNames.LOAD_CHARTS]: {
|
|
185
|
+
actionLabel: this.t('catalog.install.button.stages.loadCharts.action', { repo }),
|
|
186
|
+
waitingLabel: this.t('catalog.install.button.stages.loadCharts.waiting', { repo }),
|
|
187
|
+
successLabel: this.t('catalog.install.button.stages.loadCharts.success', { chart }),
|
|
188
|
+
errorLabel: this.t('catalog.install.button.stages.loadCharts.error', { chart }),
|
|
189
|
+
action: this.fetchRepoCharts
|
|
190
|
+
},
|
|
191
|
+
// using action label for 'success' here and in waitForLogs to hack around a timing issue where the success message briefly shows
|
|
192
|
+
[stageNames.INSTALL]: {
|
|
193
|
+
actionLabel: this.t('catalog.install.button.stages.installChart.action', { chart }),
|
|
194
|
+
waitingLabel: this.t('catalog.install.button.stages.installChart.waiting', { chart }),
|
|
195
|
+
successLabel: this.t('catalog.install.button.stages.installChart.action', { chart }),
|
|
196
|
+
errorLabel: this.t('catalog.install.button.stages.installChart.error', { chart }),
|
|
197
|
+
action: this.installChart
|
|
198
|
+
},
|
|
199
|
+
[stageNames.WAIT]: {
|
|
200
|
+
actionLabel: this.t('catalog.install.button.stages.waitForLogs.action', { chart }),
|
|
201
|
+
waitingLabel: this.t('catalog.install.button.stages.waitForLogs.waiting', { chart }),
|
|
202
|
+
successLabel: this.t('catalog.install.button.stages.waitForLogs.action', { chart }),
|
|
203
|
+
errorLabel: this.t('catalog.install.button.stages.waitForLogs.error', { chart }),
|
|
204
|
+
action: () => this.logsReady ? this.openLogs() : this.waitForLogs()
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
errors: [],
|
|
209
|
+
btnCb: () => {}
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
watch: {
|
|
214
|
+
targetRepo: {
|
|
215
|
+
handler(neu) {
|
|
216
|
+
if (neu) {
|
|
217
|
+
if (!this.chart) {
|
|
218
|
+
this.doingButtonAction = true;
|
|
219
|
+
this.fetchRepoCharts();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
immediate: true
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
chart: {
|
|
227
|
+
handler(neu) {
|
|
228
|
+
if (neu) {
|
|
229
|
+
this.fetchVersionInfo();
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
immediate: true
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
versionInfo(neu) {
|
|
236
|
+
this.checkIfInstalled();
|
|
237
|
+
|
|
238
|
+
const { chart: chartInfo } = neu;
|
|
239
|
+
|
|
240
|
+
this.installCmd.charts[0] = {
|
|
241
|
+
chartName: this.chartName,
|
|
242
|
+
releaseName: chartInfo.name,
|
|
243
|
+
version: chartInfo.version,
|
|
244
|
+
annotations: {
|
|
245
|
+
[CATALOG_ANNOTATIONS.SOURCE_REPO_TYPE]: this.repoType,
|
|
246
|
+
[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: this.repoName
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
this.installCmd.namespace = this.targetNamespace || this.chart?.targetNamespace || 'default';
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
methods: {
|
|
256
|
+
async addRepository() {
|
|
257
|
+
this.errors = [];
|
|
258
|
+
this.doingButtonAction = true;
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const repoObj = await this.$store.dispatch(`${ this.store }/create`, {
|
|
262
|
+
type: CATALOG.CLUSTER_REPO,
|
|
263
|
+
metadata: { name: this.repoName },
|
|
264
|
+
spec: { url: this.repoUrl },
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
await repoObj.save();
|
|
269
|
+
} catch (e) {
|
|
270
|
+
this.$store.dispatch('growl/fromError', { err: e });
|
|
271
|
+
|
|
272
|
+
this.errors.push(e);
|
|
273
|
+
this.doingButtonAction = false;
|
|
274
|
+
|
|
275
|
+
this.btnCb(false);
|
|
276
|
+
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this.throttledRefreshCharts();
|
|
281
|
+
} catch (e) {
|
|
282
|
+
this.$store.dispatch('growl/fromError', { err: e });
|
|
283
|
+
|
|
284
|
+
this.errors.push(e);
|
|
285
|
+
this.doingButtonAction = false;
|
|
286
|
+
|
|
287
|
+
this.btnCb(false);
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// it takes time to fetch a repo's charts when the repo resource is created
|
|
292
|
+
// so we retry loading the chart info using the "refresh" clusterrepo model action
|
|
293
|
+
async fetchRepoCharts() {
|
|
294
|
+
this.doingButtonAction = true;
|
|
295
|
+
let tries = 0;
|
|
296
|
+
|
|
297
|
+
while (tries < MAX_TRIES) {
|
|
298
|
+
try {
|
|
299
|
+
tries++;
|
|
300
|
+
await this.targetRepo.refresh();
|
|
301
|
+
if (this.chart) {
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_WAIT));
|
|
305
|
+
} catch (err) {
|
|
306
|
+
// some errors here are expected
|
|
307
|
+
// we only show error state when while block finishes and still no chart found
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// tried all our tries and the chart still isn't found - tell users something has gone wrong
|
|
312
|
+
if (!this.chart) {
|
|
313
|
+
this.btnCb(false);
|
|
314
|
+
this.$store.dispatch('growl/fromError', { err: this.t('catalog.install.button.errors.fetchChart') });
|
|
315
|
+
|
|
316
|
+
this.doingButtonAction = false;
|
|
317
|
+
} else {
|
|
318
|
+
this.btnCb(true);
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
// once a chart is located fetch installation info from the repo
|
|
323
|
+
async fetchVersionInfo() {
|
|
324
|
+
try {
|
|
325
|
+
// assume we want the latest non-prerelease version
|
|
326
|
+
const targetVersion = (this.chart?.versions || []).find((v) => v.version && !isPrerelease(v.version))?.version;
|
|
327
|
+
|
|
328
|
+
if (!targetVersion) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
this.versionInfo = await this.$store.dispatch('catalog/getVersionInfo', {
|
|
332
|
+
repoType: this.repoType,
|
|
333
|
+
repoName: this.repoName,
|
|
334
|
+
chartName: this.chartName,
|
|
335
|
+
versionName: targetVersion
|
|
336
|
+
});
|
|
337
|
+
} catch (e) {
|
|
338
|
+
this.$store.dispatch('growl/fromError', { err: e });
|
|
339
|
+
this.errors.push(e);
|
|
340
|
+
this.doingButtonAction = false;
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Check if the chart is already installed
|
|
346
|
+
* also check if the user can install charts generally, and can install charts from this repo
|
|
347
|
+
* If the chart has provides-gvr annotation, check that
|
|
348
|
+
* if no gvr annotation, look for an app with id targetNamespce/targetName
|
|
349
|
+
* if app is found, tell the user which version is installed
|
|
350
|
+
*/
|
|
351
|
+
async checkIfInstalled() {
|
|
352
|
+
const chartInfo = this.versionInfo?.chart || {};
|
|
353
|
+
|
|
354
|
+
if (chartInfo.annotations?.[CATALOG_ANNOTATIONS.PROVIDES]) {
|
|
355
|
+
this.isInstalled = this.$store.getters['catalog/isInstalled']({ gvr: chartInfo?.annotations?.[CATALOG_ANNOTATIONS.PROVIDES] });
|
|
356
|
+
} else {
|
|
357
|
+
const appSchema = this.$store.getters[`${ this.store }/schemaFor`](CATALOG.APP);
|
|
358
|
+
|
|
359
|
+
if (!appSchema) {
|
|
360
|
+
this.canInstallChart = false;
|
|
361
|
+
} else {
|
|
362
|
+
try {
|
|
363
|
+
const app = await this.$store.dispatch(`${ this.store }/find`, { type: CATALOG.APP, id: `${ this.targetNamespace || this.chart?.targetNamespace || 'default' }/${ this.chartName }` });
|
|
364
|
+
|
|
365
|
+
if (app) {
|
|
366
|
+
this.installedVersion = app?.spec?.chart?.metadata?.appVersion;
|
|
367
|
+
this.isInstalled = true;
|
|
368
|
+
}
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
this.doingButtonAction = false;
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
async installChart() {
|
|
378
|
+
this.doingButtonAction = true;
|
|
379
|
+
this.errors = [];
|
|
380
|
+
|
|
381
|
+
if (this.isInstalled) {
|
|
382
|
+
this.doingButtonAction = false;
|
|
383
|
+
|
|
384
|
+
this.errors.push(this.t('catalog.install.button.errors.alreadyInstalled'));
|
|
385
|
+
this.$store.dispatch('growl/fromError', { err: this.t('catalog.install.button.errors.alreadyInstalled') });
|
|
386
|
+
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
let res;
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
this.didInstall = true;
|
|
393
|
+
// should only be values that differ from default
|
|
394
|
+
this.installCmd.charts[0].values = {
|
|
395
|
+
...this.getGlobalValues(), ...this.extraValues, ...this.userValues
|
|
396
|
+
};
|
|
397
|
+
res = await this.targetRepo.doAction('install', this.installCmd);
|
|
398
|
+
} catch (err) {
|
|
399
|
+
this.btnCb(false);
|
|
400
|
+
this.errors.push(err);
|
|
401
|
+
this.doingButtonAction = false;
|
|
402
|
+
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
this.installOperationName = res.operationName;
|
|
407
|
+
this.installOperationNamespace = res.operationNamespace;
|
|
408
|
+
await this.waitForLogs();
|
|
409
|
+
this.btnCb(true);
|
|
410
|
+
|
|
411
|
+
setTimeout(() => this.$emit('done', { namespace: res.operationNamespace, name: res.operationName }), this.delay);
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
// find the helm operation and check if logs are available
|
|
415
|
+
async waitForLogs() {
|
|
416
|
+
const { installOperationName, installOperationNamespace } = this;
|
|
417
|
+
const operationId = `${ installOperationNamespace }/${ installOperationName }`;
|
|
418
|
+
|
|
419
|
+
await this.targetRepo.waitForOperation(operationId);
|
|
420
|
+
|
|
421
|
+
this.operation = await this.$store.dispatch(`${ this.store }/find`, {
|
|
422
|
+
type: CATALOG.OPERATION,
|
|
423
|
+
id: operationId
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
await this.operation.waitForLink('logs');
|
|
428
|
+
this.logsReady = true;
|
|
429
|
+
this.doingButtonAction = false;
|
|
430
|
+
} catch (e) {
|
|
431
|
+
// The wait times out eventually, move on...
|
|
432
|
+
this.doingButtonAction = false;
|
|
433
|
+
this.errors.push(this.t('catalog.install.button.errors.timeoutWaiting'));
|
|
434
|
+
this.$store.dispatch('growl/fromError', { err: this.t('catalog.install.button.errors.timeoutWaiting') });
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
|
|
438
|
+
// set some global values based off the target cluster's configuration
|
|
439
|
+
getGlobalValues() {
|
|
440
|
+
let clusterId = '';
|
|
441
|
+
let clusterName = '';
|
|
442
|
+
|
|
443
|
+
if (this.currentCluster) {
|
|
444
|
+
clusterId = this.currentCluster?.id;
|
|
445
|
+
clusterName = this.currentCluster.nameDisplay;
|
|
446
|
+
} else {
|
|
447
|
+
clusterId = 'local';
|
|
448
|
+
clusterName = 'local';
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const serverUrl = this.serverUrlSetting?.value || '';
|
|
452
|
+
const rkePathPrefix = this.currentCluster?.spec?.rancherKubernetesEngineConfig?.prefixPath || '';
|
|
453
|
+
const rkeWindowsPathPrefix = this.currentCluster?.spec?.rancherKubernetesEngineConfig?.winPrefixPath || '';
|
|
454
|
+
const isWindows = (this.currentCluster?.workerOSs || []).includes(WINDOWS);
|
|
455
|
+
|
|
456
|
+
const projects = this.$store.getters[`${ this.store }/all`](MANAGEMENT.PROJECT);
|
|
457
|
+
|
|
458
|
+
const systemProjectId = projects.find((p) => p.spec?.chartDisplayName === 'System')?.id?.split('/')?.[1] || '';
|
|
459
|
+
|
|
460
|
+
const values = {
|
|
461
|
+
global: {
|
|
462
|
+
cattle: {
|
|
463
|
+
clusterName,
|
|
464
|
+
clusterId,
|
|
465
|
+
url: serverUrl,
|
|
466
|
+
rkePathPrefix,
|
|
467
|
+
rkeWindowsPathPrefix,
|
|
468
|
+
systemProjectId
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
if (isWindows) {
|
|
474
|
+
values.global.cattle.windows = { enabled: true };
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return values;
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
// this method is passed to a slot parent components can use to expose some chart configuration options
|
|
481
|
+
setValue(key, val) {
|
|
482
|
+
set(this.userValues, key, val);
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
openLogs() {
|
|
486
|
+
this.operation.openLogs();
|
|
487
|
+
this.btnCb(true);
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
// go to the full chart install/upgrade experience
|
|
491
|
+
goToInstall() {
|
|
492
|
+
if (!this.chart) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
this.chart.goToInstall(null, 'local');
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
|
|
499
|
+
computed: {
|
|
500
|
+
...mapGetters({
|
|
501
|
+
charts: 'catalog/charts',
|
|
502
|
+
repos: 'catalog/repos',
|
|
503
|
+
t: 'i18n/t'
|
|
504
|
+
}),
|
|
505
|
+
|
|
506
|
+
currentCluster() {
|
|
507
|
+
const storeCluster = this.$store.getters['currentCluster'];
|
|
508
|
+
|
|
509
|
+
if (storeCluster) {
|
|
510
|
+
return storeCluster;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return this.$store.getters[`management/byId`](MANAGEMENT.CLUSTER, 'local' );
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
chart() {
|
|
517
|
+
if (this.targetRepo) {
|
|
518
|
+
return this.$store.getters['catalog/chart']({
|
|
519
|
+
repoName: this.repoName,
|
|
520
|
+
repoType: this.repoType,
|
|
521
|
+
chartName: this.chartName
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return null;
|
|
526
|
+
},
|
|
527
|
+
|
|
528
|
+
targetRepo() {
|
|
529
|
+
return this.$store.getters['catalog/repo']({ repoType: this.repoType, repoName: this.repoName });
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
canInstallChart() {
|
|
533
|
+
const appSchema = this.$store.getters[`${ this.store }/schemaFor`](CATALOG.APP);
|
|
534
|
+
|
|
535
|
+
return appSchema && this.targetRepo && this.targetRepo?.actions?.install;
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Determine what set of labels and what action to use with the button depending on what catalog resources the component has available
|
|
540
|
+
* There are 4 stages of chart installation:
|
|
541
|
+
* creating the repo
|
|
542
|
+
* getting charts from the repo + configuring the chart
|
|
543
|
+
* initializing the installation and waiting for logs
|
|
544
|
+
* offering logs
|
|
545
|
+
*/
|
|
546
|
+
buttonStage() {
|
|
547
|
+
if (!this.targetRepo) {
|
|
548
|
+
return this.stageNames.ADD_REPO;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (!this.chart || (!this.didInstall && this.doingButtonAction)) {
|
|
552
|
+
return this.stageNames.LOAD_CHARTS;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (!this.installOperationName) {
|
|
556
|
+
return this.stageNames.INSTALL;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return this.stageNames.WAIT;
|
|
560
|
+
},
|
|
561
|
+
|
|
562
|
+
// at each stage of installing the chart, the button may be in one of 3 states
|
|
563
|
+
// action (blue, ready to be clicked)
|
|
564
|
+
// waiting (disabled with a loading spinner, doing the thing it was clicked to do)
|
|
565
|
+
// error (red, re-enabled, showing generic error label)
|
|
566
|
+
buttonPhase() {
|
|
567
|
+
if (this.errors.length) {
|
|
568
|
+
return 'error';
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (this.doingButtonAction) {
|
|
572
|
+
return 'waiting';
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return 'action';
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
// the user has permission to install charts, the chart has been found, and it has not been installed already
|
|
579
|
+
readyToInstall() {
|
|
580
|
+
return this.canInstallChart && !this.isInstalled && this.chart && !this.doingButtonAction && this.buttonStage === this.stageNames.INSTALL;
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
|
|
584
|
+
}
|
|
585
|
+
;
|
|
586
|
+
</script>
|
|
587
|
+
|
|
588
|
+
<template>
|
|
589
|
+
<Loading v-if="$fetchState.pending" />
|
|
590
|
+
<!-- the user doesn't have permission either to create repos or install charts generally or install from this repo -->
|
|
591
|
+
<div v-else-if="!canCreateRepos && !doingButtonAction">
|
|
592
|
+
<slot name="cant-install">
|
|
593
|
+
<span>{{ t('catalog.install.button.canNotCreateRepos', ) }}</span>
|
|
594
|
+
</slot>
|
|
595
|
+
</div>
|
|
596
|
+
<div v-else-if="!canInstallChart && !doingButtonAction && targetRepo">
|
|
597
|
+
<slot name="cant-install">
|
|
598
|
+
<span>{{ t('catalog.install.button.canNotInstallApps', {repo: repoName} ) }}</span>
|
|
599
|
+
</slot>
|
|
600
|
+
</div>
|
|
601
|
+
|
|
602
|
+
<!-- the chart is already installed -->
|
|
603
|
+
<div
|
|
604
|
+
v-else-if="isInstalled && !installOperationName"
|
|
605
|
+
>
|
|
606
|
+
<span>{{ t('catalog.install.button.alreadyInstalled', {chart: chartName, version: installedVersion}) }}
|
|
607
|
+
<a
|
|
608
|
+
aria-label="go to chart installation page"
|
|
609
|
+
@click="goToInstall"
|
|
610
|
+
>
|
|
611
|
+
{{ t('catalog.install.button.viewDetails') }}
|
|
612
|
+
</a>
|
|
613
|
+
</span>
|
|
614
|
+
</div>
|
|
615
|
+
|
|
616
|
+
<div v-else>
|
|
617
|
+
<div
|
|
618
|
+
v-if="readyToInstall"
|
|
619
|
+
>
|
|
620
|
+
<!-- the chart is ready to be installed, and installation has not yet begun -->
|
|
621
|
+
<slot
|
|
622
|
+
:set-value="setValue"
|
|
623
|
+
:values="userValues"
|
|
624
|
+
name="values"
|
|
625
|
+
/>
|
|
626
|
+
</div>
|
|
627
|
+
<div v-if="errors && errors.length">
|
|
628
|
+
<slot
|
|
629
|
+
:errors="errors"
|
|
630
|
+
name="errors"
|
|
631
|
+
/>
|
|
632
|
+
</div>
|
|
633
|
+
<AsyncButton
|
|
634
|
+
v-if="canCreateRepos && buttonStage === stageNames.ADD_REPO || canInstallChart && buttonStage !== stageNames.ADD_REPO"
|
|
635
|
+
:current-phase="buttonPhase"
|
|
636
|
+
:action-label="stages[buttonStage].actionLabel"
|
|
637
|
+
:waiting-label="stages[buttonStage].waitingLabel"
|
|
638
|
+
:success-label="stages[buttonStage].successLabel"
|
|
639
|
+
:error-label="stages[buttonStage].errorLabel"
|
|
640
|
+
:delay="delay"
|
|
641
|
+
type="button"
|
|
642
|
+
class="btn role-primary"
|
|
643
|
+
@click="cb=>{btnCb=cb;stages[buttonStage].action()}"
|
|
644
|
+
/>
|
|
645
|
+
<div>
|
|
646
|
+
<button
|
|
647
|
+
v-if="readyToInstall"
|
|
648
|
+
type="button"
|
|
649
|
+
class="btn btn-sm role-link"
|
|
650
|
+
@click="goToInstall"
|
|
651
|
+
>
|
|
652
|
+
{{ t('catalog.install.button.customizeInstall') }}
|
|
653
|
+
</button>
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
</template>
|