@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
package/components/LazyImage.vue
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { BLANK_IMAGE } from '@shell/utils/style';
|
|
3
|
+
|
|
2
4
|
export default {
|
|
3
5
|
props: {
|
|
4
6
|
initialSrc: {
|
|
5
7
|
type: String,
|
|
6
|
-
default:
|
|
8
|
+
default: BLANK_IMAGE,
|
|
7
9
|
},
|
|
8
10
|
|
|
9
11
|
errorSrc: {
|
|
@@ -20,29 +22,83 @@ export default {
|
|
|
20
22
|
watch: {
|
|
21
23
|
src(neu, old) {
|
|
22
24
|
if (neu !== old) {
|
|
23
|
-
|
|
25
|
+
// Show error image if src is falsy
|
|
26
|
+
if (!neu) {
|
|
27
|
+
return this.onError();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (this.intersected) {
|
|
31
|
+
// The component is in the viewport, load the new image right away
|
|
32
|
+
this.loadImage();
|
|
33
|
+
} else if (!this.observer) {
|
|
34
|
+
// The component is not in the viewport and not being observed,
|
|
35
|
+
// which can happen if `src` was null during mount.
|
|
36
|
+
// Start observing.
|
|
37
|
+
this.startObserver();
|
|
38
|
+
}
|
|
24
39
|
}
|
|
25
40
|
}
|
|
26
41
|
},
|
|
27
42
|
|
|
43
|
+
created() {
|
|
44
|
+
// initialize non-reactive data
|
|
45
|
+
this.observer = null;
|
|
46
|
+
this.intersected = false;
|
|
47
|
+
this.boundError = null;
|
|
48
|
+
},
|
|
49
|
+
|
|
28
50
|
mounted() {
|
|
29
|
-
|
|
51
|
+
// Show error image if src is falsy
|
|
52
|
+
if (!this.src) {
|
|
53
|
+
this.onError();
|
|
54
|
+
} else {
|
|
55
|
+
this.startObserver();
|
|
56
|
+
}
|
|
30
57
|
},
|
|
31
58
|
|
|
32
59
|
beforeUnmount() {
|
|
33
60
|
const img = this.$refs.img;
|
|
34
61
|
|
|
35
|
-
if (img) {
|
|
62
|
+
if (img && this.boundError) {
|
|
36
63
|
img.removeEventListener('error', this.boundError);
|
|
37
64
|
}
|
|
65
|
+
|
|
66
|
+
if (this.observer) {
|
|
67
|
+
this.observer.disconnect();
|
|
68
|
+
this.observer = null;
|
|
69
|
+
}
|
|
38
70
|
},
|
|
39
71
|
|
|
40
72
|
methods: {
|
|
73
|
+
startObserver() {
|
|
74
|
+
if (this.src && !this.observer && this.$refs.img) {
|
|
75
|
+
this.observer = new IntersectionObserver((entries) => {
|
|
76
|
+
const image = entries[0];
|
|
77
|
+
|
|
78
|
+
if (image.isIntersecting) {
|
|
79
|
+
this.intersected = true;
|
|
80
|
+
this.loadImage();
|
|
81
|
+
// Once the image is loaded, we don't need the observer anymore
|
|
82
|
+
if (this.observer) {
|
|
83
|
+
this.observer.disconnect();
|
|
84
|
+
this.observer = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
this.observer.observe(this.$refs.img);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
|
|
41
92
|
// Ensure we load the image when the source changes
|
|
42
93
|
loadImage() {
|
|
43
94
|
const img = this.$refs.img;
|
|
44
95
|
|
|
45
96
|
if (this.src) {
|
|
97
|
+
// Remove previous error listener if any
|
|
98
|
+
if (img && this.boundError) {
|
|
99
|
+
img.removeEventListener('error', this.boundError);
|
|
100
|
+
}
|
|
101
|
+
|
|
46
102
|
img.setAttribute('src', this.src);
|
|
47
103
|
this.boundError = this.onError.bind(this);
|
|
48
104
|
|
package/components/Loading.vue
CHANGED
|
@@ -80,7 +80,7 @@ export default {
|
|
|
80
80
|
<rc-dropdown-trigger
|
|
81
81
|
data-testid="locale-selector"
|
|
82
82
|
link
|
|
83
|
-
class="baseline"
|
|
83
|
+
class="baseline locale-selector-btn"
|
|
84
84
|
:aria-label="t('locale.menu')"
|
|
85
85
|
>
|
|
86
86
|
{{ selectedLocaleLabel }}
|
|
@@ -88,7 +88,7 @@ export default {
|
|
|
88
88
|
v-if="showIcon"
|
|
89
89
|
#after
|
|
90
90
|
>
|
|
91
|
-
<i class="
|
|
91
|
+
<i class="ml-5 icon icon-chevron-down" />
|
|
92
92
|
</template>
|
|
93
93
|
</rc-dropdown-trigger>
|
|
94
94
|
<template #dropdownCollection>
|
|
@@ -126,4 +126,9 @@ export default {
|
|
|
126
126
|
.baseline {
|
|
127
127
|
align-items: baseline;
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
.locale-selector-btn {
|
|
131
|
+
align-items: center;
|
|
132
|
+
display: flex;
|
|
133
|
+
}
|
|
129
134
|
</style>
|
package/components/Markdown.vue
CHANGED
|
@@ -36,6 +36,35 @@ export default defineComponent({
|
|
|
36
36
|
default: null,
|
|
37
37
|
},
|
|
38
38
|
|
|
39
|
+
groupTooltip: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: 'resourceTable.groupBy.namespace',
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Field to group rows by, row[groupBy] must be something that can be a map key (or also use groupSort)
|
|
46
|
+
*/
|
|
47
|
+
groupBy: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: null,
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Field to order groups by, defaults to groupBy
|
|
54
|
+
*/
|
|
55
|
+
groupSort: {
|
|
56
|
+
type: String,
|
|
57
|
+
default: null
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Override any product based group options
|
|
62
|
+
*/
|
|
63
|
+
groupOptions: {
|
|
64
|
+
type: Array,
|
|
65
|
+
default: null
|
|
66
|
+
},
|
|
67
|
+
|
|
39
68
|
groupable: {
|
|
40
69
|
type: Boolean,
|
|
41
70
|
default: null, // Null: auto based on namespaced and type custom groupings
|
|
@@ -58,6 +87,14 @@ export default defineComponent({
|
|
|
58
87
|
default: null,
|
|
59
88
|
},
|
|
60
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Use this store instead of the store `inStore` getters
|
|
92
|
+
*/
|
|
93
|
+
overrideInStore: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: undefined,
|
|
96
|
+
},
|
|
97
|
+
|
|
61
98
|
/**
|
|
62
99
|
* Information may be required from resources other than the primary one shown per row
|
|
63
100
|
*
|
|
@@ -79,7 +116,7 @@ export default defineComponent({
|
|
|
79
116
|
|
|
80
117
|
async fetch() {
|
|
81
118
|
const promises = [
|
|
82
|
-
this.$fetchType(this.resource, [], this.inStore),
|
|
119
|
+
this.$fetchType(this.resource, [], this.overrideInStore || this.inStore),
|
|
83
120
|
];
|
|
84
121
|
|
|
85
122
|
if (this.fetchSecondaryResources) {
|
|
@@ -115,13 +152,21 @@ export default defineComponent({
|
|
|
115
152
|
:rows="rows"
|
|
116
153
|
:alt-loading="canPaginate && !isFirstLoad"
|
|
117
154
|
:loading="loading"
|
|
155
|
+
|
|
156
|
+
:group-by="groupBy"
|
|
157
|
+
:group-sort="groupSort"
|
|
118
158
|
:groupable="groupable"
|
|
159
|
+
:groupTooltip="groupTooltip"
|
|
160
|
+
:groupOptions="groupOptions"
|
|
161
|
+
|
|
162
|
+
:override-in-store="overrideInStore"
|
|
119
163
|
|
|
120
164
|
:headers="safeHeaders"
|
|
121
165
|
:namespaced="namespaced"
|
|
122
166
|
|
|
123
167
|
:external-pagination-enabled="canPaginate"
|
|
124
168
|
:external-pagination-result="paginationResult"
|
|
169
|
+
|
|
125
170
|
@pagination-changed="paginationChanged"
|
|
126
171
|
>
|
|
127
172
|
<!-- Pass down templates provided by the caller -->
|
|
@@ -7,7 +7,7 @@ import Date from '@shell/components/formatter/Date.vue';
|
|
|
7
7
|
import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
|
|
8
8
|
import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
|
|
9
9
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
|
10
|
-
import { CAPI,
|
|
10
|
+
import { CAPI, SNAPSHOT } from '@shell/config/types';
|
|
11
11
|
import { set } from '@shell/utils/object';
|
|
12
12
|
import ChildHook, { BEFORE_SAVE_HOOKS } from '@shell/mixins/child-hook';
|
|
13
13
|
import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
|
|
@@ -47,14 +47,13 @@ export default {
|
|
|
47
47
|
},
|
|
48
48
|
|
|
49
49
|
computed: {
|
|
50
|
-
// toRestore can be a provisioning.cattle.io.cluster or a rke.cattle.io.etcdsnapshot
|
|
50
|
+
// toRestore can be a provisioning.cattle.io.cluster or a rke.cattle.io.etcdsnapshot resource
|
|
51
51
|
...mapState('action-menu', ['showPromptRestore', 'toRestore']),
|
|
52
52
|
...mapGetters({ t: 'i18n/t' }),
|
|
53
53
|
|
|
54
54
|
// Was the dialog opened to restore a specific snapshot, or opened on a cluster to choose
|
|
55
55
|
isCluster() {
|
|
56
|
-
const isSnapshot = this.toRestore[0]?.type.toLowerCase() ===
|
|
57
|
-
this.toRestore[0]?.type.toLowerCase() === SNAPSHOT;
|
|
56
|
+
const isSnapshot = this.toRestore[0]?.type.toLowerCase() === SNAPSHOT;
|
|
58
57
|
|
|
59
58
|
return !isSnapshot;
|
|
60
59
|
},
|
|
@@ -79,9 +78,7 @@ export default {
|
|
|
79
78
|
}
|
|
80
79
|
},
|
|
81
80
|
restoreModeOptions() {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return [etcdOption, 'kubernetesVersion', 'all'];
|
|
81
|
+
return ['none', 'kubernetesVersion', 'all'];
|
|
85
82
|
}
|
|
86
83
|
},
|
|
87
84
|
|
|
@@ -112,20 +109,12 @@ export default {
|
|
|
112
109
|
}
|
|
113
110
|
|
|
114
111
|
const cluster = this.toRestore?.[0];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (!cluster?.isRke2) {
|
|
118
|
-
promise = this.$store.dispatch('rancher/findAll', { type: NORMAN.ETCD_BACKUP }).then((snapshots) => {
|
|
119
|
-
return snapshots.filter((s) => s.state === STATES_ENUM.ACTIVE && s.clusterId === cluster.metadata.name);
|
|
120
|
-
});
|
|
121
|
-
} else {
|
|
122
|
-
promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
|
|
123
|
-
const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
|
|
112
|
+
const promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
|
|
113
|
+
const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
|
|
124
114
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
115
|
+
return snapshots.filter((s) => s?.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && s.clusterName === toRestoreClusterName
|
|
116
|
+
);
|
|
117
|
+
});
|
|
129
118
|
|
|
130
119
|
// Map of snapshots by name
|
|
131
120
|
const allSnapshots = await promise.then((snapshots) => {
|
|
@@ -154,30 +143,19 @@ export default {
|
|
|
154
143
|
|
|
155
144
|
async apply(buttonDone) {
|
|
156
145
|
try {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
await cluster.save();
|
|
171
|
-
} else {
|
|
172
|
-
await this.$store.dispatch('rancher/request', {
|
|
173
|
-
url: `/v3/clusters/${ escape(this.snapshot.clusterId) }?action=restoreFromEtcdBackup`,
|
|
174
|
-
method: 'post',
|
|
175
|
-
data: {
|
|
176
|
-
etcdBackupId: this.snapshot.id,
|
|
177
|
-
restoreRkeConfig: this.restoreMode,
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
}
|
|
146
|
+
const cluster = this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.snapshot.clusterId);
|
|
147
|
+
|
|
148
|
+
await this.applyHooks(BEFORE_SAVE_HOOKS);
|
|
149
|
+
|
|
150
|
+
const now = cluster.spec?.rkeConfig?.etcdSnapshotRestore?.generation || 0;
|
|
151
|
+
|
|
152
|
+
set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', {
|
|
153
|
+
generation: now + 1,
|
|
154
|
+
name: this.snapshot.name,
|
|
155
|
+
restoreRKEConfig: this.restoreMode,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
await cluster.save();
|
|
181
159
|
|
|
182
160
|
this.$store.dispatch('growl/success', {
|
|
183
161
|
title: this.t('promptRestore.notification.title'),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useDefaultMetadataForLegacyPagesProps } from '@shell/components/Resource/Detail/Metadata/composables';
|
|
2
|
+
import { useDefaultTitleBarProps } from '@shell/components/Resource/Detail/TitleBar/composables';
|
|
3
|
+
import { MastheadProps } from '@shell/components/Resource/Detail/Masthead/index.vue';
|
|
4
|
+
import { computed, Ref } from 'vue';
|
|
5
|
+
|
|
6
|
+
export const useDefaultMastheadProps = (resource: any): Ref<MastheadProps> => {
|
|
7
|
+
const titleBarProps = useDefaultTitleBarProps(resource);
|
|
8
|
+
const metadataProps = useDefaultMetadataForLegacyPagesProps(resource);
|
|
9
|
+
|
|
10
|
+
return computed(() => {
|
|
11
|
+
return {
|
|
12
|
+
titleBarProps: titleBarProps.value,
|
|
13
|
+
metadataProps: metadataProps.value,
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import TitleBar, { TitleBarProps } from '@shell/components/Resource/Detail/TitleBar/index.vue';
|
|
3
|
+
import Metadata, { MetadataProps } from '@shell/components/Resource/Detail/Metadata/index.vue';
|
|
4
|
+
|
|
5
|
+
export interface MastheadProps {
|
|
6
|
+
titleBarProps: TitleBarProps;
|
|
7
|
+
metadataProps: MetadataProps;
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
const props = defineProps<MastheadProps>();
|
|
13
|
+
|
|
14
|
+
</script>
|
|
15
|
+
<template>
|
|
16
|
+
<div class="masthead">
|
|
17
|
+
<TitleBar v-bind="props.titleBarProps">
|
|
18
|
+
<template
|
|
19
|
+
v-if="$slots['additional-actions']"
|
|
20
|
+
#additional-actions
|
|
21
|
+
>
|
|
22
|
+
<slot name="additional-actions" />
|
|
23
|
+
</template>
|
|
24
|
+
</TitleBar>
|
|
25
|
+
<Metadata
|
|
26
|
+
v-bind="props.metadataProps"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<style lang='scss' scoped>
|
|
32
|
+
.masthead {
|
|
33
|
+
:deep().metadata {
|
|
34
|
+
margin-top: 24px;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -3,7 +3,7 @@ import { Row } from '@shell/components/Resource/Detail/Metadata/IdentifyingInfor
|
|
|
3
3
|
import {
|
|
4
4
|
useCertificate,
|
|
5
5
|
useExpires,
|
|
6
|
-
useImage, useIssuer, useLiveDate, useNamespace, useReady, useSecretType,
|
|
6
|
+
useImage, useIssuer, useLiveDate, useNamespace, useProject, useReady, useSecretCluster, useSecretType,
|
|
7
7
|
useServiceAccount
|
|
8
8
|
} from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields';
|
|
9
9
|
import { useStore } from 'vuex';
|
|
@@ -21,7 +21,11 @@ export const useDefaultIdentifyingInformation = (resource: any): ComputedRef<Row
|
|
|
21
21
|
});
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export const useSecretIdentifyingInformation = (resource: any): ComputedRef<Row[]> => {
|
|
24
|
+
export const useSecretIdentifyingInformation = (resource: any, isProjectSecret: boolean): ComputedRef<Row[]> => {
|
|
25
|
+
const namespace = isProjectSecret ? undefined : useNamespace(resource);
|
|
26
|
+
const project = isProjectSecret ? useProject(resource) : undefined;
|
|
27
|
+
const cluster = isProjectSecret ? useSecretCluster(resource) : undefined;
|
|
28
|
+
const age = useLiveDate(resource);
|
|
25
29
|
const secretType = useSecretType(resource);
|
|
26
30
|
const serviceAccount = useServiceAccount(resource);
|
|
27
31
|
const certificate = useCertificate(resource);
|
|
@@ -30,6 +34,10 @@ export const useSecretIdentifyingInformation = (resource: any): ComputedRef<Row[
|
|
|
30
34
|
|
|
31
35
|
return computed(() => {
|
|
32
36
|
const rows = [
|
|
37
|
+
age?.value,
|
|
38
|
+
namespace?.value,
|
|
39
|
+
project?.value,
|
|
40
|
+
cluster?.value,
|
|
33
41
|
secretType?.value,
|
|
34
42
|
serviceAccount?.value,
|
|
35
43
|
certificate?.value,
|
|
@@ -2,7 +2,9 @@ import { useI18n } from '@shell/composables/useI18n';
|
|
|
2
2
|
import { computed, ComputedRef, markRaw, toValue } from 'vue';
|
|
3
3
|
import Additional from '@shell/components/Resource/Detail/Additional.vue';
|
|
4
4
|
import { useStore } from 'vuex';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
NAMESPACE, FLEET, SERVICE_ACCOUNT, SECRET, CAPI
|
|
7
|
+
} from '@shell/config/types';
|
|
6
8
|
import { Row } from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue';
|
|
7
9
|
import { NAME as FLEET_NAME } from '@shell/config/product/fleet';
|
|
8
10
|
import { useRoute } from 'vue-router';
|
|
@@ -14,7 +16,7 @@ export const useNamespace = (resource: any): ComputedRef<Row> | undefined => {
|
|
|
14
16
|
const i18n = useI18n(store);
|
|
15
17
|
const resourceValue = toValue(resource);
|
|
16
18
|
|
|
17
|
-
if (!resourceValue.namespace || resourceValue.namespaces) {
|
|
19
|
+
if (!resourceValue.namespace || resourceValue.namespaces || resourceValue.isProjectScoped) {
|
|
18
20
|
return;
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -113,7 +115,12 @@ export const useProject = (resource: any): ComputedRef<Row> | undefined => {
|
|
|
113
115
|
const i18n = useI18n(store);
|
|
114
116
|
const resourceValue = toValue(resource);
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
// Only show project if one of these types
|
|
119
|
+
if (resource.type !== NAMESPACE && resource.type !== SECRET) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!resourceValue.project) {
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
119
126
|
|
|
@@ -126,10 +133,22 @@ export const useProject = (resource: any): ComputedRef<Row> | undefined => {
|
|
|
126
133
|
});
|
|
127
134
|
};
|
|
128
135
|
|
|
136
|
+
export const useSecretCluster = (resource: any): ComputedRef<Row> | undefined => {
|
|
137
|
+
const store = useStore();
|
|
138
|
+
const resourceValue = toValue(resource);
|
|
139
|
+
|
|
140
|
+
return computed(() => {
|
|
141
|
+
return {
|
|
142
|
+
label: store.getters['type-map/labelFor']({ id: CAPI.RANCHER_CLUSTER }),
|
|
143
|
+
value: resourceValue.projectCluster?.nameDisplay,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
129
148
|
export const useResourceDetails = (resource: any): undefined | ComputedRef<Row[]> => {
|
|
130
|
-
const details = resource.details;
|
|
149
|
+
const details = computed(() => resource.details);
|
|
131
150
|
|
|
132
|
-
if (!details) {
|
|
151
|
+
if (!details.value) {
|
|
133
152
|
return;
|
|
134
153
|
}
|
|
135
154
|
|
|
@@ -148,8 +167,8 @@ export const useResourceDetails = (resource: any): undefined | ComputedRef<Row[]
|
|
|
148
167
|
};
|
|
149
168
|
|
|
150
169
|
return computed(() => {
|
|
151
|
-
return details
|
|
152
|
-
.filter((detail: any) => !detail.separator)
|
|
170
|
+
return details.value
|
|
171
|
+
.filter((detail: any) => !detail.separator && detail.content !== undefined && detail.content !== null)
|
|
153
172
|
.map((detail: any) => {
|
|
154
173
|
return {
|
|
155
174
|
label: detail.label,
|
|
@@ -34,7 +34,7 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
|
|
|
34
34
|
:data-testid="row.dataTestid"
|
|
35
35
|
>
|
|
36
36
|
<label
|
|
37
|
-
class="label text-
|
|
37
|
+
class="label text-deemphasized"
|
|
38
38
|
:for="getRowValueId(row)"
|
|
39
39
|
>
|
|
40
40
|
{{ row.label }}
|
|
@@ -95,6 +95,13 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
|
|
|
95
95
|
display: flex;
|
|
96
96
|
flex-direction: row;
|
|
97
97
|
align-items: center;
|
|
98
|
+
|
|
99
|
+
&, & * {
|
|
100
|
+
max-width: 100%;
|
|
101
|
+
overflow: hidden;
|
|
102
|
+
text-overflow: ellipsis;
|
|
103
|
+
white-space: nowrap;
|
|
104
|
+
}
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
.label {
|
|
@@ -54,12 +54,12 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
|
|
|
54
54
|
<template>
|
|
55
55
|
<div class="key-value">
|
|
56
56
|
<div class="heading">
|
|
57
|
-
<span class="title text-
|
|
57
|
+
<span class="title text-deemphasized">{{ propertyName }}</span>
|
|
58
58
|
<span class="count">{{ rows.length }}</span>
|
|
59
59
|
</div>
|
|
60
60
|
<div
|
|
61
61
|
v-if="visibleRows.length === 0"
|
|
62
|
-
class="empty mmt-2 text-
|
|
62
|
+
class="empty mmt-2 text-deemphasized"
|
|
63
63
|
>
|
|
64
64
|
<div class="no-rows">
|
|
65
65
|
{{ i18n.t('component.resource.detail.metadata.keyValue.noRows', {propertyName: lowercasePropertyName}) }}
|
|
@@ -67,7 +67,7 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
|
|
|
67
67
|
<div class="show-configuration mmt-1">
|
|
68
68
|
<a
|
|
69
69
|
:data-testid="showConfigurationEmptyDataTestId"
|
|
70
|
-
class="secondary text-
|
|
70
|
+
class="secondary text-deemphasized"
|
|
71
71
|
href="#"
|
|
72
72
|
@click="(ev: MouseEvent) => {ev.preventDefault(); emit('show-configuration', showConfigurationEmptyFocusSelector);}"
|
|
73
73
|
>
|
|
@@ -91,7 +91,7 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
|
|
|
91
91
|
v-if="showShowAllButton"
|
|
92
92
|
:data-testid="showConfigurationMoreDataTestId"
|
|
93
93
|
href="#"
|
|
94
|
-
class="show-all
|
|
94
|
+
class="show-all"
|
|
95
95
|
@click="(ev: MouseEvent) => {ev.preventDefault(); emit('show-configuration', showConfigurationMoreFocusSelector);}"
|
|
96
96
|
>
|
|
97
97
|
{{ showAllLabel }}
|
|
@@ -110,29 +110,31 @@ const showConfigurationMoreFocusSelector = computed(() => `[data-testid="${ show
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
.heading {
|
|
113
|
-
margin-bottom:
|
|
113
|
+
margin-bottom: 8px;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
.row {
|
|
117
|
+
display: block;
|
|
117
118
|
width: 100%;
|
|
118
119
|
|
|
119
|
-
&:not(:
|
|
120
|
+
&:not(:nth-child(2)) {
|
|
120
121
|
margin-top: 4px;
|
|
121
122
|
}
|
|
122
|
-
|
|
123
|
-
& {
|
|
124
|
-
margin-top: 8px;
|
|
125
|
-
}
|
|
126
123
|
}
|
|
127
124
|
.show-all {
|
|
128
125
|
margin-top: 8px;
|
|
129
126
|
}
|
|
130
127
|
|
|
131
128
|
.rectangle {
|
|
129
|
+
display: inline-block;
|
|
132
130
|
max-width: 100%;
|
|
133
131
|
overflow: hidden;
|
|
134
132
|
text-overflow: ellipsis;
|
|
135
133
|
white-space: nowrap;
|
|
136
134
|
}
|
|
135
|
+
|
|
136
|
+
.no-rows {
|
|
137
|
+
line-height: 21px;
|
|
138
|
+
}
|
|
137
139
|
}
|
|
138
140
|
</style>
|
|
@@ -2,16 +2,7 @@ import { useDefaultMetadataForLegacyPagesProps } from '@shell/components/Resourc
|
|
|
2
2
|
import * as IdentifyingFields from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields';
|
|
3
3
|
import { computed } from 'vue';
|
|
4
4
|
|
|
5
|
-
const mockDrawer = { openResourceDetailDrawer: jest.fn() };
|
|
6
|
-
|
|
7
5
|
jest.mock('@shell/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields');
|
|
8
|
-
jest.mock('@shell/components/Drawer/ResourceDetailDrawer/composables', () => {
|
|
9
|
-
return {
|
|
10
|
-
useResourceDetailDrawer() {
|
|
11
|
-
return mockDrawer;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
});
|
|
15
6
|
|
|
16
7
|
describe('composables: Metadata/composables', () => {
|
|
17
8
|
beforeEach(() => {
|
|
@@ -28,9 +19,10 @@ describe('composables: Metadata/composables', () => {
|
|
|
28
19
|
|
|
29
20
|
it('should filter out undefined identifyingInformation', () => {
|
|
30
21
|
const resource = {
|
|
31
|
-
type:
|
|
32
|
-
annotations:
|
|
33
|
-
labels:
|
|
22
|
+
type: 'RESOURCE',
|
|
23
|
+
annotations: { annotation: 'ANNOTATION' },
|
|
24
|
+
labels: { label: 'LABEL' },
|
|
25
|
+
showConfiguration: jest.fn()
|
|
34
26
|
};
|
|
35
27
|
const result = useDefaultMetadataForLegacyPagesProps(resource);
|
|
36
28
|
|
|
@@ -39,7 +31,7 @@ describe('composables: Metadata/composables', () => {
|
|
|
39
31
|
expect(result.value.annotations).toStrictEqual([{ key: 'annotation', value: resource.annotations.annotation }]);
|
|
40
32
|
expect(result.value.labels).toStrictEqual([{ key: 'label', value: resource.labels.label }]);
|
|
41
33
|
expect(result.value.identifyingInformation).toHaveLength(0);
|
|
42
|
-
expect(
|
|
34
|
+
expect(resource.showConfiguration).toHaveBeenCalledTimes(1);
|
|
43
35
|
});
|
|
44
36
|
|
|
45
37
|
it('should fill identifyingInformation', () => {
|
|
@@ -51,9 +43,10 @@ describe('composables: Metadata/composables', () => {
|
|
|
51
43
|
useResourceDetailsSpy.mockReturnValue(computed(() => [{ label: 'RESOURCE_DETAILS' }]));
|
|
52
44
|
|
|
53
45
|
const resource = {
|
|
54
|
-
type:
|
|
55
|
-
annotations:
|
|
56
|
-
labels:
|
|
46
|
+
type: 'RESOURCE',
|
|
47
|
+
annotations: { annotation: 'ANNOTATION' },
|
|
48
|
+
labels: { label: 'LABEL' },
|
|
49
|
+
showConfiguration: jest.fn()
|
|
57
50
|
};
|
|
58
51
|
const result = useDefaultMetadataForLegacyPagesProps(resource);
|
|
59
52
|
|
|
@@ -69,7 +62,7 @@ describe('composables: Metadata/composables', () => {
|
|
|
69
62
|
{ label: 'CREATED_BY' },
|
|
70
63
|
{ label: 'RESOURCE_DETAILS' }
|
|
71
64
|
]);
|
|
72
|
-
expect(
|
|
65
|
+
expect(resource.showConfiguration).toHaveBeenCalledTimes(1);
|
|
73
66
|
});
|
|
74
67
|
});
|
|
75
68
|
});
|