@rancher/shell 0.3.4 → 0.3.6
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.
- package/assets/images/providers/outscale.svg +19 -0
- package/assets/styles/app.scss +1 -1
- package/assets/styles/base/_basic.scss +18 -0
- package/assets/styles/base/_mixins.scss +0 -11
- package/assets/styles/base/_variables.scss +2 -4
- package/assets/styles/fonts/_fontstack.scss +11 -11
- package/assets/styles/global/_button.scss +12 -2
- package/assets/styles/vendor/vue-js-modal.scss +3 -3
- package/assets/translations/en-us.yaml +113 -22
- package/assets/translations/zh-hans.yaml +113 -24
- package/babel.config.js +13 -0
- package/chart/gatekeeper.vue +78 -0
- package/chart/istio.vue +135 -112
- package/chart/logging/index.vue +13 -4
- package/chart/monitoring/index.vue +15 -5
- package/chart/monitoring/steps/uninstall-v1.vue +2 -2
- package/chart/rancher-backup/index.vue +10 -3
- package/cloud-credential/aws.vue +1 -1
- package/cloud-credential/digitalocean.vue +1 -1
- package/cloud-credential/gcp.vue +1 -1
- package/cloud-credential/generic.vue +2 -2
- package/cloud-credential/linode.vue +1 -1
- package/cloud-credential/pnap.vue +1 -1
- package/components/ActionMenu.vue +3 -4
- package/components/AssignTo.vue +1 -1
- package/components/AsyncButton.vue +1 -1
- package/components/BannerGraphic.vue +1 -1
- package/components/BrandImage.vue +1 -4
- package/components/ButtonDropdown.vue +2 -3
- package/components/Carousel.vue +85 -37
- package/components/ChartPsp.vue +76 -0
- package/components/CruResource.vue +6 -2
- package/components/DashboardMetrics.vue +12 -10
- package/components/DetailText.vue +1 -1
- package/components/DisableAuthProviderModal.vue +1 -1
- package/components/EmberPage.vue +1 -1
- package/components/EtcdInfoBanner.vue +12 -7
- package/components/ExplorerMembers.vue +101 -6
- package/components/ExplorerProjectsNamespaces.vue +46 -3
- package/components/FileDiff.vue +6 -7
- package/components/GrafanaDashboard.vue +27 -23
- package/components/LazyImage.vue +10 -12
- package/components/LogItem.vue +1 -1
- package/components/Markdown.vue +1 -1
- package/components/PromptRemove.vue +2 -2
- package/components/PromptRestore.vue +1 -1
- package/components/ResourceDetail/Masthead.vue +16 -0
- package/components/ResourceDetail/index.vue +21 -4
- package/components/ResourceList/index.vue +1 -1
- package/components/ResourceTable.vue +4 -1
- package/components/SingleClusterInfo.vue +2 -2
- package/components/SortableTable/THead.vue +1 -1
- package/components/SortableTable/index.vue +28 -13
- package/components/SortableTable/selection.js +58 -50
- package/components/Wizard.vue +4 -2
- package/components/__tests__/AsyncButton.test.ts +3 -1
- package/components/__tests__/ChartPsp.test.ts +75 -0
- package/components/__tests__/CruResource.test.ts +3 -1
- package/components/auth/Principal.vue +1 -1
- package/components/auth/RoleDetailEdit.vue +2 -2
- package/components/fleet/FleetBundles.vue +3 -1
- package/components/fleet/FleetClusters.vue +1 -2
- package/components/fleet/FleetIntro.vue +9 -1
- package/components/fleet/FleetNoWorkspaces.vue +62 -0
- package/components/fleet/FleetSummary.vue +7 -1
- package/components/form/HookOption.vue +14 -10
- package/components/form/LabeledSelect.vue +14 -11
- package/components/form/Labels.vue +32 -27
- package/components/form/MatchExpressions.vue +19 -4
- package/components/form/Members/ClusterPermissionsEditor.vue +32 -7
- package/components/form/NameNsDescription.vue +32 -46
- package/components/form/ProjectMemberEditor.vue +46 -21
- package/components/form/ResourceSelector.vue +1 -1
- package/components/form/SecretSelector.vue +5 -1
- package/components/form/ServiceNameSelect.vue +1 -1
- package/components/form/SimpleSecretSelector.vue +9 -9
- package/components/form/Tolerations.vue +4 -1
- package/components/form/ValueFromResource.vue +14 -9
- package/components/form/WorkloadPorts.vue +2 -2
- package/components/form/__tests__/LabeledSelect.test.ts +138 -0
- package/components/form/__tests__/NameNsDescription.ts +59 -0
- package/components/formatter/InternalExternalIP.vue +6 -0
- package/components/formatter/InvolvedObjectLink.vue +54 -0
- package/components/formatter/Link.vue +20 -4
- package/components/formatter/LinkName.vue +6 -1
- package/components/formatter/ServiceTargets.vue +1 -1
- package/components/formatter/WorkloadHealthScale.vue +8 -2
- package/components/nav/Group.vue +2 -2
- package/components/nav/NamespaceFilter.vue +23 -11
- package/components/nav/TopLevelMenu.vue +2 -4
- package/components/nav/Type.vue +1 -1
- package/components/nav/WorkspaceSwitcher.vue +46 -5
- package/components/nuxt/nuxt-build-indicator.vue +143 -0
- package/components/nuxt/nuxt-child.js +122 -0
- package/components/nuxt/nuxt-error.vue +98 -0
- package/components/nuxt/nuxt-link.client.js +98 -0
- package/components/nuxt/nuxt-link.server.js +16 -0
- package/components/nuxt/nuxt-loading.vue +154 -0
- package/components/nuxt/nuxt.js +101 -0
- package/config/labels-annotations.js +17 -0
- package/config/middleware.js +12 -0
- package/config/product/auth.js +3 -2
- package/config/product/explorer.js +34 -6
- package/config/product/fleet.js +2 -0
- package/config/query-params.js +1 -0
- package/config/router.js +414 -0
- package/config/store.js +181 -0
- package/config/table-headers.js +54 -12
- package/config/types.js +18 -8
- package/config/uiplugins.js +30 -0
- package/content/docs/en-us/whats-new.md +10 -0
- package/content/docs/zh-hans/whats-new.md +11 -1
- package/core/plugin-routes.ts +23 -0
- package/core/plugin.ts +4 -2
- package/core/types.ts +258 -1
- package/creators/app/app.package.json +2 -1
- package/creators/app/files/.eslintrc.js +1 -1
- package/creators/app/files/babel.config.js +1 -18
- package/creators/app/files/tsconfig.json +0 -1
- package/creators/app/files/vue.config.js +6 -0
- package/creators/app/init +5 -5
- package/creators/pkg/files/.github/workflows/build-extension.yml +110 -0
- package/creators/pkg/files/tsconfig.json +0 -1
- package/creators/pkg/init +35 -4
- package/creators/pkg/pkg.package.json +3 -3
- package/creators/update/init +1 -1
- package/detail/constraints.gatekeeper.sh.constraint.vue +34 -17
- package/detail/fleet.cattle.io.clustergroup.vue +7 -1
- package/detail/fleet.cattle.io.gitrepo.vue +19 -11
- package/detail/harvesterhci.io.management.cluster.vue +3 -3
- package/detail/provisioning.cattle.io.cluster.vue +54 -12
- package/detail/workload/index.vue +3 -3
- package/dialog/AddClusterMemberDialog.vue +1 -1
- package/dialog/AddProjectMemberDialog.vue +2 -2
- package/dialog/AddonConfigConfirmationDialog.vue +27 -15
- package/dialog/DiagnosticTimingsDialog.vue +1 -1
- package/dialog/ForceMachineRemoveDialog.vue +1 -1
- package/dialog/GenericPrompt.vue +18 -6
- package/dialog/RotateEncryptionKeyDialog.vue +1 -1
- package/dialog/SaveAsRKETemplateDialog.vue +1 -1
- package/dialog/ScaleMachineDownDialog.vue +1 -1
- package/edit/auth/github.vue +8 -8
- package/edit/auth/googleoauth.vue +5 -5
- package/edit/auth/ldap/index.vue +1 -1
- package/edit/auth/oidc.vue +1 -1
- package/edit/auth/saml.vue +1 -1
- package/edit/cis.cattle.io.clusterscan.vue +1 -1
- package/edit/fleet.cattle.io.clustergroup.vue +6 -4
- package/edit/fleet.cattle.io.gitrepo.vue +32 -4
- package/edit/helm.cattle.io.projecthelmchart.vue +5 -1
- package/edit/logging.banzaicloud.io.output/index.vue +18 -5
- package/edit/logging.banzaicloud.io.output/providers/loki.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +141 -6
- package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +4 -1
- package/edit/management.cattle.io.setting.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/webhook.vue +2 -2
- package/edit/monitoring.coreos.com.receiver/tls.vue +18 -18
- package/edit/monitoring.coreos.com.receiver/types/webhook.banner.vue +4 -4
- package/edit/monitoring.coreos.com.receiver/types/webhook.vue +1 -1
- package/edit/namespace.vue +14 -10
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +126 -45
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +21 -4
- package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +202 -2
- package/edit/provisioning.cattle.io.cluster/import.vue +23 -25
- package/edit/provisioning.cattle.io.cluster/rke2.vue +344 -102
- package/edit/resources.cattle.io.backup.vue +1 -1
- package/edit/service.vue +1 -1
- package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +2 -2
- package/edit/workload/__tests__/Job.test.ts +3 -1
- package/edit/workload/index.vue +8 -3
- package/edit/workload/mixins/workload.js +22 -7
- package/edit/workload/storage/Mount.vue +3 -3
- package/initialize/App.js +206 -0
- package/initialize/client.js +863 -0
- package/initialize/index.js +364 -0
- package/layouts/default.vue +7 -3
- package/layouts/standalone.vue +13 -0
- package/list/catalog.cattle.io.clusterrepo.vue +1 -0
- package/list/fleet.cattle.io.bundle.vue +6 -3
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +3 -1
- package/list/fleet.cattle.io.gitrepo.vue +44 -5
- package/list/management.cattle.io.fleetworkspace.vue +45 -0
- package/list/node.vue +69 -16
- package/list/provisioning.cattle.io.cluster.vue +30 -1
- package/list/rbac.authorization.k8s.io.clusterrolebinding.vue +48 -0
- package/list/workload.vue +6 -4
- package/machine-config/azure.vue +97 -38
- package/middleware/authenticated.js +34 -0
- package/mixins/chart.js +101 -2
- package/mixins/fetch.client.js +95 -0
- package/mixins/fetch.server.js +73 -0
- package/mixins/labeled-form-element.ts +2 -2
- package/mixins/resource-fetch.js +2 -2
- package/models/apps.statefulset.js +28 -0
- package/models/cluster/node.js +23 -2
- package/models/cluster.x-k8s.io.machine.js +4 -2
- package/models/clusterroletemplatebinding.js +7 -0
- package/models/constraints.gatekeeper.sh.constraint.js +46 -0
- package/models/fleet.cattle.io.cluster.js +19 -10
- package/models/fleet.cattle.io.gitrepo.js +7 -2
- package/models/management.cattle.io.cluster.js +1 -1
- package/models/management.cattle.io.fleetworkspace.js +12 -0
- package/models/management.cattle.io.gitreporestriction.js +5 -0
- package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.js +3 -0
- package/models/pod.js +4 -0
- package/models/provisioning.cattle.io.cluster.js +7 -5
- package/models/rbac.authorization.k8s.io.clusterrolebinding.js +16 -0
- package/models/rbac.authorization.k8s.io.rolebinding.js +16 -0
- package/package.json +13 -21
- package/pages/auth/setup.vue +2 -2
- package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +33 -0
- package/pages/c/_cluster/apps/charts/chart.vue +4 -4
- package/pages/c/_cluster/apps/charts/install.helpers.js +26 -0
- package/pages/c/_cluster/apps/charts/install.vue +98 -102
- package/pages/c/_cluster/explorer/EventsTable.vue +5 -19
- package/pages/c/_cluster/explorer/index.vue +29 -25
- package/pages/c/_cluster/explorer/tools/index.vue +8 -8
- package/pages/c/_cluster/fleet/index.vue +95 -34
- package/pages/c/_cluster/gatekeeper/index.vue +1 -1
- package/pages/c/_cluster/istio/index.vue +5 -5
- package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
- package/pages/c/_cluster/monitoring/index.vue +7 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +8 -8
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +20 -7
- package/pages/c/_cluster/uiplugins/index.vue +49 -17
- package/pages/diagnostic.vue +32 -25
- package/pages/home.vue +9 -4
- package/pages/index.vue +10 -1
- package/pages/rio/mesh.vue +1 -2
- package/pkg/tsconfig.json +0 -1
- package/plugins/clean-html-directive.js +34 -0
- package/plugins/dashboard-store/actions.js +32 -9
- package/plugins/dashboard-store/index.js +1 -1
- package/plugins/dashboard-store/mutations.js +5 -2
- package/plugins/dashboard-store/resource-class.js +8 -1
- package/plugins/plugin.js +0 -14
- package/plugins/portal-vue.js +4 -0
- package/plugins/steve/mutations.js +3 -2
- package/plugins/steve/steve-description-class.js +5 -1
- package/plugins/steve/subscribe.js +63 -54
- package/plugins/steve-create-worker.js +14 -0
- package/promptRemove/management.cattle.io.globalrole.vue +2 -2
- package/promptRemove/management.cattle.io.project.vue +2 -2
- package/promptRemove/management.cattle.io.roletemplate.vue +2 -2
- package/promptRemove/pod.vue +1 -1
- package/public/index.html +65 -0
- package/rancher-components/components/Banner/Banner.test.ts +7 -1
- package/rancher-components/components/Banner/Banner.vue +2 -1
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -0
- package/rancher-components/components/Form/Radio/RadioButton.test.ts +31 -0
- package/rancher-components/components/Form/Radio/RadioButton.vue +14 -3
- package/scripts/build-pkg.sh +1 -0
- package/scripts/clean +6 -0
- package/scripts/extension/bundle +58 -0
- package/scripts/extension/helmpatch +89 -0
- package/scripts/extension/publish +333 -0
- package/scripts/serve-pkgs +6 -2
- package/scripts/test-plugins-build.sh +4 -0
- package/store/__tests__/index.test.ts +110 -0
- package/store/index.js +145 -58
- package/store/type-map.js +6 -2
- package/tsconfig.default.json +36 -0
- package/tsconfig.json +23 -0
- package/types/rancher/index.d.ts +2 -0
- package/types/shell/index.d.ts +466 -320
- package/utils/__tests__/grafana.test.ts +44 -0
- package/utils/__tests__/string.test.ts +12 -0
- package/utils/auth.js +65 -0
- package/utils/axios.js +190 -0
- package/utils/cookie-universal-nuxt.js +10 -0
- package/utils/dom.js +15 -0
- package/utils/grafana.js +35 -16
- package/utils/monitoring.js +2 -1
- package/utils/nuxt.js +659 -0
- package/utils/position.js +5 -8
- package/utils/router.scrollBehavior.js +80 -0
- package/utils/select.js +1 -3
- package/utils/socket.js +1 -0
- package/utils/string.js +13 -0
- package/utils/time.js +9 -0
- package/vue.config.js +690 -0
- package/chart/rancher-alerting-drivers.vue +0 -53
- package/chart/rancher-gatekeeper.vue +0 -37
- package/creators/app/files/nuxt.config.js +0 -6
- package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +0 -4
- package/nuxt.config.js +0 -798
- package/plugins/dashboard-store/extensions.js +0 -22
package/mixins/chart.js
CHANGED
|
@@ -14,6 +14,8 @@ import { CAPI, CATALOG } from '@shell/config/types';
|
|
|
14
14
|
import { isPrerelease } from '@shell/utils/version';
|
|
15
15
|
import difference from 'lodash/difference';
|
|
16
16
|
import { LINUX } from '@shell/store/catalog';
|
|
17
|
+
import { clone } from 'utils/object';
|
|
18
|
+
import { merge } from 'lodash';
|
|
17
19
|
|
|
18
20
|
export default {
|
|
19
21
|
data() {
|
|
@@ -292,7 +294,13 @@ export default {
|
|
|
292
294
|
// use the first version provided by the Helm chart
|
|
293
295
|
// as the default.
|
|
294
296
|
if ( !this.query.versionName && this.chart.versions?.length ) {
|
|
295
|
-
this.
|
|
297
|
+
if (this.showPreRelease) {
|
|
298
|
+
this.query.versionName = this.chart.versions[0].version;
|
|
299
|
+
} else {
|
|
300
|
+
const firstRelease = this.chart.versions.find(v => !isPrerelease(v.version));
|
|
301
|
+
|
|
302
|
+
this.query.versionName = firstRelease?.version || this.chart.versions[0].version;
|
|
303
|
+
}
|
|
296
304
|
}
|
|
297
305
|
|
|
298
306
|
if ( !this.query.versionName ) {
|
|
@@ -341,6 +349,97 @@ export default {
|
|
|
341
349
|
}
|
|
342
350
|
}, // End of fetchChart
|
|
343
351
|
|
|
352
|
+
// Charts have an annotation that specifies any additional charts that should be installed at the same time eg CRDs
|
|
353
|
+
async fetchAutoInstallInfo() {
|
|
354
|
+
const out = [];
|
|
355
|
+
/*
|
|
356
|
+
An example value for auto is ["rancher-monitoring-crd=match"].
|
|
357
|
+
It is an array of chart names that lets Rancher know of other
|
|
358
|
+
charts that should be auto-installed at the same time.
|
|
359
|
+
*/
|
|
360
|
+
const auto = (this.version?.annotations?.[CATALOG_ANNOTATIONS.AUTO_INSTALL] || '').split(/\s*,\s*/).filter(x => !!x).reverse();
|
|
361
|
+
|
|
362
|
+
for ( const constraint of auto ) {
|
|
363
|
+
const provider = this.$store.getters['catalog/versionSatisfying']({
|
|
364
|
+
constraint,
|
|
365
|
+
repoName: this.chart.repoName,
|
|
366
|
+
repoType: this.chart.repoType,
|
|
367
|
+
chartVersion: this.version.version,
|
|
368
|
+
});
|
|
369
|
+
/* An example return value for "provider":
|
|
370
|
+
[
|
|
371
|
+
{
|
|
372
|
+
"name": "rancher-monitoring-crd",
|
|
373
|
+
"version": "100.1.3+up19.0.3",
|
|
374
|
+
"description": "Installs the CRDs for rancher-monitoring.",
|
|
375
|
+
"apiVersion": "v1",
|
|
376
|
+
"annotations": {
|
|
377
|
+
"catalog.cattle.io/certified": "rancher",
|
|
378
|
+
"catalog.cattle.io/hidden": "true",
|
|
379
|
+
"catalog.cattle.io/namespace": "cattle-monitoring-system",
|
|
380
|
+
"catalog.cattle.io/release-name": "rancher-monitoring-crd"
|
|
381
|
+
},
|
|
382
|
+
"type": "application",
|
|
383
|
+
"urls": [
|
|
384
|
+
"https://192.168.0.18:8005/k8s/clusters/c-m-hhpg69fv/v1/catalog.cattle.io.clusterrepos/rancher-charts?chartName=rancher-monitoring-crd&link=chart&version=100.1.3%2Bup19.0.3"
|
|
385
|
+
],
|
|
386
|
+
"created": "2022-04-27T10:04:18.343124-07:00",
|
|
387
|
+
"digest": "ecf07ba23a9cdaa7ffbbb14345d94ea1240b7f3b8e0ce9be4640e3e585c484e2",
|
|
388
|
+
"key": "cluster/rancher-charts/rancher-monitoring-crd/100.1.3+up19.0.3",
|
|
389
|
+
"repoType": "cluster",
|
|
390
|
+
"repoName": "rancher-charts"
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
if ( provider ) {
|
|
396
|
+
try {
|
|
397
|
+
const crdVersionInfo = await this.$store.dispatch('catalog/getVersionInfo', {
|
|
398
|
+
repoType: provider.repoType,
|
|
399
|
+
repoName: provider.repoName,
|
|
400
|
+
chartName: provider.name,
|
|
401
|
+
versionName: provider.version
|
|
402
|
+
});
|
|
403
|
+
let existingCRDApp;
|
|
404
|
+
|
|
405
|
+
// search for an existing crd app to track any non-default values used on the previous install/upgrade
|
|
406
|
+
if (this.mode === _EDIT) {
|
|
407
|
+
const targetNamespace = crdVersionInfo?.chart?.annotations?.[CATALOG_ANNOTATIONS.NAMESPACE];
|
|
408
|
+
const targetName = crdVersionInfo?.chart?.annotations?.[CATALOG_ANNOTATIONS.RELEASE_NAME];
|
|
409
|
+
|
|
410
|
+
if (targetName && targetNamespace) {
|
|
411
|
+
existingCRDApp = await this.$store.dispatch('cluster/find', {
|
|
412
|
+
type: CATALOG.APP,
|
|
413
|
+
id: `${ targetNamespace }/${ targetName }`,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (existingCRDApp) {
|
|
418
|
+
// spec.values are any non-default values the user configured
|
|
419
|
+
// the installation form should show these, as well as any default values from the chart
|
|
420
|
+
const existingValues = clone(existingCRDApp.spec?.values || {});
|
|
421
|
+
const defaultValues = clone(existingCRDApp.spec?.chart?.values || {});
|
|
422
|
+
|
|
423
|
+
crdVersionInfo.existingValues = existingValues;
|
|
424
|
+
crdVersionInfo.allValues = merge(defaultValues, existingValues);
|
|
425
|
+
} else {
|
|
426
|
+
// allValues will potentially be updated in the UI - we want to track this separately from values to avoid mutating the chart object in the store
|
|
427
|
+
// this is similar to userValues for the main chart
|
|
428
|
+
crdVersionInfo.allValues = clone(crdVersionInfo.values);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
out.push(crdVersionInfo);
|
|
432
|
+
} catch (e) {
|
|
433
|
+
console.error('Unable to fetch VersionInfo: ', e); // eslint-disable-line no-console
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
this.errors.push(`This chart requires ${ constraint } but no matching chart was found`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
this.$set(this, 'autoInstallInfo', out);
|
|
441
|
+
},
|
|
442
|
+
|
|
344
443
|
selectVersion({ id: version }) {
|
|
345
444
|
this.$router.applyQuery({ [VERSION]: version });
|
|
346
445
|
},
|
|
@@ -361,7 +460,7 @@ export default {
|
|
|
361
460
|
repoType: this.chart.repoType,
|
|
362
461
|
repoName: this.chart.repoName,
|
|
363
462
|
name: this.chart.chartName,
|
|
364
|
-
version: this.
|
|
463
|
+
version: this.query.versionName,
|
|
365
464
|
};
|
|
366
465
|
|
|
367
466
|
return {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Vue from 'vue';
|
|
2
|
+
import { hasFetch, normalizeError, addLifecycleHook } from '../utils/nuxt';
|
|
3
|
+
|
|
4
|
+
const isSsrHydration = vm => vm.$vnode && vm.$vnode.elm && vm.$vnode.elm.dataset && vm.$vnode.elm.dataset.fetchKey;
|
|
5
|
+
const nuxtState = window.__NUXT__;
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
beforeCreate() {
|
|
9
|
+
if (!hasFetch(this)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this._fetchDelay = typeof this.$options.fetchDelay === 'number' ? this.$options.fetchDelay : 200;
|
|
14
|
+
|
|
15
|
+
Vue.util.defineReactive(this, '$fetchState', {
|
|
16
|
+
pending: false,
|
|
17
|
+
error: null,
|
|
18
|
+
timestamp: Date.now()
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
this.$fetch = $fetch.bind(this);
|
|
22
|
+
addLifecycleHook(this, 'created', created);
|
|
23
|
+
addLifecycleHook(this, 'beforeMount', beforeMount);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function beforeMount() {
|
|
28
|
+
if (!this._hydrated) {
|
|
29
|
+
return this.$fetch();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function created() {
|
|
34
|
+
if (!isSsrHydration(this)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Hydrate component
|
|
39
|
+
this._hydrated = true;
|
|
40
|
+
this._fetchKey = this.$vnode.elm.dataset.fetchKey;
|
|
41
|
+
const data = nuxtState.fetch[this._fetchKey];
|
|
42
|
+
|
|
43
|
+
// If fetch error
|
|
44
|
+
if (data && data._error) {
|
|
45
|
+
this.$fetchState.error = data._error;
|
|
46
|
+
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Merge data
|
|
51
|
+
for (const key in data) {
|
|
52
|
+
Vue.set(this.$data, key, data[key]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function $fetch() {
|
|
57
|
+
if (!this._fetchPromise) {
|
|
58
|
+
this._fetchPromise = $_fetch.call(this)
|
|
59
|
+
.then(() => {
|
|
60
|
+
delete this._fetchPromise;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return this._fetchPromise;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function $_fetch() { // eslint-disable-line camelcase
|
|
68
|
+
this.$nuxt.nbFetching++;
|
|
69
|
+
this.$fetchState.pending = true;
|
|
70
|
+
this.$fetchState.error = null;
|
|
71
|
+
this._hydrated = false;
|
|
72
|
+
let error = null;
|
|
73
|
+
const startTime = Date.now();
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
await this.$options.fetch.call(this);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (process.dev) {
|
|
79
|
+
console.error('Error in fetch():', err); // eslint-disable-line no-console
|
|
80
|
+
}
|
|
81
|
+
error = normalizeError(err);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const delayLeft = this._fetchDelay - (Date.now() - startTime);
|
|
85
|
+
|
|
86
|
+
if (delayLeft > 0) {
|
|
87
|
+
await new Promise(resolve => setTimeout(resolve, delayLeft));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.$fetchState.error = error;
|
|
91
|
+
this.$fetchState.pending = false;
|
|
92
|
+
this.$fetchState.timestamp = Date.now();
|
|
93
|
+
|
|
94
|
+
this.$nextTick(() => this.$nuxt.nbFetching--);
|
|
95
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import Vue from 'vue';
|
|
2
|
+
import {
|
|
3
|
+
hasFetch, normalizeError, addLifecycleHook, purifyData, createGetCounter
|
|
4
|
+
} from '../../utils/nuxt';
|
|
5
|
+
|
|
6
|
+
async function serverPrefetch() {
|
|
7
|
+
if (!this._fetchOnServer) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Call and await on $fetch
|
|
12
|
+
try {
|
|
13
|
+
await this.$options.fetch.call(this);
|
|
14
|
+
} catch (err) {
|
|
15
|
+
if (process.dev) {
|
|
16
|
+
console.error('Error in fetch():', err); // eslint-disable-line no-console
|
|
17
|
+
}
|
|
18
|
+
this.$fetchState.error = normalizeError(err);
|
|
19
|
+
}
|
|
20
|
+
this.$fetchState.pending = false;
|
|
21
|
+
|
|
22
|
+
// Define an ssrKey for hydration
|
|
23
|
+
this._fetchKey = this._fetchKey || this.$ssrContext.fetchCounters['']++;
|
|
24
|
+
|
|
25
|
+
// Add data-fetch-key on parent element of Component
|
|
26
|
+
const attrs = this.$vnode.data.attrs = this.$vnode.data.attrs || {};
|
|
27
|
+
|
|
28
|
+
attrs['data-fetch-key'] = this._fetchKey;
|
|
29
|
+
|
|
30
|
+
// Add to ssrContext for window.__NUXT__.fetch
|
|
31
|
+
|
|
32
|
+
if (this.$ssrContext.nuxt.fetch[this._fetchKey] !== undefined) {
|
|
33
|
+
console.warn(`Duplicate fetch key detected (${ this._fetchKey }). This may lead to unexpected results.`); // eslint-disable-line no-console
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.$ssrContext.nuxt.fetch[this._fetchKey] =
|
|
37
|
+
this.$fetchState.error ? { _error: this.$fetchState.error } : purifyData(this._data);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
created() {
|
|
42
|
+
if (!hasFetch(this)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (typeof this.$options.fetchOnServer === 'function') {
|
|
47
|
+
this._fetchOnServer = this.$options.fetchOnServer.call(this) !== false;
|
|
48
|
+
} else {
|
|
49
|
+
this._fetchOnServer = this.$options.fetchOnServer !== false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const defaultKey = this.$options._scopeId || this.$options.name || '';
|
|
53
|
+
const getCounter = createGetCounter(this.$ssrContext.fetchCounters, defaultKey);
|
|
54
|
+
|
|
55
|
+
if (typeof this.$options.fetchKey === 'function') {
|
|
56
|
+
this._fetchKey = this.$options.fetchKey.call(this, getCounter);
|
|
57
|
+
} else {
|
|
58
|
+
const key = typeof this.$options.fetchKey === 'string' ? this.$options.fetchKey : defaultKey;
|
|
59
|
+
|
|
60
|
+
this._fetchKey = key ? `${ key }:${ getCounter(key) }` : String(getCounter(key));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Added for remove vue undefined warning while ssr
|
|
64
|
+
this.$fetch = () => {}; // issue #8043
|
|
65
|
+
Vue.util.defineReactive(this, '$fetchState', {
|
|
66
|
+
pending: true,
|
|
67
|
+
error: null,
|
|
68
|
+
timestamp: Date.now()
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
addLifecycleHook(this, 'serverPrefetch', serverPrefetch);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
@@ -122,7 +122,7 @@ export default Vue.extend({
|
|
|
122
122
|
},
|
|
123
123
|
validationMessage(): string | undefined {
|
|
124
124
|
// we want to grab the required rule passed in if we can but if it's not there then we can just grab it from the formRulesGenerator
|
|
125
|
-
const requiredRule = this.rules.find((rule: any) => rule?.name === 'required');
|
|
125
|
+
const requiredRule = this.rules.find((rule: any) => rule?.name === 'required') as Function;
|
|
126
126
|
const ruleMessages = [];
|
|
127
127
|
const value = this?.value;
|
|
128
128
|
|
|
@@ -134,7 +134,7 @@ export default Vue.extend({
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
for (const rule of this.rules) {
|
|
137
|
+
for (const rule of this.rules as Function[]) {
|
|
138
138
|
const message = rule(value);
|
|
139
139
|
|
|
140
140
|
if (!!message && rule.name !== 'required') { // we're catching 'required' above so we can ignore it here
|
package/mixins/resource-fetch.js
CHANGED
|
@@ -131,8 +131,8 @@ export default {
|
|
|
131
131
|
|
|
132
132
|
const schema = this.$store.getters[`${ currStore }/schemaFor`](type);
|
|
133
133
|
|
|
134
|
-
if (schema?.attributes?.namespaced) {
|
|
135
|
-
opt.namespaced = this.namespaceFilter;
|
|
134
|
+
if (schema?.attributes?.namespaced) { // Is this specific resource namespaced (could be primary or secondary resource)?
|
|
135
|
+
opt.namespaced = this.namespaceFilter; // namespaceFilter will only be populated if applicable for primary resource
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
return this.$store.dispatch(`${ currStore }/findAll`, {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import Workload from './workload';
|
|
2
|
+
import { WORKLOAD_TYPES, POD } from '@shell/config/types';
|
|
3
|
+
import { WORKLOAD_TYPE_TO_KIND_MAPPING } from '@shell/detail/workload/index';
|
|
2
4
|
|
|
3
5
|
export default class StatefulSet extends Workload {
|
|
4
6
|
async rollBack(cluster, statefulSet, revision) {
|
|
@@ -19,4 +21,30 @@ export default class StatefulSet extends Workload {
|
|
|
19
21
|
|
|
20
22
|
await this.rollBackWorkload(cluster, statefulSet, 'statefulsets', body);
|
|
21
23
|
}
|
|
24
|
+
|
|
25
|
+
// we need to provide a new pods getter for statefulsets because the relationship
|
|
26
|
+
// done on the parent model "workload" is not correct
|
|
27
|
+
get pods() {
|
|
28
|
+
const relationships = this.metadata?.relationships || [];
|
|
29
|
+
const podRelationship = relationships.filter(relationship => relationship.toType === POD)[0];
|
|
30
|
+
|
|
31
|
+
if (podRelationship) {
|
|
32
|
+
const pods = this.$getters['podsByNamespace'](this.metadata.namespace);
|
|
33
|
+
|
|
34
|
+
return pods.filter((pod) => {
|
|
35
|
+
// a bit of a duplication of podRelationship, but always safe to check...
|
|
36
|
+
if (pod.metadata?.ownerReferences?.length) {
|
|
37
|
+
const ownerReferencesStatefulSet = pod.metadata?.ownerReferences?.find(own => own.kind === WORKLOAD_TYPE_TO_KIND_MAPPING[WORKLOAD_TYPES.STATEFUL_SET]);
|
|
38
|
+
|
|
39
|
+
if (ownerReferencesStatefulSet) {
|
|
40
|
+
return `${ pod.metadata.namespace }/${ ownerReferencesStatefulSet.name }` === this.id;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return false;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
22
50
|
}
|
package/models/cluster/node.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatPercent } from '@shell/utils/string';
|
|
2
|
-
import { CAPI as CAPI_ANNOTATIONS, NODE_ROLES, RKE } from '@shell/config/labels-annotations.js';
|
|
2
|
+
import { CAPI as CAPI_ANNOTATIONS, NODE_ROLES, RKE, SYSTEM_LABELS } from '@shell/config/labels-annotations.js';
|
|
3
3
|
import {
|
|
4
4
|
CAPI, MANAGEMENT, METRIC, NORMAN, POD
|
|
5
5
|
} from '@shell/config/types';
|
|
@@ -76,7 +76,8 @@ export default class ClusterNode extends SteveModel {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
openSsh() {
|
|
79
|
-
|
|
79
|
+
// Pass in the name of the node, so we display that rather than the name of the provisioned machine
|
|
80
|
+
this.provisionedMachine.openSsh(this.nameDisplay);
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
downloadKeys() {
|
|
@@ -109,6 +110,26 @@ export default class ClusterNode extends SteveModel {
|
|
|
109
110
|
return this.metadata?.labels || {};
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
get customLabelCount() {
|
|
114
|
+
return this.customLabels.length;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get customLabels() {
|
|
118
|
+
const parsedLabels = [];
|
|
119
|
+
|
|
120
|
+
if (this.labels) {
|
|
121
|
+
for (const k in this.labels) {
|
|
122
|
+
const [prefix] = k.split('/');
|
|
123
|
+
|
|
124
|
+
if (!SYSTEM_LABELS.includes(prefix)) {
|
|
125
|
+
parsedLabels.push(`${ k }=${ this.labels[k] }`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return parsedLabels;
|
|
131
|
+
}
|
|
132
|
+
|
|
112
133
|
get isWorker() {
|
|
113
134
|
return this.managementNode ? this.managementNode.isWorker : `${ this.labels[NODE_ROLES.WORKER] }` === 'true';
|
|
114
135
|
}
|
|
@@ -98,10 +98,12 @@ export default class CapiMachine extends SteveModel {
|
|
|
98
98
|
return false;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
openSsh() {
|
|
101
|
+
openSsh(name) {
|
|
102
|
+
const label = name || this.nameDisplay;
|
|
103
|
+
|
|
102
104
|
this.$dispatch('wm/open', {
|
|
103
105
|
id: `${ this.id }-ssh`,
|
|
104
|
-
label
|
|
106
|
+
label,
|
|
105
107
|
icon: 'terminal',
|
|
106
108
|
component: 'MachineSsh',
|
|
107
109
|
attrs: { machine: this, pod: {} }
|
|
@@ -21,4 +21,11 @@ export default class CRTB extends NormanModel {
|
|
|
21
21
|
get clusterroletemplatebinding() {
|
|
22
22
|
return this.$rootGetters[`management/byId`](MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING, this.id?.replace(':', '/'));
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
get steve() {
|
|
26
|
+
return this.$dispatch(`management/find`, {
|
|
27
|
+
type: MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING,
|
|
28
|
+
id: this.id?.replace(':', '/')
|
|
29
|
+
}, { root: true });
|
|
30
|
+
}
|
|
24
31
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import jsyaml from 'js-yaml';
|
|
2
2
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
3
|
+
import { downloadFile } from '@shell/utils/download';
|
|
3
4
|
|
|
4
5
|
export const ENFORCEMENT_ACTION_VALUES = {
|
|
5
6
|
DENY: 'deny',
|
|
@@ -7,21 +8,62 @@ export const ENFORCEMENT_ACTION_VALUES = {
|
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
export default class GateKeeperConstraint extends SteveModel {
|
|
11
|
+
get _availableActions() {
|
|
12
|
+
const out = super._availableActions;
|
|
13
|
+
|
|
14
|
+
const t = this.$rootGetters['i18n/t'];
|
|
15
|
+
|
|
16
|
+
const downloadViolations = {
|
|
17
|
+
action: 'downloadViolations',
|
|
18
|
+
icon: 'icon icon-fw icon-download',
|
|
19
|
+
label: t('gatekeeperConstraint.downloadViolations'),
|
|
20
|
+
total: 1,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
out.unshift(downloadViolations);
|
|
24
|
+
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
|
|
10
28
|
async save() {
|
|
11
29
|
let constraint;
|
|
30
|
+
let resourceVersion;
|
|
12
31
|
|
|
13
32
|
if (this.constraint) {
|
|
14
33
|
constraint = await this.findLatestConstraint();
|
|
34
|
+
resourceVersion = constraint?.metadata?.resourceVersion;
|
|
15
35
|
} else {
|
|
16
36
|
constraint = await this.$dispatch('cluster/create', { type: `constraints.gatekeeper.sh.${ this.kind.toLowerCase() }` }, { root: true });
|
|
17
37
|
}
|
|
18
38
|
|
|
19
39
|
constraint.spec = this.spec;
|
|
20
40
|
constraint.metadata = this.metadata;
|
|
41
|
+
if (resourceVersion) {
|
|
42
|
+
constraint.metadata.resourceVersion = resourceVersion;
|
|
43
|
+
}
|
|
21
44
|
|
|
22
45
|
await constraint.save();
|
|
23
46
|
}
|
|
24
47
|
|
|
48
|
+
async downloadViolations() {
|
|
49
|
+
const Papa = await import(/* webpackChunkName: "csv" */'papaparse');
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const violations = (this.violations || []).map((violation) => {
|
|
53
|
+
delete violation.resourceLink;
|
|
54
|
+
delete violation.constraintLink;
|
|
55
|
+
|
|
56
|
+
return violation;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const csv = Papa.unparse(violations);
|
|
60
|
+
|
|
61
|
+
downloadFile(`violations-${ this.name }.csv`, csv, 'application/csv');
|
|
62
|
+
} catch (err) {
|
|
63
|
+
this.$dispatch('growl/fromError', { title: 'Error downloading file', err }, { root: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
25
67
|
cleanForNew() {
|
|
26
68
|
this.$dispatch(`cleanForNew`, this);
|
|
27
69
|
|
|
@@ -50,6 +92,10 @@ export default class GateKeeperConstraint extends SteveModel {
|
|
|
50
92
|
}, { root: true });
|
|
51
93
|
}
|
|
52
94
|
|
|
95
|
+
get totalViolations() {
|
|
96
|
+
return this.status?.totalViolations || this.violations.length;
|
|
97
|
+
}
|
|
98
|
+
|
|
53
99
|
get violations() {
|
|
54
100
|
const violations = this.status?.violations || [];
|
|
55
101
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { MANAGEMENT, NORMAN } from '@shell/config/types';
|
|
2
|
+
import { CAPI, FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
|
|
3
|
+
import { _RKE2 } from '@shell/store/prefs';
|
|
4
|
+
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
2
5
|
import { escapeHtml } from '@shell/utils/string';
|
|
3
6
|
import { insertAt } from '@shell/utils/array';
|
|
4
|
-
import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
|
|
5
|
-
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
6
7
|
|
|
7
8
|
export default class FleetCluster extends SteveModel {
|
|
8
9
|
get _availableActions() {
|
|
@@ -32,14 +33,16 @@ export default class FleetCluster extends SteveModel {
|
|
|
32
33
|
enabled: !!this.links.update
|
|
33
34
|
});
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
if (!this.isRke2) {
|
|
37
|
+
insertAt(out, 3, {
|
|
38
|
+
action: 'assignTo',
|
|
39
|
+
label: 'Change workspace',
|
|
40
|
+
icon: 'icon icon-copy',
|
|
41
|
+
bulkable: true,
|
|
42
|
+
bulkAction: 'assignToBulk',
|
|
43
|
+
enabled: !!this.links.update && !!this.mgmt,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
insertAt(out, 4, { divider: true });
|
|
45
48
|
|
|
@@ -75,6 +78,12 @@ export default class FleetCluster extends SteveModel {
|
|
|
75
78
|
return false;
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
get isRke2() {
|
|
82
|
+
const provider = this?.metadata?.labels?.[CAPI.PROVIDER] || this?.status?.provider;
|
|
83
|
+
|
|
84
|
+
return provider === _RKE2;
|
|
85
|
+
}
|
|
86
|
+
|
|
78
87
|
get nameDisplay() {
|
|
79
88
|
return this.metadata?.labels?.[FLEET_LABELS.CLUSTER_DISPLAY_NAME] || this.metadata?.name || this.id;
|
|
80
89
|
}
|
|
@@ -96,9 +96,8 @@ export default class GitRepo extends SteveModel {
|
|
|
96
96
|
const workspace = this.$getters['byId'](FLEET.WORKSPACE, this.metadata.namespace);
|
|
97
97
|
const clusters = workspace?.clusters || [];
|
|
98
98
|
const groups = workspace?.clusterGroups || [];
|
|
99
|
-
const out = [];
|
|
100
99
|
|
|
101
|
-
if ( workspace
|
|
100
|
+
if ( workspace?.id === 'fleet-local' ) {
|
|
102
101
|
const local = findBy(groups, 'id', 'fleet-local/default');
|
|
103
102
|
|
|
104
103
|
if ( local ) {
|
|
@@ -108,6 +107,12 @@ export default class GitRepo extends SteveModel {
|
|
|
108
107
|
return [];
|
|
109
108
|
}
|
|
110
109
|
|
|
110
|
+
if (!this.spec.targets) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const out = [];
|
|
115
|
+
|
|
111
116
|
for ( const tgt of this.spec.targets ) {
|
|
112
117
|
if ( tgt.clusterName ) {
|
|
113
118
|
const cluster = findBy(clusters, 'metadata.name', tgt.clusterName);
|
|
@@ -196,7 +196,7 @@ export default class MgmtCluster extends HybridModel {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
get kubernetesVersionExtension() {
|
|
199
|
-
if ( this.kubernetesVersion.match(/[+-]
|
|
199
|
+
if ( this.kubernetesVersion.match(/[+-]/) ) {
|
|
200
200
|
return this.kubernetesVersion.replace(/^.*([+-])/, '$1');
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -66,6 +66,18 @@ export default class Workspace extends HybridModel {
|
|
|
66
66
|
await norman.save();
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
waitForWorkspaceSchema(timeout = 20000, schemaCallback) {
|
|
70
|
+
return this.waitForTestFn(() => {
|
|
71
|
+
const schema = this.$rootGetters['management/schemaFor'](FLEET.WORKSPACE);
|
|
72
|
+
|
|
73
|
+
if (!schemaCallback) {
|
|
74
|
+
return schema;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return schemaCallback(schema);
|
|
78
|
+
}, this.$rootGetters['i18n/t']('fleet.workspaces.timeout'), timeout);
|
|
79
|
+
}
|
|
80
|
+
|
|
69
81
|
async remove() {
|
|
70
82
|
const norman = await this.norman;
|
|
71
83
|
|