@rancher/shell 0.1.3 → 0.1.4
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/brand/suse/dark/rancher-logo.svg +1 -148
- package/assets/brand/suse/rancher-logo.svg +1 -130
- package/assets/images/featured/img1.jpg +0 -0
- package/assets/images/featured.jpg +0 -0
- package/assets/images/generic-plugin.svg +7 -0
- package/assets/styles/themes/_dark.scss +3 -0
- package/assets/styles/themes/_light.scss +3 -0
- package/assets/styles/themes/_suse.scss +1 -1
- package/assets/translations/en-us.yaml +183 -45
- package/assets/translations/zh-hans.yaml +21 -24
- package/components/AsyncButton.vue +17 -2
- package/components/ButtonDropdown.vue +4 -0
- package/components/Carousel.vue +291 -0
- package/components/CommunityLinks.vue +69 -18
- package/components/CruResource.vue +11 -3
- package/components/Dialog.vue +102 -0
- package/components/ExplorerMembers.vue +2 -4
- package/components/ExplorerProjectsNamespaces.vue +6 -7
- package/components/IconMessage.vue +9 -1
- package/components/LocaleSelector.vue +62 -29
- package/components/ResourceTable.vue +7 -2
- package/components/SimpleBox.vue +6 -4
- package/components/SortableTable/index.vue +11 -21
- package/components/Tabbed/Tab.vue +5 -0
- package/components/Tabbed/index.vue +29 -2
- package/components/auth/Principal.vue +1 -0
- package/components/fleet/FleetBundles.vue +8 -3
- package/components/fleet/FleetSummary.vue +6 -0
- package/components/form/KeyValue.vue +80 -58
- package/components/form/NameNsDescription.vue +10 -4
- package/components/form/ResourceTabs/index.vue +5 -1
- package/components/formatter/ClusterLink.vue +3 -7
- package/components/nav/NamespaceFilter.vue +3 -3
- package/components/nav/TopLevelMenu.vue +10 -28
- package/config/footer.js +13 -14
- package/config/labels-annotations.js +2 -1
- package/config/product/explorer.js +5 -4
- package/config/product/legacy.js +0 -47
- package/config/product/multi-cluster-apps.js +0 -12
- package/config/product/settings.js +12 -1
- package/config/product/uiplugins.js +17 -0
- package/config/settings.js +21 -2
- package/config/types.js +5 -1
- package/config/uiplugins.js +60 -0
- package/content/docs/en-us/getting-started.md +1 -26
- package/core/plugins.js +12 -0
- package/detail/provisioning.cattle.io.cluster.vue +3 -3
- package/detail/workload/index.vue +2 -2
- package/dialog/DiagnosticTimingsDialog.vue +116 -0
- package/dialog/RotateCertificatesDialog.vue +9 -3
- package/edit/auth/azuread.vue +28 -9
- package/edit/networking.k8s.io.ingress/index.vue +2 -2
- package/edit/persistentvolume/index.vue +3 -0
- package/edit/pod.vue +27 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +76 -5
- package/edit/service.vue +7 -5
- package/edit/workload/__tests__/Upgrading.test.ts +1 -0
- package/edit/workload/index.vue +13 -1
- package/edit/workload/mixins/workload.js +13 -13
- package/edit/workload/storage/ContainerMountPaths.vue +240 -0
- package/edit/workload/storage/Mount.vue +1 -0
- package/edit/workload/storage/awsElasticBlockStore.vue +20 -1
- package/edit/workload/storage/azureDisk.vue +22 -2
- package/edit/workload/storage/azureFile.vue +20 -2
- package/edit/workload/storage/csi/index.vue +23 -1
- package/edit/workload/storage/gcePersistentDisk.vue +20 -2
- package/edit/workload/storage/index.vue +23 -49
- package/edit/workload/storage/vsphereVolume.vue +11 -1
- package/layouts/default.vue +14 -8
- package/layouts/home.vue +9 -4
- package/layouts/plain.vue +10 -5
- package/list/management.cattle.io.setting.vue +3 -3
- package/list/provisioning.cattle.io.cluster.vue +1 -1
- package/machine-config/harvester.vue +5 -3
- package/models/catalog.cattle.io.uiplugin.js +34 -0
- package/models/cluster/node.js +25 -2
- package/models/fleet.cattle.io.bundle.js +1 -1
- package/models/harvesterhci.io.management.cluster.js +11 -5
- package/models/provisioning.cattle.io.cluster.js +12 -6
- package/models/workload.js +5 -3
- package/nuxt.config.js +69 -25
- package/package.json +108 -109
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/index.vue +46 -1
- package/pages/c/_cluster/apps/charts/install.vue +10 -9
- package/pages/c/_cluster/explorer/index.vue +72 -9
- package/pages/c/_cluster/explorer/tools/index.vue +12 -5
- package/pages/c/_cluster/mcapps/index.vue +1 -1
- package/pages/c/_cluster/settings/brand.vue +0 -40
- package/pages/c/_cluster/settings/links.vue +200 -0
- package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +242 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +284 -0
- package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +130 -0
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +253 -0
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +115 -0
- package/pages/c/_cluster/uiplugins/index.vue +694 -0
- package/pages/diagnostic.vue +185 -101
- package/pages/docs/_doc.vue +3 -1
- package/pages/home.vue +21 -56
- package/pages/prefs.vue +108 -88
- package/pages/safeMode.vue +17 -0
- package/pages/support/index.vue +23 -15
- package/pkg/dynamic-importer.lib.js +4 -0
- package/plugins/dashboard-store/resource-class.js +2 -2
- package/plugins/formatters.js +15 -0
- package/plugins/plugin.js +56 -4
- package/plugins/steve/mutations.js +1 -1
- package/plugins/steve/subscribe.js +94 -72
- package/plugins/steve/web-worker.steve-sub-worker.js +24 -15
- package/promptRemove/management.cattle.io.globalrole.vue +47 -0
- package/promptRemove/management.cattle.io.roletemplate.vue +47 -0
- package/promptRemove/mixin/roleDeletionCheck.js +97 -0
- package/scripts/publish-shell.sh +1 -1
- package/scripts/sync-shell-deps +37 -0
- package/store/catalog.js +9 -8
- package/store/i18n.js +10 -1
- package/store/prefs.js +16 -0
- package/store/type-map.js +32 -5
- package/store/uiplugins.ts +15 -61
- package/utils/__tests__/object.test.ts +0 -24
- package/utils/__tests__/selector.test.ts +1 -1
- package/utils/dynamic-importer.js +4 -0
- package/utils/grafana.js +2 -6
- package/utils/socket.js +41 -20
- package/utils/string.js +1 -7
- package/utils/validators/formRules/__tests__/index.test.ts +108 -0
- package/utils/validators/formRules/index.ts +9 -1
- package/yarn-error.log +195 -0
- package/pages/plugins.vue +0 -387
- package/server/verdaccio-middleware.js +0 -56
package/store/prefs.js
CHANGED
|
@@ -15,6 +15,7 @@ export const create = function(name, def, opt = {}) {
|
|
|
15
15
|
const asCookie = opt.asCookie === true;
|
|
16
16
|
const asUserPreference = opt.asUserPreference !== false;
|
|
17
17
|
const options = opt.options;
|
|
18
|
+
const inheritFrom = opt.inheritFrom;
|
|
18
19
|
|
|
19
20
|
definitions[name] = {
|
|
20
21
|
def,
|
|
@@ -22,6 +23,7 @@ export const create = function(name, def, opt = {}) {
|
|
|
22
23
|
parseJSON,
|
|
23
24
|
asCookie,
|
|
24
25
|
asUserPreference,
|
|
26
|
+
inheritFrom, // if value is not defined on server, we can default it to another pref
|
|
25
27
|
mangleRead: opt.mangleRead, // Alter the value read from the API (to match old Rancher expectations)
|
|
26
28
|
mangleWrite: opt.mangleWrite, // Alter the value written back to the API (ditto)
|
|
27
29
|
};
|
|
@@ -72,6 +74,7 @@ export const HIDE_REPOS = create('hide-repos', [], { parseJSON });
|
|
|
72
74
|
export const HIDE_DESC = create('hide-desc', [], { parseJSON });
|
|
73
75
|
export const HIDE_SENSITIVE = create('hide-sensitive', true, { options: [true, false], parseJSON });
|
|
74
76
|
export const SHOW_PRE_RELEASE = create('show-pre-release', false, { options: [false, true], parseJSON });
|
|
77
|
+
export const SHOW_CHART_MODE = create('chartMode', 'featured', { parseJSON });
|
|
75
78
|
|
|
76
79
|
export const DATE_FORMAT = create('date-format', 'ddd, MMM D YYYY', {
|
|
77
80
|
options: [
|
|
@@ -91,12 +94,18 @@ export const TIME_FORMAT = create('time-format', 'h:mm:ss a', {
|
|
|
91
94
|
});
|
|
92
95
|
|
|
93
96
|
export const TIME_ZONE = create('time-zone', 'local');
|
|
97
|
+
// DEV will be deprecated on v2.7.0, but is needed so that we can grab the value for the new settings that derived from it
|
|
98
|
+
// such as: VIEW_IN_API, ALL_NAMESPACES, THEME_SHORTCUT
|
|
94
99
|
export const DEV = create('dev', false, { parseJSON });
|
|
100
|
+
export const VIEW_IN_API = create('view-in-api', false, { parseJSON, inheritFrom: DEV });
|
|
101
|
+
export const ALL_NAMESPACES = create('all-namespaces', false, { parseJSON, inheritFrom: DEV });
|
|
102
|
+
export const THEME_SHORTCUT = create('theme-shortcut', false, { parseJSON, inheritFrom: DEV });
|
|
95
103
|
export const LAST_VISITED = create('last-visited', 'home', { parseJSON });
|
|
96
104
|
export const SEEN_WHATS_NEW = create('seen-whatsnew', '', { parseJSON });
|
|
97
105
|
export const READ_WHATS_NEW = create('read-whatsnew', '', { parseJSON });
|
|
98
106
|
export const AFTER_LOGIN_ROUTE = create('after-login-route', 'home', { parseJSON } );
|
|
99
107
|
export const HIDE_HOME_PAGE_CARDS = create('home-page-cards', {}, { parseJSON } );
|
|
108
|
+
export const PLUGIN_DEVELOPER = create('plugin-developer', false, { parseJSON, inheritFrom: DEV }); // Is the user a plugin developer?
|
|
100
109
|
|
|
101
110
|
export const _RKE1 = 'rke1';
|
|
102
111
|
export const _RKE2 = 'rke2';
|
|
@@ -105,6 +114,9 @@ export const PROVISIONER = create('provisioner', _RKE1, { options: [_RKE1, _RKE2
|
|
|
105
114
|
// Promo for Cluster Tools feature on Cluster Dashboard page
|
|
106
115
|
export const CLUSTER_TOOLS_TIP = create('hide-cluster-tools-tip', false, { parseJSON });
|
|
107
116
|
|
|
117
|
+
// Promo for Pod Security Policies (PSPs) being deprecated on kube version 1.25 on Cluster Dashboard page
|
|
118
|
+
export const PSP_DEPRECATION_BANNER = create('hide-psp-deprecation-banner', false, { parseJSON });
|
|
119
|
+
|
|
108
120
|
// Maximum number of clusters to show in the slide-in menu
|
|
109
121
|
export const MENU_MAX_CLUSTERS = create('menu-max-clusters', 4, { options: [2, 3, 4, 5, 6, 7, 8, 9, 10], parseJSON });
|
|
110
122
|
|
|
@@ -431,6 +443,10 @@ export const actions = {
|
|
|
431
443
|
const definition = definitions[key];
|
|
432
444
|
let value = clone(server.data[key]);
|
|
433
445
|
|
|
446
|
+
if (value === undefined && definition.inheritFrom) {
|
|
447
|
+
value = clone(server.data[definition.inheritFrom]);
|
|
448
|
+
}
|
|
449
|
+
|
|
434
450
|
if ( value === undefined || key === ignoreKey) {
|
|
435
451
|
continue;
|
|
436
452
|
}
|
package/store/type-map.js
CHANGED
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
// )
|
|
123
123
|
import { AGE, NAME, NAMESPACE as NAMESPACE_COL, STATE } from '@shell/config/table-headers';
|
|
124
124
|
import { COUNT, SCHEMA, MANAGEMENT, NAMESPACE } from '@shell/config/types';
|
|
125
|
-
import {
|
|
125
|
+
import { VIEW_IN_API, EXPANDED_GROUPS, FAVORITE_TYPES } from '@shell/store/prefs';
|
|
126
126
|
import {
|
|
127
127
|
addObject, findBy, insertAt, isArray, removeObject, filterBy
|
|
128
128
|
} from '@shell/utils/array';
|
|
@@ -131,7 +131,7 @@ import {
|
|
|
131
131
|
ensureRegex, escapeHtml, escapeRegex, ucFirst, pluralize
|
|
132
132
|
} from '@shell/utils/string';
|
|
133
133
|
import {
|
|
134
|
-
importList, importDetail, importEdit, listProducts, loadProduct, importCustomPromptRemove, resolveList, resolveEdit, resolveWindowComponent, importWindowComponent, resolveDetail, importDialog
|
|
134
|
+
importChart, importList, importDetail, importEdit, listProducts, loadProduct, importCustomPromptRemove, resolveList, resolveEdit, resolveWindowComponent, importWindowComponent, resolveChart, resolveDetail, importDialog
|
|
135
135
|
} from '@shell/utils/dynamic-importer';
|
|
136
136
|
|
|
137
137
|
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
@@ -168,6 +168,7 @@ export const IF_HAVE = {
|
|
|
168
168
|
NOT_V1_ISTIO: 'not-v1-istio',
|
|
169
169
|
MULTI_CLUSTER: 'multi-cluster',
|
|
170
170
|
NEUVECTOR_NAMESPACE: 'neuvector-namespace',
|
|
171
|
+
ADMIN: 'admin-user',
|
|
171
172
|
};
|
|
172
173
|
|
|
173
174
|
export function DSL(store, product, module = 'type-map') {
|
|
@@ -360,6 +361,7 @@ export const state = function() {
|
|
|
360
361
|
groupLabel: {},
|
|
361
362
|
ignore: {},
|
|
362
363
|
list: {},
|
|
364
|
+
chart: {},
|
|
363
365
|
detail: {},
|
|
364
366
|
edit: {},
|
|
365
367
|
componentFor: {},
|
|
@@ -802,7 +804,7 @@ export const getters = {
|
|
|
802
804
|
const module = findBy(state.products, 'name', product).inStore;
|
|
803
805
|
const schemas = rootGetters[`${ module }/all`](SCHEMA);
|
|
804
806
|
const counts = rootGetters[`${ module }/all`](COUNT)?.[0]?.counts || {};
|
|
805
|
-
const isDev = rootGetters['prefs/get'](
|
|
807
|
+
const isDev = rootGetters['prefs/get'](VIEW_IN_API);
|
|
806
808
|
const isBasic = mode === BASIC;
|
|
807
809
|
|
|
808
810
|
const out = {};
|
|
@@ -1055,6 +1057,14 @@ export const getters = {
|
|
|
1055
1057
|
};
|
|
1056
1058
|
},
|
|
1057
1059
|
|
|
1060
|
+
hasCustomChart(state, getters, rootState) {
|
|
1061
|
+
return (rawType) => {
|
|
1062
|
+
const key = getters.componentFor(rawType);
|
|
1063
|
+
|
|
1064
|
+
return hasCustom(state, rootState, 'chart', key, key => resolveChart(key));
|
|
1065
|
+
};
|
|
1066
|
+
},
|
|
1067
|
+
|
|
1058
1068
|
hasCustomDetail(state, getters, rootState) {
|
|
1059
1069
|
return (rawType, subType) => {
|
|
1060
1070
|
const key = getters.componentFor(rawType, subType);
|
|
@@ -1123,6 +1133,12 @@ export const getters = {
|
|
|
1123
1133
|
};
|
|
1124
1134
|
},
|
|
1125
1135
|
|
|
1136
|
+
importChart(state, getters, rootState) {
|
|
1137
|
+
return (rawType) => {
|
|
1138
|
+
return loadExtension(rootState, 'chart', getters.componentFor(rawType), importChart);
|
|
1139
|
+
};
|
|
1140
|
+
},
|
|
1141
|
+
|
|
1126
1142
|
importDetail(state, getters, rootState) {
|
|
1127
1143
|
return (rawType, subType) => {
|
|
1128
1144
|
return loadExtension(rootState, 'detail', getters.componentFor(rawType, subType), importDetail);
|
|
@@ -1215,7 +1231,7 @@ export const getters = {
|
|
|
1215
1231
|
activeProducts(state, getters, rootState, rootGetters) {
|
|
1216
1232
|
const knownTypes = {};
|
|
1217
1233
|
const knownGroups = {};
|
|
1218
|
-
const isDev = rootGetters['prefs/get'](
|
|
1234
|
+
const isDev = rootGetters['prefs/get'](VIEW_IN_API);
|
|
1219
1235
|
|
|
1220
1236
|
if ( state.schemaGeneration < 0 ) {
|
|
1221
1237
|
// This does nothing, but makes activeProducts depend on schemaGeneration
|
|
@@ -1544,7 +1560,7 @@ export const mutations = {
|
|
|
1544
1560
|
let obj = { ...options, match };
|
|
1545
1561
|
|
|
1546
1562
|
if ( idx >= 0 ) {
|
|
1547
|
-
obj = Object.assign(
|
|
1563
|
+
obj = Object.assign(state.typeOptions[idx], obj);
|
|
1548
1564
|
state.typeOptions.splice(idx, 1, obj);
|
|
1549
1565
|
} else {
|
|
1550
1566
|
const obj = Object.assign({}, options, { match });
|
|
@@ -1732,11 +1748,22 @@ function ifHave(getters, option) {
|
|
|
1732
1748
|
case IF_HAVE.NEUVECTOR_NAMESPACE: {
|
|
1733
1749
|
return getters[`cluster/all`](NAMESPACE).find(n => n.metadata.name === NEU_VECTOR_NAMESPACE);
|
|
1734
1750
|
}
|
|
1751
|
+
case IF_HAVE.ADMIN: {
|
|
1752
|
+
return isAdminUser(getters);
|
|
1753
|
+
}
|
|
1735
1754
|
default:
|
|
1736
1755
|
return false;
|
|
1737
1756
|
}
|
|
1738
1757
|
}
|
|
1739
1758
|
|
|
1759
|
+
// Could list a larger set of resources that typically only an admin user would have
|
|
1760
|
+
export function isAdminUser(getters) {
|
|
1761
|
+
const canEditSettings = (getters['management/schemaFor'](MANAGEMENT.SETTING)?.resourceMethods || []).includes('PUT');
|
|
1762
|
+
const canEditFeatureFlags = (getters['management/schemaFor'](MANAGEMENT.FEATURE)?.resourceMethods || []).includes('PUT');
|
|
1763
|
+
|
|
1764
|
+
return canEditSettings && canEditFeatureFlags;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1740
1767
|
// Is V1 Istio installed?
|
|
1741
1768
|
function isV1Istio(getters) {
|
|
1742
1769
|
const cluster = getters['currentCluster'];
|
package/store/uiplugins.ts
CHANGED
|
@@ -3,20 +3,22 @@
|
|
|
3
3
|
|
|
4
4
|
// import { addObject, removeObject } from '@shell/utils/array';
|
|
5
5
|
|
|
6
|
-
import { allHash } from '@shell/utils/promise';
|
|
7
6
|
import { Plugin } from '@shell/core/plugin';
|
|
8
7
|
|
|
9
8
|
interface UIPluginState {
|
|
10
9
|
plugins: Plugin[],
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
errors: any,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface LoadError {
|
|
14
|
+
name: string,
|
|
15
|
+
error: boolean,
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export const state = function(): UIPluginState {
|
|
16
19
|
return {
|
|
17
20
|
plugins: [],
|
|
18
|
-
|
|
19
|
-
catalogs: [''],
|
|
21
|
+
errors: {},
|
|
20
22
|
};
|
|
21
23
|
};
|
|
22
24
|
|
|
@@ -25,16 +27,16 @@ export const getters = {
|
|
|
25
27
|
return state.plugins;
|
|
26
28
|
},
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
return state.
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
catalogs: (state: any) => {
|
|
33
|
-
return state.catalogs;
|
|
30
|
+
errors: (state: any) => {
|
|
31
|
+
return state.errors;
|
|
34
32
|
},
|
|
35
33
|
};
|
|
36
34
|
|
|
37
35
|
export const mutations = {
|
|
36
|
+
setError(state: UIPluginState, error: LoadError) {
|
|
37
|
+
state.errors[error.name] = error.error;
|
|
38
|
+
},
|
|
39
|
+
|
|
38
40
|
addPlugin(state: UIPluginState, plugin: Plugin) {
|
|
39
41
|
// TODO: Duplicates?
|
|
40
42
|
state.plugins.push(plugin);
|
|
@@ -47,59 +49,11 @@ export const mutations = {
|
|
|
47
49
|
state.plugins.splice(index, 1);
|
|
48
50
|
}
|
|
49
51
|
},
|
|
50
|
-
|
|
51
|
-
setCatalog(state: UIPluginState, catalog: any) {
|
|
52
|
-
state.catalog = catalog;
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
addCatalog(state: UIPluginState, catalog: string) {
|
|
56
|
-
state.catalogs.push(catalog);
|
|
57
|
-
}
|
|
58
52
|
};
|
|
59
53
|
|
|
60
54
|
export const actions = {
|
|
61
|
-
|
|
62
|
-
commit('
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
// This is just for PoC - we wouldn't get the catalog from Verdaccio
|
|
66
|
-
// This fetches the catalog each time
|
|
67
|
-
async loadCatalogs( { getters, commit, dispatch }: any) {
|
|
68
|
-
const packages: any[] = [];
|
|
69
|
-
const catalogHash = {} as any;
|
|
70
|
-
const catalogs = getters['catalogs'];
|
|
71
|
-
|
|
72
|
-
catalogs.forEach((url: string) => {
|
|
73
|
-
const base = url;
|
|
74
|
-
|
|
75
|
-
if (!url) {
|
|
76
|
-
url = '/verdaccio/data/packages';
|
|
77
|
-
} else {
|
|
78
|
-
url = `/uiplugins-catalog/?${ url }`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
catalogHash[base] = dispatch('rancher/request', { url }, { root: true });
|
|
83
|
-
} catch (err) {
|
|
84
|
-
// Ignore errors... or all plugins fail
|
|
85
|
-
console.warn('Unable to fetch catalog: ', url, err); // eslint-disable-line no-console
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const res = await allHash(catalogHash);
|
|
90
|
-
|
|
91
|
-
Object.keys(res as any).forEach((r: string) => {
|
|
92
|
-
const v: any = (res as any)[r];
|
|
93
|
-
|
|
94
|
-
v.forEach((p: any) => {
|
|
95
|
-
p.location = r;
|
|
96
|
-
packages.push(p);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const uiPackages = packages.filter((pkg: any) => pkg.rancher);
|
|
101
|
-
|
|
102
|
-
commit('setCatalog', uiPackages);
|
|
55
|
+
setError( { commit }: any, error: LoadError ) {
|
|
56
|
+
commit('setError', error);
|
|
103
57
|
},
|
|
104
58
|
|
|
105
59
|
addPlugin({ commit }: any, plugin: Plugin) {
|
|
@@ -38,18 +38,6 @@ describe('fx: get', () => {
|
|
|
38
38
|
expect(result).toBeUndefined();
|
|
39
39
|
expect(() => result).not.toThrow();
|
|
40
40
|
});
|
|
41
|
-
|
|
42
|
-
it.each([
|
|
43
|
-
'key2.nonsense',
|
|
44
|
-
'non.sense',
|
|
45
|
-
])('should catch error and return undefined', (path) => {
|
|
46
|
-
const obj = { key1: 'value', key2: { bat: 42, 'with.dots': 43 } };
|
|
47
|
-
|
|
48
|
-
const result = get(obj, path);
|
|
49
|
-
|
|
50
|
-
expect(result).toBeUndefined();
|
|
51
|
-
expect(() => result).not.toThrow();
|
|
52
|
-
});
|
|
53
41
|
});
|
|
54
42
|
|
|
55
43
|
describe('fx: getter', () => {
|
|
@@ -95,18 +83,6 @@ describe('fx: getter', () => {
|
|
|
95
83
|
expect(result).toBeUndefined();
|
|
96
84
|
expect(() => result).not.toThrow();
|
|
97
85
|
});
|
|
98
|
-
|
|
99
|
-
it.each([
|
|
100
|
-
'key2.nonsense',
|
|
101
|
-
'non.sense',
|
|
102
|
-
])('should catch error and return undefined', (path) => {
|
|
103
|
-
const obj = { key1: 'value', key2: { bat: 42, 'with.dots': 43 } };
|
|
104
|
-
|
|
105
|
-
const result = getter(path)(obj);
|
|
106
|
-
|
|
107
|
-
expect(result).toBeUndefined();
|
|
108
|
-
expect(() => result).not.toThrow();
|
|
109
|
-
});
|
|
110
86
|
});
|
|
111
87
|
});
|
|
112
88
|
|
|
@@ -171,7 +171,7 @@ describe('fx: parse', () => {
|
|
|
171
171
|
['!some.prefix/key-bar_baz '],
|
|
172
172
|
['! some.prefix/key-bar_baz '],
|
|
173
173
|
[' ! some.prefix/key-bar_baz '],
|
|
174
|
-
])('should parse expression %p to selector %p', (expression) => {
|
|
174
|
+
])('should parse expression %p to selector %p using prefixes', (expression) => {
|
|
175
175
|
const expected = {
|
|
176
176
|
key: 'some.prefix/key-bar_baz',
|
|
177
177
|
operator: 'DoesNotExist',
|
|
@@ -107,6 +107,10 @@ export function resolveList(key) {
|
|
|
107
107
|
return require.resolve(`@shell/list/${ key }`);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
export function resolveChart(key) {
|
|
111
|
+
return require.resolve(`@shell/chart/${ key }`);
|
|
112
|
+
}
|
|
113
|
+
|
|
110
114
|
export function resolveEdit(key) {
|
|
111
115
|
return require.resolve(`@shell/edit/${ key }`);
|
|
112
116
|
}
|
package/utils/grafana.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { haveV2Monitoring } from '@shell/utils/monitoring';
|
|
1
2
|
import { parse as parseUrl, addParam } from '@shell/utils/url';
|
|
2
|
-
import { MONITORING } from '@shell/config/types';
|
|
3
3
|
|
|
4
4
|
export function computeDashboardUrl(embedUrl, clusterId, params) {
|
|
5
5
|
const url = parseUrl(embedUrl);
|
|
@@ -21,7 +21,7 @@ export function computeDashboardUrl(embedUrl, clusterId, params) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export async function dashboardExists(store, clusterId, embedUrl, storeName = 'cluster') {
|
|
24
|
-
if (!
|
|
24
|
+
if ( !haveV2Monitoring(store.getters) ) {
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -81,7 +81,3 @@ export async function failedProposals(dispatch, clusterId) {
|
|
|
81
81
|
|
|
82
82
|
return response.data.result[0]?.values?.[0]?.[1] || 0;
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
function isMonitoringInstalled(getters, storeName = 'cluster') {
|
|
86
|
-
return !!getters[`${ storeName }/schemaFor`](MONITORING.SERVICEMONITOR);
|
|
87
|
-
}
|
package/utils/socket.js
CHANGED
|
@@ -31,6 +31,8 @@ export default class Socket extends EventTarget {
|
|
|
31
31
|
hasBeenOpen = false;
|
|
32
32
|
hasReconnected = false;
|
|
33
33
|
protocol = null;
|
|
34
|
+
maxTries = null;
|
|
35
|
+
tries = 0;
|
|
34
36
|
|
|
35
37
|
// "Private"
|
|
36
38
|
socket = null;
|
|
@@ -38,17 +40,19 @@ export default class Socket extends EventTarget {
|
|
|
38
40
|
framesReceived = 0;
|
|
39
41
|
frameTimer;
|
|
40
42
|
reconnectTimer;
|
|
41
|
-
tries = 0;
|
|
42
43
|
disconnectCbs = [];
|
|
43
44
|
disconnectedAt = 0;
|
|
44
45
|
closingId = 0;
|
|
45
46
|
|
|
46
|
-
constructor(url, autoReconnect = true, frameTimeout = null, protocol = null) {
|
|
47
|
+
constructor(url, autoReconnect = true, frameTimeout = null, protocol = null, maxTries = null) {
|
|
47
48
|
super();
|
|
48
49
|
|
|
49
50
|
this.setUrl(url);
|
|
50
51
|
this.autoReconnect = autoReconnect;
|
|
51
52
|
this.protocol = protocol;
|
|
53
|
+
// maxTries = null === never stop trying to reconnect
|
|
54
|
+
// allow maxTries to be defined on individual sockets bc not all will clearly warn the user that we've stopped trying
|
|
55
|
+
this.maxTries = maxTries;
|
|
52
56
|
|
|
53
57
|
if ( frameTimeout !== null ) {
|
|
54
58
|
this.frameTimeout = frameTimeout;
|
|
@@ -57,10 +61,10 @@ export default class Socket extends EventTarget {
|
|
|
57
61
|
|
|
58
62
|
setUrl(url) {
|
|
59
63
|
if ( !url.match(/wss?:\/\//) ) {
|
|
60
|
-
url =
|
|
64
|
+
url = self.location.origin.replace(/^http/, 'ws') + url;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
if (
|
|
67
|
+
if ( self.location.protocol === 'https:' && url.startsWith(INSECURE) ) {
|
|
64
68
|
url = SECURE + url.substr(INSECURE.length);
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -74,6 +78,10 @@ export default class Socket extends EventTarget {
|
|
|
74
78
|
return;
|
|
75
79
|
}
|
|
76
80
|
|
|
81
|
+
if (this.state !== STATE_RECONNECTING) {
|
|
82
|
+
this.state = STATE_CONNECTING;
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
Object.assign(this.metadata, metadata);
|
|
78
86
|
|
|
79
87
|
const id = sockId++;
|
|
@@ -83,6 +91,8 @@ export default class Socket extends EventTarget {
|
|
|
83
91
|
|
|
84
92
|
let socket;
|
|
85
93
|
|
|
94
|
+
this.tries++;
|
|
95
|
+
|
|
86
96
|
if ( this.protocol ) {
|
|
87
97
|
socket = new WebSocket(url, this.protocol);
|
|
88
98
|
} else {
|
|
@@ -209,11 +219,11 @@ export default class Socket extends EventTarget {
|
|
|
209
219
|
this._log('opened');
|
|
210
220
|
const now = (new Date()).getTime();
|
|
211
221
|
|
|
212
|
-
const
|
|
213
|
-
let
|
|
222
|
+
const atTime = this.disconnectedAt;
|
|
223
|
+
let afterMilliseconds = 0;
|
|
214
224
|
|
|
215
|
-
if (
|
|
216
|
-
|
|
225
|
+
if ( atTime ) {
|
|
226
|
+
afterMilliseconds = now - atTime;
|
|
217
227
|
}
|
|
218
228
|
|
|
219
229
|
if ( this.hasBeenOpen ) {
|
|
@@ -225,7 +235,8 @@ export default class Socket extends EventTarget {
|
|
|
225
235
|
this.framesReceived = 0;
|
|
226
236
|
this.disconnectedAt = 0;
|
|
227
237
|
|
|
228
|
-
this.dispatchEvent(new CustomEvent(EVENT_CONNECTED, { detail: { tries: this.tries,
|
|
238
|
+
this.dispatchEvent(new CustomEvent(EVENT_CONNECTED, { detail: { tries: this.tries, afterMilliseconds } }));
|
|
239
|
+
this.tries = 0;
|
|
229
240
|
this._resetWatchdog();
|
|
230
241
|
clearTimeout(this.reconnectTimer);
|
|
231
242
|
}
|
|
@@ -291,18 +302,26 @@ export default class Socket extends EventTarget {
|
|
|
291
302
|
this.dispatchEvent(e);
|
|
292
303
|
warningShown = true;
|
|
293
304
|
} else if ( this.autoReconnect ) {
|
|
294
|
-
|
|
295
|
-
|
|
305
|
+
this.state = STATE_RECONNECTING;
|
|
306
|
+
|
|
307
|
+
if (this.maxTries && this.tries > 1 && this.tries <= this.maxTries) {
|
|
308
|
+
// dispatch an event which will trigger a growl from steve-plugin sockets warning users that we've lost connection and are attemping to reconnect
|
|
309
|
+
const e = new CustomEvent(EVENT_CONNECT_ERROR);
|
|
296
310
|
|
|
297
311
|
this.dispatchEvent(e);
|
|
298
312
|
}
|
|
299
|
-
this.state = STATE_RECONNECTING;
|
|
300
|
-
this.tries++;
|
|
301
|
-
const delay = Math.max(1000, Math.min(1000 * this.tries, 30000));
|
|
302
313
|
|
|
303
|
-
this.
|
|
304
|
-
this.
|
|
305
|
-
|
|
314
|
+
if (this.maxTries && this.tries > this.maxTries) {
|
|
315
|
+
this.state = STATE_DISCONNECTED;
|
|
316
|
+
// dispatch an event which will trigger a growl from steve-plugin sockets warning users that we've given up trying to reconnect
|
|
317
|
+
this.dispatchEvent(new CustomEvent(EVENT_DISCONNECT_ERROR));
|
|
318
|
+
} else {
|
|
319
|
+
const delay = Math.max(1000, Math.min(1000 * this.tries, 30000));
|
|
320
|
+
|
|
321
|
+
this.reconnectTimer = setTimeout(() => {
|
|
322
|
+
this.connect();
|
|
323
|
+
}, delay);
|
|
324
|
+
}
|
|
306
325
|
} else {
|
|
307
326
|
this.state = STATE_DISCONNECTED;
|
|
308
327
|
}
|
|
@@ -315,10 +334,12 @@ export default class Socket extends EventTarget {
|
|
|
315
334
|
}
|
|
316
335
|
|
|
317
336
|
_log(...args) {
|
|
318
|
-
|
|
337
|
+
const message = JSON.parse(JSON.stringify([...args]));
|
|
338
|
+
|
|
339
|
+
message.unshift('Socket');
|
|
319
340
|
|
|
320
|
-
|
|
341
|
+
message.push(`(state=${ this.state }, id=${ this.socket ? this.socket.sockId : 0 })`);
|
|
321
342
|
|
|
322
|
-
console.log(
|
|
343
|
+
console.log(message.join(' ')); // eslint-disable-line no-console
|
|
323
344
|
}
|
|
324
345
|
}
|
package/utils/string.js
CHANGED
|
@@ -70,13 +70,7 @@ export function random32(count) {
|
|
|
70
70
|
const out = [];
|
|
71
71
|
let i;
|
|
72
72
|
|
|
73
|
-
if (
|
|
74
|
-
const crypto = require('crypto');
|
|
75
|
-
|
|
76
|
-
for ( i = 0 ; i < count ; i++ ) {
|
|
77
|
-
out[i] = crypto.randomBytes(4).readUInt32BE(0, true);
|
|
78
|
-
}
|
|
79
|
-
} else if (window.crypto && window.crypto.getRandomValues) {
|
|
73
|
+
if (window.crypto && window.crypto.getRandomValues) {
|
|
80
74
|
const tmp = new Uint32Array(count);
|
|
81
75
|
|
|
82
76
|
window.crypto.getRandomValues(tmp);
|
|
@@ -906,6 +906,114 @@ describe('formRules', () => {
|
|
|
906
906
|
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
907
907
|
});
|
|
908
908
|
|
|
909
|
+
// this rule is pretty much identical to the standard hostname, but also allows for wildcards
|
|
910
|
+
it('"wildcardHostname" : returns undefined when value is valid hostname', () => {
|
|
911
|
+
const testValue = 'www.url.com';
|
|
912
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
913
|
+
|
|
914
|
+
expect(formRuleResult).toBeUndefined();
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
it('"wildcardHostname" : returns expected message when value starts with a dot', () => {
|
|
918
|
+
const testValue = '.hostname';
|
|
919
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
920
|
+
const expectedResult = JSON.stringify({ message: 'validation.dns.hostname.startDot', key: 'testDisplayKey' });
|
|
921
|
+
|
|
922
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
it('"wildcardHostname" : returns expected message when value starts is too long for a hostname', () => {
|
|
926
|
+
const testValue = 'There.are.many.variations.of.passages.of.Lorem.Ipsum.available.but.the.majority.have.suffered.alteration.in.some.form.by.injected.humour.or.randomised.words.which.dont.look.even.slightly.believable.If.you.are.going.to.use.a.passage.of.Lorem.Ipsum.you.need';
|
|
927
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
928
|
+
const expectedResult = JSON.stringify({
|
|
929
|
+
message: 'validation.dns.hostname.tooLong', key: 'testDisplayKey', max: 253
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
it('"wildcardHostname" : returns expected message when value contains invalid characters', () => {
|
|
936
|
+
const testValue = 'www.host*name.com';
|
|
937
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
938
|
+
const expectedResult = JSON.stringify({
|
|
939
|
+
message: 'validation.chars', key: 'testDisplayKey', count: 1, chars: '"*"'
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
it('"wildcardHostname" : returns expected message when value contains a space character', () => {
|
|
946
|
+
const testValue = 'www.host name.com';
|
|
947
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
948
|
+
const expectedResult = JSON.stringify({
|
|
949
|
+
message: 'validation.chars', key: 'testDisplayKey', count: 1, chars: 'Space'
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
it('"wildcardHostname" : returns expected message when hostname label starts with a dash', () => {
|
|
956
|
+
const testValue = 'www.-hostname.com';
|
|
957
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
958
|
+
const expectedResult = JSON.stringify({ message: 'validation.dns.hostname.startHyphen', key: 'testDisplayKey' });
|
|
959
|
+
|
|
960
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it('"wildcardHostname" : returns expected message when hostname label ends with a dash', () => {
|
|
964
|
+
const testValue = 'www.hostname-.com';
|
|
965
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
966
|
+
const expectedResult = JSON.stringify({ message: 'validation.dns.hostname.endHyphen', key: 'testDisplayKey' });
|
|
967
|
+
|
|
968
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it('"wildcardHostname" : returns expected message when hostname label contains a double-dash at the third character position', () => {
|
|
972
|
+
const testValue = 'www.ho--stname.com';
|
|
973
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
974
|
+
const expectedResult = JSON.stringify({ message: 'validation.dns.doubleHyphen', key: 'testDisplayKey' });
|
|
975
|
+
|
|
976
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
it('"wildcardHostname" : returns expected message when hostname label is too long', () => {
|
|
980
|
+
const testValue = 'www.0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef.com';
|
|
981
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
982
|
+
const expectedResult = JSON.stringify({
|
|
983
|
+
message: 'validation.dns.hostname.tooLongLabel', key: 'testDisplayKey', max: 63
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
it('"wildcardHostname" : returns expected message when wildcard character is not the first part', () => {
|
|
990
|
+
const testValue = 'www.*.hostname.com';
|
|
991
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
992
|
+
const expectedResult = JSON.stringify({
|
|
993
|
+
message: 'validation.chars', key: 'testDisplayKey', count: 1, chars: '"*"'
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
it('"wildcardHostname" : returns expected message when wildcard character is at the beginning but not its own part', () => {
|
|
1000
|
+
const testValue = '*hostname.com';
|
|
1001
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
1002
|
+
|
|
1003
|
+
const expectedResult = JSON.stringify({
|
|
1004
|
+
message: 'validation.chars', key: 'testDisplayKey', count: 1, chars: '"*"'
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it('"wildcardHostname" : returns valid when wildcard character is the first part', () => {
|
|
1011
|
+
const testValue = '*.hostname.com';
|
|
1012
|
+
const formRuleResult = formRules.wildcardHostname(testValue);
|
|
1013
|
+
|
|
1014
|
+
expect(formRuleResult).toBeUndefined();
|
|
1015
|
+
});
|
|
1016
|
+
|
|
909
1017
|
it('"absolutePath" : return expected message when path doesn\'t begin with a "/"', () => {
|
|
910
1018
|
const formRuleResult = formRules.absolutePath('absolute_path');
|
|
911
1019
|
const expectedResult = JSON.stringify({ message: 'validation.path', key: 'testDisplayKey' });
|
|
@@ -204,6 +204,13 @@ export default function(t: (key: string, options?: any) => string, opt: {display
|
|
|
204
204
|
}
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
+
const wildcardHostname: Validator = (val: string) => {
|
|
208
|
+
// allow wildcard in first part of hostname
|
|
209
|
+
val = val ? val.replace(/^\*\./, '') : val;
|
|
210
|
+
|
|
211
|
+
return hostname(val);
|
|
212
|
+
};
|
|
213
|
+
|
|
207
214
|
const externalName: Validator = (val: string) => {
|
|
208
215
|
if (isEmpty(val)) {
|
|
209
216
|
return t('validation.service.externalName.none');
|
|
@@ -442,6 +449,7 @@ export default function(t: (key: string, options?: any) => string, opt: {display
|
|
|
442
449
|
hostname,
|
|
443
450
|
testRule,
|
|
444
451
|
subDomain,
|
|
445
|
-
absolutePath
|
|
452
|
+
absolutePath,
|
|
453
|
+
wildcardHostname,
|
|
446
454
|
};
|
|
447
455
|
}
|