@rancher/shell 0.3.21 → 0.3.23

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 (60) hide show
  1. package/assets/translations/en-us.yaml +4 -0
  2. package/assets/translations/zh-hans.yaml +8 -1
  3. package/babel.config.js +3 -0
  4. package/cloud-credential/__tests__/azure.test.ts +53 -0
  5. package/cloud-credential/azure.vue +6 -0
  6. package/components/GrowlManager.vue +33 -30
  7. package/components/SortableTable/paging.js +10 -0
  8. package/components/form/GitPicker.vue +16 -0
  9. package/components/form/ResourceQuota/ProjectRow.vue +38 -15
  10. package/components/form/SelectOrCreateAuthSecret.vue +9 -3
  11. package/components/formatter/ClusterProvider.vue +9 -3
  12. package/components/formatter/__tests__/ClusterProvider.test.ts +5 -1
  13. package/components/nav/Header.vue +1 -0
  14. package/config/settings.ts +59 -2
  15. package/config/types.js +2 -0
  16. package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +28 -0
  17. package/creators/pkg/files/.github/workflows/build-extension-charts.yml +26 -0
  18. package/creators/pkg/init +63 -4
  19. package/detail/provisioning.cattle.io.cluster.vue +4 -2
  20. package/edit/fleet.cattle.io.gitrepo.vue +1 -0
  21. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -4
  22. package/edit/resources.cattle.io.backup.vue +3 -1
  23. package/edit/resources.cattle.io.restore.vue +3 -1
  24. package/mixins/__tests__/chart.test.ts +40 -0
  25. package/mixins/chart.js +5 -0
  26. package/models/catalog.cattle.io.clusterrepo.js +6 -2
  27. package/models/fleet.cattle.io.cluster.js +10 -2
  28. package/package.json +1 -1
  29. package/pages/c/_cluster/gatekeeper/index.vue +10 -1
  30. package/plugins/steve/__tests__/header-warnings.spec.ts +238 -0
  31. package/plugins/steve/actions.js +4 -23
  32. package/plugins/steve/header-warnings.ts +91 -0
  33. package/promptRemove/management.cattle.io.project.vue +9 -6
  34. package/rancher-components/BadgeState/BadgeState.vue +1 -5
  35. package/rancher-components/Banner/Banner.test.ts +1 -51
  36. package/rancher-components/Banner/Banner.vue +53 -134
  37. package/rancher-components/Card/Card.vue +7 -24
  38. package/rancher-components/Form/Checkbox/Checkbox.test.ts +29 -20
  39. package/rancher-components/Form/Checkbox/Checkbox.vue +20 -45
  40. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +8 -2
  41. package/rancher-components/Form/LabeledInput/LabeledInput.vue +10 -22
  42. package/rancher-components/Form/Radio/RadioButton.vue +13 -30
  43. package/rancher-components/Form/Radio/RadioGroup.vue +7 -26
  44. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  45. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +38 -25
  46. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +11 -23
  47. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +5 -19
  48. package/rancher-components/StringList/StringList.test.ts +49 -453
  49. package/rancher-components/StringList/StringList.vue +58 -92
  50. package/scripts/extension/parse-tag-name +30 -0
  51. package/types/shell/index.d.ts +16 -9
  52. package/utils/__tests__/formatter.test.ts +77 -0
  53. package/utils/formatter.js +11 -0
  54. package/utils/settings.ts +2 -17
  55. package/vue-config-helper.js +135 -0
  56. package/vue.config.js +29 -141
  57. package/creators/pkg/files/.github/workflows/build-container.yml +0 -64
  58. package/creators/pkg/files/.github/workflows/build-extension.yml +0 -110
  59. package/rancher-components/Card/Card.test.ts +0 -37
  60. package/rancher-components/Form/Radio/RadioButton.test.ts +0 -31
package/creators/pkg/init CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
+ const https = require('https');
5
6
 
6
7
  const targets = {
7
8
  dev: './node_modules/.bin/nuxt dev',
@@ -16,9 +17,10 @@ const files = [
16
17
  ];
17
18
 
18
19
  const topLevelScripts = {
19
- 'build-pkg': './node_modules/@rancher/shell/scripts/build-pkg.sh',
20
- 'serve-pkgs': './node_modules/@rancher/shell/scripts/serve-pkgs',
21
- 'publish-pkgs': './node_modules/@rancher/shell/scripts/extension/publish',
20
+ 'build-pkg': './node_modules/@rancher/shell/scripts/build-pkg.sh',
21
+ 'serve-pkgs': './node_modules/@rancher/shell/scripts/serve-pkgs',
22
+ 'publish-pkgs': './node_modules/@rancher/shell/scripts/extension/publish',
23
+ 'parse-tag-name': './node_modules/@rancher/shell/scripts/extension/parse-tag-name'
22
24
  };
23
25
 
24
26
  const typeFolders = [
@@ -88,11 +90,68 @@ Object.keys(targets).forEach((target) => {
88
90
  }
89
91
  });
90
92
 
93
+ // Add annotation for the latest Rancher version by default
94
+ function fetchLatestVersion() {
95
+ console.log(' Fetching latest Rancher Version');
96
+ const options = { headers: { 'User-Agent': 'nodejs' } };
97
+
98
+ https.get('https://api.github.com/repos/rancher/rancher/releases/latest', options, (res) => {
99
+ const { statusCode } = res;
100
+ const contentType = res.headers['content-type'];
101
+
102
+ let error;
103
+
104
+ if ( statusCode !== 200 ) {
105
+ error = new Error(' Request Failed.\n' +
106
+ ` Status Code: ${ statusCode }`);
107
+ } else if ( !/^application\/json/.test(contentType) ) {
108
+ error = new Error(' Invalid content-type.\n' +
109
+ ` Expected application/json but received ${ contentType }`);
110
+ }
111
+
112
+ if ( error ) {
113
+ console.log(error.message);
114
+
115
+ res.resume();
116
+
117
+ return;
118
+ }
119
+
120
+ res.setEncoding('utf8');
121
+ let rawData = '';
122
+
123
+ res.on('data', (chunk) => {
124
+ rawData += chunk;
125
+ });
126
+ res.on('end', () => {
127
+ try {
128
+ const release = JSON.parse(rawData);
129
+
130
+ if ( release.tag_name ) {
131
+ console.log(` Adding rancher-version annotation '>= ${ release.tag_name }' to package.json`);
132
+
133
+ pkg.rancher = { annotations: { 'catalog.cattle.io/rancher-version': `>= ${ release.tag_name }` } };
134
+ writePackageJson();
135
+ }
136
+ } catch (e) {
137
+ console.log(' Error parsing release data', e);
138
+ }
139
+ });
140
+ }).on('error', (e) => {
141
+ console.log(' Error fetching latest Rancher Version', e);
142
+ });
143
+ }
144
+
145
+ fetchLatestVersion();
146
+ writePackageJson();
147
+
91
148
  // Add dependencies
92
149
  // pkg.dependencies['@rancher/shell'] = '^0.6.2';
93
150
  // pkg.dependencies['core-js'] = '3.18.3';
94
151
 
95
- fs.writeFileSync(path.join(pkgFolder, 'package.json'), JSON.stringify(pkg, null, 2));
152
+ function writePackageJson() {
153
+ fs.writeFileSync(path.join(pkgFolder, 'package.json'), JSON.stringify(pkg, null, 2));
154
+ }
96
155
 
97
156
  // Create type folders if needed
98
157
  if (addTypeFolders) {
@@ -250,8 +250,10 @@ export default {
250
250
 
251
251
  computed: {
252
252
  defaultTab() {
253
- if (this.showRegistration && !this.machines?.length) {
254
- return 'registration';
253
+ if (this.showRegistration) {
254
+ if (this.value.isRke2 ? !this.machines?.length : !this.nodes?.length) {
255
+ return 'registration';
256
+ }
255
257
  }
256
258
 
257
259
  if (this.showMachines) {
@@ -569,6 +569,7 @@ export default {
569
569
  />
570
570
 
571
571
  <SelectOrCreateAuthSecret
572
+ data-testid="gitrepo-helm-auth"
572
573
  :value="value.spec.helmSecretName"
573
574
  :register-before-hook="registerBeforeHook"
574
575
  :namespace="value.metadata.namespace"
@@ -792,10 +792,10 @@ export default {
792
792
  const names = [];
793
793
  const cni = this.serverConfig.cni;
794
794
 
795
- if ( cni ) {
796
- const parts = cni.split(',').map((x) => `rke2-${ x }`);
797
-
798
- names.push(...parts);
795
+ if (typeof cni === 'string') {
796
+ names.push(...cni.split(',').map((x) => `rke2-${ x }`));
797
+ } else if (Array.isArray(cni)) {
798
+ names.push(...cni.map((x) => `rke2-${ x }`));
799
799
  }
800
800
 
801
801
  if (this.showCloudProvider) { // Shouldn't be removed such that changes to it will re-trigger this watch
@@ -16,6 +16,8 @@ import { allHash } from '@shell/utils/promise';
16
16
  import { NAMESPACE, _VIEW } from '@shell/config/query-params';
17
17
  import { sortBy } from '@shell/utils/sort';
18
18
  import { get } from '@shell/utils/object';
19
+ import { formatEncryptionSecretNames } from '@shell/utils/formatter';
20
+
19
21
  export default {
20
22
 
21
23
  components: {
@@ -108,7 +110,7 @@ export default {
108
110
  },
109
111
 
110
112
  encryptionSecretNames() {
111
- return this.allSecrets.filter((secret) => (secret.data || {})['encryption-provider-config.yaml'] && secret.metadata.namespace === this.chartNamespace && !secret.metadata?.state?.error).map((secret) => secret.metadata.name);
113
+ return formatEncryptionSecretNames(this.allSecrets, this.chartNamespace);
112
114
  },
113
115
 
114
116
  storageOptions() {
@@ -13,6 +13,8 @@ import { SECRET, BACKUP_RESTORE, CATALOG } from '@shell/config/types';
13
13
  import { allHash } from '@shell/utils/promise';
14
14
  import { get } from '@shell/utils/object';
15
15
  import { _CREATE } from '@shell/config/query-params';
16
+ import { formatEncryptionSecretNames } from '@shell/utils/formatter';
17
+
16
18
  export default {
17
19
 
18
20
  components: {
@@ -93,7 +95,7 @@ export default {
93
95
  },
94
96
 
95
97
  encryptionSecretNames() {
96
- return this.allSecrets.filter((secret) => !!(secret.data || {})['encryption-provider-config.yaml'] && secret.metadata.namespace === this.chartNamespace && !secret.metadata?.state?.error).map((secret) => secret.metadata.name);
98
+ return formatEncryptionSecretNames(this.allSecrets, this.chartNamespace);
97
99
  },
98
100
 
99
101
  isEncrypted() {
@@ -0,0 +1,40 @@
1
+ import { createLocalVue } from '@vue/test-utils';
2
+ import Vuex from 'vuex';
3
+ import ChartMixin from '@shell/mixins/chart';
4
+ import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
5
+
6
+ describe('chartMixin', () => {
7
+ const testCases = [[null, 0], [OPA_GATE_KEEPER_ID, 1], ['any_other_id', 0]];
8
+
9
+ it.each(testCases)(
10
+ 'should add OPA deprecation warning properly', (chartId, expected) => {
11
+ const localVue = createLocalVue();
12
+
13
+ localVue.use(Vuex);
14
+ localVue.mixin(ChartMixin);
15
+
16
+ const store = new Vuex.Store({
17
+ getters: {
18
+ currentCluster: () => {},
19
+ isRancher: () => true,
20
+ 'catalog/repo': () => {
21
+ return () => 'repo';
22
+ },
23
+ 'catalog/chart': () => {
24
+ return () => ({ id: chartId });
25
+ },
26
+ 'i18n/t': () => jest.fn()
27
+ }
28
+ });
29
+
30
+ const vm = localVue.extend({});
31
+ const instance = new vm({ store });
32
+
33
+ instance.$route = { query: { chart: 'chart_name' } };
34
+
35
+ const warnings = instance.warnings;
36
+
37
+ expect(warnings).toHaveLength(expected);
38
+ }
39
+ );
40
+ });
package/mixins/chart.js CHANGED
@@ -8,6 +8,7 @@ import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations
8
8
  import { SHOW_PRE_RELEASE, mapPref } from '@shell/store/prefs';
9
9
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
10
10
  import { NAME as MANAGER } from '@shell/config/product/manager';
11
+ import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
11
12
 
12
13
  import { formatSi, parseSi } from '@shell/utils/units';
13
14
  import { CAPI, CATALOG } from '@shell/config/types';
@@ -185,6 +186,10 @@ export default {
185
186
  }
186
187
  }
187
188
 
189
+ if (this.chart?.id === OPA_GATE_KEEPER_ID) {
190
+ warnings.unshift(this.t('gatekeeperIndex.deprecated', {}, true));
191
+ }
192
+
188
193
  return warnings;
189
194
  },
190
195
 
@@ -29,11 +29,15 @@ export default class ClusterRepo extends SteveModel {
29
29
  return out;
30
30
  }
31
31
 
32
- refresh() {
32
+ async refresh() {
33
33
  const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');
34
34
 
35
35
  this.spec.forceUpdate = now;
36
- this.save();
36
+ await this.save();
37
+
38
+ await this.waitForState('active', 10000, 1000);
39
+
40
+ this.$dispatch('catalog/load', { force: true, reset: true }, { root: true });
37
41
  }
38
42
 
39
43
  get isGit() {
@@ -1,4 +1,4 @@
1
- import { MANAGEMENT, NORMAN } from '@shell/config/types';
1
+ import { LOCAL_CLUSTER, MANAGEMENT, NORMAN } from '@shell/config/types';
2
2
  import { CAPI, FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
3
3
  import { _RKE2 } from '@shell/store/prefs';
4
4
  import SteveModel from '@shell/plugins/steve/steve-class';
@@ -34,7 +34,7 @@ export default class FleetCluster extends SteveModel {
34
34
  enabled: !!this.links.update
35
35
  });
36
36
 
37
- if (!this.isRke2) {
37
+ if (this.canChangeWorkspace) {
38
38
  insertAt(out, 3, {
39
39
  action: 'assignTo',
40
40
  label: 'Change workspace',
@@ -79,6 +79,14 @@ export default class FleetCluster extends SteveModel {
79
79
  return false;
80
80
  }
81
81
 
82
+ get canChangeWorkspace() {
83
+ return !this.isRke2 && !this.isLocal;
84
+ }
85
+
86
+ get isLocal() {
87
+ return this.metadata.name === LOCAL_CLUSTER || this.metadata?.labels?.[FLEET_LABELS.CLUSTER_NAME] === LOCAL_CLUSTER;
88
+ }
89
+
82
90
  get isRke2() {
83
91
  const provider = this?.metadata?.labels?.[CAPI.PROVIDER] || this?.status?.provider;
84
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -3,10 +3,16 @@ import { NAME, CHART_NAME } from '@shell/config/product/gatekeeper';
3
3
  import InstallRedirect from '@shell/utils/install-redirect';
4
4
  import ChartHeading from '@shell/components/ChartHeading';
5
5
  import SortableTable from '@shell/components/SortableTable';
6
+ import { Banner } from '@components/Banner';
6
7
  import { CONSTRAINT_VIOLATION_CONSTRAINT_LINK, CONSTRAINT_VIOLATION_COUNT, CONSTRAINT_VIOLATION_TEMPLATE_LINK } from '@shell/config/table-headers';
7
8
  import { GATEKEEPER } from '@shell/config/types';
9
+
10
+ export const OPA_GATE_KEEPER_ID = 'cluster/rancher-charts/rancher-gatekeeper';
11
+
8
12
  export default {
9
- components: { ChartHeading, SortableTable },
13
+ components: {
14
+ ChartHeading, SortableTable, Banner
15
+ },
10
16
  middleware: InstallRedirect(NAME, CHART_NAME),
11
17
  async fetch() {
12
18
  const constraints = this.constraint ? [this.constraint] : await this.$store.dispatch('cluster/findAll', { type: GATEKEEPER.SPOOFED.CONSTRAINT });
@@ -49,6 +55,9 @@ export default {
49
55
  :label="t('gatekeeperIndex.poweredBy')"
50
56
  url="https://github.com/open-policy-agent/gatekeeper"
51
57
  />
58
+ <Banner color="warning">
59
+ <span v-clean-html="t('gatekeeperIndex.deprecated', {}, true)" />
60
+ </Banner>
52
61
  <div class="spacer" />
53
62
  <div class="mb-10">
54
63
  <h2><t k="gatekeeperIndex.violations" /></h2>
@@ -0,0 +1,238 @@
1
+ import { handleKubeApiHeaderWarnings } from '@shell/plugins/steve/header-warnings';
2
+ import { DEFAULT_PERF_SETTING } from '@shell/config/settings';
3
+
4
+ describe('steve: header-warnings', () => {
5
+ // eslint-disable-next-line jest/no-hooks
6
+
7
+ function setupMocks(settings = {
8
+ separator: '299 - ',
9
+ notificationBlockList: ['299 - unknown field']
10
+ }) {
11
+ return {
12
+ dispatch: jest.fn(),
13
+ consoleWarn: jest.spyOn(console, 'warn').mockImplementation(),
14
+ consoleDebug: jest.spyOn(console, 'debug').mockImplementation(),
15
+ rootGetter: {
16
+ 'management/byId': (resource: string, id: string): { value: string} => {
17
+ return {
18
+ value: JSON.stringify({
19
+ ...DEFAULT_PERF_SETTING,
20
+ kubeAPI: { warningHeader: settings }
21
+ })
22
+ };
23
+ },
24
+ 'i18n/t': (key: string, { resourceType }: any) => {
25
+ return `${ key }_${ resourceType }`;
26
+ }
27
+ }
28
+ };
29
+ }
30
+
31
+ function createMockRes( headers = {}, data = { type: inputResourceType }) {
32
+ return {
33
+ headers, config: { url: 'unit/test' }, data
34
+ };
35
+ }
36
+
37
+ function createGrowlResponse(message: string, action = updateKey) {
38
+ return [
39
+ 'growl/warning',
40
+ {
41
+ message, timeout: 0, title: `${ action }_${ inputResourceType }`
42
+ },
43
+ { root: true }
44
+ ];
45
+ }
46
+
47
+ function createConsoleResponse(warnings: string) {
48
+ return `Validation Warnings for unit/test\n\n${ warnings }`;
49
+ }
50
+
51
+ const inputResourceType = 'abc';
52
+ const updateKey = 'growl.kubeApiHeaderWarning.titleUpdate';
53
+ const createKey = 'growl.kubeApiHeaderWarning.titleCreate';
54
+ const podSecurity = '299 - would violate PodSecurity "restricted:latest": unrestricted capabilities (container "container-0" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (container "container-0" must not set securityContext.runAsNonRoot=false), seccompProfile (pod or container "container-0" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")';
55
+ const deprecated = "299 - i'm deprecated";
56
+ const validation = '299 - unknown field "spec.containers[0].__active"';
57
+
58
+ describe('no warnings', () => {
59
+ it('put, no header warning', () => {
60
+ const mocks = setupMocks();
61
+
62
+ handleKubeApiHeaderWarnings(createMockRes(),
63
+ mocks.dispatch,
64
+ mocks.rootGetter,
65
+ 'put',
66
+ true,
67
+ );
68
+
69
+ expect(mocks.dispatch).not.toHaveBeenCalled();
70
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
71
+ expect(mocks.consoleDebug).not.toHaveBeenCalled();
72
+ });
73
+
74
+ it('post, no header warning', () => {
75
+ const mocks = setupMocks();
76
+
77
+ handleKubeApiHeaderWarnings(createMockRes(),
78
+ mocks.dispatch,
79
+ mocks.rootGetter,
80
+ 'post',
81
+ true,
82
+ );
83
+
84
+ expect(mocks.dispatch).not.toHaveBeenCalled();
85
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
86
+ expect(mocks.consoleDebug).not.toHaveBeenCalled();
87
+ });
88
+
89
+ it('patch, no header warning', () => {
90
+ const mocks = setupMocks();
91
+
92
+ handleKubeApiHeaderWarnings(createMockRes(),
93
+ mocks.dispatch,
94
+ mocks.rootGetter,
95
+ 'patch',
96
+ true,
97
+ );
98
+
99
+ expect(mocks.dispatch).not.toHaveBeenCalled();
100
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
101
+ expect(mocks.consoleDebug).not.toHaveBeenCalled();
102
+ });
103
+
104
+ it('get, no header warning', () => {
105
+ const mocks = setupMocks();
106
+
107
+ handleKubeApiHeaderWarnings(createMockRes(),
108
+ mocks.dispatch,
109
+ mocks.rootGetter,
110
+ 'get',
111
+ true,
112
+ );
113
+
114
+ expect(mocks.dispatch).not.toHaveBeenCalled();
115
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
116
+ expect(mocks.consoleDebug).not.toHaveBeenCalled();
117
+ });
118
+
119
+ it('get, warnings', () => {
120
+ const mocks = setupMocks();
121
+
122
+ handleKubeApiHeaderWarnings(createMockRes( { warnings: ['erg'] }),
123
+ mocks.dispatch,
124
+ mocks.rootGetter,
125
+ 'get',
126
+ true,
127
+ );
128
+
129
+ expect(mocks.dispatch).not.toHaveBeenCalled();
130
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
131
+ expect(mocks.consoleDebug).not.toHaveBeenCalled();
132
+ });
133
+
134
+ it('blocked (via default config)', () => {
135
+ const mocks = setupMocks();
136
+
137
+ handleKubeApiHeaderWarnings(
138
+ createMockRes({ warning: validation }),
139
+ mocks.dispatch,
140
+ mocks.rootGetter,
141
+ 'put',
142
+ true
143
+ );
144
+
145
+ expect(mocks.dispatch).not.toHaveBeenCalled();
146
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
147
+ expect(mocks.consoleDebug).toHaveBeenCalledWith(createConsoleResponse(validation));
148
+ });
149
+
150
+ it('blocked (via custom config)', () => {
151
+ const mocks = setupMocks({
152
+ separator: '299 - ',
153
+ notificationBlockList: [deprecated]
154
+ });
155
+
156
+ handleKubeApiHeaderWarnings(
157
+ createMockRes({ warning: deprecated }),
158
+ mocks.dispatch,
159
+ mocks.rootGetter,
160
+ 'put',
161
+ true
162
+ );
163
+
164
+ expect(mocks.dispatch).not.toHaveBeenCalled();
165
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
166
+ expect(mocks.consoleDebug).toHaveBeenCalledWith(createConsoleResponse(deprecated));
167
+ });
168
+
169
+ it('multi blocked (via custom config)', () => {
170
+ const mocks = setupMocks({
171
+ separator: '299 - ',
172
+ notificationBlockList: [deprecated, podSecurity]
173
+ });
174
+
175
+ handleKubeApiHeaderWarnings(
176
+ createMockRes({ warning: `${ deprecated },${ podSecurity }` }),
177
+ mocks.dispatch,
178
+ mocks.rootGetter,
179
+ 'put',
180
+ true
181
+ );
182
+
183
+ expect(mocks.dispatch).not.toHaveBeenCalled();
184
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
185
+ expect(mocks.consoleDebug).toHaveBeenCalledWith(createConsoleResponse(`${ deprecated }\n${ podSecurity }`));
186
+ });
187
+ });
188
+
189
+ describe('warnings', () => {
190
+ it('deprecated', () => {
191
+ const mocks = setupMocks();
192
+
193
+ handleKubeApiHeaderWarnings(
194
+ createMockRes({ warning: deprecated }),
195
+ mocks.dispatch,
196
+ mocks.rootGetter,
197
+ 'put',
198
+ true,
199
+ );
200
+
201
+ expect(mocks.dispatch).toHaveBeenCalledWith(...createGrowlResponse(deprecated));
202
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
203
+ expect(mocks.consoleDebug).toHaveBeenCalledWith(createConsoleResponse(deprecated));
204
+ });
205
+
206
+ it('pod security', () => {
207
+ const mocks = setupMocks();
208
+
209
+ handleKubeApiHeaderWarnings(
210
+ createMockRes({ warning: podSecurity }),
211
+ mocks.dispatch,
212
+ mocks.rootGetter,
213
+ 'post',
214
+ true,
215
+ );
216
+
217
+ expect(mocks.dispatch).toHaveBeenCalledWith(...createGrowlResponse(podSecurity, createKey));
218
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
219
+ expect(mocks.consoleDebug).toHaveBeenCalledWith(createConsoleResponse(podSecurity));
220
+ });
221
+
222
+ it('deprecated & pod security', () => {
223
+ const mocks = setupMocks();
224
+
225
+ handleKubeApiHeaderWarnings(
226
+ createMockRes({ warning: `${ deprecated },${ podSecurity }` }),
227
+ mocks.dispatch,
228
+ mocks.rootGetter,
229
+ 'put',
230
+ true,
231
+ );
232
+
233
+ expect(mocks.dispatch).toHaveBeenCalledWith(...createGrowlResponse(`${ deprecated }, ${ podSecurity }` ));
234
+ expect(mocks.consoleWarn).not.toHaveBeenCalled();
235
+ expect(mocks.consoleDebug).toHaveBeenCalledWith(createConsoleResponse(`${ deprecated }\n${ podSecurity }`));
236
+ });
237
+ });
238
+ });
@@ -8,6 +8,7 @@ import isObject from 'lodash/isObject';
8
8
  import { classify } from '@shell/plugins/dashboard-store/classify';
9
9
  import { NAMESPACE } from '@shell/config/types';
10
10
  import jsyaml from 'js-yaml';
11
+ import { handleKubeApiHeaderWarnings } from '@shell/plugins/steve/header-warnings';
11
12
 
12
13
  export default {
13
14
 
@@ -85,7 +86,7 @@ export default {
85
86
 
86
87
  while (true) {
87
88
  try {
88
- const out = await makeRequest(this, opt);
89
+ const out = await makeRequest(this, opt, rootGetters);
89
90
 
90
91
  if (!opt.depaginate) {
91
92
  return out;
@@ -116,7 +117,7 @@ export default {
116
117
  }
117
118
  }
118
119
 
119
- function makeRequest(that, opt) {
120
+ function makeRequest(that, opt, rootGetters) {
120
121
  return that.$axios(opt).then((res) => {
121
122
  let out;
122
123
 
@@ -128,9 +129,7 @@ export default {
128
129
 
129
130
  finishDeferred(key, 'resolve', out);
130
131
 
131
- if (opt.method === 'post' || opt.method === 'put') {
132
- handleValidationWarnings(res);
133
- }
132
+ handleKubeApiHeaderWarnings(res, dispatch, rootGetters, opt.method);
134
133
 
135
134
  return out;
136
135
  });
@@ -196,24 +195,6 @@ export default {
196
195
 
197
196
  return Promise.reject(out);
198
197
  }
199
-
200
- function handleValidationWarnings(res) {
201
- const warnings = (res.headers?.warning || '').split(',');
202
-
203
- if (!warnings.length || !warnings[0]) {
204
- return;
205
- }
206
-
207
- const message = warnings.reduce((message, warning) => {
208
- return `${ message }\n${ warning.trim() }`;
209
- }, `Validation Warnings for ${ opt.url }\n`);
210
-
211
- if (process.env.dev) {
212
- console.warn(`${ message }\n\n`, res.data); // eslint-disable-line no-console
213
- } else {
214
- console.debug(message); // eslint-disable-line no-console
215
- }
216
- }
217
198
  },
218
199
 
219
200
  promptMove({ commit, state }, resources) {