@rancher/shell 3.0.2 → 3.0.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/styles/themes/_light.scss +1 -2
- package/assets/styles/themes/_suse.scss +1 -0
- package/assets/translations/en-us.yaml +18 -3
- package/chart/monitoring/prometheus/index.vue +13 -10
- package/components/ButtonGroup.vue +4 -0
- package/components/LocaleSelector.vue +2 -0
- package/components/SortableTable/THead.vue +2 -0
- package/components/SortableTable/index.vue +35 -5
- package/components/form/MatchExpressions.vue +4 -0
- package/components/form/Select.vue +11 -2
- package/components/nav/Favorite.vue +5 -1
- package/components/nav/Group.vue +4 -0
- package/components/nav/Jump.vue +7 -0
- package/components/nav/Pinned.vue +1 -1
- package/components/nav/Type.vue +1 -0
- package/config/router/routes.js +1 -0
- package/core/plugin-routes.ts +5 -115
- package/core/plugins.js +1 -1
- package/core/types.ts +18 -2
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +84 -23
- package/detail/autoscaling.horizontalpodautoscaler/index.vue +13 -3
- package/edit/auth/ldap/__tests__/config.test.ts +0 -14
- package/edit/auth/ldap/config.vue +0 -24
- package/edit/autoscaling.horizontalpodautoscaler/metric-identifier.vue +5 -2
- package/edit/fleet.cattle.io.clustergroup.vue +5 -3
- package/edit/fleet.cattle.io.gitrepo.vue +1 -0
- package/edit/logging-flow/Match.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +1 -1
- package/models/__tests__/logging.banzaicloud.io.flow.test.ts +88 -0
- package/models/logging.banzaicloud.io.flow.js +2 -1
- package/package.json +1 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +9 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -2
- package/scripts/test-plugins-build.sh +4 -6
- package/utils/color.js +9 -8
|
@@ -26,7 +26,6 @@ $disabled : $medium;
|
|
|
26
26
|
$primary : #3D98D3;
|
|
27
27
|
$secondary : $darker;
|
|
28
28
|
$link : #3D98D3;
|
|
29
|
-
$keyboard-focus : #{darken($primary, 10%)};
|
|
30
29
|
|
|
31
30
|
// Status colors
|
|
32
31
|
$success : #5D995D;
|
|
@@ -54,7 +53,7 @@ BODY, .theme-light {
|
|
|
54
53
|
--primary-border : #{$primary};
|
|
55
54
|
--primary-banner-bg : #{rgba($primary, 0.15)};
|
|
56
55
|
--primary-light-bg : #{rgba($primary, 0.05)};
|
|
57
|
-
--primary-keyboard-focus :
|
|
56
|
+
--primary-keyboard-focus : hsl(from var(--primary) h s calc(l - 10));
|
|
58
57
|
|
|
59
58
|
|
|
60
59
|
.text-primary {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
--primary-border : #{$primary};
|
|
13
13
|
--primary-banner-bg : #{rgba($primary, 0.15)};
|
|
14
14
|
--primary-light-bg : #{rgba($primary, 0.05)};
|
|
15
|
+
--primary-keyboard-focus : hsl(from var(--primary) h s calc(l - 10));
|
|
15
16
|
|
|
16
17
|
--info : #{$info};
|
|
17
18
|
--info-text : #{contrast-color($info)};
|
|
@@ -239,6 +239,7 @@ nav:
|
|
|
239
239
|
label: Resource Search
|
|
240
240
|
toolTip: Resource Search {key}
|
|
241
241
|
placeholder: Type to search for a resource...
|
|
242
|
+
filteringDescription: Using this input will immediately filter the results in the list below
|
|
242
243
|
header:
|
|
243
244
|
setLoginPage: Set as login page
|
|
244
245
|
restoreCards: Restore hidden cards
|
|
@@ -545,9 +546,6 @@ authConfig:
|
|
|
545
546
|
starttls:
|
|
546
547
|
label: Start TLS
|
|
547
548
|
tip: Upgrades non-encrypted connections by wrapping with TLS during the connection process. Can not be used in conjunction with TLS.
|
|
548
|
-
searchUsingServiceAccount:
|
|
549
|
-
label: Enable Service Account Search
|
|
550
|
-
tip: When enabled, Rancher will use the service account instead of the user account to search for users and groups.
|
|
551
549
|
tls: TLS
|
|
552
550
|
userEnabledAttribute: User Enabled Attribute
|
|
553
551
|
userMemberAttribute: User Member Attribute
|
|
@@ -2779,6 +2777,7 @@ hpa:
|
|
|
2779
2777
|
label: Metric Name
|
|
2780
2778
|
placeholder: e.g. packets-per-second
|
|
2781
2779
|
selector:
|
|
2780
|
+
header: Metric Selector
|
|
2782
2781
|
label: Add Selector
|
|
2783
2782
|
metricTarget:
|
|
2784
2783
|
averageVal:
|
|
@@ -5138,6 +5137,9 @@ resourceDetail:
|
|
|
5138
5137
|
stage: "Stage from {subtype} {name}"
|
|
5139
5138
|
view: "{subtype} {name}"
|
|
5140
5139
|
masthead:
|
|
5140
|
+
ariaLabel:
|
|
5141
|
+
favoriteAction: Add {resource} to your starred/favorites list
|
|
5142
|
+
unfavoriteAction: Remove {resource} from your starred/favorites list
|
|
5141
5143
|
age: Age
|
|
5142
5144
|
createdBy: Created by
|
|
5143
5145
|
restartCount: Pod Restarts
|
|
@@ -5490,6 +5492,18 @@ setup:
|
|
|
5490
5492
|
welcome: Welcome to {vendor}!
|
|
5491
5493
|
|
|
5492
5494
|
sortableTable:
|
|
5495
|
+
ariaLabel:
|
|
5496
|
+
firstPageBtn: First page of results
|
|
5497
|
+
prevPageBtn: Previous page of results
|
|
5498
|
+
nextPageBtn: Next page of results
|
|
5499
|
+
lastPageBtn: Last page of results
|
|
5500
|
+
alt:
|
|
5501
|
+
firstPageBtn: First page of results icon
|
|
5502
|
+
prevPageBtn: Previous page of results icon
|
|
5503
|
+
nextPageBtn: Next page of results icon
|
|
5504
|
+
lastPageBtn: Last page of results icon
|
|
5505
|
+
sortingIconDesc: Table header descending sort icon
|
|
5506
|
+
sortingIconAsc: Table header ascending sort icon
|
|
5493
5507
|
genericGroupCheckbox: Table group selection checkbox
|
|
5494
5508
|
genericRowCheckbox: Table row selection checkbox for item {item}
|
|
5495
5509
|
tableActionsLabel: More actions - { resource }
|
|
@@ -5518,6 +5532,7 @@ sortableTable:
|
|
|
5518
5532
|
searchLabel: Filter table results
|
|
5519
5533
|
in: in
|
|
5520
5534
|
addFilter: Add Filter
|
|
5535
|
+
filteringDescription: Using this input will immediately filter the results in the table below
|
|
5521
5536
|
filterFor: Filter for...
|
|
5522
5537
|
selectCol: Select a column
|
|
5523
5538
|
resetFilters: Reset
|
|
@@ -341,22 +341,25 @@ export default {
|
|
|
341
341
|
</div>
|
|
342
342
|
<div class="row">
|
|
343
343
|
<div class="col span-12">
|
|
344
|
-
<div class="mb-5 mt-5">
|
|
345
|
-
<h4 class=" mb-10">
|
|
346
|
-
{{ t('monitoring.prometheus.storage.selector') }}
|
|
347
|
-
</h4>
|
|
348
|
-
</div>
|
|
349
|
-
<Banner
|
|
350
|
-
color="warning"
|
|
351
|
-
:label="t('monitoring.prometheus.storage.selectorWarning', {}, true)"
|
|
352
|
-
/>
|
|
353
344
|
<MatchExpressions
|
|
354
345
|
:initial-empty-row="false"
|
|
355
346
|
:mode="mode"
|
|
356
347
|
:value="matchExpressions"
|
|
357
348
|
:show-remove="false"
|
|
358
349
|
@update:value="matchChanged($event)"
|
|
359
|
-
|
|
350
|
+
>
|
|
351
|
+
<template #header>
|
|
352
|
+
<div class="mb-5 mt-5">
|
|
353
|
+
<h4 class=" mb-10">
|
|
354
|
+
{{ t('monitoring.prometheus.storage.selector') }}
|
|
355
|
+
</h4>
|
|
356
|
+
</div>
|
|
357
|
+
<Banner
|
|
358
|
+
color="warning"
|
|
359
|
+
:label="t('monitoring.prometheus.storage.selectorWarning', {}, true)"
|
|
360
|
+
/>
|
|
361
|
+
</template>
|
|
362
|
+
</MatchExpressions>
|
|
360
363
|
</div>
|
|
361
364
|
</div>
|
|
362
365
|
</template>
|
|
@@ -77,6 +77,9 @@ export default {
|
|
|
77
77
|
const tooltip = opt.tooltipKey ? this.t(opt.tooltipKey) : opt.tooltip;
|
|
78
78
|
|
|
79
79
|
return ariaLabel || tooltip || label || undefined;
|
|
80
|
+
},
|
|
81
|
+
isPressed(opt) {
|
|
82
|
+
return this.value === opt.value;
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
};
|
|
@@ -97,6 +100,7 @@ export default {
|
|
|
97
100
|
:disabled="disabled || opt.disabled"
|
|
98
101
|
role="button"
|
|
99
102
|
:aria-label="actionAriaLabel(opt)"
|
|
103
|
+
:aria-pressed="isPressed(opt)"
|
|
100
104
|
@click="change(opt.value)"
|
|
101
105
|
>
|
|
102
106
|
<slot
|
|
@@ -119,6 +119,7 @@ export default {
|
|
|
119
119
|
tabindex="0"
|
|
120
120
|
role="menuitem"
|
|
121
121
|
class="hand"
|
|
122
|
+
:lang="name"
|
|
122
123
|
@click.stop="switchLocale(name)"
|
|
123
124
|
@keyup.enter.stop="switchLocale(name)"
|
|
124
125
|
@keyup.space.stop="switchLocale(name)"
|
|
@@ -134,6 +135,7 @@ export default {
|
|
|
134
135
|
<Select
|
|
135
136
|
:value="selectedOption"
|
|
136
137
|
:options="localesOptions"
|
|
138
|
+
:is-lang-select="true"
|
|
137
139
|
@update:value="switchLocale($event)"
|
|
138
140
|
/>
|
|
139
141
|
</div>
|
|
@@ -282,10 +282,12 @@ export default {
|
|
|
282
282
|
<i
|
|
283
283
|
v-if="isCurrent(col) && !descending"
|
|
284
284
|
class="icon icon-sort-down icon-stack-1x"
|
|
285
|
+
:alt="t('sortableTable.alt.sortingIconDesc')"
|
|
285
286
|
/>
|
|
286
287
|
<i
|
|
287
288
|
v-if="isCurrent(col) && descending"
|
|
288
289
|
class="icon icon-sort-up icon-stack-1x"
|
|
290
|
+
:alt="t('sortableTable.alt.sortingIconAsc')"
|
|
289
291
|
/>
|
|
290
292
|
</span>
|
|
291
293
|
</div>
|
|
@@ -1081,6 +1081,8 @@ export default {
|
|
|
1081
1081
|
:class="{[bulkActionClass]:true}"
|
|
1082
1082
|
:disabled="!act.enabled"
|
|
1083
1083
|
:data-testid="componentTestid + '-' + act.action"
|
|
1084
|
+
role="button"
|
|
1085
|
+
:aria-label="act.label"
|
|
1084
1086
|
@click="applyTableAction(act, null, $event)"
|
|
1085
1087
|
@keydown.enter.stop
|
|
1086
1088
|
@mouseover="setBulkActionOfInterest(act)"
|
|
@@ -1234,13 +1236,21 @@ export default {
|
|
|
1234
1236
|
</div>
|
|
1235
1237
|
</div>
|
|
1236
1238
|
</div>
|
|
1237
|
-
<
|
|
1239
|
+
<p
|
|
1238
1240
|
v-else-if="search"
|
|
1241
|
+
id="describe-filter-sortable-table"
|
|
1242
|
+
hidden
|
|
1243
|
+
>
|
|
1244
|
+
{{ t('sortableTable.filteringDescription') }}
|
|
1245
|
+
</p>
|
|
1246
|
+
<input
|
|
1247
|
+
v-if="search"
|
|
1239
1248
|
ref="searchQuery"
|
|
1240
1249
|
v-model="eventualSearchQuery"
|
|
1241
1250
|
type="search"
|
|
1242
1251
|
class="input-sm search-box"
|
|
1243
1252
|
:aria-label="t('sortableTable.searchLabel')"
|
|
1253
|
+
aria-describedby="describe-filter-sortable-table"
|
|
1244
1254
|
:placeholder="t('sortableTable.search')"
|
|
1245
1255
|
>
|
|
1246
1256
|
<slot name="header-button" />
|
|
@@ -1532,18 +1542,28 @@ export default {
|
|
|
1532
1542
|
class="btn btn-sm role-multi-action"
|
|
1533
1543
|
data-testid="pagination-first"
|
|
1534
1544
|
:disabled="page == 1 || loading"
|
|
1545
|
+
role="button"
|
|
1546
|
+
:aria-label="t('sortableTable.ariaLabel.firstPageBtn')"
|
|
1535
1547
|
@click="goToPage('first')"
|
|
1536
1548
|
>
|
|
1537
|
-
<i
|
|
1549
|
+
<i
|
|
1550
|
+
class="icon icon-chevron-beginning"
|
|
1551
|
+
:alt="t('sortableTable.alt.firstPageBtn')"
|
|
1552
|
+
/>
|
|
1538
1553
|
</button>
|
|
1539
1554
|
<button
|
|
1540
1555
|
type="button"
|
|
1541
1556
|
class="btn btn-sm role-multi-action"
|
|
1542
1557
|
data-testid="pagination-prev"
|
|
1543
1558
|
:disabled="page == 1 || loading"
|
|
1559
|
+
role="button"
|
|
1560
|
+
:aria-label="t('sortableTable.ariaLabel.prevPageBtn')"
|
|
1544
1561
|
@click="goToPage('prev')"
|
|
1545
1562
|
>
|
|
1546
|
-
<i
|
|
1563
|
+
<i
|
|
1564
|
+
class="icon icon-chevron-left"
|
|
1565
|
+
:alt="t('sortableTable.alt.prevPageBtn')"
|
|
1566
|
+
/>
|
|
1547
1567
|
</button>
|
|
1548
1568
|
<span>
|
|
1549
1569
|
{{ pagingDisplay }}
|
|
@@ -1553,18 +1573,28 @@ export default {
|
|
|
1553
1573
|
class="btn btn-sm role-multi-action"
|
|
1554
1574
|
data-testid="pagination-next"
|
|
1555
1575
|
:disabled="page == totalPages || loading"
|
|
1576
|
+
role="button"
|
|
1577
|
+
:aria-label="t('sortableTable.ariaLabel.nextPageBtn')"
|
|
1556
1578
|
@click="goToPage('next')"
|
|
1557
1579
|
>
|
|
1558
|
-
<i
|
|
1580
|
+
<i
|
|
1581
|
+
class="icon icon-chevron-right"
|
|
1582
|
+
:alt="t('sortableTable.alt.nextPageBtn')"
|
|
1583
|
+
/>
|
|
1559
1584
|
</button>
|
|
1560
1585
|
<button
|
|
1561
1586
|
type="button"
|
|
1562
1587
|
class="btn btn-sm role-multi-action"
|
|
1563
1588
|
data-testid="pagination-last"
|
|
1564
1589
|
:disabled="page == totalPages || loading"
|
|
1590
|
+
role="button"
|
|
1591
|
+
:aria-label="t('sortableTable.ariaLabel.lastPageBtn')"
|
|
1565
1592
|
@click="goToPage('last')"
|
|
1566
1593
|
>
|
|
1567
|
-
<i
|
|
1594
|
+
<i
|
|
1595
|
+
class="icon icon-chevron-end"
|
|
1596
|
+
:alt="t('sortableTable.alt.lastPageBtn')"
|
|
1597
|
+
/>
|
|
1568
1598
|
</button>
|
|
1569
1599
|
</div>
|
|
1570
1600
|
<button
|
|
@@ -87,6 +87,10 @@ export default {
|
|
|
87
87
|
type: Boolean,
|
|
88
88
|
default: null
|
|
89
89
|
},
|
|
90
|
+
isLangSelect: {
|
|
91
|
+
type: Boolean,
|
|
92
|
+
default: false
|
|
93
|
+
},
|
|
90
94
|
},
|
|
91
95
|
|
|
92
96
|
methods: {
|
|
@@ -289,8 +293,13 @@ export default {
|
|
|
289
293
|
@open="resizeHandler"
|
|
290
294
|
@option:created="(e) => $emit('createdListItem', e)"
|
|
291
295
|
>
|
|
292
|
-
<template
|
|
293
|
-
|
|
296
|
+
<template
|
|
297
|
+
#option="option"
|
|
298
|
+
>
|
|
299
|
+
<div
|
|
300
|
+
:lang="isLangSelect ? option.value : undefined"
|
|
301
|
+
@mousedown="(e) => onClickOption(option, e)"
|
|
302
|
+
>
|
|
294
303
|
{{ getOptionLabel(option.label) }}
|
|
295
304
|
</div>
|
|
296
305
|
</template>
|
|
@@ -10,6 +10,9 @@ export default {
|
|
|
10
10
|
computed: {
|
|
11
11
|
isFavorite() {
|
|
12
12
|
return this.$store.getters['type-map/isFavorite'](this.resource);
|
|
13
|
+
},
|
|
14
|
+
ariaLabel() {
|
|
15
|
+
return this.t(`resourceDetail.masthead.ariaLabel.${ this.isFavorite ? 'unfavoriteAction' : 'favoriteAction' }`, { resource: this.resource });
|
|
13
16
|
}
|
|
14
17
|
},
|
|
15
18
|
|
|
@@ -28,10 +31,11 @@ export default {
|
|
|
28
31
|
<template>
|
|
29
32
|
<i
|
|
30
33
|
:tabindex="0"
|
|
31
|
-
:aria-
|
|
34
|
+
:aria-pressed="!!isFavorite"
|
|
32
35
|
class="favorite icon"
|
|
33
36
|
:class="{'icon-star-open': !isFavorite, 'icon-star': isFavorite}"
|
|
34
37
|
aria-role="button"
|
|
38
|
+
:aria-label="ariaLabel"
|
|
35
39
|
@click.stop.prevent="toggle"
|
|
36
40
|
@keydown.enter.prevent="toggle"
|
|
37
41
|
@keydown.space.prevent="toggle"
|
package/components/nav/Group.vue
CHANGED
package/components/nav/Jump.vue
CHANGED
|
@@ -71,6 +71,12 @@ export default {
|
|
|
71
71
|
|
|
72
72
|
<template>
|
|
73
73
|
<div>
|
|
74
|
+
<p
|
|
75
|
+
id="describe-filter-resource-search"
|
|
76
|
+
hidden
|
|
77
|
+
>
|
|
78
|
+
{{ t('nav.resourceSearch.filteringDescription') }}
|
|
79
|
+
</p>
|
|
74
80
|
<input
|
|
75
81
|
ref="input"
|
|
76
82
|
v-model="value"
|
|
@@ -78,6 +84,7 @@ export default {
|
|
|
78
84
|
class="search"
|
|
79
85
|
role="textbox"
|
|
80
86
|
:aria-label="t('nav.resourceSearch.label')"
|
|
87
|
+
aria-describedby="describe-filter-resource-search"
|
|
81
88
|
@keyup.esc="$emit('closeSearch')"
|
|
82
89
|
>
|
|
83
90
|
<div class="results">
|
|
@@ -36,7 +36,7 @@ export default {
|
|
|
36
36
|
:aria-checked="!!pinned"
|
|
37
37
|
class="pin icon"
|
|
38
38
|
:class="{'icon-pin-outlined': !pinned, 'icon-pin': pinned}"
|
|
39
|
-
|
|
39
|
+
role="button"
|
|
40
40
|
:aria-label="t('nav.ariaLabel.pinCluster', { cluster: cluster.label })"
|
|
41
41
|
@click.stop.prevent="toggle"
|
|
42
42
|
@keydown.enter.prevent="toggle"
|
package/components/nav/Type.vue
CHANGED
|
@@ -122,6 +122,7 @@ export default {
|
|
|
122
122
|
:aria-label="type.labelKey ? t(type.labelKey) : (type.labelDisplay || type.label)"
|
|
123
123
|
:href="href"
|
|
124
124
|
class="type-link"
|
|
125
|
+
:aria-current="isActive ? 'page' : undefined"
|
|
125
126
|
@click="selectType(); navigate($event);"
|
|
126
127
|
@mouseenter="setNear(true)"
|
|
127
128
|
@mouseleave="setNear(false)"
|
package/config/router/routes.js
CHANGED
package/core/plugin-routes.ts
CHANGED
|
@@ -22,123 +22,13 @@ export class PluginRoutes {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
public addRoutes(
|
|
26
|
-
|
|
27
|
-
...(this.router.options.routes || [])
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
// Need to take into account if routes are being replaced
|
|
31
|
-
// Despite what the docs say, routes are not replaced, so we use a workaround
|
|
32
|
-
// Remove all routes that are being replaced
|
|
33
|
-
const newRoutes = newRouteInfos.map((ri) => ri.route);
|
|
34
|
-
|
|
35
|
-
this.forEachNestedRoutes(newRoutes, (route: RouteRecordRaw) => {
|
|
36
|
-
// Patch colliding legacy routes that start /:product
|
|
37
|
-
if (route.path?.startsWith('/:product')) {
|
|
38
|
-
// Legacy pattern used by extensions - routes may collide, so modify them not to
|
|
39
|
-
let productName;
|
|
40
|
-
|
|
41
|
-
// If the route has a name (which is always the case for the extensions we have written), use it to get the product name
|
|
42
|
-
if (route.name && typeof route.name === 'string') {
|
|
43
|
-
const nameParts = route.name.split('-');
|
|
44
|
-
|
|
45
|
-
// First part of the route name is the product name
|
|
46
|
-
productName = nameParts[0];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Use the plugin name as the product, if the route does not have a name
|
|
50
|
-
productName = productName || plugin.name;
|
|
51
|
-
|
|
52
|
-
// Replace the path - removing :product and using the actual product name instead - this avoids route collisions
|
|
53
|
-
route.path = `/${ productName }${ route.path.substr(9) }`;
|
|
54
|
-
route.meta = route.meta || {};
|
|
55
|
-
|
|
56
|
-
route.meta.product = route.meta.product || productName;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
this.updateMatcher(newRouteInfos, allRoutes);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private updateMatcher(newRoutes: RouteInfo[], allRoutes: RouteRecordRaw[]) {
|
|
64
|
-
// Note - Always use a new router and replace the existing router's matching
|
|
65
|
-
// Using the existing router and adding routes to it will force nuxt middleware to
|
|
66
|
-
// execute many times (nuxt middleware boils down to route.beforeEach). This issue was seen refreshing in a harvester cluster with a
|
|
67
|
-
// dynamically loaded cluster
|
|
68
|
-
|
|
69
|
-
if (newRoutes.length === 0) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const orderedPluginRoutes: RouteRecordRaw[] = [];
|
|
74
|
-
|
|
75
|
-
// separate plugin routes that have parent and not, you want to push the new routes in REVERSE order to the front of the existing list so that the order of routes specified by the extension is preserved
|
|
76
|
-
newRoutes.reverse().forEach((routeInfo: RouteInfo) => {
|
|
77
|
-
let foundParentRoute;
|
|
78
|
-
|
|
25
|
+
public addRoutes(newRouteInfos: RouteInfo[]) {
|
|
26
|
+
newRouteInfos.forEach((routeInfo) => {
|
|
79
27
|
if (routeInfo.parent) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
foundParentRoute.children = foundParentRoute?.children || [];
|
|
84
|
-
foundParentRoute.children.unshift(routeInfo.route);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (!foundParentRoute) {
|
|
89
|
-
orderedPluginRoutes.unshift(routeInfo.route);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
this.router.clearRoutes();
|
|
94
|
-
|
|
95
|
-
const allRoutesToAdd = [...orderedPluginRoutes, ...allRoutes];
|
|
96
|
-
|
|
97
|
-
allRoutesToAdd.forEach((route) => this.router.addRoute(route));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Traverse the entire tree of nested routes
|
|
102
|
-
*
|
|
103
|
-
* @param routes The routes we wish to traverse through
|
|
104
|
-
* @param fn -> Return true if you'd like to break the loop early (small)
|
|
105
|
-
* @returns {@boolean} -> Returns true if breaking early
|
|
106
|
-
*/
|
|
107
|
-
private forEachNestedRoutes(
|
|
108
|
-
routes: RouteRecordRaw[] = [],
|
|
109
|
-
fn: (route: RouteRecordRaw) => boolean | undefined | void
|
|
110
|
-
) {
|
|
111
|
-
for (let i = 0; i < routes.length; ++i) {
|
|
112
|
-
const route = routes[i];
|
|
113
|
-
const result = fn(route);
|
|
114
|
-
|
|
115
|
-
if (result || this.forEachNestedRoutes(route.children, fn)) {
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Find a route that matches the criteria defined by fn.
|
|
123
|
-
*
|
|
124
|
-
* @param routes The routes we wish to search through
|
|
125
|
-
* @param fn -> Returns true if the passed in route matches the expected criteria
|
|
126
|
-
* @returns The found route or undefined
|
|
127
|
-
*/
|
|
128
|
-
private findInNestedRoutes(
|
|
129
|
-
routes: RouteRecordRaw[] = [],
|
|
130
|
-
fn: (route: RouteRecordRaw) => boolean
|
|
131
|
-
): RouteRecordRaw | undefined {
|
|
132
|
-
let found: RouteRecordRaw | undefined;
|
|
133
|
-
|
|
134
|
-
this.forEachNestedRoutes(routes, (route) => {
|
|
135
|
-
if (fn(route)) {
|
|
136
|
-
found = route;
|
|
137
|
-
|
|
138
|
-
return true;
|
|
28
|
+
this.router.addRoute(routeInfo.parent, routeInfo.route);
|
|
29
|
+
} else {
|
|
30
|
+
this.router.addRoute(routeInfo.route);
|
|
139
31
|
}
|
|
140
32
|
});
|
|
141
|
-
|
|
142
|
-
return found;
|
|
143
33
|
}
|
|
144
34
|
}
|
package/core/plugins.js
CHANGED
package/core/types.ts
CHANGED
|
@@ -471,6 +471,24 @@ export interface DSLReturnType {
|
|
|
471
471
|
*/
|
|
472
472
|
virtualType: (options: ConfigureVirtualTypeOptions) => void;
|
|
473
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Side menu ordering for grouping of pages
|
|
476
|
+
* @param input Name of the group
|
|
477
|
+
* @param weight Ordering to be applied for the specified group
|
|
478
|
+
* @param forBasic Apply to basic type instead of regular type tree
|
|
479
|
+
* @returns {@link void}
|
|
480
|
+
*/
|
|
481
|
+
weightGroup: (input: string, weight: number, forBasic: boolean) => void;
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Side menu ordering for simple pages
|
|
485
|
+
* @param input Name of the page/resource
|
|
486
|
+
* @param weight Ordering to be applied for the specified page/resource
|
|
487
|
+
* @param forBasic Apply to basic type instead of regular type tree
|
|
488
|
+
* @returns {@link void}
|
|
489
|
+
*/
|
|
490
|
+
weightType: (input: string, weight: number, forBasic: boolean) => void;
|
|
491
|
+
|
|
474
492
|
/**
|
|
475
493
|
* Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
|
|
476
494
|
*/
|
|
@@ -484,8 +502,6 @@ export interface DSLReturnType {
|
|
|
484
502
|
// moveType: (match, group)
|
|
485
503
|
// setGroupDefaultType: (input, defaultType)
|
|
486
504
|
// spoofedType: (obj)
|
|
487
|
-
// weightGroup: (input, weight, forBasic)
|
|
488
|
-
// weightType: (input, weight, forBasic)
|
|
489
505
|
}
|
|
490
506
|
|
|
491
507
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
|
+
import camelCase from 'lodash/camelCase';
|
|
2
3
|
import HorizontalPodAutoScaler from '@shell/detail/autoscaling.horizontalpodautoscaler/index.vue';
|
|
3
4
|
|
|
4
5
|
describe('view: autoscaling.horizontalpodautoscaler', () => {
|
|
@@ -43,7 +44,7 @@ describe('view: autoscaling.horizontalpodautoscaler', () => {
|
|
|
43
44
|
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
const
|
|
47
|
+
const valueWithResourceMetrics = {
|
|
47
48
|
status: {
|
|
48
49
|
currentMetrics: [
|
|
49
50
|
{
|
|
@@ -100,36 +101,96 @@ describe('view: autoscaling.horizontalpodautoscaler', () => {
|
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
};
|
|
104
|
+
const valueWithOtherMetrics = {
|
|
105
|
+
status: {
|
|
106
|
+
currentMetrics: [
|
|
103
107
|
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
{
|
|
109
|
+
external: {
|
|
110
|
+
metric: { name: 's1-prometheus' },
|
|
111
|
+
current: { averageValue: 50 }
|
|
112
|
+
},
|
|
113
|
+
type: 'External'
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
spec: {
|
|
119
|
+
metrics: [
|
|
120
|
+
{
|
|
121
|
+
external: {
|
|
122
|
+
metric: { name: 's1-prometheus' },
|
|
123
|
+
target: { averageValue: 50, type: 'AverageValue' }
|
|
124
|
+
},
|
|
125
|
+
type: 'External'
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
;
|
|
111
131
|
|
|
112
|
-
describe
|
|
113
|
-
const
|
|
132
|
+
describe('with resource metrics:', () => {
|
|
133
|
+
const metricsValue = Object.values(valueWithResourceMetrics.spec.metrics);
|
|
134
|
+
const currentMetrics = Object.values(valueWithResourceMetrics.status.currentMetrics);
|
|
114
135
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const metricValue = metricsValue.find((f) => f.resource.name === name)?.resource;
|
|
136
|
+
const wrapper = mount(HorizontalPodAutoScaler, {
|
|
137
|
+
props: { value: valueWithResourceMetrics },
|
|
138
|
+
global: { mocks, stubs },
|
|
139
|
+
});
|
|
120
140
|
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
const averageValue = wrapper.get(`[data-testid="current-metrics-Average Value-${ name }"]`);
|
|
124
|
-
const currentResource = currentMetrics.find((f) => f.resource.name === name)?.resource.current;
|
|
141
|
+
describe.each(valueWithResourceMetrics.spec.metrics)('should display metrics for each resource:', (metric) => {
|
|
142
|
+
const name = metric.resource.name;
|
|
125
143
|
|
|
144
|
+
it(`${ name }:`, () => {
|
|
126
145
|
// Resource metrics
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
const resourceValue = wrapper.get(`[data-testid="resource-metrics-value-${ name }"]`);
|
|
147
|
+
const resourceName = wrapper.get(`[data-testid="resource-metrics-name-${ name }"]`);
|
|
148
|
+
const metricValue = metricsValue.find((f) => f.resource.name === name)?.resource;
|
|
149
|
+
|
|
150
|
+
// Current Metrics
|
|
151
|
+
const averageUtilization = wrapper.get(`[data-testid="current-metrics-Average Utilization-${ name }"]`);
|
|
152
|
+
const averageValue = wrapper.get(`[data-testid="current-metrics-Average Value-${ name }"]`);
|
|
153
|
+
const currentResource = currentMetrics.find((f) => f.resource.name === name)?.resource.current;
|
|
154
|
+
|
|
155
|
+
// Resource metrics
|
|
156
|
+
expect(resourceValue.element.textContent).toBe(`${ metricValue?.target?.averageUtilization }`);
|
|
157
|
+
expect(resourceName.element.textContent).toBe(`${ metricValue?.name }`);
|
|
158
|
+
|
|
159
|
+
// Current Metrics
|
|
160
|
+
expect(averageUtilization.element.textContent).toBe(`${ currentResource?.averageUtilization }`);
|
|
161
|
+
expect(averageValue.element.textContent).toBe(`${ currentResource?.averageValue }`);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('with other metrics:', () => {
|
|
167
|
+
const currentMetrics = Object.values(valueWithOtherMetrics.status.currentMetrics);
|
|
168
|
+
|
|
169
|
+
const wrapper = mount(HorizontalPodAutoScaler, {
|
|
170
|
+
props: { value: valueWithOtherMetrics },
|
|
171
|
+
global: { mocks, stubs },
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe.each(valueWithOtherMetrics.spec.metrics)('should display metrics for each resource:', (metric) => {
|
|
175
|
+
const metricType = camelCase(metric.type);
|
|
176
|
+
const name = metric[metricType as keyof typeof metric].metric.name;
|
|
177
|
+
|
|
178
|
+
it(`${ name }:`, () => {
|
|
179
|
+
// Resource metrics
|
|
180
|
+
const resourceValue = wrapper.get(`[data-testid="resource-metrics-value-${ name }"]`);
|
|
181
|
+
const metricValue = metric[metricType as keyof typeof metric];
|
|
182
|
+
|
|
183
|
+
// Current Metrics
|
|
184
|
+
const averageValue = wrapper.get(`[data-testid="current-metrics-Average Value-${ name }"]`);
|
|
185
|
+
const currentMatch = currentMetrics.find((f) => f[metricType as keyof typeof metric]?.metric.name === name);
|
|
186
|
+
const currentValue = currentMatch[metricType as keyof typeof metric]?.current;
|
|
187
|
+
|
|
188
|
+
// Resource metrics
|
|
189
|
+
expect(resourceValue.element.textContent).toBe(`${ metricValue?.target?.averageValue }`);
|
|
129
190
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
191
|
+
// Current Metrics
|
|
192
|
+
expect(averageValue.element.textContent).toBe(`${ currentValue?.averageValue }`);
|
|
193
|
+
});
|
|
133
194
|
});
|
|
134
195
|
});
|
|
135
196
|
});
|
|
@@ -43,11 +43,21 @@ export default {
|
|
|
43
43
|
} = this.value;
|
|
44
44
|
|
|
45
45
|
return metrics.map((metric) => {
|
|
46
|
+
const currentMetricsKVs = [];
|
|
47
|
+
let currentMatch;
|
|
48
|
+
const metricType = camelCase(metric.type);
|
|
46
49
|
const metricValue = get(metric, camelCase(metric.type));
|
|
47
50
|
const targetType = metricValue?.target?.type;
|
|
48
|
-
|
|
51
|
+
|
|
52
|
+
// The format is different between 'Resource' metrics and others.
|
|
53
|
+
// See for examples: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#appendix-horizontal-pod-autoscaler-status-conditions
|
|
54
|
+
if (metricType !== 'resource') {
|
|
55
|
+
currentMatch = findBy(currentMetrics, `${ metricType }.metric.name`, metricValue.metric.name);
|
|
56
|
+
} else {
|
|
57
|
+
currentMatch = findBy(currentMetrics, 'resource.name', metric.resource.name);
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
const current = currentMatch ? get(currentMatch, `${ camelCase(metric.type) }.current`) : null;
|
|
50
|
-
const currentMetricsKVs = [];
|
|
51
61
|
|
|
52
62
|
if (current) {
|
|
53
63
|
keys(current).forEach((k) => {
|
|
@@ -67,7 +77,7 @@ export default {
|
|
|
67
77
|
objectApiVersion: metricValue?.describedObject?.apiVersion ?? null,
|
|
68
78
|
objectKind: metricValue?.describedObject?.kind ?? null,
|
|
69
79
|
objectName: metricValue?.describedObject?.name ?? null,
|
|
70
|
-
resourceName: metricValue?.name
|
|
80
|
+
resourceName: metricValue?.name || metricValue?.metric?.name || null,
|
|
71
81
|
currentMetrics: currentMetricsKVs,
|
|
72
82
|
},
|
|
73
83
|
};
|
|
@@ -2,20 +2,6 @@ import { mount } from '@vue/test-utils';
|
|
|
2
2
|
import LDAPConfig from '@shell/edit/auth/ldap/config.vue';
|
|
3
3
|
|
|
4
4
|
describe('lDAP config', () => {
|
|
5
|
-
it.each([
|
|
6
|
-
'openldap', 'freeipa'
|
|
7
|
-
])('should display searchUsingServiceAccount checkbox if type %p', (type) => {
|
|
8
|
-
const wrapper = mount(LDAPConfig, {
|
|
9
|
-
propsData: {
|
|
10
|
-
value: {},
|
|
11
|
-
type,
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
const checkbox = wrapper.find('[data-testid="searchUsingServiceAccount"]');
|
|
15
|
-
|
|
16
|
-
expect(checkbox).toBeDefined();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
5
|
it('updates user login filter when value is entered', async() => {
|
|
20
6
|
const wrapper = mount(
|
|
21
7
|
LDAPConfig,
|
|
@@ -11,8 +11,6 @@ const DEFAULT_TLS_PORT = 636;
|
|
|
11
11
|
|
|
12
12
|
export const SHIBBOLETH = 'shibboleth';
|
|
13
13
|
export const OKTA = 'okta';
|
|
14
|
-
export const OPEN_LDAP = 'openldap';
|
|
15
|
-
export const FREE_IPA = 'freeipa';
|
|
16
14
|
|
|
17
15
|
export default {
|
|
18
16
|
emits: ['update:value'],
|
|
@@ -66,11 +64,6 @@ export default {
|
|
|
66
64
|
// Does the auth provider support LDAP for search in addition to SAML?
|
|
67
65
|
isSamlProvider() {
|
|
68
66
|
return this.type === SHIBBOLETH || this.type === OKTA;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
// Allow to enable user search just for these providers
|
|
72
|
-
isSearchAllowed() {
|
|
73
|
-
return this.type === OPEN_LDAP || this.type === FREE_IPA;
|
|
74
67
|
}
|
|
75
68
|
},
|
|
76
69
|
|
|
@@ -233,23 +226,6 @@ export default {
|
|
|
233
226
|
/>
|
|
234
227
|
</div>
|
|
235
228
|
</div>
|
|
236
|
-
|
|
237
|
-
<div
|
|
238
|
-
v-if="isSearchAllowed"
|
|
239
|
-
class="row mb-20"
|
|
240
|
-
>
|
|
241
|
-
<div class="col">
|
|
242
|
-
<Checkbox
|
|
243
|
-
v-model:value="model.searchUsingServiceAccount"
|
|
244
|
-
:mode="mode"
|
|
245
|
-
data-testid="searchUsingServiceAccount"
|
|
246
|
-
class="full-height"
|
|
247
|
-
:label="t('authConfig.ldap.searchUsingServiceAccount.label')"
|
|
248
|
-
:tooltip="t('authConfig.ldap.searchUsingServiceAccount.tip')"
|
|
249
|
-
/>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
|
|
253
229
|
<div class="row mb-20">
|
|
254
230
|
<div class="col span-6">
|
|
255
231
|
<LabeledInput
|
|
@@ -65,14 +65,17 @@ export default {
|
|
|
65
65
|
</div>
|
|
66
66
|
<div class="row">
|
|
67
67
|
<div class="col span-12">
|
|
68
|
-
<h3>Metric Selector</h3>
|
|
69
68
|
<MatchExpressions
|
|
70
69
|
:mode="mode"
|
|
71
70
|
:value="matchExpressions"
|
|
72
71
|
:label="t('hpa.metricIdentifier.selector.label')"
|
|
73
72
|
:show-remove="false"
|
|
74
73
|
@input="matchChanged($event)"
|
|
75
|
-
|
|
74
|
+
>
|
|
75
|
+
<template #header>
|
|
76
|
+
<h3>{{ t('hpa.metricIdentifier.selector.header') }}</h3>
|
|
77
|
+
</template>
|
|
78
|
+
</MatchExpressions>
|
|
76
79
|
</div>
|
|
77
80
|
</div>
|
|
78
81
|
</div>
|
|
@@ -149,14 +149,16 @@ export default {
|
|
|
149
149
|
:namespace-type="FLEET_WORKSPACE"
|
|
150
150
|
@update:value="$emit('input', $event)"
|
|
151
151
|
/>
|
|
152
|
-
|
|
153
|
-
<h2 v-t="'fleet.clusterGroup.selector.label'" />
|
|
154
152
|
<MatchExpressions
|
|
155
153
|
:mode="mode"
|
|
156
154
|
:value="expressions"
|
|
157
155
|
:show-remove="false"
|
|
158
156
|
@update:value="matchChanged($event)"
|
|
159
|
-
|
|
157
|
+
>
|
|
158
|
+
<template #header>
|
|
159
|
+
<h2 v-t="'fleet.clusterGroup.selector.label'" />
|
|
160
|
+
</template>
|
|
161
|
+
</MatchExpressions>
|
|
160
162
|
<Banner
|
|
161
163
|
v-if="matchingClusters"
|
|
162
164
|
:color="(matchingClusters.isNone || matchingClusters.isAll ? 'warning' : 'success')"
|
|
@@ -103,6 +103,7 @@ export default {
|
|
|
103
103
|
if (!pollingInterval) {
|
|
104
104
|
if (this.realMode === _CREATE) {
|
|
105
105
|
pollingInterval = DEFAULT_POLLING_INTERVAL;
|
|
106
|
+
this.value.spec.pollingInterval = this.durationSeconds(pollingInterval);
|
|
106
107
|
} else if (this.realMode === _EDIT || this.realMode === _VIEW) {
|
|
107
108
|
pollingInterval = MINIMUM_POLLING_INTERVAL;
|
|
108
109
|
}
|
|
@@ -124,7 +124,7 @@ export default {
|
|
|
124
124
|
<div class="row">
|
|
125
125
|
<div class="col span-12">
|
|
126
126
|
<Select
|
|
127
|
-
v-model="value.namespaces"
|
|
127
|
+
v-model:value="value.namespaces"
|
|
128
128
|
class="lg"
|
|
129
129
|
:options="namespaces"
|
|
130
130
|
:placeholder="t('logging.flow.matches.namespaces.placeholder')"
|
|
@@ -119,14 +119,17 @@ export default {
|
|
|
119
119
|
>
|
|
120
120
|
<template #default="{row, i}">
|
|
121
121
|
<template v-if="row.value.machineLabelSelector">
|
|
122
|
-
<h3>{{ t('cluster.advanced.argInfo.machineSelector.title') }}</h3>
|
|
123
122
|
<MatchExpressions
|
|
124
123
|
v-model:value="row.value.machineLabelSelector"
|
|
125
124
|
class="mb-20"
|
|
126
125
|
:mode="mode"
|
|
127
126
|
:show-remove="false"
|
|
128
127
|
:initial-empty-row="true"
|
|
129
|
-
|
|
128
|
+
>
|
|
129
|
+
<template #header>
|
|
130
|
+
<h3>{{ t('cluster.advanced.argInfo.machineSelector.title') }}</h3>
|
|
131
|
+
</template>
|
|
132
|
+
</MatchExpressions>
|
|
130
133
|
<h3>{{ t('cluster.advanced.argInfo.machineSelector.subTitle') }}</h3>
|
|
131
134
|
</template>
|
|
132
135
|
<h3 v-else>
|
|
@@ -48,7 +48,7 @@ export default {
|
|
|
48
48
|
return this.value.spec.rkeConfig.etcd;
|
|
49
49
|
},
|
|
50
50
|
argsEtcdExposeMetrics() {
|
|
51
|
-
return !!this.selectedVersion?.serverArgs['etcd-expose-metrics'];
|
|
51
|
+
return !!this.selectedVersion?.serverArgs?.['etcd-expose-metrics'];
|
|
52
52
|
},
|
|
53
53
|
configEtcdExposeMetrics() {
|
|
54
54
|
return !!this.value.spec.rkeConfig.machineGlobalConfig['etcd-expose-metrics'];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import LogFlow from '@shell/models/logging.banzaicloud.io.flow';
|
|
2
|
+
|
|
3
|
+
describe('class LogFlow', () => {
|
|
4
|
+
it('prop "outputs" should take namespace in consideration when filtering logging v2 "outputs"', () => {
|
|
5
|
+
const logOutputs = [
|
|
6
|
+
{
|
|
7
|
+
apiVersion: 'logging.banzaicloud.io/v1beta1',
|
|
8
|
+
kind: 'Output',
|
|
9
|
+
metadata: {
|
|
10
|
+
creationTimestamp: '2025-03-17T10:51:55Z',
|
|
11
|
+
namespace: 'default',
|
|
12
|
+
name: 'output1',
|
|
13
|
+
uid: '927b4a2e-6be0-476f-9bdd-cf30c4a27d8b'
|
|
14
|
+
},
|
|
15
|
+
name: 'output1',
|
|
16
|
+
spec: { awsElasticsearch: { endpoint: {} } },
|
|
17
|
+
status: { active: false }
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
apiVersion: 'logging.banzaicloud.io/v1beta1',
|
|
21
|
+
kind: 'Output',
|
|
22
|
+
metadata: {
|
|
23
|
+
creationTimestamp: '2025-03-17T10:51:55Z',
|
|
24
|
+
namespace: 'cattle-fleet-system',
|
|
25
|
+
name: 'output2',
|
|
26
|
+
uid: '927b4a2e-6be0-476f-9bdd-cf30c4a27d8c'
|
|
27
|
+
},
|
|
28
|
+
name: 'output2',
|
|
29
|
+
spec: { awsElasticsearch: { endpoint: {} } },
|
|
30
|
+
status: { active: false }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
apiVersion: 'logging.banzaicloud.io/v1beta1',
|
|
34
|
+
kind: 'Output',
|
|
35
|
+
metadata: {
|
|
36
|
+
creationTimestamp: '2025-03-17T10:51:55Z',
|
|
37
|
+
namespace: 'cattle-fleet-system',
|
|
38
|
+
name: 'output3',
|
|
39
|
+
uid: '927b4a2e-6be0-476f-9bdd-cf30c4a27d8d'
|
|
40
|
+
},
|
|
41
|
+
name: 'output3',
|
|
42
|
+
spec: { awsElasticsearch: { endpoint: {} } },
|
|
43
|
+
status: { active: false }
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
apiVersion: 'logging.banzaicloud.io/v1beta1',
|
|
47
|
+
kind: 'Output',
|
|
48
|
+
metadata: {
|
|
49
|
+
creationTimestamp: '2025-03-17T10:51:55Z',
|
|
50
|
+
namespace: 'kube-system',
|
|
51
|
+
name: 'output4',
|
|
52
|
+
uid: '927b4a2e-6be0-476f-9bdd-cf30c4a27d8e'
|
|
53
|
+
},
|
|
54
|
+
name: 'output4',
|
|
55
|
+
spec: { awsElasticsearch: { endpoint: {} } },
|
|
56
|
+
status: { active: false }
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const logFlowData = {
|
|
61
|
+
apiVersion: 'logging.banzaicloud.io/v1beta1',
|
|
62
|
+
kind: 'Flow',
|
|
63
|
+
metadata: {
|
|
64
|
+
name: 'flow2',
|
|
65
|
+
creationTimestamp: '2025-03-17T10:53:02Z',
|
|
66
|
+
generation: 1,
|
|
67
|
+
namespace: 'cattle-fleet-system',
|
|
68
|
+
resourceVersion: '4070',
|
|
69
|
+
uid: 'fdf7d553-d101-4c37-91b0-784f95dc950a',
|
|
70
|
+
fields: [
|
|
71
|
+
'flow2', true, null
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
spec: {
|
|
75
|
+
localOutputRefs: [
|
|
76
|
+
'output2',
|
|
77
|
+
'output3'
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const logFlow = new LogFlow(logFlowData);
|
|
83
|
+
|
|
84
|
+
jest.spyOn(logFlow, 'allOutputs', 'get').mockReturnValue(logOutputs);
|
|
85
|
+
|
|
86
|
+
expect(logFlow.outputs).toStrictEqual([logOutputs[1], logOutputs[2]]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -64,7 +64,8 @@ export default class LogFlow extends SteveModel {
|
|
|
64
64
|
get outputs() {
|
|
65
65
|
const localOutputRefs = this.spec?.localOutputRefs || [];
|
|
66
66
|
|
|
67
|
-
return this.allOutputs.filter((output) => localOutputRefs.includes(output.name)
|
|
67
|
+
return this.allOutputs.filter((output) => localOutputRefs.includes(output.name) &&
|
|
68
|
+
output.metadata?.namespace === this.metadata?.namespace);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
get outputsSortable() {
|
package/package.json
CHANGED
|
@@ -128,6 +128,10 @@ export default defineComponent({
|
|
|
128
128
|
|
|
129
129
|
emits: ['update:value'],
|
|
130
130
|
|
|
131
|
+
data() {
|
|
132
|
+
return { describedById: `described-by-${ generateRandomAlphaString(12) }` };
|
|
133
|
+
},
|
|
134
|
+
|
|
131
135
|
computed: {
|
|
132
136
|
/**
|
|
133
137
|
* Determines if the checkbox is disabled.
|
|
@@ -270,6 +274,7 @@ export default defineComponent({
|
|
|
270
274
|
:aria-label="replacementLabel"
|
|
271
275
|
:aria-checked="!!value"
|
|
272
276
|
:aria-labelledby="labelKey || label ? idForLabel : undefined"
|
|
277
|
+
:aria-describedby="descriptionKey || description ? describedById : undefined"
|
|
273
278
|
role="checkbox"
|
|
274
279
|
/>
|
|
275
280
|
<span
|
|
@@ -311,10 +316,13 @@ export default defineComponent({
|
|
|
311
316
|
>
|
|
312
317
|
<t
|
|
313
318
|
v-if="descriptionKey"
|
|
319
|
+
:id="describedById"
|
|
314
320
|
:k="descriptionKey"
|
|
315
321
|
/>
|
|
316
322
|
<template v-else-if="description">
|
|
317
|
-
|
|
323
|
+
<p :id="describedById">
|
|
324
|
+
{{ description }}
|
|
325
|
+
</p>
|
|
318
326
|
</template>
|
|
319
327
|
</div>
|
|
320
328
|
<div class="checkbox-outer-container-extra">
|
|
@@ -148,7 +148,8 @@ export default defineComponent({
|
|
|
148
148
|
return {
|
|
149
149
|
updated: false,
|
|
150
150
|
validationErrors: '',
|
|
151
|
-
inputId: `input-${ generateRandomAlphaString(12) }
|
|
151
|
+
inputId: `input-${ generateRandomAlphaString(12) }`,
|
|
152
|
+
describedById: `described-by-${ generateRandomAlphaString(12) }`
|
|
152
153
|
};
|
|
153
154
|
},
|
|
154
155
|
|
|
@@ -380,6 +381,7 @@ export default defineComponent({
|
|
|
380
381
|
:placeholder="_placeholder"
|
|
381
382
|
autocapitalize="off"
|
|
382
383
|
:class="{ conceal: type === 'multiline-password' }"
|
|
384
|
+
:aria-describedby="cronHint || subLabel ? describedById : undefined"
|
|
383
385
|
@update:value="onInput"
|
|
384
386
|
@focus="onFocus"
|
|
385
387
|
@blur="onBlur"
|
|
@@ -400,6 +402,7 @@ export default defineComponent({
|
|
|
400
402
|
autocomplete="off"
|
|
401
403
|
autocapitalize="off"
|
|
402
404
|
:data-lpignore="ignorePasswordManagers"
|
|
405
|
+
:aria-describedby="cronHint || subLabel ? describedById : undefined"
|
|
403
406
|
@input="onInput"
|
|
404
407
|
@focus="onFocus"
|
|
405
408
|
@blur="onBlur"
|
|
@@ -428,13 +431,15 @@ export default defineComponent({
|
|
|
428
431
|
>
|
|
429
432
|
<div
|
|
430
433
|
v-if="cronHint"
|
|
434
|
+
:id="describedById"
|
|
431
435
|
role="alert"
|
|
432
436
|
:aria-label="cronHint"
|
|
433
437
|
>
|
|
434
438
|
{{ cronHint }}
|
|
435
439
|
</div>
|
|
436
440
|
<div
|
|
437
|
-
v-if="subLabel"
|
|
441
|
+
v-else-if="subLabel"
|
|
442
|
+
:id="describedById"
|
|
438
443
|
v-clean-html="subLabel"
|
|
439
444
|
/>
|
|
440
445
|
</div>
|
|
@@ -238,11 +238,9 @@ function clone_repo_test_extension_build() {
|
|
|
238
238
|
# Don't forget to add the unit tests exception to clone_repo_test_extension_build function if a new extension has those
|
|
239
239
|
clone_repo_test_extension_build "rancher" "kubewarden-ui" "kubewarden"
|
|
240
240
|
clone_repo_test_extension_build "rancher" "elemental-ui" "elemental"
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
# clone_repo_test_extension_build "StackVista" "rancher-extension-stackstate" "observability"
|
|
246
|
-
# clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
|
|
241
|
+
clone_repo_test_extension_build "neuvector" "manager-ext" "neuvector-ui-ext"
|
|
242
|
+
clone_repo_test_extension_build "rancher" "capi-ui-extension" "capi"
|
|
243
|
+
clone_repo_test_extension_build "StackVista" "rancher-extension-stackstate" "observability"
|
|
244
|
+
clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
|
|
247
245
|
|
|
248
246
|
echo "All done"
|
package/utils/color.js
CHANGED
|
@@ -19,14 +19,15 @@ export function createCssVars(color, theme = 'light', name = 'primary') {
|
|
|
19
19
|
const contrastOpts = theme === 'light' ? LIGHT_CONTRAST_COLORS : DARK_CONTRAST_COLORS;
|
|
20
20
|
|
|
21
21
|
return {
|
|
22
|
-
[`--${ name }`]:
|
|
23
|
-
[`--${ name }-text `]:
|
|
24
|
-
[`--${ name }-hover-bg`]:
|
|
25
|
-
[`--${ name }-active-bg`]:
|
|
26
|
-
[`--${ name }-active-text`]:
|
|
27
|
-
[`--${ name }-border`]:
|
|
28
|
-
[`--${ name }-banner-bg`]:
|
|
29
|
-
[`--${ name }-light-bg`]:
|
|
22
|
+
[`--${ name }`]: color,
|
|
23
|
+
[`--${ name }-text `]: contrastColor(color, contrastOpts),
|
|
24
|
+
[`--${ name }-hover-bg`]: lighten(color, -10),
|
|
25
|
+
[`--${ name }-active-bg`]: lighten(color, -25),
|
|
26
|
+
[`--${ name }-active-text`]: contrastColor(lighten(color, -25), contrastOpts),
|
|
27
|
+
[`--${ name }-border`]: color,
|
|
28
|
+
[`--${ name }-banner-bg`]: opacity(color, 0.15),
|
|
29
|
+
[`--${ name }-light-bg`]: opacity(color, 0.05),
|
|
30
|
+
[`--${ name }-keyboard-focus`]: lighten(color, -10),
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
|