@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.
Files changed (131) hide show
  1. package/assets/brand/suse/dark/rancher-logo.svg +1 -148
  2. package/assets/brand/suse/rancher-logo.svg +1 -130
  3. package/assets/images/featured/img1.jpg +0 -0
  4. package/assets/images/featured.jpg +0 -0
  5. package/assets/images/generic-plugin.svg +7 -0
  6. package/assets/styles/themes/_dark.scss +3 -0
  7. package/assets/styles/themes/_light.scss +3 -0
  8. package/assets/styles/themes/_suse.scss +1 -1
  9. package/assets/translations/en-us.yaml +183 -45
  10. package/assets/translations/zh-hans.yaml +21 -24
  11. package/components/AsyncButton.vue +17 -2
  12. package/components/ButtonDropdown.vue +4 -0
  13. package/components/Carousel.vue +291 -0
  14. package/components/CommunityLinks.vue +69 -18
  15. package/components/CruResource.vue +11 -3
  16. package/components/Dialog.vue +102 -0
  17. package/components/ExplorerMembers.vue +2 -4
  18. package/components/ExplorerProjectsNamespaces.vue +6 -7
  19. package/components/IconMessage.vue +9 -1
  20. package/components/LocaleSelector.vue +62 -29
  21. package/components/ResourceTable.vue +7 -2
  22. package/components/SimpleBox.vue +6 -4
  23. package/components/SortableTable/index.vue +11 -21
  24. package/components/Tabbed/Tab.vue +5 -0
  25. package/components/Tabbed/index.vue +29 -2
  26. package/components/auth/Principal.vue +1 -0
  27. package/components/fleet/FleetBundles.vue +8 -3
  28. package/components/fleet/FleetSummary.vue +6 -0
  29. package/components/form/KeyValue.vue +80 -58
  30. package/components/form/NameNsDescription.vue +10 -4
  31. package/components/form/ResourceTabs/index.vue +5 -1
  32. package/components/formatter/ClusterLink.vue +3 -7
  33. package/components/nav/NamespaceFilter.vue +3 -3
  34. package/components/nav/TopLevelMenu.vue +10 -28
  35. package/config/footer.js +13 -14
  36. package/config/labels-annotations.js +2 -1
  37. package/config/product/explorer.js +5 -4
  38. package/config/product/legacy.js +0 -47
  39. package/config/product/multi-cluster-apps.js +0 -12
  40. package/config/product/settings.js +12 -1
  41. package/config/product/uiplugins.js +17 -0
  42. package/config/settings.js +21 -2
  43. package/config/types.js +5 -1
  44. package/config/uiplugins.js +60 -0
  45. package/content/docs/en-us/getting-started.md +1 -26
  46. package/core/plugins.js +12 -0
  47. package/detail/provisioning.cattle.io.cluster.vue +3 -3
  48. package/detail/workload/index.vue +2 -2
  49. package/dialog/DiagnosticTimingsDialog.vue +116 -0
  50. package/dialog/RotateCertificatesDialog.vue +9 -3
  51. package/edit/auth/azuread.vue +28 -9
  52. package/edit/networking.k8s.io.ingress/index.vue +2 -2
  53. package/edit/persistentvolume/index.vue +3 -0
  54. package/edit/pod.vue +27 -0
  55. package/edit/provisioning.cattle.io.cluster/rke2.vue +76 -5
  56. package/edit/service.vue +7 -5
  57. package/edit/workload/__tests__/Upgrading.test.ts +1 -0
  58. package/edit/workload/index.vue +13 -1
  59. package/edit/workload/mixins/workload.js +13 -13
  60. package/edit/workload/storage/ContainerMountPaths.vue +240 -0
  61. package/edit/workload/storage/Mount.vue +1 -0
  62. package/edit/workload/storage/awsElasticBlockStore.vue +20 -1
  63. package/edit/workload/storage/azureDisk.vue +22 -2
  64. package/edit/workload/storage/azureFile.vue +20 -2
  65. package/edit/workload/storage/csi/index.vue +23 -1
  66. package/edit/workload/storage/gcePersistentDisk.vue +20 -2
  67. package/edit/workload/storage/index.vue +23 -49
  68. package/edit/workload/storage/vsphereVolume.vue +11 -1
  69. package/layouts/default.vue +14 -8
  70. package/layouts/home.vue +9 -4
  71. package/layouts/plain.vue +10 -5
  72. package/list/management.cattle.io.setting.vue +3 -3
  73. package/list/provisioning.cattle.io.cluster.vue +1 -1
  74. package/machine-config/harvester.vue +5 -3
  75. package/models/catalog.cattle.io.uiplugin.js +34 -0
  76. package/models/cluster/node.js +25 -2
  77. package/models/fleet.cattle.io.bundle.js +1 -1
  78. package/models/harvesterhci.io.management.cluster.js +11 -5
  79. package/models/provisioning.cattle.io.cluster.js +12 -6
  80. package/models/workload.js +5 -3
  81. package/nuxt.config.js +69 -25
  82. package/package.json +108 -109
  83. package/pages/auth/login.vue +1 -1
  84. package/pages/c/_cluster/apps/charts/index.vue +46 -1
  85. package/pages/c/_cluster/apps/charts/install.vue +10 -9
  86. package/pages/c/_cluster/explorer/index.vue +72 -9
  87. package/pages/c/_cluster/explorer/tools/index.vue +12 -5
  88. package/pages/c/_cluster/mcapps/index.vue +1 -1
  89. package/pages/c/_cluster/settings/brand.vue +0 -40
  90. package/pages/c/_cluster/settings/links.vue +200 -0
  91. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
  92. package/pages/c/_cluster/uiplugins/InstallDialog.vue +242 -0
  93. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +284 -0
  94. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +130 -0
  95. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +253 -0
  96. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +115 -0
  97. package/pages/c/_cluster/uiplugins/index.vue +694 -0
  98. package/pages/diagnostic.vue +185 -101
  99. package/pages/docs/_doc.vue +3 -1
  100. package/pages/home.vue +21 -56
  101. package/pages/prefs.vue +108 -88
  102. package/pages/safeMode.vue +17 -0
  103. package/pages/support/index.vue +23 -15
  104. package/pkg/dynamic-importer.lib.js +4 -0
  105. package/plugins/dashboard-store/resource-class.js +2 -2
  106. package/plugins/formatters.js +15 -0
  107. package/plugins/plugin.js +56 -4
  108. package/plugins/steve/mutations.js +1 -1
  109. package/plugins/steve/subscribe.js +94 -72
  110. package/plugins/steve/web-worker.steve-sub-worker.js +24 -15
  111. package/promptRemove/management.cattle.io.globalrole.vue +47 -0
  112. package/promptRemove/management.cattle.io.roletemplate.vue +47 -0
  113. package/promptRemove/mixin/roleDeletionCheck.js +97 -0
  114. package/scripts/publish-shell.sh +1 -1
  115. package/scripts/sync-shell-deps +37 -0
  116. package/store/catalog.js +9 -8
  117. package/store/i18n.js +10 -1
  118. package/store/prefs.js +16 -0
  119. package/store/type-map.js +32 -5
  120. package/store/uiplugins.ts +15 -61
  121. package/utils/__tests__/object.test.ts +0 -24
  122. package/utils/__tests__/selector.test.ts +1 -1
  123. package/utils/dynamic-importer.js +4 -0
  124. package/utils/grafana.js +2 -6
  125. package/utils/socket.js +41 -20
  126. package/utils/string.js +1 -7
  127. package/utils/validators/formRules/__tests__/index.test.ts +108 -0
  128. package/utils/validators/formRules/index.ts +9 -1
  129. package/yarn-error.log +195 -0
  130. package/pages/plugins.vue +0 -387
  131. package/server/verdaccio-middleware.js +0 -56
@@ -1,7 +1,7 @@
1
1
  import { addObject, clear, removeObject } from '@shell/utils/array';
2
2
  import { get } from '@shell/utils/object';
3
- import { COUNT, MANAGEMENT, SCHEMA } from '@shell/config/types';
4
- import { SETTING } from '@shell/config/settings';
3
+ import { COUNT, SCHEMA } from '@shell/config/types';
4
+ import { getPerformanceSetting } from '@shell/config/settings';
5
5
  import Socket, {
6
6
  EVENT_CONNECTED,
7
7
  EVENT_DISCONNECTED,
@@ -16,8 +16,7 @@ import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
16
16
  import { escapeHtml } from '@shell/utils/string';
17
17
 
18
18
  // eslint-disable-next-line
19
- import Worker from './web-worker.steve-sub-worker.js'
20
- import * as Comlink from 'comlink';
19
+ import webworker from './web-worker.steve-sub-worker.js';
21
20
 
22
21
  export const NO_WATCH = 'NO_WATCH';
23
22
  export const NO_SCHEMA = 'NO_SCHEMA';
@@ -25,9 +24,6 @@ export const NO_SCHEMA = 'NO_SCHEMA';
25
24
  // minimum length of time a disconnect notification is shown
26
25
  const MINIMUM_TIME_NOTIFIED = 3000;
27
26
 
28
- // minimum time a socket must be disconnected for before sending a growl
29
- const MINIMUM_TIME_DISCONNECTED = 10000;
30
-
31
27
  // We only create a worker for the cluster store
32
28
  export function createWorker(store, ctx) {
33
29
  const { getters } = ctx;
@@ -39,16 +35,33 @@ export function createWorker(store, ctx) {
39
35
  return;
40
36
  }
41
37
 
42
- function callback(resource) {
43
- queueChange(ctx, resource, true, 'Change');
44
- }
38
+ const workerActions = {
39
+ load: (resource) => {
40
+ queueChange(ctx, resource, true, 'Change');
41
+ },
42
+ destroyWorker: () => {
43
+ if (store.$workers) {
44
+ delete store.$workers[storeName];
45
+ }
46
+ }
47
+ };
45
48
 
46
49
  if (!store.$workers[storeName]) {
47
- const worker = Comlink.wrap(new Worker());
50
+ const worker = new webworker();
48
51
 
49
52
  store.$workers[storeName] = worker;
50
53
 
51
- worker.initWorker(storeName, Comlink.proxy(callback));
54
+ worker.postMessage({ initWorker: { storeName } });
55
+
56
+ store.$workers[storeName].onmessage = (e) => {
57
+ /* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
58
+ // good enough for now considering that we never send more than one message action at a time right now */
59
+ const messageActions = Object.keys(e?.data);
60
+
61
+ messageActions.forEach((action) => {
62
+ workerActions[action](e?.data[action]);
63
+ });
64
+ };
52
65
  }
53
66
  }
54
67
 
@@ -119,6 +132,10 @@ function queueChange({ getters, state }, { data, revision }, load, label) {
119
132
  }
120
133
  }
121
134
 
135
+ function growlsDisabled(rootGetters) {
136
+ return getPerformanceSetting(rootGetters)?.disableWebsocketNotification;
137
+ }
138
+
122
139
  export const actions = {
123
140
  subscribe(ctx, opt) {
124
141
  const {
@@ -145,7 +162,9 @@ export const actions = {
145
162
  socket.setAutoReconnect(true);
146
163
  socket.setUrl(url);
147
164
  } else {
148
- socket = new Socket(`${ state.config.baseUrl }/subscribe`);
165
+ const maxTries = growlsDisabled(rootGetters) ? null : 3;
166
+
167
+ socket = new Socket(`${ state.config.baseUrl }/subscribe`, true, null, null, maxTries);
149
168
 
150
169
  commit('setSocket', socket);
151
170
  socket.addEventListener(EVENT_CONNECTED, (e) => {
@@ -177,24 +196,17 @@ export const actions = {
177
196
  });
178
197
  }
179
198
 
180
- socket.connect(get(opt, 'metadata'));
199
+ socket.connect(get(opt, 'metadata') );
181
200
  },
182
201
 
183
- unsubscribe({ state, commit, getters }) {
202
+ unsubscribe({ commit, getters, state }) {
184
203
  const socket = state.socket;
185
204
  const worker = (this.$workers || {})[getters.storeName];
186
205
 
187
206
  commit('setWantSocket', false);
188
207
 
189
208
  if (worker) {
190
- try {
191
- worker.destroyWorker();
192
- worker[Comlink.releaseProxy]();
193
- } catch (e) {
194
- console.error(e); // eslint-disable-line no-console
195
- }
196
-
197
- delete this.$workers[getters.storeName];
209
+ worker.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
198
210
  }
199
211
 
200
212
  if ( socket ) {
@@ -396,8 +408,10 @@ export const actions = {
396
408
  commit, dispatch, state, getters, rootGetters
397
409
  }, event) {
398
410
  state.debugSocket && console.info(`WebSocket Opened [${ getters.storeName }]`); // eslint-disable-line no-console
399
-
400
411
  const socket = event.currentTarget;
412
+ const tries = event?.detail?.tries; // have to pull it off of the event because the socket's tries is already reset to 0
413
+ const t = rootGetters['i18n/t'];
414
+ const disableGrowl = growlsDisabled(rootGetters);
401
415
 
402
416
  this.$socket = socket;
403
417
 
@@ -420,19 +434,16 @@ export const actions = {
420
434
  if ( socket.hasReconnected ) {
421
435
  await dispatch('reconnectWatches');
422
436
  // Check for disconnect notifications and clear them
423
- const growlErr = rootGetters['growl/find']({ key: 'url', val: this.$socket.url });
437
+ const growlErr = rootGetters['growl/find']({ key: 'url', val: socket.url });
424
438
 
425
439
  if (growlErr) {
426
- const now = Date.now();
427
-
428
- // even if the socket reconnected, keep the error growl for at least a few seconds to ensure its readable
429
- if (now >= growlErr.earliestClose) {
430
- dispatch('growl/remove', growlErr.id, { root: true });
431
- } else {
432
- setTimeout(() => {
433
- dispatch('growl/remove', growlErr.id, { root: true });
434
- }, growlErr.earliestClose - now);
435
- }
440
+ dispatch('growl/remove', growlErr.id, { root: true });
441
+ }
442
+ if (tries > 1 && !disableGrowl) {
443
+ dispatch('growl/success', {
444
+ title: t('growl.reconnected.title'),
445
+ message: t('growl.reconnected.message', { url: this.$socket.url, tries }),
446
+ }, { root: true });
436
447
  }
437
448
  }
438
449
 
@@ -456,42 +467,53 @@ export const actions = {
456
467
  }, e) {
457
468
  clearTimeout(state.queueTimer);
458
469
  state.queueTimer = null;
459
- if (e.type === EVENT_DISCONNECT_ERROR) {
460
- // determine if websocket notifications are disabled
461
- const perfSetting = rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_PERFORMANCE);
462
- let disableGrowl = false;
463
470
 
464
- if ( perfSetting?.value ) {
465
- disableGrowl = JSON.parse(perfSetting.value).disableWebsocketNotification || false;
466
- }
471
+ // determine if websocket notifications are disabled
472
+ const disableGrowl = growlsDisabled(rootGetters);
467
473
 
468
- if ( !disableGrowl ) {
469
- // do not send a growl notification unless the socket stays disconnected for more than MINIMUM_TIME_DISCONNECTED
470
- setTimeout(() => {
471
- if (state.socket.isConnected()) {
472
- return;
473
- }
474
- const dateFormat = escapeHtml( rootGetters['prefs/get'](DATE_FORMAT));
475
- const timeFormat = escapeHtml( rootGetters['prefs/get'](TIME_FORMAT));
476
- const time = e?.srcElement?.disconnectedAt || Date.now();
477
-
478
- const timeFormatted = `${ day(time).format(`${ dateFormat } ${ timeFormat }`) }`;
479
- const url = e?.srcElement?.url;
480
-
481
- const t = rootGetters['i18n/t'];
482
-
483
- dispatch('growl/error', {
484
- title: t('growl.disconnected.title'),
485
- message: t('growl.disconnected.message', { url, time: timeFormatted }, { raw: true }),
486
- icon: 'error',
487
- earliestClose: time + MINIMUM_TIME_NOTIFIED + MINIMUM_TIME_DISCONNECTED,
488
- url
489
- }, { root: true });
490
- }, MINIMUM_TIME_DISCONNECTED);
474
+ if (!disableGrowl) {
475
+ const dateFormat = escapeHtml( rootGetters['prefs/get'](DATE_FORMAT));
476
+ const timeFormat = escapeHtml( rootGetters['prefs/get'](TIME_FORMAT));
477
+ const time = e?.srcElement?.disconnectedAt || Date.now();
478
+
479
+ const timeFormatted = `${ day(time).format(`${ dateFormat } ${ timeFormat }`) }`;
480
+ const url = e?.srcElement?.url;
481
+ const tries = state?.socket?.tries;
482
+
483
+ const t = rootGetters['i18n/t'];
484
+
485
+ const growlErr = rootGetters['growl/find']({ key: 'url', val: url });
486
+
487
+ if (e.type === EVENT_CONNECT_ERROR) { // if this occurs, then we're at least retrying to connect
488
+ if (growlErr) {
489
+ dispatch('growl/remove', growlErr.id, { root: true });
490
+ }
491
+ dispatch('growl/error', {
492
+ title: t('growl.connectError.title'),
493
+ message: t('growl.connectError.message', {
494
+ url, time: timeFormatted, tries
495
+ }, { raw: true }),
496
+ icon: 'error',
497
+ earliestClose: time + MINIMUM_TIME_NOTIFIED,
498
+ url
499
+ }, { root: true });
500
+ } else if (e.type === EVENT_DISCONNECT_ERROR) { // if this occurs, we've given up on trying to reconnect
501
+ if (growlErr) {
502
+ dispatch('growl/remove', growlErr.id, { root: true });
503
+ }
504
+ dispatch('growl/error', {
505
+ title: t('growl.disconnectError.title'),
506
+ message: t('growl.disconnectError.message', {
507
+ url, time: timeFormatted, tries
508
+ }, { raw: true }),
509
+ icon: 'error',
510
+ earliestClose: time + MINIMUM_TIME_NOTIFIED,
511
+ url
512
+ }, { root: true });
513
+ } else {
514
+ // if the error is not a connect error or disconnect error, the socket never worked: log whether the current browser is safari
515
+ console.error(`WebSocket Connection Error [${ getters.storeName }]`, e.detail); // eslint-disable-line no-console
491
516
  }
492
- } else {
493
- // if the error is not a disconnect error, the socket never worked: log whether the current browser is safari
494
- console.error(`WebSocket Connection Error [${ getters.storeName }]`, e.detail); // eslint-disable-line no-console
495
517
  }
496
518
  },
497
519
 
@@ -583,7 +605,7 @@ export const actions = {
583
605
  const worker = (this.$workers || {})[ctx.getters.storeName];
584
606
 
585
607
  if (worker) {
586
- worker.countsUpdate(msg);
608
+ worker.postMessage({ countsUpdate: msg });
587
609
 
588
610
  // No further processing - let the web worker debounce the counts
589
611
  return;
@@ -596,7 +618,7 @@ export const actions = {
596
618
  const worker = (this.$workers || {})[ctx.getters.storeName];
597
619
 
598
620
  if (worker) {
599
- worker.updateSchema(data);
621
+ worker.postMessage({ updateSchema: data });
600
622
 
601
623
  // No further processing - let the web worker check the schema updates
602
624
  return;
@@ -631,7 +653,7 @@ export const actions = {
631
653
  const worker = (this.$workers || {})[ctx.getters.storeName];
632
654
 
633
655
  if (worker) {
634
- worker.removeSchema(data.id);
656
+ worker.postMessage({ removeSchema: data.id });
635
657
  }
636
658
  }
637
659
 
@@ -712,7 +734,7 @@ export const mutations = {
712
734
  clear(state.started);
713
735
  clear(state.pendingFrames);
714
736
  clear(state.queue);
715
- clearInterval(state.queueTimer);
737
+ clearTimeout(state.queueTimer);
716
738
  state.deferredRequests = {};
717
739
  state.queueTimer = null;
718
740
  }
@@ -1,4 +1,3 @@
1
- import * as Comlink from 'comlink';
2
1
  import { SCHEMA } from '@shell/config/types';
3
2
 
4
3
  const COUNTS_FLUSH_TIMEOUT = 5000;
@@ -6,7 +5,6 @@ const SCHEMA_FLUSH_TIMEOUT = 2500;
6
5
 
7
6
  const state = {
8
7
  store: '', // Store name
9
- load: undefined, // Load callback to load a resource into the store
10
8
  counts: [], // Buffer of count resources recieved in a given window
11
9
  countTimer: undefined, // Tiemr to flush the count buffer
12
10
  flushTimer: undefined, // Timer to flush the schema chaneg queue
@@ -61,22 +59,33 @@ state.flushTimer = setTimeout(flush, SCHEMA_FLUSH_TIMEOUT);
61
59
 
62
60
  // Callback to the store's load function (in the main thread) to process a load
63
61
  function load(data) {
64
- if (state.load) {
65
- state.load(data);
66
- }
62
+ self.postMessage({ load: data });
67
63
  }
68
64
 
69
- // Web Worker API
70
- const fns = {
71
- initWorker(storeName, loadFn) {
65
+ const workerActions = {
66
+ onmessage: (e) => {
67
+ /* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
68
+ // good enough for now considering that we never send more than one message action at a time right now */
69
+ const messageActions = Object.keys(e?.data);
70
+
71
+ messageActions.forEach((action) => {
72
+ if (workerActions[action]) {
73
+ workerActions[action](e?.data[action]);
74
+ } else {
75
+ console.warn('no associated action for:', action); // eslint-disable-line no-console
76
+ }
77
+ });
78
+ },
79
+ initWorker: ({ storeName }) => {
72
80
  state.store = storeName;
73
- state.load = loadFn;
74
81
  },
75
82
 
76
- destroyWorker() {
83
+ destroyWorker: () => {
77
84
  clearTimeout(state.countTimer);
78
85
  clearTimeout(state.flushTimer);
79
86
 
87
+ self.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
88
+
80
89
  // Web worker global function to terminate the web worker
81
90
  close();
82
91
  },
@@ -98,7 +107,7 @@ const fns = {
98
107
  },
99
108
 
100
109
  // Called to load schema
101
- loadSchema(schemas) {
110
+ loadSchemas: (schemas) => {
102
111
  schemas.forEach((schema) => {
103
112
  // These properties are added to the object, but aren't on the raw object, so remove them
104
113
  // otherwise our comparison will show changes when there aren't any
@@ -107,16 +116,17 @@ const fns = {
107
116
 
108
117
  state.schemas[schema.id] = hashObj(schema);
109
118
  });
119
+ // console.log(JSON.parse(JSON.stringify(state.resources.schemas)));
110
120
  },
111
121
 
112
122
  // Called when schema is updated
113
- updateSchema(schema) {
123
+ updateSchema: (schema) => {
114
124
  // Add the schema to the queue to be checked to see if the schema really changed
115
125
  state.queue.push(schema);
116
126
  },
117
127
 
118
128
  // Remove the cached schema
119
- removeSchema(id) {
129
+ removeSchema: (id) => {
120
130
  // Remove anything in the queue related to the schema - we don't want to send any pending updates later for a schema that has been removed
121
131
  state.queue = state.queue.filter(schema => schema.id !== id);
122
132
 
@@ -125,5 +135,4 @@ const fns = {
125
135
  }
126
136
  };
127
137
 
128
- // Expose the Web Worker API - see: https://github.com/GoogleChromeLabs/comlink
129
- Comlink.expose(fns);
138
+ onmessage = workerActions.onmessage; // bind everything to the worker's onmessage handler via the workerAction
@@ -0,0 +1,47 @@
1
+ <script>
2
+ import RoleDeletionCheck from '@shell/promptRemove/mixin/roleDeletionCheck';
3
+ export default {
4
+ name: 'GlobalRolePromptRemove',
5
+ mixins: [RoleDeletionCheck],
6
+ props: {
7
+ value: {
8
+ type: Array,
9
+ default: () => {
10
+ return [];
11
+ }
12
+ },
13
+
14
+ type: {
15
+ type: String,
16
+ required: true
17
+ }
18
+ },
19
+ };
20
+ </script>
21
+
22
+ <template>
23
+ <div>
24
+ <template>
25
+ {{ t('promptRemove.attemptingToRemove', { type }) }} <span
26
+ v-html="resourceNames(names, plusMore, t)"
27
+ ></span>
28
+ </template>
29
+ <div v-if="info" class="text info mb-10 mt-20">
30
+ <span v-html="info" />
31
+ </div>
32
+ <div v-if="warning" class="text-warning mb-10 mt-20">
33
+ {{ warning }}
34
+ </div>
35
+ </div>
36
+ </template>
37
+
38
+ <style lang="scss" scoped>
39
+ .text.info {
40
+ display: flex;
41
+ align-items: center;
42
+
43
+ > span {
44
+ margin-right: 5px;
45
+ }
46
+ }
47
+ </style>
@@ -0,0 +1,47 @@
1
+ <script>
2
+ import RoleDeletionCheck from '@shell/promptRemove/mixin/roleDeletionCheck';
3
+ export default {
4
+ name: 'RoleTemplatePromptRemove',
5
+ mixins: [RoleDeletionCheck],
6
+ props: {
7
+ value: {
8
+ type: Array,
9
+ default: () => {
10
+ return [];
11
+ }
12
+ },
13
+
14
+ type: {
15
+ type: String,
16
+ required: true
17
+ }
18
+ },
19
+ };
20
+ </script>
21
+
22
+ <template>
23
+ <div>
24
+ <template>
25
+ {{ t('promptRemove.attemptingToRemove', { type }) }} <span
26
+ v-html="resourceNames(names, plusMore, t)"
27
+ ></span>
28
+ </template>
29
+ <div v-if="info" class="text info mb-10 mt-20">
30
+ <span v-html="info" />
31
+ </div>
32
+ <div v-if="warning" class="text-warning mb-10 mt-20">
33
+ {{ warning }}
34
+ </div>
35
+ </div>
36
+ </template>
37
+
38
+ <style lang="scss" scoped>
39
+ .text.info {
40
+ display: flex;
41
+ align-items: center;
42
+
43
+ > span {
44
+ margin-right: 5px;
45
+ }
46
+ }
47
+ </style>
@@ -0,0 +1,97 @@
1
+ import { mapState, mapGetters } from 'vuex';
2
+ import { resourceNames } from '@shell/utils/string';
3
+ import { MANAGEMENT } from '@shell/config/types';
4
+ import { SUBTYPE_MAPPING } from '@shell/models/management.cattle.io.roletemplate';
5
+ const CLUSTER = SUBTYPE_MAPPING.CLUSTER.key;
6
+
7
+ export default {
8
+ data() {
9
+ return {
10
+ warning: '',
11
+ info: '',
12
+ };
13
+ },
14
+
15
+ computed: {
16
+ ...mapState('action-menu', ['toRemove']),
17
+ ...mapGetters({ t: 'i18n/t' }),
18
+
19
+ names() {
20
+ return this.toRemove.map(obj => obj.nameDisplay).slice(0, 5);
21
+ },
22
+
23
+ plusMore() {
24
+ const remaining = this.toRemove.length - this.names.length;
25
+
26
+ return this.t('promptRemove.andOthers', { count: remaining });
27
+ },
28
+ },
29
+ watch: {
30
+ value: {
31
+ handler(neu) {
32
+ this.handleRoleDeletionCheck(neu, neu[0].type, this.$route.hash);
33
+ },
34
+ immediate: true
35
+ }
36
+ },
37
+ methods: {
38
+ resourceNames,
39
+ async handleRoleDeletionCheck(rolesToRemove, resourceType, queryHash) {
40
+ this.warning = '';
41
+ let resourceToCheck;
42
+ let propToMatch;
43
+ let numberOfRolesWithBinds = 0;
44
+ const uniqueUsersWithBinds = new Set();
45
+
46
+ this.info = this.t('rbac.globalRoles.waiting', { count: rolesToRemove.length });
47
+
48
+ switch (resourceType) {
49
+ case MANAGEMENT.GLOBAL_ROLE:
50
+ resourceToCheck = MANAGEMENT.GLOBAL_ROLE_BINDING;
51
+ propToMatch = 'globalRoleName';
52
+ break;
53
+ default:
54
+ if (queryHash.includes(CLUSTER)) {
55
+ resourceToCheck = MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING;
56
+ } else {
57
+ resourceToCheck = MANAGEMENT.PROJECT_ROLE_TEMPLATE_BINDING;
58
+ }
59
+ propToMatch = 'roleTemplateName';
60
+ break;
61
+ }
62
+
63
+ try {
64
+ const request = await this.$store.dispatch('management/request', {
65
+ url: `/v1/${ resourceToCheck }`,
66
+ method: 'get',
67
+ }, { root: true });
68
+
69
+ if (request.data && request.data.length) {
70
+ rolesToRemove.forEach((toRemove) => {
71
+ const usedRoles = request.data.filter(item => item[propToMatch] === toRemove.id);
72
+
73
+ if (usedRoles.length) {
74
+ const uniqueUsers = [...new Set(usedRoles.map(item => item.userName))];
75
+
76
+ if (uniqueUsers.length) {
77
+ numberOfRolesWithBinds++;
78
+ uniqueUsers.forEach(user => uniqueUsersWithBinds.add(user));
79
+ }
80
+ }
81
+ });
82
+
83
+ if (numberOfRolesWithBinds && uniqueUsersWithBinds.size) {
84
+ this.info = '';
85
+ this.warning = this.t('rbac.globalRoles.usersBound', { count: uniqueUsersWithBinds.size });
86
+ } else {
87
+ this.info = this.t('rbac.globalRoles.notBound', null, true);
88
+ }
89
+ } else {
90
+ this.info = this.t('rbac.globalRoles.notBound', null, true);
91
+ }
92
+ } catch (e) {
93
+ this.info = this.t('rbac.globalRoles.unableToCheck');
94
+ }
95
+ },
96
+ },
97
+ };
@@ -6,7 +6,7 @@ BASE_DIR="$(
6
6
  pwd
7
7
  )"
8
8
  SHELL_DIR=$BASE_DIR/shell/
9
- PUBLISH_ARGS="--no-git-tag-version --access public"
9
+ PUBLISH_ARGS="--no-git-tag-version --access public $PUBLISH_ARGS"
10
10
 
11
11
  echo "Publishing Shell Packages"
12
12
 
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+
6
+ console.log(__dirname);
7
+
8
+ const dir = path.resolve('.');
9
+
10
+ console.log('Syncing shell dependencies');
11
+
12
+ const topFile = path.join(dir, 'package.json');
13
+ const shellFile = path.join(dir, 'shell', 'package.json');
14
+
15
+ console.log(topFile);
16
+ console.log(shellFile);
17
+
18
+ const mainPkg = JSON.parse(fs.readFileSync(topFile));
19
+
20
+ console.log(mainPkg.version);
21
+
22
+ const shellPkg = JSON.parse(fs.readFileSync(shellFile));
23
+
24
+ console.log(shellPkg.version);
25
+
26
+ Object.keys(shellPkg.dependencies).forEach(k => {
27
+ const version = mainPkg.dependencies[k] || mainPkg.devDependencies[k];
28
+ const current = shellPkg.dependencies[k];
29
+
30
+ if (version) {
31
+ console.log(`Syncing ${ k } -> ${ version } was ${ current }`);
32
+ shellPkg.dependencies[k] = version;
33
+ }
34
+ });
35
+
36
+ fs.writeFileSync(shellFile, JSON.stringify(shellPkg, undefined, 2));
37
+
package/store/catalog.js CHANGED
@@ -61,6 +61,11 @@ export const getters = {
61
61
  return [...clustered, ...namespaced];
62
62
  },
63
63
 
64
+ // Raw charts
65
+ rawCharts(state) {
66
+ return state.charts;
67
+ },
68
+
64
69
  repo(state, getters) {
65
70
  return ({ repoType, repoName }) => {
66
71
  const ary = (repoType === 'cluster' ? state.clusterRepos : state.namespacedRepos);
@@ -250,19 +255,13 @@ export const getters = {
250
255
 
251
256
  haveComponent() {
252
257
  return (name) => {
253
- try {
254
- require.resolve(`@shell/chart/${ name }`);
255
-
256
- return true;
257
- } catch (e) {
258
- return false;
259
- }
258
+ return getters['type-map/hasCustomChart'](name);
260
259
  };
261
260
  },
262
261
 
263
262
  importComponent(state, getters) {
264
263
  return (name) => {
265
- return importChart(name);
264
+ return getters['type-map/importChart'](name);
266
265
  };
267
266
  },
268
267
 
@@ -340,6 +339,7 @@ export const actions = {
340
339
  // Installing an app? This is fine (in cluster store)
341
340
  // Fetching list of cluster templates? This is fine (in management store)
342
341
  // Installing a cluster template? This isn't fine (in cluster store as per installing app, but if there is no cluster we need to default to management)
342
+
343
343
  const inStore = rootGetters['currentCluster'] ? rootGetters['currentProduct'].inStore : 'management';
344
344
 
345
345
  if ( rootGetters[`${ inStore }/schemaFor`](CATALOG.CLUSTER_REPO) ) {
@@ -517,6 +517,7 @@ function addChart(ctx, map, chart, repo) {
517
517
  chartName: chart.name,
518
518
  chartNameDisplay: chart.annotations?.[CATALOG_ANNOTATIONS.DISPLAY_NAME] || chart.name,
519
519
  chartDescription: chart.description,
520
+ featured: chart.annotations?.[CATALOG_ANNOTATIONS.FEATURED],
520
521
  repoKey: repo._key,
521
522
  versions: [],
522
523
  categories: filterCategories(chart.keywords),
package/store/i18n.js CHANGED
@@ -139,6 +139,14 @@ export const getters = {
139
139
  return state.default;
140
140
  },
141
141
 
142
+ multiWithFallback: (state, getters) => (items, key = 'key') => {
143
+ return items.map((item) => {
144
+ item[key] = getters.withFallback(item[key], null, item[key]);
145
+
146
+ return item;
147
+ });
148
+ },
149
+
142
150
  withFallback: (state, getters) => (key, args, fallback, fallbackIsKey = false) => {
143
151
  // Support withFallback(key,fallback) when no args
144
152
  if ( !fallback && typeof args === 'string' ) {
@@ -153,7 +161,8 @@ export const getters = {
153
161
  } else {
154
162
  return fallback;
155
163
  }
156
- }
164
+ },
165
+
157
166
  };
158
167
 
159
168
  export const mutations = {