diff --git a/frontend/packages/console-shared/src/selectors/common.ts b/frontend/packages/console-shared/src/selectors/common.ts new file mode 100644 index 00000000000..98abc0c2d68 --- /dev/null +++ b/frontend/packages/console-shared/src/selectors/common.ts @@ -0,0 +1,6 @@ +import * as _ from 'lodash-es'; + +import { K8sResourceKind } from '@console/internal/module/k8s'; + +export const getName = (value: K8sResourceKind): string => _.get(value, 'metadata.name'); +export const getNamespace = (value: K8sResourceKind): string => _.get(value, 'metadata.namespace'); diff --git a/frontend/packages/console-shared/src/selectors/index.ts b/frontend/packages/console-shared/src/selectors/index.ts index 98abc0c2d68..01e75f3d7c1 100644 --- a/frontend/packages/console-shared/src/selectors/index.ts +++ b/frontend/packages/console-shared/src/selectors/index.ts @@ -1,6 +1,2 @@ -import * as _ from 'lodash-es'; - -import { K8sResourceKind } from '@console/internal/module/k8s'; - -export const getName = (value: K8sResourceKind): string => _.get(value, 'metadata.name'); -export const getNamespace = (value: K8sResourceKind): string => _.get(value, 'metadata.namespace'); +export * from './common'; +export * from './machine'; diff --git a/frontend/packages/console-shared/src/selectors/machine.ts b/frontend/packages/console-shared/src/selectors/machine.ts new file mode 100644 index 00000000000..bfeb8ba7651 --- /dev/null +++ b/frontend/packages/console-shared/src/selectors/machine.ts @@ -0,0 +1,18 @@ +import * as _ from 'lodash-es'; + +import { + MachineKind, + MachineSetKind, + MachineDeploymentKind, + NodeKind, +} from '@console/internal/module/k8s'; +import { getName } from './common'; + +export const getMachineRole = (obj: MachineKind | MachineSetKind | MachineDeploymentKind) => + _.get(obj, ['metadata', 'labels', 'machine.openshift.io/cluster-api-machine-role']); +export const getMachineNodeName = (obj: MachineKind) => _.get(obj, 'status.nodeRef.name'); +export const getMachineAWSPlacement = (machine: MachineKind) => + _.get(machine, 'spec.providerSpec.value.placement') || {}; + +export const getMachineNode = (machine: MachineKind, nodes: NodeKind[]) => + nodes.find((node) => getMachineNodeName(machine) === getName(node)); diff --git a/frontend/packages/eslint-plugin-console/lib/config/rules/react.js b/frontend/packages/eslint-plugin-console/lib/config/rules/react.js index 51d11b214ad..5feacbdf934 100644 --- a/frontend/packages/eslint-plugin-console/lib/config/rules/react.js +++ b/frontend/packages/eslint-plugin-console/lib/config/rules/react.js @@ -11,4 +11,8 @@ module.exports = { // One JSX element Per line 'react/jsx-one-expression-per-line': 'off', + + // Prevent missing props validation in a React component definition + // Off due to false positives in typescript + 'react/prop-types': 'off', }; diff --git a/frontend/packages/metal3-plugin/src/components/host-role.tsx b/frontend/packages/metal3-plugin/src/components/host-role.tsx new file mode 100644 index 00000000000..5df85d02a83 --- /dev/null +++ b/frontend/packages/metal3-plugin/src/components/host-role.tsx @@ -0,0 +1,8 @@ +import { getMachineRole, DASH } from '@console/shared'; +import { MachineKind } from '@console/internal/module/k8s'; + +type BaremetalHostRoleProps = { + machine: MachineKind; +}; +export const BaremetalHostRole: React.FC = ({ machine }) => + getMachineRole(machine) || DASH; diff --git a/frontend/packages/metal3-plugin/src/components/host.tsx b/frontend/packages/metal3-plugin/src/components/host.tsx index 8c76972e102..78e25de6cc6 100644 --- a/frontend/packages/metal3-plugin/src/components/host.tsx +++ b/frontend/packages/metal3-plugin/src/components/host.tsx @@ -1,57 +1,78 @@ import * as React from 'react'; +import * as _ from 'lodash-es'; +import * as classNames from 'classnames'; +import { sortable } from '@patternfly/react-table'; -import { getName, getNamespace } from '@console/shared'; -// TODO(jtomasek): update import once models are moved to console-shared package -// import { MachineModel, NodeModel } from '@console/internal/models'; -import { referenceForModel } from '@console/internal/module/k8s'; +import { getName, getNamespace, getMachineNode } from '@console/shared'; +import { MachineModel, NodeModel } from '@console/internal/models'; +import { MultiListPage, Table, TableRow, TableData } from '@console/internal/components/factory'; +import { ResourceLink, Kebab } from '@console/internal/components/utils'; import { - ListHeader, - ColHead, - List, - ListPage, - ResourceRow, -} from '@console/internal/components/factory'; - -import { ResourceLink } from '@console/internal/components/utils'; -// import { WithResources } from '@console/shared'; + referenceForModel, + K8sResourceKind, + MachineKind, + NodeKind, +} from '@console/internal/module/k8s'; import { BaremetalHostModel } from '../models'; -import { getHostBMCAddress } from '../selectors'; +import { getHostBMCAddress, getHostMachine } from '../selectors'; +import { BaremetalHostRole } from './host-role'; import MachineCell from './machine-cell'; -// const nameColumnClasses = 'col-lg-2 col-md-4 col-sm-6 col-xs-6'; -// const statusColumnClasses = 'col-lg-2 col-md-4 hidden-sm hidden-xs'; -// const machineColumnClasses = 'col-lg-3 visible-lg'; -// const roleColumnClasses = 'col-lg-2 visible-lg'; -// const addressColumnClasses = 'col-lg-2 visible-lg'; -const columnClasses = 'col-sm-4'; - -const HostHeader = (props: React.ComponentProps) => ( - - - Name - - {/* - Status - */} - - Machine - - {/* - Role - */} - - Management Address - - -); +const tableColumnClasses = [ + classNames('col-lg-5', 'col-md-8', 'col-sm-12', 'col-xs-12'), + classNames('col-lg-2', 'col-md-4', 'col-sm-6', 'hidden-xs'), + classNames('col-lg-3', 'col-md-4', 'hidden-sm', 'hidden-xs'), + classNames('col-lg-2', 'hidden-md', 'hidden-sm', 'hidden-xs'), + classNames('col-lg-2', 'hidden-md', 'hidden-sm', 'hidden-xs'), + Kebab.columnClass, +]; + +const HostsTableHeader = () => [ + { + title: 'Name', + sortField: 'metadata.name', + transforms: [sortable], + props: { className: tableColumnClasses[0] }, + }, + { + title: 'Machine', + sortField: 'spec.machineRef.name', + transforms: [sortable], + props: { className: tableColumnClasses[2] }, + }, + { + title: 'Role', + sortField: 'machine.metadata.labels["machine.openshift.io/cluster-api-machine-role"]', + transforms: [sortable], + props: { className: tableColumnClasses[3] }, + }, + { + title: 'Management Address', + sortField: 'spec.machineRef.name', + transforms: [sortable], + props: { className: tableColumnClasses[4] }, + }, + // { + // title: '', + // props: { className: tableColumnClasses[5] }, + // }, +]; + +type HostsTableRowProps = { + obj: K8sResourceKind & { machine: MachineKind; node: NodeKind }; + index: number; + key?: string; + style: React.StyleHTMLAttributes; +}; -const HostRow = ({ obj: host }: React.ComponentProps) => { +const HostsTableRow: React.FC = ({ obj: host, index, key, style }) => { const name = getName(host); const namespace = getNamespace(host); // const machineName = getHostMachineName(host); const address = getHostBMCAddress(host); + const { machine } = host; // TODO(jtomasek): other resource references will be updated as a subsequent change // const machineResource = { @@ -73,34 +94,39 @@ const HostRow = ({ obj: host }: React.ComponentProps) => { // }; return ( - -
+ + -
- {/*
- - - -
*/} -
+ + {/* + + */} + -
- {/*
- - - -
*/} -
{address}
-
+ + + + + {address} + {/* + TODO(jtomasek): Add host actions here + */} + ); }; -const HostList = (props: React.ComponentProps) => ( - +const HostList: React.FC> = (props) => ( + ); // TODO(jtomasek): re-enable filters once the extension point for list.tsx is in place @@ -118,13 +144,55 @@ type BaremetalHostsPageProps = { namespace: string; }; -export const BaremetalHostsPage = (props: BaremetalHostsPageProps) => ( - -); +export const BaremetalHostsPage: React.FC = (props) => { + const hostsResource = { + kind: referenceForModel(BaremetalHostModel), + namespaced: true, + prop: 'hosts', + }; + const machinesResource = { + kind: referenceForModel(MachineModel), + namespaced: true, + prop: 'machines', + }; + const nodesResource = { + kind: NodeModel.kind, + namespaced: false, + prop: 'nodes', + }; + + const flatten = (resources) => { + // TODO(jtomasek): Remove loaded check once ListPageWrapper_ is updated to call flatten only + // when resources are loaded + const loaded = _.every(resources, (resource) => + resource.optional ? resource.loaded || !_.isEmpty(resource.loadError) : resource.loaded, + ); + const { + hosts: { data: hostsData }, + machines: { data: machinesData }, + nodes: { data: nodesData }, + } = resources; + + if (loaded) { + return hostsData.map((host) => { + const machine = getHostMachine(host, machinesData); + const node = getMachineNode(machine, nodesData); + return { ...host, machine, node }; + }); + } + return []; + }; + + return ( + + ); +}; diff --git a/frontend/packages/metal3-plugin/src/components/machine-cell.tsx b/frontend/packages/metal3-plugin/src/components/machine-cell.tsx index ee194f676a0..75cfd60883a 100644 --- a/frontend/packages/metal3-plugin/src/components/machine-cell.tsx +++ b/frontend/packages/metal3-plugin/src/components/machine-cell.tsx @@ -13,7 +13,7 @@ interface MachineCellProps { host: K8sResourceKind; } -const MachineCell = ({ host }: MachineCellProps) => { +const MachineCell: React.FC = ({ host }) => { const machineName = getHostMachineName(host); const { diff --git a/frontend/packages/metal3-plugin/src/models.ts b/frontend/packages/metal3-plugin/src/models.ts index e8204ad97ab..7437216dee9 100644 --- a/frontend/packages/metal3-plugin/src/models.ts +++ b/frontend/packages/metal3-plugin/src/models.ts @@ -5,7 +5,7 @@ export const BaremetalHostModel: K8sKind = { labelPlural: 'Bare Metal Hosts', apiVersion: 'v1alpha1', path: 'baremetalhosts', - apiGroup: 'metalkube.org', + apiGroup: 'metal3.io', plural: 'baremetalhosts', abbr: 'BMH', namespaced: true, diff --git a/frontend/packages/metal3-plugin/src/selectors/index.ts b/frontend/packages/metal3-plugin/src/selectors/index.ts index bdd78b9e73b..ac699b9f06c 100644 --- a/frontend/packages/metal3-plugin/src/selectors/index.ts +++ b/frontend/packages/metal3-plugin/src/selectors/index.ts @@ -1,5 +1,8 @@ import * as _ from 'lodash-es'; +import { K8sResourceKind, MachineKind } from '@console/internal/module/k8s'; +import { getName } from '@console/shared'; + export const getOperationalStatus = (host) => _.get(host, 'status.operationalStatus'); export const getProvisioningState = (host) => _.get(host, 'status.provisioning.state'); export const getHostMachineName = (host) => _.get(host, 'spec.machineRef.name'); @@ -14,3 +17,5 @@ export const getHostDescription = (host) => _.get(host, 'spec.description', ''); export const isHostPoweredOn = (host) => _.get(host, 'status.poweredOn', false); export const getHostTotalStorageCapacity = (host) => _.reduce(getHostStorage(host), (sum, disk) => sum + disk.sizeGiB, 0); +export const getHostMachine = (host: K8sResourceKind, machines: MachineKind[]) => + machines.find((machine) => getHostMachineName(host) === getName(machine)); diff --git a/frontend/public/components/machine-deployment.tsx b/frontend/public/components/machine-deployment.tsx index 4257c4571c8..0b6d3beb8a0 100644 --- a/frontend/public/components/machine-deployment.tsx +++ b/frontend/public/components/machine-deployment.tsx @@ -3,9 +3,9 @@ import * as _ from 'lodash-es'; import { Link } from 'react-router-dom'; import { sortable } from '@patternfly/react-table'; import * as classNames from 'classnames'; +import { getMachineRole } from '@console/shared'; import { MachineModel, MachineDeploymentModel } from '../models'; import { MachineDeploymentKind, referenceForModel } from '../module/k8s'; -import { getMachineRole } from './machine'; import { editCountAction, getAWSPlacement, diff --git a/frontend/public/components/machine-set.tsx b/frontend/public/components/machine-set.tsx index 40c09a56c03..fc2ee535f05 100644 --- a/frontend/public/components/machine-set.tsx +++ b/frontend/public/components/machine-set.tsx @@ -3,9 +3,10 @@ import * as _ from 'lodash-es'; import { Link } from 'react-router-dom'; import { sortable } from '@patternfly/react-table'; import * as classNames from 'classnames'; +import { getMachineRole } from '@console/shared'; import { MachineAutoscalerModel, MachineModel, MachineSetModel } from '../models'; import { K8sKind, MachineDeploymentKind, MachineSetKind, referenceForModel } from '../module/k8s'; -import { getMachineRole, MachinePage } from './machine'; +import { MachinePage } from './machine'; import { configureMachineAutoscalerModal, configureReplicaCountModal } from './modals'; import { DetailsPage, ListPage, Table, TableRow, TableData } from './factory'; import { diff --git a/frontend/public/components/machine.tsx b/frontend/public/components/machine.tsx index c27db16994c..7e978c9d72e 100644 --- a/frontend/public/components/machine.tsx +++ b/frontend/public/components/machine.tsx @@ -2,8 +2,9 @@ import * as React from 'react'; import * as _ from 'lodash-es'; import { sortable } from '@patternfly/react-table'; import * as classNames from 'classnames'; +import { getMachineRole, getMachineNodeName, getMachineAWSPlacement } from '@console/shared'; import { MachineModel } from '../models'; -import { MachineDeploymentKind, MachineKind, MachineSetKind, referenceForModel } from '../module/k8s'; +import { MachineKind, referenceForModel } from '../module/k8s'; import { Conditions } from './conditions'; import { NodeIPList } from './node'; import { DetailsPage, ListPage, Table, TableRow, TableData } from './factory'; @@ -21,11 +22,6 @@ import { breadcrumbsForOwnerRefs } from './utils/breadcrumbs'; const { common } = Kebab.factory; const menuActions = [...common]; export const machineReference = referenceForModel(MachineModel); -const getAWSPlacement = (machine: MachineKind) => _.get(machine, 'spec.providerSpec.value.placement') || {}; - -export const getMachineRole = (obj: MachineKind | MachineSetKind | MachineDeploymentKind) => _.get(obj, ['metadata', 'labels', 'sigs.k8s.io/cluster-api-machine-role']); - -const getNodeName = (obj) => _.get(obj, 'status.nodeRef.name'); const tableColumnClasses = [ classNames('col-lg-3', 'col-md-4', 'col-sm-4', 'col-xs-6'), @@ -66,8 +62,8 @@ const MachineTableHeader = () => { MachineTableHeader.displayName = 'MachineTableHeader'; const MachineTableRow: React.FC = ({obj, index, key, style}) => { - const { availabilityZone, region } = getAWSPlacement(obj); - const nodeName = getNodeName(obj); + const { availabilityZone, region } = getMachineAWSPlacement(obj); + const nodeName = getMachineNodeName(obj); return ( @@ -100,9 +96,9 @@ type MachineTableRowProps = { }; const MachineDetails: React.SFC = ({obj}: {obj: MachineKind}) => { - const nodeName = getNodeName(obj); + const nodeName = getMachineNodeName(obj); const machineRole = getMachineRole(obj); - const { availabilityZone, region } = getAWSPlacement(obj); + const { availabilityZone, region } = getMachineAWSPlacement(obj); return