@rancher/shell 3.0.2-rc.2 → 3.0.2-rc.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/base/_basic.scss +5 -7
- package/assets/styles/global/_button.scss +10 -0
- package/assets/styles/global/_tooltip.scss +2 -2
- package/assets/styles/themes/_dark.scss +14 -2
- package/assets/styles/themes/_light.scss +7 -2
- package/assets/styles/vendor/vue-select.scss +4 -0
- package/assets/translations/en-us.yaml +44 -5
- package/components/BannerGraphic.vue +0 -42
- package/components/ButtonMultiAction.vue +1 -1
- package/components/Carousel.vue +36 -29
- package/components/CommunityLinks.vue +6 -1
- package/components/GrowlManager.vue +9 -2
- package/components/LocaleSelector.vue +8 -1
- package/components/PaginatedResourceTable.vue +4 -7
- package/components/ProgressBarMulti.vue +14 -0
- package/components/Questions/Reference.vue +57 -28
- package/components/SelectIconGrid.vue +12 -1
- package/components/SideNav.vue +12 -38
- package/components/SortableTable/index.vue +1 -0
- package/components/Tabbed/index.vue +12 -1
- package/components/YamlEditor.vue +1 -0
- package/components/auth/Principal.vue +5 -3
- package/components/fleet/FleetClusters.vue +82 -1
- package/components/fleet/FleetRepos.vue +13 -30
- package/components/fleet/ForceDirectedTreeChart/index.vue +2 -2
- package/components/form/ChangePassword.vue +2 -0
- package/components/form/ColorInput.vue +24 -1
- package/components/form/FileSelector.vue +2 -0
- package/components/form/KeyValue.vue +230 -160
- package/components/form/LabeledSelect.vue +1 -1
- package/components/form/PlusMinus.vue +14 -2
- package/components/form/ResourceLabeledSelect.vue +13 -53
- package/components/form/ResourceSelector.vue +1 -0
- package/components/form/ResourceTabs/index.vue +79 -36
- package/components/form/SecretSelector.vue +2 -2
- package/components/form/__tests__/KeyValue.test.ts +1 -1
- package/components/formatter/FleetClusterSummaryGraph.vue +2 -2
- package/components/formatter/FleetSummaryGraph.vue +6 -7
- package/components/formatter/WorkloadHealthScale.vue +7 -0
- package/components/nav/Group.vue +30 -4
- package/components/nav/Header.vue +82 -114
- package/components/nav/HeaderPageActionMenu.vue +27 -131
- package/components/nav/NamespaceFilter.vue +1 -1
- package/components/nav/Type.vue +15 -0
- package/config/home-links.js +21 -13
- package/config/labels-annotations.js +2 -0
- package/config/page-actions.js +1 -0
- package/config/pagination-table-headers.js +15 -1
- package/config/product/explorer.js +7 -17
- package/config/table-headers.js +6 -0
- package/config/version.js +5 -1
- package/core/plugin.ts +41 -1
- package/core/plugins.js +125 -72
- package/core/types-provisioning.ts +91 -2
- package/core/types.ts +55 -0
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +12 -3
- package/detail/catalog.cattle.io.app.vue +1 -1
- package/detail/fleet.cattle.io.cluster.vue +3 -3
- package/detail/namespace.vue +13 -19
- package/detail/networking.k8s.io.ingress.vue +13 -53
- package/detail/provisioning.cattle.io.cluster.vue +12 -1
- package/detail/workload/index.vue +3 -3
- package/dialog/AddCustomBadgeDialog.vue +5 -1
- package/edit/auth/ldap/__tests__/config.test.ts +18 -0
- package/edit/auth/ldap/config.vue +24 -0
- package/edit/auth/saml.vue +8 -6
- package/edit/fleet.cattle.io.gitrepo.vue +7 -1
- package/edit/logging-flow/index.vue +4 -19
- package/edit/networking.k8s.io.ingress/index.vue +18 -65
- package/edit/networking.k8s.io.networkpolicy/index.vue +4 -5
- package/edit/provisioning.cattle.io.cluster/index.vue +13 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -115
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/tabs/networking/ACE.vue +14 -28
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +25 -12
- package/edit/service.vue +1 -2
- package/list/networking.k8s.io.ingress.vue +1 -1
- package/list/node.vue +15 -8
- package/list/persistentvolume.vue +12 -4
- package/list/service.vue +1 -1
- package/list/workload.vue +4 -0
- package/mixins/chart.js +4 -1
- package/models/catalog.cattle.io.app.js +3 -1
- package/models/catalog.cattle.io.clusterrepo.js +56 -7
- package/models/fleet.cattle.io.bundle.js +0 -11
- package/models/fleet.cattle.io.cluster.js +17 -1
- package/models/fleet.cattle.io.gitrepo.js +86 -50
- package/models/provisioning.cattle.io.cluster.js +47 -2
- package/models/service.js +1 -0
- package/models/workload.js +19 -1
- package/package.json +5 -4
- package/pages/c/_cluster/apps/charts/index.vue +4 -0
- package/pages/c/_cluster/explorer/ConfigBadge.vue +8 -7
- package/pages/c/_cluster/explorer/index.vue +13 -6
- package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +3 -3
- package/pages/c/_cluster/fleet/index.vue +75 -89
- package/pages/c/_cluster/settings/links.vue +2 -2
- package/pages/diagnostic.vue +17 -15
- package/pages/home.vue +32 -6
- package/plugins/clean-html.js +50 -0
- package/plugins/dashboard-store/resource-class.js +4 -0
- package/plugins/plugin.js +54 -49
- package/plugins/steve/mutations.js +1 -1
- package/plugins/steve/steve-class.js +8 -0
- package/plugins/steve/steve-pagination-utils.ts +3 -1
- package/rancher-components/Accordion/Accordion.vue +4 -4
- package/rancher-components/BadgeState/BadgeState.vue +7 -0
- package/rancher-components/Card/Card.vue +27 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +9 -2
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +18 -1
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +39 -2
- package/rancher-components/RcButton/RcButton.vue +90 -0
- package/rancher-components/RcButton/index.ts +2 -0
- package/rancher-components/RcButton/types.ts +17 -0
- package/rancher-components/RcDropdown/RcDropdown.vue +111 -0
- package/rancher-components/RcDropdown/RcDropdownItem.vue +127 -0
- package/rancher-components/RcDropdown/RcDropdownSeparator.vue +6 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +43 -0
- package/rancher-components/RcDropdown/index.ts +4 -0
- package/rancher-components/RcDropdown/types.ts +22 -0
- package/rancher-components/RcDropdown/useDropdownCollection.ts +45 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +83 -0
- package/scripts/test-plugins-build.sh +2 -0
- package/scripts/typegen.sh +2 -0
- package/store/catalog.js +1 -1
- package/tsconfig.json +2 -1
- package/types/components/paginatedResourceTable.ts +25 -0
- package/types/components/resourceLabeledSelect.ts +48 -0
- package/types/resources/fleet.d.ts +17 -0
- package/types/shell/index.d.ts +61 -0
- package/utils/auth.js +5 -1
- package/utils/cluster.js +106 -0
- package/utils/fleet.ts +35 -3
- package/utils/ingress.ts +64 -0
- package/utils/uiplugins.ts +56 -44
- package/utils/validators/cron-schedule.js +7 -2
- package/utils/validators/formRules/__tests__/index.test.ts +53 -17
- package/utils/validators/formRules/index.ts +20 -5
- package/vue.config.js +1 -1
- package/components/RelatedWorkloadsTable.vue +0 -50
package/core/plugins.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { productsLoaded } from '@shell/store/type-map';
|
|
2
2
|
import { clearModelCache } from '@shell/plugins/dashboard-store/model-loader';
|
|
3
|
-
import { Plugin } from './plugin';
|
|
3
|
+
import { EXT_IDS, Plugin } from './plugin';
|
|
4
4
|
import { PluginRoutes } from './plugin-routes';
|
|
5
5
|
import { UI_PLUGIN_BASE_URL } from '@shell/config/uiplugins';
|
|
6
6
|
import { ExtensionPoint } from './types';
|
|
7
|
-
|
|
8
|
-
const MODEL_TYPE = 'models';
|
|
7
|
+
import { addLinkInterceptor, removeLinkInterceptor } from '@shell/plugins/clean-html';
|
|
9
8
|
|
|
10
9
|
export default function(context, inject, vueApp) {
|
|
11
10
|
const {
|
|
@@ -22,10 +21,28 @@ export default function(context, inject, vueApp) {
|
|
|
22
21
|
|
|
23
22
|
const uiConfig = {};
|
|
24
23
|
|
|
24
|
+
// Builtin extensions - these are registered when the UI loads and then initialized/loaded at the same time as the external extensions
|
|
25
|
+
let builtin = [];
|
|
26
|
+
|
|
25
27
|
for (const ep in ExtensionPoint) {
|
|
26
28
|
uiConfig[ExtensionPoint[ep]] = {};
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
/**
|
|
32
|
+
* When an extension adds a model extension, it provides the class - we will instantiate that class and store and use that
|
|
33
|
+
*/
|
|
34
|
+
function instantiateModelExtension($plugin, clz) {
|
|
35
|
+
const context = {
|
|
36
|
+
dispatch: store.dispatch,
|
|
37
|
+
getters: store.getters,
|
|
38
|
+
t: store.getters['i18n/t'],
|
|
39
|
+
$axios,
|
|
40
|
+
$plugin,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return new clz(context);
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
inject(
|
|
30
47
|
'plugin',
|
|
31
48
|
{
|
|
@@ -78,77 +95,79 @@ export default function(context, inject, vueApp) {
|
|
|
78
95
|
element.id = id;
|
|
79
96
|
element.dataset.purpose = 'extension';
|
|
80
97
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (oldPlugin) {
|
|
87
|
-
// Uninstall existing plugin if there is one. This ensures that last loaded plugin is not always used
|
|
88
|
-
// (nav harv1-->harv2-->harv1 and harv2 would be shown)
|
|
89
|
-
removed = this.removePlugin(oldPlugin.name).then(() => {
|
|
90
|
-
delete window[oldPlugin.id];
|
|
91
|
-
|
|
92
|
-
delete plugins[oldPlugin.id];
|
|
93
|
-
|
|
94
|
-
const oldElement = document.getElementById(oldPlugin.id);
|
|
95
|
-
|
|
96
|
-
oldElement.parentElement.removeChild(oldElement);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
removed.then(() => {
|
|
101
|
-
element.onload = () => {
|
|
102
|
-
if (!window[id]) {
|
|
103
|
-
return reject(new Error('Could not load plugin code'));
|
|
104
|
-
}
|
|
98
|
+
element.onload = () => {
|
|
99
|
+
if (!window[id] || (typeof window[id].default !== 'function')) {
|
|
100
|
+
return reject(new Error('Could not load plugin code'));
|
|
101
|
+
}
|
|
105
102
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
// Update the timestamp that new plugins were loaded - may be needed
|
|
104
|
+
// to update caches when new plugins are loaded
|
|
105
|
+
_lastLoaded = new Date().getTime();
|
|
109
106
|
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
// name is the name of the plugin, including the version number
|
|
108
|
+
const plugin = new Plugin(id);
|
|
112
109
|
|
|
113
|
-
|
|
110
|
+
plugins[id] = plugin;
|
|
114
111
|
|
|
115
|
-
|
|
112
|
+
// Initialize the plugin
|
|
113
|
+
try {
|
|
116
114
|
window[id].default(plugin, this.internal());
|
|
115
|
+
} catch (e) {
|
|
116
|
+
delete plugins[id];
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// Load all of the types etc from the plugin
|
|
122
|
-
this.applyPlugin(plugin);
|
|
123
|
-
|
|
124
|
-
// Add the plugin to the store
|
|
125
|
-
store.dispatch('uiplugins/addPlugin', plugin);
|
|
118
|
+
return reject(new Error('Could not initialize plugin'));
|
|
119
|
+
}
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
// Load all of the types etc from the plugin
|
|
122
|
+
this.applyPlugin(plugin);
|
|
129
123
|
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
// Add the plugin to the store
|
|
125
|
+
store.dispatch('uiplugins/addPlugin', plugin);
|
|
132
126
|
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
resolve();
|
|
128
|
+
};
|
|
135
129
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
};
|
|
130
|
+
element.onerror = (e) => {
|
|
131
|
+
element.parentElement.removeChild(element);
|
|
139
132
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const errorMessage = `Failed to unload old plugin${ oldPlugin?.id }`;
|
|
133
|
+
// Massage the error into something useful
|
|
134
|
+
const errorMessage = `Failed to load script from '${ e.target.src }'`;
|
|
143
135
|
|
|
144
136
|
console.error(errorMessage, e); // eslint-disable-line no-console
|
|
145
137
|
reject(new Error(errorMessage)); // This is more useful where it's used
|
|
146
|
-
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
document.head.appendChild(element);
|
|
147
141
|
});
|
|
148
142
|
},
|
|
149
143
|
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Load the builtin extensions by initializing them in turn
|
|
146
|
+
*/
|
|
147
|
+
loadBuiltinExtensions() {
|
|
148
|
+
builtin.forEach((ext) => {
|
|
149
|
+
this.initBuiltinExtension(ext.id, ext.module);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// We've loaded the builtin extensions, so clear out the list so we don't load again
|
|
153
|
+
builtin = [];
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Register a builtin extension that should be loaded
|
|
158
|
+
*
|
|
159
|
+
* Used by the dynamic loader when a plugin is included in the build (see shell/vue.config.js)
|
|
160
|
+
*/
|
|
161
|
+
registerBuiltinExtension(id, module) {
|
|
162
|
+
builtin.push({ id, module });
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Initialize a builtin extension
|
|
167
|
+
*
|
|
168
|
+
* This is only used by the 'loadBuiltinExtensions' function above
|
|
169
|
+
*/
|
|
170
|
+
initBuiltinExtension(id, module) {
|
|
152
171
|
const plugin = new Plugin(id);
|
|
153
172
|
|
|
154
173
|
// Mark the plugin as being built-in
|
|
@@ -160,19 +179,32 @@ export default function(context, inject, vueApp) {
|
|
|
160
179
|
const p = module;
|
|
161
180
|
|
|
162
181
|
try {
|
|
163
|
-
p.default(plugin, this.internal());
|
|
164
|
-
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
182
|
+
const load = p.default(plugin, this.internal());
|
|
183
|
+
|
|
184
|
+
// The function must explicitly return false to skip loading of the extension (this is only allows on builtin extensions)
|
|
185
|
+
// Only built-in extensions can return that they should not be loaded, because the extension can still do 'things'
|
|
186
|
+
// in its init code (inject code, styles etc), so we do not want to hide an extension that has 'partially' loaded,
|
|
187
|
+
// just because it tells us it should not load.
|
|
188
|
+
// Built-in extensions are compiled into the app, so there is a level of trust assumed with them
|
|
189
|
+
if (load !== false) {
|
|
190
|
+
// Update last load so that the translations get loaded
|
|
191
|
+
_lastLoaded = new Date().getTime();
|
|
192
|
+
|
|
193
|
+
// Load all of the types etc from the extension
|
|
194
|
+
this.applyPlugin(plugin);
|
|
195
|
+
|
|
196
|
+
// Add the extension to the store
|
|
197
|
+
store.dispatch('uiplugins/addPlugin', plugin);
|
|
198
|
+
} else {
|
|
199
|
+
// Plugin did not load, so remove it so it is not shown as loaded
|
|
200
|
+
delete plugins[id];
|
|
201
|
+
}
|
|
173
202
|
} catch (e) {
|
|
174
|
-
console.error(`Error loading
|
|
203
|
+
console.error(`Error loading extension ${ plugin.name }`); // eslint-disable-line no-console
|
|
175
204
|
console.error(e); // eslint-disable-line no-console
|
|
205
|
+
|
|
206
|
+
// Plugin did not load, so remove it so it is not shown as loaded
|
|
207
|
+
delete plugins[id];
|
|
176
208
|
}
|
|
177
209
|
},
|
|
178
210
|
|
|
@@ -189,7 +221,7 @@ export default function(context, inject, vueApp) {
|
|
|
189
221
|
try {
|
|
190
222
|
await this.removePlugin(plugin.name);
|
|
191
223
|
} catch (e) {
|
|
192
|
-
console.error('Error removing
|
|
224
|
+
console.error('Error removing extension', e); // eslint-disable-line no-console
|
|
193
225
|
}
|
|
194
226
|
|
|
195
227
|
delete plugins[plugin.id];
|
|
@@ -215,7 +247,7 @@ export default function(context, inject, vueApp) {
|
|
|
215
247
|
Object.keys(plugin.types[typ]).forEach((name) => {
|
|
216
248
|
this.unregister(typ, name);
|
|
217
249
|
|
|
218
|
-
if (typ ===
|
|
250
|
+
if (typ === EXT_IDS.MODELS) {
|
|
219
251
|
clearModelCache(name);
|
|
220
252
|
}
|
|
221
253
|
});
|
|
@@ -246,6 +278,13 @@ export default function(context, inject, vueApp) {
|
|
|
246
278
|
delete validators[key];
|
|
247
279
|
});
|
|
248
280
|
|
|
281
|
+
// Remove link interceptors
|
|
282
|
+
if (plugin.types.linkInterceptor) {
|
|
283
|
+
Object.keys(plugin.types.linkInterceptor).forEach((name) => {
|
|
284
|
+
removeLinkInterceptor(plugin.types.linkInterceptor[name]);
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
249
288
|
await Promise.all(promises);
|
|
250
289
|
|
|
251
290
|
// Update last load since we removed a plugin
|
|
@@ -284,6 +323,13 @@ export default function(context, inject, vueApp) {
|
|
|
284
323
|
});
|
|
285
324
|
});
|
|
286
325
|
|
|
326
|
+
// Model extensions
|
|
327
|
+
Object.keys(plugin.modelExtensions).forEach((name) => {
|
|
328
|
+
plugin.modelExtensions[name].forEach((fn) => {
|
|
329
|
+
this.register(EXT_IDS.MODEL_EXTENSION, name, instantiateModelExtension(this, fn));
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
287
333
|
// Initialize the product if the store is ready
|
|
288
334
|
if (productsLoaded()) {
|
|
289
335
|
this.loadProducts([plugin]);
|
|
@@ -304,6 +350,13 @@ export default function(context, inject, vueApp) {
|
|
|
304
350
|
Object.keys(plugin.validators).forEach((key) => {
|
|
305
351
|
validators[key] = plugin.validators[key];
|
|
306
352
|
});
|
|
353
|
+
|
|
354
|
+
// Link Interceptors
|
|
355
|
+
if (dynamic.linkInterceptor) {
|
|
356
|
+
Object.keys(dynamic.linkInterceptor).forEach((name) => {
|
|
357
|
+
addLinkInterceptor(dynamic.linkInterceptor[name], name);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
307
360
|
},
|
|
308
361
|
|
|
309
362
|
/**
|
|
@@ -317,8 +370,8 @@ export default function(context, inject, vueApp) {
|
|
|
317
370
|
dynamic[type] = {};
|
|
318
371
|
}
|
|
319
372
|
|
|
320
|
-
// Accumulate l10n resources rather than replace
|
|
321
|
-
if (type === 'l10n') {
|
|
373
|
+
// Accumulate l10n resources and model extensions rather than replace
|
|
374
|
+
if (type === 'l10n' || type === EXT_IDS.MODEL_EXTENSION) {
|
|
322
375
|
if (!dynamic[type][name]) {
|
|
323
376
|
dynamic[type][name] = [];
|
|
324
377
|
}
|
|
@@ -13,6 +13,37 @@ export type ClusterSaveHook = (cluster: any) => Promise<any>
|
|
|
13
13
|
*/
|
|
14
14
|
export type RegisterClusterSaveHook = (hook: ClusterSaveHook, name: string, priority?: number, fnContext?: any) => void;
|
|
15
15
|
|
|
16
|
+
export type ClusterDetailTabs = {
|
|
17
|
+
/**
|
|
18
|
+
* RKE2 machine pool tabs
|
|
19
|
+
*/
|
|
20
|
+
machines: boolean,
|
|
21
|
+
/**
|
|
22
|
+
* RKE2 provisioning logs
|
|
23
|
+
*/
|
|
24
|
+
logs: boolean,
|
|
25
|
+
/**
|
|
26
|
+
* RKE2 registration commands
|
|
27
|
+
*/
|
|
28
|
+
registration: boolean,
|
|
29
|
+
/**
|
|
30
|
+
* RKE2 snapshots
|
|
31
|
+
*/
|
|
32
|
+
snapshots: boolean,
|
|
33
|
+
/**
|
|
34
|
+
* Kube resources related to the instance of provisioning.cattle.io.cluster
|
|
35
|
+
*/
|
|
36
|
+
related: boolean,
|
|
37
|
+
/**
|
|
38
|
+
* Kube events associated with the instance of provisioning.cattle.io.cluster
|
|
39
|
+
*/
|
|
40
|
+
events: boolean,
|
|
41
|
+
/**
|
|
42
|
+
* Kube conditions of the provisioning.cattle.io.cluster instance
|
|
43
|
+
*/
|
|
44
|
+
conditions: boolean
|
|
45
|
+
};
|
|
46
|
+
|
|
16
47
|
/**
|
|
17
48
|
* Params used when constructing an instance of the cluster provisioner
|
|
18
49
|
*/
|
|
@@ -57,7 +88,6 @@ export interface ClusterProvisionerContext {
|
|
|
57
88
|
* The majority of these hooks are used in shell/edit/provisioning.cattle.io.cluster/rke2.vue
|
|
58
89
|
*/
|
|
59
90
|
export interface IClusterProvisioner {
|
|
60
|
-
|
|
61
91
|
/**
|
|
62
92
|
* Unique ID of the Cluster Provisioner
|
|
63
93
|
* If this overlaps with the name of an existing provisioner (seen in the type query param while creating a cluster) this provisioner will overwrite the built-in ui
|
|
@@ -233,7 +263,7 @@ export interface IClusterProvisioner {
|
|
|
233
263
|
registerSaveHooks?(registerBeforeHook: RegisterClusterSaveHook, registerAfterHook: RegisterClusterSaveHook, cluster: any): void;
|
|
234
264
|
|
|
235
265
|
/**
|
|
236
|
-
* Optionally override the save of the cluster resource itself
|
|
266
|
+
* Optionally override the save of the cluster resource itself
|
|
237
267
|
*
|
|
238
268
|
* https://github.com/rancher/dashboard/blob/master/shell/mixins/create-edit-view/impl.js#L179
|
|
239
269
|
*
|
|
@@ -263,3 +293,62 @@ export interface IClusterProvisioner {
|
|
|
263
293
|
*/
|
|
264
294
|
provision?(cluster: any, pools: any[]): Promise<any[]>;
|
|
265
295
|
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Interface that a model extension for the provisioning cluster model should implement
|
|
299
|
+
*/
|
|
300
|
+
export interface IClusterModelExtension {
|
|
301
|
+
/**
|
|
302
|
+
* Indicates if this extension should be used for the given cluster
|
|
303
|
+
*
|
|
304
|
+
* This allows the extension to determine if it should be used for a cluster based on attributes/metadata of its choosing
|
|
305
|
+
*
|
|
306
|
+
* @param cluster The cluster model (`provisioning.cattle.io.cluster`)
|
|
307
|
+
* @returns Whether to use this provisioner for the given cluster.
|
|
308
|
+
*/
|
|
309
|
+
useFor(cluster: any): boolean;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Optionally Process the available actions for a cluster and return a (possibly modified) set of actions
|
|
313
|
+
*
|
|
314
|
+
* @param cluster The cluster model (`provisioning.cattle.io.cluster`)
|
|
315
|
+
* @returns List of actions for the cluster or undefined if the list is modified in-place
|
|
316
|
+
*/
|
|
317
|
+
availableActions?(cluster: any, actions: any[]): any[] | undefined;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get the display name for the machine provider for this model
|
|
321
|
+
*
|
|
322
|
+
* @param cluster The cluster model (`provisioning.cattle.io.cluster`)
|
|
323
|
+
* @returns Machine provider display name
|
|
324
|
+
*/
|
|
325
|
+
machineProviderDisplay?(cluster: any): string;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get the display name for the provisioner for this model
|
|
329
|
+
*
|
|
330
|
+
* @param cluster The cluster model (`provisioning.cattle.io.cluster`)
|
|
331
|
+
* @returns Provisioner display name
|
|
332
|
+
*/
|
|
333
|
+
provisionerDisplay?(cluster: any): string;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get the parent cluster for this cluster, or undefined if no parent cluster
|
|
337
|
+
*
|
|
338
|
+
* @param cluster The cluster model (`provisioning.cattle.io.cluster`)
|
|
339
|
+
* @returns ID of the parent cluster
|
|
340
|
+
*/
|
|
341
|
+
parentCluster?(cluster: any): string;
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Function to run after the cluster has been deleted
|
|
345
|
+
*
|
|
346
|
+
* @param cluster The cluster (`provisioning.cattle.io.cluster`)
|
|
347
|
+
*/
|
|
348
|
+
postDelete?(cluster: any): void;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Existing tabs to show or hide in the cluster's detail view
|
|
352
|
+
*/
|
|
353
|
+
get detailTabs(): ClusterDetailTabs;
|
|
354
|
+
}
|
package/core/types.ts
CHANGED
|
@@ -156,6 +156,16 @@ export type LocationConfig = {
|
|
|
156
156
|
context?: { [key: string]: string},
|
|
157
157
|
};
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Environment metadata that extensions can access
|
|
161
|
+
*/
|
|
162
|
+
export type ExtensionEnvironment = {
|
|
163
|
+
version: string;
|
|
164
|
+
commit: string;
|
|
165
|
+
isPrime: boolean;
|
|
166
|
+
docsVersion: string; /** e.g. 'v2.10' */
|
|
167
|
+
};
|
|
168
|
+
|
|
159
169
|
export interface ProductOptions {
|
|
160
170
|
/**
|
|
161
171
|
* The category this product belongs under. i.e. 'config'
|
|
@@ -478,6 +488,37 @@ export interface DSLReturnType {
|
|
|
478
488
|
// weightType: (input, weight, forBasic)
|
|
479
489
|
}
|
|
480
490
|
|
|
491
|
+
/**
|
|
492
|
+
* Context for the constructor of a model extension
|
|
493
|
+
*/
|
|
494
|
+
export type ModelExtensionContext = {
|
|
495
|
+
/**
|
|
496
|
+
* Dispatch vuex actions
|
|
497
|
+
*/
|
|
498
|
+
dispatch: any,
|
|
499
|
+
/**
|
|
500
|
+
* Get from vuex store
|
|
501
|
+
*/
|
|
502
|
+
getters: any,
|
|
503
|
+
/**
|
|
504
|
+
* Used to make http requests
|
|
505
|
+
*/
|
|
506
|
+
axios: any,
|
|
507
|
+
/**
|
|
508
|
+
* Definition of the extension
|
|
509
|
+
*/
|
|
510
|
+
$plugin: any,
|
|
511
|
+
/**
|
|
512
|
+
* Function to retrieve a localised string
|
|
513
|
+
*/
|
|
514
|
+
t: (key: string) => string,
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Constructor signature for a model extension
|
|
519
|
+
*/
|
|
520
|
+
export type ModelExtensionConstructor = (context: ModelExtensionContext) => Object;
|
|
521
|
+
|
|
481
522
|
/**
|
|
482
523
|
* Interface for a Dashboard plugin
|
|
483
524
|
*/
|
|
@@ -584,6 +625,15 @@ export interface IPlugin {
|
|
|
584
625
|
onLogOut?: OnLogOut
|
|
585
626
|
): void;
|
|
586
627
|
|
|
628
|
+
/**
|
|
629
|
+
* Adds a model extension
|
|
630
|
+
* @experimental May change or be removed in the future
|
|
631
|
+
*
|
|
632
|
+
* @param type Model type
|
|
633
|
+
* @param clz Class for the model extension (constructor)
|
|
634
|
+
*/
|
|
635
|
+
addModelExtension(type: string, clz: ModelExtensionConstructor): void;
|
|
636
|
+
|
|
587
637
|
/**
|
|
588
638
|
* Register 'something' that can be dynamically loaded - e.g. model, edit, create, list, i18n
|
|
589
639
|
* @param {String} type type of thing to register, e.g. 'edit'
|
|
@@ -598,6 +648,11 @@ export interface IPlugin {
|
|
|
598
648
|
* @param productName The name of the new product. This name is displayed in the navigation.
|
|
599
649
|
*/
|
|
600
650
|
DSL(store: any, productName: string): DSLReturnType;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Get information about the Extension Environment
|
|
654
|
+
*/
|
|
655
|
+
get environment(): ExtensionEnvironment;
|
|
601
656
|
}
|
|
602
657
|
|
|
603
658
|
// Internal interface
|
|
@@ -7,9 +7,18 @@ describe('view: autoscaling.horizontalpodautoscaler', () => {
|
|
|
7
7
|
'i18n/t': (text: string) => text,
|
|
8
8
|
t: (text: string) => text,
|
|
9
9
|
currentStore: () => 'current_store',
|
|
10
|
-
'current_store/schemaFor':
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
'current_store/schemaFor': () => ({
|
|
11
|
+
attributes: {
|
|
12
|
+
columns: [
|
|
13
|
+
{ name: 'Subobject', field: '' },
|
|
14
|
+
{ name: 'Source', field: '' },
|
|
15
|
+
{ name: 'First Seen', field: '' },
|
|
16
|
+
{ name: 'Count', field: '' }]
|
|
17
|
+
}
|
|
18
|
+
}),
|
|
19
|
+
'current_store/all': jest.fn(),
|
|
20
|
+
workspace: jest.fn(),
|
|
21
|
+
'i18n/exists': jest.fn(),
|
|
13
22
|
},
|
|
14
23
|
};
|
|
15
24
|
|
|
@@ -34,7 +34,7 @@ export default {
|
|
|
34
34
|
const promises = {
|
|
35
35
|
catalog: this.$store.dispatch('catalog/load'),
|
|
36
36
|
allOperations: this.$store.dispatch('cluster/findAll', { type: CATALOG.OPERATION }),
|
|
37
|
-
|
|
37
|
+
secret: this.value.fetchValues(true),
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const res = await allHash(promises);
|
|
@@ -29,11 +29,11 @@ export default {
|
|
|
29
29
|
},
|
|
30
30
|
|
|
31
31
|
async fetch() {
|
|
32
|
-
const
|
|
32
|
+
const managementClusterId = this.value?.metadata?.labels[FLEET_LABELS.CLUSTER_NAME];
|
|
33
33
|
const hash = await allHash({
|
|
34
34
|
rancherCluster: this.$store.dispatch('management/find', {
|
|
35
35
|
type: MANAGEMENT.CLUSTER,
|
|
36
|
-
id:
|
|
36
|
+
id: managementClusterId
|
|
37
37
|
}),
|
|
38
38
|
repos: this.$store.dispatch('management/findAll', { type: FLEET.GIT_REPO }),
|
|
39
39
|
workspaces: this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE }),
|
|
@@ -53,7 +53,7 @@ export default {
|
|
|
53
53
|
return this.value.bundleDeployments;
|
|
54
54
|
},
|
|
55
55
|
clusterId() {
|
|
56
|
-
return this.value
|
|
56
|
+
return this.value.id;
|
|
57
57
|
},
|
|
58
58
|
|
|
59
59
|
repos() {
|
package/detail/namespace.vue
CHANGED
|
@@ -10,9 +10,7 @@ import Tab from '@shell/components/Tabbed/Tab';
|
|
|
10
10
|
import ResourceTable from '@shell/components/ResourceTable';
|
|
11
11
|
import SortableTable from '@shell/components/SortableTable';
|
|
12
12
|
import Loading from '@shell/components/Loading';
|
|
13
|
-
import {
|
|
14
|
-
flatten, compact, filter, findKey, values
|
|
15
|
-
} from 'lodash';
|
|
13
|
+
import { flatten, compact, findKey, values } from 'lodash';
|
|
16
14
|
|
|
17
15
|
export default {
|
|
18
16
|
emits: ['input'],
|
|
@@ -89,6 +87,9 @@ export default {
|
|
|
89
87
|
},
|
|
90
88
|
];
|
|
91
89
|
|
|
90
|
+
const params = this.$route.params;
|
|
91
|
+
const { id: namespaceId } = params;
|
|
92
|
+
|
|
92
93
|
return {
|
|
93
94
|
allWorkloads: {
|
|
94
95
|
default: () => ([]),
|
|
@@ -97,7 +98,10 @@ export default {
|
|
|
97
98
|
resourceTypes: [],
|
|
98
99
|
summaryStates: ['success', 'info', 'warning', 'error', 'unknown'],
|
|
99
100
|
headers,
|
|
100
|
-
workloadSchema: WORKLOAD_SCHEMA
|
|
101
|
+
workloadSchema: WORKLOAD_SCHEMA,
|
|
102
|
+
inStore: this.$store.getters['currentProduct'].inStore,
|
|
103
|
+
statesByType: getStatesByType(),
|
|
104
|
+
namespaceId,
|
|
101
105
|
};
|
|
102
106
|
},
|
|
103
107
|
|
|
@@ -106,10 +110,6 @@ export default {
|
|
|
106
110
|
},
|
|
107
111
|
|
|
108
112
|
computed: {
|
|
109
|
-
inStore() {
|
|
110
|
-
return this.$store.getters['currentProduct'].inStore;
|
|
111
|
-
},
|
|
112
|
-
|
|
113
113
|
namespacedResourceCounts() {
|
|
114
114
|
const allClusterResourceCounts = this.$store.getters[`${ this.inStore }/all`](COUNT)[0].counts;
|
|
115
115
|
|
|
@@ -148,20 +148,11 @@ export default {
|
|
|
148
148
|
}, totals);
|
|
149
149
|
},
|
|
150
150
|
|
|
151
|
-
statesByType() {
|
|
152
|
-
return getStatesByType();
|
|
153
|
-
},
|
|
154
|
-
|
|
155
151
|
/**
|
|
156
152
|
* Workload table data for current namespace
|
|
157
153
|
*/
|
|
158
154
|
workloadRows() {
|
|
159
|
-
|
|
160
|
-
const { id } = params;
|
|
161
|
-
const rows = flatten(compact(this.allWorkloads)).filter((row) => !row.ownedByWorkload);
|
|
162
|
-
const namespacedRows = filter(rows, ({ metadata: { namespace } }) => namespace === id);
|
|
163
|
-
|
|
164
|
-
return namespacedRows;
|
|
155
|
+
return flatten(compact(this.allWorkloads)).filter((row) => !row.ownedByWorkload);
|
|
165
156
|
}
|
|
166
157
|
},
|
|
167
158
|
|
|
@@ -198,7 +189,10 @@ export default {
|
|
|
198
189
|
return Promise.all(values(WORKLOAD_TYPES)
|
|
199
190
|
// You may not have RBAC to see some of the types
|
|
200
191
|
.filter((type) => Boolean(this.schemaFor(type)))
|
|
201
|
-
|
|
192
|
+
// findAll on each workload type here, argh! however...
|
|
193
|
+
// - results are shown in a single table containing all workloads rather than an SSP compatible way (one table per type)
|
|
194
|
+
// - we're restricting by namespace. not great, but a big improvement
|
|
195
|
+
.map((type) => this.$store.dispatch('cluster/findAll', { type, opt: { namespaced: this.namespaceId } }))
|
|
202
196
|
);
|
|
203
197
|
},
|
|
204
198
|
|