@rancher/shell 3.0.2-rc.2 → 3.0.2-rc.4
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/styles/base/_basic.scss +7 -8
- package/assets/styles/global/_button.scss +10 -0
- package/assets/styles/global/_form.scss +2 -1
- package/assets/styles/global/_tooltip.scss +2 -2
- package/assets/styles/themes/_dark.scss +15 -3
- package/assets/styles/themes/_light.scss +7 -2
- package/assets/styles/vendor/vue-select.scss +4 -0
- package/assets/translations/en-us.yaml +66 -9
- package/assets/translations/zh-hans.yaml +2 -3
- package/components/AppModal.vue +50 -0
- package/components/BannerGraphic.vue +0 -42
- package/components/ButtonMultiAction.vue +1 -1
- package/components/Carousel.vue +88 -74
- package/components/CommunityLinks.vue +6 -1
- package/components/CopyToClipboardText.vue +3 -0
- package/components/Dialog.vue +20 -1
- package/components/GrowlManager.vue +9 -2
- package/components/LocaleSelector.vue +8 -1
- package/components/PaginatedResourceTable.vue +4 -7
- package/components/ProgressBarMulti.vue +14 -0
- package/components/PromptChangePassword.vue +3 -0
- package/components/Questions/Reference.vue +57 -28
- package/components/ResourceDetail/Masthead.vue +1 -1
- package/components/SelectIconGrid.vue +12 -1
- package/components/SideNav.vue +12 -38
- package/components/SortableTable/index.vue +1 -0
- package/components/Tabbed/index.vue +9 -1
- package/components/YamlEditor.vue +1 -0
- package/components/__tests__/Carousel.test.ts +56 -27
- package/components/auth/Principal.vue +5 -3
- package/components/fleet/FleetClusters.vue +82 -1
- package/components/fleet/FleetRepos.vue +13 -30
- package/components/fleet/ForceDirectedTreeChart/index.vue +2 -2
- package/components/form/ChangePassword.vue +2 -0
- package/components/form/ColorInput.vue +24 -1
- package/components/form/FileSelector.vue +2 -0
- package/components/form/KeyValue.vue +230 -160
- package/components/form/LabeledSelect.vue +2 -2
- package/components/form/PlusMinus.vue +14 -2
- package/components/form/ResourceLabeledSelect.vue +13 -53
- package/components/form/ResourceSelector.vue +1 -0
- package/components/form/ResourceTabs/index.vue +79 -36
- package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +192 -0
- package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +104 -0
- package/components/form/SSHKnownHosts/index.vue +101 -0
- package/components/form/SecretSelector.vue +2 -2
- package/components/form/Select.vue +1 -1
- package/components/form/SelectOrCreateAuthSecret.vue +43 -11
- package/components/form/__tests__/KeyValue.test.ts +1 -1
- package/components/form/__tests__/SSHKnownHosts.test.ts +59 -0
- package/components/formatter/FleetClusterSummaryGraph.vue +2 -2
- package/components/formatter/FleetSummaryGraph.vue +6 -7
- package/components/formatter/WorkloadHealthScale.vue +7 -0
- package/components/nav/Group.vue +30 -4
- package/components/nav/Header.vue +82 -114
- package/components/nav/HeaderPageActionMenu.vue +27 -131
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/Type.vue +15 -0
- package/composables/focusTrap.ts +68 -0
- package/config/home-links.js +21 -13
- package/config/labels-annotations.js +2 -0
- package/config/page-actions.js +1 -0
- package/config/pagination-table-headers.js +15 -1
- package/config/product/explorer.js +7 -17
- package/config/table-headers.js +6 -0
- package/config/version.js +5 -1
- package/core/plugin.ts +41 -1
- package/core/plugins.js +125 -72
- package/core/types-provisioning.ts +91 -2
- package/core/types.ts +55 -0
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +12 -3
- package/detail/catalog.cattle.io.app.vue +1 -1
- package/detail/fleet.cattle.io.cluster.vue +3 -3
- package/detail/namespace.vue +13 -19
- package/detail/networking.k8s.io.ingress.vue +13 -53
- package/detail/provisioning.cattle.io.cluster.vue +12 -1
- package/detail/secret.vue +25 -0
- package/detail/workload/index.vue +3 -3
- package/dialog/AddCustomBadgeDialog.vue +5 -1
- package/edit/auth/ldap/__tests__/config.test.ts +18 -0
- package/edit/auth/ldap/config.vue +24 -0
- package/edit/auth/saml.vue +8 -6
- package/edit/fleet.cattle.io.gitrepo.vue +34 -23
- package/edit/logging-flow/index.vue +4 -19
- package/edit/networking.k8s.io.ingress/index.vue +18 -65
- package/edit/networking.k8s.io.networkpolicy/index.vue +4 -5
- package/edit/provisioning.cattle.io.cluster/index.vue +27 -8
- package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -115
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/tabs/networking/ACE.vue +14 -28
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +25 -12
- package/edit/secret/index.vue +1 -1
- package/edit/secret/ssh.vue +21 -3
- package/edit/service.vue +1 -2
- package/list/networking.k8s.io.ingress.vue +1 -1
- package/list/node.vue +15 -8
- package/list/persistentvolume.vue +12 -4
- package/list/provisioning.cattle.io.cluster.vue +1 -0
- package/list/service.vue +1 -1
- package/list/workload.vue +4 -0
- package/mixins/chart.js +4 -1
- package/models/catalog.cattle.io.app.js +3 -1
- package/models/catalog.cattle.io.clusterrepo.js +56 -7
- package/models/fleet.cattle.io.bundle.js +0 -11
- package/models/fleet.cattle.io.cluster.js +17 -1
- package/models/fleet.cattle.io.gitrepo.js +88 -52
- package/models/provisioning.cattle.io.cluster.js +36 -1
- package/models/secret.js +5 -0
- package/models/service.js +1 -0
- package/models/workload.js +19 -1
- package/package.json +5 -4
- package/pages/account/index.vue +4 -0
- package/pages/c/_cluster/apps/charts/index.vue +4 -0
- package/pages/c/_cluster/explorer/ConfigBadge.vue +4 -2
- package/pages/c/_cluster/explorer/index.vue +13 -6
- package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +3 -3
- package/pages/c/_cluster/fleet/index.vue +75 -89
- package/pages/c/_cluster/settings/links.vue +2 -2
- package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +3 -1
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +3 -0
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +7 -1
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +3 -1
- package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +10 -7
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +7 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +181 -106
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +2 -0
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +9 -1
- package/pages/c/_cluster/uiplugins/index.vue +50 -12
- package/pages/diagnostic.vue +17 -15
- package/pages/home.vue +32 -6
- package/plugins/clean-html.js +50 -0
- package/plugins/dashboard-store/resource-class.js +4 -0
- package/plugins/plugin.js +54 -49
- package/plugins/steve/mutations.js +1 -1
- package/plugins/steve/steve-class.js +8 -0
- package/plugins/steve/steve-pagination-utils.ts +3 -1
- package/rancher-components/Accordion/Accordion.vue +4 -4
- package/rancher-components/BadgeState/BadgeState.vue +7 -0
- package/rancher-components/Card/Card.vue +12 -0
- package/rancher-components/Form/Checkbox/Checkbox.vue +9 -2
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +19 -1
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +39 -2
- package/rancher-components/RcButton/RcButton.vue +90 -0
- package/rancher-components/RcButton/index.ts +2 -0
- package/rancher-components/RcButton/types.ts +17 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +122 -0
- package/rancher-components/RcDropdown/RcDropdownItem.vue +127 -0
- package/rancher-components/RcDropdown/RcDropdownSeparator.vue +6 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +42 -0
- package/rancher-components/RcDropdown/index.ts +4 -0
- package/rancher-components/RcDropdown/types.ts +22 -0
- package/rancher-components/RcDropdown/useDropdownCollection.ts +46 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +110 -0
- package/scripts/test-plugins-build.sh +2 -0
- package/scripts/typegen.sh +2 -0
- package/store/catalog.js +1 -1
- package/tsconfig.json +2 -1
- package/types/components/paginatedResourceTable.ts +25 -0
- package/types/components/resourceLabeledSelect.ts +48 -0
- package/types/resources/fleet.d.ts +17 -0
- package/types/shell/index.d.ts +61 -0
- package/utils/auth.js +5 -1
- package/utils/cluster.js +106 -0
- package/utils/fleet.ts +35 -3
- package/utils/ingress.ts +64 -0
- package/utils/uiplugins.ts +56 -44
- package/utils/validators/cron-schedule.js +7 -2
- package/utils/validators/formRules/__tests__/index.test.ts +53 -17
- package/utils/validators/formRules/index.ts +20 -5
- package/vue.config.js +1 -1
- package/components/RelatedWorkloadsTable.vue +0 -50
package/list/node.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import PaginatedResourceTable
|
|
2
|
+
import PaginatedResourceTable from '@shell/components/PaginatedResourceTable.vue';
|
|
3
3
|
import Tag from '@shell/components/Tag.vue';
|
|
4
4
|
import { Banner } from '@components/Banner';
|
|
5
5
|
import { PODS } from '@shell/config/table-headers';
|
|
@@ -19,6 +19,7 @@ import { GROUP_RESOURCES, mapPref } from '@shell/store/prefs';
|
|
|
19
19
|
import { COLUMN_BREAKPOINTS } from '@shell/types/store/type-map';
|
|
20
20
|
|
|
21
21
|
import { mapGetters } from 'vuex';
|
|
22
|
+
import { PagTableFetchPageSecondaryResourcesOpts, PagTableFetchSecondaryResourcesOpts, PagTableFetchSecondaryResourcesReturns } from '@shell/types/components/paginatedResourceTable';
|
|
22
23
|
|
|
23
24
|
export default defineComponent({
|
|
24
25
|
name: 'ListNode',
|
|
@@ -185,19 +186,25 @@ export default defineComponent({
|
|
|
185
186
|
row['displayLabels'] = !row.displayLabels;
|
|
186
187
|
},
|
|
187
188
|
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
/**
|
|
190
|
+
* of type PagTableFetchSecondaryResources
|
|
191
|
+
*/
|
|
192
|
+
async fetchSecondaryResources({ canPaginate }: PagTableFetchSecondaryResourcesOpts): PagTableFetchSecondaryResourcesReturns {
|
|
193
|
+
if (canPaginate) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const promises = [];
|
|
190
197
|
|
|
191
198
|
if (this.canViewMgmtNodes) {
|
|
192
|
-
|
|
199
|
+
promises.push(this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.NODE }));
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
if (this.canViewNormanNodes) {
|
|
196
|
-
|
|
203
|
+
promises.push(this.$store.dispatch(`rancher/findAll`, { type: NORMAN.NODE }));
|
|
197
204
|
}
|
|
198
205
|
|
|
199
206
|
if (this.canViewMachines) {
|
|
200
|
-
|
|
207
|
+
promises.push(this.$store.dispatch(`management/findAll`, { type: CAPI.MACHINE }));
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
if (this.canViewPods) {
|
|
@@ -205,7 +212,7 @@ export default defineComponent({
|
|
|
205
212
|
this.$store.dispatch(`cluster/findAll`, { type: POD });
|
|
206
213
|
}
|
|
207
214
|
|
|
208
|
-
|
|
215
|
+
await Promise.all(promises);
|
|
209
216
|
},
|
|
210
217
|
|
|
211
218
|
/**
|
|
@@ -215,7 +222,7 @@ export default defineComponent({
|
|
|
215
222
|
*
|
|
216
223
|
* So when we have a page.... use those entries as filters when fetching the other resources
|
|
217
224
|
*/
|
|
218
|
-
async fetchPageSecondaryResources({ canPaginate, force, page }:
|
|
225
|
+
async fetchPageSecondaryResources({ canPaginate, force, page }: PagTableFetchPageSecondaryResourcesOpts) {
|
|
219
226
|
if (!page?.length) {
|
|
220
227
|
return;
|
|
221
228
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { defineComponent } from 'vue';
|
|
3
|
-
import PaginatedResourceTable
|
|
3
|
+
import PaginatedResourceTable from '@shell/components/PaginatedResourceTable.vue';
|
|
4
4
|
import { PVC } from '@shell/config/types';
|
|
5
5
|
import { ActionFindPageArgs } from '@shell/types/store/dashboard-store.types';
|
|
6
6
|
import { FilterArgs, PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
7
|
+
import { PagTableFetchPageSecondaryResourcesOpts, PagTableFetchSecondaryResourcesOpts, PagTableFetchSecondaryResourcesReturns } from '@shell/types/components/paginatedResourceTable';
|
|
7
8
|
|
|
8
9
|
export default defineComponent({
|
|
9
10
|
name: 'ListPersistentVolume',
|
|
@@ -38,8 +39,15 @@ export default defineComponent({
|
|
|
38
39
|
},
|
|
39
40
|
|
|
40
41
|
methods: {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* of type PagTableFetchSecondaryResources
|
|
44
|
+
*/
|
|
45
|
+
async fetchSecondaryResources({ canPaginate }: PagTableFetchSecondaryResourcesOpts): PagTableFetchSecondaryResourcesReturns {
|
|
46
|
+
if (canPaginate) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return this.$store.dispatch(`cluster/findAll`, { type: PVC });
|
|
43
51
|
},
|
|
44
52
|
|
|
45
53
|
/**
|
|
@@ -47,7 +55,7 @@ export default defineComponent({
|
|
|
47
55
|
*
|
|
48
56
|
* So when we have a page.... use those entries as filters when fetching the other resources
|
|
49
57
|
*/
|
|
50
|
-
async fetchPageSecondaryResources({ canPaginate, force, page }:
|
|
58
|
+
async fetchPageSecondaryResources({ canPaginate, force, page }: PagTableFetchPageSecondaryResourcesOpts) {
|
|
51
59
|
if (!page?.length) {
|
|
52
60
|
return;
|
|
53
61
|
}
|
package/list/service.vue
CHANGED
package/list/workload.vue
CHANGED
|
@@ -159,6 +159,10 @@ export default {
|
|
|
159
159
|
}
|
|
160
160
|
},
|
|
161
161
|
|
|
162
|
+
typeDisplay() {
|
|
163
|
+
// Used by shell/components/ResourceList/index.vue to override list title (usually found via schema, which doesn't exist for this virtual type)
|
|
164
|
+
return this.$store.getters['type-map/labelFor'](this.schema || workloadSchema, 99);
|
|
165
|
+
},
|
|
162
166
|
};
|
|
163
167
|
</script>
|
|
164
168
|
|
package/mixins/chart.js
CHANGED
|
@@ -182,9 +182,12 @@ export default {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
if (this.existing && this.existing.upgradeAvailable === false) {
|
|
185
|
+
const manager = this.existing?.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.MANAGED] || 'Rancher';
|
|
186
|
+
|
|
185
187
|
warnings.unshift(this.t('catalog.install.warning.managed', {
|
|
186
188
|
name: this.existing.name,
|
|
187
|
-
version: this.chart ? this.query.versionName : null
|
|
189
|
+
version: this.chart ? this.query.versionName : null,
|
|
190
|
+
manager: manager === 'true' ? 'Rancher' : manager
|
|
188
191
|
}, true));
|
|
189
192
|
}
|
|
190
193
|
|
|
@@ -41,7 +41,9 @@ export default class CatalogApp extends SteveModel {
|
|
|
41
41
|
|
|
42
42
|
get warnDeletionMessage() {
|
|
43
43
|
if (this.upgradeAvailable === false) {
|
|
44
|
-
|
|
44
|
+
const manager = this.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.MANAGED] || 'Rancher';
|
|
45
|
+
|
|
46
|
+
return this.t('catalog.delete.warning.managed', { manager: manager === 'true' ? 'Rancher' : manager, name: this.name });
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
return null;
|
|
@@ -2,6 +2,7 @@ import { parse } from '@shell/utils/url';
|
|
|
2
2
|
import { CATALOG } from '@shell/config/labels-annotations';
|
|
3
3
|
import { insertAt } from '@shell/utils/array';
|
|
4
4
|
import { CATALOG as CATALOG_TYPE } from '@shell/config/types';
|
|
5
|
+
import { colorForState, stateDisplay } from '@shell/plugins/dashboard-store/resource-class';
|
|
5
6
|
|
|
6
7
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
7
8
|
|
|
@@ -12,18 +13,40 @@ export default class ClusterRepo extends SteveModel {
|
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
get _isClusterRepoDisabled() {
|
|
17
|
+
return this.spec?.enabled === false;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
get _availableActions() {
|
|
16
21
|
const out = super._availableActions;
|
|
17
22
|
|
|
18
23
|
insertAt(out, 0, { divider: true });
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if (this._isClusterRepoDisabled) {
|
|
26
|
+
insertAt(out, 1, {
|
|
27
|
+
action: 'enableClusterRepo',
|
|
28
|
+
label: this.t('action.enable'),
|
|
29
|
+
icon: 'icon icon-play',
|
|
30
|
+
enabled: true,
|
|
31
|
+
bulkable: true,
|
|
32
|
+
});
|
|
33
|
+
} else {
|
|
34
|
+
insertAt(out, 1, {
|
|
35
|
+
action: 'disableClusterRepo',
|
|
36
|
+
label: this.t('action.disable'),
|
|
37
|
+
icon: 'icon icon-pause',
|
|
38
|
+
enabled: true,
|
|
39
|
+
bulkable: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
insertAt(out, 0, {
|
|
43
|
+
action: 'refresh',
|
|
44
|
+
label: this.t('action.refresh'),
|
|
45
|
+
icon: 'icon icon-refresh',
|
|
46
|
+
enabled: !!this.links.update,
|
|
47
|
+
bulkable: true,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
27
50
|
|
|
28
51
|
return out;
|
|
29
52
|
}
|
|
@@ -39,6 +62,16 @@ export default class ClusterRepo extends SteveModel {
|
|
|
39
62
|
this.$dispatch('catalog/load', { force: true, reset: true }, { root: true });
|
|
40
63
|
}
|
|
41
64
|
|
|
65
|
+
async disableClusterRepo() {
|
|
66
|
+
this.spec.enabled = false;
|
|
67
|
+
await this.save();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async enableClusterRepo() {
|
|
71
|
+
this.spec.enabled = true;
|
|
72
|
+
await this.save();
|
|
73
|
+
}
|
|
74
|
+
|
|
42
75
|
get isGit() {
|
|
43
76
|
return !!this.spec?.gitRepo;
|
|
44
77
|
}
|
|
@@ -157,6 +190,22 @@ export default class ClusterRepo extends SteveModel {
|
|
|
157
190
|
} : undefined;
|
|
158
191
|
}
|
|
159
192
|
|
|
193
|
+
get stateDisplay() {
|
|
194
|
+
if (this._isClusterRepoDisabled) {
|
|
195
|
+
return this.t('generic.disabled');
|
|
196
|
+
} else {
|
|
197
|
+
return stateDisplay(this.state);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
get stateBackground() {
|
|
202
|
+
if (this._isClusterRepoDisabled) {
|
|
203
|
+
return 'badge-disabled';
|
|
204
|
+
} else {
|
|
205
|
+
return colorForState(this.state, this.stateObj?.error, this.stateObj?.transitioning).replace('text-', 'bg-');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
160
209
|
waitForOperation(operationId, timeout, interval = 2000) {
|
|
161
210
|
return this.waitForTestFn(() => {
|
|
162
211
|
if (!this.$getters['schemaFor'](CATALOG_TYPE.OPERATION)) {
|
|
@@ -6,17 +6,6 @@ import { FLEET } from '@shell/config/types';
|
|
|
6
6
|
import { convertSelectorObj, matching } from '@shell/utils/selector';
|
|
7
7
|
|
|
8
8
|
export default class FleetBundle extends SteveModel {
|
|
9
|
-
get deploymentInfo() {
|
|
10
|
-
const ready = this.status?.summary?.ready || 0;
|
|
11
|
-
const total = this.status?.summary?.desiredReady || 0;
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
ready,
|
|
15
|
-
unready: total - ready,
|
|
16
|
-
total
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
9
|
get lastUpdateTime() {
|
|
21
10
|
return this.status?.conditions?.[0].lastUpdateTime;
|
|
22
11
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LOCAL_CLUSTER, MANAGEMENT, NORMAN } from '@shell/config/types';
|
|
2
|
-
import { CAPI, FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
|
|
2
|
+
import { CAPI, FLEET as FLEET_LABELS, SYSTEM_LABELS } from '@shell/config/labels-annotations';
|
|
3
3
|
import { _RKE2 } from '@shell/store/prefs';
|
|
4
4
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
5
5
|
import { escapeHtml } from '@shell/utils/string';
|
|
@@ -190,6 +190,22 @@ export default class FleetCluster extends SteveModel {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
get customLabels() {
|
|
194
|
+
const parsedLabels = [];
|
|
195
|
+
|
|
196
|
+
if (this.labels) {
|
|
197
|
+
for (const k in this.labels) {
|
|
198
|
+
const [prefix] = k.split('/');
|
|
199
|
+
|
|
200
|
+
if (!SYSTEM_LABELS.includes(prefix) && k !== CAPI.PROVIDER) {
|
|
201
|
+
parsedLabels.push(`${ k }=${ this.labels[k] }`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return parsedLabels;
|
|
207
|
+
}
|
|
208
|
+
|
|
193
209
|
async saveYaml(yaml) {
|
|
194
210
|
await this._saveYaml(yaml);
|
|
195
211
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { convert, matching, convertSelectorObj } from '@shell/utils/selector';
|
|
2
2
|
import jsyaml from 'js-yaml';
|
|
3
|
+
import isEmpty from 'lodash/isEmpty';
|
|
3
4
|
import { escapeHtml } from '@shell/utils/string';
|
|
4
5
|
import { FLEET } from '@shell/config/types';
|
|
5
6
|
import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
@@ -7,7 +8,7 @@ import { addObject, addObjects, findBy, insertAt } from '@shell/utils/array';
|
|
|
7
8
|
import { set } from '@shell/utils/object';
|
|
8
9
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
9
10
|
import {
|
|
10
|
-
colorForState, mapStateToEnum, primaryDisplayStatusFromCount, stateDisplay, stateSort
|
|
11
|
+
colorForState, mapStateToEnum, primaryDisplayStatusFromCount, stateDisplay, STATES_ENUM, stateSort,
|
|
11
12
|
} from '@shell/plugins/dashboard-store/resource-class';
|
|
12
13
|
import { NAME } from '@shell/config/product/explorer';
|
|
13
14
|
import FleetUtils from '@shell/utils/fleet';
|
|
@@ -20,6 +21,26 @@ function quacksLikeAHash(str) {
|
|
|
20
21
|
return false;
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
function normalizeStateCounts(data) {
|
|
25
|
+
if (isEmpty(data)) {
|
|
26
|
+
return {
|
|
27
|
+
total: 0,
|
|
28
|
+
states: {},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const { desiredReady, ...rest } = data ;
|
|
32
|
+
const states = Object.entries(rest).reduce((res, [key, value]) => {
|
|
33
|
+
res[mapStateToEnum(key)] = value;
|
|
34
|
+
|
|
35
|
+
return res;
|
|
36
|
+
}, {});
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
total: desiredReady,
|
|
40
|
+
states,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
23
44
|
export default class GitRepo extends SteveModel {
|
|
24
45
|
applyDefaults() {
|
|
25
46
|
const spec = this.spec || {};
|
|
@@ -157,7 +178,7 @@ export default class GitRepo extends SteveModel {
|
|
|
157
178
|
}
|
|
158
179
|
|
|
159
180
|
get github() {
|
|
160
|
-
const match = this.spec.repo.match(/^https?:\/\/github\.com\/(.*?)(\.git)?\/*$/);
|
|
181
|
+
const match = (this.spec.repo || '').match(/^https?:\/\/github\.com\/(.*?)(\.git)?\/*$/);
|
|
161
182
|
|
|
162
183
|
if (match) {
|
|
163
184
|
return match[1];
|
|
@@ -175,7 +196,7 @@ export default class GitRepo extends SteveModel {
|
|
|
175
196
|
}
|
|
176
197
|
|
|
177
198
|
get repoDisplay() {
|
|
178
|
-
let repo = this.spec.repo;
|
|
199
|
+
let repo = this.spec.repo || '';
|
|
179
200
|
|
|
180
201
|
if (!repo) {
|
|
181
202
|
return null;
|
|
@@ -305,18 +326,7 @@ export default class GitRepo extends SteveModel {
|
|
|
305
326
|
}
|
|
306
327
|
|
|
307
328
|
get bundles() {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return all.filter((bundle) => bundle.repoName === this.name &&
|
|
311
|
-
bundle.namespace === this.namespace &&
|
|
312
|
-
bundle.namespacedName.startsWith(`${ this.namespace }:${ this.name }`));
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Bundles with state of active
|
|
317
|
-
*/
|
|
318
|
-
get bundlesReady() {
|
|
319
|
-
return this.bundles?.filter((bundle) => bundle.state === 'active');
|
|
329
|
+
return this.$getters['matching'](FLEET.BUNDLE, { 'fleet.cattle.io/repo-name': this.name }, this.namespace);
|
|
320
330
|
}
|
|
321
331
|
|
|
322
332
|
get bundleDeployments() {
|
|
@@ -325,6 +335,65 @@ export default class GitRepo extends SteveModel {
|
|
|
325
335
|
return bds.filter((bd) => bd.metadata?.labels?.['fleet.cattle.io/repo-name'] === this.name);
|
|
326
336
|
}
|
|
327
337
|
|
|
338
|
+
get allBundlesStatuses() {
|
|
339
|
+
const bundleDeploymentCountsPerBundle = this.bundleDeployments.reduce((acc, bd) => {
|
|
340
|
+
const bundleId = FleetUtils.bundleIdFromBundleDeploymentLabels(bd.metadata?.labels);
|
|
341
|
+
const state = mapStateToEnum(FleetUtils.bundleDeploymentState(bd));
|
|
342
|
+
|
|
343
|
+
if (!acc[bundleId]) {
|
|
344
|
+
acc[bundleId] = {
|
|
345
|
+
total: 0,
|
|
346
|
+
states: { [STATES_ENUM.READY]: 0 },
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
acc[bundleId].total++;
|
|
350
|
+
|
|
351
|
+
if (!acc[bundleId].states[state]) {
|
|
352
|
+
acc[bundleId].states[state] = 0;
|
|
353
|
+
}
|
|
354
|
+
acc[bundleId].states[state]++;
|
|
355
|
+
|
|
356
|
+
return acc;
|
|
357
|
+
}, {});
|
|
358
|
+
const bundleIds = Object.keys(bundleDeploymentCountsPerBundle);
|
|
359
|
+
|
|
360
|
+
return bundleIds.reduce((acc, bundleId) => {
|
|
361
|
+
const state = primaryDisplayStatusFromCount(bundleDeploymentCountsPerBundle[bundleId].states);
|
|
362
|
+
|
|
363
|
+
if (!acc.states[state]) {
|
|
364
|
+
acc.states[state] = 0;
|
|
365
|
+
}
|
|
366
|
+
acc.states[state]++;
|
|
367
|
+
|
|
368
|
+
return acc;
|
|
369
|
+
}, { total: bundleIds.length, states: { [STATES_ENUM.READY]: 0 } } );
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
get allResourceStatuses() {
|
|
373
|
+
return normalizeStateCounts(this.status?.resourceCounts || {});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
statusResourceCountsForCluster(clusterId) {
|
|
377
|
+
if (!this.targetClusters.some((c) => c.id === clusterId)) {
|
|
378
|
+
return {};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return this.bundleDeployments
|
|
382
|
+
.filter((bd) => FleetUtils.clusterIdFromBundleDeploymentLabels(bd.metadata?.labels) === clusterId)
|
|
383
|
+
.map((bd) => FleetUtils.resourcesFromBundleDeploymentStatus(bd.status))
|
|
384
|
+
.flat()
|
|
385
|
+
.map((r) => r.state)
|
|
386
|
+
.reduce((prev, state) => {
|
|
387
|
+
if (!prev[state]) {
|
|
388
|
+
prev[state] = 0;
|
|
389
|
+
}
|
|
390
|
+
prev[state]++;
|
|
391
|
+
prev.desiredReady++;
|
|
392
|
+
|
|
393
|
+
return prev;
|
|
394
|
+
}, { desiredReady: 0 });
|
|
395
|
+
}
|
|
396
|
+
|
|
328
397
|
get resourcesStatuses() {
|
|
329
398
|
const bundleDeployments = this.bundleDeployments || [];
|
|
330
399
|
const clusters = (this.targetClusters || []).reduce((res, c) => {
|
|
@@ -357,7 +426,7 @@ export default class GitRepo extends SteveModel {
|
|
|
357
426
|
name: `c-cluster-product-resource${ r.namespace ? '-namespace' : '' }-id`,
|
|
358
427
|
params: {
|
|
359
428
|
product: NAME,
|
|
360
|
-
cluster: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
|
|
429
|
+
cluster: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME], // explorer uses the "management" Cluster name, which differs from the Fleet Cluster name
|
|
361
430
|
resource: type,
|
|
362
431
|
namespace: r.namespace,
|
|
363
432
|
id: r.name,
|
|
@@ -385,7 +454,6 @@ export default class GitRepo extends SteveModel {
|
|
|
385
454
|
creationTimestamp: r.createdAt,
|
|
386
455
|
|
|
387
456
|
// other properties
|
|
388
|
-
clusterLabel: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
|
|
389
457
|
stateBackground: color,
|
|
390
458
|
stateDisplay: display,
|
|
391
459
|
stateSort: stateSort(color, display),
|
|
@@ -408,42 +476,10 @@ export default class GitRepo extends SteveModel {
|
|
|
408
476
|
};
|
|
409
477
|
}
|
|
410
478
|
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
const { clusterId, clusterLabel, state } = curr;
|
|
414
|
-
|
|
415
|
-
if (!prev[clusterId]) {
|
|
416
|
-
prev[clusterId] = {
|
|
417
|
-
clusterLabel,
|
|
418
|
-
resourceCounts: { [state]: 0, desiredReady: 0 }
|
|
419
|
-
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (!prev[clusterId].resourceCounts[state]) {
|
|
424
|
-
prev[clusterId].resourceCounts[state] = 0;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
prev[clusterId].resourceCounts[state] += 1;
|
|
428
|
-
prev[clusterId].resourceCounts.desiredReady += 1;
|
|
429
|
-
|
|
430
|
-
return prev;
|
|
431
|
-
}, {});
|
|
432
|
-
|
|
433
|
-
const values = Object.keys(clusterStatuses).map((key) => {
|
|
434
|
-
const { clusterLabel, resourceCounts } = clusterStatuses[key];
|
|
435
|
-
|
|
436
|
-
return {
|
|
437
|
-
clusterId: key,
|
|
438
|
-
clusterLabel, // FLEET LABEL
|
|
439
|
-
status: {
|
|
440
|
-
displayStatus: primaryDisplayStatusFromCount(resourceCounts),
|
|
441
|
-
resourceCounts: { ...resourceCounts }
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
});
|
|
479
|
+
clusterState(clusterId) {
|
|
480
|
+
const resourceCounts = this.statusResourceCountsForCluster(clusterId);
|
|
445
481
|
|
|
446
|
-
return
|
|
482
|
+
return primaryDisplayStatusFromCount(resourceCounts) || STATES_ENUM.ACTIVE;
|
|
447
483
|
}
|
|
448
484
|
|
|
449
485
|
get clustersList() {
|
|
@@ -178,7 +178,15 @@ export default class ProvCluster extends SteveModel {
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
const all = actions.concat(out);
|
|
182
|
+
|
|
183
|
+
// If we have a helper that wants to modify the available actions, let it do it
|
|
184
|
+
if (this.customProvisionerHelper?.availableActions) {
|
|
185
|
+
// Provider can either modify the provided list or return one of its own
|
|
186
|
+
return this.customProvisionerHelper?.availableActions(this, all) || all;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return all;
|
|
182
190
|
}
|
|
183
191
|
|
|
184
192
|
get normanCluster() {
|
|
@@ -401,6 +409,11 @@ export default class ProvCluster extends SteveModel {
|
|
|
401
409
|
}
|
|
402
410
|
|
|
403
411
|
get provisionerDisplay() {
|
|
412
|
+
// Allow a model extension to override the display of the provisioner
|
|
413
|
+
if (this.customProvisionerHelper?.provisionerDisplay) {
|
|
414
|
+
return this.customProvisionerHelper?.provisionerDisplay(this);
|
|
415
|
+
}
|
|
416
|
+
|
|
404
417
|
let provisioner = (this.provisioner || '').toLowerCase();
|
|
405
418
|
|
|
406
419
|
// RKE provisioner can actually do K3s too...
|
|
@@ -496,6 +509,10 @@ export default class ProvCluster extends SteveModel {
|
|
|
496
509
|
}
|
|
497
510
|
|
|
498
511
|
get machineProviderDisplay() {
|
|
512
|
+
if (this.customProvisionerHelper?.machineProviderDisplay) {
|
|
513
|
+
return this.customProvisionerHelper?.machineProviderDisplay(this);
|
|
514
|
+
}
|
|
515
|
+
|
|
499
516
|
if ( this.isImported ) {
|
|
500
517
|
return null;
|
|
501
518
|
}
|
|
@@ -954,6 +971,24 @@ export default class ProvCluster extends SteveModel {
|
|
|
954
971
|
if ( res?._status === 204 ) {
|
|
955
972
|
await this.$dispatch('ws.resource.remove', { data: this });
|
|
956
973
|
}
|
|
974
|
+
|
|
975
|
+
// If this cluster has a custom provisioner, allow it to do custom deletion
|
|
976
|
+
if (this.customProvisionerHelper?.postDelete) {
|
|
977
|
+
return this.customProvisionerHelper?.postDelete(this);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Get the custom provisioner helper for this model
|
|
983
|
+
*/
|
|
984
|
+
get customProvisionerHelper() {
|
|
985
|
+
// Find the first model extension that says it can be used for this model
|
|
986
|
+
return this.modelExtensions.find((modelExt) => modelExt.useFor ? modelExt.useFor(this) : false);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
get groupByParent() {
|
|
990
|
+
// Customer helper can report if the cluster has a parent cluster
|
|
991
|
+
return this.customProvisionerHelper?.parentCluster?.(this) || this.t('resourceTable.groupLabel.notInACluster');
|
|
957
992
|
}
|
|
958
993
|
|
|
959
994
|
get hasError() {
|
package/models/secret.js
CHANGED
|
@@ -49,6 +49,11 @@ export default class Secret extends SteveModel {
|
|
|
49
49
|
return this._type === TYPES.CLOUD_CREDENTIAL || (this.metadata.namespace === 'cattle-global-data' && this.metadata.generateName === 'cc-');
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// For Fleet SSH secrets - does the secret have the 'known_hosts' data key?
|
|
53
|
+
get supportsSshKnownHosts() {
|
|
54
|
+
return this._type === TYPES.SSH && 'known_hosts' in this.data;
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
get issuer() {
|
|
53
58
|
const { metadata:{ annotations = {} } } = this;
|
|
54
59
|
|
package/models/service.js
CHANGED
|
@@ -138,6 +138,7 @@ export default class extends SteveModel {
|
|
|
138
138
|
|
|
139
139
|
async fetchPods() {
|
|
140
140
|
if (this.podRelationship) {
|
|
141
|
+
// Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
|
|
141
142
|
await this.$dispatch('cluster/findMatching', {
|
|
142
143
|
type: POD,
|
|
143
144
|
selector: this.podRelationship.selector,
|
package/models/workload.js
CHANGED
|
@@ -554,6 +554,7 @@ export default class Workload extends WorkloadService {
|
|
|
554
554
|
if (podRelationship) {
|
|
555
555
|
const pods = this.$getters['podsByNamespace'](this.metadata.namespace);
|
|
556
556
|
|
|
557
|
+
// Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
|
|
557
558
|
return pods.filter((obj) => {
|
|
558
559
|
return matches(obj, podRelationship.selector);
|
|
559
560
|
});
|
|
@@ -594,6 +595,23 @@ export default class Workload extends WorkloadService {
|
|
|
594
595
|
return (get(this, 'metadata.relationships') || []).filter((relationship) => relationship.toType === WORKLOAD_TYPES.JOB);
|
|
595
596
|
}
|
|
596
597
|
|
|
598
|
+
/**
|
|
599
|
+
* Ensure the store has all matching jobs
|
|
600
|
+
*/
|
|
601
|
+
async matchingJobs() {
|
|
602
|
+
if (this.type !== WORKLOAD_TYPES.CRON_JOB) {
|
|
603
|
+
return undefined;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// This will be 1 request per relationship, though there's not likely to be many per cron job
|
|
607
|
+
return Promise.all(this.jobRelationships.map((obj) => {
|
|
608
|
+
return this.$dispatch('find', { type: WORKLOAD_TYPES.JOB, id: obj.toId });
|
|
609
|
+
}));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Expects all required pods are fetched upfront
|
|
614
|
+
*/
|
|
597
615
|
get jobs() {
|
|
598
616
|
if (this.type !== WORKLOAD_TYPES.CRON_JOB) {
|
|
599
617
|
return undefined;
|
|
@@ -643,12 +661,12 @@ export default class Workload extends WorkloadService {
|
|
|
643
661
|
}
|
|
644
662
|
|
|
645
663
|
async matchingPods() {
|
|
664
|
+
// Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
|
|
646
665
|
const all = await this.$dispatch('findAll', { type: POD });
|
|
647
666
|
const allInNamespace = all.filter((pod) => pod.metadata.namespace === this.metadata.namespace);
|
|
648
667
|
|
|
649
668
|
const selector = convertSelectorObj(this.spec.selector);
|
|
650
669
|
|
|
651
|
-
// See https://github.com/rancher/dashboard/issues/10417, all pods bad, need to replace local selector somehow
|
|
652
670
|
return matching(allInNamespace, selector);
|
|
653
671
|
}
|
|
654
672
|
|