@rancher/shell 0.3.21 → 0.3.22

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 (35) hide show
  1. package/assets/translations/en-us.yaml +4 -0
  2. package/assets/translations/zh-hans.yaml +8 -1
  3. package/cloud-credential/__tests__/azure.test.ts +53 -0
  4. package/cloud-credential/azure.vue +6 -0
  5. package/components/GrowlManager.vue +33 -30
  6. package/components/form/ResourceQuota/ProjectRow.vue +38 -15
  7. package/components/formatter/ClusterProvider.vue +9 -3
  8. package/components/formatter/__tests__/ClusterProvider.test.ts +5 -1
  9. package/components/nav/Header.vue +1 -0
  10. package/config/settings.ts +59 -2
  11. package/config/types.js +2 -0
  12. package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +28 -0
  13. package/creators/pkg/files/.github/workflows/build-extension-charts.yml +26 -0
  14. package/creators/pkg/init +63 -4
  15. package/detail/provisioning.cattle.io.cluster.vue +4 -2
  16. package/edit/fleet.cattle.io.gitrepo.vue +1 -0
  17. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -4
  18. package/mixins/__tests__/chart.test.ts +40 -0
  19. package/mixins/chart.js +5 -0
  20. package/models/catalog.cattle.io.clusterrepo.js +6 -2
  21. package/models/fleet.cattle.io.cluster.js +10 -2
  22. package/package.json +1 -1
  23. package/pages/c/_cluster/gatekeeper/index.vue +10 -1
  24. package/plugins/steve/__tests__/header-warnings.spec.ts +238 -0
  25. package/plugins/steve/actions.js +4 -23
  26. package/plugins/steve/header-warnings.ts +91 -0
  27. package/promptRemove/management.cattle.io.project.vue +9 -6
  28. package/scripts/extension/parse-tag-name +30 -0
  29. package/types/shell/index.d.ts +1 -0
  30. package/utils/settings.ts +2 -17
  31. package/vue-config-helper.js +135 -0
  32. package/vue.config.js +23 -139
  33. package/yarn-error.log +200 -0
  34. package/creators/pkg/files/.github/workflows/build-container.yml +0 -64
  35. package/creators/pkg/files/.github/workflows/build-extension.yml +0 -110
@@ -2291,6 +2291,7 @@ gatekeeperIndex:
2291
2291
  poweredBy: OPA Gatekeeper
2292
2292
  unavailable: OPA + Gatekeeper is not available in the system-charts catalog.
2293
2293
  violations: Violations
2294
+ deprecated: The OPA Gatekeeper chart is deprecated as of Rancher 2.8 and will be removed in a future release. Please consider switching to <a target="_blank" rel="noopener noreferrer nofollow" href="https://www.kubewarden.io">Kubewarden</a>.
2294
2295
 
2295
2296
  gatekeeperInstall:
2296
2297
  auditInterval: Auto Interval
@@ -2357,6 +2358,9 @@ growl:
2357
2358
  podSecurity:
2358
2359
  message: "The creation of this Pod would violate existing restricted policies for the adopted Namespace"
2359
2360
  title: PodSecurity violation
2361
+ kubeApiHeaderWarning:
2362
+ titleCreate: "{resourceType} Creation Warning"
2363
+ titleUpdate: "{resourceType} Update Warning"
2360
2364
 
2361
2365
  hpa:
2362
2366
  detail:
@@ -3051,6 +3051,7 @@ login:
3051
3051
  noResponse: "没有收到响应。"
3052
3052
  error: 登录时发生错误,请重试。
3053
3053
  clientError: 无效的用户名或密码,请重试。
3054
+ specificError: '登录时发生错误:{msg}'
3054
3055
  useLocal: 使用本地账号登录
3055
3056
  loginWithProvider: 使用 {provider} 登录
3056
3057
  username: 用户名
@@ -3525,8 +3526,11 @@ navLink:
3525
3526
  label: 链接标签
3526
3527
  description:
3527
3528
  label: 链接描述
3529
+ groupImage:
3530
+ label: 组图片
3528
3531
  iconSrc:
3529
- label: 添加镜像
3532
+ tip: '图片的高度应为 21 像素,最大宽度为 200 像素。最大文件大小为 20KB。支持的格式:JPEG、PNG、SVG。'
3533
+ label: 添加图片
3530
3534
  networkpolicy:
3531
3535
  egress:
3532
3536
  label: Egress 规则
@@ -5640,6 +5644,8 @@ validation:
5640
5644
  logdna:
5641
5645
  apiKey: 需要设置“API 密钥”。
5642
5646
  invalidCron: 无效的 cron 调度
5647
+ invalidCidr: "无效的 CIDR"
5648
+ invalidIP: "无效的 IP"
5643
5649
  k8s:
5644
5650
  name: 必须由小写字母数字或“-”组成,并且开头和结尾必须是字母数字(例如“my-name”或“123-abc”)。
5645
5651
  identifier:
@@ -5776,6 +5782,7 @@ wm:
5776
5782
  containerShell:
5777
5783
  clear: 清除
5778
5784
  containerName: "容器:{label}"
5785
+ failed: "无法打开容器 shell(所有 shell 命令均未成功)\n\r"
5779
5786
  kubectlShell:
5780
5787
  title: "Kubectl: {name}"
5781
5788
 
@@ -0,0 +1,53 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import AzureCloudCreds from '@shell/cloud-credential/azure.vue';
3
+
4
+ const mockStore = {
5
+ getters: { 'i18n/t': jest.fn() },
6
+ dispatch: () => jest.fn()
7
+ };
8
+
9
+ describe('cloud credentials: Azure', () => {
10
+ const wrapper = mount(AzureCloudCreds, {
11
+ propsData: {
12
+ value: {
13
+ decodedData: {
14
+ environment: '',
15
+ subscriptionId: '',
16
+ clientId: '',
17
+ clientSecret: '',
18
+ },
19
+ setData: jest.fn()
20
+ }
21
+ },
22
+ mocks: { $store: mockStore }
23
+ });
24
+
25
+ it('should pass all the correct fields when checking if credentials are valid', async() => {
26
+ const spyDispatch = jest.spyOn(mockStore, 'dispatch');
27
+
28
+ wrapper.setData({
29
+ value: {
30
+ decodedData: {
31
+ environment: 'my-env',
32
+ subscriptionId: 'my-sub-id',
33
+ clientId: 'my-c-id',
34
+ clientSecret: 'my-c-secret',
35
+ }
36
+ }
37
+ });
38
+
39
+ await wrapper.vm.test();
40
+
41
+ expect(spyDispatch).toHaveBeenCalledWith('management/request', {
42
+ data: {
43
+ environment: 'my-env',
44
+ subscriptionId: 'my-sub-id',
45
+ clientId: 'my-c-id',
46
+ clientSecret: 'my-c-secret',
47
+ },
48
+ method: 'POST',
49
+ redirectUnauthorized: false,
50
+ url: '/meta/aksCheckCredentials'
51
+ });
52
+ });
53
+ });
@@ -40,6 +40,7 @@ export default {
40
40
  clientId,
41
41
  clientSecret,
42
42
  subscriptionId,
43
+ environment,
43
44
  } = this.value.decodedData;
44
45
 
45
46
  try {
@@ -50,6 +51,7 @@ export default {
50
51
  clientId,
51
52
  clientSecret,
52
53
  subscriptionId,
54
+ environment,
53
55
  },
54
56
  redirectUnauthorized: false,
55
57
  });
@@ -96,6 +98,7 @@ export default {
96
98
  :searchable="false"
97
99
  :required="true"
98
100
  :label="t('cluster.credential.azure.environment.label')"
101
+ data-testid="azure-cloud-credentials-environment"
99
102
  @input="value.setData('environment', $event)"
100
103
  />
101
104
  </div>
@@ -106,6 +109,7 @@ export default {
106
109
  type="text"
107
110
  :mode="mode"
108
111
  :required="true"
112
+ data-testid="azure-cloud-credentials-subscription-id"
109
113
  @input="value.setData('subscriptionId', $event)"
110
114
  />
111
115
  </div>
@@ -118,6 +122,7 @@ export default {
118
122
  type="text"
119
123
  :mode="mode"
120
124
  :required="true"
125
+ data-testid="azure-cloud-credentials-client-id"
121
126
  @input="value.setData('clientId', $event)"
122
127
  />
123
128
  </div>
@@ -128,6 +133,7 @@ export default {
128
133
  type="password"
129
134
  :mode="mode"
130
135
  :required="true"
136
+ data-testid="azure-cloud-credentials-client-secret"
131
137
  @input="value.setData('clientSecret', $event)"
132
138
  />
133
139
  </div>
@@ -99,15 +99,17 @@ export default {
99
99
  <i :class="{icon: true, ['icon-'+growl.icon]: true}" />
100
100
  </div>
101
101
  <div class="growl-text">
102
- <div>{{ growl.title }}</div>
102
+ <i
103
+ class="close hand icon icon-close"
104
+ @click="close(growl)"
105
+ />
106
+ <div class="growl-text-title">
107
+ {{ growl.title }}
108
+ </div>
103
109
  <p v-if="growl.message">
104
110
  {{ growl.message }}
105
111
  </p>
106
112
  </div>
107
- <i
108
- class="close hand icon icon-close"
109
- @click="close(growl)"
110
- />
111
113
  </div>
112
114
  </div>
113
115
  </div>
@@ -135,7 +137,7 @@ export default {
135
137
  width: 100%;
136
138
 
137
139
  @media only screen and (min-width: map-get($breakpoints, '--viewport-7')) {
138
- width: 350px;
140
+ width: 420px;
139
141
  }
140
142
  }
141
143
 
@@ -149,13 +151,15 @@ export default {
149
151
  margin: 10px;
150
152
  position: relative;
151
153
  word-break: break-all;
152
-
153
- .close {
154
- padding: 5px;
155
- }
154
+ box-shadow: 0 3px 5px 0px var(--shadow);
156
155
 
157
156
  .icon-container {
158
157
  align-self: center;
158
+ flex-basis: 10%;
159
+ padding: 10px 20px 10px 10px;
160
+ i {
161
+ font-size: 24px;
162
+ }
159
163
  }
160
164
 
161
165
  .growl-message {
@@ -164,28 +168,27 @@ export default {
164
168
  &.growl-center {
165
169
  align-items: center;
166
170
  }
167
- }
168
-
169
- .growl-text {
170
- flex-basis: 90%;
171
- padding: 10px 10px 10px 0;
172
- word-break: break-word;
173
- white-space: normal;
174
-
175
- > div {
176
- font-size: 16px;
177
- }
178
171
 
179
- > P {
180
- margin-top: 5px;
181
- }
182
- }
172
+ .growl-text {
173
+ position: relative;
174
+ flex-basis: 90%;
175
+ padding: 10px 10px 10px 0;
176
+ word-break: break-word;
177
+ white-space: normal;
178
+
179
+ .close {
180
+ position: absolute;
181
+ top: 12px;
182
+ right: 10px;
183
+ }
184
+ .growl-text-title {
185
+ font-size: 16px;
186
+ margin-bottom: 20px;
187
+ }
183
188
 
184
- .icon-container {
185
- flex-basis: 10%;
186
- padding: 10px;
187
- i {
188
- font-size: 24px;
189
+ > P {
190
+ margin-top: 5px;
191
+ }
189
192
  }
190
193
  }
191
194
  }
@@ -27,37 +27,58 @@ export default {
27
27
  }
28
28
  },
29
29
 
30
- computed: { ...ROW_COMPUTED },
30
+ computed: {
31
+ ...ROW_COMPUTED,
32
+
33
+ resourceQuotaLimit: {
34
+ get() {
35
+ return this.value.spec.resourceQuota?.limit || {};
36
+ },
37
+ },
38
+
39
+ namespaceDefaultResourceQuotaLimit: {
40
+ get() {
41
+ return this.value.spec.namespaceDefaultResourceQuota?.limit || {};
42
+ },
43
+ }
44
+ },
31
45
 
32
46
  methods: {
33
47
  updateType(type) {
34
- if (typeof this.value.spec.resourceQuota.limit[this.type] !== 'undefined') {
48
+ if (typeof this.value.spec.resourceQuota?.limit[this.type] !== 'undefined') {
35
49
  this.$delete(this.value.spec.resourceQuota.limit, this.type);
36
50
  }
37
-
38
- if (typeof this.value.spec.namespaceDefaultResourceQuota.limit[this.type] !== 'undefined') {
51
+ if (typeof this.value.spec.namespaceDefaultResourceQuota?.limit[this.type] !== 'undefined') {
39
52
  this.$delete(this.value.spec.namespaceDefaultResourceQuota.limit, this.type);
40
53
  }
41
54
 
42
55
  this.$emit('type-change', type);
56
+ },
57
+
58
+ updateQuotaLimit(prop, type, val) {
59
+ if (!this.value.spec[prop]) {
60
+ this.value.spec[prop] = { limit: { } };
61
+ }
62
+
63
+ this.value.spec[prop].limit[type] = val;
43
64
  }
44
65
  },
45
66
  };
46
67
  </script>
47
68
  <template>
48
- <div
49
- v-if="typeOption"
50
- class="row"
69
+ <div
70
+ v-if="typeOption"
71
+ class="row"
51
72
  >
52
- <Select
53
- class="mr-10"
54
- :mode="mode"
55
- :value="type"
56
- :options="types"
57
- @input="updateType($event)"
73
+ <Select
74
+ class="mr-10"
75
+ :mode="mode"
76
+ :value="type"
77
+ :options="types"
78
+ @input="updateType($event)"
58
79
  />
59
80
  <UnitInput
60
- v-model="value.spec.resourceQuota.limit[type]"
81
+ :value="resourceQuotaLimit[type]"
61
82
  class="mr-10"
62
83
  :mode="mode"
63
84
  :placeholder="typeOption.placeholder"
@@ -65,15 +86,17 @@ export default {
65
86
  :input-exponent="typeOption.inputExponent"
66
87
  :base-unit="typeOption.baseUnit"
67
88
  :output-modifier="true"
89
+ @input="updateQuotaLimit('resourceQuota', type, $event)"
68
90
  />
69
91
  <UnitInput
70
- v-model="value.spec.namespaceDefaultResourceQuota.limit[type]"
92
+ :value="namespaceDefaultResourceQuotaLimit[type]"
71
93
  :mode="mode"
72
94
  :placeholder="typeOption.placeholder"
73
95
  :increment="typeOption.increment"
74
96
  :input-exponent="typeOption.inputExponent"
75
97
  :base-unit="typeOption.baseUnit"
76
98
  :output-modifier="true"
99
+ @input="updateQuotaLimit('namespaceDefaultResourceQuota', type, $event)"
77
100
  />
78
101
  </div>
79
102
  </template>
@@ -7,14 +7,20 @@ export default {
7
7
  },
8
8
  },
9
9
  data(props) {
10
+ const mgmt = props.row?.mgmt;
11
+
10
12
  return {
11
13
  // The isImported getter on the provisioning cluster
12
14
  // model doesn't work for imported K3s clusters, in
13
15
  // which case it returns 'k3s' instead of 'imported.'
14
16
  // This is the workaround.
15
- isImported: props.row?.mgmt?.providerForEmberParam === 'import' ||
16
- // when imported cluster is Google GKE
17
- props.row?.mgmt?.spec?.gkeConfig?.imported
17
+ isImported: mgmt?.providerForEmberParam === 'import' ||
18
+ // when imported cluster is GKE
19
+ !!mgmt?.spec?.gkeConfig?.imported ||
20
+ // or AKS
21
+ !!mgmt?.spec?.aksConfig?.imported ||
22
+ // or EKS
23
+ !!mgmt?.spec?.eksConfig?.imported
18
24
  };
19
25
  },
20
26
  };
@@ -3,15 +3,19 @@ import ClusterProvider from '@shell/components/formatter/ClusterProvider.vue';
3
3
 
4
4
  describe('component: ClusterProvider', () => {
5
5
  const importedGkeClusterInfo = { mgmt: { spec: { gkeConfig: { imported: true } } } };
6
+ const importedAksClusterInfo = { mgmt: { spec: { aksConfig: { imported: true } } } };
7
+ const importedEksClusterInfo = { mgmt: { spec: { eksConfig: { imported: true } } } };
6
8
  const notImportedGkeClusterInfo = { mgmt: { spec: { gkeConfig: { imported: false } } } };
7
9
  const importedClusterInfoWithProviderForEmberParam = { mgmt: { providerForEmberParam: 'import' } };
8
10
 
9
11
  describe('isImported', () => {
10
12
  const testCases = [
11
13
  [importedGkeClusterInfo, true],
14
+ [importedAksClusterInfo, true],
15
+ [importedEksClusterInfo, true],
12
16
  [notImportedGkeClusterInfo, false],
13
17
  [importedClusterInfoWithProviderForEmberParam, true],
14
- [{}, undefined],
18
+ [{}, false],
15
19
  ];
16
20
 
17
21
  it.each(testCases)('should return the isImported value properly based on the props data', (row, expected) => {
@@ -335,6 +335,7 @@ export default {
335
335
  <template>
336
336
  <header
337
337
  ref="header"
338
+ data-testid="header"
338
339
  >
339
340
  <div>
340
341
  <TopLevelMenu v-if="isRancherInHarvester || isMultiCluster || !isSingleProduct" />
@@ -1,5 +1,5 @@
1
1
  // Settings
2
- import { GC_DEFAULTS } from '../utils/gc/gc-types';
2
+ import { GC_DEFAULTS, GC_PREFERENCES } from '@shell/utils/gc/gc-types';
3
3
 
4
4
  interface GlobalSettingRuleset {
5
5
  name: string,
@@ -148,7 +148,47 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
148
148
  [SETTING.HIDE_LOCAL_CLUSTER]: { kind: 'boolean' },
149
149
  };
150
150
 
151
- export const DEFAULT_PERF_SETTING = {
151
+ /**
152
+ * Settings on how to handle warnings returning in api responses, specifically which to show as growls
153
+ */
154
+ export interface PerfSettingsWarningHeaders {
155
+ /**
156
+ * Warning is a string containing multiple entries. This determines how they are split up
157
+ *
158
+ * See https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/1693-warnings#design-details
159
+ */
160
+ separator: string,
161
+ /**
162
+ * Show warnings in a notification if they're not in this block list
163
+ */
164
+ notificationBlockList: string[]
165
+ }
166
+
167
+ export interface PerfSettingsKubeApi {
168
+ /**
169
+ * Settings related to the response header `warnings` value
170
+ */
171
+ warningHeader: PerfSettingsWarningHeaders
172
+ }
173
+
174
+ export interface PerfSettings {
175
+ inactivity: {
176
+ enabled: boolean;
177
+ threshold: number;
178
+ };
179
+ incrementalLoading: {
180
+ enabled: boolean;
181
+ threshold: number;
182
+ };
183
+ manualRefresh: {};
184
+ disableWebsocketNotification: boolean;
185
+ garbageCollection: GC_PREFERENCES;
186
+ forceNsFilterV2: any;
187
+ advancedWorker: {};
188
+ kubeAPI: PerfSettingsKubeApi;
189
+ }
190
+
191
+ export const DEFAULT_PERF_SETTING: PerfSettings = {
152
192
  inactivity: {
153
193
  enabled: false,
154
194
  threshold: 900,
@@ -165,4 +205,21 @@ export const DEFAULT_PERF_SETTING = {
165
205
  garbageCollection: GC_DEFAULTS,
166
206
  forceNsFilterV2: { enabled: false },
167
207
  advancedWorker: { enabled: false },
208
+ kubeAPI: {
209
+ /**
210
+ * Settings related to the response header `warnings` value
211
+ */
212
+ warningHeader: {
213
+ /**
214
+ * Warning is a string containing multiple entries. This determines how they are split up
215
+ *
216
+ * See https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/1693-warnings#design-details
217
+ */
218
+ separator: '299 - ',
219
+ /**
220
+ * Show warnings in a notification if they're not in this block list
221
+ */
222
+ notificationBlockList: ['299 - unknown field']
223
+ }
224
+ }
168
225
  };
package/config/types.js CHANGED
@@ -311,3 +311,5 @@ export const AUTH_TYPE = {
311
311
  _SSH: '_ssh',
312
312
  _S3: '_S3'
313
313
  };
314
+
315
+ export const LOCAL_CLUSTER = 'local';
@@ -0,0 +1,28 @@
1
+ name: Build and Release Extension Catalog
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches:
7
+ - main
8
+ pull_request:
9
+ branches:
10
+ - main
11
+
12
+ defaults:
13
+ run:
14
+ shell: bash
15
+ working-directory: ./
16
+
17
+ jobs:
18
+ build-extension-catalog:
19
+ uses: rancher/dashboard/.github/workflows/build-extension-catalog.yml@master
20
+ permissions:
21
+ actions: write
22
+ contents: read
23
+ packages: write
24
+ with:
25
+ registry_target: ghcr.io
26
+ registry_user: ${{ github.actor }}
27
+ secrets:
28
+ registry_token: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,26 @@
1
+ name: Build and Release Extension Charts
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches:
7
+ - main
8
+ pull_request:
9
+ branches:
10
+ - main
11
+
12
+ defaults:
13
+ run:
14
+ shell: bash
15
+ working-directory: ./
16
+
17
+ jobs:
18
+ build-extension-charts:
19
+ uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
20
+ permissions:
21
+ actions: write
22
+ contents: write
23
+ deployments: write
24
+ pages: write
25
+ with:
26
+ target_branch: gh-pages
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