@rancher/shell 3.0.12-rc.2 → 3.0.12-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.
- package/apis/impl/apis.ts +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +22 -5
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +0 -10
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/Wizard.vue +14 -13
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +37 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/LabeledSelect.vue +20 -3
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/templates/home.vue +18 -0
- package/components/templates/plain.vue +18 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/router/routes.js +6 -0
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +107 -7
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +111 -1
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +57 -20
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/azuread.vue +122 -14
- package/edit/auth/oidc.vue +2 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +12 -8
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +3 -1
- package/models/management.cattle.io.cluster.js +417 -40
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +11 -29
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/chart.vue +60 -8
- package/pages/c/_cluster/apps/charts/install.vue +10 -7
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
- package/pages/c/_cluster/uiplugins/index.vue +203 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +27 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +102 -2
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +418 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
|
@@ -660,12 +660,8 @@ export default {
|
|
|
660
660
|
return getters.all(type);
|
|
661
661
|
},
|
|
662
662
|
|
|
663
|
-
// opt:
|
|
664
|
-
//
|
|
665
|
-
// limit: Number of records to return per page (default: 1000)
|
|
666
|
-
// sortBy: Sort by field
|
|
667
|
-
// sortOrder: asc or desc
|
|
668
|
-
// url: Use this specific URL instead of looking up the URL for the type/id. This should only be used for bootstrapping schemas on startup.
|
|
663
|
+
// opt: @ActionFindArgs
|
|
664
|
+
// @returns @ActionFindResponse
|
|
669
665
|
// @TODO depaginate: If the response is paginated, retrieve all the pages. (default: true)
|
|
670
666
|
async find(ctx, { type, id, opt }) {
|
|
671
667
|
if (!id) {
|
|
@@ -697,11 +693,23 @@ export default {
|
|
|
697
693
|
}
|
|
698
694
|
}
|
|
699
695
|
|
|
696
|
+
const havePage = getters.havePage(type);
|
|
697
|
+
|
|
700
698
|
opt = opt || {};
|
|
701
699
|
opt.url = getters.urlFor(type, id, opt);
|
|
702
700
|
|
|
703
701
|
const res = await dispatch('request', { opt, type });
|
|
704
702
|
|
|
703
|
+
if (!havePage && getters.havePage(type)) {
|
|
704
|
+
// There may be a super edge case where list --> detail (whilst loading) --> list navigation causes the list's rows to disappear
|
|
705
|
+
// Somehow the `findPage` from the list page returns before the `find`. The `find` then clears the page state in the cache.
|
|
706
|
+
// If this has happened silently return (we don't care about result)
|
|
707
|
+
// https://github.com/rancher/dashboard/issues/17524
|
|
708
|
+
console.warn(`Prevented \`find\` action from polluting cache for type "${ type }" (currently represents a page).`); // eslint-disable-line no-console
|
|
709
|
+
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
705
713
|
if (!opt.transient) {
|
|
706
714
|
await dispatch('load', { data: res, invalidatePageCache: opt.invalidatePageCache });
|
|
707
715
|
}
|
|
@@ -830,19 +838,33 @@ export default {
|
|
|
830
838
|
/**
|
|
831
839
|
* Remove all cached entries for a resource and stop watches
|
|
832
840
|
*/
|
|
833
|
-
forgetType({ commit, dispatch, state },
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
841
|
+
forgetType({ commit, dispatch, state }, payload) {
|
|
842
|
+
let type = payload;
|
|
843
|
+
let config = {};
|
|
844
|
+
|
|
845
|
+
if ( typeof payload === 'object' && payload !== null && payload.type ) {
|
|
846
|
+
type = payload.type;
|
|
847
|
+
config = payload;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const { compareWatches, unwatch = true, forget = true } = config;
|
|
843
851
|
|
|
844
|
-
|
|
845
|
-
|
|
852
|
+
if (unwatch) {
|
|
853
|
+
// Stop all known watches
|
|
854
|
+
state.started
|
|
855
|
+
.filter((entry) => compareWatches ? compareWatches(entry) : entry.type === type)
|
|
856
|
+
.forEach((entry) => dispatch('unwatch', entry));
|
|
857
|
+
|
|
858
|
+
// Stop all known back-off watch processes for this type
|
|
859
|
+
dispatch('resetWatchBackOff', {
|
|
860
|
+
type, compareWatches, resetStarted: false
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (forget) {
|
|
865
|
+
// Remove entries from store
|
|
866
|
+
commit('forgetType', type);
|
|
867
|
+
}
|
|
846
868
|
},
|
|
847
869
|
|
|
848
870
|
promptRemove({ commit, state }, resources ) {
|
|
@@ -946,12 +946,15 @@ export default class Resource {
|
|
|
946
946
|
// where mostly likely extension CRD model is extending from resource-class
|
|
947
947
|
const isResourceDetailDrawerCompatibleWithRancherSystem = semver.satisfies(parsedRancherVersion, '>= 2.13.0');
|
|
948
948
|
|
|
949
|
+
// If the resource can't show an edit or a yaml we don't want to show the configuration drawer
|
|
950
|
+
const showConfigEnabled = isResourceDetailDrawerCompatibleWithRancherSystem && this.disableResourceDetailDrawer !== true && (this.canCustomEdit || this.canYaml);
|
|
951
|
+
|
|
949
952
|
const all = [
|
|
950
953
|
{
|
|
951
954
|
action: 'showConfiguration',
|
|
952
955
|
label: this.t('action.showConfiguration'),
|
|
953
956
|
icon: 'icon icon-document',
|
|
954
|
-
enabled:
|
|
957
|
+
enabled: showConfigEnabled,
|
|
955
958
|
},
|
|
956
959
|
{ divider: true },
|
|
957
960
|
{
|
|
@@ -964,7 +967,7 @@ export default class Resource {
|
|
|
964
967
|
action: this.canEditYaml ? 'goToEditYaml' : 'goToViewYaml',
|
|
965
968
|
label: this.t(this.canEditYaml ? 'action.editYaml' : 'action.viewYaml'),
|
|
966
969
|
icon: 'icon icon-file',
|
|
967
|
-
enabled: this.canYaml,
|
|
970
|
+
enabled: this.canYaml && (this.canEditYaml || !showConfigEnabled), // Hide "View YAML" when "Show Configuration" is available since it already includes YAML viewing
|
|
968
971
|
},
|
|
969
972
|
{
|
|
970
973
|
action: (this.canCustomEdit ? 'goToClone' : 'cloneYaml'),
|
|
@@ -14,7 +14,8 @@ describe('steve: subscribe', () => {
|
|
|
14
14
|
schemaFor: () => null,
|
|
15
15
|
inError: () => false,
|
|
16
16
|
watchStarted: () => false,
|
|
17
|
-
listenerManager: state.listenerManager
|
|
17
|
+
listenerManager: state.listenerManager,
|
|
18
|
+
typeRegistered: () => true,
|
|
18
19
|
};
|
|
19
20
|
const rootGetters = {
|
|
20
21
|
'type-map/isSpoofed': () => false,
|
|
@@ -343,7 +344,7 @@ describe('steve: subscribe', () => {
|
|
|
343
344
|
|
|
344
345
|
// call watch
|
|
345
346
|
actions.watch({
|
|
346
|
-
state, dispatch, getters, rootGetters
|
|
347
|
+
state, dispatch, getters, rootGetters, commit
|
|
347
348
|
}, {
|
|
348
349
|
...obj,
|
|
349
350
|
revision,
|
|
@@ -488,6 +489,7 @@ describe('steve: subscribe', () => {
|
|
|
488
489
|
const state = {
|
|
489
490
|
started: [],
|
|
490
491
|
inError: {},
|
|
492
|
+
queue: [],
|
|
491
493
|
listenerManager: new SteveWatchEventListenerManager()
|
|
492
494
|
};
|
|
493
495
|
const _getters = {
|
|
@@ -498,7 +500,8 @@ describe('steve: subscribe', () => {
|
|
|
498
500
|
watchStarted: (...args) => getters.watchStarted(state)(...args),
|
|
499
501
|
backOffId: (...args) => getters.backOffId()(...args),
|
|
500
502
|
canBackoff: () => true,
|
|
501
|
-
listenerManager: state.listenerManager
|
|
503
|
+
listenerManager: state.listenerManager,
|
|
504
|
+
typeRegistered: () => true,
|
|
502
505
|
};
|
|
503
506
|
const commit = (type, ...args) => mutations[type](state, ...args);
|
|
504
507
|
|
|
@@ -258,6 +258,11 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
258
258
|
{ field: 'spec.displayName' },
|
|
259
259
|
{ field: `status.provider` },
|
|
260
260
|
{ field: `status.connected` },
|
|
261
|
+
{ field: `status.info.machineProvider` },
|
|
262
|
+
{ field: `status.driver` },
|
|
263
|
+
{ field: `status.provider` },
|
|
264
|
+
{ field: `status.info.kubernetesVersion` },
|
|
265
|
+
{ field: `spec.fleetWorkspaceName` },
|
|
261
266
|
],
|
|
262
267
|
[SECRET]: [
|
|
263
268
|
{ field: `metadata.annotations[${ UI_PROJECT_SECRET_COPY }]` },
|
|
@@ -265,7 +270,10 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
265
270
|
[NAMESPACE]: [
|
|
266
271
|
],
|
|
267
272
|
[CAPI.MACHINE]: [
|
|
268
|
-
{ field: 'spec.clusterName' }
|
|
273
|
+
{ field: 'spec.clusterName' },
|
|
274
|
+
],
|
|
275
|
+
[CAPI.MACHINE_DEPLOYMENT]: [
|
|
276
|
+
{ field: 'spec.clusterName' },
|
|
269
277
|
],
|
|
270
278
|
[EVENT]: [
|
|
271
279
|
{ field: '_type' },
|
|
@@ -770,8 +778,8 @@ export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStores = {
|
|
|
770
778
|
enableAll: false,
|
|
771
779
|
enableSome: {
|
|
772
780
|
enabled: [
|
|
773
|
-
{ resource: CAPI.RANCHER_CLUSTER, context: ['side-bar'] },
|
|
774
|
-
{ resource: MANAGEMENT.CLUSTER, context: ['side-bar'] },
|
|
781
|
+
{ resource: CAPI.RANCHER_CLUSTER, context: ['side-bar', 'home', 'cluster-management'] },
|
|
782
|
+
{ resource: MANAGEMENT.CLUSTER, context: ['side-bar', 'home', 'cluster-management'] },
|
|
775
783
|
{ resource: CATALOG.APP, context: ['branding'] },
|
|
776
784
|
SECRET
|
|
777
785
|
],
|
|
@@ -469,8 +469,10 @@ const sharedActions = {
|
|
|
469
469
|
const worker = (this.$workers || {})[getters.storeName];
|
|
470
470
|
|
|
471
471
|
if (worker) {
|
|
472
|
+
const storeName = getters.storeName;
|
|
473
|
+
|
|
472
474
|
worker.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
|
|
473
|
-
cleanupTasks.push(waitFor(() => !this.$workers[
|
|
475
|
+
cleanupTasks.push(waitFor(() => !this.$workers?.[storeName], 'Worker to be destroyed', 30000, 10, true));
|
|
474
476
|
}
|
|
475
477
|
|
|
476
478
|
if ( socket ) {
|
|
@@ -553,17 +555,32 @@ const sharedActions = {
|
|
|
553
555
|
* @param {STEVE_WATCH_PARAMS} params
|
|
554
556
|
*/
|
|
555
557
|
watch({
|
|
556
|
-
state, dispatch, getters, rootGetters
|
|
558
|
+
state, dispatch, getters, rootGetters, commit
|
|
557
559
|
}, params) {
|
|
558
560
|
state.debugSocket && console.info(`Watch Request [${ getters.storeName }]`, JSON.stringify(params)); // eslint-disable-line no-console
|
|
559
561
|
let {
|
|
560
562
|
// eslint-disable-next-line prefer-const
|
|
561
|
-
type, selector, id, revision, namespace, stop, force, mode, standardWatch = true
|
|
563
|
+
type, selector, id, revision, namespace, stop, force, mode, standardWatch = true, registerType = false
|
|
562
564
|
} = params;
|
|
563
565
|
|
|
564
566
|
namespace = acceptOrRejectSocketMessage.subscribeNamespace(namespace);
|
|
565
567
|
type = getters.normalizeType(type);
|
|
566
568
|
|
|
569
|
+
if ( !getters.typeRegistered(type) ) {
|
|
570
|
+
if (registerType) {
|
|
571
|
+
commit('registerType', type);
|
|
572
|
+
} else if (mode !== STEVE_WATCH_MODE.RESOURCE_CHANGES) {
|
|
573
|
+
// - If we continue and open up a watch whenever we receive a `resource.` notification we go to queueChanges (bar resource.changes mode).
|
|
574
|
+
// - queueChanges ignores any change that's for a type that hasn't been registered
|
|
575
|
+
// - So here we're just exiting early, avoiding the watch --> queueChanges --> ignore loop
|
|
576
|
+
//
|
|
577
|
+
// Interestingly this is hit quite a few times (on cluster create screens there's token, cluster, project, projectRoleTemplateBinding, etc)
|
|
578
|
+
state.debugSocket && console.info('Will not Watch (type is not registered)', JSON.stringify(params)); // eslint-disable-line no-console
|
|
579
|
+
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
567
584
|
if (rootGetters['type-map/isSpoofed'](type)) {
|
|
568
585
|
state.debugSocket && console.info('Will not Watch (type is spoofed)', JSON.stringify(params)); // eslint-disable-line no-console
|
|
569
586
|
|
|
@@ -620,6 +637,9 @@ const sharedActions = {
|
|
|
620
637
|
if (debounceMs) {
|
|
621
638
|
msg.debounceMs = debounceMs;
|
|
622
639
|
}
|
|
640
|
+
|
|
641
|
+
// Anything in the queue will pollute the result set, so clear (and print to console so we know it's working)
|
|
642
|
+
commit('clearFromQueue', { type, log: true });
|
|
623
643
|
}
|
|
624
644
|
}
|
|
625
645
|
|
|
@@ -1556,10 +1576,20 @@ const defaultMutations = {
|
|
|
1556
1576
|
state.socketListenerManager = new SteveWatchEventListenerManager(state.config.namespace);
|
|
1557
1577
|
},
|
|
1558
1578
|
|
|
1559
|
-
clearFromQueue(state,
|
|
1579
|
+
clearFromQueue(state, args) {
|
|
1580
|
+
const safeArgs = typeof args === 'object' ? args : { type: args };
|
|
1581
|
+
const { type, log } = safeArgs;
|
|
1582
|
+
|
|
1560
1583
|
// Remove anything in the queue that is a resource update for the given type
|
|
1561
1584
|
state.queue = state.queue.filter((item) => {
|
|
1562
|
-
|
|
1585
|
+
const keep = item.body?.type !== type;
|
|
1586
|
+
|
|
1587
|
+
if (!keep && log) {
|
|
1588
|
+
// eslint-disable-next-line no-console
|
|
1589
|
+
console.info(`Clearing queued item of type \`${ type }\` from queue`, item);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
return keep;
|
|
1563
1593
|
});
|
|
1564
1594
|
},
|
|
1565
1595
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineComponent, nextTick } from 'vue';
|
|
1
|
+
import { defineComponent, nextTick, provide, ref } from 'vue';
|
|
2
2
|
import { mount, flushPromises } from '@vue/test-utils';
|
|
3
3
|
import { useForm } from 'vee-validate';
|
|
4
4
|
import { LabeledInput } from './index';
|
|
@@ -195,21 +195,27 @@ describe('component: LabeledInput', () => {
|
|
|
195
195
|
|
|
196
196
|
it('with name prop: form-level validation schema error is shown when the form validates', async() => {
|
|
197
197
|
const errorMessage = 'Username is required';
|
|
198
|
+
const showAllErrors = ref(false);
|
|
198
199
|
let triggerFormValidation!: () => Promise<unknown>;
|
|
199
200
|
|
|
200
201
|
const TestWrapper = defineComponent({
|
|
201
202
|
components: { LabeledInput },
|
|
202
203
|
setup() {
|
|
204
|
+
provide('vee-show-all-errors', showAllErrors);
|
|
205
|
+
|
|
203
206
|
const { validate } = useForm({
|
|
204
207
|
validationSchema: { username: (v: string) => (!v ? errorMessage : true) },
|
|
205
|
-
initialValues: { username: '' }
|
|
208
|
+
initialValues: { username: '' },
|
|
206
209
|
});
|
|
207
210
|
|
|
208
|
-
triggerFormValidation =
|
|
211
|
+
triggerFormValidation = async() => {
|
|
212
|
+
await validate();
|
|
213
|
+
showAllErrors.value = true;
|
|
214
|
+
};
|
|
209
215
|
|
|
210
216
|
return {};
|
|
211
217
|
},
|
|
212
|
-
template: '<LabeledInput name="username" value="" />'
|
|
218
|
+
template: '<LabeledInput name="username" value="" />',
|
|
213
219
|
});
|
|
214
220
|
|
|
215
221
|
const wrapper = mount(TestWrapper, { global: { mocks: { $store: { getters: { 'i18n/t': jest.fn() } } } } });
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { defineComponent, inject, computed,
|
|
3
|
-
import { useField } from 'vee-validate';
|
|
2
|
+
import { defineComponent, inject, computed, toRef } from 'vue';
|
|
4
3
|
import TextAreaAutoGrow from '@components/Form/TextArea/TextAreaAutoGrow.vue';
|
|
5
4
|
import LabeledTooltip from '@components/LabeledTooltip/LabeledTooltip.vue';
|
|
6
5
|
import { escapeHtml, generateRandomAlphaString } from '@shell/utils/string';
|
|
@@ -9,6 +8,7 @@ import { isValidCron } from 'cron-validator';
|
|
|
9
8
|
import { debounce } from 'lodash';
|
|
10
9
|
import { useLabeledFormElement, labeledFormElementProps } from '@shell/composables/useLabeledFormElement';
|
|
11
10
|
import { useCompactInput } from '@shell/composables/useCompactInput';
|
|
11
|
+
import { useVeeValidateField } from '@shell/composables/useVeeValidateField';
|
|
12
12
|
|
|
13
13
|
interface NonReactiveProps {
|
|
14
14
|
onInput: (event: Event) => void | ((event: Event) => void);
|
|
@@ -142,56 +142,11 @@ export default defineComponent({
|
|
|
142
142
|
|
|
143
143
|
const onInput = inject('onInput', provideProps.onInput);
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// Pass existing rules to vee-validate so field-level validation still runs
|
|
151
|
-
const veeValidator = (value: unknown): boolean | string => {
|
|
152
|
-
// Return true when name is absent to avoid duplicating
|
|
153
|
-
// useLabeledFormElement validation
|
|
154
|
-
if (!props.name) {
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
for (const rule of props.rules as Array<(v: unknown) => string | undefined>) {
|
|
158
|
-
const msg = rule(value);
|
|
159
|
-
|
|
160
|
-
if (msg) {
|
|
161
|
-
return msg;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return true;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const {
|
|
169
|
-
errorMessage: veeError,
|
|
170
|
-
handleBlur: veeHandleBlur,
|
|
171
|
-
validate: veeValidate,
|
|
172
|
-
value: veeValue,
|
|
173
|
-
} = useField<string | number | Record<string, unknown>>(
|
|
174
|
-
veeFieldName,
|
|
175
|
-
veeValidator,
|
|
176
|
-
{
|
|
177
|
-
initialValue: props.value as string | number,
|
|
178
|
-
validateOnValueUpdate: true,
|
|
179
|
-
}
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
// Keep vee-validate's internal value in sync with the controlled prop value.
|
|
183
|
-
watch(() => props.value, (v) => {
|
|
184
|
-
if (veeValue.value !== v) {
|
|
185
|
-
veeValue.value = v as string | number;
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const effectiveValidationMessage = computed(() => {
|
|
190
|
-
if (props.name && veeError.value) {
|
|
191
|
-
return veeError.value;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return validationMessage.value;
|
|
145
|
+
const { effectiveValidationMessage, veeHandleBlur, veeValidate } = useVeeValidateField({
|
|
146
|
+
name: toRef(props, 'name'),
|
|
147
|
+
rules: toRef(props, 'rules'),
|
|
148
|
+
value: toRef(props, 'value'),
|
|
149
|
+
validationMessage,
|
|
195
150
|
});
|
|
196
151
|
|
|
197
152
|
const effectiveStatus = computed(() => props.status);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils';
|
|
1
|
+
import { mount, RouterLinkStub } from '@vue/test-utils';
|
|
2
2
|
import RcButton from './RcButton.vue';
|
|
3
3
|
|
|
4
4
|
describe('rcButton.vue', () => {
|
|
@@ -138,4 +138,40 @@ describe('rcButton.vue', () => {
|
|
|
138
138
|
expect(button.classes()).toContain('variant-ghost');
|
|
139
139
|
});
|
|
140
140
|
});
|
|
141
|
+
|
|
142
|
+
describe('to prop', () => {
|
|
143
|
+
it('renders as a <button> when no "to" prop is provided', () => {
|
|
144
|
+
const wrapper = mount(RcButton);
|
|
145
|
+
|
|
146
|
+
expect(wrapper.find('button').exists()).toBe(true);
|
|
147
|
+
expect(wrapper.findComponent(RouterLinkStub).exists()).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('renders as a RouterLink when "to" prop is provided', () => {
|
|
151
|
+
const to = { name: 'some-route' };
|
|
152
|
+
const wrapper = mount(RcButton, {
|
|
153
|
+
props: { to },
|
|
154
|
+
global: { stubs: { RouterLink: RouterLinkStub } },
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const link = wrapper.findComponent(RouterLinkStub);
|
|
158
|
+
|
|
159
|
+
expect(link.exists()).toBe(true);
|
|
160
|
+
expect(wrapper.find('button').exists()).toBe(false);
|
|
161
|
+
expect(link.props('to')).toStrictEqual(to);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('applies button classes when rendered as a RouterLink', () => {
|
|
165
|
+
const wrapper = mount(RcButton, {
|
|
166
|
+
props: { to: '/foo', variant: 'secondary' },
|
|
167
|
+
global: { stubs: { RouterLink: RouterLinkStub } },
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const link = wrapper.findComponent(RouterLinkStub);
|
|
171
|
+
|
|
172
|
+
expect(link.classes()).toContain('rc-button');
|
|
173
|
+
expect(link.classes()).toContain('btn');
|
|
174
|
+
expect(link.classes()).toContain('variant-secondary');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
141
177
|
});
|
|
@@ -7,10 +7,15 @@
|
|
|
7
7
|
*
|
|
8
8
|
* <rc-button variant="primary" @click="doAction">Perform an Action</rc-button>
|
|
9
9
|
*/
|
|
10
|
-
import { computed, ref } from 'vue';
|
|
10
|
+
import { computed, ref, resolveComponent } from 'vue';
|
|
11
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
11
12
|
import {
|
|
12
|
-
ButtonVariantProps,
|
|
13
|
-
|
|
13
|
+
ButtonVariantProps,
|
|
14
|
+
ButtonSizeProps,
|
|
15
|
+
ButtonVariantNewProps,
|
|
16
|
+
ButtonSizeNewProps,
|
|
17
|
+
ButtonSize,
|
|
18
|
+
IconProps,
|
|
14
19
|
} from './types';
|
|
15
20
|
import RcIcon from '@components/RcIcon/RcIcon.vue';
|
|
16
21
|
|
|
@@ -33,7 +38,22 @@ const buttonSizesNew: { size: ButtonSize, className: string }[] = [
|
|
|
33
38
|
{ size: 'large', className: 'btn-large' },
|
|
34
39
|
];
|
|
35
40
|
|
|
36
|
-
const props = withDefaults(
|
|
41
|
+
const props = withDefaults(
|
|
42
|
+
defineProps<
|
|
43
|
+
ButtonVariantProps &
|
|
44
|
+
ButtonSizeProps &
|
|
45
|
+
ButtonVariantNewProps &
|
|
46
|
+
ButtonSizeNewProps &
|
|
47
|
+
IconProps & { to?: RouteLocationRaw }
|
|
48
|
+
>(),
|
|
49
|
+
{
|
|
50
|
+
size: 'medium',
|
|
51
|
+
to: undefined,
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const tag = computed(() => (props.to ? resolveComponent('RouterLink') : 'button'));
|
|
56
|
+
const role = computed(() => (props.to ? 'link' : 'button'));
|
|
37
57
|
|
|
38
58
|
const activeVariantClassName = computed(() => {
|
|
39
59
|
if (props.variant === 'multiAction' || props.multiAction) {
|
|
@@ -94,9 +114,11 @@ defineExpose({ focus });
|
|
|
94
114
|
</script>
|
|
95
115
|
|
|
96
116
|
<template>
|
|
97
|
-
<
|
|
117
|
+
<component
|
|
118
|
+
:is="tag"
|
|
98
119
|
ref="RcFocusTarget"
|
|
99
|
-
role="
|
|
120
|
+
:role="role"
|
|
121
|
+
:to="to"
|
|
100
122
|
:class="{ ...buttonClass }"
|
|
101
123
|
>
|
|
102
124
|
<slot
|
|
@@ -124,15 +146,23 @@ defineExpose({ focus });
|
|
|
124
146
|
size="inherit"
|
|
125
147
|
/>
|
|
126
148
|
</slot>
|
|
127
|
-
</
|
|
149
|
+
</component>
|
|
128
150
|
</template>
|
|
129
151
|
|
|
130
152
|
<style lang="scss" scoped>
|
|
131
|
-
button {
|
|
153
|
+
.rc-button {
|
|
132
154
|
display: inline-flex;
|
|
133
155
|
align-items: center;
|
|
134
156
|
justify-content: center;
|
|
135
157
|
|
|
158
|
+
// Override global .btn > .icon:not(:only-child) { margin-right: 6px } from _button.scss.
|
|
159
|
+
// RcButton uses flex gap for spacing instead. The :not(:only-child) clause matches the global
|
|
160
|
+
// selector so we win on specificity regardless of stylesheet load order; :deep() is needed to
|
|
161
|
+
// target slotted content.
|
|
162
|
+
& > :deep(.icon:not(:only-child)) {
|
|
163
|
+
margin-right: 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
136
166
|
// Much of the styling in here came from _button.scss. Because we're making changes from role to variant and we don't want to impact existing use cases we're pulling in some of these styles. We should in the long run deprecate that file.
|
|
137
167
|
// Variant styles
|
|
138
168
|
&.variant-primary {
|
|
@@ -35,18 +35,20 @@ defineExpose({ focus });
|
|
|
35
35
|
@keydown.enter.space="handleKeydown"
|
|
36
36
|
@click="showMenu(true)"
|
|
37
37
|
>
|
|
38
|
-
<template
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
<template
|
|
39
|
+
v-if="$slots.before"
|
|
40
|
+
#before
|
|
41
|
+
>
|
|
42
|
+
<slot name="before" />
|
|
42
43
|
</template>
|
|
43
44
|
<slot name="default">
|
|
44
45
|
<!--Empty slot content-->
|
|
45
46
|
</slot>
|
|
46
|
-
<template
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
<template
|
|
48
|
+
v-if="$slots.after"
|
|
49
|
+
#after
|
|
50
|
+
>
|
|
51
|
+
<slot name="after" />
|
|
50
52
|
</template>
|
|
51
53
|
</RcButton>
|
|
52
54
|
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CATALOG } from '@shell/config/types';
|
|
2
2
|
import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
3
3
|
import {
|
|
4
|
-
state, getters, actions, mutations, filterAndArrangeCharts
|
|
4
|
+
state, getters, actions, mutations, filterAndArrangeCharts, isRancherRepo, compatibleVersionsFor, getPermittedOSs
|
|
5
5
|
} from '../catalog';
|
|
6
6
|
import { createStore } from 'vuex';
|
|
7
7
|
|
|
@@ -509,4 +509,118 @@ describe('catalog', () => {
|
|
|
509
509
|
});
|
|
510
510
|
});
|
|
511
511
|
});
|
|
512
|
+
|
|
513
|
+
describe('isRancherRepo', () => {
|
|
514
|
+
it('should return true if chart.isRancherRepo is true', () => {
|
|
515
|
+
const chart = { isRancherRepo: true } as any;
|
|
516
|
+
|
|
517
|
+
expect(isRancherRepo(null, chart)).toBe(true);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('should return true if repo.isRancherSource is true', () => {
|
|
521
|
+
const repo = { isRancherSource: true } as any;
|
|
522
|
+
|
|
523
|
+
expect(isRancherRepo(repo, null)).toBe(true);
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it('should return false if repo is not a rancher source', () => {
|
|
527
|
+
const repo = { isRancherSource: false } as any;
|
|
528
|
+
|
|
529
|
+
expect(isRancherRepo(repo, null)).toBe(false);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
describe('compatibleVersionsFor', () => {
|
|
534
|
+
it('should allow versions if no OS constraint is provided', () => {
|
|
535
|
+
const chart = { versions: [{ version: '1.0.0' }] } as any;
|
|
536
|
+
const versions = compatibleVersionsFor(chart, undefined);
|
|
537
|
+
|
|
538
|
+
expect(versions).toHaveLength(1);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it('should allow windows nodes if permitted-os includes windows', () => {
|
|
542
|
+
const chart = {
|
|
543
|
+
versions: [{
|
|
544
|
+
version: '1.0.0',
|
|
545
|
+
annotations: { 'catalog.cattle.io/permits-os': 'linux,windows' }
|
|
546
|
+
}]
|
|
547
|
+
} as any;
|
|
548
|
+
const versions = compatibleVersionsFor(chart, 'windows');
|
|
549
|
+
|
|
550
|
+
expect(versions).toHaveLength(1);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it('should block windows nodes if permitted-os does not include windows', () => {
|
|
554
|
+
const chart = {
|
|
555
|
+
versions: [{
|
|
556
|
+
version: '1.0.0',
|
|
557
|
+
annotations: { 'catalog.cattle.io/permits-os': 'linux' }
|
|
558
|
+
}]
|
|
559
|
+
} as any;
|
|
560
|
+
const versions = compatibleVersionsFor(chart, 'windows');
|
|
561
|
+
|
|
562
|
+
expect(versions).toHaveLength(0);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it('should fallback to LINUX for rancher repos and block windows nodes', () => {
|
|
566
|
+
const chart = { isRancherRepo: true, versions: [{ version: '1.0.0' }] } as any;
|
|
567
|
+
const versions = compatibleVersionsFor(chart, 'windows');
|
|
568
|
+
|
|
569
|
+
expect(versions).toHaveLength(0);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('should not fallback to LINUX for non-rancher repos and allow windows nodes', () => {
|
|
573
|
+
const chart = { isRancherRepo: false, versions: [{ version: '1.0.0' }] } as any;
|
|
574
|
+
const versions = compatibleVersionsFor(chart, 'windows');
|
|
575
|
+
|
|
576
|
+
expect(versions).toHaveLength(1);
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
describe('getPermittedOSs', () => {
|
|
581
|
+
it('should return explicitly permitted OSs when the annotation is present on a Rancher repo', () => {
|
|
582
|
+
const annotations = { [CATALOG_ANNOTATIONS.PERMITTED_OS]: 'linux,windows' };
|
|
583
|
+
const result = getPermittedOSs(annotations, true);
|
|
584
|
+
|
|
585
|
+
expect(result).toHaveLength(2);
|
|
586
|
+
expect(result).toContain('linux');
|
|
587
|
+
expect(result).toContain('windows');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('should return explicitly permitted OSs when the annotation is present on a non-Rancher repo', () => {
|
|
591
|
+
const annotations = { [CATALOG_ANNOTATIONS.PERMITTED_OS]: 'linux' };
|
|
592
|
+
const result = getPermittedOSs(annotations, false);
|
|
593
|
+
|
|
594
|
+
expect(result).toHaveLength(1);
|
|
595
|
+
expect(result).toContain('linux');
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it('should fallback to linux if the annotation is missing on a Rancher repo', () => {
|
|
599
|
+
const annotations = {};
|
|
600
|
+
const result = getPermittedOSs(annotations, true);
|
|
601
|
+
|
|
602
|
+
expect(result).toHaveLength(1);
|
|
603
|
+
expect(result).toContain('linux');
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it('should return an empty array (no restrictions) if the annotation is missing on a non-Rancher repo', () => {
|
|
607
|
+
const annotations = {};
|
|
608
|
+
const result = getPermittedOSs(annotations, false);
|
|
609
|
+
|
|
610
|
+
expect(result).toHaveLength(0);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it('should handle undefined annotations safely for Rancher repos', () => {
|
|
614
|
+
const result = getPermittedOSs(undefined, true);
|
|
615
|
+
|
|
616
|
+
expect(result).toHaveLength(1);
|
|
617
|
+
expect(result).toContain('linux');
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('should handle undefined annotations safely for non-Rancher repos', () => {
|
|
621
|
+
const result = getPermittedOSs(undefined, false);
|
|
622
|
+
|
|
623
|
+
expect(result).toHaveLength(0);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
512
626
|
});
|