@rancher/shell 3.0.12-rc.4 → 3.0.12-rc.6
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/apis/intf/resources-api/resource-base.ts +42 -2
- package/apis/intf/resources-api/resource-instance.ts +101 -0
- package/apis/intf/resources-api/resources-api.ts +177 -23
- package/apis/intf/resources.ts +8 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
- package/apis/resources/resources-api-class.ts +232 -43
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/global/_button.scss +1 -1
- package/assets/translations/en-us.yaml +49 -14
- package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
- package/chart/rancher-monitoring-dashboards/index.vue +155 -0
- package/cloud-credential/__tests__/generic.test.ts +132 -0
- package/cloud-credential/generic.vue +33 -3
- package/components/ActionDropdownShell.vue +5 -3
- package/components/ButtonGroup.vue +26 -1
- package/components/CruResource.vue +51 -2
- package/components/EtcdInfoBanner.vue +2 -12
- package/components/GrafanaDashboard.vue +2 -8
- package/components/PromptRestore.vue +93 -32
- package/components/Questions/index.vue +1 -0
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
- package/components/Resource/Detail/Masthead/index.vue +8 -3
- package/components/ResourceTable.vue +1 -0
- package/components/SortableTable/index.vue +4 -3
- package/components/Wizard.vue +14 -1
- package/components/__tests__/ButtonGroup.test.ts +56 -0
- package/components/__tests__/PromptRestore.test.ts +169 -19
- package/components/fleet/GitRepoAdvancedTab.vue +1 -0
- package/components/fleet/GitRepoMetadataTab.vue +5 -0
- package/components/fleet/HelmOpAppCoConfigTab.vue +4 -0
- package/components/fleet/HelmOpMetadataTab.vue +5 -0
- package/components/form/FileSelector.vue +39 -1
- package/components/form/PrivateRegistry.constants.ts +7 -0
- package/components/form/PrivateRegistry.vue +253 -18
- package/components/form/Security.vue +1 -1
- package/components/form/SelectOrCreateAuthSecret.vue +140 -17
- package/components/form/__tests__/FileSelector.test.ts +23 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
- package/components/formatter/EtcdSnapshotName.vue +73 -0
- package/components/formatter/Translate.vue +22 -1
- package/components/nav/Group.vue +5 -10
- package/components/nav/Header.vue +8 -1
- package/components/nav/TopLevelMenu.vue +1 -30
- package/components/nav/__tests__/Group.test.ts +61 -0
- package/components/templates/default.vue +7 -0
- package/config/features.js +1 -0
- package/config/labels-annotations.js +3 -0
- package/config/product/manager.js +6 -0
- package/config/secret.ts +10 -0
- package/config/settings.ts +6 -2
- package/config/table-headers.js +5 -2
- package/config/types.js +7 -0
- package/core/__tests__/plugin-products-apply.test.ts +787 -0
- package/core/__tests__/plugin-products-extend.test.ts +189 -0
- package/core/__tests__/plugin-products-helpers.test.ts +81 -78
- package/core/__tests__/plugin-products-new.test.ts +389 -0
- package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
- package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
- package/core/plugin-products-base.ts +201 -93
- package/core/plugin-products-extending.ts +1 -1
- package/core/plugin-products-external.ts +380 -0
- package/core/plugin-products-helpers.ts +28 -24
- package/core/plugin-products-internal.ts +207 -0
- package/core/plugin-products-top-level.ts +10 -5
- package/core/plugin-products-type-guards.ts +17 -6
- package/core/plugin-products.ts +5 -7
- package/core/plugin-types.ts +1 -389
- package/core/plugin.ts +10 -11
- package/core/types.ts +6 -202
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +79 -5
- package/dialog/RotateEncryptionKeyDialog.vue +33 -9
- package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +101 -0
- package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
- package/edit/auth/AuthProviderWarningBanners.vue +12 -0
- package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
- package/edit/auth/__tests__/azuread.test.ts +1 -0
- package/edit/auth/__tests__/github.test.ts +1 -0
- package/edit/auth/__tests__/oidc.test.ts +1 -0
- package/edit/auth/__tests__/saml.test.ts +1 -0
- package/edit/auth/ldap/__tests__/index.test.ts +1 -0
- package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
- package/edit/fleet.cattle.io.gitrepo.vue +70 -16
- package/edit/fleet.cattle.io.helmop.vue +51 -5
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
- package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +6 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
- package/list/management.cattle.io.user.vue +24 -5
- package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
- package/machine-config/__tests__/generic.test.ts +163 -0
- package/machine-config/components/EC2Networking.vue +31 -7
- package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
- package/machine-config/generic.vue +32 -5
- package/mixins/resource-fetch.js +1 -1
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +156 -0
- package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
- package/models/__tests__/secret.test.ts +68 -1
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/pod.js +25 -12
- package/models/provisioning.cattle.io.cluster.js +59 -9
- package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
- package/models/rke.cattle.io.etcdsnapshot.js +17 -9
- package/models/secret.js +19 -0
- package/models/workload.js +12 -7
- package/package.json +1 -1
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +569 -81
- package/pages/c/_cluster/apps/charts/install.vue +143 -28
- package/pages/c/_cluster/auth/config/index.vue +11 -4
- package/pages/c/_cluster/monitoring/index.vue +6 -1
- package/pkg/require-asset.lib.js +25 -0
- package/pkg/vue.config.js +7 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +84 -0
- package/plugins/dashboard-store/getters.js +0 -1
- package/plugins/dashboard-store/resource-class.js +62 -15
- package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
- package/plugins/steve/steve-class.js +43 -0
- package/plugins/steve/subscribe.js +11 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
- package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
- package/rancher-components/RcButton/index.ts +1 -1
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
- package/store/__tests__/auth.test.ts +18 -0
- package/store/__tests__/features.test.ts +131 -0
- package/store/__tests__/growl.test.ts +374 -0
- package/store/__tests__/modal.test.ts +131 -0
- package/store/__tests__/notifications.test.ts +466 -1
- package/store/__tests__/slideInPanel.test.ts +88 -0
- package/store/__tests__/type-map.utils.test.ts +433 -0
- package/store/__tests__/ui-context.test.ts +255 -0
- package/store/auth.js +11 -1
- package/store/features.js +5 -0
- package/store/plugins.js +6 -0
- package/types/rancher/steve.api.ts +2 -2
- package/types/shell/index.d.ts +120 -0
- package/types/store/dashboard-store.types.ts +2 -2
- package/types/store/type-map.ts +262 -1
- package/utils/__tests__/operation-cr.test.ts +34 -0
- package/utils/async.ts +2 -0
- package/utils/grafana.js +2 -17
- package/utils/monitoring.js +38 -1
- package/utils/operation-cr.js +19 -0
- package/utils/require-asset.ts +7 -0
- package/utils/validators/__tests__/private-registry.test.ts +27 -15
- package/utils/validators/private-registry.ts +15 -4
- package/core/__tests__/plugin-products.test.ts +0 -4694
|
@@ -66,4 +66,126 @@ describe('component: SelectOrCreateAuthSecret', () => {
|
|
|
66
66
|
|
|
67
67
|
expect(passwordLabeledInput!.props('labelKey')).toBe(expectedLabelKey);
|
|
68
68
|
});
|
|
69
|
+
|
|
70
|
+
describe('GitHub App auth', () => {
|
|
71
|
+
const githubAppSetup = () => mount(SelectOrCreateAuthSecret, {
|
|
72
|
+
...requiredSetup(),
|
|
73
|
+
props: {
|
|
74
|
+
mode: _EDIT,
|
|
75
|
+
namespace: 'default',
|
|
76
|
+
value: {},
|
|
77
|
+
allowGithubApp: true,
|
|
78
|
+
registerBeforeHook: () => {},
|
|
79
|
+
},
|
|
80
|
+
data() {
|
|
81
|
+
return { selected: AUTH_TYPE._GITHUB_APP } as any;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should render the three GitHub App fields when selected', () => {
|
|
86
|
+
const wrapper = githubAppSetup();
|
|
87
|
+
|
|
88
|
+
expect(wrapper.find('[data-testid="auth-secret-github-app-id"]').exists()).toBe(true);
|
|
89
|
+
expect(wrapper.find('[data-testid="auth-secret-github-app-installation-id"]').exists()).toBe(true);
|
|
90
|
+
expect(wrapper.find('[data-testid="auth-secret-github-app-private-key"]').exists()).toBe(true);
|
|
91
|
+
expect(wrapper.find('[data-testid="auth-secret-github-app-private-key-file"]').exists()).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should not narrow the select column for GitHub App (keeps span-6)', () => {
|
|
95
|
+
const wrapper = githubAppSetup();
|
|
96
|
+
|
|
97
|
+
expect((wrapper.vm as any).firstCol).toBe('col span-6');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should emit inputauthval with the GitHub App field values', async() => {
|
|
101
|
+
const wrapper = githubAppSetup();
|
|
102
|
+
|
|
103
|
+
await wrapper.setData({
|
|
104
|
+
githubAppId: 'app-id',
|
|
105
|
+
githubAppInstallationId: 'installation-id',
|
|
106
|
+
githubAppPrivateKey: 'private-key',
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const emitted = wrapper.emitted('inputauthval') as any[];
|
|
110
|
+
const last = emitted[emitted.length - 1][0];
|
|
111
|
+
|
|
112
|
+
expect(last).toStrictEqual({
|
|
113
|
+
selected: AUTH_TYPE._GITHUB_APP,
|
|
114
|
+
publicKey: '',
|
|
115
|
+
privateKey: '',
|
|
116
|
+
githubAppId: 'app-id',
|
|
117
|
+
githubAppInstallationId: 'installation-id',
|
|
118
|
+
githubAppPrivateKey: 'private-key',
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should populate the private key when a file is read', async() => {
|
|
123
|
+
const wrapper = githubAppSetup();
|
|
124
|
+
|
|
125
|
+
const fileSelector = wrapper.findComponent({ name: 'FileSelector' });
|
|
126
|
+
|
|
127
|
+
await fileSelector.vm.$emit('selected', 'key-from-file');
|
|
128
|
+
|
|
129
|
+
expect((wrapper.vm as any).githubAppPrivateKey).toBe('key-from-file');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it.each([
|
|
133
|
+
['offer', true],
|
|
134
|
+
['not offer', false],
|
|
135
|
+
])('should %s the GitHub App create option when allowGithubApp is %p', (_, allowGithubApp) => {
|
|
136
|
+
const wrapper = mount(SelectOrCreateAuthSecret, {
|
|
137
|
+
...requiredSetup(),
|
|
138
|
+
props: {
|
|
139
|
+
mode: _EDIT,
|
|
140
|
+
namespace: 'default',
|
|
141
|
+
value: {},
|
|
142
|
+
allowGithubApp,
|
|
143
|
+
registerBeforeHook: () => {},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const hasGithubAppOption = (wrapper.vm as any).options
|
|
148
|
+
.some((o: any) => o.value === AUTH_TYPE._GITHUB_APP);
|
|
149
|
+
|
|
150
|
+
expect(hasGithubAppOption).toBe(allowGithubApp);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should list existing GitHub App secrets but exclude plain Opaque secrets', () => {
|
|
154
|
+
const githubAppSecret = {
|
|
155
|
+
_type: 'Opaque',
|
|
156
|
+
isGithubApp: true,
|
|
157
|
+
id: 'default/gh-app',
|
|
158
|
+
dataPreview: '3 keys',
|
|
159
|
+
subTypeDisplay: 'Opaque',
|
|
160
|
+
metadata: { name: 'gh-app', namespace: 'default' },
|
|
161
|
+
};
|
|
162
|
+
const plainOpaqueSecret = {
|
|
163
|
+
_type: 'Opaque',
|
|
164
|
+
isGithubApp: false,
|
|
165
|
+
id: 'default/plain',
|
|
166
|
+
dataPreview: '1 key',
|
|
167
|
+
subTypeDisplay: 'Opaque',
|
|
168
|
+
metadata: { name: 'plain', namespace: 'default' },
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const wrapper = mount(SelectOrCreateAuthSecret, {
|
|
172
|
+
...requiredSetup(),
|
|
173
|
+
props: {
|
|
174
|
+
mode: _EDIT,
|
|
175
|
+
namespace: 'default',
|
|
176
|
+
value: {},
|
|
177
|
+
allowGithubApp: true,
|
|
178
|
+
registerBeforeHook: () => {},
|
|
179
|
+
},
|
|
180
|
+
data() {
|
|
181
|
+
return { allSecrets: [githubAppSecret, plainOpaqueSecret] } as any;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const optionValues = (wrapper.vm as any).options.map((o: any) => o.value);
|
|
186
|
+
|
|
187
|
+
expect(optionValues).toContain('default/gh-app');
|
|
188
|
+
expect(optionValues).not.toContain('default/plain');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
69
191
|
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { get } from '@shell/utils/object';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
props: {
|
|
6
|
+
value: {
|
|
7
|
+
type: String,
|
|
8
|
+
default: ''
|
|
9
|
+
},
|
|
10
|
+
row: {
|
|
11
|
+
type: Object,
|
|
12
|
+
required: true
|
|
13
|
+
},
|
|
14
|
+
col: {
|
|
15
|
+
type: Object,
|
|
16
|
+
default: () => ({})
|
|
17
|
+
},
|
|
18
|
+
reference: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: null,
|
|
21
|
+
},
|
|
22
|
+
getCustomDetailLink: {
|
|
23
|
+
type: Function,
|
|
24
|
+
default: null
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
computed: {
|
|
29
|
+
to() {
|
|
30
|
+
if (this.getCustomDetailLink) {
|
|
31
|
+
return this.getCustomDetailLink(this.row);
|
|
32
|
+
}
|
|
33
|
+
if ( this.row && this.reference ) {
|
|
34
|
+
return get(this.row, this.reference);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return this.row?.detailLocation;
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
showPredatesImportIcon() {
|
|
41
|
+
return !!this.row?.isSnapshotTooOld;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
predatesImportMessage() {
|
|
45
|
+
return this.t('cluster.snapshot.predatesImportTooltip');
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<span>
|
|
53
|
+
<router-link
|
|
54
|
+
v-if="to"
|
|
55
|
+
:to="to"
|
|
56
|
+
>
|
|
57
|
+
{{ value }}
|
|
58
|
+
</router-link>
|
|
59
|
+
<span v-else>
|
|
60
|
+
{{ value }}
|
|
61
|
+
<template v-if="!value && col.dashIfEmpty">
|
|
62
|
+
<span class="text-muted">—</span>
|
|
63
|
+
</template>
|
|
64
|
+
</span>
|
|
65
|
+
<i
|
|
66
|
+
v-if="showPredatesImportIcon"
|
|
67
|
+
v-clean-tooltip="{ content: predatesImportMessage, triggers: ['hover', 'touch', 'focus'] }"
|
|
68
|
+
v-stripped-aria-label="predatesImportMessage"
|
|
69
|
+
tabindex="0"
|
|
70
|
+
class="icon icon-error text-error ml-5"
|
|
71
|
+
/>
|
|
72
|
+
</span>
|
|
73
|
+
</template>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { get } from '@shell/utils/object';
|
|
3
|
+
|
|
2
4
|
export default {
|
|
3
5
|
props: {
|
|
4
6
|
row: {
|
|
@@ -6,6 +8,11 @@ export default {
|
|
|
6
8
|
required: true,
|
|
7
9
|
},
|
|
8
10
|
|
|
11
|
+
fallbackPath: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: '',
|
|
14
|
+
},
|
|
15
|
+
|
|
9
16
|
prefix: {
|
|
10
17
|
type: String,
|
|
11
18
|
default: '',
|
|
@@ -18,8 +25,22 @@ export default {
|
|
|
18
25
|
},
|
|
19
26
|
|
|
20
27
|
computed: {
|
|
28
|
+
l10nId() {
|
|
29
|
+
return `${ this.prefix ? `${ this.prefix }.` : '' }${ this.value || this.row.id }`;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
l10nFallback() {
|
|
33
|
+
const fallback = this.value;
|
|
34
|
+
|
|
35
|
+
if (this.fallbackPath) {
|
|
36
|
+
return get(this.row, this.fallbackPath) || fallback;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return fallback;
|
|
40
|
+
},
|
|
41
|
+
|
|
21
42
|
text() {
|
|
22
|
-
return this.$store.getters['i18n/withFallback'](
|
|
43
|
+
return this.$store.getters['i18n/withFallback'](this.l10nId, null, this.l10nFallback);
|
|
23
44
|
},
|
|
24
45
|
},
|
|
25
46
|
};
|
package/components/nav/Group.vue
CHANGED
|
@@ -384,14 +384,12 @@ export default {
|
|
|
384
384
|
user-select: none;
|
|
385
385
|
text-transform: none;
|
|
386
386
|
font-size: 14px;
|
|
387
|
+
height: 100%;
|
|
388
|
+
padding: 8px 0 8px 16px;
|
|
389
|
+
display: inline-flex;
|
|
390
|
+
align-items: center;
|
|
387
391
|
}
|
|
388
392
|
|
|
389
|
-
> H6 {
|
|
390
|
-
text-transform: none;
|
|
391
|
-
height: 100%;
|
|
392
|
-
padding: 8px 0 8px 16px;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
393
|
> A {
|
|
396
394
|
display: block;
|
|
397
395
|
box-sizing:border-box;
|
|
@@ -403,10 +401,6 @@ export default {
|
|
|
403
401
|
&:focus{
|
|
404
402
|
outline:none;
|
|
405
403
|
}
|
|
406
|
-
> H6 {
|
|
407
|
-
text-transform: none;
|
|
408
|
-
padding: 8px 0 8px 16px;
|
|
409
|
-
}
|
|
410
404
|
}
|
|
411
405
|
}
|
|
412
406
|
|
|
@@ -574,6 +568,7 @@ export default {
|
|
|
574
568
|
padding-left: 24px;
|
|
575
569
|
display: flex;
|
|
576
570
|
justify-content: space-between;
|
|
571
|
+
align-items: center;
|
|
577
572
|
}
|
|
578
573
|
|
|
579
574
|
A:focus {
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
RcDropdownTrigger
|
|
31
31
|
} from '@components/RcDropdown';
|
|
32
32
|
import { SLO_AUTH_PROVIDERS } from '@shell/store/auth';
|
|
33
|
+
import { CLUSTER_SHELL } from '@shell/store/features';
|
|
33
34
|
|
|
34
35
|
export default {
|
|
35
36
|
|
|
@@ -147,10 +148,16 @@ export default {
|
|
|
147
148
|
return true;
|
|
148
149
|
},
|
|
149
150
|
|
|
151
|
+
// Does the user have permissions to use the shell
|
|
150
152
|
shellEnabled() {
|
|
151
153
|
return !!this.currentCluster?.links?.shell;
|
|
152
154
|
},
|
|
153
155
|
|
|
156
|
+
// Is the feature flag enabled for cluster shell access?
|
|
157
|
+
shellFeatureEnabled() {
|
|
158
|
+
return !!this.$store.getters['features/get'](CLUSTER_SHELL);
|
|
159
|
+
},
|
|
160
|
+
|
|
154
161
|
showKubeShell() {
|
|
155
162
|
return !this.rootProduct?.hideKubeShell;
|
|
156
163
|
},
|
|
@@ -629,7 +636,7 @@ export default {
|
|
|
629
636
|
</button>
|
|
630
637
|
|
|
631
638
|
<button
|
|
632
|
-
v-if="showKubeShell"
|
|
639
|
+
v-if="showKubeShell && shellFeatureEnabled"
|
|
633
640
|
id="btn-kubectl"
|
|
634
641
|
v-clean-tooltip="t('nav.shellShortcut', {key: shellShortcut})"
|
|
635
642
|
v-shortkey="{windows: ['ctrl', '`'], mac: ['meta', '`']}"
|
|
@@ -955,19 +955,6 @@ export default {
|
|
|
955
955
|
<div
|
|
956
956
|
class="footer"
|
|
957
957
|
>
|
|
958
|
-
<div
|
|
959
|
-
v-if="canEditSettings"
|
|
960
|
-
class="support"
|
|
961
|
-
@click="hide()"
|
|
962
|
-
>
|
|
963
|
-
<router-link
|
|
964
|
-
:to="{name: 'support'}"
|
|
965
|
-
role="link"
|
|
966
|
-
:aria-label="t('nav.ariaLabel.support')"
|
|
967
|
-
>
|
|
968
|
-
{{ t('nav.support', {hasSupport}) }}
|
|
969
|
-
</router-link>
|
|
970
|
-
</div>
|
|
971
958
|
<div
|
|
972
959
|
class="version"
|
|
973
960
|
:class="{'version-small': largeAboutText}"
|
|
@@ -1574,10 +1561,6 @@ export default {
|
|
|
1574
1561
|
margin: 20px 10px;
|
|
1575
1562
|
width: 50px;
|
|
1576
1563
|
|
|
1577
|
-
.support {
|
|
1578
|
-
display: none;
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
1564
|
.version{
|
|
1582
1565
|
text-align: center;
|
|
1583
1566
|
|
|
@@ -1597,19 +1580,7 @@ export default {
|
|
|
1597
1580
|
> * {
|
|
1598
1581
|
flex: 1;
|
|
1599
1582
|
color: var(--link);
|
|
1600
|
-
|
|
1601
|
-
&:first-child {
|
|
1602
|
-
text-align: left;
|
|
1603
|
-
}
|
|
1604
|
-
&:last-child {
|
|
1605
|
-
text-align: right;
|
|
1606
|
-
}
|
|
1607
|
-
text-align: center;
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
.support a:focus-visible {
|
|
1611
|
-
@include focus-outline;
|
|
1612
|
-
outline-offset: 4px;
|
|
1583
|
+
text-align: left;
|
|
1613
1584
|
}
|
|
1614
1585
|
|
|
1615
1586
|
.version {
|
|
@@ -64,4 +64,65 @@ describe('component: Group', () => {
|
|
|
64
64
|
|
|
65
65
|
expect((wrapper.vm as any).hasActiveRoute()).toBe(true);
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
describe('group header label', () => {
|
|
69
|
+
const mountGroup = (group: any) => shallowMount(Group as any, {
|
|
70
|
+
props: {
|
|
71
|
+
group, canCollapse: true, idPrefix: ''
|
|
72
|
+
},
|
|
73
|
+
global: {
|
|
74
|
+
stubs: { 'router-link': { template: '<a><slot /></a>' } },
|
|
75
|
+
mocks: {
|
|
76
|
+
$route: {
|
|
77
|
+
params: {}, path: '/test/route', fullPath: '/test/route', matched: []
|
|
78
|
+
},
|
|
79
|
+
$router: {
|
|
80
|
+
resolve: jest.fn().mockReturnValue({ path: '/test/route', fullPath: '/test/route' }),
|
|
81
|
+
getRoutes: jest.fn().mockReturnValue([])
|
|
82
|
+
},
|
|
83
|
+
t: (key: string) => key
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('renders the label in an h6 for a non-linked group header', () => {
|
|
89
|
+
const group = {
|
|
90
|
+
name: 'test',
|
|
91
|
+
label: 'My Group',
|
|
92
|
+
children: [
|
|
93
|
+
{ route: { name: 'child-route', params: {} } }
|
|
94
|
+
]
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const wrapper = mountGroup(group);
|
|
98
|
+
const header = wrapper.find('.header');
|
|
99
|
+
const h6 = header.find('h6');
|
|
100
|
+
|
|
101
|
+
// Non-linked header: the label sits in a bare h6, not wrapped in a router-link
|
|
102
|
+
expect(header.find('a').exists()).toBe(false);
|
|
103
|
+
expect(h6.exists()).toBe(true);
|
|
104
|
+
expect(h6.find('span').html()).toContain('My Group');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders the label in an h6 wrapped in a router-link for a group header with overview', () => {
|
|
108
|
+
const group = {
|
|
109
|
+
name: 'test',
|
|
110
|
+
label: 'My Group',
|
|
111
|
+
children: [
|
|
112
|
+
{
|
|
113
|
+
route: { name: 'overview-route' }, overview: true, exact: true
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const wrapper = mountGroup(group);
|
|
119
|
+
const link = wrapper.find('.header a');
|
|
120
|
+
const h6 = link.find('h6');
|
|
121
|
+
|
|
122
|
+
// Linked header: the same h6 label markup is wrapped in a router-link
|
|
123
|
+
expect(link.exists()).toBe(true);
|
|
124
|
+
expect(h6.exists()).toBe(true);
|
|
125
|
+
expect(h6.find('span').html()).toContain('My Group');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
67
128
|
});
|
|
@@ -25,6 +25,7 @@ import { getClusterFromRoute, getProductFromRoute } from '@shell/utils/router';
|
|
|
25
25
|
import SideNav from '@shell/components/SideNav';
|
|
26
26
|
import { Layout } from '@shell/types/window-manager';
|
|
27
27
|
import { RcButton } from '@components/RcButton';
|
|
28
|
+
import { CLUSTER_SHELL } from '@shell/store/features';
|
|
28
29
|
|
|
29
30
|
const SET_LOGIN_ACTION = 'set-as-login';
|
|
30
31
|
|
|
@@ -140,6 +141,7 @@ export default {
|
|
|
140
141
|
debugger;
|
|
141
142
|
},
|
|
142
143
|
|
|
144
|
+
// Open the shell for the current cluster if the user has permissions and the feature is enabled (invoked via keyboard shortcut)
|
|
143
145
|
async toggleShell() {
|
|
144
146
|
const clusterId = this.$route.params.cluster;
|
|
145
147
|
|
|
@@ -147,6 +149,11 @@ export default {
|
|
|
147
149
|
return;
|
|
148
150
|
}
|
|
149
151
|
|
|
152
|
+
// Cluster shell is disabled via feature flag
|
|
153
|
+
if (!this.$store.getters['features/get'](CLUSTER_SHELL)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
150
157
|
const cluster = await this.$store.dispatch('management/find', {
|
|
151
158
|
type: MANAGEMENT.CLUSTER,
|
|
152
159
|
id: clusterId,
|
package/config/features.js
CHANGED
|
@@ -20,6 +20,7 @@ export const IMPORTED_CLUSTER_VERSION_MANAGEMENT = 'rancher.io/imported-cluster-
|
|
|
20
20
|
export const UI_PROJECT_SECRET = 'management.cattle.io/project-scoped-secret';
|
|
21
21
|
export const UI_PROJECT_SECRET_COPY = 'management.cattle.io/project-scoped-secret-copy';
|
|
22
22
|
export const SERVICE_LINKS = 'ui.rancher/service-links';
|
|
23
|
+
export const NODE_DRIVER_FIELD_HINTS = 'io.cattle.nodedriver/ui-field-hints';
|
|
23
24
|
|
|
24
25
|
export const KUBERNETES = {
|
|
25
26
|
SERVICE_ACCOUNT_UID: 'kubernetes.io/service-account.uid',
|
|
@@ -150,6 +151,8 @@ export const RKE = { EXTERNAL_IP: 'rke.cattle.io/external-ip' };
|
|
|
150
151
|
|
|
151
152
|
export const SNAPSHOT = { CLUSTER_NAME: 'rke.cattle.io/cluster-name' };
|
|
152
153
|
|
|
154
|
+
export const OPERATION_ANNOTATIONS = { ENABLED: 'operations.cattle.io/ops-enabled' };
|
|
155
|
+
|
|
153
156
|
export const ISTIO = { AUTO_INJECTION: 'istio-injection' };
|
|
154
157
|
|
|
155
158
|
const CATTLE_REGEX = /cattle\.io\//;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
HCI,
|
|
11
11
|
MANAGEMENT,
|
|
12
12
|
SNAPSHOT,
|
|
13
|
+
OPERATION,
|
|
13
14
|
VIRTUAL_TYPES,
|
|
14
15
|
HOSTED_PROVIDER,
|
|
15
16
|
SAVED_COUNTS
|
|
@@ -80,6 +81,11 @@ export function init(store) {
|
|
|
80
81
|
configureType(SNAPSHOT, { depaginate: true });
|
|
81
82
|
configureType(CATALOG.CLUSTER_REPO, { listCreateButtonLabelKey: 'catalog.repo.add' });
|
|
82
83
|
|
|
84
|
+
// Day 2 operation CRDs - read-only, not user-creatable or editable
|
|
85
|
+
configureType(OPERATION.ETCD_SNAPSHOT, { isCreatable: false, isEditable: false });
|
|
86
|
+
configureType(OPERATION.ETCD_SNAPSHOT_RESTORE, { isCreatable: false, isEditable: false });
|
|
87
|
+
configureType(OPERATION.ENCRYPTION_KEY_ROTATE, { isCreatable: false, isEditable: false });
|
|
88
|
+
|
|
83
89
|
configureType(CAPI.RANCHER_CLUSTER, {
|
|
84
90
|
showListMasthead: false, namespaced: false, alias: [HCI.CLUSTER]
|
|
85
91
|
});
|
package/config/secret.ts
CHANGED
|
@@ -13,3 +13,13 @@ export const SECRET_TYPES = {
|
|
|
13
13
|
RKE_AUTH_CONFIG: 'rke.cattle.io/auth-config',
|
|
14
14
|
FLEET_OCI_STORAGE: 'fleet.cattle.io/bundle-oci-storage/v1alpha1'
|
|
15
15
|
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Data keys for a Fleet GitHub App authentication secret (stored as an Opaque secret).
|
|
19
|
+
* Shared so the create flows stay in sync.
|
|
20
|
+
*/
|
|
21
|
+
export const GITHUB_APP_SECRET_KEYS = {
|
|
22
|
+
APP_ID: 'github_app_id',
|
|
23
|
+
INSTALLATION_ID: 'github_app_installation_id',
|
|
24
|
+
PRIVATE_KEY: 'github_app_private_key',
|
|
25
|
+
};
|
package/config/settings.ts
CHANGED
|
@@ -67,6 +67,7 @@ export const SETTING = {
|
|
|
67
67
|
UI_DASHBOARD_HARVESTER_LEGACY_PLUGIN: 'ui-dashboard-harvester-legacy-plugin',
|
|
68
68
|
UI_OFFLINE_PREFERRED: 'ui-offline-preferred',
|
|
69
69
|
SYSTEM_DEFAULT_REGISTRY: 'system-default-registry',
|
|
70
|
+
SYSTEM_DEFAULT_REGISTRY_PULL_SECRETS: 'system-default-registry-pull-secrets',
|
|
70
71
|
UI_ISSUES: 'ui-issues',
|
|
71
72
|
PL: 'ui-pl',
|
|
72
73
|
PL_RANCHER_VALUE: 'rancher',
|
|
@@ -124,6 +125,7 @@ export const SETTING = {
|
|
|
124
125
|
*/
|
|
125
126
|
DYNAMIC_CONTENT_ENABLED: 'ui-content-enabled',
|
|
126
127
|
DYNAMIC_CONTENT_ENDPOINT: 'ui-content-endpoint',
|
|
128
|
+
IMPORTED_CLUSTER_DAY2_OPS_DEFAULT: 'imported-cluster-day2-ops-enabled'
|
|
127
129
|
} as const;
|
|
128
130
|
|
|
129
131
|
// These are the settings that are allowed to be edited via the UI
|
|
@@ -163,6 +165,7 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
|
|
|
163
165
|
[SETTING.SERVER_URL]: { kind: 'url', canReset: true },
|
|
164
166
|
[SETTING.RKE_METADATA_CONFIG]: { kind: 'json' },
|
|
165
167
|
[SETTING.SYSTEM_DEFAULT_REGISTRY]: {},
|
|
168
|
+
[SETTING.SYSTEM_DEFAULT_REGISTRY_PULL_SECRETS]: {},
|
|
166
169
|
[SETTING.UI_DASHBOARD_INDEX]: {},
|
|
167
170
|
[SETTING.UI_OFFLINE_PREFERRED]: {
|
|
168
171
|
kind: 'enum',
|
|
@@ -184,12 +187,12 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
|
|
|
184
187
|
ruleSet: [{ name: 'minValue', factoryArg: 1 }]
|
|
185
188
|
},
|
|
186
189
|
[SETTING.IMPORTED_CLUSTER_VERSION_MANAGEMENT]: { kind: 'boolean' },
|
|
190
|
+
[SETTING.IMPORTED_CLUSTER_DAY2_OPS_DEFAULT]: { kind: 'boolean' },
|
|
187
191
|
// Configuration setup for agent configuration. Setting this up will activate the specific banner configuration.
|
|
188
192
|
[SETTING.CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS]: { kind: 'json', agent: AGENT_CONFIGURATION_TYPES.CLUSTER },
|
|
189
193
|
[SETTING.CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET]: { kind: 'json', agent: AGENT_CONFIGURATION_TYPES.CLUSTER },
|
|
190
194
|
[SETTING.FLEET_AGENT_DEFAULT_PRIORITY_CLASS]: { kind: 'json', agent: AGENT_CONFIGURATION_TYPES.FLEET },
|
|
191
|
-
[SETTING.FLEET_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET]: { kind: 'json', agent: AGENT_CONFIGURATION_TYPES.FLEET }
|
|
192
|
-
|
|
195
|
+
[SETTING.FLEET_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET]: { kind: 'json', agent: AGENT_CONFIGURATION_TYPES.FLEET },
|
|
193
196
|
};
|
|
194
197
|
|
|
195
198
|
/**
|
|
@@ -206,6 +209,7 @@ export const PROVISIONING_SETTINGS = [
|
|
|
206
209
|
SETTING.CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET,
|
|
207
210
|
SETTING.FLEET_AGENT_DEFAULT_PRIORITY_CLASS,
|
|
208
211
|
SETTING.FLEET_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET,
|
|
212
|
+
SETTING.IMPORTED_CLUSTER_DAY2_OPS_DEFAULT
|
|
209
213
|
];
|
|
210
214
|
|
|
211
215
|
/**
|
package/config/table-headers.js
CHANGED
|
@@ -1072,11 +1072,14 @@ export const ROLE = {
|
|
|
1072
1072
|
export const FEATURE_DESCRIPTION = {
|
|
1073
1073
|
name: 'description',
|
|
1074
1074
|
labelKey: 'tableHeaders.description',
|
|
1075
|
-
value: '
|
|
1075
|
+
value: 'id',
|
|
1076
1076
|
align: 'left',
|
|
1077
1077
|
sort: ['status.description'],
|
|
1078
1078
|
formatter: 'Translate',
|
|
1079
|
-
formatterOpts: {
|
|
1079
|
+
formatterOpts: {
|
|
1080
|
+
prefix: 'featureFlags.description',
|
|
1081
|
+
fallbackPath: 'status.description'
|
|
1082
|
+
},
|
|
1080
1083
|
};
|
|
1081
1084
|
|
|
1082
1085
|
export const STATE_NORMAN = {
|
package/config/types.js
CHANGED
|
@@ -219,6 +219,12 @@ export const LONGHORN_VERSION_V2 = 'LonghornV2';
|
|
|
219
219
|
|
|
220
220
|
export const SNAPSHOT = 'rke.cattle.io.etcdsnapshot';
|
|
221
221
|
|
|
222
|
+
export const OPERATION = {
|
|
223
|
+
ETCD_SNAPSHOT: 'operation.cattle.io.etcdsnapshotsave',
|
|
224
|
+
ETCD_SNAPSHOT_RESTORE: 'operation.cattle.io.etcdsnapshotrestore',
|
|
225
|
+
ENCRYPTION_KEY_ROTATE: 'operation.cattle.io.encryptionkeyrotation',
|
|
226
|
+
};
|
|
227
|
+
|
|
222
228
|
// --------------------------------------
|
|
223
229
|
// 2. Only if Rancher is installed
|
|
224
230
|
// --------------------------------------
|
|
@@ -400,6 +406,7 @@ export const AUTH_TYPE = {
|
|
|
400
406
|
_S3: '_S3',
|
|
401
407
|
_RKE: '_RKE',
|
|
402
408
|
_IMAGE_PULL_SECRET: '_IPS',
|
|
409
|
+
_GITHUB_APP: '_GITHUB_APP',
|
|
403
410
|
};
|
|
404
411
|
|
|
405
412
|
export const LOCAL_CLUSTER = 'local';
|