@rancher/shell 0.3.10 → 0.3.12

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 (96) 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/DetailText.vue +1 -0
  7. package/components/DetailTop.vue +0 -2
  8. package/components/ExplorerMembers.vue +22 -10
  9. package/components/ExplorerProjectsNamespaces.vue +1 -0
  10. package/components/GrafanaDashboard.vue +2 -2
  11. package/components/Inactivity.vue +1 -0
  12. package/components/ModalWithCard.vue +1 -0
  13. package/components/ResourceList/index.vue +1 -1
  14. package/components/SortableTable/THead.vue +1 -0
  15. package/components/Tabbed/index.vue +2 -0
  16. package/components/Wizard.vue +4 -3
  17. package/components/form/ChangePassword.vue +3 -0
  18. package/components/form/KeyValue.vue +12 -7
  19. package/components/form/NodeAffinity.vue +29 -7
  20. package/components/form/PodAffinity.vue +27 -7
  21. package/components/form/Taints.vue +6 -0
  22. package/components/formatter/ExtensionCache.vue +74 -0
  23. package/components/nav/Header.vue +1 -0
  24. package/components/nav/WindowManager/ContainerShell.vue +10 -0
  25. package/components/nav/WindowManager/index.vue +1 -0
  26. package/config/product/explorer.js +1 -10
  27. package/config/product/monitoring.js +2 -1
  28. package/config/router.js +3 -3
  29. package/config/table-headers.js +32 -24
  30. package/config/uiplugins.js +11 -0
  31. package/config/workload.ts +1 -0
  32. package/core/types.ts +25 -7
  33. package/creators/pkg/files/.github/workflows/build-container.yml +64 -0
  34. package/creators/pkg/init +13 -6
  35. package/detail/helm.cattle.io.projecthelmchart.vue +2 -2
  36. package/detail/node.vue +2 -2
  37. package/detail/workload/index.vue +1 -1
  38. package/edit/__tests__/management.cattle.io.setting.test.ts +1 -1
  39. package/edit/autoscaling.horizontalpodautoscaler/metric-target.vue +0 -2
  40. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +43 -0
  41. package/edit/logging.banzaicloud.io.output/index.vue +8 -5
  42. package/edit/logging.banzaicloud.io.output/providers/__tests__/loki.test.ts +13 -0
  43. package/edit/logging.banzaicloud.io.output/providers/loki.vue +1 -0
  44. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +0 -2
  45. package/edit/monitoring.coreos.com.receiver/index.vue +32 -1
  46. package/edit/monitoring.coreos.com.receiver/types/email.vue +12 -4
  47. package/edit/namespace.vue +1 -0
  48. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +36 -6
  49. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +2 -2
  50. package/edit/provisioning.cattle.io.cluster/rke2.vue +58 -13
  51. package/edit/token.vue +1 -0
  52. package/middleware/authenticated.js +1 -0
  53. package/models/__tests__/batch.cronjob.test.ts +88 -0
  54. package/models/cluster/node.js +8 -0
  55. package/models/management.cattle.io.clusterroletemplatebinding.js +5 -1
  56. package/models/projectroletemplatebinding.js +9 -1
  57. package/models/workload.js +1 -1
  58. package/package.json +1 -1
  59. package/pages/__tests__/prefs.test.ts +96 -0
  60. package/pages/account/index.vue +3 -0
  61. package/pages/auth/setup.vue +13 -13
  62. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  63. package/pages/c/_cluster/apps/charts/install.vue +5 -2
  64. package/pages/c/_cluster/monitoring/index.vue +10 -5
  65. package/pages/c/_cluster/settings/performance.vue +2 -0
  66. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +601 -0
  67. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +183 -0
  68. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +50 -9
  69. package/pages/c/_cluster/uiplugins/index.vue +329 -224
  70. package/pages/fail-whale.vue +1 -1
  71. package/pages/home.vue +11 -0
  72. package/pages/prefs.vue +20 -1
  73. package/plugins/plugin.js +1 -1
  74. package/plugins/steve/subscribe.js +34 -7
  75. package/plugins/steve/worker/index.js +4 -3
  76. package/public/index.html +6 -1
  77. package/rancher-components/components/Card/Card.vue +1 -0
  78. package/rancher-components/components/Form/Radio/RadioGroup.vue +1 -0
  79. package/scripts/extension/bundle +20 -4
  80. package/scripts/extension/helm/charts/ui-plugin-server/.helmignore +23 -0
  81. package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +20 -0
  82. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +52 -0
  83. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +12 -0
  84. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +6 -0
  85. package/scripts/extension/helm/package/Dockerfile +27 -0
  86. package/scripts/extension/helm/package/nginx.conf +17 -0
  87. package/scripts/extension/helm/scripts/package +23 -0
  88. package/scripts/extension/helm/scripts/patch +101 -0
  89. package/scripts/extension/helm/scripts/version +31 -0
  90. package/scripts/extension/helmpatch +3 -25
  91. package/scripts/extension/publish +47 -32
  92. package/types/shell/index.d.ts +30 -24
  93. package/utils/__tests__/grafana.test.ts +2 -2
  94. package/utils/error.js +11 -0
  95. package/utils/grafana.js +5 -4
  96. package/vue.config.js +3 -17
@@ -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"
@@ -61,6 +61,12 @@ export default {
61
61
  type: String,
62
62
  default: null,
63
63
  },
64
+
65
+ // Runs this command immediately after connecting
66
+ commandOnFirstConnect: {
67
+ type: String,
68
+ default: null
69
+ }
64
70
  },
65
71
 
66
72
  data() {
@@ -267,6 +273,10 @@ export default {
267
273
  this.isOpening = false;
268
274
  this.fit();
269
275
  this.flush();
276
+
277
+ if (this.commandOnFirstConnect) {
278
+ this.terminal.paste(`${ this.commandOnFirstConnect }`);
279
+ }
270
280
  });
271
281
 
272
282
  this.socket.addEventListener(EVENT_DISCONNECTED, (e) => {
@@ -307,6 +307,7 @@ export default {
307
307
  <template>
308
308
  <div
309
309
  id="windowmanager"
310
+ data-testid="windowmanager"
310
311
  class="windowmanager"
311
312
  :class="{[pinClass]: true}"
312
313
  >
@@ -18,7 +18,7 @@ import {
18
18
  STORAGE_CLASS_PROVISIONER, PERSISTENT_VOLUME_SOURCE,
19
19
  HPA_REFERENCE, MIN_REPLICA, MAX_REPLICA, CURRENT_REPLICA,
20
20
  ACCESS_KEY, DESCRIPTION, EXPIRES, EXPIRY_STATE, SUB_TYPE, AGE_NORMAN, SCOPE_NORMAN, PERSISTENT_VOLUME_CLAIM, RECLAIM_POLICY, PV_REASON, WORKLOAD_HEALTH_SCALE, POD_RESTARTS,
21
- DURATION, MESSAGE, REASON, LAST_SEEN_TIME, EVENT_TYPE, OBJECT, RBAC_GROUPS, RBAC_USERS, RBAC_SERVICE_ACCOUNTS, ROLE,
21
+ DURATION, MESSAGE, REASON, LAST_SEEN_TIME, EVENT_TYPE, OBJECT, ROLE,
22
22
  } from '@shell/config/table-headers';
23
23
 
24
24
  import { DSL } from '@shell/store/type-map';
@@ -227,15 +227,6 @@ export function init(store) {
227
227
  AGE,
228
228
  ]);
229
229
 
230
- headers(RBAC.CLUSTER_ROLE_BINDING, [
231
- STATE,
232
- NAME_COL,
233
- RBAC_GROUPS,
234
- RBAC_USERS,
235
- RBAC_SERVICE_ACCOUNTS, // groupname //roles // users// service accounts
236
- AGE
237
- ]);
238
-
239
230
  configureType(MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING, {
240
231
  listGroups: [
241
232
  {
@@ -79,7 +79,8 @@ export function init(store) {
79
79
  to: { type: 'string' },
80
80
  send_resolved: { type: 'boolean' },
81
81
  from: { type: 'string' },
82
- smarthost: { type: 'string' },
82
+ host: { type: 'string' },
83
+ port: { type: 'string' },
83
84
  require_tls: { type: 'boolean' },
84
85
  auth_username: { type: 'string' },
85
86
  auth_password: { type: 'string' }
package/config/router.js CHANGED
@@ -10,8 +10,8 @@ Vue.use(Router);
10
10
 
11
11
  export const routerOptions = {
12
12
  mode: 'history',
13
- // Note: router base comes from the VUE_APP_ROUTER_BASE env var
14
- base: process.env.VUE_APP_ROUTER_BASE || '/',
13
+ // Note: router base comes from the ROUTER_BASE env var
14
+ base: process.env.routerBase || '/',
15
15
  linkActiveClass: 'nuxt-link-active',
16
16
  linkExactActiveClass: 'nuxt-link-exact-active',
17
17
  scrollBehavior,
@@ -230,7 +230,7 @@ export const routerOptions = {
230
230
  name: 'c-cluster-settings-brand'
231
231
  }, {
232
232
  path: '/c/:cluster/settings/DefaultLinksEditor',
233
- component: () => interopDefault(import('../pages/c/_cluster/settings/DefaultLinksEditor.vue' /* webpackChunkName: "shell/pages/c/_cluster/settings/DefaultLinksEditor" */)),
233
+ component: () => interopDefault(import('../pages/c/_cluster/settings/DefaultLinksEditor.vue' /* webpackChunkName: "pages/c/_cluster/settings/DefaultLinksEditor" */)),
234
234
  name: 'c-cluster-settings-DefaultLinksEditor'
235
235
  }, {
236
236
  path: '/c/:cluster/settings/links',
@@ -970,27 +970,35 @@ export const FLEET_BUNDLE_TYPE = {
970
970
  width: 100,
971
971
  };
972
972
 
973
- // export const RBAC_ROLE = {
974
- // name: 'role',
975
- // value: 'roleRef.name',
976
- // sort: ['roleRef.name'],
977
- // };
978
-
979
- export const RBAC_USERS = {
980
- name: 'users',
981
- sort: ['users'],
982
- getValue: row => row.users?.map(({ name }) => name),
983
- };
984
-
985
- export const RBAC_GROUPS = {
986
- name: 'groups',
987
- sort: ['groups'],
988
-
989
- getValue: row => row.groups?.map(({ name }) => name),
990
- };
991
-
992
- export const RBAC_SERVICE_ACCOUNTS = {
993
- name: 'serviceAccount',
994
- sort: ['serviceAccount'],
995
- getValue: row => row.serviceAccounts?.map(({ name }) => name),
996
- };
973
+ export const UI_PLUGIN_CATALOG = [
974
+ {
975
+ name: 'state',
976
+ labelKey: 'tableHeaders.state',
977
+ sort: ['stateSort', 'nameSort'],
978
+ value: 'state',
979
+ width: 100,
980
+ default: 'unknown',
981
+ formatter: 'BadgeStateFormatter',
982
+ formatterOpts: { arbitrary: true }
983
+ },
984
+ {
985
+ name: 'name',
986
+ labelKey: 'tableHeaders.name',
987
+ value: 'name',
988
+ sort: ['nameSort'],
989
+ formatter: 'LinkDetail'
990
+ },
991
+ {
992
+ name: 'image',
993
+ sort: ['image'],
994
+ labelKey: 'plugins.manageCatalog.headers.image.label',
995
+ value: 'deploymentImage'
996
+ },
997
+ {
998
+ name: 'cacheState',
999
+ sort: ['cacheState'],
1000
+ labelKey: 'plugins.manageCatalog.headers.cacheState.label',
1001
+ value: 'cacheState',
1002
+ formatter: 'ExtensionCache'
1003
+ }
1004
+ ];
@@ -38,6 +38,13 @@ export const UI_PLUGIN_CHART_ANNOTATIONS = {
38
38
  DISPLAY_NAME: 'catalog.cattle.io/display-name',
39
39
  };
40
40
 
41
+ // Extension catalog labels
42
+ export const UI_PLUGIN_LABELS = {
43
+ CATALOG_IMAGE: 'catalog.cattle.io/ui-extensions-catalog-image',
44
+ REPOSITORY: 'catalog.cattle.io/ui-extensions-repository',
45
+ CATALOG: 'catalog.cattle.io/ui-extensions-catalog'
46
+ };
47
+
41
48
  // Plugin Metadata properties
42
49
  export const UI_PLUGIN_METADATA = {
43
50
  RANCHER_VERSION: 'rancherVersion',
@@ -99,6 +106,10 @@ export function shouldNotLoadPlugin(plugin, rancherVersion) {
99
106
  }
100
107
  }
101
108
 
109
+ if (plugin.metadata?.[UI_PLUGIN_LABELS.CATALOG]) {
110
+ return true;
111
+ }
112
+
102
113
  return false;
103
114
  }
104
115
 
@@ -0,0 +1 @@
1
+ export const SEPARATOR = { separator: true };
package/core/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ProductFunction } from './plugin';
2
- import { RouteConfig } from 'vue-router';
2
+ import { RouteConfig, Location } from 'vue-router';
3
3
 
4
4
  // package.json metadata
5
5
  export interface PackageMetadata {
@@ -213,6 +213,11 @@ export interface ProductOptions {
213
213
  */
214
214
  weight?: number;
215
215
 
216
+ /**
217
+ * The route that the product will lead to if click on in navigation.
218
+ */
219
+ to?: Location;
220
+
216
221
  /**
217
222
  * Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
218
223
  */
@@ -220,7 +225,6 @@ export interface ProductOptions {
220
225
  // removable: string;
221
226
  // showWorkspaceSwitcher: boolean;
222
227
  // supportRoute: string;
223
- // to: string;
224
228
  // typeStoreMap: string;
225
229
  }
226
230
 
@@ -231,7 +235,12 @@ export interface HeaderOptions {
231
235
  name?: string;
232
236
 
233
237
  /**
234
- * A translation key where the resulting string will show in the table column
238
+ * A string that will show in the table column as a header
239
+ */
240
+ label?: string;
241
+
242
+ /**
243
+ * A translation key where the resulting string will show in the table column as a header
235
244
  */
236
245
  labelKey?: string;
237
246
 
@@ -243,12 +252,13 @@ export interface HeaderOptions {
243
252
  /**
244
253
  * A string which represents the path to access the value from the row object which we'll use to sort i.e. `row.meta.value`
245
254
  */
246
- sort?: string;
255
+ sort?: string | string[];
247
256
 
248
257
  /**
249
- * A string which represents the path to access the value from the row object which we'll use to search i.e. `row.meta.value`
258
+ * A string which represents the path to access the value from the row object which we'll use to search i.e. `row.meta.value`.
259
+ * It can be false to disable searching on this field
250
260
  */
251
- search?: string;
261
+ search?: string | boolean;
252
262
 
253
263
  /**
254
264
  * Number of pixels the column should be in the table
@@ -368,6 +378,14 @@ export interface DSLReturnType {
368
378
  */
369
379
  product: (options: ProductOptions) => void;
370
380
 
381
+ /**
382
+ * Create and label a group. The group will show up in navigation
383
+ * @param groupNane Name of the group
384
+ * @param label Label in navigation
385
+ * @returns {@link void}
386
+ */
387
+ mapGroup: (groupName: string, label: string) => void;
388
+
371
389
  /**
372
390
  * Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
373
391
  */
@@ -376,7 +394,7 @@ export interface DSLReturnType {
376
394
  // hideBulkActions: (type: string, field)
377
395
  // ignoreGroup: (regexOrString)
378
396
  // ignoreType: (regexOrString)
379
- // mapGroup: (match, replace)
397
+ //
380
398
  // mapType: (match, replace)
381
399
  // moveType: (match, group)
382
400
  // setGroupDefaultType: (input, defaultType)
@@ -0,0 +1,64 @@
1
+ name: Build and release container to registry
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ env:
9
+ REGISTRY: ghcr.io
10
+ IMAGE_NAME: ${{ github.repository }}
11
+
12
+ jobs:
13
+ build:
14
+ name: Build container image
15
+ if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
16
+ runs-on: ubuntu-latest
17
+ permissions: write-all
18
+
19
+ steps:
20
+ - name: Checkout repository
21
+ uses: actions/checkout@v3
22
+
23
+ - name: Configure Git
24
+ run: |
25
+ git config user.name "${{ github.actor }}"
26
+ git config user.email "${{ github.actor }}@users.noreply.github.com"
27
+
28
+ - name: Login to GitHub Container Registry
29
+ uses: docker/login-action@v2
30
+ with:
31
+ registry: ${{ env.REGISTRY }}
32
+ username: ${{ github.actor }}
33
+ password: ${{ secrets.GITHUB_TOKEN }}
34
+
35
+ - name: Setup Helm
36
+ uses: azure/setup-helm@v3
37
+ with:
38
+ version: v3.8.0
39
+
40
+ - name: Setup yq
41
+ uses: chrisdickinson/setup-yq@v1.0.1
42
+ with:
43
+ yq-version: v4.28.2
44
+
45
+ - name: Setup Nodejs and npm
46
+ uses: actions/setup-node@v3
47
+ with:
48
+ node-version: '16'
49
+
50
+ - name: Setup yarn
51
+ run: npm install -g yarn
52
+
53
+ - name: Setup Nodejs with yarn caching
54
+ uses: actions/setup-node@v3
55
+ with:
56
+ node-version: '16'
57
+ cache: yarn
58
+
59
+ - name: Install dependencies
60
+ run: yarn
61
+
62
+ - name: Build and push UI image
63
+ run: |
64
+ yarn publish-pkgs -cp -r ${{ env.REGISTRY }} -o ${{ github.repository_owner }}
package/creators/pkg/init CHANGED
@@ -114,14 +114,21 @@ if (addWorkflowFolder) {
114
114
  fs.mkdirSync(workflowDir, { recursive: true });
115
115
  }
116
116
 
117
- const workflowFile = path.join(workflowDir, 'build-extension.yml');
117
+ const files = [
118
+ 'build-extension.yml',
119
+ 'build-container.yml'
120
+ ];
118
121
 
119
- if (!fs.existsSync(workflowFile)) {
120
- const src = path.join(__dirname, 'files/.github/workflows', 'build-extension.yml');
122
+ files.forEach((fileName) => {
123
+ const file = path.join(workflowDir, fileName);
121
124
 
122
- console.log(` Adding file 'build-extension.yml' to root workflows`);
123
- fs.copySync(src, workflowFile);
124
- }
125
+ if (!fs.existsSync(file)) {
126
+ const src = path.join(__dirname, 'files/.github/workflows', fileName);
127
+
128
+ console.log(` Adding file ${ fileName } to root workflows`);
129
+ fs.copySync(src, file);
130
+ }
131
+ });
125
132
  }
126
133
 
127
134
  // Copy base files
@@ -43,8 +43,8 @@ export default {
43
43
  };
44
44
  },
45
45
  monitoringNamespace() {
46
- // arbitrarily picking the alertmanagerURL here, they're all going to be the same.
47
- return this.pullKeyFromUrl(this.relativeDashboardValues.alertmanagerURL, 'namespaces');
46
+ // picking the prometheusURL here, they're all going to be the same, but alertmanager and grafana can be deactivated
47
+ return this.pullKeyFromUrl(this.relativeDashboardValues.prometheusURL, 'namespaces');
48
48
  },
49
49
  alertServiceEndpoint() {
50
50
  return this.pullServiceEndpointFromUrl(this.relativeDashboardValues.alertmanagerURL);
package/detail/node.vue CHANGED
@@ -116,7 +116,7 @@ export default {
116
116
  return null;
117
117
  },
118
118
  memoryUnits() {
119
- const exponent = exponentNeeded(this.value.ramCapacity, 1024);
119
+ const exponent = exponentNeeded(this.value.ramReserved, 1024);
120
120
 
121
121
  return `${ UNITS[exponent] }iB`;
122
122
  },
@@ -231,7 +231,7 @@ export default {
231
231
  />
232
232
  <ConsumptionGauge
233
233
  :resource-name="t('node.detail.glance.consumptionGauge.memory')"
234
- :capacity="value.ramCapacity"
234
+ :capacity="value.ramReserved"
235
235
  :used="value.ramUsage"
236
236
  :units="memoryUnits"
237
237
  :number-formatter="memoryFormatter"
@@ -15,7 +15,7 @@ import V1WorkloadMetrics from '@shell/mixins/v1-workload-metrics';
15
15
  import { mapGetters } from 'vuex';
16
16
  import { allDashboardsExist } from '@shell/utils/grafana';
17
17
  import PlusMinus from '@shell/components/form/PlusMinus';
18
- import { matches } from '~shell/utils/selector';
18
+ import { matches } from '@shell/utils/selector';
19
19
 
20
20
  const SCALABLE_TYPES = Object.values(SCALABLE_WORKLOAD_TYPES);
21
21
  const WORKLOAD_METRICS_DETAIL_URL = '/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-grafana:80/proxy/d/rancher-workload-pods-1/rancher-workload-pods?orgId=1';
@@ -2,7 +2,7 @@ import { mount } from '@vue/test-utils';
2
2
  import Settings from '@shell/edit/management.cattle.io.setting.vue';
3
3
  import { SETTING } from '@shell/config/settings';
4
4
 
5
- describe('management.cattle.io.setting should', () => {
5
+ describe('view: management.cattle.io.setting should', () => {
6
6
  const requiredSetup = () => ({
7
7
  // Remove all these mocks after migration to Vue 2.7/3 due mixin logic
8
8
  mocks: {
@@ -72,7 +72,6 @@ export default {
72
72
 
73
73
  watch: {
74
74
  resourceName(newRn, _oldRn) {
75
- // debugger;
76
75
  const {
77
76
  value: { type: metricType },
78
77
  targetTypes,
@@ -93,7 +92,6 @@ export default {
93
92
  },
94
93
 
95
94
  'value.type'(targetType, oldType) {
96
- // debugger;
97
95
  const { targetTypes, resourceName } = this;
98
96
  const toDelete = findBy(targetTypes, { value: oldType });
99
97
  const nue = findBy(targetTypes, { value: targetType });
@@ -0,0 +1,43 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Banzai from '@shell/edit/logging.banzaicloud.io.output/index.vue';
3
+
4
+ describe('view: logging.banzaicloud.io.output', () => {
5
+ it.each([
6
+ ['http://localhost:3100', []],
7
+ ['not a proper URL', ['logging.loki.urlInvalid']],
8
+ ])('should validate Loki URL on save', (url, expectation) => {
9
+ const wrapper = mount(Banzai, {
10
+ data: () => ({ selectedProvider: 'loki' }),
11
+ propsData: {
12
+ value: {
13
+ save: jest.fn(),
14
+ spec: { loki: { url } }
15
+ }
16
+ },
17
+ mocks: {
18
+ $store: {
19
+ getters: {
20
+ currentStore: () => 'current_store',
21
+ 'management/schemaFor': jest.fn(),
22
+ 'current_store/all': jest.fn(),
23
+ 'current_store/schemaFor': jest.fn(),
24
+ 'cluster/all': jest.fn(),
25
+ 'type-map/isSpoofed': jest.fn(),
26
+ 'i18n/t': jest.fn().mockImplementation((key: string) => key),
27
+ namespaces: () => ({}),
28
+ }
29
+ },
30
+ $route: {
31
+ name: 'whatever',
32
+ query: { AS: '' }
33
+ },
34
+ $router: { replace: jest.fn() },
35
+ }
36
+ });
37
+ const fakeDone = jest.fn();
38
+
39
+ (wrapper.vm as any).saveSettings(fakeDone);
40
+
41
+ expect((wrapper.vm as any).errors).toStrictEqual(expectation);
42
+ });
43
+ });
@@ -70,7 +70,7 @@ export default {
70
70
  // I'm manipulating the output since I'm not sure it's something we want to actually support
71
71
  // seeing as it's really createResourceYaml and this here is a gray area between spoofed types
72
72
  // and just a field within a spec.
73
- bufferYaml = bufferYaml.substring(bufferYaml.indexOf('\n') + 1).replaceAll('# ', '#');
73
+ bufferYaml = bufferYaml.substring(bufferYaml.indexOf('\n') + 1).replace(/# {2}/g, '#');
74
74
  }
75
75
 
76
76
  return {
@@ -78,7 +78,7 @@ export default {
78
78
  initialBufferYaml: bufferYaml,
79
79
  providers,
80
80
  selectedProvider,
81
- hasMultipleProvidersSelected: selectedProviders.length > 1,
81
+ hasMultipleProvidersSelected: selectedProviders?.length > 1,
82
82
  selectedProviders,
83
83
  LOGGING
84
84
  };
@@ -91,8 +91,11 @@ export default {
91
91
  enabledProviders() {
92
92
  return this.providers.filter(p => p.enabled);
93
93
  },
94
+ isNamespaced() {
95
+ return this.value.type !== LOGGING?.CLUSTER_OUTPUT;
96
+ },
94
97
  cruMode() {
95
- if (this.selectedProviders.length > 1 || !this.value.allProvidersSupported) {
98
+ if (this.hasMultipleProvidersSelected || !this.value.allProvidersSupported) {
96
99
  return _VIEW;
97
100
  }
98
101
 
@@ -173,10 +176,10 @@ export default {
173
176
  :mode="mode"
174
177
  label="generic.name"
175
178
  :register-before-hook="registerBeforeHook"
176
- :namespaced="value.type !== LOGGING.CLUSTER_OUTPUT"
179
+ :namespaced="isNamespaced"
177
180
  />
178
181
  <Banner
179
- v-if="selectedProviders.length > 1"
182
+ v-if="hasMultipleProvidersSelected"
180
183
  color="info"
181
184
  >
182
185
  This output is configured with multiple providers. We currently only support a single provider per output. You can view or edit the YAML.
@@ -0,0 +1,13 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import Loki from '@shell/edit/logging.banzaicloud.io.output/providers/loki.vue';
3
+
4
+ describe('component: Loki', () => {
5
+ it('should display URL placeholder', () => {
6
+ const wrapper = shallowMount(Loki, { propsData: { mode: 'edit', namespace: 'whatever' } });
7
+ const url = 'https://127.0.0.1:8000';
8
+
9
+ const placeholder = wrapper.find('[data-testid="loki-url"]').attributes('placeholder');
10
+
11
+ expect(placeholder).toBe(url);
12
+ });
13
+ });
@@ -48,6 +48,7 @@ export default {
48
48
  :disabled="disabled"
49
49
  class="url"
50
50
  placeholder="https://127.0.0.1:8000"
51
+ data-testid="loki-url"
51
52
  :label="t('logging.loki.url')"
52
53
  />
53
54
  </div>
@@ -122,8 +122,6 @@ export default {
122
122
  */
123
123
  const receiverSchema = this.$store.getters['cluster/schemaFor'](MONITORING.SPOOFED.ALERTMANAGERCONFIG_RECEIVER_SPEC);
124
124
 
125
- // debugger;
126
-
127
125
  if (!receiverSchema) {
128
126
  throw new Error("Can't render the form because the AlertmanagerConfig schema is not loaded yet.");
129
127
  }