@rancher/shell 3.0.0-rc.9 → 3.0.1-rc.1

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 (130) hide show
  1. package/assets/brand/harvester/favicon.png +0 -0
  2. package/assets/brand/harvester/metadata.json +3 -0
  3. package/assets/images/pl/harvester.svg +1 -0
  4. package/assets/translations/en-us.yaml +37 -15
  5. package/assets/translations/zh-hans.yaml +2 -5
  6. package/components/BrandImage.vue +5 -1
  7. package/components/CopyToClipboardText.vue +2 -0
  8. package/components/CruResourceFooter.vue +1 -0
  9. package/components/DetailTop.vue +1 -1
  10. package/components/EmberPage.vue +0 -8
  11. package/components/ExplorerMembers.vue +5 -1
  12. package/components/ExplorerProjectsNamespaces.vue +39 -15
  13. package/components/HardwareResourceGauge.vue +12 -2
  14. package/components/InputOrDisplay.vue +6 -2
  15. package/components/LandingPagePreference.vue +2 -2
  16. package/components/MessageLink.vue +1 -1
  17. package/components/ResourceDetail/Masthead.vue +22 -1
  18. package/components/ResourceDetail/index.vue +2 -8
  19. package/components/ResourceTable.vue +40 -7
  20. package/components/ResourceYaml.vue +1 -1
  21. package/components/SideNav.vue +1 -1
  22. package/components/SortableTable/actions.js +1 -1
  23. package/components/SortableTable/index.vue +17 -1
  24. package/components/SortableTable/selection.js +1 -1
  25. package/components/SortableTable/sorting.js +11 -3
  26. package/components/TableDataUserIcon.vue +1 -1
  27. package/components/fleet/FleetClusters.vue +0 -3
  28. package/components/fleet/FleetRepos.vue +0 -7
  29. package/components/form/ArrayList.vue +5 -1
  30. package/components/form/ArrayListSelect.vue +5 -1
  31. package/components/form/HookOption.vue +31 -29
  32. package/components/form/KeyValue.vue +1 -1
  33. package/components/form/LabeledSelect.vue +26 -7
  34. package/components/form/LifecycleHooks.vue +2 -2
  35. package/components/form/Password.vue +7 -1
  36. package/components/form/UnitInput.vue +10 -1
  37. package/components/form/__tests__/UnitInput.test.ts +1 -0
  38. package/components/formatter/ClusterProvider.vue +3 -3
  39. package/components/formatter/ImagePercentageBar.vue +1 -1
  40. package/components/formatter/SecretData.vue +1 -1
  41. package/components/formatter/Si.vue +5 -1
  42. package/components/formatter/Translate.vue +1 -1
  43. package/components/nav/Header.vue +38 -17
  44. package/components/nav/NamespaceFilter.vue +5 -8
  45. package/components/nav/TopLevelMenu.vue +11 -51
  46. package/components/nav/WorkspaceSwitcher.vue +0 -1
  47. package/config/labels-annotations.js +2 -0
  48. package/config/private-label.js +2 -1
  49. package/config/router/routes.js +2 -26
  50. package/config/settings.ts +5 -0
  51. package/config/table-headers.js +15 -0
  52. package/config/types.js +3 -0
  53. package/config/version.js +2 -0
  54. package/detail/catalog.cattle.io.app.vue +17 -4
  55. package/detail/fleet.cattle.io.bundle.vue +5 -68
  56. package/detail/fleet.cattle.io.cluster.vue +11 -9
  57. package/detail/fleet.cattle.io.gitrepo.vue +3 -2
  58. package/directives/clean-tooltip.js +4 -4
  59. package/edit/cis.cattle.io.clusterscan.vue +4 -3
  60. package/edit/fleet.cattle.io.gitrepo.vue +11 -8
  61. package/edit/logging-flow/Match.vue +75 -42
  62. package/edit/logging-flow/index.vue +89 -10
  63. package/edit/logging.banzaicloud.io.output/index.vue +2 -2
  64. package/edit/management.cattle.io.project.vue +4 -3
  65. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +5 -5
  66. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -1
  67. package/edit/namespace.vue +1 -1
  68. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +26 -0
  69. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +63 -149
  70. package/edit/provisioning.cattle.io.cluster/index.vue +2 -1
  71. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -4
  72. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +7 -2
  73. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +108 -35
  74. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +1 -1
  75. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +1 -1
  76. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +1 -1
  77. package/edit/workload/mixins/workload.js +1 -1
  78. package/list/harvesterhci.io.management.cluster.vue +244 -0
  79. package/list/namespace.vue +16 -4
  80. package/mixins/browser-tab-visibility.js +1 -1
  81. package/mixins/chart.js +6 -2
  82. package/mixins/metric-poller.js +1 -1
  83. package/mixins/resource-fetch.js +1 -1
  84. package/models/__tests__/management.cattle.io.cluster.test.ts +45 -3
  85. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +0 -86
  86. package/models/catalog.cattle.io.app.js +108 -21
  87. package/models/cloudcredential.js +4 -4
  88. package/models/fleet.cattle.io.bundle.js +3 -1
  89. package/models/fleet.cattle.io.gitrepo.js +51 -63
  90. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +88 -0
  91. package/models/management.cattle.io.cluster.js +39 -7
  92. package/models/management.cattle.io.project.js +4 -0
  93. package/models/management.cattle.io.setting.js +25 -0
  94. package/models/provisioning.cattle.io.cluster.js +6 -16
  95. package/models/storage.k8s.io.storageclass.js +15 -4
  96. package/models/workload.js +1 -1
  97. package/package.json +5 -5
  98. package/pages/auth/login.vue +3 -2
  99. package/pages/auth/logout.vue +7 -9
  100. package/pages/auth/setup.vue +4 -1
  101. package/pages/c/_cluster/apps/charts/install.vue +11 -3
  102. package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
  103. package/pages/c/_cluster/explorer/index.vue +1 -2
  104. package/pages/c/_cluster/fleet/index.vue +13 -9
  105. package/pages/c/_cluster/settings/brand.vue +4 -1
  106. package/pages/c/_cluster/uiplugins/index.vue +4 -2
  107. package/pages/diagnostic.vue +1 -0
  108. package/pages/prefs.vue +22 -10
  109. package/plugins/dashboard-store/resource-class.js +11 -3
  110. package/plugins/steve/mutations.js +4 -1
  111. package/plugins/steve/steve-pagination-utils.ts +1 -1
  112. package/plugins/steve/subscribe.js +3 -4
  113. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -2
  114. package/store/features.js +1 -0
  115. package/store/i18n.js +5 -1
  116. package/store/prefs.js +8 -0
  117. package/types/resources/fleet.d.ts +40 -0
  118. package/types/shell/index.d.ts +442 -396
  119. package/utils/__tests__/object.test.ts +152 -1
  120. package/utils/auth.js +1 -1
  121. package/utils/create-yaml.js +1 -1
  122. package/utils/favicon.js +2 -0
  123. package/utils/fleet.ts +159 -0
  124. package/utils/gc/gc.ts +1 -1
  125. package/utils/object.js +37 -0
  126. package/utils/string.js +9 -0
  127. package/utils/v-sphere.ts +31 -0
  128. package/utils/validators/cron-schedule.js +1 -1
  129. package/utils/validators/formRules/index.ts +2 -2
  130. package/config/product/multi-cluster-apps.js +0 -61
@@ -1,14 +1,29 @@
1
1
 
2
2
  <script>
3
- import { Checkbox } from '@components/Form/Checkbox';
4
3
  import { LabeledInput } from '@components/Form/LabeledInput';
5
4
  import { _CREATE } from '@shell/config/query-params';
5
+ import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
6
+
7
+ export const DATA_DIR_RADIO_OPTIONS = {
8
+ DEFAULT: 'defaultDataDir',
9
+ COMMON: 'commonBaseDataDir',
10
+ CUSTOM: 'customDataDir',
11
+ };
12
+
13
+ export const DEFAULT_COMMON_BASE_PATH = '/var/lib/rancher';
14
+
15
+ export const DEFAULT_SUBDIRS = {
16
+ AGENT: 'agent',
17
+ PROVISIONING: 'provisioning',
18
+ K8S_DISTRO_RKE2: 'rke2',
19
+ K8S_DISTRO_K3S: 'k3s',
20
+ };
6
21
 
7
22
  export default {
8
23
  name: 'DirectoryConfig',
9
24
  components: {
10
- Checkbox,
11
25
  LabeledInput,
26
+ RadioGroup
12
27
  },
13
28
  props: {
14
29
  mode: {
@@ -16,52 +31,114 @@ export default {
16
31
  required: true,
17
32
  },
18
33
 
34
+ k8sVersion: {
35
+ type: String,
36
+ required: true,
37
+ },
38
+
19
39
  value: {
20
40
  type: Object,
21
41
  required: true,
22
42
  },
23
43
  },
24
44
  data() {
25
- let atLeastOneDirWithAnIdentifier = false;
26
- let allDirsWithSameIdentifier = false;
27
-
28
- if (this.value?.systemAgent?.length || this.value?.provisioning?.length || this.value?.k8sDistro?.length) {
29
- atLeastOneDirWithAnIdentifier = true;
30
- if (this.value?.systemAgent === this.value?.provisioning && this.value?.provisioning === this.value?.k8sDistro &&
31
- this.value?.systemAgent === this.value?.k8sDistro) {
32
- allDirsWithSameIdentifier = true;
45
+ let dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.DEFAULT;
46
+ let k8sDistroSubDir = DEFAULT_SUBDIRS.K8S_DISTRO_RKE2;
47
+
48
+ if (this.k8sVersion && this.k8sVersion.includes('k3s')) {
49
+ k8sDistroSubDir = DEFAULT_SUBDIRS.K8S_DISTRO_K3S;
50
+ }
51
+
52
+ if (this.mode !== _CREATE) {
53
+ if (this.value?.systemAgent?.length || this.value?.provisioning?.length || this.value?.k8sDistro?.length) {
54
+ dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
33
55
  }
34
56
  }
35
57
 
36
58
  return {
37
- isSettingCommonConfig: !(atLeastOneDirWithAnIdentifier && !allDirsWithSameIdentifier),
38
- commonConfig: allDirsWithSameIdentifier ? this.value?.systemAgent : '',
59
+ DATA_DIR_RADIO_OPTIONS,
60
+ dataConfigRadioValue,
61
+ k8sDistroSubDir,
62
+ commonConfig: '',
39
63
  };
40
64
  },
41
65
  watch: {
42
66
  commonConfig(neu) {
43
- if (neu && neu.length && this.isSettingCommonConfig) {
44
- this.value.systemAgent = neu;
45
- this.value.provisioning = neu;
46
- this.value.k8sDistro = neu;
67
+ if (neu && neu.length && this.dataConfigRadioValue === DATA_DIR_RADIO_OPTIONS.COMMON) {
68
+ this.value.systemAgent = `${ neu }/${ DEFAULT_SUBDIRS.AGENT }`;
69
+ this.value.provisioning = `${ neu }/${ DEFAULT_SUBDIRS.PROVISIONING }`;
70
+ this.value.k8sDistro = `${ neu }/${ this.k8sDistroSubDir }`;
71
+ }
72
+ },
73
+ k8sVersion: {
74
+ handler(neu) {
75
+ if (neu && neu.includes('k3s')) {
76
+ this.k8sDistroSubDir = DEFAULT_SUBDIRS.K8S_DISTRO_K3S;
77
+ } else {
78
+ this.k8sDistroSubDir = DEFAULT_SUBDIRS.K8S_DISTRO_RKE2;
79
+ }
80
+
81
+ if (this.value.k8sDistro) {
82
+ this.value.k8sDistro = `${ neu }/${ this.k8sDistroSubDir }`;
83
+ }
47
84
  }
48
85
  }
49
86
  },
50
87
  computed: {
51
- disableEditInput() {
52
- return this.mode !== _CREATE;
88
+ dataConfigRadioOptions() {
89
+ const defaultDataDirOption = {
90
+ value: DATA_DIR_RADIO_OPTIONS.DEFAULT,
91
+ label: this.t('cluster.directoryConfig.radioInput.defaultLabel')
92
+ };
93
+ const customDataDirOption = {
94
+ value: DATA_DIR_RADIO_OPTIONS.CUSTOM,
95
+ label: this.t('cluster.directoryConfig.radioInput.customLabel')
96
+ };
97
+
98
+ if (this.mode === _CREATE) {
99
+ return [
100
+ defaultDataDirOption,
101
+ { value: DATA_DIR_RADIO_OPTIONS.COMMON, label: this.t('cluster.directoryConfig.radioInput.commonLabel') },
102
+ customDataDirOption
103
+ ];
104
+ } else {
105
+ return [
106
+ defaultDataDirOption,
107
+ customDataDirOption
108
+ ];
109
+ }
53
110
  }
54
111
  },
55
112
  methods: {
56
- handleCommonConfig(val) {
57
- this.isSettingCommonConfig = val;
113
+ handleRadioInput(val) {
114
+ switch (val) {
115
+ case DATA_DIR_RADIO_OPTIONS.DEFAULT:
116
+ if (this.mode === _CREATE) {
117
+ this.commonConfig = '';
118
+ }
119
+ this.value.systemAgent = '';
120
+ this.value.provisioning = '';
121
+ this.value.k8sDistro = '';
122
+
123
+ this.dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.DEFAULT;
124
+ break;
125
+ case DATA_DIR_RADIO_OPTIONS.COMMON:
126
+ this.commonConfig = DEFAULT_COMMON_BASE_PATH;
127
+
128
+ this.dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.COMMON;
129
+ break;
130
+ // default is custom config
131
+ default:
132
+ if (this.mode === _CREATE) {
133
+ this.commonConfig = '';
134
+ }
58
135
 
59
- if (val) {
60
136
  this.value.systemAgent = '';
61
137
  this.value.provisioning = '';
62
138
  this.value.k8sDistro = '';
63
- } else {
64
- this.commonConfig = '';
139
+
140
+ this.dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
141
+ break;
65
142
  }
66
143
  }
67
144
  },
@@ -74,33 +151,31 @@ export default {
74
151
  <h3 class="mb-20">
75
152
  {{ t('cluster.directoryConfig.title') }}
76
153
  </h3>
77
- <Checkbox
154
+ <RadioGroup
155
+ :value="dataConfigRadioValue"
78
156
  class="mb-10"
79
- :value="isSettingCommonConfig"
80
157
  :mode="mode"
81
- :label="t('cluster.directoryConfig.checkboxText')"
82
- :disabled="disableEditInput"
83
- data-testid="rke2-directory-config-individual-config-checkbox"
84
- @update:value="handleCommonConfig"
158
+ :options="dataConfigRadioOptions"
159
+ name="directory-config-radio"
160
+ data-testid="rke2-directory-config-radio-input"
161
+ @update:value="handleRadioInput"
85
162
  />
86
163
  <LabeledInput
87
- v-if="isSettingCommonConfig"
164
+ v-if="dataConfigRadioValue === DATA_DIR_RADIO_OPTIONS.COMMON"
88
165
  v-model:value="commonConfig"
89
166
  class="mb-20"
90
167
  :mode="mode"
91
168
  :label="t('cluster.directoryConfig.common.label')"
92
169
  :tooltip="t('cluster.directoryConfig.common.tooltip')"
93
- :disabled="disableEditInput"
94
170
  data-testid="rke2-directory-config-common-data-dir"
95
171
  />
96
- <div v-if="!isSettingCommonConfig">
172
+ <div v-if="dataConfigRadioValue === DATA_DIR_RADIO_OPTIONS.CUSTOM">
97
173
  <LabeledInput
98
174
  v-model:value="value.systemAgent"
99
175
  class="mb-20"
100
176
  :mode="mode"
101
177
  :label="t('cluster.directoryConfig.systemAgent.label')"
102
178
  :tooltip="t('cluster.directoryConfig.systemAgent.tooltip')"
103
- :disabled="disableEditInput"
104
179
  data-testid="rke2-directory-config-systemAgent-data-dir"
105
180
  />
106
181
  <LabeledInput
@@ -109,7 +184,6 @@ export default {
109
184
  :mode="mode"
110
185
  :label="t('cluster.directoryConfig.provisioning.label')"
111
186
  :tooltip="t('cluster.directoryConfig.provisioning.tooltip')"
112
- :disabled="disableEditInput"
113
187
  data-testid="rke2-directory-config-provisioning-data-dir"
114
188
  />
115
189
  <LabeledInput
@@ -118,7 +192,6 @@ export default {
118
192
  :mode="mode"
119
193
  :label="t('cluster.directoryConfig.k8sDistro.label')"
120
194
  :tooltip="t('cluster.directoryConfig.k8sDistro.tooltip')"
121
- :disabled="disableEditInput"
122
195
  data-testid="rke2-directory-config-k8sDistro-data-dir"
123
196
  />
124
197
  </div>
@@ -317,7 +317,7 @@ export default {
317
317
  />
318
318
  </h3>
319
319
  <UnitInput
320
- v-model.number="unhealthyNodeTimeoutInteger"
320
+ v-model:value="unhealthyNodeTimeoutInteger"
321
321
  :hide-arrows="true"
322
322
  :placeholder="t('containerResourceLimit.cpuPlaceholder')"
323
323
  :mode="mode"
@@ -154,7 +154,6 @@ export default {
154
154
  :mode="mode"
155
155
  :data-testid="`registry-auth-host-input-${i}`"
156
156
  />
157
-
158
157
  <SelectOrCreateAuthSecret
159
158
  v-model:value="row.value.authConfigSecretName"
160
159
  :register-before-hook="wrapRegisterBeforeHook"
@@ -168,6 +167,7 @@ export default {
168
167
  generate-name="registryconfig-auth-"
169
168
  :data-testid="`registry-auth-select-or-create-${i}`"
170
169
  :cache-secrets="true"
170
+ @update:value="update"
171
171
  />
172
172
  </div>
173
173
  <div class="col span-6">
@@ -90,7 +90,7 @@ export default {
90
90
  :default-add-value="defaultAddValue"
91
91
  :initial-empty-row="true"
92
92
  :mode="mode"
93
- @input="update"
93
+ @update:value="update"
94
94
  >
95
95
  <template #default="{row}">
96
96
  <div class="row">
@@ -947,7 +947,7 @@ export default {
947
947
  nameNumber++;
948
948
  }
949
949
  const container = {
950
- ...defaultContainer,
950
+ ...structuredClone(defaultContainer),
951
951
  name: `container-${ nameNumber }`,
952
952
  active: true
953
953
  };
@@ -0,0 +1,244 @@
1
+ <script>
2
+ import BrandImage from '@shell/components/BrandImage';
3
+ import TypeDescription from '@shell/components/TypeDescription';
4
+ import ResourceTable from '@shell/components/ResourceTable';
5
+ import Masthead from '@shell/components/ResourceList/Masthead';
6
+ import Loading from '@shell/components/Loading';
7
+ import { HARVESTER_NAME as VIRTUAL } from '@shell/config/features';
8
+ import { CAPI, HCI, MANAGEMENT } from '@shell/config/types';
9
+ import { isHarvesterCluster } from '@shell/utils/cluster';
10
+ import { allHash } from '@shell/utils/promise';
11
+
12
+ export default {
13
+ components: {
14
+ BrandImage,
15
+ ResourceTable,
16
+ Masthead,
17
+ TypeDescription,
18
+ Loading
19
+ },
20
+
21
+ props: {
22
+ schema: {
23
+ type: Object,
24
+ required: true,
25
+ },
26
+ useQueryParamsForSimpleFiltering: {
27
+ type: Boolean,
28
+ default: false
29
+ }
30
+ },
31
+
32
+ async fetch() {
33
+ const inStore = this.$store.getters['currentProduct'].inStore;
34
+
35
+ const hash = await allHash({
36
+ hciClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.CLUSTER }),
37
+ mgmtClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: MANAGEMENT.CLUSTER })
38
+ });
39
+
40
+ this.hciClusters = hash.hciClusters;
41
+ this.mgmtClusters = hash.mgmtClusters;
42
+ },
43
+
44
+ data() {
45
+ const resource = CAPI.RANCHER_CLUSTER;
46
+
47
+ return {
48
+ navigating: false,
49
+ VIRTUAL,
50
+ hciDashboard: HCI.DASHBOARD,
51
+ resource,
52
+ hResource: HCI.CLUSTER,
53
+ hciClusters: [],
54
+ mgmtClusters: []
55
+ };
56
+ },
57
+
58
+ computed: {
59
+ realSchema() {
60
+ return this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
61
+ },
62
+
63
+ importLocation() {
64
+ return {
65
+ name: 'c-cluster-product-resource-create',
66
+ params: {
67
+ product: this.$store.getters['currentProduct'].name,
68
+ resource: this.schema.id,
69
+ },
70
+ };
71
+ },
72
+
73
+ canCreateCluster() {
74
+ const schema = this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
75
+
76
+ return !!schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
77
+ },
78
+
79
+ rows() {
80
+ return this.hciClusters.filter((c) => {
81
+ const cluster = this.mgmtClusters.find((cluster) => cluster?.metadata?.name === c?.status?.clusterName);
82
+
83
+ return isHarvesterCluster(cluster);
84
+ });
85
+ },
86
+
87
+ typeDisplay() {
88
+ return this.t(`typeLabel."${ HCI.CLUSTER }"`, { count: this.row?.length || 0 });
89
+ },
90
+ },
91
+
92
+ methods: {
93
+ async goToCluster(row) {
94
+ const timeout = setTimeout(() => {
95
+ // Don't show loading indicator for quickly fetched plugins
96
+ this.navigating = row.id;
97
+ }, 1000);
98
+
99
+ try {
100
+ await row.goToCluster();
101
+
102
+ clearTimeout(timeout);
103
+ this.navigating = false;
104
+ } catch {
105
+ // The error handling is carried out within goToCluster, but just in case something happens before the promise chain can catch it...
106
+ clearTimeout(timeout);
107
+ this.navigating = false;
108
+ }
109
+ }
110
+ }
111
+ };
112
+ </script>
113
+
114
+ <template>
115
+ <Loading v-if="$fetchState.pending" />
116
+ <div v-else>
117
+ <Masthead
118
+ :schema="realSchema"
119
+ :resource="resource"
120
+ :is-creatable="false"
121
+ :type-display="typeDisplay"
122
+ >
123
+ <template #typeDescription>
124
+ <TypeDescription :resource="hResource" />
125
+ </template>
126
+
127
+ <template
128
+ v-if="canCreateCluster"
129
+ slot="extraActions"
130
+ >
131
+ <n-link
132
+ :to="importLocation"
133
+ class="btn role-primary"
134
+ >
135
+ {{ t('cluster.importAction') }}
136
+ </n-link>
137
+ </template>
138
+ </Masthead>
139
+
140
+ <ResourceTable
141
+ v-if="rows && rows.length"
142
+ :schema="schema"
143
+ :rows="rows"
144
+ :is-creatable="true"
145
+ :namespaced="false"
146
+ :use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
147
+ >
148
+ <template #col:name="{row}">
149
+ <td>
150
+ <span class="cluster-link">
151
+ <a
152
+ v-if="row.isReady"
153
+ class="link"
154
+ :disabled="navigating"
155
+ @click="goToCluster(row)"
156
+ >{{ row.nameDisplay }}</a>
157
+ <span v-else>
158
+ {{ row.nameDisplay }}
159
+ </span>
160
+ <i
161
+ class="icon icon-spinner icon-spin ml-5"
162
+ :class="{'navigating': navigating === row.id}"
163
+ />
164
+ </span>
165
+ </td>
166
+ </template>
167
+
168
+ <template #cell:harvester="{row}">
169
+ <n-link
170
+ class="btn btn-sm role-primary"
171
+ :to="row.detailLocation"
172
+ >
173
+ {{ t('harvesterManager.manage') }}
174
+ </n-link>
175
+ </template>
176
+ </ResourceTable>
177
+ <div v-else>
178
+ <div class="no-clusters">
179
+ {{ t('harvesterManager.cluster.none') }}
180
+ </div>
181
+ <hr class="info-section">
182
+ <div class="logo">
183
+ <BrandImage
184
+ file-name="harvester.png"
185
+ height="64"
186
+ />
187
+ </div>
188
+ <div class="tagline">
189
+ <div>{{ t('harvesterManager.cluster.description') }}</div>
190
+ </div>
191
+ <div class="tagline sub-tagline">
192
+ <div v-clean-html="t('harvesterManager.cluster.learnMore', {}, true)" />
193
+ </div>
194
+ </div>
195
+ </div>
196
+ </template>
197
+
198
+ <style lang="scss" scoped>
199
+ .cluster-link {
200
+ display: flex;
201
+ align-items: center;
202
+
203
+ .icon {
204
+ // Use visibility to avoid the columns re-adjusting when the icon is shown
205
+ visibility: hidden;
206
+
207
+ &.navigating {
208
+ visibility: visible;
209
+ }
210
+ }
211
+
212
+ }
213
+ .no-clusters {
214
+ text-align: center;
215
+ }
216
+
217
+ .info-section {
218
+ margin-top: 60px;
219
+ }
220
+
221
+ .logo {
222
+ display: flex;
223
+ justify-content: center;
224
+ margin: 60px 0 40px 0;
225
+ }
226
+
227
+ .tagline {
228
+ display: flex;
229
+ justify-content: center;
230
+ margin-top: 30px;
231
+
232
+ > div {
233
+ font-size: 16px;
234
+ line-height: 22px;
235
+ max-width: 80%;
236
+ text-align: center;
237
+ }
238
+ }
239
+
240
+ .link {
241
+ cursor: pointer;
242
+ }
243
+
244
+ </style>
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
+ import { NS_SNAPSHOT_QUOTA } from '../config/table-headers';
3
4
  import ResourceTable from '@shell/components/ResourceTable';
4
-
5
+ import { HCI } from '@shell/config/types';
5
6
  export default {
6
7
  name: 'ListNamespace',
7
8
  components: { ResourceTable },
@@ -27,13 +28,23 @@ export default {
27
28
  default: false
28
29
  }
29
30
  },
30
- data() {
31
- return { asddsa: true };
32
- },
33
31
 
34
32
  computed: {
35
33
  ...mapGetters(['currentProduct']),
34
+ hasHarvesterResourceQuotaSchema() {
35
+ const inStore = this.$store.getters['currentProduct'].inStore;
36
+
37
+ return !!this.$store.getters[`${ inStore }/schemaFor`](HCI.RESOURCE_QUOTA);
38
+ },
39
+ headers() {
40
+ const headersFromSchema = this.$store.getters['type-map/headersFor'](this.schema);
36
41
 
42
+ if (this.hasHarvesterResourceQuotaSchema) {
43
+ headersFromSchema.splice(2, 0, NS_SNAPSHOT_QUOTA);
44
+ }
45
+
46
+ return headersFromSchema;
47
+ },
37
48
  filterRow() {
38
49
  if (this.currentProduct.hideSystemResources) {
39
50
  return this.rows.filter( (N) => {
@@ -56,6 +67,7 @@ export default {
56
67
  v-bind="$attrs"
57
68
  :rows="filterRow"
58
69
  :groupable="false"
70
+ :headers="headers"
59
71
  :schema="schema"
60
72
  key-field="_key"
61
73
  :loading="loading"
@@ -25,7 +25,7 @@ export default {
25
25
  mounted() {
26
26
  this.setTabVisibilityListener(true);
27
27
  },
28
- beforeDestroy() {
28
+ beforeUnmount() {
29
29
  this.setTabVisibilityListener(false);
30
30
  },
31
31
  };
package/mixins/chart.js CHANGED
@@ -291,6 +291,8 @@ export default {
291
291
  id: `${ this.query.appNamespace }/${ this.query.appName }`,
292
292
  });
293
293
 
294
+ await this.existing?.fetchValues(true);
295
+
294
296
  this.mode = _EDIT;
295
297
  } catch (e) {
296
298
  this.mode = _CREATE;
@@ -450,10 +452,12 @@ export default {
450
452
  }
451
453
  }
452
454
  if (existingCRDApp) {
455
+ await existingCRDApp.fetchValues(true);
456
+
453
457
  // spec.values are any non-default values the user configured
454
458
  // the installation form should show these, as well as any default values from the chart
455
- const existingValues = clone(existingCRDApp.spec?.values || {});
456
- const defaultValues = clone(existingCRDApp.spec?.chart?.values || {});
459
+ const existingValues = clone(existingCRDApp.values || {});
460
+ const defaultValues = clone(existingCRDApp.chartValues || {});
457
461
 
458
462
  crdVersionInfo.existingValues = existingValues;
459
463
  crdVersionInfo.allValues = merge(defaultValues, existingValues);
@@ -13,7 +13,7 @@ export default {
13
13
  this.metricPoller.start();
14
14
  },
15
15
 
16
- beforeDestroy() {
16
+ beforeUnmount() {
17
17
  this.metricPoller.stop();
18
18
  },
19
19
  };
@@ -48,7 +48,7 @@ export default {
48
48
  };
49
49
  },
50
50
 
51
- beforeDestroy() {
51
+ beforeUnmount() {
52
52
  // make sure this only runs once, for the initialized instance
53
53
  if (this.init) {
54
54
  // clear up the store to make sure we aren't storing anything that might interfere with the next rendered list view
@@ -4,12 +4,34 @@ jest.mock('@shell/utils/clipboard', () => {
4
4
  return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
5
5
  });
6
6
 
7
+ const importedRKE2ClusterInfo = { status: { driver: 'rke2', provider: 'rke2' } };
8
+
9
+ const provisionedRKE2ClusterInfo = { status: { driver: 'rke2', provider: 'imported' } };
10
+
11
+ const importedK3sClusterInfo = { status: { driver: 'k3s', provider: 'k3s' } };
12
+
13
+ const provisionedK3sClusterInfo = { status: { driver: 'k3s', provider: 'imported' } };
14
+
15
+ const importedAksClusterInfo = { spec: { aksConfig: { imported: true } }, status: { provider: 'aks', driver: 'AKS' } };
16
+
17
+ const provisionedAksClusterInfo = { spec: { aksConfig: { imported: false } }, status: { provider: 'aks', driver: 'AKS' } };
18
+
19
+ const importedRKE1ClusterInfo = { status: { provider: 'rke', driver: 'imported' } };
20
+
21
+ const provisionedRKE1ClusterInfo = { status: { provider: 'rke', driver: 'rancherKubernetesEngine' } };
22
+
23
+ const localRKE1ClusterInfo = { status: { provider: 'rke', driver: 'imported' } };
24
+
25
+ const localRKE2ClusterInfo = { status: { provider: 'rke2', driver: 'rke2' } };
26
+
27
+ const localEKSClusterInfo = { status: { provider: 'eks', driver: 'imported' } };
28
+
7
29
  describe('class MgmtCluster', () => {
8
30
  describe('provisioner', () => {
9
31
  const testCases = [
10
- [{ provider: 'rke', driver: 'imported' }, 'rke'],
11
- [{ provider: 'k3s', driver: 'K3S' }, 'k3s'],
12
- [{ provider: 'aks', driver: 'AKS' }, 'aks'],
32
+ [{ provider: 'rke', driver: 'imported' }, 'imported'],
33
+ [{ provider: 'k3s', driver: 'K3S' }, 'K3S'],
34
+ [{ provider: 'aks', driver: 'AKS' }, 'AKS'],
13
35
  [{}, 'imported'],
14
36
  ];
15
37
 
@@ -20,4 +42,24 @@ describe('class MgmtCluster', () => {
20
42
  }
21
43
  );
22
44
  });
45
+
46
+ describe('isImported', () => {
47
+ it.each([
48
+ [importedRKE2ClusterInfo, true],
49
+ [provisionedRKE2ClusterInfo, false],
50
+ [importedK3sClusterInfo, true],
51
+ [provisionedK3sClusterInfo, false],
52
+ [importedAksClusterInfo, true],
53
+ [provisionedAksClusterInfo, false],
54
+ [importedRKE1ClusterInfo, true],
55
+ [provisionedRKE1ClusterInfo, false],
56
+ [localRKE1ClusterInfo, true],
57
+ [localRKE2ClusterInfo, true],
58
+ [localEKSClusterInfo, true]
59
+ ])('should return isImported based on props data', (clusterData, expected) => {
60
+ const cluster = new MgmtCluster(clusterData);
61
+
62
+ expect(cluster.isImported).toBe(expected);
63
+ });
64
+ });
23
65
  });