Skip to content

Commit

Permalink
CNV-51530: hide virtctl on managed udn namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
upalatucci committed Dec 10, 2024
1 parent 23798e5 commit 49ad98b
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 7 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 @@ -1394,6 +1394,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
30 changes: 29 additions & 1 deletion src/utils/components/SSHAccess/components/ConsoleOverVirtctl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import { Link } from 'react-router-dom-v5-compat';
import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt';
import { getConsoleVirtctlCommand } from '@kubevirt-utils/components/SSHAccess/utils';
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 { isEmpty } from '@kubevirt-utils/utils/utils';
import { ResourceLink } from '@openshift-console/dynamic-plugin-sdk';
import {
DescriptionListDescription,
DescriptionListGroup,
Expand All @@ -26,6 +34,10 @@ type ConsoleOverVirtctlProps = {
const ConsoleOverVirtctl: FC<ConsoleOverVirtctlProps> = ({ vm }) => {
const { t } = useKubevirtTranslation();

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

const namespaceManagedByUDN = !isEmpty(udn) || !isEmpty(clusterUDN);

return (
<DescriptionListGroup className="pf-c-description-list__group">
<DescriptionListTerm className="pf-u-font-size-xs">
Expand Down Expand Up @@ -68,7 +80,23 @@ const ConsoleOverVirtctl: FC<ConsoleOverVirtctlProps> = ({ vm }) => {
</Popover>
</DescriptionListTerm>
<DescriptionListDescription className="pf-c-description-list__description">
<VirtctlSSHCommandClipboardCopy vm={vm} />
{namespaceManagedByUDN ? (
<>
{t("Virtctl is disabled for this namespace as it's managed by")}{' '}
<ResourceLink
groupVersionKind={
udn
? UserDefinedNetworkModelGroupVersionKind
: ClusterUserDefinedNetworkModelGroupVersionKind
}
inline
name={getName(udn || clusterUDN)}
namespace={getNamespace(udn)}
/>{' '}
</>
) : (
<VirtctlSSHCommandClipboardCopy vm={vm} />
)}
</DescriptionListDescription>
</DescriptionListGroup>
);
Expand Down
43 changes: 43 additions & 0 deletions src/utils/resources/udn/hooks/useNamespaceUDN.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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 { K8sResourceCommon, useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';

const useNamespaceUDN = (namespace: string) => {
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);

return [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,9 @@ 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 { isEmpty } from '@kubevirt-utils/utils/utils';
import { VirtualizedTable } from '@openshift-console/dynamic-plugin-sdk';
import { Card, CardBody, CardTitle, Divider } from '@patternfly/react-core';

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

const [udn, clusterUDN] = useNamespaceUDN(getNamespace(vm));
const isNotUDNManaged = isEmpty(udn) && isEmpty(clusterUDN);

return (
<div className="VirtualMachinesOverviewTabInterfaces--main">
<Card>
Expand Down Expand Up @@ -57,7 +63,7 @@ const VirtualMachinesOverviewTabInterfaces: FC<VirtualMachinesOverviewTabInterfa
Row={VirtualMachinesOverviewTabNetworkInterfacesRow}
unfilteredData={data}
/>
<VirtualMachinesOverviewTabNetworkFQDN vm={vm} />
{isNotUDNManaged && <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 49ad98b

Please sign in to comment.