Skip to content

Commit

Permalink
Merge pull request #3811 from suomiy/metal.deprovisionAction
Browse files Browse the repository at this point in the history
add Deprovision action to Bare Metal Hosts
  • Loading branch information
openshift-merge-robot committed Jan 22, 2020
2 parents b39817b + 79c5503 commit c728d85
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import { referenceForModel } from '@console/internal/module/k8s';
import { DetailsPage } from '@console/internal/components/factory';
import { navFactory, FirehoseResource } from '@console/internal/components/utils';
import { MachineModel, NodeModel } from '@console/internal/models';
import { MachineModel, MachineSetModel, NodeModel } from '@console/internal/models';
import { ResourceEventStream } from '@console/internal/components/events';
import { BareMetalHostModel, NodeMaintenanceModel } from '../../models';
import BareMetalHostDashboard from './dashboard/BareMetalHostDashboard';
Expand All @@ -30,6 +30,12 @@ const BareMetalHostDetailsPage: React.FC<BareMetalHostDetailsPageProps> = ({
isList: true,
prop: 'machines',
},
{
kind: referenceForModel(MachineSetModel),
namespaced: true,
isList: true,
prop: 'machineSets',
},
{
kind: NodeModel.kind,
namespaced: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,33 @@ import * as React from 'react';
import * as _ from 'lodash';
import { connect } from 'react-redux';
import { getName, createLookup, getNodeMachineName } from '@console/shared';
import { MachineModel, NodeModel } from '@console/internal/models';
import { MachineModel, MachineSetModel, NodeModel } from '@console/internal/models';
import { MultiListPage } from '@console/internal/components/factory';
import { FirehoseResource } from '@console/internal/components/utils';
import { referenceForModel } from '@console/internal/module/k8s';
import { FirehoseResource, FirehoseResult } from '@console/internal/components/utils';
import {
MachineKind,
MachineSetKind,
NodeKind,
referenceForModel,
} from '@console/internal/module/k8s';
import { BareMetalHostModel, NodeMaintenanceModel } from '../../models';
import { getHostMachine, getNodeMaintenanceNodeName } from '../../selectors';
import { getHostStatus } from '../../status/host-status';
import { BareMetalHostBundle } from '../types';
import { BareMetalHostKind } from '../../types';
import { getMachineMachineSetOwner } from '../../selectors/machine';
import { hostStatusFilter } from './table-filters';
import BareMetalHostsTable from './BareMetalHostsTable';

const flattenResources = (resources) => {
type Resources = {
hosts: FirehoseResult<BareMetalHostKind[]>;
machines: FirehoseResult<MachineKind[]>;
machineSets: FirehoseResult<MachineSetKind[]>;
nodes: FirehoseResult<NodeKind[]>;
nodeMaintenances: FirehoseResult;
};

const flattenResources = (resources: Resources) => {
// TODO(jtomasek): Remove loaded check once ListPageWrapper_ is updated to call flatten only
// when resources are loaded
const loaded = _.every(resources, (resource) =>
Expand All @@ -22,13 +37,15 @@ const flattenResources = (resources) => {
const {
hosts: { data: hostsData },
machines: { data: machinesData },
machineSets,
nodes,
nodeMaintenances,
} = resources;

if (loaded) {
const maintenancesByNodeName = createLookup(nodeMaintenances, getNodeMaintenanceNodeName);
const nodesByMachineName = createLookup(nodes, getNodeMachineName);
const machineSetByUID = createLookup(machineSets);

return hostsData.map(
(host): BareMetalHostBundle => {
Expand All @@ -37,10 +54,21 @@ const flattenResources = (resources) => {
const machine = getHostMachine(host, machinesData);
const node = nodesByMachineName[getName(machine)];
const nodeMaintenance = maintenancesByNodeName[getName(node)];
const machineOwner = getMachineMachineSetOwner(machine);
const machineSet = machineOwner && machineSetByUID[machineOwner.uid];

const status = getHostStatus({ host, machine, node, nodeMaintenance });
// TODO(jtomasek): metadata.name is needed to make 'name' textFilter work.
// Remove it when it is possible to pass custom textFilter as a function
return { metadata: { name: getName(host) }, host, machine, node, nodeMaintenance, status };
return {
metadata: { name: getName(host) },
host,
machine,
node,
nodeMaintenance,
machineSet,
status,
};
},
);
}
Expand Down Expand Up @@ -90,6 +118,12 @@ const BareMetalHostsPage: React.FC<BareMetalHostsPageProps> = ({
namespaced: true,
prop: 'machines',
},
{
kind: referenceForModel(MachineSetModel),
namespaced: true,
isList: true,
prop: 'machineSets',
},
{
kind: NodeModel.kind,
namespaced: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ type HostsTableRowProps = {
};

const HostsTableRow: React.FC<HostsTableRowProps> = ({
obj: { host, node, nodeMaintenance, machine, status },
obj: { host, node, nodeMaintenance, machine, machineSet, status },
customData: { hasNodeMaintenanceCapability },
index,
key,
Expand Down Expand Up @@ -115,10 +115,12 @@ const HostsTableRow: React.FC<HostsTableRowProps> = ({
<TableData className={tableColumnClasses.kebab}>
<Kebab
options={menuActions.map((action) =>
action(BareMetalHostModel, host, null, {
action(BareMetalHostModel, host, {
nodeMaintenance,
nodeName,
hasNodeMaintenanceCapability,
machine,
machineSet,
status: status.status,
}),
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
import { asAccessReview, KebabOption, Kebab } from '@console/internal/components/utils';
import { asAccessReview, Kebab, KebabOption } from '@console/internal/components/utils';
import {
K8sKind,
K8sResourceKind,
k8sPatch,
K8sResourceKind,
MachineKind,
MachineSetKind,
NodeKind,
} from '@console/internal/module/k8s';
import { getMachineNode, getMachineNodeName } from '@console/shared';
import { deleteModal } from '@console/internal/components/modals';
import { getMachineNode, getMachineNodeName, getName, getAnnotations } from '@console/shared';
import { confirmModal, deleteModal } from '@console/internal/components/modals';
import { MachineModel, MachineSetModel } from '@console/internal/models';
import { findNodeMaintenance, getHostMachine, getHostPowerStatus } from '../../selectors';
import { BareMetalHostModel, NodeMaintenanceModel } from '../../models';
import { getHostStatus } from '../../status/host-status';
import {
HOST_POWER_STATUS_POWERING_OFF,
HOST_POWER_STATUS_POWERED_OFF,
HOST_POWER_STATUS_POWERED_ON,
HOST_POWER_STATUS_POWERING_OFF,
HOST_POWER_STATUS_POWERING_ON,
HOST_POWER_STATUS_POWERED_OFF,
HOST_STATUS_AVAILABLE,
HOST_STATUS_DISCOVERED,
HOST_STATUS_ERROR,
HOST_STATUS_READY,
HOST_STATUS_REGISTRATION_ERROR,
HOST_STATUS_ERROR,
HOST_STATUS_DISCOVERED,
HOST_STATUS_AVAILABLE,
} from '../../constants';
import { startNodeMaintenanceModal } from '../modals/StartNodeMaintenanceModal';
import { powerOffHostModal } from '../modals/PowerOffHostModal';
import stopNodeMaintenanceModal from '../modals/StopNodeMaintenanceModal';
import { BareMetalHostKind } from '../../types';
import { DELETE_MACHINE_ANNOTATION } from '../../constants/machine';
import { deprovision } from '../../k8s/requests/bare-metal-host';
import { getMachineMachineSetOwner } from '../../selectors/machine';
import { findMachineSet } from '../../selectors/machine-set';

type ActionArgs = {
machine?: MachineKind;
machineSet?: MachineSetKind;
nodeName?: string;
nodeMaintenance?: K8sResourceKind;
hasNodeMaintenanceCapability?: boolean;
Expand All @@ -37,7 +45,6 @@ type ActionArgs = {
export const SetNodeMaintenance = (
kindObj: K8sKind,
host: BareMetalHostKind,
resources: {},
{ hasNodeMaintenanceCapability, nodeMaintenance, nodeName }: ActionArgs,
): KebabOption => ({
hidden: !nodeName || !hasNodeMaintenanceCapability || !!nodeMaintenance,
Expand All @@ -48,7 +55,6 @@ export const SetNodeMaintenance = (
export const RemoveNodeMaintenance = (
kindObj: K8sKind,
host: BareMetalHostKind,
resources: {},
{ hasNodeMaintenanceCapability, nodeMaintenance, nodeName }: ActionArgs,
): KebabOption => ({
hidden: !nodeName || !hasNodeMaintenanceCapability || !nodeMaintenance,
Expand All @@ -71,10 +77,36 @@ export const PowerOn = (kindObj: K8sKind, host: BareMetalHostKind): KebabOption
};
};

export const Deprovision = (
kindObj: K8sKind,
host: BareMetalHostKind,
{ machine, machineSet }: ActionArgs,
): KebabOption => {
const title = 'Deprovision';
return {
hidden:
!machine ||
!!getAnnotations(machine, {})[DELETE_MACHINE_ANNOTATION] ||
(getMachineMachineSetOwner(machine) && !machineSet),
label: title,
callback: () =>
confirmModal({
title: `${title} ${getName(host)}`,
message: `Are you sure you want to delete ${getName(machine)} machine${
machineSet ? ' and scale down its machine set?' : '?'
}`,
btnText: title,
executeFn: () => deprovision(machine, machineSet),
}),
accessReview: machineSet
? asAccessReview(MachineSetModel, machineSet, 'update')
: asAccessReview(MachineModel, machine, 'delete'),
};
};

export const PowerOff = (
kindObj: K8sKind,
host: BareMetalHostKind,
resources: null,
{ hasNodeMaintenanceCapability, nodeName, status }: ActionArgs,
) => ({
hidden: [HOST_POWER_STATUS_POWERED_OFF, HOST_POWER_STATUS_POWERING_OFF].includes(
Expand All @@ -88,7 +120,6 @@ export const PowerOff = (
export const Delete = (
kindObj: K8sKind,
host: BareMetalHostKind,
resources: null,
{ status }: ActionArgs,
): KebabOption => {
const title = 'Delete Bare Metal Host';
Expand All @@ -114,6 +145,7 @@ export const menuActions = [
SetNodeMaintenance,
RemoveNodeMaintenance,
PowerOn,
Deprovision,
PowerOff,
Kebab.factory.ModifyLabels,
Kebab.factory.ModifyAnnotations,
Expand All @@ -123,14 +155,15 @@ export const menuActions = [

type ExtraResources = {
machines: MachineKind[];
machineSets: MachineSetKind[];
nodes: NodeKind[];
nodeMaintenances: K8sResourceKind[];
};

export const menuActionsCreator = (
kindObj: K8sKind,
host: BareMetalHostKind,
{ machines, nodes, nodeMaintenances }: ExtraResources,
{ machines, machineSets, nodes, nodeMaintenances }: ExtraResources,
{ hasNodeMaintenanceCapability },
) => {
const machine = getHostMachine(host, machines);
Expand All @@ -139,11 +172,16 @@ export const menuActionsCreator = (
const nodeMaintenance = findNodeMaintenance(nodeMaintenances, nodeName);
const status = getHostStatus({ host, machine, node, nodeMaintenance });

const machineOwner = getMachineMachineSetOwner(machine);
const machineSet = findMachineSet(machineSets, machineOwner && machineOwner.uid);

return menuActions.map((action) => {
return action(kindObj, host, null, {
return action(kindObj, host, {
hasNodeMaintenanceCapability,
nodeMaintenance,
nodeName,
machine,
machineSet,
status: status.status,
});
});
Expand Down
10 changes: 8 additions & 2 deletions frontend/packages/metal3-plugin/src/components/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { K8sResourceKind, MachineKind, NodeKind } from '@console/internal/module/k8s';
import {
K8sResourceKind,
MachineKind,
MachineSetKind,
NodeKind,
} from '@console/internal/module/k8s';
import { BareMetalHostKind } from '../types';

export type StatusProps = {
Expand All @@ -10,7 +15,8 @@ export type StatusProps = {

export type BareMetalHostBundle = {
metadata?: { name: string };
machine: MachineKind;
machine?: MachineKind;
machineSet?: MachineSetKind;
node: NodeKind;
host: BareMetalHostKind;
nodeMaintenance: K8sResourceKind;
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/metal3-plugin/src/constants/machine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DELETE_MACHINE_ANNOTATION = 'machine.openshift.io/cluster-api-delete-machine';
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { k8sPatch, k8sCreate } from '@console/internal/module/k8s';
import { SecretModel } from '@console/internal/models';
import {
k8sPatch,
k8sCreate,
MachineKind,
MachineSetKind,
k8sKill,
} from '@console/internal/module/k8s';
import { MachineModel, MachineSetModel, SecretModel } from '@console/internal/models';
import { PatchBuilder, PatchOperation } from '@console/shared/src/k8s';
import { getAnnotations } from '@console/shared/src';
import { BareMetalHostModel } from '../../models';
import { BareMetalHostKind } from '../../types';
import { DELETE_MACHINE_ANNOTATION } from '../../constants/machine';
import { getReplicas } from '../../selectors/machine-set';

export const powerOffHost = (host: BareMetalHostKind) =>
k8sPatch(BareMetalHostModel, host, [{ op: 'replace', path: '/spec/online', value: false }]);
Expand All @@ -13,3 +23,34 @@ export const createBareMetalHost = async (bareMetalHost, secret) => [
await k8sCreate(SecretModel, secret),
await k8sCreate(BareMetalHostModel, bareMetalHost),
];

export const deprovision = async (machine: MachineKind, machineSet?: MachineSetKind) => {
await k8sPatch(MachineModel, machine, [
new PatchBuilder('/metadata/annotations')
.setObjectUpdate(DELETE_MACHINE_ANNOTATION, 'true', getAnnotations(machine))
.build(),
]);

if (machineSet) {
const replicas = getReplicas(machineSet);

if (replicas > 0) {
try {
await k8sPatch(MachineSetModel, machineSet, [
new PatchBuilder('/spec/replicas')
.setOperation(PatchOperation.REPLACE)
.setValue(replicas - 1)
.build(),
]);
} catch (ignored) {
await k8sPatch(MachineModel, machine, [
new PatchBuilder('/metadata/annotations')
.setObjectUpdate(DELETE_MACHINE_ANNOTATION, 'false', getAnnotations(machine))
.build(),
]);
}
}
} else {
await k8sKill(MachineModel, machine);
}
};
9 changes: 9 additions & 0 deletions frontend/packages/metal3-plugin/src/selectors/machine-set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as _ from 'lodash';
import { MachineSetKind } from '@console/internal/module/k8s';
import { getUID } from '@console/shared/src';

export const findMachineSet = (machineSets: MachineSetKind[], uid: string) =>
uid && machineSets ? machineSets.find((machineSet) => getUID(machineSet) === uid) : null;

export const getReplicas = (machineSet: MachineSetKind, defaultValue: number = 0) =>
_.has(machineSet, 'spec') ? machineSet.spec.replicas : defaultValue;
14 changes: 14 additions & 0 deletions frontend/packages/metal3-plugin/src/selectors/machine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { apiVersionForModel, MachineKind } from '@console/internal/module/k8s';
import { MachineSetModel } from '@console/internal/models';
import { getOwnerReferences } from '@console/shared/src';
import { compareOwnerReference } from '@console/shared/src/utils/owner-references';

export const getMachineMachineSetOwner = (machine: MachineKind) => {
const desiredReference = {
apiVersion: apiVersionForModel(MachineSetModel),
kind: MachineSetModel.kind,
} as any;
return (getOwnerReferences(machine) || []).find((reference) =>
compareOwnerReference(desiredReference, reference, true),
);
};

0 comments on commit c728d85

Please sign in to comment.