@rancher/shell 3.0.9-rc.5 → 3.0.9
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/oci-open-containers.svg +22 -0
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -0
- package/assets/translations/en-us.yaml +129 -25
- package/components/CruResource.vue +3 -1
- package/components/ExplorerProjectsNamespaces.vue +12 -12
- package/components/IconOrSvg.vue +61 -42
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
- package/components/Resource/Detail/ResourceRow.vue +2 -2
- package/components/ResourceList/index.vue +7 -4
- package/components/SortableTable/index.vue +2 -2
- package/components/Window/ContainerLogs.vue +48 -37
- package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
- package/components/fleet/FleetClusterTargets/index.vue +6 -1
- package/components/fleet/GitRepoAdvancedTab.vue +333 -0
- package/components/fleet/GitRepoMetadataTab.vue +43 -0
- package/components/fleet/GitRepoRepositoryTab.vue +101 -0
- package/components/fleet/GitRepoTargetTab.vue +77 -0
- package/components/fleet/HelmOpAdvancedTab.vue +247 -0
- package/components/fleet/HelmOpChartTab.vue +158 -0
- package/components/fleet/HelmOpMetadataTab.vue +46 -0
- package/components/fleet/HelmOpTargetTab.vue +84 -0
- package/components/fleet/HelmOpValuesTab.vue +147 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
- package/components/form/BannerSettings.vue +2 -2
- package/components/form/NodeScheduling.vue +81 -7
- package/components/form/NotificationSettings.vue +2 -2
- package/components/form/PodAffinity.vue +1 -36
- package/components/form/ResourceLabeledSelect.vue +8 -4
- package/components/form/ResourceQuota/Namespace.vue +30 -9
- package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
- package/components/form/ResourceQuota/Project.vue +140 -82
- package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
- package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
- package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
- package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
- package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
- package/components/form/SchedulingCustomization.vue +14 -6
- package/components/form/SelectOrCreateAuthSecret.vue +107 -18
- package/components/form/__tests__/NodeScheduling.test.ts +12 -9
- package/components/form/__tests__/PodAffinity.test.ts +21 -2
- package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
- package/components/formatter/ClusterLink.vue +8 -0
- package/components/formatter/SecretOrigin.vue +79 -0
- package/config/labels-annotations.js +7 -6
- package/config/pagination-table-headers.js +6 -4
- package/config/product/explorer.js +1 -11
- package/config/product/manager.js +0 -1
- package/config/query-params.js +3 -0
- package/config/settings.ts +15 -2
- package/config/table-headers.js +21 -17
- package/config/types.js +23 -8
- package/detail/fleet.cattle.io.cluster.vue +1 -1
- package/detail/workload/index.vue +11 -16
- package/dialog/DeactivateDriverDialog.vue +1 -1
- package/dialog/FeatureFlagListDialog.vue +1 -1
- package/dialog/Ipv6NetworkingDialog.vue +156 -0
- package/dialog/ScalePoolDownDialog.vue +2 -2
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -128
- package/edit/auth/oidc.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
- package/edit/fleet.cattle.io.gitrepo.vue +153 -283
- package/edit/fleet.cattle.io.helmop.vue +190 -332
- package/edit/management.cattle.io.project.vue +5 -42
- package/edit/management.cattle.io.setting.vue +6 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
- package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
- package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
- package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +114 -0
- package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +167 -69
- package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +70 -7
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +343 -0
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
- package/edit/secret/index.vue +1 -1
- package/edit/token.vue +68 -29
- package/edit/workload/__tests__/index.test.ts +2 -37
- package/edit/workload/index.vue +6 -2
- package/edit/workload/mixins/workload.js +0 -32
- package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
- package/list/management.cattle.io.setting.vue +13 -0
- package/list/provisioning.cattle.io.cluster.vue +50 -1
- package/list/secret.vue +4 -9
- package/list/service.vue +6 -8
- package/machine-config/amazonec2.vue +11 -4
- package/machine-config/components/EC2Networking.vue +46 -30
- package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
- package/machine-config/digitalocean.vue +3 -3
- package/models/__tests__/chart.test.ts +2 -2
- package/models/__tests__/namespace.test.ts +11 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
- package/models/__tests__/workload.test.ts +42 -1
- package/models/catalog.cattle.io.clusterrepo.js +30 -4
- package/models/chart.js +3 -3
- package/models/ext.cattle.io.token.js +48 -0
- package/models/kontainerdriver.js +2 -2
- package/models/namespace.js +7 -1
- package/models/nodedriver.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +28 -7
- package/models/secret.js +0 -17
- package/models/service.js +44 -1
- package/models/token.js +4 -0
- package/models/workload.js +12 -6
- package/package.json +1 -1
- package/pages/account/index.vue +96 -67
- package/pages/auth/setup.vue +5 -14
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
- package/pages/c/_cluster/apps/charts/index.vue +82 -3
- package/pages/c/_cluster/apps/charts/install.vue +317 -42
- package/pages/c/_cluster/explorer/tools/index.vue +1 -1
- package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
- package/pages/c/_cluster/settings/index.vue +3 -1
- package/pages/c/_cluster/uiplugins/index.vue +1 -1
- package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
- package/plugins/dashboard-store/actions.js +3 -8
- package/plugins/dashboard-store/getters.js +7 -5
- package/plugins/dashboard-store/mutations.js +4 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/steve-class.test.ts +102 -141
- package/plugins/steve/steve-class.js +12 -3
- package/plugins/steve/steve-pagination-utils.ts +6 -2
- package/rancher-components/RcIcon/types.ts +2 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +72 -20
- package/store/prefs.js +3 -0
- package/types/aws-sdk.d.ts +121 -0
- package/types/resources/node.ts +15 -0
- package/types/shell/index.d.ts +537 -506
- package/types/store/pagination.types.ts +5 -5
- package/utils/__tests__/array.test.ts +1 -29
- package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
- package/utils/array.ts +0 -11
- package/utils/aws.ts +21 -0
- package/utils/cluster.js +22 -2
- package/utils/selector-typed.ts +1 -1
- package/utils/svg-filter.js +4 -3
- package/components/__tests__/ProjectRow.test.ts +0 -206
- package/components/form/ResourceQuota/ProjectRow.vue +0 -277
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import StatusCard from '@shell/components/Resource/Detail/Card/StatusCard/index.vue';
|
|
3
|
+
import StatusBar from '@shell/components/Resource/Detail/StatusBar.vue';
|
|
4
|
+
import StatusRow from '@shell/components/Resource/Detail/StatusRow.vue';
|
|
5
|
+
import Scaler from '@shell/components/Resource/Detail/Card/Scaler.vue';
|
|
6
|
+
|
|
7
|
+
describe('component: StatusCard', () => {
|
|
8
|
+
const mockResource = (stateDisplay: string, stateSimpleColor: string) => ({
|
|
9
|
+
stateDisplay,
|
|
10
|
+
stateSimpleColor,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const defaultMocks = {
|
|
14
|
+
$store: {
|
|
15
|
+
getters: { 'i18n/t': (key: string) => key },
|
|
16
|
+
dispatch: jest.fn(),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const mountCard = (props: Record<string, unknown> = {}) => {
|
|
21
|
+
return mount(StatusCard, {
|
|
22
|
+
props: { title: 'Pods', ...props },
|
|
23
|
+
global: {
|
|
24
|
+
mocks: defaultMocks,
|
|
25
|
+
stubs: {
|
|
26
|
+
StatusBar: true,
|
|
27
|
+
StatusRow: true,
|
|
28
|
+
Scaler: true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe('with resources', () => {
|
|
35
|
+
it('should render StatusBar and StatusRows when resources are present', () => {
|
|
36
|
+
const resources = [
|
|
37
|
+
mockResource('Running', 'text-success'),
|
|
38
|
+
mockResource('Running', 'text-success'),
|
|
39
|
+
mockResource('Error', 'text-error'),
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const wrapper = mountCard({ resources });
|
|
43
|
+
|
|
44
|
+
expect(wrapper.findComponent(StatusBar).exists()).toBe(true);
|
|
45
|
+
expect(wrapper.findAllComponents(StatusRow)).toHaveLength(2);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should not render noResourcesMessage when resources are present', () => {
|
|
49
|
+
const resources = [mockResource('Running', 'text-success')];
|
|
50
|
+
|
|
51
|
+
const wrapper = mountCard({ resources, noResourcesMessage: 'No pods' });
|
|
52
|
+
|
|
53
|
+
expect(wrapper.find('.text-deemphasized').exists()).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('with empty resources', () => {
|
|
58
|
+
it('should not render StatusBar or StatusRows', () => {
|
|
59
|
+
const wrapper = mountCard({ resources: [], noResourcesMessage: 'No pods' });
|
|
60
|
+
|
|
61
|
+
expect(wrapper.findComponent(StatusBar).exists()).toBe(false);
|
|
62
|
+
expect(wrapper.findAllComponents(StatusRow)).toHaveLength(0);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should render noResourcesMessage when provided', () => {
|
|
66
|
+
const wrapper = mountCard({ resources: [], noResourcesMessage: 'There are no pods currently present.' });
|
|
67
|
+
|
|
68
|
+
const emptyDiv = wrapper.find('.text-deemphasized');
|
|
69
|
+
|
|
70
|
+
expect(emptyDiv.exists()).toBe(true);
|
|
71
|
+
expect(emptyDiv.text()).toBe('There are no pods currently present.');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should not render empty-state div when noResourcesMessage is not provided', () => {
|
|
75
|
+
const wrapper = mountCard({ resources: [] });
|
|
76
|
+
|
|
77
|
+
expect(wrapper.find('.text-deemphasized').exists()).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('with undefined resources', () => {
|
|
82
|
+
it('should not render StatusBar, StatusRows, or noResourcesMessage', () => {
|
|
83
|
+
const wrapper = mountCard({ noResourcesMessage: 'No pods' });
|
|
84
|
+
|
|
85
|
+
expect(wrapper.findComponent(StatusBar).exists()).toBe(false);
|
|
86
|
+
expect(wrapper.findAllComponents(StatusRow)).toHaveLength(0);
|
|
87
|
+
expect(wrapper.find('.text-deemphasized').exists()).toBe(true);
|
|
88
|
+
expect(wrapper.find('.text-deemphasized').text()).toBe('No pods');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('scaling', () => {
|
|
93
|
+
it('should render Scaler when showScaling is true', () => {
|
|
94
|
+
const resources = [mockResource('Running', 'text-success')];
|
|
95
|
+
|
|
96
|
+
const wrapper = mountCard({ resources, showScaling: true });
|
|
97
|
+
|
|
98
|
+
expect(wrapper.findComponent(Scaler).exists()).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should not render Scaler when showScaling is false', () => {
|
|
102
|
+
const resources = [mockResource('Running', 'text-success')];
|
|
103
|
+
|
|
104
|
+
const wrapper = mountCard({ resources, showScaling: false });
|
|
105
|
+
|
|
106
|
+
expect(wrapper.findComponent(Scaler).exists()).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -13,6 +13,7 @@ export interface Props {
|
|
|
13
13
|
title: string;
|
|
14
14
|
resources?: any[];
|
|
15
15
|
showScaling?: boolean;
|
|
16
|
+
noResourcesMessage?: string;
|
|
16
17
|
}
|
|
17
18
|
</script>
|
|
18
19
|
|
|
@@ -20,7 +21,11 @@ export interface Props {
|
|
|
20
21
|
const store = useStore();
|
|
21
22
|
const i18n = useI18n(store);
|
|
22
23
|
|
|
23
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
25
|
+
resources: undefined,
|
|
26
|
+
showScaling: false,
|
|
27
|
+
noResourcesMessage: undefined
|
|
28
|
+
});
|
|
24
29
|
const emit = defineEmits(['decrease', 'increase']);
|
|
25
30
|
|
|
26
31
|
const segmentAccumulator = computed(() => {
|
|
@@ -99,9 +104,15 @@ const rows = computed(() => {
|
|
|
99
104
|
@decrease="(newValue) => emit('decrease', newValue)"
|
|
100
105
|
/>
|
|
101
106
|
</template>
|
|
102
|
-
<StatusBar
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
<StatusBar
|
|
108
|
+
v-if="rows.length > 0"
|
|
109
|
+
:segments="segments"
|
|
110
|
+
/>
|
|
111
|
+
<VerticalGap v-if="rows.length > 0" />
|
|
112
|
+
<div
|
|
113
|
+
v-if="rows.length > 0"
|
|
114
|
+
class="pod-distribution"
|
|
115
|
+
>
|
|
105
116
|
<StatusRow
|
|
106
117
|
v-for="(row, i) in rows"
|
|
107
118
|
:key="i"
|
|
@@ -111,6 +122,12 @@ const rows = computed(() => {
|
|
|
111
122
|
:percent="row.percent"
|
|
112
123
|
/>
|
|
113
124
|
</div>
|
|
125
|
+
<div
|
|
126
|
+
v-else-if="props.noResourcesMessage"
|
|
127
|
+
class="text-deemphasized"
|
|
128
|
+
>
|
|
129
|
+
{{ props.noResourcesMessage }}
|
|
130
|
+
</div>
|
|
114
131
|
</Card>
|
|
115
132
|
</template>
|
|
116
133
|
|
|
@@ -6,7 +6,12 @@ import { NAME as FLEET_NAME } from '@shell/config/product/fleet';
|
|
|
6
6
|
|
|
7
7
|
const mockStore = {
|
|
8
8
|
getters: {
|
|
9
|
-
productId:
|
|
9
|
+
productId: 'PRODUCT_ID',
|
|
10
|
+
clusterId: 'CLUSTER_ID',
|
|
11
|
+
'type-map/optionsFor': jest.fn(),
|
|
12
|
+
currentCluster: 'CLUSTER_ID',
|
|
13
|
+
currentStore: () => 'cluster',
|
|
14
|
+
'cluster/canList': () => true,
|
|
10
15
|
}
|
|
11
16
|
};
|
|
12
17
|
const mockRoute = { params: { cluster: 'CLUSTER', namespace: 'NAMESPACE' } };
|
|
@@ -32,7 +37,8 @@ describe('composables: IdentifyingFields', () => {
|
|
|
32
37
|
expect(result).toBeUndefined();
|
|
33
38
|
});
|
|
34
39
|
|
|
35
|
-
it('should return a valid namespace row', () => {
|
|
40
|
+
it('should return a valid namespace row with ResourcePopover when user canList namespaces', () => {
|
|
41
|
+
mockStore.getters['cluster/canList'] = () => true;
|
|
36
42
|
const resource = { namespace: 'NAMESPACE' };
|
|
37
43
|
const result = useNamespace(resource);
|
|
38
44
|
|
|
@@ -42,6 +48,17 @@ describe('composables: IdentifyingFields', () => {
|
|
|
42
48
|
expect(result?.value.label).toStrictEqual('component.resource.detail.metadata.identifyingInformation.namespace');
|
|
43
49
|
expect(result?.value.valueDataTestid).toStrictEqual('masthead-subheader-namespace');
|
|
44
50
|
});
|
|
51
|
+
|
|
52
|
+
it('should return a plain text namespace row when user cannot canList namespaces', () => {
|
|
53
|
+
mockStore.getters['cluster/canList'] = () => false;
|
|
54
|
+
const resource = { namespace: 'NAMESPACE' };
|
|
55
|
+
const result = useNamespace(resource);
|
|
56
|
+
|
|
57
|
+
expect(result?.value.valueOverride).toBeUndefined();
|
|
58
|
+
expect(result?.value.value).toStrictEqual(resource.namespace);
|
|
59
|
+
expect(result?.value.label).toStrictEqual('component.resource.detail.metadata.identifyingInformation.namespace');
|
|
60
|
+
expect(result?.value.valueDataTestid).toStrictEqual('masthead-subheader-namespace');
|
|
61
|
+
});
|
|
45
62
|
});
|
|
46
63
|
|
|
47
64
|
describe('useWorkspace', () => {
|
|
@@ -24,18 +24,26 @@ export const useNamespace = (resource: any): ComputedRef<Row> | undefined => {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
return computed(() => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
const currentStore = store.getters['currentStore'](NAMESPACE);
|
|
28
|
+
const canList = store.getters[`${ currentStore }/canList`](NAMESPACE);
|
|
29
|
+
|
|
30
|
+
const label = i18n.t('component.resource.detail.metadata.identifyingInformation.namespace');
|
|
31
|
+
const value = resourceValue.namespace;
|
|
32
|
+
const valueDataTestid = 'masthead-subheader-namespace';
|
|
33
|
+
const valueOverride = canList ? {
|
|
34
|
+
component: markRaw(defineAsyncComponent(() => import('@shell/components/Resource/Detail/ResourcePopover/index.vue'))),
|
|
35
|
+
props: {
|
|
36
|
+
type: NAMESPACE,
|
|
37
|
+
id: resourceValue.namespace,
|
|
38
|
+
detailLocation: resourceValue.namespaceLocation
|
|
38
39
|
}
|
|
40
|
+
} : undefined;
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
label,
|
|
44
|
+
value,
|
|
45
|
+
valueDataTestid,
|
|
46
|
+
valueOverride,
|
|
39
47
|
};
|
|
40
48
|
});
|
|
41
49
|
};
|
|
@@ -79,6 +79,18 @@ describe('component: ResourcePopover/index.vue', () => {
|
|
|
79
79
|
expect(wrapper.find('.display').exists()).toBe(false);
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
it('should show plain text and no PopoverCard when fetch fails', async() => {
|
|
83
|
+
mockClusterFind.mockRejectedValue(new Error('Not found'));
|
|
84
|
+
const wrapper = createWrapper(undefined, undefined, PopoverCardStub);
|
|
85
|
+
|
|
86
|
+
await wrapper.vm.$nextTick();
|
|
87
|
+
await wrapper.vm.$nextTick();
|
|
88
|
+
await wrapper.vm.$nextTick();
|
|
89
|
+
|
|
90
|
+
expect(wrapper.findComponent(PopoverCard).exists()).toBe(false);
|
|
91
|
+
expect(wrapper.text()).toBe('test-ns/test-pod');
|
|
92
|
+
});
|
|
93
|
+
|
|
82
94
|
it('should fetch data using the default store', async() => {
|
|
83
95
|
const wrapper = createWrapper();
|
|
84
96
|
|
|
@@ -58,6 +58,7 @@ const actionInvoked = () => {
|
|
|
58
58
|
|
|
59
59
|
<template>
|
|
60
60
|
<PopoverCard
|
|
61
|
+
v-if="!fetch.error"
|
|
61
62
|
class="resource-popover"
|
|
62
63
|
:card-title="nameDisplay"
|
|
63
64
|
fallback-focus="[data-testid='resource-popover-action-menu']"
|
|
@@ -104,6 +105,7 @@ const actionInvoked = () => {
|
|
|
104
105
|
/>
|
|
105
106
|
</template>
|
|
106
107
|
</PopoverCard>
|
|
108
|
+
<span v-else>{{ props.id }}</span>
|
|
107
109
|
</template>
|
|
108
110
|
|
|
109
111
|
<style lang="scss" scoped>
|
|
@@ -47,7 +47,7 @@ const displayCounts = computed(() => {
|
|
|
47
47
|
</SubtleLink>
|
|
48
48
|
<span
|
|
49
49
|
v-else
|
|
50
|
-
class="text-
|
|
50
|
+
class="text-deemphasized"
|
|
51
51
|
>
|
|
52
52
|
{{ label }}
|
|
53
53
|
</span>
|
|
@@ -55,7 +55,7 @@ const displayCounts = computed(() => {
|
|
|
55
55
|
<div class="right">
|
|
56
56
|
<div
|
|
57
57
|
v-if="!counts || counts.length == 0"
|
|
58
|
-
class="text-
|
|
58
|
+
class="text-deemphasized"
|
|
59
59
|
>
|
|
60
60
|
0
|
|
61
61
|
</div>
|
|
@@ -87,11 +87,14 @@ export default {
|
|
|
87
87
|
},
|
|
88
88
|
|
|
89
89
|
beforeMount() {
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
if (!this.hasListComponent) {
|
|
91
|
+
// If a list doesn't have a list component confirm via schema that the resource is listable
|
|
92
|
+
const inStore = this.$store.getters['currentStore'](this.resource);
|
|
93
|
+
const canList = this.$store.getters[`${ inStore }/canList`](this.resource);
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
if (!canList) {
|
|
96
|
+
this.$store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceListNotListable', { resource: this.schema?.id || this.resource || 'unknown' }, true)));
|
|
97
|
+
}
|
|
95
98
|
}
|
|
96
99
|
},
|
|
97
100
|
|
|
@@ -2099,13 +2099,13 @@ export default {
|
|
|
2099
2099
|
|
|
2100
2100
|
$header-padding: 20px;
|
|
2101
2101
|
.sub-header-row {
|
|
2102
|
-
padding: 0 0 $header-padding / 2 0;
|
|
2102
|
+
padding: 0 0 calc($header-padding / 2) 0;
|
|
2103
2103
|
}
|
|
2104
2104
|
|
|
2105
2105
|
.fixed-header-actions {
|
|
2106
2106
|
padding: 0 0 $header-padding 0;
|
|
2107
2107
|
&.with-sub-header {
|
|
2108
|
-
padding: 0 0 $header-padding / 4 0;
|
|
2108
|
+
padding: 0 0 calc($header-padding / 4) 0;
|
|
2109
2109
|
}
|
|
2110
2110
|
|
|
2111
2111
|
width: 100%;
|
|
@@ -13,7 +13,6 @@ import LogItem from '@shell/components/LogItem';
|
|
|
13
13
|
import ContainerLogsActions from '@shell/components/Window/ContainerLogsActions.vue';
|
|
14
14
|
import { shallowRef } from 'vue';
|
|
15
15
|
import { useStore } from 'vuex';
|
|
16
|
-
import { debounce } from 'lodash';
|
|
17
16
|
import { useRuntimeFlag } from '@shell/composables/useRuntimeFlag';
|
|
18
17
|
|
|
19
18
|
import { escapeRegex } from '@shell/utils/string';
|
|
@@ -110,12 +109,6 @@ export default {
|
|
|
110
109
|
required: true,
|
|
111
110
|
},
|
|
112
111
|
|
|
113
|
-
// The height of the window
|
|
114
|
-
height: {
|
|
115
|
-
type: Number,
|
|
116
|
-
required: true,
|
|
117
|
-
},
|
|
118
|
-
|
|
119
112
|
// The pod to connect to
|
|
120
113
|
pod: {
|
|
121
114
|
type: Object,
|
|
@@ -147,7 +140,6 @@ export default {
|
|
|
147
140
|
socket: null,
|
|
148
141
|
isOpen: false,
|
|
149
142
|
isFollowing: true,
|
|
150
|
-
scrollThreshold: 80,
|
|
151
143
|
timestamps: this.$store.getters['prefs/get'](LOGS_TIME),
|
|
152
144
|
wrap: this.$store.getters['prefs/get'](LOGS_WRAP),
|
|
153
145
|
previous: false,
|
|
@@ -290,18 +282,6 @@ export default {
|
|
|
290
282
|
container() {
|
|
291
283
|
this.connect();
|
|
292
284
|
},
|
|
293
|
-
|
|
294
|
-
lines: {
|
|
295
|
-
handler() {
|
|
296
|
-
if (this.isFollowing) {
|
|
297
|
-
this.$nextTick(() => {
|
|
298
|
-
this.follow();
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
},
|
|
302
|
-
deep: true
|
|
303
|
-
}
|
|
304
|
-
|
|
305
285
|
},
|
|
306
286
|
|
|
307
287
|
beforeUnmount() {
|
|
@@ -315,6 +295,35 @@ export default {
|
|
|
315
295
|
},
|
|
316
296
|
|
|
317
297
|
methods: {
|
|
298
|
+
onScrollToBottom() {
|
|
299
|
+
this.startFollowing();
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
onMouseWheel(ev) {
|
|
303
|
+
// On scroll up stop following
|
|
304
|
+
if (ev.deltaY < 0) {
|
|
305
|
+
this.stopFollowing();
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
onMouseDown() {
|
|
310
|
+
this.stopFollowing();
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
onMouseUp() {
|
|
314
|
+
const virtualList = this.$refs.virtualList;
|
|
315
|
+
|
|
316
|
+
if (!virtualList) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const isVirtualListScrolledToBottom = virtualList.getOffset() + virtualList.getClientSize() >= virtualList.getScrollSize();
|
|
321
|
+
|
|
322
|
+
if (isVirtualListScrolledToBottom) {
|
|
323
|
+
this.startFollowing();
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
|
|
318
327
|
openContainerMenu() {
|
|
319
328
|
this.isContainerMenuOpen = true;
|
|
320
329
|
},
|
|
@@ -451,22 +460,21 @@ export default {
|
|
|
451
460
|
if (maxLines && this.lines.length > maxLines) {
|
|
452
461
|
this.lines = this.lines.slice(-maxLines);
|
|
453
462
|
}
|
|
463
|
+
|
|
464
|
+
this.$nextTick(() => {
|
|
465
|
+
this.follow();
|
|
466
|
+
});
|
|
454
467
|
}
|
|
455
468
|
},
|
|
456
469
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if (virtualList) {
|
|
461
|
-
const scrollSize = virtualList.getScrollSize();
|
|
462
|
-
const clientSize = virtualList.getClientSize();
|
|
463
|
-
const offset = virtualList.getOffset();
|
|
464
|
-
|
|
465
|
-
const distanceFromBottom = scrollSize - clientSize - offset;
|
|
470
|
+
stopFollowing() {
|
|
471
|
+
this.isFollowing = false;
|
|
472
|
+
},
|
|
466
473
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
474
|
+
startFollowing() {
|
|
475
|
+
this.isFollowing = true;
|
|
476
|
+
this.follow();
|
|
477
|
+
},
|
|
470
478
|
|
|
471
479
|
parseRange(range) {
|
|
472
480
|
range = `${ range }`.trim().toLowerCase();
|
|
@@ -537,9 +545,8 @@ export default {
|
|
|
537
545
|
follow() {
|
|
538
546
|
const virtualList = this.$refs.virtualList;
|
|
539
547
|
|
|
540
|
-
if (virtualList) {
|
|
541
|
-
virtualList.
|
|
542
|
-
this.isFollowing = true;
|
|
548
|
+
if (virtualList && this.isFollowing) {
|
|
549
|
+
virtualList.scrollToOffset(virtualList.getScrollSize());
|
|
543
550
|
}
|
|
544
551
|
},
|
|
545
552
|
|
|
@@ -607,7 +614,7 @@ export default {
|
|
|
607
614
|
:aria-label="t('wm.containerLogs.follow')"
|
|
608
615
|
:aria-disabled="isFollowing"
|
|
609
616
|
:disabled="isFollowing"
|
|
610
|
-
@click="
|
|
617
|
+
@click="startFollowing"
|
|
611
618
|
>
|
|
612
619
|
<t
|
|
613
620
|
class="wm-btn-large"
|
|
@@ -762,7 +769,11 @@ export default {
|
|
|
762
769
|
:data-component="logItem"
|
|
763
770
|
direction="vertical"
|
|
764
771
|
:keeps="200"
|
|
765
|
-
|
|
772
|
+
:bottom-threshold="200"
|
|
773
|
+
@tobottom="onScrollToBottom"
|
|
774
|
+
@wheel.passive="onMouseWheel"
|
|
775
|
+
@mousedown="onMouseDown"
|
|
776
|
+
@mouseup="onMouseUp"
|
|
766
777
|
/>
|
|
767
778
|
<template v-if="!filtered.length">
|
|
768
779
|
<div v-if="search">
|
|
@@ -67,13 +67,13 @@ export default {
|
|
|
67
67
|
flex-direction: column;
|
|
68
68
|
}
|
|
69
69
|
.targets-list-list {
|
|
70
|
-
overflow-y:
|
|
70
|
+
overflow-y: auto;
|
|
71
71
|
}
|
|
72
72
|
.link-main{
|
|
73
73
|
word-spacing: 22px;
|
|
74
|
+
line-height: 17px; // To fit the icon size and make sure it doesnt resize
|
|
74
75
|
}
|
|
75
76
|
.link-icon {
|
|
76
|
-
position: absolute;
|
|
77
77
|
margin-left: -14px; // Remove the space of the icon to make it float to accomodate the underline
|
|
78
78
|
display: none; // Make the icon disappear by default
|
|
79
79
|
}
|
|
@@ -406,7 +406,10 @@ export default {
|
|
|
406
406
|
:placeholder="t('fleet.clusterTargets.clusters.byName.placeholder')"
|
|
407
407
|
@update:value="selectClusters"
|
|
408
408
|
/>
|
|
409
|
-
<div
|
|
409
|
+
<div
|
|
410
|
+
v-if="!isView || (clusterSelectors && clusterSelectors.length > 0)"
|
|
411
|
+
class="mmt-6"
|
|
412
|
+
>
|
|
410
413
|
<h4 class="m-0">
|
|
411
414
|
{{ t('fleet.clusterTargets.clusters.byLabel.title') }}
|
|
412
415
|
</h4>
|
|
@@ -427,6 +430,7 @@ export default {
|
|
|
427
430
|
@update:value="updateMatchExpressions(i, $event, selector.key)"
|
|
428
431
|
/>
|
|
429
432
|
<RcButton
|
|
433
|
+
v-if="!isView"
|
|
430
434
|
size="small"
|
|
431
435
|
variant="link"
|
|
432
436
|
@click="removeMatchExpressions(selector.key)"
|
|
@@ -435,6 +439,7 @@ export default {
|
|
|
435
439
|
</RcButton>
|
|
436
440
|
</div>
|
|
437
441
|
<RcButton
|
|
442
|
+
v-if="!isView"
|
|
438
443
|
size="small"
|
|
439
444
|
variant="secondary"
|
|
440
445
|
class="mmt-4"
|