@rancher/shell 0.3.11 → 0.3.13

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 (95) hide show
  1. package/assets/translations/en-us.yaml +51 -5
  2. package/chart/monitoring/StorageClassSelector.vue +1 -0
  3. package/chart/monitoring/index.vue +4 -0
  4. package/chart/monitoring/prometheus/index.vue +6 -3
  5. package/components/ActionMenu.vue +1 -1
  6. package/components/DetailTop.vue +0 -2
  7. package/components/ExplorerMembers.vue +22 -10
  8. package/components/ExplorerProjectsNamespaces.vue +1 -0
  9. package/components/GrafanaDashboard.vue +2 -2
  10. package/components/Inactivity.vue +1 -0
  11. package/components/ModalWithCard.vue +1 -0
  12. package/components/Tabbed/index.vue +2 -0
  13. package/components/Wizard.vue +4 -3
  14. package/components/form/KeyValue.vue +12 -7
  15. package/components/form/NodeAffinity.vue +29 -7
  16. package/components/form/PodAffinity.vue +27 -7
  17. package/components/form/Taints.vue +6 -0
  18. package/components/formatter/ExtensionCache.vue +74 -0
  19. package/components/nav/Header.vue +1 -0
  20. package/components/nav/WindowManager/ContainerShell.vue +10 -0
  21. package/components/nav/WindowManager/index.vue +1 -0
  22. package/config/product/explorer.js +1 -10
  23. package/config/product/monitoring.js +2 -1
  24. package/config/router.js +3 -3
  25. package/config/table-headers.js +32 -24
  26. package/config/uiplugins.js +11 -0
  27. package/config/workload.ts +1 -0
  28. package/core/types.ts +25 -7
  29. package/creators/pkg/files/.github/workflows/build-container.yml +64 -0
  30. package/creators/pkg/init +13 -6
  31. package/detail/node.vue +2 -2
  32. package/detail/workload/index.vue +1 -1
  33. package/edit/__tests__/management.cattle.io.setting.test.ts +1 -1
  34. package/edit/__tests__/namespace.test.ts +46 -0
  35. package/edit/autoscaling.horizontalpodautoscaler/metric-target.vue +0 -2
  36. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +43 -0
  37. package/edit/logging.banzaicloud.io.output/index.vue +8 -5
  38. package/edit/logging.banzaicloud.io.output/providers/__tests__/loki.test.ts +13 -0
  39. package/edit/logging.banzaicloud.io.output/providers/loki.vue +1 -0
  40. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +0 -2
  41. package/edit/monitoring.coreos.com.receiver/index.vue +32 -1
  42. package/edit/monitoring.coreos.com.receiver/types/email.vue +12 -4
  43. package/edit/namespace.vue +2 -1
  44. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +36 -6
  45. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +2 -2
  46. package/edit/provisioning.cattle.io.cluster/rke2.vue +58 -13
  47. package/middleware/authenticated.js +1 -0
  48. package/models/__tests__/batch.cronjob.test.ts +88 -0
  49. package/models/cluster/node.js +8 -0
  50. package/models/management.cattle.io.clusterroletemplatebinding.js +5 -1
  51. package/models/projectroletemplatebinding.js +9 -1
  52. package/models/workload.js +1 -1
  53. package/package.json +1 -1
  54. package/pages/__tests__/prefs.test.ts +96 -0
  55. package/pages/auth/setup.vue +13 -13
  56. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  57. package/pages/c/_cluster/apps/charts/install.vue +5 -2
  58. package/pages/c/_cluster/monitoring/index.vue +10 -5
  59. package/pages/c/_cluster/settings/performance.vue +2 -0
  60. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +601 -0
  61. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +183 -0
  62. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +50 -9
  63. package/pages/c/_cluster/uiplugins/index.vue +329 -224
  64. package/pages/fail-whale.vue +1 -1
  65. package/pages/home.vue +11 -0
  66. package/pages/prefs.vue +20 -1
  67. package/plugins/plugin.js +1 -1
  68. package/public/index.html +6 -1
  69. package/rancher-components/components/Card/Card.vue +1 -0
  70. package/rancher-components/components/Form/Radio/RadioGroup.vue +1 -0
  71. package/scripts/extension/bundle +20 -4
  72. package/scripts/extension/helm/charts/ui-plugin-server/.helmignore +23 -0
  73. package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +20 -0
  74. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +52 -0
  75. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +12 -0
  76. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +6 -0
  77. package/scripts/extension/helm/package/Dockerfile +27 -0
  78. package/scripts/extension/helm/package/nginx.conf +17 -0
  79. package/scripts/extension/helm/scripts/package +23 -0
  80. package/scripts/extension/helm/scripts/patch +101 -0
  81. package/scripts/extension/helm/scripts/version +31 -0
  82. package/scripts/extension/helmpatch +3 -25
  83. package/scripts/extension/publish +50 -33
  84. package/types/shell/index.d.ts +39 -33
  85. package/utils/__tests__/grafana.test.ts +2 -2
  86. package/utils/error.js +11 -0
  87. package/utils/grafana.js +5 -4
  88. package/vue.config.js +3 -17
  89. package/components/.DS_Store +0 -0
  90. package/components/__tests__/.DS_Store +0 -0
  91. package/creators/pkg/package-lock.json +0 -37
  92. package/rancher-components/StringList/StringList.test.ts +0 -80
  93. package/rancher-components/StringList/StringList.vue +0 -593
  94. package/rancher-components/StringList/index.ts +0 -1
  95. package/yarn-error.log +0 -196
@@ -1274,9 +1274,6 @@ cluster:
1274
1274
  label: KubeconfigContent
1275
1275
  placeholder: 'Namespace/Name'
1276
1276
  cluster: Imported Harvester Cluster
1277
- affinity:
1278
- namespaces:
1279
- placeholder: e.g. default,system,base
1280
1277
  installGuestAgent: Install guest agent
1281
1278
  description:
1282
1279
  label: Cluster Description
@@ -3994,12 +3991,14 @@ plugins:
3994
3991
  builtin: Built-in
3995
3992
  experimental: Experimental
3996
3993
  third-party: Third-Party
3994
+ image: Image
3997
3995
  installing: Installing ...
3998
3996
  uninstalling: Uninstalling ...
3999
3997
  descriptions:
4000
3998
  experimental: This Extension is marked as experimental
4001
3999
  third-party: This Extension is provided by a Third-Party
4002
4000
  built-in: This Extension is built-in
4001
+ image: This Extension Image has been loaded manually
4003
4002
  error:
4004
4003
  title: Error loading extension
4005
4004
  message: Could not load extension code
@@ -4029,12 +4028,53 @@ plugins:
4029
4028
  available: No Extensions available
4030
4029
  installed: No Extensions installed
4031
4030
  updates: No updates available for installed Extensions
4031
+ images: No Extension Images installed
4032
4032
  loadError: An error occurred loading the code for this extension
4033
4033
  helmError: "An error occurred installing the extension via Helm"
4034
4034
  manageRepos: Manage Repositories
4035
+ manageCharts: Manage Extension Charts
4036
+ manageCatalog:
4037
+ label: Manage Extension Catalogs
4038
+ title: Extension
4039
+ subtitle: Catalogs
4040
+ imageLoad:
4041
+ load: Import Extension Catalog
4042
+ prompt: An Extension Catalog contains extension assets bundled into an image, importing will take the image and host a Helm repository to act as a catalog for custom built Extensions.
4043
+ fields:
4044
+ image:
4045
+ label: Catalog Image Reference
4046
+ placeholder: "e.g. hub.docker.io/example-org/my-image:latest"
4047
+ secrets:
4048
+ banner: "If the registry that hosts the Catalog Image requires Pull Secrets, they must be created in the following namespace:<pre>cattle-ui-plugin-system</pre>"
4049
+ banner: This will create an Deployment, Service, and Helm repository to serve the extension charts.
4050
+ imageVersion:
4051
+ title: Image Version Not Found
4052
+ message: Unable to determine image version from {image}, defaulting to latest
4053
+ error:
4054
+ exists:
4055
+ deployment:
4056
+ title: Deployment Conflict
4057
+ message: A container with the image {image} already exists
4058
+ service:
4059
+ title: Service Conflict
4060
+ message: A service with the name {svc} already exists
4061
+ repo:
4062
+ title: Helm Repository Conflict
4063
+ message: A repository with the name {repo} already exists
4064
+ success:
4065
+ title: "Imported Extension Catalog from: {name}"
4066
+ message: Extension Catalog image was imported successfully
4067
+ headers:
4068
+ image:
4069
+ name: images
4070
+ label: Deployment Image
4071
+ cacheState:
4072
+ name: cacheState
4073
+ label: Cache State
4035
4074
  tabs:
4036
4075
  all: All
4037
4076
  available: Available
4077
+ images: Images
4038
4078
  installed: Installed
4039
4079
  updates: Updates
4040
4080
  title: Extensions
@@ -4057,6 +4097,7 @@ plugins:
4057
4097
  label: Uninstall
4058
4098
  title: "Uninstall Extension: {name}"
4059
4099
  prompt: "Are you sure that you want to uninstall this Extension?"
4100
+ custom: "Are you sure that you want to uninstall this Extension Image? This will also remove any Extensions provided by this image."
4060
4101
  upgradeAvailable: A newer version of this Extension is available
4061
4102
  reload: Extensions changed - reload required
4062
4103
  safeMode:
@@ -6349,8 +6390,13 @@ typeLabel:
6349
6390
  }
6350
6391
  management.cattle.io.clusterroletemplatebinding: |-
6351
6392
  {count, plural,
6352
- one { Cluster Member }
6353
- other { Cluster Members }
6393
+ one { Cluster Membership }
6394
+ other { Cluster Memberships }
6395
+ }
6396
+ projectroletemplatebinding: |-
6397
+ {count, plural,
6398
+ one { Project Membership }
6399
+ other { Project Memberships }
6354
6400
  }
6355
6401
  management.cattle.io.feature: |-
6356
6402
  {count, plural,
@@ -2,6 +2,7 @@
2
2
  import LabeledSelect from '@shell/components/form/LabeledSelect';
3
3
 
4
4
  export default {
5
+ name: 'StorageClassSelector',
5
6
  components: { LabeledSelect },
6
7
  props: {
7
8
  options: {
@@ -176,6 +176,10 @@ export default {
176
176
  const selector =
177
177
  prometheusSpec?.storageSpec?.volumeClaimTemplate?.spec?.selector;
178
178
 
179
+ // This works for UI editor installation
180
+ // However, it doesn't work for yaml editor installation
181
+ // Global values later merged again in charts/install.vue addGlobalValuesTo()
182
+ // We still need to remove the global values from charts/install.vue addGlobalValuesTo()
179
183
  if (
180
184
  selector &&
181
185
  isEmpty(selector.matchExpressions) &&
@@ -72,7 +72,7 @@ export default {
72
72
  let matchExpressions;
73
73
 
74
74
  if (selector && selector?.matchExpressions) {
75
- matchExpressions = convert((selector?.matchLabels || {}), selector.matchExpressions);
75
+ matchExpressions = convert((selector?.matchLabels || {}), selector?.matchExpressions);
76
76
 
77
77
  return matchExpressions;
78
78
  } else {
@@ -154,7 +154,6 @@ export default {
154
154
  spec: {
155
155
  accessModes: ['ReadWriteOnce'],
156
156
  resources: { requests: { storage: '50Gi' } },
157
- selector: { matchExpressions: [], matchLabels: {} },
158
157
  }
159
158
  }
160
159
  );
@@ -311,6 +310,7 @@ export default {
311
310
  <div class="col span-6">
312
311
  <Checkbox
313
312
  v-model="enablePersistentStorage"
313
+ data-testid="checkbox-chart-enable-persistent-storage"
314
314
  :label="t('monitoring.prometheus.storage.label')"
315
315
  />
316
316
  </div>
@@ -325,7 +325,10 @@ export default {
325
325
  />
326
326
  </div>
327
327
  <div class="col span-6">
328
- <div v-if="showStorageClasses">
328
+ <div
329
+ v-if="showStorageClasses"
330
+ data-testid="select-chart-prometheus-storage-class"
331
+ >
329
332
  <StorageClassSelector
330
333
  :mode="mode"
331
334
  :options="storageClasses"
@@ -60,7 +60,7 @@ export default {
60
60
  customTargetEvent: {
61
61
  // The event details from the user's click can be used
62
62
  // for positioning the menu on the page.
63
- type: PointerEvent,
63
+ type: [PointerEvent, MouseEvent],
64
64
  default: null
65
65
  },
66
66
 
@@ -6,8 +6,6 @@ import { _VIEW } from '@shell/config/query-params';
6
6
  import { ExtensionPoint, PanelLocation } from '@shell/core/types';
7
7
  import ExtensionPanel from '@shell/components/ExtensionPanel';
8
8
 
9
- export const SEPARATOR = { separator: true };
10
-
11
9
  export default {
12
10
  components: {
13
11
  DetailText, Tag, ExtensionPanel
@@ -186,17 +186,25 @@ export default {
186
186
 
187
187
  // We need to group each of the TemplateRoleBindings by the user + project
188
188
  const userRoles = [...fakeRows, ...this.filteredProjectRoleTemplateBindings].reduce((rows, curr) => {
189
- const { userId, roleTemplate, projectId } = curr;
189
+ const {
190
+ userId, groupPrincipalId, roleTemplate, projectId
191
+ } = curr;
190
192
 
191
- const userKey = userId + projectId;
193
+ const userOrGroup = userId || groupPrincipalId;
192
194
 
193
- if (!rows[userKey] && userId ) {
194
- rows[userKey] = curr;
195
- rows[userKey].allRoles = [];
195
+ if (!userOrGroup) {
196
+ return rows;
196
197
  }
197
198
 
198
- if (roleTemplate && userId) {
199
- rows[userKey].allRoles.push(curr.roleTemplate);
199
+ const userOrGroupKey = userOrGroup + projectId;
200
+
201
+ if (!rows[userOrGroupKey] ) {
202
+ rows[userOrGroupKey] = curr;
203
+ rows[userOrGroupKey].allRoles = [];
204
+ }
205
+
206
+ if (roleTemplate) {
207
+ rows[userOrGroupKey].allRoles.push(curr.roleTemplate);
200
208
  }
201
209
 
202
210
  return rows;
@@ -242,9 +250,13 @@ export default {
242
250
  },
243
251
 
244
252
  getProjectRoleBinding(row, role) {
245
- // Each row is a combination of user and project
253
+ // Each row is a combination of user/group and project
246
254
  // So find the specfic roleBindingTemplate corresponding to the specific role + project
247
- return this.projectRoleTemplateBindings.find(r => r.roleTemplateId === role.id && r.userId === row.userId);
255
+ const userOrGroupKey = row.userId ? 'userId' : 'groupPrincipalId';
256
+
257
+ return this.projectRoleTemplateBindings.find((r) => {
258
+ return r.roleTemplateId === role.id && r[userOrGroupKey] === row[userOrGroupKey];
259
+ });
248
260
  },
249
261
 
250
262
  async removeRole(row, role, event) {
@@ -339,7 +351,7 @@ export default {
339
351
  <button
340
352
  v-if="canEditProjectMembers"
341
353
  type="button"
342
- class="create-namespace btn btn-sm role-secondary mr-5 right"
354
+ class="create-namespace btn btn-sm role-secondary mr-10 right"
343
355
  @click="addProjectMember(group)"
344
356
  >
345
357
  {{ t('members.createActionLabel') }}
@@ -373,6 +373,7 @@ export default {
373
373
  <n-link
374
374
  :to="createNamespaceLocationFlatList()"
375
375
  class="btn role-primary mr-10"
376
+ data-testid="create_project_namespaces"
376
377
  >
377
378
  {{ t('projectNamespaces.createNamespace') }}
378
379
  </n-link>
@@ -40,7 +40,7 @@ export default {
40
40
  },
41
41
  data() {
42
42
  return {
43
- loading: false, error: false, interval: null, initialUrl: this.computeUrl(), errorTimer: null, monitoringVersion: null
43
+ loading: false, error: false, interval: null, errorTimer: null, monitoringVersion: null
44
44
  };
45
45
  },
46
46
  computed: {
@@ -242,7 +242,7 @@ export default {
242
242
  v-show="!error"
243
243
  ref="frame"
244
244
  :class="{loading, frame: true}"
245
- :src="initialUrl"
245
+ :src="currentUrl"
246
246
  frameborder="0"
247
247
  scrolling="no"
248
248
  />
@@ -134,6 +134,7 @@ export default {
134
134
  },
135
135
 
136
136
  unsubscribe() {
137
+ console.debug('Unsubscribing from all websocket events'); // eslint-disable-line no-console
137
138
  this.$store.dispatch('unsubscribe');
138
139
  },
139
140
  clearAllTimeouts() {
@@ -66,6 +66,7 @@ export default {
66
66
  :height="height"
67
67
  v-bind="$attrs"
68
68
  class="modal"
69
+ data-testid="mvc__card"
69
70
  >
70
71
  <Card
71
72
  class="modal"
@@ -239,10 +239,12 @@ export default {
239
239
  v-for="tab in sortedTabs"
240
240
  :id="tab.name"
241
241
  :key="tab.name"
242
+ :data-testid="tab.name"
242
243
  :class="{tab: true, active: tab.active, disabled: tab.disabled, error: (tab.error)}"
243
244
  role="presentation"
244
245
  >
245
246
  <a
247
+ :data-testid="`btn-${tab.name}`"
246
248
  :aria-controls="'#' + tab.name"
247
249
  :aria-selected="tab.active"
248
250
  role="tab"
@@ -400,7 +400,10 @@ export default {
400
400
  @close="errors.splice(idx, 1)"
401
401
  />
402
402
  </div>
403
- <div class="controls-row pt-20">
403
+ <div
404
+ id="wizard-footer-controls"
405
+ class="controls-row pt-20"
406
+ >
404
407
  <slot
405
408
  name="cancel"
406
409
  :cancel="cancel"
@@ -468,7 +471,6 @@ $spacer: 10px;
468
471
  flex-direction: column;
469
472
  flex: 1;
470
473
  padding: 0;
471
- height: 0;
472
474
  justify-content: flex-start;
473
475
  }
474
476
 
@@ -647,7 +649,6 @@ $spacer: 10px;
647
649
  overflow: hidden;
648
650
  display: flex;
649
651
  flex-direction: column;
650
- height: 100%;
651
652
  flex: 1;
652
653
  }
653
654
  }
@@ -246,7 +246,11 @@ export default {
246
246
  parseLinesFromFile: {
247
247
  default: false,
248
248
  type: Boolean
249
- }
249
+ },
250
+ disabled: {
251
+ default: false,
252
+ type: Boolean
253
+ },
250
254
  },
251
255
  data() {
252
256
  const rows = this.getRows(this.value);
@@ -614,13 +618,14 @@ export default {
614
618
  :keyName="keyName"
615
619
  :valueName="valueName"
616
620
  :queueUpdate="queueUpdate"
621
+ :disabled="disabled"
617
622
  >
618
623
  <Select
619
624
  v-if="keyOptions"
620
625
  ref="key"
621
626
  v-model="row[keyName]"
622
627
  :searchable="true"
623
- :disabled="isProtected(row.key)"
628
+ :disabled="disabled || isProtected(row.key)"
624
629
  :clearable="false"
625
630
  :taggable="keyTaggable"
626
631
  :options="calculateOptions(row[keyName])"
@@ -630,7 +635,7 @@ export default {
630
635
  v-else
631
636
  ref="key"
632
637
  v-model="row[keyName]"
633
- :disabled="isView || !keyEditable || isProtected(row.key)"
638
+ :disabled="isView || disabled || !keyEditable || isProtected(row.key)"
634
639
  :placeholder="keyPlaceholder"
635
640
  @input="queueUpdate"
636
641
  @paste="onPaste(i, $event)"
@@ -672,7 +677,7 @@ export default {
672
677
  v-else-if="valueMultiline"
673
678
  v-model="row[valueName]"
674
679
  :class="{'conceal': valueConcealed}"
675
- :disabled="isProtected(row.key)"
680
+ :disabled="disabled || isProtected(row.key)"
676
681
  :mode="mode"
677
682
  :placeholder="valuePlaceholder"
678
683
  :min-height="40"
@@ -682,7 +687,7 @@ export default {
682
687
  <input
683
688
  v-else
684
689
  v-model="row[valueName]"
685
- :disabled="isView || isProtected(row.key)"
690
+ :disabled="isView || disabled || isProtected(row.key)"
686
691
  :type="valueConcealed ? 'password' : 'text'"
687
692
  :placeholder="valuePlaceholder"
688
693
  autocorrect="off"
@@ -717,7 +722,7 @@ export default {
717
722
  >
718
723
  <button
719
724
  type="button"
720
- :disabled="isView || isProtected(row.key)"
725
+ :disabled="isView || isProtected(row.key) || disabled"
721
726
  class="btn role-link"
722
727
  @click="remove(i)"
723
728
  >
@@ -739,7 +744,7 @@ export default {
739
744
  v-if="addAllowed"
740
745
  type="button"
741
746
  class="btn role-tertiary add"
742
- :disabled="loading || (keyOptions && filteredKeyOptions.length === 0)"
747
+ :disabled="loading || disabled || (keyOptions && filteredKeyOptions.length === 0)"
743
748
  @click="add()"
744
749
  >
745
750
  <i
@@ -95,10 +95,36 @@ export default {
95
95
  const requiredDuringSchedulingIgnoredDuringExecution = { nodeSelectorTerms: [] };
96
96
  const preferredDuringSchedulingIgnoredDuringExecution = [] ;
97
97
 
98
- this.allSelectorTerms.forEach((term) => {
98
+ this.allSelectorTerms.forEach((t) => {
99
+ const term = { ...t };
100
+
101
+ // the 'matching' field isn't part of the affinity spec: including this in the save request will cause a flood of errors that might cause the request to fail
102
+ // same deal with term.preference.weight
103
+ if (term.matchExpressions) {
104
+ term.matchExpressions = (term.matchExpressions || []).map((expression) => {
105
+ const out = { ...expression };
106
+
107
+ delete out.matching;
108
+
109
+ return out;
110
+ });
111
+ }
112
+
113
+ if (term.matchFields) {
114
+ term.matchFields = (term.matchFields || []).map((field) => {
115
+ const out = { ...field };
116
+
117
+ delete out.matching;
118
+
119
+ return out;
120
+ });
121
+ }
122
+
99
123
  if (term.weight) {
100
124
  const neu = { weight: term.weight, preference: term };
101
125
 
126
+ delete neu.preference.weight;
127
+
102
128
  preferredDuringSchedulingIgnoredDuringExecution.push(neu);
103
129
  } else {
104
130
  requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.push(term);
@@ -144,12 +170,8 @@ export default {
144
170
  expressionsMatching[expression.matching || 'matchExpressions'].push(expression);
145
171
  });
146
172
 
147
- if (expressionsMatching.matchFields.length) {
148
- this.$set(row, 'matchFields', expressionsMatching.matchFields);
149
- }
150
- if (expressionsMatching.matchExpressions.length) {
151
- this.$set(row, 'matchExpressions', expressionsMatching.matchExpressions);
152
- }
173
+ this.$set(row, 'matchFields', expressionsMatching.matchFields);
174
+ this.$set(row, 'matchExpressions', expressionsMatching.matchExpressions);
153
175
 
154
176
  this.update();
155
177
  }
@@ -1,4 +1,5 @@
1
1
  <script>
2
+ import { mapGetters } from 'vuex';
2
3
  import { _VIEW } from '@shell/config/query-params';
3
4
  import { get, set, isEmpty, clone } from '@shell/utils/object';
4
5
  import { POD, NODE, NAMESPACE } from '@shell/config/types';
@@ -69,6 +70,11 @@ export default {
69
70
  type: Boolean
70
71
  },
71
72
 
73
+ overwriteLabels: {
74
+ type: Object,
75
+ default: null
76
+ },
77
+
72
78
  loading: {
73
79
  default: false,
74
80
  type: Boolean
@@ -131,6 +137,7 @@ export default {
131
137
  };
132
138
  },
133
139
  computed: {
140
+ ...mapGetters({ t: 'i18n/t' }),
134
141
  isView() {
135
142
  return this.mode === _VIEW;
136
143
  },
@@ -144,7 +151,7 @@ export default {
144
151
  },
145
152
 
146
153
  labeledInputNamespaceLabel() {
147
- return this.removeLabeledInputNamespaceLabel ? '' : this.t('workload.scheduling.affinity.matchExpressions.inNamespaces');
154
+ return this.removeLabeledInputNamespaceLabel ? '' : this.overwriteLabels?.namespaceInputLabel || this.t('workload.scheduling.affinity.matchExpressions.inNamespaces');
148
155
  },
149
156
 
150
157
  allNamespacesOptions() {
@@ -185,6 +192,10 @@ export default {
185
192
  },
186
193
 
187
194
  namespaceSelectionLabels() {
195
+ if (this.overwriteLabels?.namespaceSelectionLabels) {
196
+ return this.overwriteLabels?.namespaceSelectionLabels;
197
+ }
198
+
188
199
  if (this.allNamespacesOptionAvailable) {
189
200
  return [
190
201
  this.t('workload.scheduling.affinity.thisPodNamespace'),
@@ -199,6 +210,14 @@ export default {
199
210
  ];
200
211
  },
201
212
 
213
+ addLabel() {
214
+ return this.overwriteLabels?.addLabel || this.t('podAffinity.addLabel');
215
+ },
216
+
217
+ topologyKeyPlaceholder() {
218
+ return this.overwriteLabels?.topologyKeyPlaceholder || this.t('workload.scheduling.affinity.topologyKey.placeholder');
219
+ },
220
+
202
221
  hasNamespaces() {
203
222
  return this.allNamespacesOptions.length;
204
223
  },
@@ -230,8 +249,9 @@ export default {
230
249
  this.allSelectorTerms.forEach((term) => {
231
250
  if (term._anti) {
232
251
  if (term.weight) {
233
- const neu = { podAffinityTerm: term, weight: term.weight || this.defaultWeight };
252
+ const neu = { podAffinityTerm: { ...term }, weight: term.weight || this.defaultWeight };
234
253
 
254
+ delete neu.podAffinityTerm.weight;
235
255
  podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution.push(neu);
236
256
  } else {
237
257
  podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution.push(term);
@@ -340,7 +360,7 @@ export default {
340
360
  class="mt-20"
341
361
  :default-add-value="defaultAddValue"
342
362
  :mode="mode"
343
- :add-label="t('podAffinity.addLabel')"
363
+ :add-label="addLabel"
344
364
  @remove="remove"
345
365
  >
346
366
  <template #default="props">
@@ -389,7 +409,7 @@ export default {
389
409
  :multiple="true"
390
410
  :taggable="true"
391
411
  :options="allNamespacesOptions"
392
- :label="t('workload.scheduling.affinity.matchExpressions.inNamespaces')"
412
+ :label="labeledInputNamespaceLabel"
393
413
  :data-testid="`pod-affinity-namespace-select-index${props.i}`"
394
414
  @input="updateNamespaces(props.row.value, props.row.value.namespaces)"
395
415
  />
@@ -398,7 +418,7 @@ export default {
398
418
  v-model="props.row.value._namespaces"
399
419
  :mode="mode"
400
420
  :label="labeledInputNamespaceLabel"
401
- :placeholder="t('cluster.credential.harvester.affinity.namespaces.placeholder')"
421
+ :placeholder="t('harvesterManager.affinity.namespaces.placeholder')"
402
422
  :data-testid="`pod-affinity-namespace-input-index${props.i}`"
403
423
  @input="updateNamespaces(props.row.value, props.row.value._namespaces)"
404
424
  />
@@ -424,7 +444,7 @@ export default {
424
444
  :mode="mode"
425
445
  required
426
446
  :label="t('workload.scheduling.affinity.topologyKey.label')"
427
- :placeholder="t('workload.scheduling.affinity.topologyKey.placeholder')"
447
+ :placeholder="topologyKeyPlaceholder"
428
448
  :options="existingNodeLabels"
429
449
  :disabled="mode==='view'"
430
450
  :loading="loading"
@@ -436,7 +456,7 @@ export default {
436
456
  v-model="props.row.value.topologyKey"
437
457
  :mode="mode"
438
458
  :label="t('workload.scheduling.affinity.topologyKey.label')"
439
- :placeholder="t('workload.scheduling.affinity.topologyKey.placeholder')"
459
+ :placeholder="topologyKeyPlaceholder"
440
460
  required
441
461
  :data-testid="`pod-affinity-topology-input-index${props.i}`"
442
462
  @input="update"
@@ -20,6 +20,10 @@ export default {
20
20
  mode: {
21
21
  type: String,
22
22
  default: _VIEW
23
+ },
24
+ disabled: {
25
+ default: false,
26
+ type: Boolean
23
27
  }
24
28
  },
25
29
 
@@ -59,6 +63,7 @@ export default {
59
63
  :extra-columns="['effect']"
60
64
  :preserve-keys="['effect']"
61
65
  :add-label="t('labels.addTaint')"
66
+ :disabled="disabled"
62
67
  >
63
68
  <template #label:effect>
64
69
  {{ t('tableHeaders.effect') }}
@@ -68,6 +73,7 @@ export default {
68
73
  <Select
69
74
  v-model="row.effect"
70
75
  :options="effectOptions"
76
+ :disabled="disabled"
71
77
  class="compact-select"
72
78
  @input="queueUpdate"
73
79
  />
@@ -0,0 +1,74 @@
1
+ <script>
2
+ import { BadgeState } from '@components/BadgeState';
3
+ import { stateDisplay } from '@shell/plugins/dashboard-store/resource-class';
4
+
5
+ export default {
6
+ props: {
7
+ value: {
8
+ type: String,
9
+ default: ''
10
+ }
11
+ },
12
+
13
+ components: { BadgeState },
14
+
15
+ data() {
16
+ const STATES = {
17
+ cached: {
18
+ color: 'info', icon: 'dot-open', label: 'Cached', compoundIcon: 'checkmark'
19
+ },
20
+ pending: {
21
+ color: 'warning', icon: 'tag', label: 'In Progress', compoundIcon: 'info'
22
+ },
23
+ disabled: {
24
+ color: 'error', icon: 'dot-half', label: 'Cache Disabled', compoundIcon: 'info'
25
+ },
26
+ };
27
+
28
+ return {
29
+ STATES,
30
+ stateDisplay: '',
31
+ stateBackground: ''
32
+ };
33
+ },
34
+
35
+ watch: {
36
+ value: {
37
+ handler() {
38
+ const out = this.value || 'pending';
39
+ const color = this.colorForState(out);
40
+
41
+ this.stateDisplay = stateDisplay(out);
42
+ this.stateBackground = color.replace('text-', 'bg-');
43
+ },
44
+ immediate: true
45
+ }
46
+ },
47
+
48
+ methods: {
49
+ colorForState(state) {
50
+ const key = (state).toLowerCase();
51
+ let color;
52
+
53
+ if ( this.STATES[key] && this.STATES[key].color ) {
54
+ color = this.STATES[key].color;
55
+ }
56
+
57
+ if ( !color ) {
58
+ color = 'info';
59
+ }
60
+
61
+ return `text-${ color }`;
62
+ }
63
+ }
64
+ };
65
+ </script>
66
+
67
+ <template>
68
+ <div>
69
+ <BadgeState
70
+ :color="stateBackground"
71
+ :label="stateDisplay"
72
+ />
73
+ </div>
74
+ </template>
@@ -478,6 +478,7 @@ export default {
478
478
 
479
479
  <button
480
480
  v-if="showKubeShell"
481
+ id="btn-kubectl"
481
482
  v-clean-tooltip="t('nav.shellShortcut', {key: shellShortcut})"
482
483
  v-shortkey="{windows: ['ctrl', '`'], mac: ['meta', '`']}"
483
484
  :disabled="!shellEnabled"