@rancher/shell 3.0.2-rc.6 → 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/global/_layout.scss +3 -1
- 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/FixedBanner.vue +19 -12
- package/components/LocaleSelector.vue +2 -0
- package/components/SortableTable/THead.vue +2 -0
- package/components/SortableTable/index.vue +35 -5
- package/components/StatusBadge.vue +71 -0
- package/components/__tests__/FixedBanner.test.ts +3 -3
- package/components/form/MatchExpressions.vue +4 -0
- package/components/form/Select.vue +11 -2
- package/components/form/UnitInput.vue +2 -2
- package/components/form/__tests__/UnitInput.test.ts +4 -5
- 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/TopLevelMenu.vue +1 -12
- package/components/nav/Type.vue +1 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
- 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/__tests__/oidc.test.ts +34 -3
- package/edit/auth/ldap/__tests__/config.test.ts +0 -14
- package/edit/auth/ldap/config.vue +0 -24
- package/edit/auth/oidc.vue +1 -1
- 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/__tests__/Advanced.test.ts +0 -2
- package/edit/provisioning.cattle.io.cluster/rke2.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/edit/service.vue +0 -3
- package/edit/workload/Job.vue +6 -6
- package/edit/workload/__tests__/Job.test.ts +0 -1
- package/models/__tests__/logging.banzaicloud.io.flow.test.ts +88 -0
- package/models/logging.banzaicloud.io.flow.js +2 -1
- package/package.json +2 -2
- package/pages/auth/login.vue +1 -9
- package/rancher-components/Form/Checkbox/Checkbox.vue +9 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -2
- package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
- package/scripts/test-plugins-build.sh +4 -6
- package/store/aws.js +9 -2
- package/types/shell/index.d.ts +0 -10
- package/utils/banners.js +0 -45
- package/utils/color.js +9 -8
- package/utils/object.js +0 -3
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)"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import TopLevelMenu from '@shell/components/nav/TopLevelMenu.vue';
|
|
2
|
-
import { SETTING } from '@shell/config/settings';
|
|
3
2
|
import { mount, Wrapper } from '@vue/test-utils';
|
|
4
3
|
import { CAPI, COUNT, MANAGEMENT } from '@shell/config/types';
|
|
5
4
|
import { PINNED_CLUSTERS } from '@shell/store/prefs';
|
|
@@ -429,45 +428,6 @@ describe('topLevelMenu', () => {
|
|
|
429
428
|
expect(description4.text()).toStrictEqual('some-description4');
|
|
430
429
|
});
|
|
431
430
|
|
|
432
|
-
it('should not "crash" the component if the structure of banner settings is in an old format', async() => {
|
|
433
|
-
const wrapper: Wrapper<InstanceType<typeof TopLevelMenu>> = mount(TopLevelMenu, {
|
|
434
|
-
global: {
|
|
435
|
-
mocks: {
|
|
436
|
-
$route: {},
|
|
437
|
-
$store: {
|
|
438
|
-
...generateStore(
|
|
439
|
-
[{ name: 'whatever' }],
|
|
440
|
-
[
|
|
441
|
-
// object based on https://github.com/rancher/dashboard/issues/10140#issuecomment-1883252402
|
|
442
|
-
{
|
|
443
|
-
id: SETTING.BANNERS,
|
|
444
|
-
value: JSON.stringify({
|
|
445
|
-
banner: {
|
|
446
|
-
color: '#78c9cf',
|
|
447
|
-
background: '#27292e',
|
|
448
|
-
text: 'Hello World!'
|
|
449
|
-
},
|
|
450
|
-
showHeader: 'true',
|
|
451
|
-
showFooter: 'true'
|
|
452
|
-
})
|
|
453
|
-
}
|
|
454
|
-
]
|
|
455
|
-
),
|
|
456
|
-
}
|
|
457
|
-
},
|
|
458
|
-
|
|
459
|
-
stubs: ['BrandImage', 'router-link'],
|
|
460
|
-
},
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
await waitForIt();
|
|
464
|
-
|
|
465
|
-
expect(wrapper.vm.sideMenuStyle).toStrictEqual({
|
|
466
|
-
marginBottom: '2em',
|
|
467
|
-
marginTop: '2em'
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
|
|
471
431
|
describe('searching a term', () => {
|
|
472
432
|
describe('should displays a no results message if have clusters but', () => {
|
|
473
433
|
it('given no matching clusters', async() => {
|
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
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nextTick } from 'vue';
|
|
2
2
|
/* eslint-disable jest/no-hooks */
|
|
3
|
-
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { mount, type VueWrapper } from '@vue/test-utils';
|
|
4
4
|
import { _EDIT } from '@shell/config/query-params';
|
|
5
5
|
|
|
6
6
|
import oidc from '@shell/edit/auth/oidc.vue';
|
|
@@ -32,7 +32,7 @@ const mockModel = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
describe('oidc.vue', () => {
|
|
35
|
-
let wrapper: any
|
|
35
|
+
let wrapper: VueWrapper<any, any>;
|
|
36
36
|
const requiredSetup = () => ({
|
|
37
37
|
data() {
|
|
38
38
|
return {
|
|
@@ -44,7 +44,7 @@ describe('oidc.vue', () => {
|
|
|
44
44
|
originalModel: null,
|
|
45
45
|
principals: [],
|
|
46
46
|
authConfigName: 'oidc',
|
|
47
|
-
};
|
|
47
|
+
} as any; // any is necessary as in pre-existing tests we are including inherited mixins values
|
|
48
48
|
},
|
|
49
49
|
global: {
|
|
50
50
|
mocks: {
|
|
@@ -148,4 +148,35 @@ describe('oidc.vue', () => {
|
|
|
148
148
|
expect(groupSearchCheckbox.isVisible()).toBe(true);
|
|
149
149
|
expect(wrapper.vm.model.groupSearchEnabled).toBe(true);
|
|
150
150
|
});
|
|
151
|
+
|
|
152
|
+
it('changing URL should update issuer and auth-endpoint if Keycloak', async() => {
|
|
153
|
+
wrapper.vm.model.id = 'keycloakoidc';
|
|
154
|
+
const newUrl = 'whatever';
|
|
155
|
+
|
|
156
|
+
await wrapper.find(`[data-testid="oidc-url"]`).setValue(newUrl);
|
|
157
|
+
await wrapper.vm.$nextTick();
|
|
158
|
+
|
|
159
|
+
const issuer = (wrapper.find('[data-testid="oidc-issuer"]').element as HTMLInputElement).value;
|
|
160
|
+
const endpoint = (wrapper.find('[data-testid="oidc-auth-endpoint"]').element as HTMLInputElement).value;
|
|
161
|
+
|
|
162
|
+
expect(issuer).toBe(`${ newUrl }/realms/`);
|
|
163
|
+
expect(endpoint).toBe(`${ newUrl }/realms//protocol/openid-connect/auth`);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('changing realm should update issuer and auth-endpoint if Keycloak', async() => {
|
|
167
|
+
const newRealm = 'newRealm';
|
|
168
|
+
const oldUrl = 'oldUrl';
|
|
169
|
+
|
|
170
|
+
wrapper.vm.model.id = 'keycloakoidc';
|
|
171
|
+
wrapper.vm.oidcUrls.url = oldUrl;
|
|
172
|
+
|
|
173
|
+
await wrapper.find(`[data-testid="oidc-realm"]`).setValue(newRealm);
|
|
174
|
+
await wrapper.vm.$nextTick();
|
|
175
|
+
|
|
176
|
+
const issuer = (wrapper.find('[data-testid="oidc-issuer"]').element as HTMLInputElement).value;
|
|
177
|
+
const endpoint = (wrapper.find('[data-testid="oidc-auth-endpoint"]').element as HTMLInputElement).value;
|
|
178
|
+
|
|
179
|
+
expect(issuer).toBe(`${ oldUrl }/realms/${ newRealm }`);
|
|
180
|
+
expect(endpoint).toBe(`${ oldUrl }/realms/${ newRealm }/protocol/openid-connect/auth`);
|
|
181
|
+
});
|
|
151
182
|
});
|
|
@@ -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
|
package/edit/auth/oidc.vue
CHANGED
|
@@ -136,7 +136,7 @@ export default {
|
|
|
136
136
|
const isKeycloak = this.model.id === 'keycloakoidc';
|
|
137
137
|
|
|
138
138
|
const url = this.oidcUrls.url.replaceAll(' ', '');
|
|
139
|
-
const realmsPath =
|
|
139
|
+
const realmsPath = 'realms';
|
|
140
140
|
|
|
141
141
|
this.model.issuer = `${ url }/${ realmsPath }/${ this.oidcUrls.realm || '' }`;
|
|
142
142
|
|
|
@@ -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')"
|
|
@@ -1701,7 +1701,7 @@ export default {
|
|
|
1701
1701
|
const defaultChartValue = this.versionInfo[name];
|
|
1702
1702
|
const key = this.chartVersionKey(name);
|
|
1703
1703
|
|
|
1704
|
-
return
|
|
1704
|
+
return merge({}, defaultChartValue?.values || {}, this.userChartValues[key] || {});
|
|
1705
1705
|
},
|
|
1706
1706
|
|
|
1707
1707
|
initServerAgentArgs() {
|
|
@@ -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'];
|