@rancher/shell 0.1.1 → 0.1.2

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 (48) hide show
  1. package/assets/translations/en-us.yaml +8 -2
  2. package/assets/translations/zh-hans.yaml +158 -25
  3. package/components/auth/RoleDetailEdit.vue +14 -1
  4. package/components/form/ResourceTabs/index.vue +27 -18
  5. package/components/formatter/ClusterLink.vue +13 -0
  6. package/components/formatter/PodImages.vue +11 -1
  7. package/components/formatter/RKETemplateName.vue +37 -0
  8. package/config/types.js +3 -1
  9. package/creators/app/tsconfig.json +6 -1
  10. package/creators/pkg/init +3 -0
  11. package/creators/pkg/tsconfig.json +7 -2
  12. package/detail/provisioning.cattle.io.cluster.vue +10 -0
  13. package/edit/cloudcredential.vue +7 -1
  14. package/edit/networking.k8s.io.ingress/index.vue +2 -1
  15. package/edit/persistentvolumeclaim.vue +32 -2
  16. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
  17. package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
  18. package/edit/provisioning.cattle.io.cluster/rke2.vue +21 -6
  19. package/edit/workload/types/Deployment.vue +1 -1
  20. package/edit/workload/types/Generic.vue +1 -1
  21. package/list/provisioning.cattle.io.cluster.vue +6 -0
  22. package/mixins/brand.js +3 -4
  23. package/mixins/create-edit-view/impl.js +0 -8
  24. package/models/chart.js +1 -1
  25. package/models/cluster/node.js +12 -1
  26. package/models/management.cattle.io.globalrole.js +0 -19
  27. package/models/management.cattle.io.roletemplate.js +2 -21
  28. package/models/provisioning.cattle.io.cluster.js +67 -0
  29. package/models/service.js +5 -1
  30. package/nuxt.config.js +3 -3
  31. package/package.json +1 -1
  32. package/pages/c/_cluster/apps/charts/install.vue +0 -6
  33. package/plugins/console.js +10 -5
  34. package/plugins/dashboard-store/actions.js +8 -3
  35. package/plugins/dashboard-store/getters.js +7 -2
  36. package/plugins/steve/steve-description-class.js +32 -0
  37. package/rancher-components/Banner/Banner.vue +2 -2
  38. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +0 -2
  39. package/rancher-components/Form/LabeledInput/LabeledInput.vue +2 -0
  40. package/rancher-components/Form/Radio/RadioButton.vue +14 -1
  41. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
  42. package/{components/form → rancher-components/Form/ToggleSwitch}/ToggleSwitch.vue +18 -8
  43. package/rancher-components/Form/ToggleSwitch/index.ts +1 -0
  44. package/rancher-components/Form/index.ts +1 -0
  45. package/scripts/test-plugins-build.sh +18 -1
  46. package/types/{index.d.ts → rancher/index.d.ts} +0 -0
  47. package/plugins/lookup.js +0 -50
  48. package/yarn-error.log +0 -196
@@ -66,22 +66,21 @@ export default {
66
66
  }
67
67
  },
68
68
 
69
- async fetch() {
69
+ data() {
70
70
  const inStore = this.$store.getters['currentStore'](EVENT);
71
71
 
72
- if ( this.$store.getters[`${ inStore }/schemaFor`](EVENT) ) {
73
- this.hasEvents = true; // @TODO be smarter about which ones actually ever have events
74
- this.allEvents = await this.$store.dispatch(`${ inStore }/findAll`, { type: EVENT });
75
- }
76
- },
77
-
78
- data() {
79
72
  return {
80
- hasEvents: null,
81
- allEvents: []
73
+ hasEvents: this.$store.getters[`${ inStore }/schemaFor`](EVENT), // @TODO be smarter about which resources actually ever have events
74
+ allEvents: [],
75
+ selectedTab: this.defaultTab,
76
+ didLoadEvents: false,
82
77
  };
83
78
  },
84
79
 
80
+ beforeDestroy() {
81
+ this.$store.dispatch('cluster/forgetType', EVENT);
82
+ },
83
+
85
84
  computed: {
86
85
  showConditions() {
87
86
  const inStore = this.$store.getters['currentStore'](this.value.type);
@@ -89,10 +88,10 @@ export default {
89
88
  return this.isView && this.needConditions && this.value?.type && this.$store.getters[`${ inStore }/pathExistsInSchema`](this.value.type, 'status.conditions');
90
89
  },
91
90
  showEvents() {
92
- return this.isView && this.needEvents && !this.$fetchState.pending && this.hasEvents && (this.events.length || this.alwaysShowEvents);
91
+ return this.isView && this.needEvents && this.hasEvents && (this.events.length || this.alwaysShowEvents);
93
92
  },
94
93
  showRelated() {
95
- return this.isView && this.needRelated && !this.$fetchState.pending;
94
+ return this.isView && this.needRelated;
96
95
  },
97
96
  eventHeaders() {
98
97
  return [
@@ -146,17 +145,26 @@ export default {
146
145
  }
147
146
  },
148
147
 
149
- mounted() {
150
- // For easy access debugging...
151
- if ( typeof window !== 'undefined' ) {
152
- window.v = this.value;
148
+ methods: {
149
+ // Ensures we only fetch events and show the table when the events tab has been activated
150
+ tabChange(neu) {
151
+ this.selectedTab = neu?.selectedName;
152
+
153
+ if (!this.didLoadEvents && this.selectedTab === 'events') {
154
+ const inStore = this.$store.getters['currentStore'](EVENT);
155
+
156
+ this.$store.dispatch(`${ inStore }/findAll`, { type: EVENT }).then((events) => {
157
+ this.allEvents = events;
158
+ this.didLoadEvents = true;
159
+ });
160
+ }
153
161
  }
154
- },
162
+ }
155
163
  };
156
164
  </script>
157
165
 
158
166
  <template>
159
- <Tabbed v-bind="$attrs" :default-tab="defaultTab">
167
+ <Tabbed v-bind="$attrs" :default-tab="defaultTab" @changed="tabChange">
160
168
  <slot />
161
169
 
162
170
  <Tab v-if="showConditions" label-key="resourceTabs.conditions.tab" name="conditions" :weight="-1" :display-alert-icon="conditionsHaveIssues">
@@ -165,6 +173,7 @@ export default {
165
173
 
166
174
  <Tab v-if="showEvents" label-key="resourceTabs.events.tab" name="events" :weight="-2">
167
175
  <SortableTable
176
+ v-if="selectedTab === 'events'"
168
177
  :rows="events"
169
178
  :headers="eventHeaders"
170
179
  key-field="id"
@@ -63,6 +63,11 @@ export default {
63
63
  {{ value }}
64
64
  </n-link>
65
65
  <span v-else>{{ value }}</span>
66
+ <i
67
+ v-if="row.rkeTemplateUpgrade"
68
+ v-tooltip="t('cluster.rkeTemplateUpgrade', { name: row.rkeTemplateUpgrade })"
69
+ class="template-upgrade-icon icon-alert icon"
70
+ />
66
71
  <i
67
72
  v-if="clusterHasIssues"
68
73
  v-tooltip="{ content: `<div>${formattedConditions}</div>`, html: true }"
@@ -93,4 +98,12 @@ export default {
93
98
  .mytooltip ul {
94
99
  outline: 1px dashed red;
95
100
  }
101
+ .template-upgrade-icon {
102
+ border: 1px solid var(--warning);
103
+ border-radius: 50%;
104
+ color: var(--warning);
105
+ margin-left: 4px;
106
+ font-size: 14px;
107
+ padding: 2px;
108
+ }
96
109
  </style>
@@ -1,6 +1,10 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
3
 
4
+ // For the main image hat we show, use these to ignore istio proxy images so that
5
+ // we are more likely to show the important main iamge and not the istio sidecar
6
+ const IGNORE_IMAGES = ['istio/proxy', 'gcr.io/istio-release/proxy', 'mirrored-istio-proxy'];
7
+
4
8
  export default {
5
9
  props: {
6
10
  value: {
@@ -38,6 +42,12 @@ export default {
38
42
  }
39
43
 
40
44
  return null;
45
+ },
46
+ mainImage() {
47
+ const images = this.images;
48
+ const filter = images.filter(image => !IGNORE_IMAGES.find(i => image.includes(i)));
49
+
50
+ return filter.length > 0 ? filter[0] : images[0];
41
51
  }
42
52
  }
43
53
 
@@ -46,7 +56,7 @@ export default {
46
56
 
47
57
  <template>
48
58
  <span>
49
- <span>{{ images[0] }}</span><br>
59
+ <span>{{ mainImage }}</span><br>
50
60
  <span
51
61
  v-if="images.length-1>0"
52
62
  v-tooltip.bottom="imageLabels"
@@ -0,0 +1,37 @@
1
+ <script>
2
+ export default {
3
+ props: {
4
+ value: {
5
+ type: Object,
6
+ required: true
7
+ },
8
+ }
9
+ };
10
+ </script>
11
+
12
+ <template>
13
+ <div class="rke-template">
14
+ <span>{{ value.displayName }}</span>
15
+ <i
16
+ v-if="value.upgrade"
17
+ v-tooltip="t('cluster.rkeTemplateUpgrade', { name: value.upgrade })"
18
+ class="template-upgrade-icon icon-alert icon"
19
+ />
20
+ </div>
21
+ </template>
22
+
23
+ <style lang="scss" scoped>
24
+ .rke-template {
25
+ align-items: center;
26
+ display: inline-flex;
27
+
28
+ .template-upgrade-icon {
29
+ border: 1px solid var(--warning);
30
+ border-radius: 50%;
31
+ color: var(--warning);
32
+ margin-left: 4px;
33
+ font-size: 14px;
34
+ padding: 2px;
35
+ }
36
+ }
37
+ </style>
package/config/types.js CHANGED
@@ -169,7 +169,9 @@ export const MANAGEMENT = {
169
169
  POD_SECURITY_POLICY_TEMPLATE: 'management.cattle.io.podsecuritypolicytemplate',
170
170
  MANAGED_CHART: 'management.cattle.io.managedchart',
171
171
  USER_NOTIFICATION: 'management.cattle.io.rancherusernotification',
172
- GLOBAL_DNS_PROVIDER: 'management.cattle.io.globaldnsprovider'
172
+ GLOBAL_DNS_PROVIDER: 'management.cattle.io.globaldnsprovider',
173
+ RKE_TEMPLATE: 'management.cattle.io.clustertemplate',
174
+ RKE_TEMPLATE_REVISION: 'management.cattle.io.clustertemplaterevision',
173
175
  };
174
176
 
175
177
  export const CAPI = {
@@ -25,10 +25,15 @@
25
25
  "./node_modules/@rancher/shell/*"
26
26
  ]
27
27
  },
28
+ "typeRoots": [
29
+ "./node_modules",
30
+ "./node_modules/@rancher/shell/types"
31
+ ],
28
32
  "types": [
29
33
  "@types/node",
30
34
  "@nuxt/types",
31
- "cypress"
35
+ "cypress",
36
+ "rancher"
32
37
  ]
33
38
  },
34
39
  "exclude": [
package/creators/pkg/init CHANGED
@@ -137,6 +137,9 @@ if (!isNodeModulesShell) {
137
137
  tsconfig.compilerOptions.paths[p] = updateArray(tsconfig.compilerOptions.paths[p]);
138
138
  });
139
139
 
140
+ // Update typeRoots
141
+ tsconfig.compilerOptions.typeRoots = updateArray(tsconfig.compilerOptions.typeRoots);
142
+
140
143
  fs.writeFileSync(path.join(pkgFolder, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
141
144
  console.log(' Updated tsconfig.json for shell folder location');
142
145
  }
@@ -13,9 +13,15 @@
13
13
  "sourceMap": true,
14
14
  "baseUrl": ".",
15
15
  "preserveSymlinks": true,
16
+ "typeRoots": [
17
+ "../../node_modules",
18
+ "../../node_modules/@rancher/shell/types"
19
+ ],
16
20
  "types": [
17
21
  "node",
18
- "webpack-env"
22
+ "webpack-env",
23
+ "@nuxt/types",
24
+ "rancher"
19
25
  ],
20
26
  "lib": [
21
27
  "esnext",
@@ -39,7 +45,6 @@
39
45
  "**/*.ts",
40
46
  "**/*.tsx",
41
47
  "**/*.vue",
42
- "../../node_modules/@rancher/shell/types/*.d.ts",
43
48
  "../../node_modules/@rancher/shell/core/types.ts"
44
49
  ],
45
50
  "exclude": [
@@ -146,6 +146,16 @@ export default {
146
146
  this.allNodePools = fetchTwoRes.allNodePools || [];
147
147
  this.haveNodePools = !!fetchTwoRes.allNodePools;
148
148
  this.machineTemplates = fetchTwoRes.mdtt || [];
149
+
150
+ // Fetch RKE template revisions so we can show when an updated template is available
151
+ // This request does not need to be blocking
152
+ if ( this.$store.getters['management/canList'](MANAGEMENT.RKE_TEMPLATE) ) {
153
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.RKE_TEMPLATE });
154
+ }
155
+
156
+ if ( this.$store.getters['management/canList'](MANAGEMENT.RKE_TEMPLATE_REVISION) ) {
157
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.RKE_TEMPLATE_REVISION });
158
+ }
149
159
  },
150
160
 
151
161
  created() {
@@ -260,7 +260,13 @@ export default {
260
260
  @select-type="selectType"
261
261
  @error="e=>errors = e"
262
262
  >
263
- <NameNsDescription v-model="value" name-key="_name" :mode="mode" :namespaced="false" />
263
+ <NameNsDescription
264
+ v-model="value"
265
+ name-key="_name"
266
+ description-key="description"
267
+ :mode="mode"
268
+ :namespaced="false"
269
+ />
264
270
  <keep-alive>
265
271
  <component
266
272
  :is="cloudComponent"
@@ -44,10 +44,11 @@ export default {
44
44
  }
45
45
  },
46
46
  async fetch() {
47
+ const ingressClassSchema = this.$store.getters[`cluster/schemaFor`](INGRESS_CLASS);
47
48
  const hash = await allHash({
48
49
  secrets: this.$store.dispatch('cluster/findAll', { type: SECRET }),
49
50
  services: this.$store.dispatch('cluster/findAll', { type: SERVICE }),
50
- ingressClasses: this.$store.dispatch('cluster/findAll', { type: INGRESS_CLASS }),
51
+ ingressClasses: ingressClassSchema ? this.$store.dispatch('cluster/findAll', { type: INGRESS_CLASS }) : Promise.resolve([]),
51
52
  });
52
53
 
53
54
  this.allServices = hash.services;
@@ -5,6 +5,7 @@ import CruResource from '@shell/components/CruResource';
5
5
  import NameNsDescription from '@shell/components/form/NameNsDescription';
6
6
  import Tab from '@shell/components/Tabbed/Tab';
7
7
  import { RadioGroup } from '@components/Form/Radio';
8
+ import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
8
9
  import LabeledSelect from '@shell/components/form/LabeledSelect';
9
10
  import UnitInput from '@shell/components/form/UnitInput';
10
11
  import uniq from 'lodash/uniq';
@@ -24,6 +25,7 @@ export default {
24
25
  Banner,
25
26
  Checkbox,
26
27
  CruResource,
28
+ LabeledInput,
27
29
  LabeledSelect,
28
30
  Labels,
29
31
  NameNsDescription,
@@ -38,7 +40,9 @@ export default {
38
40
  async fetch() {
39
41
  const storageClasses = await this.$store.dispatch('cluster/findAll', { type: STORAGE_CLASS });
40
42
 
41
- this.persistentVolumes = await this.$store.dispatch('cluster/findAll', { type: PV });
43
+ if (this.$store.getters['management/canList'](PV)) {
44
+ this.persistentVolumes = await this.$store.dispatch('cluster/findAll', { type: PV });
45
+ }
42
46
 
43
47
  this.storageClassOptions = storageClasses.map(s => s.name).sort();
44
48
  this.storageClassOptions.unshift(this.t('persistentVolumeClaim.useDefault'));
@@ -55,6 +59,8 @@ export default {
55
59
  this.$set(this.value.spec, 'storageClassName', this.value.spec.storageClassName || this.storageClassOptions[0]);
56
60
  },
57
61
  data() {
62
+ const canListPersistentVolumes = this.$store.getters['management/canList'](PV);
63
+ const canListStorageClasses = this.$store.getters['management/canList'](STORAGE_CLASS);
58
64
  const sourceOptions = [
59
65
  {
60
66
  label: this.t('persistentVolumeClaim.source.options.new'),
@@ -65,6 +71,7 @@ export default {
65
71
  value: 'existing'
66
72
  }
67
73
  ];
74
+
68
75
  const defaultAccessModes = ['ReadWriteOnce'];
69
76
 
70
77
  this.$set(this.value, 'spec', this.value.spec || {});
@@ -85,6 +92,8 @@ export default {
85
92
  persistentVolumes: [],
86
93
  storageClassOptions: [],
87
94
  defaultTab,
95
+ canListPersistentVolumes,
96
+ canListStorageClasses,
88
97
  };
89
98
  },
90
99
  computed: {
@@ -216,16 +225,37 @@ export default {
216
225
  <div class="col span-6">
217
226
  <div class="row">
218
227
  <div v-if="source === 'new'" class="col span-12">
219
- <LabeledSelect v-model="value.spec.storageClassName" :options="storageClassOptions" :label="t('persistentVolumeClaim.volumeClaim.storageClass')" :mode="immutableMode" />
228
+ <LabeledSelect
229
+ v-if="canListStorageClasses"
230
+ v-model="value.spec.storageClassName"
231
+ :options="storageClassOptions"
232
+ :label="t('persistentVolumeClaim.volumeClaim.storageClass')"
233
+ :mode="immutableMode"
234
+ />
235
+ <LabeledInput
236
+ v-else
237
+ v-model="value.spec.storageClassName"
238
+ :label="t('persistentVolumeClaim.volumeClaim.storageClass')"
239
+ :mode="immutableMode"
240
+ :tooltip="t('persistentVolumeClaim.volumeClaim.tooltips.noStorageClass')"
241
+ />
220
242
  </div>
221
243
  <div v-else class="col span-12">
222
244
  <LabeledSelect
245
+ v-if="canListPersistentVolumes"
223
246
  v-model="persistentVolume"
224
247
  :options="persistentVolumeOptions"
225
248
  :label="t('persistentVolumeClaim.volumeClaim.persistentVolume')"
226
249
  :selectable="isPersistentVolumeSelectable"
227
250
  :mode="immutableMode"
228
251
  />
252
+ <LabeledInput
253
+ v-else
254
+ v-model="persistentVolume"
255
+ :label="t('persistentVolumeClaim.volumeClaim.persistentVolume')"
256
+ :mode="immutableMode"
257
+ :tooltip="t('persistentVolumeClaim.volumeClaim.tooltips.noPersistentVolume')"
258
+ />
229
259
  </div>
230
260
  </div>
231
261
  <div class="row">
@@ -210,7 +210,7 @@ export default {
210
210
  :mode="mode"
211
211
  :output-modifier="true"
212
212
  :base-unit="t('cluster.machinePool.autoReplace.unit')"
213
- @input="value.pool.unhealthyNodeTimeout = `${unhealthyNodeTimeoutInteger}m`"
213
+ @input="value.pool.unhealthyNodeTimeout = `${unhealthyNodeTimeoutInteger}s`"
214
214
  />
215
215
  </div>
216
216
  <div class="col span-4">
@@ -4,7 +4,7 @@ import Loading from '@shell/components/Loading';
4
4
  import CruResource from '@shell/components/CruResource';
5
5
  import SelectIconGrid from '@shell/components/SelectIconGrid';
6
6
  import EmberPage from '@shell/components/EmberPage';
7
- import ToggleSwitch from '@shell/components/form/ToggleSwitch';
7
+ import { ToggleSwitch } from '@components/Form/ToggleSwitch';
8
8
  import {
9
9
  CHART, FROM_CLUSTER, SUB_TYPE, _EDIT, _IMPORT, _CONFIG, _VIEW
10
10
  } from '@shell/config/query-params';
@@ -249,10 +249,6 @@ export default {
249
249
  set(this.value.spec, 'defaultPodSecurityPolicyTemplateName', '');
250
250
  }
251
251
 
252
- if (this.isHarvesterDriver && this.mode === _CREATE && this.agentConfig['cloud-provider-name'] === undefined) {
253
- this.agentConfig['cloud-provider-name'] = HARVESTER;
254
- }
255
-
256
252
  await this.initAddons();
257
253
  await this.initRegistry();
258
254
 
@@ -501,11 +497,17 @@ export default {
501
497
  // If we have a preferred provider... only show default, preferred and external
502
498
  const isPreferred = opt === preferred;
503
499
  const isExternal = opt === 'external';
500
+ let disabled = false;
501
+
502
+ if (this.isHarvesterExternalCredential && isPreferred) {
503
+ disabled = true;
504
+ }
504
505
 
505
506
  if (showAllOptions || isPreferred || isExternal) {
506
507
  out.push({
507
508
  label: this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ opt }".label`, null, opt),
508
509
  value: opt,
510
+ disabled,
509
511
  });
510
512
  }
511
513
  }
@@ -843,7 +845,11 @@ export default {
843
845
 
844
846
  showForm() {
845
847
  return !!this.credentialId || !this.needCredential;
846
- }
848
+ },
849
+
850
+ isHarvesterExternalCredential() {
851
+ return this.credential?.harvestercredentialConfig?.clusterType === 'external';
852
+ },
847
853
  },
848
854
 
849
855
  watch: {
@@ -861,6 +867,7 @@ export default {
861
867
  credentialId(val) {
862
868
  if ( val ) {
863
869
  this.credential = this.$store.getters['rancher/byId'](NORMAN.CLOUD_CREDENTIAL, this.credentialId);
870
+ this.setHarvesterDefaultCloudProvider();
864
871
  } else {
865
872
  this.credential = null;
866
873
  }
@@ -1552,7 +1559,15 @@ export default {
1552
1559
  }
1553
1560
  });
1554
1561
  },
1555
- get
1562
+ get,
1563
+
1564
+ setHarvesterDefaultCloudProvider() {
1565
+ if (this.isHarvesterDriver && this.mode === _CREATE && this.agentConfig['cloud-provider-name'] === undefined && !this.isHarvesterExternalCredential) {
1566
+ this.agentConfig['cloud-provider-name'] = HARVESTER;
1567
+ } else {
1568
+ this.agentConfig['cloud-provider-name'] = '';
1569
+ }
1570
+ },
1556
1571
  },
1557
1572
  };
1558
1573
  </script>
@@ -249,7 +249,7 @@ export default {
249
249
  <LabeledInput v-model.number="podTemplateSpec.priority" :mode="mode" :label="t('workload.scheduling.priority.priority')" />
250
250
  </div>
251
251
  <div class="col span-6">
252
- <LabeledInput v-model="podTemplateSpec.priorityClassname" :mode="mode" :label="t('workload.scheduling.priority.className')" />
252
+ <LabeledInput v-model="podTemplateSpec.priorityClassName" :mode="mode" :label="t('workload.scheduling.priority.className')" />
253
253
  </div>
254
254
  </div>
255
255
  </div>
@@ -196,7 +196,7 @@ export default {
196
196
  <LabeledInput v-model.number="podTemplateSpec.priority" :mode="mode" :label="t('workload.scheduling.priority.priority')" />
197
197
  </div>
198
198
  <div class="col span-6">
199
- <LabeledInput v-model="podTemplateSpec.priorityClassname" :mode="mode" :label="t('workload.scheduling.priority.className')" />
199
+ <LabeledInput v-model="podTemplateSpec.priorityClassName" :mode="mode" :label="t('workload.scheduling.priority.className')" />
200
200
  </div>
201
201
  </div>
202
202
  </div>
@@ -41,6 +41,12 @@ export default {
41
41
  hash.machineDeployments = this.$store.dispatch('management/findAll', { type: CAPI.MACHINE_DEPLOYMENT });
42
42
  }
43
43
 
44
+ // Fetch RKE template revisions so we can show when an updated template is available
45
+ // This request does not need to be blocking
46
+ if ( this.$store.getters['management/canList'](MANAGEMENT.RKE_TEMPLATE_REVISION) ) {
47
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.RKE_TEMPLATE_REVISION });
48
+ }
49
+
44
50
  const res = await allHash(hash);
45
51
 
46
52
  this.mgmtClusters = res.mgmtClusters;
package/mixins/brand.js CHANGED
@@ -12,16 +12,15 @@ export default {
12
12
  this.apps = await this.$store.dispatch('management/findAll', { type: CATALOG.APP });
13
13
  }
14
14
  } catch (e) {}
15
+
16
+ this.globalSettings = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.SETTING });
15
17
  },
16
18
 
17
19
  data() {
18
- return { apps: [] };
20
+ return { apps: [], globalSettings: [] };
19
21
  },
20
22
 
21
23
  computed: {
22
- globalSettings() {
23
- return this.$store.getters['management/all'](MANAGEMENT.SETTING);
24
- },
25
24
 
26
25
  brand() {
27
26
  const setting = findBy(this.globalSettings, 'id', SETTING.BRAND);
@@ -12,14 +12,6 @@ export default {
12
12
 
13
13
  mixins: [ChildHook],
14
14
 
15
- mounted() {
16
- // For easy access debugging...
17
- if ( typeof window !== 'undefined' ) {
18
- window.v = this.value;
19
- window.c = this;
20
- }
21
- },
22
-
23
15
  data() {
24
16
  // Keep label and annotation filters in data so each resource CRUD page can alter individually
25
17
  return { errors: [] };
package/models/chart.js CHANGED
@@ -10,7 +10,7 @@ export default class Chart extends SteveModel {
10
10
  let version;
11
11
  const chartVersions = this.versions;
12
12
  const currentCluster = this.$rootGetters['currentCluster'];
13
- const workerOSs = currentCluster.workerOSs;
13
+ const workerOSs = currentCluster?.workerOSs;
14
14
  const compatibleVersions = compatibleVersionsFor(this, workerOSs);
15
15
 
16
16
  if (compatibleVersions.length) {
@@ -7,6 +7,7 @@ import { parseSi } from '@shell/utils/units';
7
7
  import findLast from 'lodash/findLast';
8
8
 
9
9
  import SteveModel from '@shell/plugins/steve/steve-class';
10
+ import { LOCAL } from '@shell/config/query-params';
10
11
 
11
12
  export default class ClusterNode extends SteveModel {
12
13
  get _availableActions() {
@@ -254,10 +255,20 @@ export default class ClusterNode extends SteveModel {
254
255
  }));
255
256
  }
256
257
 
258
+ /**
259
+ *Find the node's cluster id from it's url
260
+ */
257
261
  get clusterId() {
258
262
  const parts = this.links.self.split('/');
259
263
 
260
- return parts[parts.length - 4];
264
+ // Local cluster url links omit `/k8s/clusters/<cluster id>`
265
+ // `/v1/nodes` vs `k8s/clusters/c-m-274kcrc4/v1/nodes`
266
+ // Be safe when determining this, so work back through the url from a known point
267
+ if (parts.length > 6 && parts[parts.length - 6] === 'k8s' && parts[parts.length - 5] === 'clusters') {
268
+ return parts[parts.length - 4];
269
+ }
270
+
271
+ return LOCAL;
261
272
  }
262
273
 
263
274
  get normanNodeId() {
@@ -14,25 +14,6 @@ const SPECIAL = [BASE, ADMIN, USER];
14
14
  const GLOBAL = SUBTYPE_MAPPING.GLOBAL.key;
15
15
 
16
16
  export default class GlobalRole extends SteveModel {
17
- get availableActions() {
18
- const out = super._availableActions;
19
-
20
- const toFilter = ['goToEdit', 'promptRemove'];
21
- const editActions = out.filter((a) => {
22
- if ( toFilter.includes(a.action) ) {
23
- return a;
24
- }
25
- });
26
-
27
- if ( editActions.length > 0 ) {
28
- editActions.forEach((a) => {
29
- a.enabled = !this.builtin;
30
- });
31
- }
32
-
33
- return out;
34
- }
35
-
36
17
  get customValidationRules() {
37
18
  return Role.customValidationRules();
38
19
  }
@@ -2,7 +2,7 @@ import Vue from 'vue';
2
2
  import { get } from '@shell/utils/object';
3
3
  import { DESCRIPTION } from '@shell/config/labels-annotations';
4
4
  import { NORMAN } from '@shell/config/types';
5
- import SteveModel from '@shell/plugins/steve/steve-class';
5
+ import SteveDescriptionModel from '@shell/plugins/steve/steve-description-class';
6
6
  import Role from './rbac.authorization.k8s.io.role';
7
7
 
8
8
  export const CATTLE_API_GROUP = '.cattle.io';
@@ -55,26 +55,7 @@ export const VERBS = [
55
55
  'watch',
56
56
  ];
57
57
 
58
- export default class RoleTemplate extends SteveModel {
59
- get availableActions() {
60
- const out = super._availableActions;
61
-
62
- const toFilter = ['goToEdit', 'promptRemove'];
63
- const editActions = out.filter((a) => {
64
- if ( toFilter.includes(a.action) ) {
65
- return a;
66
- }
67
- });
68
-
69
- if ( editActions.length > 0 ) {
70
- editActions.forEach((a) => {
71
- a.enabled = !this.builtin;
72
- });
73
- }
74
-
75
- return out;
76
- }
77
-
58
+ export default class RoleTemplate extends SteveDescriptionModel {
78
59
  get customValidationRules() {
79
60
  return Role.customValidationRules();
80
61
  }