@rancher/shell 3.0.0-rc.7 → 3.0.0-rc.9

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/styles/global/_tooltip.scss +1 -12
  2. package/assets/translations/en-us.yaml +13 -1
  3. package/components/CodeMirror.vue +18 -15
  4. package/components/PromptRemove.vue +2 -2
  5. package/components/Questions/index.vue +2 -2
  6. package/components/ResourceDetail/Masthead.vue +1 -0
  7. package/components/ResourceDetail/index.vue +6 -7
  8. package/components/auth/RoleDetailEdit.vue +1 -6
  9. package/components/auth/__tests__/RoleDetailEdit.test.ts +53 -16
  10. package/components/form/ArrayList.vue +7 -3
  11. package/components/form/ColorInput.vue +13 -2
  12. package/components/form/__tests__/ColorInput.test.ts +27 -0
  13. package/components/formatter/CloudCredExpired.vue +69 -0
  14. package/components/formatter/Date.vue +1 -1
  15. package/components/nav/TopLevelMenu.vue +115 -51
  16. package/components/nav/__tests__/TopLevelMenu.test.ts +49 -23
  17. package/config/labels-annotations.js +9 -5
  18. package/core/types.ts +1 -1
  19. package/detail/provisioning.cattle.io.cluster.vue +0 -4
  20. package/edit/cis.cattle.io.clusterscanbenchmark.vue +2 -0
  21. package/edit/constraints.gatekeeper.sh.constraint/index.vue +2 -0
  22. package/edit/fleet.cattle.io.cluster.vue +4 -0
  23. package/edit/helm.cattle.io.projecthelmchart.vue +2 -0
  24. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +2 -0
  25. package/edit/kontainerDriver.vue +2 -0
  26. package/edit/logging-flow/index.vue +2 -0
  27. package/edit/management.cattle.io.project.vue +4 -1
  28. package/edit/management.cattle.io.projectroletemplatebinding.vue +2 -1
  29. package/edit/management.cattle.io.user.vue +3 -0
  30. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +2 -0
  31. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -0
  32. package/edit/monitoring.coreos.com.prometheusrule/index.vue +2 -0
  33. package/edit/monitoring.coreos.com.receiver/index.vue +2 -0
  34. package/edit/monitoring.coreos.com.route.vue +2 -0
  35. package/edit/networking.istio.io.destinationrule/index.vue +2 -0
  36. package/edit/nodeDriver.vue +2 -0
  37. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +4 -1
  38. package/edit/provisioning.cattle.io.cluster/import.vue +2 -0
  39. package/edit/provisioning.cattle.io.cluster/index.vue +7 -2
  40. package/edit/provisioning.cattle.io.cluster/rke2.vue +30 -7
  41. package/edit/ui.cattle.io.navlink.vue +4 -3
  42. package/list/provisioning.cattle.io.cluster.vue +57 -10
  43. package/machine-config/vmwarevsphere.vue +133 -95
  44. package/mixins/vue-select-overrides.js +0 -1
  45. package/models/catalog.cattle.io.app.js +4 -3
  46. package/models/cloudcredential.js +158 -2
  47. package/models/management.cattle.io.globalrole.js +6 -0
  48. package/models/management.cattle.io.roletemplate.js +6 -0
  49. package/models/nodedriver.js +5 -0
  50. package/models/provisioning.cattle.io.cluster.js +35 -1
  51. package/package.json +9 -2
  52. package/pages/c/_cluster/apps/charts/index.vue +0 -6
  53. package/pages/c/_cluster/explorer/__tests__/index.test.ts +6 -3
  54. package/pages/c/_cluster/explorer/index.vue +44 -22
  55. package/pages/c/_cluster/manager/cloudCredential/index.vue +68 -4
  56. package/pages/home.vue +1 -0
  57. package/plugins/dashboard-store/mutations.js +24 -3
  58. package/plugins/dashboard-store/resource-class.js +6 -0
  59. package/store/type-map.js +4 -2
  60. package/types/shell/index.d.ts +12 -1
@@ -15,10 +15,7 @@
15
15
  }
16
16
 
17
17
  .v-popper__arrow-container {
18
- width: 0;
19
- height: 0;
20
18
  border: 0 solid transparent;
21
- position: absolute;
22
19
  z-index: 1;
23
20
 
24
21
  .v-popper__arrow-inner {
@@ -30,9 +27,7 @@
30
27
  .v-popper__arrow-container {
31
28
 
32
29
  .v-popper__arrow-outer {
33
- border-bottom-width: 0;
34
30
  border-top-color: var(--tooltip-bg);
35
- left: -$triangle-inner-size;
36
31
  }
37
32
  }
38
33
  }
@@ -42,9 +37,7 @@
42
37
  .v-popper__arrow-container {
43
38
 
44
39
  .v-popper__arrow-outer {
45
- border-top-width: 0;
46
40
  border-bottom-color: var(--tooltip-bg);
47
- left: -$triangle-inner-size;
48
41
  background: transparent;
49
42
  }
50
43
  }
@@ -55,8 +48,6 @@
55
48
 
56
49
  .v-popper__arrow-outer {
57
50
  border-right-color: var(--tooltip-bg);
58
- top: -$triangle-inner-size;
59
- border-left-width: 0;
60
51
  }
61
52
  }
62
53
  }
@@ -65,9 +56,7 @@
65
56
  .v-popper__arrow-container {
66
57
 
67
58
  .v-popper__arrow-outer {
68
- border-right-width: 0;
69
59
  border-left-color: var(--tooltip-bg);
70
- top: -$triangle-inner-size;
71
60
  }
72
61
  }
73
62
  }
@@ -173,4 +162,4 @@
173
162
  //icon tooltip
174
163
  .icon-info.v-popper--has-tooltip {
175
164
  font-size: 14px;
176
- }
165
+ }
@@ -1822,6 +1822,13 @@ cluster:
1822
1822
  other { {pool_name}: The provided values for {fields} were not found in the list of expected values. This can happen with clusters provisioned outside of Rancher or when options for the provider have changed. }
1823
1823
  }
1824
1824
  rkeTemplateUpgrade: Template revision {name} available for upgrade
1825
+ cloudCredentials:
1826
+ renew: Renew Cloud Credentials
1827
+ expired: Cloud Credential expired, please Renew Cloud Credentials
1828
+ expiring: Cloud Credential will expire on { expires }, please Renew Cloud Credentials
1829
+ banners:
1830
+ expiring: "{count} {count, plural, =1 { Cluster has a Cloud Credential that expires soon} other { Clusters have Cloud Credentials that expire soon}}, please Renew Cloud {count, plural, =1 { Credential} other { Credentials}}"
1831
+ expired: "{count} {count, plural, =1 { Cluster has a Cloud Credential that has expired} other { Clusters have Cloud Credentials that have expired}}, please Renew Cloud {count, plural, =1 { Credential} other { Credentials}}"
1825
1832
 
1826
1833
  architecture:
1827
1834
  label:
@@ -5655,7 +5662,6 @@ tableHeaders:
5655
5662
  createdAt: Created At
5656
5663
  customVerbs: Custom Verbs
5657
5664
  description: Description
5658
- expires: Expires
5659
5665
  cpu: CPU
5660
5666
  currentReplicas: Current Replicas
5661
5667
  date: Date
@@ -5672,6 +5678,7 @@ tableHeaders:
5672
5678
  distinguisherMethod: Distinguisher Method
5673
5679
  effect: Effect
5674
5680
  endpoints: Endpoints
5681
+ expires: Expires
5675
5682
  firstSeen: First Seen
5676
5683
  fleetBundleType: Type
5677
5684
  flow: Flow
@@ -7751,6 +7758,11 @@ volumeClaimTemplate:
7751
7758
  manager:
7752
7759
  cloudCredentials:
7753
7760
  label: Cloud Credentials
7761
+ renew: Renew
7762
+ expired: Expired
7763
+ banners:
7764
+ expiring: "{count} Cloud {count, plural, =1 { Credential expires soon} other { Credentials expire soon}}, please Renew"
7765
+ expired: "{count} Cloud {count, plural, =1 { Credential has expired} other { Credentials have expired}}, please Renew"
7754
7766
  drivers:
7755
7767
  label: Drivers
7756
7768
  rkeTemplates:
@@ -292,11 +292,24 @@ export default {
292
292
  }
293
293
  }
294
294
 
295
- .code-mirror .codemirror-container {
296
- z-index: 0;
297
- font-size: inherit !important;
295
+ .code-mirror {
296
+ position: relative;
297
+
298
+ .codemirror-container {
299
+ z-index: 0;
300
+ font-size: inherit !important;
301
+
302
+ //rm no longer extant selector
303
+ .CodeMirror {
304
+ height: initial;
305
+ background: none
306
+ }
307
+
308
+ .CodeMirror-gutters {
309
+ background: inherit;
310
+ }
311
+ }
298
312
 
299
- // Keyboard mapping overlap
300
313
  .keymap.overlay {
301
314
  position: absolute;
302
315
  display: flex;
@@ -352,16 +365,6 @@ export default {
352
365
  }
353
366
  }
354
367
  }
355
-
356
- //rm no longer extant selector
357
- .CodeMirror {
358
- height: initial;
359
- background: none
360
- }
361
-
362
- .CodeMirror-gutters {
363
- background: inherit;
364
- }
365
-
366
368
  }
369
+
367
370
  </style>
@@ -105,8 +105,8 @@ export default {
105
105
  return null;
106
106
  }
107
107
 
108
- if (this.toRemove[0].doneLocationRemove) {
109
- return this.toRemove[0].doneLocationRemove;
108
+ if (this.toRemove[0].doneOverride) {
109
+ return this.toRemove[0].doneOverride;
110
110
  }
111
111
 
112
112
  const currentRoute = this.toRemove[0].currentRoute();
@@ -45,9 +45,9 @@ export function componentForQuestion(q) {
45
45
 
46
46
  if ( knownTypes[type] ) {
47
47
  return type;
48
- } else if ( type.startsWith('array[') ) { // This only really works for array[string|multiline], but close enough for now.
48
+ } else if ( type.startsWith('array') ) { // This only really works for array[string|multiline], but close enough for now.
49
49
  return ArrayType;
50
- } else if ( type.startsWith('map[') ) { // Same, only works with map[string|multiline]
50
+ } else if ( type.startsWith('map') ) { // Same, only works with map[string|multiline]
51
51
  return MapType;
52
52
  } else if ( type.startsWith('reference[') ) { // Same, only works with map[string|multiline]
53
53
  return ReferenceType;
@@ -521,6 +521,7 @@ export default {
521
521
  <button
522
522
  v-if="isView"
523
523
  ref="actions"
524
+ data-testid="mathead-action-menu"
524
525
  aria-haspopup="true"
525
526
  type="button"
526
527
  class="btn role-multi-action actions"
@@ -318,9 +318,12 @@ export default {
318
318
  },
319
319
 
320
320
  watch: {
321
- '$route.query'(inNeu, inOld) {
322
- const neu = clone(inNeu);
323
- const old = clone(inOld);
321
+ '$route'(current, prev) {
322
+ if (current.name !== prev.name) {
323
+ return;
324
+ }
325
+ const neu = clone(current.query);
326
+ const old = clone(prev.query);
324
327
 
325
328
  delete neu[PREVIEW];
326
329
  delete old[PREVIEW];
@@ -332,10 +335,6 @@ export default {
332
335
 
333
336
  const queryDiff = Object.keys(diff(neu, old));
334
337
 
335
- if (Object.keys(neu).length <= 0) {
336
- return;
337
- }
338
-
339
338
  if (queryDiff.includes(MODE) || queryDiff.includes(AS)) {
340
339
  this.$fetch();
341
340
  }
@@ -127,12 +127,6 @@ export default {
127
127
 
128
128
  created() {
129
129
  this.value['rules'] = this.value.rules || [];
130
- this.value.rules.forEach((rule) => {
131
- if (rule.verbs[0] === '*') {
132
- rule['verbs'] = [...VERBS];
133
- }
134
- });
135
-
136
130
  const query = { ...this.$route.query };
137
131
  const { roleContext } = query;
138
132
 
@@ -701,6 +695,7 @@ export default {
701
695
  <template #columns="props">
702
696
  <div class="columns row mr-20">
703
697
  <div :class="ruleClass">
698
+ <!-- Select verbs -->
704
699
  <Select
705
700
  :value="props.row.value.verbs"
706
701
  class="lg"
@@ -2,22 +2,20 @@ import { mount } from '@vue/test-utils';
2
2
  import RoleDetailEdit from '@shell/components/auth/RoleDetailEdit.vue';
3
3
  import { SUBTYPE_MAPPING } from '@shell/models/management.cattle.io.roletemplate';
4
4
 
5
- const role = {
6
- apiVersion: 'management.cattle.io/v3',
7
- kind: 'GlobalRole',
8
- metadata: { name: 'global-role-with-inherited' },
9
- inheritedClusterRoles: ['cluster-admin'],
10
- rules:
11
- [{
12
- verbs: ['get', 'list'],
13
- resources: ['pods'],
14
- apiGroups: ['']
15
- }],
16
- subtype: SUBTYPE_MAPPING.GLOBAL.id
17
- };
18
-
19
5
  describe('component: RoleDetailEdit', () => {
20
6
  it('does not have validation errors when the role has no displayName', () => {
7
+ const role = {
8
+ apiVersion: 'management.cattle.io/v3',
9
+ kind: 'GlobalRole',
10
+ metadata: { name: 'global-role-with-inherited' },
11
+ inheritedClusterRoles: ['cluster-admin'],
12
+ rules: [{
13
+ verbs: ['get', 'list'],
14
+ resources: ['pods'],
15
+ apiGroups: ['']
16
+ }],
17
+ subtype: SUBTYPE_MAPPING.GLOBAL.id
18
+ };
21
19
  const wrapper = mount(RoleDetailEdit, {
22
20
  props: { value: role },
23
21
 
@@ -28,14 +26,16 @@ describe('component: RoleDetailEdit', () => {
28
26
  $store: {
29
27
  dispatch: jest.fn(),
30
28
  getters: {
31
- currentStore: () => 'store', 'i18n/t': jest.fn(), 'store/schemaFor': jest.fn(), 'store/customisation/': jest.fn()
29
+ currentStore: () => 'store',
30
+ 'i18n/t': jest.fn(),
31
+ 'store/schemaFor': jest.fn(),
32
+ 'store/customisation/': jest.fn()
32
33
  }
33
34
  }
34
35
  },
35
36
 
36
37
  stubs: {
37
38
  CruResource: { template: '<div><slot></slot></div>' },
38
- // NameNsDescription: true,
39
39
  Tab: { template: '<div><slot></slot></div>' },
40
40
  },
41
41
  },
@@ -43,4 +43,41 @@ describe('component: RoleDetailEdit', () => {
43
43
 
44
44
  expect((wrapper.vm as any).fvFormIsValid).toBe(true);
45
45
  });
46
+
47
+ it.each([
48
+ [['*']],
49
+ [['create', 'delete', 'get', 'list', 'patch', 'update', 'watch']],
50
+ ])('should display the verbs %p', (verbs: string[]) => {
51
+ const wrapper = mount(RoleDetailEdit, {
52
+ props: {
53
+ value: {
54
+ rules: [{ verbs }],
55
+ subtype: 'GLOBAL'
56
+ },
57
+ },
58
+
59
+ global: {
60
+ mocks: {
61
+ $fetchState: { pending: false },
62
+ $route: { name: 'anything' },
63
+ $store: {
64
+ dispatch: jest.fn(),
65
+ getters: {
66
+ currentStore: () => 'store',
67
+ 'i18n/t': jest.fn(),
68
+ 'store/schemaFor': jest.fn(),
69
+ 'store/customisation/': jest.fn()
70
+ }
71
+ }
72
+ },
73
+
74
+ stubs: {
75
+ CruResource: { template: '<div><slot></slot></div>' },
76
+ Tab: { template: '<div><slot></slot></div>' },
77
+ },
78
+ },
79
+ });
80
+
81
+ expect(wrapper.vm.value.rules[0].verbs).toStrictEqual(verbs);
82
+ });
46
83
  });
@@ -138,10 +138,14 @@ export default {
138
138
  }
139
139
  },
140
140
  watch: {
141
- value() {
142
- this.lastUpdateWasFromValue = true;
143
- this.rows = (this.value || []).map((v) => ({ value: v }));
141
+ value: {
142
+ deep: true,
143
+ handler() {
144
+ this.lastUpdateWasFromValue = true;
145
+ this.rows = (this.value || []).map((v) => ({ value: v }));
146
+ }
144
147
  },
148
+
145
149
  rows: {
146
150
  deep: true,
147
151
  handler(newValue, oldValue) {
@@ -40,6 +40,11 @@ export default {
40
40
  componentTestid: {
41
41
  type: String,
42
42
  default: 'color-input'
43
+ },
44
+
45
+ disabled: {
46
+ type: Boolean,
47
+ default: false,
43
48
  }
44
49
  },
45
50
 
@@ -56,6 +61,12 @@ export default {
56
61
  */
57
62
  inputValue() {
58
63
  return this.value ? this.value : this.defaultValue;
64
+ },
65
+
66
+ isDisabled() {
67
+ const disabled = this.disabled;
68
+
69
+ return this.mode !== this.editMode || disabled;
59
70
  }
60
71
  },
61
72
 
@@ -69,7 +80,7 @@ export default {
69
80
  <template>
70
81
  <div
71
82
  class="color-input"
72
- :class="{[mode]:mode, disabled: mode !== editMode}"
83
+ :class="{[mode]:mode, disabled: isDisabled}"
73
84
  :data-testid="componentTestid + '-color-input'"
74
85
  >
75
86
  <label class="text-label"><t
@@ -89,7 +100,7 @@ export default {
89
100
  <input
90
101
  ref="input"
91
102
  type="color"
92
- :disabled="mode !== editMode"
103
+ :disabled="isDisabled"
93
104
  :value="inputValue"
94
105
  @input="$emit('update:value', $event.target.value)"
95
106
  >
@@ -0,0 +1,27 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import ColorInput from '@shell/components/form/ColorInput.vue';
3
+
4
+ describe('colorInput.vue', () => {
5
+ it('disables the input when disabled prop is true', () => {
6
+ const wrapper = shallowMount(
7
+ ColorInput,
8
+ { props: { disabled: true } }
9
+ );
10
+
11
+ const colorWrapper = wrapper.find('.color-input');
12
+ const colorInput = wrapper.find('input');
13
+
14
+ expect(colorWrapper.classes()).toContain('disabled');
15
+ expect(Object.keys(colorInput.attributes())).toContain('disabled');
16
+ });
17
+
18
+ it('defaults to enabled when no disabled prop is passed', () => {
19
+ const wrapper = shallowMount(ColorInput);
20
+
21
+ const colorWrapper = wrapper.find('.color-input');
22
+ const colorInput = wrapper.find('input');
23
+
24
+ expect(colorWrapper.classes()).not.toContain('disabled');
25
+ expect(Object.keys(colorInput.attributes())).not.toContain('disabled');
26
+ });
27
+ });
@@ -0,0 +1,69 @@
1
+ <script>
2
+
3
+ export default {
4
+ props: {
5
+ value: {
6
+ type: [Number, String],
7
+ required: true
8
+ },
9
+
10
+ row: {
11
+ type: Object,
12
+ default: () => {
13
+ return {};
14
+ }
15
+ },
16
+
17
+ verbose: {
18
+ type: Boolean,
19
+ default: false,
20
+ }
21
+
22
+ },
23
+
24
+ computed: {
25
+ outputString() {
26
+ return this.verbose ? this.verboseOutputString : this.row.expiresString;
27
+ },
28
+
29
+ verboseOutputString() {
30
+ const expireData = this.row?.expireData;
31
+
32
+ if (expireData?.expired) {
33
+ return this.t('cluster.cloudCredentials.expired');
34
+ } else if (expireData?.expiring) {
35
+ return this.t('cluster.cloudCredentials.expiring', { expires: this.row.expiresString });
36
+ }
37
+
38
+ return null;
39
+ }
40
+ }
41
+ };
42
+ </script>
43
+
44
+ <template>
45
+ <div
46
+ v-if="outputString"
47
+ class="cloud-cred-expired"
48
+ :class="{ 'text-error': row.expireData.expired, 'text-warning': row.expireData.expiring}"
49
+ >
50
+ <div class="token-icon mr-5">
51
+ <i
52
+ class="icon"
53
+ :class="{'icon-error': row.expireData.expired, 'icon-warning': row.expireData.expiring}"
54
+ />
55
+ </div>
56
+ {{ outputString }}
57
+ </div>
58
+ </template>
59
+
60
+ <style lang="scss" scoped>
61
+ .cloud-cred-expired {
62
+ display: flex;
63
+ align-items: center;
64
+ .token-icon {
65
+ display: flex;
66
+ align-items: center;
67
+ }
68
+ }
69
+ </style>
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  value: {
14
- type: [String, Date],
14
+ type: [String, Date, Number],
15
15
  default: ''
16
16
  },
17
17