@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
@@ -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
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: {
@@ -0,0 +1,46 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Namespace from '@shell/edit/namespace.vue';
3
+ import { DefaultProps } from 'vue/types/options';
4
+ import { ExtendedVue, Vue } from 'vue/types/vue';
5
+
6
+ describe('view Namespace should', () => {
7
+ it('retrieve resource limits from project', () => {
8
+ const name = 'my project name';
9
+ const limits = 'whatever';
10
+ const project = { id: name, spec: { containerDefaultResourceLimit: limits } };
11
+ const wrapper = mount(Namespace as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
12
+ propsData: {
13
+ value: {
14
+ metadata: { labels: { 'field.cattle.io/projectId': name } },
15
+ annotations: { 'field.cattle.io/containerDefaultResourceLimit': undefined },
16
+ listLocation: {},
17
+ }
18
+ },
19
+ mocks: {
20
+ $fetchState: {},
21
+ $route: {
22
+ name: 'anything',
23
+ query: { AS: 'yaml' }
24
+ },
25
+ $router: { applyQuery: {} },
26
+ $store: {
27
+ getters: {
28
+ 'i18n/t': jest.fn(),
29
+ 'management/all': () => ([project]),
30
+ currentProduct: jest.fn(),
31
+ }
32
+ }
33
+ },
34
+ stubs: {
35
+ CruResource: { template: '<div><slot></slot></div>' }, // Required to render the slot content
36
+ ContainerResourceLimit: { template: '<div data-testid="limits"></div>' }, // Ensure value to be added to component
37
+ NameNsDescription: true,
38
+ Tab: true,
39
+ }
40
+ });
41
+
42
+ const limitsUi = wrapper.find('[data-testid="limits"]');
43
+
44
+ expect(limitsUi.vm.$attrs.value).toStrictEqual(limits);
45
+ });
46
+ });
@@ -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
  }
@@ -20,6 +20,7 @@ import CreateEditView from '@shell/mixins/create-edit-view';
20
20
  import jsyaml from 'js-yaml';
21
21
  import { RECEIVERS_TYPES } from '@shell/models/monitoring.coreos.com.receiver';
22
22
  import ButtonDropdown from '@shell/components/ButtonDropdown';
23
+ import { _EDIT, _VIEW } from '@shell/config/query-params';
23
24
 
24
25
  export default {
25
26
  components: {
@@ -43,6 +44,18 @@ export default {
43
44
  data() {
44
45
  this.$set(this.value, 'spec', this.value.spec || {});
45
46
 
47
+ if (this.mode === _EDIT || this.mode === _VIEW) {
48
+ for (let i = 0; i < this.value.spec.email_configs.length; i++) {
49
+ if (this.value.spec.email_configs[i].smarthost) {
50
+ const hostPort = this.value.spec.email_configs[i].smarthost.split(':');
51
+
52
+ this.$set(this.value.spec.email_configs[i], 'host', hostPort[0] || '');
53
+ this.$set(this.value.spec.email_configs[i], 'port', hostPort[1] || '');
54
+ delete this.value.spec.email_configs[i]['smarthost'];
55
+ }
56
+ }
57
+ }
58
+
46
59
  RECEIVERS_TYPES.forEach((receiverType) => {
47
60
  this.$set(this.value.spec, receiverType.key, this.value.spec[receiverType.key] || []);
48
61
  });
@@ -148,8 +161,26 @@ export default {
148
161
 
149
162
  createAddOptions(receiverType) {
150
163
  return receiverType.addOptions.map();
164
+ },
165
+
166
+ createSmarthost() {
167
+ if (this.value.spec.email_configs.length > 0) {
168
+ this.value.spec.email_configs.forEach((email) => {
169
+ if (email['port'] || email['host']) {
170
+ const hostValue = email.host ? `${ email.host }` : '';
171
+
172
+ email.smarthost = email.port ? `${ hostValue }:${ email.port }` : `${ hostValue }`;
173
+ delete email['port'];
174
+ delete email['host'];
175
+ }
176
+ });
177
+ }
151
178
  }
152
- }
179
+ },
180
+
181
+ created() {
182
+ this.registerBeforeHook(this.createSmarthost, 'create-smarthost');
183
+ },
153
184
  };
154
185
  </script>
155
186
 
@@ -73,15 +73,23 @@ export default {
73
73
  </div>
74
74
  </div>
75
75
  <div class="row mb-20">
76
- <div class="col span-6">
76
+ <div class="col span-4">
77
77
  <LabeledInput
78
- v-model="value.smarthost"
78
+ v-model="value.host"
79
79
  :mode="mode"
80
80
  label="Host"
81
- placeholder="e.g. 192.168.1.121:587"
81
+ placeholder="e.g. 192.168.1.121"
82
82
  />
83
83
  </div>
84
- <div class="col span-6">
84
+ <div class="col span-4">
85
+ <LabeledInput
86
+ v-model="value.port"
87
+ :mode="mode"
88
+ label="Port"
89
+ placeholder="e.g. 80"
90
+ />
91
+ </div>
92
+ <div class="col span-4">
85
93
  <Checkbox
86
94
  v-model="value.require_tls"
87
95
  :mode="mode"