Skip to content

Commit

Permalink
Merge pull request #2307 from upalatucci/use-namspace-udn
Browse files Browse the repository at this point in the history
CNV-51530: hide virtctl on managed udn namespaces
  • Loading branch information
openshift-merge-bot[bot] authored Dec 19, 2024
2 parents bfefc56 + a844612 commit 8e17468
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 39 deletions.
1 change: 1 addition & 0 deletions locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,7 @@
"View pod logs": "View pod logs",
"View virtualization dashboard": "View virtualization dashboard",
"View YAML & CLI": "View YAML & CLI",
"Virtctl is disabled for this namespace as it's managed by": "Virtctl is disabled for this namespace as it's managed by",
"virtio": "virtio",
"VirtIO": "VirtIO",
"Virtual machine is not running": "Virtual machine is not running",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
},
"devDependencies": {
"@cypress/webpack-preprocessor": "^6.0.2",
"@kubevirt-ui/kubevirt-api": "^1.3.3",
"@kubevirt-ui/kubevirt-api": "^1.3.5",
"@openshift-console/dynamic-plugin-sdk": "1.8.0",
"@openshift-console/dynamic-plugin-sdk-internal": "1.0.0",
"@openshift-console/dynamic-plugin-sdk-webpack": "^1.3.0",
Expand Down
95 changes: 63 additions & 32 deletions src/utils/components/SSHAccess/components/ConsoleOverVirtctl.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import React, { FC } from 'react';
import { Trans } from 'react-i18next';
import { Link } from 'react-router-dom-v5-compat';

import UserDefinedNetworkModel from '@kubevirt-ui/kubevirt-api/console/models/UserDefinedNetworkModel';
import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt';
import ExternalLink from '@kubevirt-utils/components/ExternalLink/ExternalLink';
import { getConsoleVirtctlCommand } from '@kubevirt-utils/components/SSHAccess/utils';
import { documentationURL } from '@kubevirt-utils/constants/documentation';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import {
ClusterUserDefinedNetworkModelGroupVersionKind,
UserDefinedNetworkModelGroupVersionKind,
} from '@kubevirt-utils/models';
import { getName, getNamespace } from '@kubevirt-utils/resources/shared';
import useNamespaceUDN from '@kubevirt-utils/resources/udn/hooks/useNamespaceUDN';
import { ResourceLink } from '@openshift-console/dynamic-plugin-sdk';
import {
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTerm,
Grid,
GridItem,
Popover,
Text,
} from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';

Expand All @@ -27,43 +36,65 @@ type ConsoleOverVirtctlProps = {
const ConsoleOverVirtctl: FC<ConsoleOverVirtctlProps> = ({ vm }) => {
const { t } = useKubevirtTranslation();

const [isNamespaceManagedByUDN, udn] = useNamespaceUDN(getNamespace(vm));

return (
<DescriptionListGroup className="pf-c-description-list__group">
<DescriptionListTerm className="pf-u-font-size-xs">
{t('SSH using virtctl')}{' '}
<Popover
bodyContent={
<>
<Trans t={t}>
<div>
Open an SSH connection with the VM using the cluster API server. You must be able
to access the API server and have virtctl command line tool installed.
</div>
<Text className="pf-v5-u-disabled-color-100">
{t('SSH using virtctl')}{' '}
<Popover
bodyContent={
<>
<Trans t={t}>
<div>
Open an SSH connection with the VM using the cluster API server. You must be
able to access the API server and have virtctl command line tool installed.
</div>
<br />
<div>
For more details, see{' '}
<ExternalLink href={documentationURL.VIRTCTL_CLI}>
Installing virtctl
</ExternalLink>{' '}
in Getting started with OpenShift Virtualization.
</div>
</Trans>
<br />
<div>
For more details, see{' '}
<Link to={documentationURL.VIRT_CTL}>Installing virtctl</Link> in Getting started
with OpenShift Virtualization.
</div>
</Trans>
<br />
<Grid>
<GridItem span={2}>{t('Example: ')}</GridItem>
<GridItem id="ssh-using-virtctl--example" span={10}>
{getConsoleVirtctlCommand(vm)}
</GridItem>
</Grid>
</>
}
aria-label="Help"
className="virtctl-popover"
position="right"
>
<HelpIcon />
</Popover>
<Grid>
<GridItem span={2}>{t('Example: ')}</GridItem>
<GridItem id="ssh-using-virtctl--example" span={10}>
{getConsoleVirtctlCommand(vm)}
</GridItem>
</Grid>
</>
}
aria-label="Help"
className="virtctl-popover"
position="right"
>
<HelpIcon />
</Popover>
</Text>
</DescriptionListTerm>
<DescriptionListDescription className="pf-c-description-list__description">
<VirtctlSSHCommandClipboardCopy vm={vm} />
{isNamespaceManagedByUDN ? (
<>
{t("Virtctl is disabled for this namespace as it's managed by")}{' '}
<ResourceLink
groupVersionKind={
udn.kind === UserDefinedNetworkModel.kind
? UserDefinedNetworkModelGroupVersionKind
: ClusterUserDefinedNetworkModelGroupVersionKind
}
inline
name={getName(udn)}
namespace={getNamespace(udn)}
/>{' '}
</>
) : (
<VirtctlSSHCommandClipboardCopy vm={vm} />
)}
</DescriptionListDescription>
</DescriptionListGroup>
);
Expand Down
4 changes: 3 additions & 1 deletion src/utils/constants/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export const documentationURL = {
CDI_UPLOAD_SUPPORTED_TYPES: `${REDHAT_DOC_URL}/openshift_container_platform/4.9/html/virtualization/virtual-machines#virt-cdi-supported-operations-matrix_virt-importing-virtual-machine-images-datavolumes`,
CHECKUPS: `${OPENSHIFT_DOC_URL}/4.16/virt/monitoring/virt-running-cluster-checkups.html`,
CHECKUPS_LATENCY: `${OPENSHIFT_DOC_URL}/4.15/virt/monitoring/virt-running-cluster-checkups.html#virt-measuring-latency-vm-secondary-network_virt-running-cluster-checkups`,

CLOUDINIT_INFO: 'https://cloudinit.readthedocs.io/en/latest/index.html',

CREATING_VMS_FROM_TEMPLATES: `${OPENSHIFT_DOC_URL}/4.15/virt/virtual_machines/creating_vms_rh/virt-creating-vms-from-templates.html`,
CRON_INFO: `${REDHAT_BASE_URL}/sysadmin/automate-linux-tasks-cron`,
DATA_FOUNDATION_OPERATOR:
Expand Down Expand Up @@ -55,6 +55,8 @@ export const documentationURL = {
VIRT_CTL: `${OPENSHIFT_DOC_URL}/4.15/virt/getting_started/virt-using-the-cli-tools.html`,
VIRT_MANAGER_DOWNLOAD: 'https://virt-manager.org/download.html',
VIRT_SECONDARY_NETWORK: `${OPENSHIFT_DOC_URL}/4.15/virt/vm_networking/virt-networking-overview.html#secondary-network-config`,
VIRTCTL_CLI:
'https://docs.openshift.com/container-platform/4.15/virt/getting_started/virt-using-the-cli-tools.html',
VIRTUALIZATION_BLOG: `https://cloud.redhat.com/learn/topics/virtualization/`,
VIRTUALIZATION_WHAT_YOU_CAN_DO:
'https://docs.redhat.com/en/documentation/openshift_container_platform/4.16/html/virtualization/about#virt-what-you-can-do-with-virt_about-virt',
Expand Down
51 changes: 51 additions & 0 deletions src/utils/resources/udn/hooks/useNamespaceUDN.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
ClusterUserDefinedNetworkModelGroupVersionKind,
modelToGroupVersionKind,
ProjectModel,
UserDefinedNetworkModelGroupVersionKind,
} from '@kubevirt-utils/models';
import {
ClusterUserDefinedNetworkKind,
UserDefinedNetworkKind,
UserDefinedNetworkRole,
} from '@kubevirt-utils/resources/udn/types';
import { matchSelector } from '@kubevirt-utils/utils/matchSelector';
import { isEmpty } from '@kubevirt-utils/utils/utils';
import { K8sResourceCommon, useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';

const useNamespaceUDN = (
namespace: string,
): [
isNamespaceManagedByUDN: boolean,
udn: ClusterUserDefinedNetworkKind | UserDefinedNetworkKind,
] => {
const [project] = useK8sWatchResource<K8sResourceCommon>({
groupVersionKind: modelToGroupVersionKind(ProjectModel),
name: namespace,
});

const [udns] = useK8sWatchResource<UserDefinedNetworkKind[]>({
groupVersionKind: UserDefinedNetworkModelGroupVersionKind,
isList: true,
namespace,
});

const [clusterUDNs] = useK8sWatchResource<ClusterUserDefinedNetworkKind[]>({
groupVersionKind: ClusterUserDefinedNetworkModelGroupVersionKind,
isList: true,
});

const primaryUDN = udns?.find(
(udn) => udn?.spec?.layer2?.role === UserDefinedNetworkRole.Primary,
);

const primaryClustersUDNs = clusterUDNs
?.filter((clusterUDN) => matchSelector(project, clusterUDN?.spec?.namespaceSelector))
?.find((udn) => udn?.spec?.network?.layer2?.role === UserDefinedNetworkRole.Primary);

const isNamespaceManagedByUDN = !isEmpty(primaryUDN || primaryClustersUDNs);

return [isNamespaceManagedByUDN, primaryUDN || primaryClustersUDNs];
};

export default useNamespaceUDN;
51 changes: 51 additions & 0 deletions src/utils/resources/udn/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { K8sResourceKind, Selector } from '@openshift-console/dynamic-plugin-sdk';

export type UserDefinedNetworkAnnotations = {
description?: string;
};

export enum UserDefinedNetworkRole {
Primary = 'Primary',
Secondary = 'Secondary',
}

export type UserDefinedNetworkLayer2 = {
ipamLifecycle?: string;
joinSubnets?: string[];
mtu?: number;
role: UserDefinedNetworkRole;
subnets?: string[];
};

export type UserDefinedNetworkLayer3Subnet = {
cidr: string;
hostSubnet?: number;
};

export type UserDefinedNetworkSubnet = string | UserDefinedNetworkLayer3Subnet;

export type UserDefinedNetworkLayer3 = {
joinSubnets?: string[];
mtu?: number;
role: string;
subnets?: UserDefinedNetworkLayer3Subnet[];
};

export type ClusterUserDefinedNetworkSpec = {
namespaceSelector?: Selector;
network?: UserDefinedNetworkSpec;
};

export type UserDefinedNetworkSpec = {
layer2?: UserDefinedNetworkLayer2;
layer3?: UserDefinedNetworkLayer3;
topology: string;
};

export type ClusterUserDefinedNetworkKind = {
spec?: ClusterUserDefinedNetworkSpec;
} & K8sResourceKind;

export type UserDefinedNetworkKind = {
spec?: UserDefinedNetworkSpec;
} & K8sResourceKind;
54 changes: 54 additions & 0 deletions src/utils/utils/matchSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
K8sResourceCommon,
MatchExpression,
Operator,
Selector,
} from '@openshift-console/dynamic-plugin-sdk';

import { isEmpty } from './utils';

export const createEquals = (key: string, value: string): MatchExpression => ({
key,
operator: Operator.Equals,
values: [value],
});

export const matchExpressionSatisfied = (
expression: MatchExpression,
labels: { [key in string]: string },
): boolean => {
switch (expression.operator) {
case Operator.Equals:
return labels?.[expression.key] === expression.values?.[0];
case Operator.NotEqual:
return labels?.[expression.key] !== expression.values?.[0];
case Operator.NotEquals:
return labels?.[expression.key] !== expression.values?.[0];
case Operator.Exists:
return !isEmpty(labels?.[expression.key]);
case Operator.DoesNotExist:
return isEmpty(labels?.[expression.key]);
case Operator.GreaterThan:
return labels?.[expression.key] > expression.values?.[0];
case Operator.LessThan:
return labels?.[expression.key] < expression.values?.[0];
case Operator.In:
return expression.values?.includes(labels?.[expression.key]);
case Operator.NotIn:
return !expression.values?.includes(labels?.[expression.key]);
}
};

export const matchSelector = (resource: K8sResourceCommon, selector: Selector) => {
const { matchExpressions, matchLabels } = selector || {};

const requirements = Object.keys(matchLabels || {})
.sort()
.map((matchLabel) => createEquals(matchLabel, matchLabels[matchLabel]));

const allExpressions = [...requirements, ...(matchExpressions || [])];

return allExpressions.every((expression) =>
matchExpressionSatisfied(expression, resource?.metadata?.labels),
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Link } from 'react-router-dom-v5-compat';
import { V1VirtualMachine, V1VirtualMachineInstance } from '@kubevirt-ui/kubevirt-api/kubevirt';
import { VirtualMachineDetailsTab } from '@kubevirt-utils/constants/tabs-constants';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { getNamespace } from '@kubevirt-utils/resources/shared';
import useNamespaceUDN from '@kubevirt-utils/resources/udn/hooks/useNamespaceUDN';
import { VirtualizedTable } from '@openshift-console/dynamic-plugin-sdk';
import { Card, CardBody, CardTitle, Divider } from '@patternfly/react-core';

Expand All @@ -29,6 +31,8 @@ const VirtualMachinesOverviewTabInterfaces: FC<VirtualMachinesOverviewTabInterfa
const data = useVirtualMachinesOverviewTabInterfacesData(vm, vmi);
const columns = useVirtualMachinesOverviewTabInterfacesColumns();

const [isNamespaceManagedByUDN] = useNamespaceUDN(getNamespace(vm));

return (
<div className="VirtualMachinesOverviewTabInterfaces--main">
<Card>
Expand Down Expand Up @@ -57,7 +61,7 @@ const VirtualMachinesOverviewTabInterfaces: FC<VirtualMachinesOverviewTabInterfa
Row={VirtualMachinesOverviewTabNetworkInterfacesRow}
unfilteredData={data}
/>
<VirtualMachinesOverviewTabNetworkFQDN vm={vm} />
{isNamespaceManagedByUDN && <VirtualMachinesOverviewTabNetworkFQDN vm={vm} />}
</CardBody>
</Card>
</div>
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -871,10 +871,10 @@
resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.5.0.tgz#6008e35b9d9d8ee27bc4bfaa70c8cbf33a537b4c"
integrity sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==

"@kubevirt-ui/kubevirt-api@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@kubevirt-ui/kubevirt-api/-/kubevirt-api-1.3.3.tgz#c43ac3c392365c5a5651fd8b60f89c0f7ac5b4c2"
integrity sha512-YqZo/KKq9ndSq6YW052x9GzXMudQV8MSddgX4URO6UAKfEhVDrpKItspX97/dvBFv/qy0hMN7J5CXHDiTG3lMg==
"@kubevirt-ui/kubevirt-api@^1.3.5":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@kubevirt-ui/kubevirt-api/-/kubevirt-api-1.3.5.tgz#c9c10b30d8cc9770fbc6b08077b9a997fdc3ca58"
integrity sha512-FI9RXYwVl3/INr4z7uemuJk3D218DzxOSm4iiVjJIiYp5DHj+r5EqwTaaWi9NQQYj+aZUw3a1akd8p/JO8lMZw==

"@leichtgewicht/ip-codec@^2.0.1":
version "2.0.4"
Expand Down

0 comments on commit 8e17468

Please sign in to comment.