@rancher/shell 3.0.2-rc.2 → 3.0.2-rc.3

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 (141) hide show
  1. package/assets/styles/base/_basic.scss +5 -7
  2. package/assets/styles/global/_button.scss +10 -0
  3. package/assets/styles/global/_tooltip.scss +2 -2
  4. package/assets/styles/themes/_dark.scss +14 -2
  5. package/assets/styles/themes/_light.scss +7 -2
  6. package/assets/styles/vendor/vue-select.scss +4 -0
  7. package/assets/translations/en-us.yaml +44 -5
  8. package/components/BannerGraphic.vue +0 -42
  9. package/components/ButtonMultiAction.vue +1 -1
  10. package/components/Carousel.vue +36 -29
  11. package/components/CommunityLinks.vue +6 -1
  12. package/components/GrowlManager.vue +9 -2
  13. package/components/LocaleSelector.vue +8 -1
  14. package/components/PaginatedResourceTable.vue +4 -7
  15. package/components/ProgressBarMulti.vue +14 -0
  16. package/components/Questions/Reference.vue +57 -28
  17. package/components/SelectIconGrid.vue +12 -1
  18. package/components/SideNav.vue +12 -38
  19. package/components/SortableTable/index.vue +1 -0
  20. package/components/Tabbed/index.vue +12 -1
  21. package/components/YamlEditor.vue +1 -0
  22. package/components/auth/Principal.vue +5 -3
  23. package/components/fleet/FleetClusters.vue +82 -1
  24. package/components/fleet/FleetRepos.vue +13 -30
  25. package/components/fleet/ForceDirectedTreeChart/index.vue +2 -2
  26. package/components/form/ChangePassword.vue +2 -0
  27. package/components/form/ColorInput.vue +24 -1
  28. package/components/form/FileSelector.vue +2 -0
  29. package/components/form/KeyValue.vue +230 -160
  30. package/components/form/LabeledSelect.vue +1 -1
  31. package/components/form/PlusMinus.vue +14 -2
  32. package/components/form/ResourceLabeledSelect.vue +13 -53
  33. package/components/form/ResourceSelector.vue +1 -0
  34. package/components/form/ResourceTabs/index.vue +79 -36
  35. package/components/form/SecretSelector.vue +2 -2
  36. package/components/form/__tests__/KeyValue.test.ts +1 -1
  37. package/components/formatter/FleetClusterSummaryGraph.vue +2 -2
  38. package/components/formatter/FleetSummaryGraph.vue +6 -7
  39. package/components/formatter/WorkloadHealthScale.vue +7 -0
  40. package/components/nav/Group.vue +30 -4
  41. package/components/nav/Header.vue +82 -114
  42. package/components/nav/HeaderPageActionMenu.vue +27 -131
  43. package/components/nav/NamespaceFilter.vue +1 -1
  44. package/components/nav/Type.vue +15 -0
  45. package/config/home-links.js +21 -13
  46. package/config/labels-annotations.js +2 -0
  47. package/config/page-actions.js +1 -0
  48. package/config/pagination-table-headers.js +15 -1
  49. package/config/product/explorer.js +7 -17
  50. package/config/table-headers.js +6 -0
  51. package/config/version.js +5 -1
  52. package/core/plugin.ts +41 -1
  53. package/core/plugins.js +125 -72
  54. package/core/types-provisioning.ts +91 -2
  55. package/core/types.ts +55 -0
  56. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +12 -3
  57. package/detail/catalog.cattle.io.app.vue +1 -1
  58. package/detail/fleet.cattle.io.cluster.vue +3 -3
  59. package/detail/namespace.vue +13 -19
  60. package/detail/networking.k8s.io.ingress.vue +13 -53
  61. package/detail/provisioning.cattle.io.cluster.vue +12 -1
  62. package/detail/workload/index.vue +3 -3
  63. package/dialog/AddCustomBadgeDialog.vue +5 -1
  64. package/edit/auth/ldap/__tests__/config.test.ts +18 -0
  65. package/edit/auth/ldap/config.vue +24 -0
  66. package/edit/auth/saml.vue +8 -6
  67. package/edit/fleet.cattle.io.gitrepo.vue +7 -1
  68. package/edit/logging-flow/index.vue +4 -19
  69. package/edit/networking.k8s.io.ingress/index.vue +18 -65
  70. package/edit/networking.k8s.io.networkpolicy/index.vue +4 -5
  71. package/edit/provisioning.cattle.io.cluster/index.vue +13 -1
  72. package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -115
  73. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +2 -2
  74. package/edit/provisioning.cattle.io.cluster/tabs/networking/ACE.vue +14 -28
  75. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +25 -12
  76. package/edit/service.vue +1 -2
  77. package/list/networking.k8s.io.ingress.vue +1 -1
  78. package/list/node.vue +15 -8
  79. package/list/persistentvolume.vue +12 -4
  80. package/list/service.vue +1 -1
  81. package/list/workload.vue +4 -0
  82. package/mixins/chart.js +4 -1
  83. package/models/catalog.cattle.io.app.js +3 -1
  84. package/models/catalog.cattle.io.clusterrepo.js +56 -7
  85. package/models/fleet.cattle.io.bundle.js +0 -11
  86. package/models/fleet.cattle.io.cluster.js +17 -1
  87. package/models/fleet.cattle.io.gitrepo.js +86 -50
  88. package/models/provisioning.cattle.io.cluster.js +47 -2
  89. package/models/service.js +1 -0
  90. package/models/workload.js +19 -1
  91. package/package.json +5 -4
  92. package/pages/c/_cluster/apps/charts/index.vue +4 -0
  93. package/pages/c/_cluster/explorer/ConfigBadge.vue +8 -7
  94. package/pages/c/_cluster/explorer/index.vue +13 -6
  95. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +3 -3
  96. package/pages/c/_cluster/fleet/index.vue +75 -89
  97. package/pages/c/_cluster/settings/links.vue +2 -2
  98. package/pages/diagnostic.vue +17 -15
  99. package/pages/home.vue +32 -6
  100. package/plugins/clean-html.js +50 -0
  101. package/plugins/dashboard-store/resource-class.js +4 -0
  102. package/plugins/plugin.js +54 -49
  103. package/plugins/steve/mutations.js +1 -1
  104. package/plugins/steve/steve-class.js +8 -0
  105. package/plugins/steve/steve-pagination-utils.ts +3 -1
  106. package/rancher-components/Accordion/Accordion.vue +4 -4
  107. package/rancher-components/BadgeState/BadgeState.vue +7 -0
  108. package/rancher-components/Card/Card.vue +27 -1
  109. package/rancher-components/Form/Checkbox/Checkbox.vue +9 -2
  110. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  111. package/rancher-components/Form/LabeledInput/LabeledInput.vue +18 -1
  112. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +39 -2
  113. package/rancher-components/RcButton/RcButton.vue +90 -0
  114. package/rancher-components/RcButton/index.ts +2 -0
  115. package/rancher-components/RcButton/types.ts +17 -0
  116. package/rancher-components/RcDropdown/RcDropdown.vue +111 -0
  117. package/rancher-components/RcDropdown/RcDropdownItem.vue +127 -0
  118. package/rancher-components/RcDropdown/RcDropdownSeparator.vue +6 -0
  119. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +43 -0
  120. package/rancher-components/RcDropdown/index.ts +4 -0
  121. package/rancher-components/RcDropdown/types.ts +22 -0
  122. package/rancher-components/RcDropdown/useDropdownCollection.ts +45 -0
  123. package/rancher-components/RcDropdown/useDropdownContext.ts +83 -0
  124. package/scripts/test-plugins-build.sh +2 -0
  125. package/scripts/typegen.sh +2 -0
  126. package/store/catalog.js +1 -1
  127. package/tsconfig.json +2 -1
  128. package/types/components/paginatedResourceTable.ts +25 -0
  129. package/types/components/resourceLabeledSelect.ts +48 -0
  130. package/types/resources/fleet.d.ts +17 -0
  131. package/types/shell/index.d.ts +61 -0
  132. package/utils/auth.js +5 -1
  133. package/utils/cluster.js +106 -0
  134. package/utils/fleet.ts +35 -3
  135. package/utils/ingress.ts +64 -0
  136. package/utils/uiplugins.ts +56 -44
  137. package/utils/validators/cron-schedule.js +7 -2
  138. package/utils/validators/formRules/__tests__/index.test.ts +53 -17
  139. package/utils/validators/formRules/index.ts +20 -5
  140. package/vue.config.js +1 -1
  141. package/components/RelatedWorkloadsTable.vue +0 -50
@@ -0,0 +1,64 @@
1
+ import { SECRET, SERVICE } from '@shell/config/types';
2
+ import { SECRET_TYPES as TYPES } from '@shell/config/secret';
3
+ import { VuexStore } from '@shell/types/store/vuex';
4
+
5
+ /**
6
+ * Helper class for common functionality shared between the detail and edit ingress pages
7
+ *
8
+ * This could be an untyped mixin.. but this setups up us better for the future
9
+ */
10
+ class IngressDetailEditHelper {
11
+ private $store: VuexStore;
12
+ private namespace: string;
13
+
14
+ constructor({
15
+ $store,
16
+ namespace,
17
+ }: {
18
+ $store: VuexStore
19
+ namespace: string,
20
+ }) {
21
+ this.$store = $store;
22
+ this.namespace = namespace;
23
+ }
24
+
25
+ /**
26
+ * Fetch services that will either be used to show
27
+ * - Create - the possible rule's target service
28
+ * - Edit - the selected and possible rule's target service
29
+ * - Detail - the selected rule's target service
30
+ */
31
+ async fetchServices(args?: { namespace: string}): Promise<any[]> {
32
+ return this.$store.dispatch('cluster/findAll', { type: SERVICE, opt: { namespaced: args?.namespace || this.namespace } });
33
+ }
34
+
35
+ /**
36
+ * Fetch secrets that will either be used to show
37
+ * - Create - the possible secrets to use as a certificates
38
+ * - Edit - the selected and possible secrets to use as a certificates
39
+ * - Detail - the selected secrets to use as certificates
40
+ */
41
+ async fetchSecrets(args?: { namespace: string}): Promise<any[]> {
42
+ return this.$store.dispatch('cluster/findAll', { type: SECRET, opt: { namespaced: args?.namespace || this.namespace } });
43
+ }
44
+
45
+ findAndMapCerts(secrets: any[]) {
46
+ return secrets
47
+ .filter((secret) => secret._type === TYPES.TLS)
48
+ .map((secret) => {
49
+ const { id } = secret;
50
+
51
+ return id.slice(id.indexOf('/') + 1);
52
+ });
53
+ }
54
+
55
+ findAndMapServiceTargets(services: any[]) {
56
+ return services.map((service) => ({
57
+ label: service.metadata.name,
58
+ value: service.metadata.name,
59
+ ports: service.spec.ports?.map((p: any) => p.port)
60
+ }));
61
+ }
62
+ }
63
+
64
+ export default IngressDetailEditHelper;
@@ -1,3 +1,4 @@
1
+ import { matchesSomeRegex } from '@shell/utils/string';
1
2
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
2
3
  import { CATALOG } from '@shell/config/types';
3
4
  import { UI_PLUGIN_BASE_URL, isSupportedChartVersion } from '@shell/config/uiplugins';
@@ -158,37 +159,56 @@ export async function installHelmChart(repo: any, chart: any, values: any = {},
158
159
  }
159
160
 
160
161
  /**
161
- * Get the Helm repository object
162
162
  *
163
163
  * @param store Vue store
164
- * @param url The url of the Helm repository
165
- * @param branch The branch of the Helm repository
164
+ * @param url Repository Url
166
165
  * @returns HelmRepository
167
166
  */
168
- export async function getHelmRepository(store: any, url: string, branch?: string): Promise<HelmRepository> {
167
+ export async function getHelmRepositoryExact(store: any, url: string): Promise<HelmRepository> {
168
+ return await getHelmRepository(store, (repository: any) => {
169
+ const target = repository.spec?.gitRepo || repository.spec?.url;
170
+
171
+ return target === url;
172
+ });
173
+ }
174
+
175
+ /**
176
+ *
177
+ * @param store Vue store
178
+ * @param urlRegexes Regex to match a community repository
179
+ * @returns HelmRepository
180
+ */
181
+ export async function getHelmRepositoryMatch(store: any, urlRegexes: string[]): Promise<HelmRepository> {
182
+ return await getHelmRepository(store, (repository: any) => {
183
+ const target = repository.spec?.gitBranch ? repository.spec?.gitRepo : repository.spec?.url;
184
+
185
+ return matchesSomeRegex(target, urlRegexes);
186
+ });
187
+ }
188
+
189
+ /**
190
+ *
191
+ * @param store Vue store
192
+ * @param matchFn Match function for repository's urls
193
+ * @returns HelmRepository
194
+ */
195
+ async function getHelmRepository(store: any, matchFn: (repository: any) => boolean): Promise<HelmRepository> {
169
196
  if (store.getters['management/schemaFor'](CATALOG.CLUSTER_REPO)) {
170
197
  const repos = await store.dispatch('management/findAll', { type: CATALOG.CLUSTER_REPO, opt: { force: true, watch: false } });
171
198
 
172
- return repos.find((r: any) => {
173
- const target = branch ? r.spec?.gitRepo : r.spec?.url ;
174
-
175
- return target === url;
176
- });
199
+ return repos.find(matchFn);
177
200
  } else {
178
201
  throw new Error('No permissions');
179
202
  }
180
203
  }
181
204
 
182
205
  /**
183
- * Refresh the Helm repository
184
- * Ensures that we find the latest extension versions
185
206
  *
186
207
  * @param store Vue store
187
- * @param gitRepo Extension Repository url
188
- * @param gitBranch Extension Repository branch
208
+ * @param url Repository Url
189
209
  */
190
- export async function refreshHelmRepository(store: any, gitRepo: string, gitBranch: string): Promise<void> {
191
- const repository = await getHelmRepository(store, gitRepo, gitBranch);
210
+ export async function refreshHelmRepository(store: any, url: string): Promise<void> {
211
+ const repository = await getHelmRepositoryExact(store, url);
192
212
 
193
213
  const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');
194
214
 
@@ -202,40 +222,32 @@ export async function refreshHelmRepository(store: any, gitRepo: string, gitBran
202
222
  }
203
223
 
204
224
  /**
205
- * Ensure the required Helm Repository exits, if it does not, add it with the specified name
206
- *
207
- * Wait until the newly added repository has been downloaded
208
225
  *
209
226
  * @param store Vue store
210
- * @param url The url of the Helm repository
211
- * @param name The name of the cluster repository
212
- * @param branch The branch of the Helm repository
213
- * @returns HelmRepository object
227
+ * @param name Repository name
228
+ * @param url Repository Url
229
+ * @param branch Repository Branch
230
+ * @returns HelmRepository
214
231
  */
215
- export async function ensureHelmRepository(store: any, url: string, name: string, branch?: string): Promise<HelmRepository> {
216
- let helmRepo = await getHelmRepository(store, url, branch);
217
-
218
- // Add the Helm repository, if it is not there
219
- if (!helmRepo) {
220
- const data = {
221
- type: CATALOG.CLUSTER_REPO,
222
- metadata: { name },
223
- spec: {} as any
224
- };
225
-
226
- if (branch) {
227
- data.spec.gitBranch = branch;
228
- data.spec.gitRepo = url;
229
- } else {
230
- data.spec.url = url;
231
- }
232
-
233
- // Create a model for the new repository and save it
234
- const repo = await store.dispatch('management/create', data);
232
+ export async function createHelmRepository(store: any, name: string, url: string, branch?: string): Promise<HelmRepository> {
233
+ const data = {
234
+ type: CATALOG.CLUSTER_REPO,
235
+ metadata: { name },
236
+ spec: {} as any
237
+ };
235
238
 
236
- helmRepo = await repo.save();
239
+ if (branch) {
240
+ data.spec.gitBranch = branch;
241
+ data.spec.gitRepo = url;
242
+ } else {
243
+ data.spec.url = url;
237
244
  }
238
245
 
246
+ // Create a model for the new repository and save it
247
+ const repo = await store.dispatch('management/create', data);
248
+
249
+ const helmRepo = await repo.save();
250
+
239
251
  // Poll the repository until it says it has been downloaded
240
252
  let fetched = false;
241
253
  let tries = 0;
@@ -278,7 +290,7 @@ export async function ensureHelmRepository(store: any, url: string, name: string
278
290
  * Get the given Helm Chart from the specified Helm Repository
279
291
  *
280
292
  * @param store Vue store
281
- * @param repository Helm Repository
293
+ * @param repository Repository Url
282
294
  * @param chartName Helm Chart name
283
295
  * @returns Helm Chart
284
296
  */
@@ -2,8 +2,13 @@ import cronstrue from 'cronstrue';
2
2
 
3
3
  export function cronSchedule(schedule = '', getters, errors) {
4
4
  try {
5
- cronstrue.toString(schedule, { verbose: true });
5
+ cronScheduleRule.validation(schedule);
6
6
  } catch (e) {
7
- errors.push(getters['i18n/t']('validation.invalidCron'));
7
+ errors.push(getters['i18n/t'](cronScheduleRule.message));
8
8
  }
9
9
  }
10
+
11
+ export const cronScheduleRule = {
12
+ validation: (text) => cronstrue.toString(text, { verbose: true }),
13
+ message: 'validation.invalidCron'
14
+ };
@@ -27,22 +27,6 @@ describe('formRules', () => {
27
27
  expect(formRuleResult).toStrictEqual(expectedResult);
28
28
  });
29
29
 
30
- it('"cronSchedule" : returns undefined when valid cron string value supplied', () => {
31
- const testValue = '0 * * * *';
32
- const formRuleResult = formRules.cronSchedule(testValue);
33
-
34
- expect(formRuleResult).toBeUndefined();
35
- });
36
-
37
- it('"cronSchedule" : returns the correct message when invalid cron string value supplied', () => {
38
- // specific logic of what constitutes a cron string is in the "cronstrue" function in an external library and not tested here
39
- const testValue = '0 * * **';
40
- const formRuleResult = formRules.cronSchedule(testValue);
41
- const expectedResult = JSON.stringify({ message: 'validation.invalidCron' });
42
-
43
- expect(formRuleResult).toStrictEqual(expectedResult);
44
- });
45
-
46
30
  it('"https" : returns undefined when valid https url value is supplied', () => {
47
31
  const testValue = 'https://url.com';
48
32
  const formRuleResult = formRules.https(testValue);
@@ -103,6 +87,45 @@ describe('formRules', () => {
103
87
  );
104
88
  });
105
89
 
90
+ describe('gitRepository', () => {
91
+ const message = JSON.stringify({ message: 'validation.git.repository' });
92
+ const testCases = [
93
+ // Valid HTTP(s)
94
+ ['https://github.com/rancher/dashboard.git', undefined],
95
+ ['http://github.com/rancher/dashboard.git', undefined],
96
+ ['https://github.com/rancher/dashboard', undefined],
97
+ ['https://github.com/rancher/dashboard/', undefined],
98
+
99
+ // Valid SSH
100
+ ['git@github.com:rancher/dashboard.git', undefined],
101
+ ['git@github.com:rancher/dashboard', undefined],
102
+ ['git@github.com:rancher/dashboard/', undefined],
103
+
104
+ // Not valid HTTP(s)
105
+ ['https://github.com/rancher/ dashboard.git', message],
106
+ ['http://github.com/rancher/ dashboard.git', message],
107
+ ['https://github.com/rancher/dashboard ', message],
108
+ ['foo://github.com/rancher/dashboard/', message],
109
+ ['github.com/rancher/dashboard/', message],
110
+
111
+ // Not valid SSH
112
+ ['git@github.com:rancher/ dashboard.git', message],
113
+ ['git@github.com:rancher/dashboard ', message],
114
+ ['git@github.comrancher/dashboard', message],
115
+
116
+ [undefined, undefined]
117
+ ];
118
+
119
+ it.each(testCases)(
120
+ 'should return undefined or correct message based on the provided Git url: %p',
121
+ (url, expected) => {
122
+ const formRuleResult = formRules.gitRepository(url);
123
+
124
+ expect(formRuleResult).toStrictEqual(expected);
125
+ }
126
+ );
127
+ });
128
+
106
129
  describe('alphanumeric', () => {
107
130
  const message = JSON.stringify({ message: 'validation.alphanumeric', key: 'testDisplayKey' });
108
131
  const testCases = [
@@ -1112,6 +1135,13 @@ describe('formRules', () => {
1112
1135
  expect(formRuleResult).toStrictEqual(expectedResult);
1113
1136
  });
1114
1137
 
1138
+ /**
1139
+ * Test all factory validators
1140
+ * @param rule - the name of the factory validator
1141
+ * @param argument - the value to validate
1142
+ * @param correctValues - an array of values that should pass the validation
1143
+ * @param wrongValues - an array of values that should fail the validation
1144
+ */
1115
1145
  describe.each([
1116
1146
  ['minValue', 2, [3], [1]],
1117
1147
  ['maxValue', 256, [1], [300]],
@@ -1133,12 +1163,18 @@ describe('formRules', () => {
1133
1163
  });
1134
1164
  });
1135
1165
 
1166
+ /**
1167
+ * Test all standard validators
1168
+ * @param rule - the name of the standard validator
1169
+ * @param correctValues - an array of values that should pass the validation
1170
+ * @param wrongValues - an array of values that should fail the validation
1171
+ */
1136
1172
  describe.each([
1137
1173
  ['requiredInt', [2, 2.2], ['e']],
1138
1174
  ['isInteger', ['2', 2, 0], [2.2, 'e', '1.0']],
1139
1175
  ['isPositive', ['0', 1], [-1]],
1140
1176
  ['isOctal', ['0', 0, 10], ['01']],
1141
-
1177
+ ['cronSchedule', ['0 * * * *', '@daily'], ['0 * * **']],
1142
1178
  ])('given validator %p', (rule, correctValues, wrongValues) => {
1143
1179
  it.each(wrongValues as [])('should return error for value %p', (wrong) => {
1144
1180
  const formRuleResult = (formRules as any)[rule](wrong);
@@ -4,14 +4,26 @@ import isEmpty from 'lodash/isEmpty';
4
4
  import has from 'lodash/has';
5
5
  import isUrl from 'is-url';
6
6
  // import uniq from 'lodash/uniq';
7
- import cronstrue from 'cronstrue';
8
7
  import { Translation } from '@shell/types/t';
9
8
  import { isHttps, isLocalhost, hasTrailingForwardSlash } from '@shell/utils/validators/setting';
9
+ import { cronScheduleRule } from '@shell/utils/validators/cron-schedule';
10
10
 
11
11
  // import uniq from 'lodash/uniq';
12
- export type Validator<T = undefined | string> = (val: any, arg?: any) => T;
13
12
 
14
- export type ValidatorFactory = (arg1: any, arg2?: any) => Validator
13
+ /**
14
+ * Fixed validation rule which require only the value to be evaluated
15
+ * @param value
16
+ * @returns { string | undefined }
17
+ */
18
+ export type Validator<T = undefined | string> = (value: any, arg?: any) => T;
19
+
20
+ /**
21
+ * Factory function which returns a validation rule
22
+ * @param arg Argument used as part of the validation rule process, not necessarily as parameter of the validation rule
23
+ * @param value Value to be evaluated
24
+ * @returns { Validator }
25
+ */
26
+ export type ValidatorFactory = (arg: any, value?: any) => Validator
15
27
 
16
28
  type ServicePort = {
17
29
  name?: string,
@@ -131,9 +143,9 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
131
143
 
132
144
  const cronSchedule: Validator = (val: string) => {
133
145
  try {
134
- cronstrue.toString(val, { verbose: true });
146
+ cronScheduleRule.validation(val);
135
147
  } catch (e) {
136
- return t('validation.invalidCron');
148
+ return t(cronScheduleRule.message);
137
149
  }
138
150
  };
139
151
 
@@ -145,6 +157,8 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
145
157
 
146
158
  const url: Validator = (val: string) => val && !isUrl(val) ? t('validation.setting.serverUrl.url') : undefined;
147
159
 
160
+ const gitRepository: Validator = (val: string) => val && !/^((http|git|ssh|http(s)|file|\/?)|(git@[\w\.]+))(:(\/\/)?)([\w\.@\:\/\-]+)([\d\/\w.-]+?)(.git){0,1}(\/)?$/gm.test(val) ? t('validation.git.repository') : undefined;
161
+
148
162
  const alphanumeric: Validator = (val: string) => val && !/^[a-zA-Z0-9]+$/.test(val) ? t('validation.alphanumeric', { key }) : undefined;
149
163
 
150
164
  const interval: Validator = (val: string) => !/^\d+[hms]$/.test(val) ? t('validation.monitoring.route.interval', { key }) : undefined;
@@ -474,6 +488,7 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
474
488
  dnsLabelRestricted,
475
489
  externalName,
476
490
  fileRequired,
491
+ gitRepository,
477
492
  groupsAreValid,
478
493
  hostname,
479
494
  imageUrl,
package/vue.config.js CHANGED
@@ -323,7 +323,7 @@ const getVirtualModules = (dir, includePkg) => {
323
323
 
324
324
  // Package file must have rancher field to be a plugin
325
325
  if (includePkg(name) && library.rancher) {
326
- reqs += `$plugin.initPlugin('${ name }', require(\'~/pkg/${ name }\')); `;
326
+ reqs += `$plugin.registerBuiltinExtension('${ name }', require(\'~/pkg/${ name }\')); `;
327
327
  }
328
328
  });
329
329
  }
@@ -1,50 +0,0 @@
1
- <script>
2
- import {
3
- STATE, NAME, WORKLOAD_IMAGES, WORKLOAD_ENDPOINTS, TYPE, AGE
4
- } from '@shell/config/table-headers';
5
- import { WORKLOAD_TYPES } from '@shell/config/types';
6
- import ResourceTable from '@shell/components/ResourceTable';
7
-
8
- export default {
9
- components: { ResourceTable },
10
- props: {
11
- filter: {
12
- type: Function,
13
- required: true
14
- }
15
- },
16
- async fetch() {
17
- // Enumerating instead of using Object.values() because it looks like we don't want to include replica sets for this.
18
- const types = [
19
- WORKLOAD_TYPES.DEPLOYMENT,
20
- WORKLOAD_TYPES.CRON_JOB,
21
- WORKLOAD_TYPES.DAEMON_SET,
22
- WORKLOAD_TYPES.JOB,
23
- WORKLOAD_TYPES.STATEFUL_SET
24
- ];
25
- const allWorkloadsNested = await Promise.all(types.map((type) => this.$store.dispatch('cluster/findAll', { type })));
26
- const allWorkloads = allWorkloadsNested.flat();
27
-
28
- this.relatedWorkloadRows = allWorkloads.filter(this.filter);
29
- },
30
- data() {
31
- const relatedWorkloadHeaders = [STATE, NAME, WORKLOAD_IMAGES, WORKLOAD_ENDPOINTS, TYPE, AGE];
32
-
33
- return {
34
- relatedWorkloadRows: [],
35
- relatedWorkloadHeaders,
36
- schema: this.$store.getters['cluster/schemaFor'](WORKLOAD_TYPES.DEPLOYMENT)
37
- };
38
- },
39
- };
40
- </script>
41
-
42
- <template>
43
- <div>
44
- <ResourceTable
45
- :schema="schema"
46
- :rows="relatedWorkloadRows"
47
- :headers="relatedWorkloadHeaders"
48
- />
49
- </div>
50
- </template>