@rancher/shell 0.5.2 → 0.5.3
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/translations/en-us.yaml +8 -4
- package/components/ClusterIconMenu.vue +24 -9
- package/components/CodeMirror.vue +79 -18
- package/components/FixedBanner.vue +1 -0
- package/components/ResourceDetail/index.vue +1 -4
- package/components/ResourceYaml.vue +29 -5
- package/components/SideNav.vue +42 -64
- package/components/SortableTable/index.vue +1 -1
- package/components/YamlEditor.vue +1 -0
- package/components/__tests__/CodeMirror.spec.ts +99 -0
- package/components/form/BannerSettings.vue +3 -0
- package/components/form/FileSelector.vue +1 -0
- package/components/form/KeyValue.vue +1 -0
- package/components/formatter/WorkloadDetailEndpoints.vue +12 -22
- package/components/formatter/__tests__/WorkloadDetailEndpoints.test.ts +81 -0
- package/components/nav/Header.vue +1 -0
- package/components/nav/Jump.vue +19 -9
- package/components/nav/TopLevelMenu.vue +37 -15
- package/components/nav/Type.vue +15 -4
- package/components/nav/__tests__/TopLevelMenu.test.ts +1 -1
- package/components/nav/__tests__/Type.test.ts +30 -0
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +77 -0
- package/detail/fleet.cattle.io.bundle.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +19 -4
- package/edit/management.cattle.io.setting.vue +1 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/opsgenie.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +1 -2
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/slack.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +14 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +22 -50
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +9 -11
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +3 -1
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +3 -0
- package/edit/token.vue +1 -0
- package/list/catalog.cattle.io.app.vue +1 -0
- package/list/management.cattle.io.setting.vue +1 -0
- package/machine-config/amazonec2.vue +1 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +151 -0
- package/models/__tests__/secret.test.ts +37 -0
- package/models/__tests__/storage.k8s.io.storageclass.test.ts +22 -0
- package/models/provisioning.cattle.io.cluster.js +36 -1
- package/models/secret.js +9 -0
- package/models/storage.k8s.io.storageclass.js +1 -1
- package/package.json +1 -1
- package/pages/c/_cluster/settings/DefaultLinksEditor.vue +1 -0
- package/pages/c/_cluster/settings/brand.vue +3 -0
- package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +4 -4
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +5 -2
- package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +96 -0
- package/pages/c/_cluster/uiplugins/__tests__/SetupUIPlugins.test.ts +128 -0
- package/plugins/dashboard-store/__tests__/actions.test.ts +196 -111
- package/plugins/dashboard-store/actions.js +4 -6
- package/plugins/dashboard-store/getters.js +60 -2
- package/plugins/dashboard-store/resource-class.js +6 -2
- package/plugins/steve/__tests__/getters.spec.ts +10 -0
- package/plugins/steve/__tests__/resource-utils.test.ts +159 -0
- package/plugins/steve/actions.js +3 -37
- package/plugins/steve/getters.js +6 -0
- package/plugins/steve/resource-utils.ts +38 -0
- package/store/__tests__/type-map.test.ts +1122 -0
- package/store/index.js +3 -2
- package/store/type-map.js +145 -75
- package/types/shell/index.d.ts +2 -0
- package/utils/__tests__/create-yaml.test.ts +10 -0
- package/utils/create-yaml.js +5 -1
- package/utils/object.js +10 -0
|
@@ -143,6 +143,7 @@ export default ({
|
|
|
143
143
|
<RadioGroup
|
|
144
144
|
v-model="value[bannerType].textAlignment"
|
|
145
145
|
name="bannerAlignment"
|
|
146
|
+
:data-testid="`banner_alignment_radio_options${bannerType}`"
|
|
146
147
|
:label="t('banner.bannerAlignment.label')"
|
|
147
148
|
:options="radioOptions.options"
|
|
148
149
|
:labels="radioOptions.labels"
|
|
@@ -160,6 +161,7 @@ export default ({
|
|
|
160
161
|
<Checkbox
|
|
161
162
|
v-model="value[bannerType][o.style]"
|
|
162
163
|
name="bannerDecoration"
|
|
164
|
+
:data-testid="`banner_decoration_checkbox${bannerType}${o.label}`"
|
|
163
165
|
class="banner-decoration-checkbox"
|
|
164
166
|
:mode="mode"
|
|
165
167
|
:label="o.label"
|
|
@@ -169,6 +171,7 @@ export default ({
|
|
|
169
171
|
<div class="col span-2">
|
|
170
172
|
<LabeledSelect
|
|
171
173
|
v-model="value[bannerType].fontSize"
|
|
174
|
+
:data-testid="`banner_font_size_options${bannerType}`"
|
|
172
175
|
:disabled="isUiDisabled"
|
|
173
176
|
:label="t('banner.bannerFontSize.label')"
|
|
174
177
|
:options="uiBannerFontSizeOptions"
|
|
@@ -27,6 +27,7 @@ export default {
|
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
out = JSON.parse(this.value);
|
|
30
|
+
|
|
30
31
|
out.forEach((endpoint) => {
|
|
31
32
|
let protocol = 'http';
|
|
32
33
|
|
|
@@ -34,10 +35,19 @@ export default {
|
|
|
34
35
|
protocol = 'https';
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
const linkDefaultDisplay = endpoint.port ? `${ endpoint.port }/${ endpoint.protocol }` : endpoint.protocol;
|
|
39
|
+
|
|
40
|
+
// If there's an ingress and it has a hostname, we use the hostname address instead
|
|
41
|
+
// https://github.com/rancher/dashboard/issues/8087
|
|
42
|
+
if (endpoint.ingressName && endpoint.hostname) {
|
|
43
|
+
endpoint.link = `${ protocol }://${ endpoint.hostname }${ endpoint.path }`;
|
|
44
|
+
endpoint.linkDisplay = endpoint.link;
|
|
45
|
+
} else if (endpoint.addresses && endpoint.addresses.length) {
|
|
38
46
|
endpoint.link = `${ protocol }://${ endpoint.addresses[0] }:${ endpoint.port }`;
|
|
47
|
+
endpoint.linkDisplay = linkDefaultDisplay;
|
|
39
48
|
} else if (externalIp) {
|
|
40
49
|
endpoint.link = `${ protocol }://${ externalIp }:${ endpoint.port }`;
|
|
50
|
+
endpoint.linkDisplay = linkDefaultDisplay;
|
|
41
51
|
} else {
|
|
42
52
|
endpoint.display = `[${ this.t('servicesPage.anyNode') }]:${ endpoint.port }`;
|
|
43
53
|
}
|
|
@@ -49,26 +59,6 @@ export default {
|
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
return null;
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
protocol() {
|
|
56
|
-
const { parsed } = this;
|
|
57
|
-
|
|
58
|
-
if ( parsed) {
|
|
59
|
-
if (this.parsed[0].protocol) {
|
|
60
|
-
return this.parsed[0].protocol;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const match = parsed.match(/^([^:]+):\/\//);
|
|
64
|
-
|
|
65
|
-
if ( match ) {
|
|
66
|
-
return match[1];
|
|
67
|
-
} else {
|
|
68
|
-
return 'link';
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
62
|
return null;
|
|
73
63
|
}
|
|
74
64
|
},
|
|
@@ -90,7 +80,7 @@ export default {
|
|
|
90
80
|
:href="endpoint.link"
|
|
91
81
|
target="_blank"
|
|
92
82
|
rel="nofollow noopener noreferrer"
|
|
93
|
-
|
|
83
|
+
>{{ endpoint.linkDisplay }}</a>
|
|
94
84
|
</template>
|
|
95
85
|
</span>
|
|
96
86
|
</template>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import WorkloadDetailEndpoints from '@shell/components/formatter/WorkloadDetailEndpoints.vue';
|
|
3
|
+
import Tag from '@shell/components/Tag';
|
|
4
|
+
|
|
5
|
+
describe('component: WorkloadDetailEndpoints', () => {
|
|
6
|
+
const withIngressAndHostname = [{
|
|
7
|
+
addresses: [
|
|
8
|
+
'172.18.0.3'
|
|
9
|
+
],
|
|
10
|
+
port: 80,
|
|
11
|
+
protocol: 'HTTP',
|
|
12
|
+
serviceName: 'kube-public:tetris',
|
|
13
|
+
ingressName: 'kube-public:tetris',
|
|
14
|
+
hostname: 'tetris.kube-public.172.18.0.3.sslip.io',
|
|
15
|
+
path: '/',
|
|
16
|
+
allNodes: false,
|
|
17
|
+
}];
|
|
18
|
+
|
|
19
|
+
const withoutIngress = [
|
|
20
|
+
{
|
|
21
|
+
addresses: [
|
|
22
|
+
'172.18.0.3'
|
|
23
|
+
],
|
|
24
|
+
port: 80,
|
|
25
|
+
protocol: 'TCP',
|
|
26
|
+
serviceName: 'kube-system:traefik',
|
|
27
|
+
allNodes: false
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
addresses: [
|
|
31
|
+
'172.18.0.3'
|
|
32
|
+
],
|
|
33
|
+
port: 443,
|
|
34
|
+
protocol: 'TCP',
|
|
35
|
+
serviceName: 'kube-system:traefik',
|
|
36
|
+
allNodes: false
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const withoutAddresses = [
|
|
41
|
+
{
|
|
42
|
+
port: 443,
|
|
43
|
+
protocol: 'TCP',
|
|
44
|
+
serviceName: 'kube-system:traefik',
|
|
45
|
+
allNodes: false
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const basicNodesOutput = [
|
|
50
|
+
{ externalIp: 'some-external-ip' }
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
it.each([
|
|
54
|
+
[withIngressAndHostname, [], ['http://tetris.kube-public.172.18.0.3.sslip.io/']],
|
|
55
|
+
[withoutIngress, [], ['http://172.18.0.3:80', 'https://172.18.0.3:443']],
|
|
56
|
+
[withoutAddresses, basicNodesOutput, ['https://some-external-ip:443']],
|
|
57
|
+
])('should display a link given the appropriate conditions', (value:any[], nodesOutput:any[], expectationArr:any[]) => {
|
|
58
|
+
const wrapper = mount(WorkloadDetailEndpoints, {
|
|
59
|
+
propsData: { value: JSON.stringify(value) },
|
|
60
|
+
mocks: { $store: { getters: { 'cluster/all': () => nodesOutput } } }
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
wrapper.vm.parsed.forEach((endpoint:{[key: string]: string}, i:number) => {
|
|
64
|
+
expect(endpoint.link).toBe(expectationArr[i]);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it.each([
|
|
69
|
+
[withoutAddresses, [], ['[some-translation]:443']],
|
|
70
|
+
])('should render a Tag component with the appropriate content', (value:any[], nodesOutput:any[], expectationArr:any[]) => {
|
|
71
|
+
const wrapper = mount(WorkloadDetailEndpoints, {
|
|
72
|
+
propsData: { value: JSON.stringify(value) },
|
|
73
|
+
mocks: { $store: { getters: { 'cluster/all': () => nodesOutput, 'i18n/t': () => 'some-translation' } } }
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
wrapper.vm.parsed.forEach((endpoint:{[key: string]: string}, i:number) => {
|
|
77
|
+
expect(endpoint.display).toBe(expectationArr[i]);
|
|
78
|
+
expect(wrapper.findComponent(Tag).exists()).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
package/components/nav/Jump.vue
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import debounce from 'lodash/debounce';
|
|
3
3
|
import Group from '@shell/components/nav/Group';
|
|
4
4
|
import { isMac } from '@shell/utils/platform';
|
|
5
|
-
import { BOTH,
|
|
5
|
+
import { BOTH, TYPE_MODES } from '@shell/store/type-map';
|
|
6
|
+
import { COUNT } from '@shell/config/types';
|
|
6
7
|
|
|
7
8
|
export default {
|
|
8
9
|
components: { Group },
|
|
@@ -31,17 +32,26 @@ export default {
|
|
|
31
32
|
methods: {
|
|
32
33
|
updateMatches() {
|
|
33
34
|
const clusterId = this.$store.getters['clusterId'];
|
|
34
|
-
const
|
|
35
|
-
const product = this.$store.getters['
|
|
35
|
+
const productId = this.$store.getters['productId'];
|
|
36
|
+
const product = this.$store.getters['currentProduct'];
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
const allTypesByMode = this.$store.getters['type-map/allTypes'](productId, [TYPE_MODES.ALL]) || {};
|
|
39
|
+
const allTypes = allTypesByMode[TYPE_MODES.ALL];
|
|
40
|
+
const out = this.$store.getters['type-map/getTree'](productId, TYPE_MODES.ALL, allTypes, clusterId, BOTH, null, this.value);
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
+
// Suplement the output with count info. Usualy the `Type` component would handle this individualy... but scales real bad so give it
|
|
43
|
+
// some help
|
|
44
|
+
const counts = this.$store.getters[`${ product.inStore }/all`](COUNT)?.[0]?.counts || {};
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
out.forEach((o) => {
|
|
47
|
+
o.children?.forEach((t) => {
|
|
48
|
+
const count = counts[t.name];
|
|
49
|
+
|
|
50
|
+
t.count = count ? count.summary.count || 0 : null;
|
|
51
|
+
t.byNamespace = count ? count.namespaces : {};
|
|
52
|
+
t.revision = count ? count.revision : null;
|
|
53
|
+
});
|
|
54
|
+
});
|
|
45
55
|
|
|
46
56
|
this.groups = out;
|
|
47
57
|
|
|
@@ -52,13 +52,11 @@ export default {
|
|
|
52
52
|
...mapGetters(['clusterId']),
|
|
53
53
|
...mapGetters(['clusterReady', 'isRancher', 'currentCluster', 'currentProduct', 'isRancherInHarvester']),
|
|
54
54
|
...mapGetters({ features: 'features/get' }),
|
|
55
|
-
|
|
56
55
|
value: {
|
|
57
56
|
get() {
|
|
58
57
|
return this.$store.getters['productId'];
|
|
59
58
|
},
|
|
60
59
|
},
|
|
61
|
-
|
|
62
60
|
sideMenuStyle() {
|
|
63
61
|
return {
|
|
64
62
|
marginBottom: this.globalBannerSettings?.footerFont,
|
|
@@ -171,7 +169,6 @@ export default {
|
|
|
171
169
|
|
|
172
170
|
return `min-height: ${ height }px`;
|
|
173
171
|
},
|
|
174
|
-
|
|
175
172
|
clusterFilterCount() {
|
|
176
173
|
return this.clusterFilter ? this.clustersFiltered.length : this.clusters.length;
|
|
177
174
|
},
|
|
@@ -376,7 +373,10 @@ export default {
|
|
|
376
373
|
/><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" /></svg>
|
|
377
374
|
</div>
|
|
378
375
|
<div class="side-menu-logo">
|
|
379
|
-
<BrandImage
|
|
376
|
+
<BrandImage
|
|
377
|
+
data-testid="side-menu__brand-img"
|
|
378
|
+
file-name="rancher-logo.svg"
|
|
379
|
+
/>
|
|
380
380
|
</div>
|
|
381
381
|
</div>
|
|
382
382
|
|
|
@@ -425,6 +425,10 @@ export default {
|
|
|
425
425
|
v-model="clusterFilter"
|
|
426
426
|
:placeholder="t('nav.search.placeholder')"
|
|
427
427
|
>
|
|
428
|
+
<i
|
|
429
|
+
class="magnifier icon icon-search"
|
|
430
|
+
:class="{ active: clusterFilter }"
|
|
431
|
+
/>
|
|
428
432
|
<i
|
|
429
433
|
v-if="clusterFilter"
|
|
430
434
|
class="icon icon-close"
|
|
@@ -789,6 +793,7 @@ export default {
|
|
|
789
793
|
|
|
790
794
|
&.menu-open {
|
|
791
795
|
width: 300px;
|
|
796
|
+
box-shadow: 3px 1px 3px var(--shadow);
|
|
792
797
|
}
|
|
793
798
|
|
|
794
799
|
.title {
|
|
@@ -796,7 +801,6 @@ export default {
|
|
|
796
801
|
height: 55px;
|
|
797
802
|
flex: 0 0 55px;
|
|
798
803
|
width: 100%;
|
|
799
|
-
border-bottom: 1px solid var(--nav-border);
|
|
800
804
|
justify-content: flex-start;
|
|
801
805
|
align-items: center;
|
|
802
806
|
|
|
@@ -949,13 +953,31 @@ export default {
|
|
|
949
953
|
position: relative;
|
|
950
954
|
> input {
|
|
951
955
|
background-color: transparent;
|
|
952
|
-
margin-bottom: 8px;
|
|
953
956
|
padding-right: 35px;
|
|
957
|
+
padding-left: 25px;
|
|
958
|
+
height: 32px;
|
|
959
|
+
}
|
|
960
|
+
> .magnifier {
|
|
961
|
+
position: absolute;
|
|
962
|
+
top: 12px;
|
|
963
|
+
left: 8px;
|
|
964
|
+
width: 12px;
|
|
965
|
+
height: 12px;
|
|
966
|
+
font-size: 12px;
|
|
967
|
+
opacity: 0.4;
|
|
968
|
+
|
|
969
|
+
&.active {
|
|
970
|
+
opacity: 1;
|
|
971
|
+
|
|
972
|
+
&:hover {
|
|
973
|
+
color: var(--body-text);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
954
976
|
}
|
|
955
977
|
> i {
|
|
956
978
|
position: absolute;
|
|
957
|
-
font-size:
|
|
958
|
-
top:
|
|
979
|
+
font-size: 12px;
|
|
980
|
+
top: 12px;
|
|
959
981
|
right: 8px;
|
|
960
982
|
opacity: 0.7;
|
|
961
983
|
cursor: pointer;
|
|
@@ -992,10 +1014,10 @@ export default {
|
|
|
992
1014
|
height: 42px;
|
|
993
1015
|
|
|
994
1016
|
.search {
|
|
995
|
-
transition: all 0.
|
|
1017
|
+
transition: all 0.25s ease-in-out;
|
|
996
1018
|
transition-delay: 2s;
|
|
997
1019
|
width: 72%;
|
|
998
|
-
height:
|
|
1020
|
+
height: 36px;
|
|
999
1021
|
|
|
1000
1022
|
input {
|
|
1001
1023
|
height: 100%;
|
|
@@ -1051,7 +1073,7 @@ export default {
|
|
|
1051
1073
|
hr {
|
|
1052
1074
|
margin: 0;
|
|
1053
1075
|
width: 94%;
|
|
1054
|
-
transition: all 0.
|
|
1076
|
+
transition: all 0.25s ease-in-out;
|
|
1055
1077
|
max-width: 100%;
|
|
1056
1078
|
}
|
|
1057
1079
|
}
|
|
@@ -1078,7 +1100,7 @@ export default {
|
|
|
1078
1100
|
text-transform: uppercase;
|
|
1079
1101
|
|
|
1080
1102
|
span {
|
|
1081
|
-
transition: all 0.
|
|
1103
|
+
transition: all 0.25s ease-in-out;
|
|
1082
1104
|
display: flex;
|
|
1083
1105
|
max-height: 16px;
|
|
1084
1106
|
}
|
|
@@ -1087,7 +1109,7 @@ export default {
|
|
|
1087
1109
|
margin: 0;
|
|
1088
1110
|
max-width: 50px;
|
|
1089
1111
|
width: 0;
|
|
1090
|
-
transition: all 0.
|
|
1112
|
+
transition: all 0.25s ease-in-out;
|
|
1091
1113
|
}
|
|
1092
1114
|
}
|
|
1093
1115
|
|
|
@@ -1199,12 +1221,12 @@ export default {
|
|
|
1199
1221
|
}
|
|
1200
1222
|
|
|
1201
1223
|
.fade-enter-active, .fade-leave-active {
|
|
1202
|
-
transition: all 0.
|
|
1224
|
+
transition: all 0.25s;
|
|
1203
1225
|
transition-timing-function: ease;
|
|
1204
1226
|
}
|
|
1205
1227
|
|
|
1206
1228
|
.fade-leave-active {
|
|
1207
|
-
transition: all 0.
|
|
1229
|
+
transition: all 0.25s;
|
|
1208
1230
|
}
|
|
1209
1231
|
|
|
1210
1232
|
.fade-leave-to {
|
package/components/nav/Type.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Favorite from '@shell/components/nav/Favorite';
|
|
3
|
-
import {
|
|
3
|
+
import { TYPE_MODES } from '@shell/store/type-map';
|
|
4
4
|
|
|
5
|
-
const showFavoritesFor = [FAVORITE, USED];
|
|
5
|
+
const showFavoritesFor = [TYPE_MODES.FAVORITE, TYPE_MODES.USED];
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
8
|
|
|
@@ -90,12 +90,23 @@ export default {
|
|
|
90
90
|
},
|
|
91
91
|
|
|
92
92
|
showCount() {
|
|
93
|
-
return
|
|
93
|
+
return this.count !== undefined;
|
|
94
94
|
},
|
|
95
95
|
|
|
96
96
|
namespaceIcon() {
|
|
97
97
|
return this.type.namespaced;
|
|
98
98
|
},
|
|
99
|
+
|
|
100
|
+
count() {
|
|
101
|
+
if (this.type.count !== undefined) {
|
|
102
|
+
return this.type.count;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const inStore = this.$store.getters['currentStore'](this.type.name);
|
|
106
|
+
|
|
107
|
+
return this.$store.getters[`${ inStore }/count`]({ name: this.type.name });
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
},
|
|
100
111
|
|
|
101
112
|
methods: {
|
|
@@ -162,7 +173,7 @@ export default {
|
|
|
162
173
|
v-if="namespaceIcon"
|
|
163
174
|
class="icon icon-namespace namespaced"
|
|
164
175
|
/>
|
|
165
|
-
{{
|
|
176
|
+
{{ count }}
|
|
166
177
|
</span>
|
|
167
178
|
</a>
|
|
168
179
|
</n-link>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { mount, Wrapper } from '@vue/test-utils';
|
|
2
1
|
import TopLevelMenu from '@shell/components/nav/TopLevelMenu';
|
|
3
2
|
import { SETTING } from '@shell/config/settings';
|
|
3
|
+
import { mount, Wrapper } from '@vue/test-utils';
|
|
4
4
|
|
|
5
5
|
// DISCLAIMER: This should not be added here, although we have several store requests which are irrelevant
|
|
6
6
|
const defaultStore = {
|
|
@@ -16,6 +16,12 @@ describe('component: Type', () => {
|
|
|
16
16
|
mocks: {
|
|
17
17
|
$route: { path: 'whatever' },
|
|
18
18
|
$router: { resolve: () => ({ route: { path: 'whatever' } }) },
|
|
19
|
+
$store: {
|
|
20
|
+
getters: {
|
|
21
|
+
currentStore: () => 'cluster',
|
|
22
|
+
'cluster/count': () => 1,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
19
25
|
},
|
|
20
26
|
});
|
|
21
27
|
|
|
@@ -31,6 +37,12 @@ describe('component: Type', () => {
|
|
|
31
37
|
mocks: {
|
|
32
38
|
$route: { hash: 'whatever' },
|
|
33
39
|
$router: { resolve: () => ({ route: { path: 'whatever' } }) },
|
|
40
|
+
$store: {
|
|
41
|
+
getters: {
|
|
42
|
+
currentStore: () => 'cluster',
|
|
43
|
+
'cluster/count': () => 1,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
34
46
|
},
|
|
35
47
|
});
|
|
36
48
|
|
|
@@ -67,6 +79,12 @@ describe('component: Type', () => {
|
|
|
67
79
|
path: 'whatever',
|
|
68
80
|
},
|
|
69
81
|
$router: { resolve: () => ({ route: { path: 'many/parts' } }) },
|
|
82
|
+
$store: {
|
|
83
|
+
getters: {
|
|
84
|
+
currentStore: () => 'cluster',
|
|
85
|
+
'cluster/count': () => 1,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
70
88
|
},
|
|
71
89
|
});
|
|
72
90
|
|
|
@@ -101,6 +119,12 @@ describe('component: Type', () => {
|
|
|
101
119
|
path: currentPath,
|
|
102
120
|
},
|
|
103
121
|
$router: { resolve: () => ({ route: { path: menuPath } }) },
|
|
122
|
+
$store: {
|
|
123
|
+
getters: {
|
|
124
|
+
currentStore: () => 'cluster',
|
|
125
|
+
'cluster/count': () => 1,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
104
128
|
},
|
|
105
129
|
});
|
|
106
130
|
|
|
@@ -126,6 +150,12 @@ describe('component: Type', () => {
|
|
|
126
150
|
path: currentPath,
|
|
127
151
|
},
|
|
128
152
|
$router: { resolve: () => ({ route: { path: menuPath } }) },
|
|
153
|
+
$store: {
|
|
154
|
+
getters: {
|
|
155
|
+
currentStore: () => 'cluster',
|
|
156
|
+
'cluster/count': () => 1,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
129
159
|
},
|
|
130
160
|
});
|
|
131
161
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import ProvisioningCattleIoCluster from '@shell/detail/provisioning.cattle.io.cluster.vue';
|
|
3
|
+
|
|
4
|
+
jest.mock('@shell/utils/clipboard', () => {
|
|
5
|
+
return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
describe('view: provisioning.cattle.io.cluster', () => {
|
|
9
|
+
const mockStore = {
|
|
10
|
+
getters: {
|
|
11
|
+
'management/canList': () => true,
|
|
12
|
+
'management/schemaFor': jest.fn(),
|
|
13
|
+
'i18n/t': (text: string) => text,
|
|
14
|
+
t: (text: string) => text,
|
|
15
|
+
currentStore: () => 'current_store',
|
|
16
|
+
'current_store/schemaFor': jest.fn(),
|
|
17
|
+
'current_store/all': jest.fn(),
|
|
18
|
+
workspace: jest.fn(),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const mocks = {
|
|
23
|
+
$store: mockStore,
|
|
24
|
+
$fetchState: { pending: false },
|
|
25
|
+
$route: {
|
|
26
|
+
query: { AS: '' },
|
|
27
|
+
name: {
|
|
28
|
+
endsWith: () => {
|
|
29
|
+
return false;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('registration tab visibility', () => {
|
|
36
|
+
it('a hosted Kubernetes Provider with a private endpoint (network config) and cluster not ready should SHOW the registration tab', async() => {
|
|
37
|
+
const value = {
|
|
38
|
+
isHostedKubernetesProvider: true,
|
|
39
|
+
isPrivateHostedProvider: true,
|
|
40
|
+
mgmt: {
|
|
41
|
+
hasLink: () => jest.fn(),
|
|
42
|
+
linkFor: () => '',
|
|
43
|
+
isReady: false
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const wrapper = shallowMount(ProvisioningCattleIoCluster, {
|
|
48
|
+
mocks,
|
|
49
|
+
propsData: { value },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
await wrapper.setData({ clusterToken: {} });
|
|
53
|
+
|
|
54
|
+
expect(wrapper.vm.showRegistration).toStrictEqual(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('a hosted Kubernetes Provider WITHOUT a private endpoint (network config) and cluster not ready should NOT SHOW the registration tab', async() => {
|
|
58
|
+
const value = {
|
|
59
|
+
isHostedKubernetesProvider: true,
|
|
60
|
+
mgmt: {
|
|
61
|
+
hasLink: () => jest.fn(),
|
|
62
|
+
linkFor: () => '',
|
|
63
|
+
isReady: false
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const wrapper = shallowMount(ProvisioningCattleIoCluster, {
|
|
68
|
+
mocks,
|
|
69
|
+
propsData: { value },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await wrapper.setData({ clusterToken: {} });
|
|
73
|
+
|
|
74
|
+
expect(wrapper.vm.showRegistration).toStrictEqual(false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -38,7 +38,7 @@ export default {
|
|
|
38
38
|
if (this.hasRepoLabel) {
|
|
39
39
|
const bundleResourceIds = this.bundleResourceIds;
|
|
40
40
|
|
|
41
|
-
return this.repo?.status?.resources
|
|
41
|
+
return this.repo?.status?.resources?.filter((resource) => {
|
|
42
42
|
return bundleResourceIds.includes(resource.name);
|
|
43
43
|
});
|
|
44
44
|
} else if (this.value?.spec?.resources?.length) {
|
|
@@ -519,7 +519,10 @@ export default {
|
|
|
519
519
|
return this.extDetailTabs.registration;
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
-
|
|
522
|
+
// Hosted kubernetes providers with private endpoints need the registration tab
|
|
523
|
+
// https://github.com/rancher/dashboard/issues/6036
|
|
524
|
+
// https://github.com/rancher/dashboard/issues/4545
|
|
525
|
+
if ( this.value.isHostedKubernetesProvider && this.value.isPrivateHostedProvider && !this.isClusterReady ) {
|
|
523
526
|
return this.extDetailTabs.registration;
|
|
524
527
|
}
|
|
525
528
|
|
|
@@ -552,6 +555,18 @@ export default {
|
|
|
552
555
|
|
|
553
556
|
snapshotsGroupBy() {
|
|
554
557
|
return 'backupLocation';
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
extDetailTabsRelated() {
|
|
561
|
+
return this.extDetailTabs?.related;
|
|
562
|
+
},
|
|
563
|
+
|
|
564
|
+
extDetailTabsEvents() {
|
|
565
|
+
return this.extDetailTabs?.events;
|
|
566
|
+
},
|
|
567
|
+
|
|
568
|
+
extDetailTabsConditions() {
|
|
569
|
+
return this.extDetailTabs?.conditions;
|
|
555
570
|
}
|
|
556
571
|
},
|
|
557
572
|
|
|
@@ -725,9 +740,9 @@ export default {
|
|
|
725
740
|
:default-tab="defaultTab"
|
|
726
741
|
:need-related="hasLocalAccess"
|
|
727
742
|
:extension-params="extCustomParams"
|
|
728
|
-
:needRelated="
|
|
729
|
-
:needEvents="
|
|
730
|
-
:needConditions="
|
|
743
|
+
:needRelated="extDetailTabsRelated"
|
|
744
|
+
:needEvents="extDetailTabsEvents"
|
|
745
|
+
:needConditions="extDetailTabsConditions"
|
|
731
746
|
>
|
|
732
747
|
<Tab
|
|
733
748
|
v-if="showMachines"
|
|
@@ -123,7 +123,6 @@ export default {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
|
|
127
126
|
};
|
|
128
127
|
</script>
|
|
129
128
|
|
|
@@ -185,7 +184,7 @@ export default {
|
|
|
185
184
|
<div class="row mb-20">
|
|
186
185
|
<div class="col span-12">
|
|
187
186
|
<LabeledInput
|
|
188
|
-
v-model="value.httpConfig.
|
|
187
|
+
v-model="value.httpConfig.proxyURL"
|
|
189
188
|
:mode="mode"
|
|
190
189
|
label="Proxy URL"
|
|
191
190
|
placeholder="e.g. http://my-proxy/"
|