@rancher/shell 3.0.5-rc.6 → 3.0.5-rc.7
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/_variables.scss +17 -11
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +8 -2
- package/assets/translations/en-us.yaml +26 -4
- package/components/CodeMirror.vue +1 -1
- package/components/Drawer/Chrome.vue +0 -1
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +26 -2
- package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -1
- package/components/Drawer/ResourceDetailDrawer/index.vue +1 -0
- package/components/Loading.vue +1 -1
- package/components/PaginatedResourceTable.vue +46 -1
- package/components/PromptRestore.vue +22 -44
- package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +21 -2
- 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/SpacedRow.vue +1 -1
- package/components/Resource/Detail/TitleBar/composables.ts +4 -3
- package/components/Resource/Detail/TitleBar/index.vue +2 -2
- package/components/ResourceDetail/Masthead/legacy.vue +1 -1
- package/components/ResourceDetail/index.vue +5 -3
- 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 +3 -7
- package/components/SortableTable/index.vue +1 -1
- package/components/SortableTable/selection.js +0 -1
- package/components/Tabbed/index.vue +6 -1
- package/components/__tests__/PromptRestore.test.ts +1 -65
- package/components/__tests__/RichTranslation.test.ts +115 -0
- package/components/fleet/dashboard/ResourcePanel.vue +2 -1
- package/components/form/FileImageSelector.vue +1 -1
- package/components/form/NameNsDescription.vue +1 -0
- package/components/form/Networking.vue +24 -19
- package/components/form/ResourceLabeledSelect.vue +4 -3
- package/components/form/SelectOrCreateAuthSecret.vue +6 -3
- package/components/form/__tests__/Networking.test.ts +116 -0
- 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/Group.vue +4 -1
- package/composables/resources.ts +2 -2
- package/config/labels-annotations.js +2 -0
- 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/provisioning.cattle.io.cluster.vue +3 -47
- package/dialog/RotateEncryptionKeyDialog.vue +10 -30
- 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/networking.k8s.io.ingress/Certificate.vue +12 -12
- package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
- package/edit/provisioning.cattle.io.cluster/rke2.vue +102 -48
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +2 -2
- package/edit/secret/basic.vue +1 -0
- package/edit/secret/index.vue +126 -15
- package/list/projectsecret.vue +345 -0
- package/list/secret.vue +109 -0
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/create-edit-view/impl.js +10 -1
- package/mixins/resource-fetch-api-pagination.js +9 -9
- package/mixins/resource-fetch.js +3 -1
- package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
- package/models/fleet.cattle.io.cluster.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +24 -28
- package/models/secret.js +157 -2
- package/package.json +2 -2
- package/pages/c/_cluster/apps/charts/index.vue +46 -35
- package/pages/c/_cluster/explorer/projectsecret.vue +34 -0
- package/pages/c/_cluster/fleet/index.vue +0 -1
- package/pages/explorer/resource/detail/projectsecret.vue +9 -0
- package/pages/explorer/resource/detail/secret.vue +18 -5
- 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/normalize.js +29 -17
- package/plugins/dashboard-store/resource-class.js +52 -17
- package/plugins/steve/steve-pagination-utils.ts +14 -3
- package/types/kube/kube-api.ts +12 -0
- package/types/shell/index.d.ts +616 -558
- package/types/store/pagination.types.ts +16 -6
- package/utils/__tests__/create-yaml.test.ts +235 -0
- package/utils/create-yaml.js +103 -9
- package/utils/pagination-utils.ts +18 -0
- package/models/etcdbackup.js +0 -45
|
@@ -42,25 +42,31 @@ $z-indexes: (
|
|
|
42
42
|
|
|
43
43
|
cruFooter: 19,
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
modalContent: 21,
|
|
45
|
+
loadingMain: 51,
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
slide-in: 52,
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
modalOverlay: 53,
|
|
50
|
+
modalContent: 54,
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
dropdownOverlay: 55,
|
|
53
|
+
dropdownContent: 56,
|
|
54
|
+
|
|
55
|
+
tooltip: 57,
|
|
54
56
|
);
|
|
55
57
|
|
|
56
58
|
// Usage Example:
|
|
57
59
|
// @media only screen and (min-width: map-get($breakpoints, '--viewport-*')) {
|
|
58
60
|
// }
|
|
59
61
|
$breakpoints: (
|
|
60
|
-
'--viewport-4':
|
|
61
|
-
|
|
62
|
-
'--viewport-
|
|
63
|
-
|
|
62
|
+
'--viewport-4': 480px,
|
|
63
|
+
// Phone
|
|
64
|
+
'--viewport-7': 768px,
|
|
65
|
+
// Tablet
|
|
66
|
+
'--viewport-9': 992px,
|
|
67
|
+
// Laptop/Desktop
|
|
68
|
+
'--viewport-12': 1281px,
|
|
69
|
+
// Desktop
|
|
64
70
|
);
|
|
65
71
|
|
|
66
72
|
$font-size-h2: 21px;
|
|
@@ -68,4 +74,4 @@ $font-size-h2: 21px;
|
|
|
68
74
|
// Global spacing variables
|
|
69
75
|
$space-s: 10px;
|
|
70
76
|
$space-m: 20px;
|
|
71
|
-
$space-l: 40px;
|
|
77
|
+
$space-l: 40px;
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
--slider-light-bg-right : #{rgba($darker, 0)};
|
|
52
52
|
|
|
53
53
|
--muted : #{$disabled};
|
|
54
|
+
--deemphasized : #{$disabled};
|
|
54
55
|
|
|
55
56
|
--body-bg : #{$darker};
|
|
56
57
|
--body-text : #{$lightest};
|
|
@@ -94,6 +95,7 @@
|
|
|
94
95
|
|
|
95
96
|
--modal-bg : #{$dark};
|
|
96
97
|
--modal-border : #{$medium};
|
|
98
|
+
--subtle-overlay-bg : #{rgba($darkest, 0.75)};
|
|
97
99
|
--overlay-bg : #{rgba($darkest, 0.75)};
|
|
98
100
|
--shadow : #{rgba($darkest, 0.9)};
|
|
99
101
|
|
|
@@ -156,11 +156,16 @@ BODY, .theme-light {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
--muted : #{$dark};
|
|
159
|
+
--deemphasized : #717179;
|
|
159
160
|
|
|
160
161
|
.text-muted {
|
|
161
162
|
color: var(--muted) !important;
|
|
162
163
|
}
|
|
163
164
|
|
|
165
|
+
.text-deemphasized {
|
|
166
|
+
color: var(--deemphasized);
|
|
167
|
+
}
|
|
168
|
+
|
|
164
169
|
--darker : #{$darker};
|
|
165
170
|
--darker-text : #{contrast-color($darker)};
|
|
166
171
|
--darker-hover-bg : #{darken($darker, 10%)};
|
|
@@ -402,7 +407,8 @@ BODY, .theme-light {
|
|
|
402
407
|
|
|
403
408
|
--modal-bg : #{$lightest};
|
|
404
409
|
--modal-border : #{$dark};
|
|
405
|
-
--overlay-bg
|
|
410
|
+
--subtle-overlay-bg : #{rgba($lighter, 0.75)};
|
|
411
|
+
--overlay-bg : rgba(38, 42, 64, 0.35);
|
|
406
412
|
--shadow : #{rgba($medium, 0.85)};
|
|
407
413
|
|
|
408
414
|
--checkbox-tick : #{$lightest};
|
|
@@ -468,7 +474,7 @@ BODY, .theme-light {
|
|
|
468
474
|
--sortable-table-group-label : #{$secondary};
|
|
469
475
|
|
|
470
476
|
--tag-primary : #{$darkest};
|
|
471
|
-
--tag-bg : #
|
|
477
|
+
--tag-bg : #EEEFF5;
|
|
472
478
|
|
|
473
479
|
--popover-bg : var(--body-bg);
|
|
474
480
|
--popover-border : var(--border);
|
|
@@ -607,6 +607,9 @@ authConfig:
|
|
|
607
607
|
starttls:
|
|
608
608
|
label: Start TLS
|
|
609
609
|
tip: Upgrades non-encrypted connections by wrapping with TLS during the connection process. Can not be used in conjunction with TLS.
|
|
610
|
+
searchUsingServiceAccount:
|
|
611
|
+
label: Enable Service Account Search
|
|
612
|
+
tip: When enabled, Rancher will use the service account instead of the user account to search for users and groups.
|
|
610
613
|
tls: TLS
|
|
611
614
|
userEnabledAttribute: User Enabled Attribute
|
|
612
615
|
userMemberAttribute: User Member Attribute
|
|
@@ -1074,10 +1077,8 @@ catalog:
|
|
|
1074
1077
|
header: Charts
|
|
1075
1078
|
noCharts:
|
|
1076
1079
|
title: No charts to show
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
messagePart3: ', and ensure you have the right <a tabindex="0" href={repositoriesUrl} rel="noopener noreferrer nofollow">repositories</a> in place.'
|
|
1080
|
-
messagePart4: 'Want to learn more about Helm Charts and Apps? Read our <a tabindex="0" href="{docsBase}/how-to-guides/new-user-guides/helm-charts-in-rancher" target="_blank" class="secondary-text-link">documentation <i class="icon icon-external-link"></i></a>.'
|
|
1080
|
+
message: '<b>Tips:</b> undo the last filter you applied or <resetAllFilters>clear all filters</resetAllFilters>, and ensure you have the right <repositoriesUrl>repositories</repositoriesUrl> in place.'
|
|
1081
|
+
docsMessage: 'Want to learn more about Helm Charts and Apps? Read our <docsUrl>documentation</docsUrl>.'
|
|
1081
1082
|
noWindows: Your repos do not contain any charts capable of being deployed on a cluster with Windows nodes.
|
|
1082
1083
|
noWindowsAndLinux: Your repos do not contain any charts capable of being deployed on a cluster with both Windows and Linux worker nodes.
|
|
1083
1084
|
operatingSystems:
|
|
@@ -1654,6 +1655,9 @@ cluster:
|
|
|
1654
1655
|
sshUser:
|
|
1655
1656
|
placeholder: e.g. ubuntu
|
|
1656
1657
|
toolTip: SSH user to login with the selected OS image.
|
|
1658
|
+
kubeconfigSecret:
|
|
1659
|
+
nameRequired: Cluster name is required.
|
|
1660
|
+
error: 'Error generating Harvester kubeconfig secret: {err}'
|
|
1657
1661
|
haveOneOwner: There must be at least one member with the Owner role.
|
|
1658
1662
|
import:
|
|
1659
1663
|
warningBanner: 'You should not import a cluster which has already been connected to another instance of Rancher as it will lead to data corruption.'
|
|
@@ -2302,6 +2306,7 @@ cluster:
|
|
|
2302
2306
|
server: Server compliance Profile
|
|
2303
2307
|
agent: Compliance Profile
|
|
2304
2308
|
override: Allow the default Pod Security Admission Configuration Template to be overridden when using a compliance profile
|
|
2309
|
+
warning: After saving changes, and if this cluster has already been provisioned, please restart the rke2-server service on all nodes to ensure etcd file permissions are correct
|
|
2305
2310
|
defaultPodSecurityPolicyTemplateName:
|
|
2306
2311
|
label: Default Pod Security Policy
|
|
2307
2312
|
option: Default - RKE2 Embedded
|
|
@@ -5868,6 +5873,13 @@ secret:
|
|
|
5868
5873
|
'kubernetes.io/tls':
|
|
5869
5874
|
description: Store a certificate and key for TLS
|
|
5870
5875
|
docLink: https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets
|
|
5876
|
+
tabs:
|
|
5877
|
+
namespaced:
|
|
5878
|
+
label: Namespaced
|
|
5879
|
+
create: Create Secret
|
|
5880
|
+
projectScoped:
|
|
5881
|
+
label: Project Scoped
|
|
5882
|
+
create: Create Project Secret
|
|
5871
5883
|
|
|
5872
5884
|
selectOrCreateAuthSecret:
|
|
5873
5885
|
label: Authentication
|
|
@@ -6570,6 +6582,11 @@ tableHeaders:
|
|
|
6570
6582
|
scope: Scope
|
|
6571
6583
|
selector: Selector
|
|
6572
6584
|
secrets: Secrets
|
|
6585
|
+
secret:
|
|
6586
|
+
project-clone: Created by Project Secret
|
|
6587
|
+
project-clone-tooltip: Has a Project Scoped Secret created this Secret in this Namespace
|
|
6588
|
+
project-scoped: Project Secret
|
|
6589
|
+
project-scoped-tooltip: This Project Scoped Secret will create matching Secrets in the Namespaces it contains
|
|
6573
6590
|
schedule: Schedule
|
|
6574
6591
|
service: Service
|
|
6575
6592
|
serviceAccounts: Service Accounts
|
|
@@ -7855,6 +7872,11 @@ typeLabel:
|
|
|
7855
7872
|
one { Secret }
|
|
7856
7873
|
other { Secrets }
|
|
7857
7874
|
}
|
|
7875
|
+
projectsecret: |-
|
|
7876
|
+
{count, plural,
|
|
7877
|
+
one { Project Secret }
|
|
7878
|
+
other { Project Secrets }
|
|
7879
|
+
}
|
|
7858
7880
|
apiregistration.k8s.io.apiservice: |-
|
|
7859
7881
|
{count, plural,
|
|
7860
7882
|
one { APIService }
|
|
@@ -26,11 +26,13 @@ describe('composables: ResourceDetailDrawer', () => {
|
|
|
26
26
|
describe('useDefaultConfigTabProps', () => {
|
|
27
27
|
const hasCustomEdit = jest.fn();
|
|
28
28
|
const importEdit = jest.fn();
|
|
29
|
+
const hasCustomDetail = jest.fn();
|
|
29
30
|
const editComponent = { component: 'EDIT_COMPONENT' };
|
|
30
31
|
const store: any = {
|
|
31
32
|
getters: {
|
|
32
|
-
'type-map/hasCustomEdit':
|
|
33
|
-
'type-map/importEdit':
|
|
33
|
+
'type-map/hasCustomEdit': hasCustomEdit,
|
|
34
|
+
'type-map/importEdit': importEdit,
|
|
35
|
+
'type-map/hasCustomDetail': hasCustomDetail,
|
|
34
36
|
}
|
|
35
37
|
};
|
|
36
38
|
|
|
@@ -43,13 +45,35 @@ describe('composables: ResourceDetailDrawer', () => {
|
|
|
43
45
|
expect(props).toBeUndefined();
|
|
44
46
|
});
|
|
45
47
|
|
|
48
|
+
it('should return undefined if it does not have a customDetail', async() => {
|
|
49
|
+
jest.spyOn(vuex, 'useStore').mockImplementation(() => store);
|
|
50
|
+
const hasCustomEditSpy = hasCustomEdit.mockImplementation(() => true);
|
|
51
|
+
const hasCustomDetailSpy = hasCustomDetail.mockImplementation(() => false);
|
|
52
|
+
const props = useDefaultConfigTabProps(resource);
|
|
53
|
+
|
|
54
|
+
expect(hasCustomEditSpy).toHaveBeenCalledWith(resource.type);
|
|
55
|
+
expect(hasCustomDetailSpy).toHaveBeenCalledWith(resource.type);
|
|
56
|
+
expect(props).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should return undefined if resource disableResourceDetailDrawerConfigTab is true', async() => {
|
|
60
|
+
jest.spyOn(vuex, 'useStore').mockImplementation(() => store);
|
|
61
|
+
const hasCustomDetailSpy = hasCustomDetail.mockImplementation(() => false);
|
|
62
|
+
const props = useDefaultConfigTabProps({ ...resource, disableResourceDetailDrawerConfigTab: true });
|
|
63
|
+
|
|
64
|
+
expect(hasCustomDetailSpy).toHaveBeenCalledWith(resource.type);
|
|
65
|
+
expect(props).toBeUndefined();
|
|
66
|
+
});
|
|
67
|
+
|
|
46
68
|
it('should return props if it has a customEdit', async() => {
|
|
47
69
|
jest.spyOn(vuex, 'useStore').mockImplementation(() => store);
|
|
48
70
|
const hasCustomEditSpy = hasCustomEdit.mockImplementation(() => true);
|
|
71
|
+
const hasCustomDetailSpy = hasCustomDetail.mockImplementation(() => true);
|
|
49
72
|
const importEditSpy = importEdit.mockImplementation(() => editComponent);
|
|
50
73
|
const props = useDefaultConfigTabProps(resource);
|
|
51
74
|
|
|
52
75
|
expect(hasCustomEditSpy).toHaveBeenCalledWith(resource.type);
|
|
76
|
+
expect(hasCustomDetailSpy).toHaveBeenCalledWith(resource.type);
|
|
53
77
|
expect(importEditSpy).toHaveBeenCalledWith(resource.type);
|
|
54
78
|
expect(props?.component).toStrictEqual(editComponent);
|
|
55
79
|
expect(props?.resource).toStrictEqual(resource);
|
|
@@ -38,7 +38,10 @@ export async function useDefaultYamlTabProps(resource: any): Promise<YamlTabProp
|
|
|
38
38
|
export function useDefaultConfigTabProps(resource: any): ConfigTabProps | undefined {
|
|
39
39
|
const store = useStore();
|
|
40
40
|
|
|
41
|
-
if
|
|
41
|
+
// You don't want to show the Config tab if there isn't a an edit page to show and you don't want to show it if there isn't
|
|
42
|
+
// a detail page because we default to showing the existing edit page if the detail page doesn't exist. Showing them again
|
|
43
|
+
// wouldn't be worth while.
|
|
44
|
+
if (!store.getters['type-map/hasCustomEdit'](resource.type) || !store.getters['type-map/hasCustomDetail'](resource.type) || resource.disableResourceDetailDrawerConfigTab) {
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
44
47
|
|
package/components/Loading.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'),
|
|
@@ -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';
|
|
@@ -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,6 +133,18 @@ 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
149
|
const details = resource.details;
|
|
131
150
|
|
|
@@ -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>
|