Skip to content

Commit

Permalink
Merge pull request #6150 from andrewballantyne/remaining-hpa
Browse files Browse the repository at this point in the history
HPA In-Context (Delete | Edit | Topology Sidebar)
  • Loading branch information
openshift-merge-robot committed Jul 31, 2020
2 parents 753c866 + e530edf commit dc3a26e
Show file tree
Hide file tree
Showing 27 changed files with 508 additions and 146 deletions.
78 changes: 73 additions & 5 deletions frontend/packages/console-app/src/actions/modify-hpa.ts
@@ -1,16 +1,84 @@
import { history, KebabAction } from '@console/internal/components/utils';
import { K8sKind, K8sResourceCommon } from '@console/internal/module/k8s';
import { KebabAction } from '@console/internal/components/utils';
import {
HorizontalPodAutoscalerKind,
K8sKind,
K8sResourceCommon,
referenceForModel,
} from '@console/internal/module/k8s';
import { HorizontalPodAutoscalerModel } from '@console/internal/models';
import deleteHPAModal from '@console/dev-console/src/components/hpa/DeleteHPAModal';

export const AddHorizontalPodAutoScaler: KebabAction = (kind: K8sKind, obj: K8sResourceCommon) => ({
type RelatedResources = {
hpas?: HorizontalPodAutoscalerKind[];
};

const hasHPAs = (mapOfResources: RelatedResources) =>
Array.isArray(mapOfResources?.hpas) && mapOfResources.hpas.length > 0;

const hpaRoute = ({ metadata: { name, namespace } }: K8sResourceCommon, kind: K8sKind) =>
`/workload-hpa/ns/${namespace}/${referenceForModel(kind)}/${name}`;

export const AddHorizontalPodAutoScaler: KebabAction = (
kind: K8sKind,
obj: K8sResourceCommon,
resources: RelatedResources,
) => ({
label: `Add ${HorizontalPodAutoscalerModel.label}`,
href: hpaRoute(obj, kind),
hidden: hasHPAs(resources),
accessReview: {
group: HorizontalPodAutoscalerModel.apiGroup,
resource: HorizontalPodAutoscalerModel.plural,
namespace: obj.metadata.namespace,
verb: 'create',
},
});

export const EditHorizontalPodAutoScaler: KebabAction = (
kind: K8sKind,
obj: K8sResourceCommon,
resources: RelatedResources,
) => ({
label: `Edit ${HorizontalPodAutoscalerModel.label}`,
href: hpaRoute(obj, kind),
hidden: !hasHPAs(resources),
accessReview: {
group: HorizontalPodAutoscalerModel.apiGroup,
resource: HorizontalPodAutoscalerModel.plural,
namespace: obj.metadata.namespace,
verb: 'update',
},
});

export const DeleteHorizontalPodAutoScaler: KebabAction = (
kind: K8sKind,
obj: K8sResourceCommon,
resources: RelatedResources,
) => ({
label: `Remove ${HorizontalPodAutoscalerModel.label}`,
callback: () => {
history.push(`/workload-hpa/ns/${obj.metadata.namespace}/${kind.kind}/${obj.metadata.name}`);
deleteHPAModal({
workload: obj,
hpa: resources?.hpas?.[0],
});
},
hidden: !hasHPAs(resources),
accessReview: {
group: HorizontalPodAutoscalerModel.apiGroup,
resource: HorizontalPodAutoscalerModel.plural,
namespace: obj.metadata.namespace,
verb: 'create',
verb: 'delete',
},
});

export const hideActionForHPAs = (action: KebabAction): KebabAction => (
kind: K8sKind,
obj: K8sResourceCommon,
resources: RelatedResources,
) => {
const actionOptions = action(kind, obj);
return {
...actionOptions,
hidden: hasHPAs(resources) || actionOptions.hidden,
};
};
20 changes: 17 additions & 3 deletions frontend/packages/console-shared/src/components/pod/PodRing.tsx
Expand Up @@ -3,7 +3,8 @@ import * as _ from 'lodash';
import { Button, Split, SplitItem, Bullseye } from '@patternfly/react-core';
import { K8sResourceKind, k8sPatch, K8sKind } from '@console/internal/module/k8s';
import { AngleUpIcon, AngleDownIcon } from '@patternfly/react-icons';
import { podRingLabel, usePodScalingAccessStatus } from '../../utils';
import { useRelatedHPA } from '@console/dev-console/src/components/hpa/hooks';
import { hpaPodRingLabel, podRingLabel, usePodScalingAccessStatus } from '../../utils';
import { ExtPodKind } from '../../types';
import PodStatus from './PodStatus';
import './PodRing.scss';
Expand All @@ -28,7 +29,7 @@ const PodRing: React.FC<PodRingProps> = ({
enableScaling = true,
}) => {
const [clickCount, setClickCount] = React.useState(obj.spec.replicas);
const isScalingAllowed = usePodScalingAccessStatus(
const isAccessScalingAllowed = usePodScalingAccessStatus(
obj,
resourceKind,
pods,
Expand Down Expand Up @@ -67,8 +68,21 @@ const PodRing: React.FC<PodRingProps> = ({
setClickCount(clickCount + operation);
handleScaling(clickCount + operation);
};

const {
apiVersion,
kind,
metadata: { name, namespace },
} = obj;
const [hpa] = useRelatedHPA(apiVersion, kind, name, namespace);
const hpaControlledScaling = !!hpa;

const isScalingAllowed = isAccessScalingAllowed && !hpaControlledScaling;

const resourceObj = rc || obj;
const { title, subTitle, titleComponent } = podRingLabel(resourceObj, obj.kind, pods);
const { title, subTitle, titleComponent } = hpaControlledScaling
? hpaPodRingLabel(resourceObj, hpa, pods)
: podRingLabel(resourceObj, kind, pods);

return (
<Split>
Expand Down
9 changes: 8 additions & 1 deletion frontend/packages/console-shared/src/types/resource.ts
@@ -1,4 +1,10 @@
import { JobKind, K8sResourceKind, PodKind, RouteKind } from '@console/internal/module/k8s';
import {
HorizontalPodAutoscalerKind,
JobKind,
K8sResourceKind,
PodKind,
RouteKind,
} from '@console/internal/module/k8s';
import { DEPLOYMENT_STRATEGY } from '../constants';
import { OverviewItemAlerts, PodControllerOverviewItem } from './pod';
import { ClusterServiceVersionKind } from '@console/operator-lifecycle-manager';
Expand All @@ -25,6 +31,7 @@ export type OverviewItem<T = K8sResourceKind> = {
current?: PodControllerOverviewItem;
isRollingOut?: boolean;
obj: T;
hpas?: HorizontalPodAutoscalerKind[];
pods?: PodKind[];
previous?: PodControllerOverviewItem;
routes: RouteKind[];
Expand Down
18 changes: 18 additions & 0 deletions frontend/packages/console-shared/src/utils/pod-ring-utils.ts
Expand Up @@ -16,6 +16,7 @@ import {
K8sResourceKind,
K8sKind,
SelfSubjectAccessReviewKind,
HorizontalPodAutoscalerKind,
} from '@console/internal/module/k8s';
import { useSafetyFirst } from '@console/internal/components/safety-first';
import { PodRCData, PodRingResources, PodRingData, ExtPodKind } from '../types';
Expand Down Expand Up @@ -206,6 +207,23 @@ export const podRingLabel = (
}
};

export const hpaPodRingLabel = (
obj: K8sResourceKind,
hpa: HorizontalPodAutoscalerKind,
pods: ExtPodKind[],
): PodRingLabelType => {
const desiredPodCount = obj.spec?.replicas;
const desiredPods = hpa.status?.desiredReplicas || desiredPodCount;
const currentPods = hpa.status?.currentReplicas;
const scaling =
(!currentPods && !!desiredPods) || !pods.every((p) => p.status?.phase === 'Running');
return {
title: scaling ? 'Autoscaling' : 'Autoscaled',
subTitle: `to ${desiredPods}`,
titleComponent: getTitleComponent(false, true),
};
};

export const usePodScalingAccessStatus = (
obj: K8sResourceKind,
resourceKind: K8sKind,
Expand Down
5 changes: 5 additions & 0 deletions frontend/packages/console-shared/src/utils/resource-utils.ts
Expand Up @@ -54,6 +54,7 @@ import {
} from '../constants';
import { resourceStatus, podStatus } from './ResourceStatus';
import { isKnativeServing, isIdled } from './pod-utils';
import { doesHpaMatch } from '@console/dev-console/src/components/hpa/hpa-utils';

export const getResourceList = (namespace: string, resList?: any): FirehoseResource[] => {
let resources: FirehoseResource[] = [
Expand Down Expand Up @@ -855,10 +856,12 @@ export const createDeploymentConfigItem = (
deploymentConfig,
resources?.monitoringAlerts,
);
const hpas = resources?.hpas?.data?.filter(doesHpaMatch(deploymentConfig));
const overviewItems = {
alerts,
buildConfigs,
current,
hpas,
isRollingOut,
obj: deploymentConfig,
previous,
Expand Down Expand Up @@ -907,11 +910,13 @@ export const createDeploymentItem = (
const status = resourceStatus(deployment, current, isRollingOut);
const pods = [..._.get(current, 'pods', []), ..._.get(previous, 'pods', [])];
const monitoringAlerts = getWorkloadMonitoringAlerts(deployment, resources?.monitoringAlerts);
const hpas = resources?.hpas?.data?.filter(doesHpaMatch(deployment));
const overviewItem = {
obj: deployment,
alerts,
buildConfigs,
current,
hpas,
isRollingOut,
previous,
pods,
Expand Down
@@ -0,0 +1,82 @@
import * as React from 'react';
import { Form } from '@patternfly/react-core';
import { ExclamationTriangleIcon } from '@patternfly/react-icons';
import { global_warning_color_100 as warningColor } from '@patternfly/react-tokens';
import {
createModalLauncher,
ModalBody,
ModalComponentProps,
ModalSubmitFooter,
ModalTitle,
} from '@console/internal/components/factory/modal';
import { HorizontalPodAutoscalerModel } from '@console/internal/models';
import { LoadingInline } from '@console/internal/components/utils';
import {
HorizontalPodAutoscalerKind,
k8sKill,
K8sResourceCommon,
} from '@console/internal/module/k8s';

type DeleteHPAModalProps = ModalComponentProps & {
hpa: HorizontalPodAutoscalerKind;
workload: K8sResourceCommon;
};

const DeleteHPAModal: React.FC<DeleteHPAModalProps> = ({ close, hpa, workload }) => {
const [submitError, setSubmitError] = React.useState<string>(null);
const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
const hpaName = hpa.metadata.name;
const workloadName = workload.metadata.name;

const handleSubmit = (e) => {
e.preventDefault();
setIsSubmitting(true);
k8sKill(HorizontalPodAutoscalerModel, hpa)
.then(() => {
close();
})
.catch((error) => {
setSubmitError(
error?.message ||
`Unknown error removing ${HorizontalPodAutoscalerModel.label} ${hpaName}.`,
);
});
};

return (
<Form onSubmit={handleSubmit}>
<div className="modal-content">
<ModalTitle>
<ExclamationTriangleIcon color={warningColor.value} /> Remove{' '}
{HorizontalPodAutoscalerModel.label}?
</ModalTitle>
<ModalBody>
{hpaName ? (
<>
<p>
Are you sure you want to remove the {HorizontalPodAutoscalerModel.label}{' '}
<b>{hpaName}</b> from <b>{workloadName}</b>?
</p>
<p>
The resources that are attached to the {HorizontalPodAutoscalerModel.label} will be
deleted.
</p>
</>
) : (
!submitError && <LoadingInline />
)}
</ModalBody>
<ModalSubmitFooter
errorMessage={submitError}
inProgress={isSubmitting}
submitText="Remove"
submitDanger
submitDisabled={!!submitError}
cancel={close}
/>
</div>
</Form>
);
};

export default createModalLauncher(DeleteHPAModal);
Expand Up @@ -13,7 +13,7 @@ const HPADetailsForm: React.FC = () => {
const {
setFieldValue,
values: {
disabledFields: { cpuUtilization, memoryUtilization },
disabledFields: { name: nameDisabled, cpuUtilization, memoryUtilization },
showCanUseYAMLMessage,
},
} = useFormikContext<HPAFormValues>();
Expand Down Expand Up @@ -63,7 +63,7 @@ const HPADetailsForm: React.FC = () => {
<div className="row">
<div className="col-lg-8">
<Flex direction={{ default: 'column' }}>
<InputField label="Name" name={`${name}.metadata.name`} />
<InputField isDisabled={nameDisabled} label="Name" name={`${name}.metadata.name`} />
<NumberSpinnerField label="Minimum Pods" name={`${name}.spec.minReplicas`} />
<NumberSpinnerField label="Maximum Pods" name={`${name}.spec.maxReplicas`} />
<HPAUtilizationField
Expand Down
11 changes: 9 additions & 2 deletions frontend/packages/dev-console/src/components/hpa/HPAForm.tsx
Expand Up @@ -7,6 +7,7 @@ import { HorizontalPodAutoscalerKind, K8sResourceCommon } from '@console/interna
import HPADetailsForm from './HPADetailsForm';
import { sanitizeHPAToForm } from './hpa-utils';
import { HPAFormValues } from './types';
import { HorizontalPodAutoscalerModel } from '@console/internal/models';

type HPAFormProps = {
targetResource: K8sResourceCommon;
Expand All @@ -25,7 +26,13 @@ const HPAForm: React.FC<FormikProps<HPAFormValues> & HPAFormProps> = ({
}) => {
const isForm = values.editorType === EditorType.Form;
const formEditor = <HPADetailsForm />;
const yamlEditor = <YAMLEditorField name="yamlData" onSave={handleSubmit} />;
const yamlEditor = (
<YAMLEditorField
name="yamlData"
onSave={handleSubmit}
schemaModel={HorizontalPodAutoscalerModel}
/>
);
const customMetrics = false;

React.useEffect(() => {
Expand Down Expand Up @@ -55,7 +62,7 @@ const HPAForm: React.FC<FormikProps<HPAFormValues> & HPAFormProps> = ({
errorMessage={status?.submitError}
isSubmitting={isSubmitting}
submitLabel="Save"
disableSubmit={isForm && (!isEmpty(errors) || status?.submitError)}
disableSubmit={isForm && !isEmpty(errors)}
resetLabel="Cancel"
sticky
/>
Expand Down

0 comments on commit dc3a26e

Please sign in to comment.