Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ui): Move pod name functions and add tests. Fixes #6946 #6954

Merged
merged 3 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {ResourceEditor} from '../../../shared/components/resource-editor/resourc
import {services} from '../../../shared/services';
import {WorkflowArtifacts} from '../../../workflows/components/workflow-artifacts';

import {Utils} from '../../../shared/utils';
import {getPodName} from '../../../shared/pod-name';
import {WorkflowResourcePanel} from '../../../workflows/components/workflow-details/workflow-resource-panel';
import {WorkflowLogsViewer} from '../../../workflows/components/workflow-logs-viewer/workflow-logs-viewer';
import {WorkflowNodeInfo} from '../../../workflows/components/workflow-node-info/workflow-node-info';
Expand Down Expand Up @@ -253,7 +253,7 @@ export class ArchivedWorkflowDetails extends BasePage<RouteComponentProps<any>,
if (this.nodeId && this.state.workflow) {
const workflowName = this.state.workflow.metadata.name;
const {name, templateName} = this.node;
return Utils.getPodName(workflowName, name, templateName, this.nodeId);
return getPodName(workflowName, name, templateName, this.nodeId);
}
}

Expand Down
35 changes: 35 additions & 0 deletions ui/src/app/shared/pod-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {createFNVHash, ensurePodNamePrefixLength, getPodName, k8sNamingHashLength, maxK8sResourceNameLength} from './pod-name';

describe('pod names', () => {
test('createFNVHash', () => {
expect(createFNVHash('hello')).toEqual(1335831723);
expect(createFNVHash('world')).toEqual(933488787);
expect(createFNVHash('You cannot alter your fate. However, you can rise to meet it.')).toEqual(827171719);
});

const nodeName = 'nodename';
const nodeID = '1';

const shortWfName = 'wfname';
const shortTemplateName = 'templatename';

const longWfName = 'alongworkflownamethatincludeslotsofdetailsandisessentiallyalargerunonsentencewithpoorstyleandnopunctuationtobehadwhatsoever';
const longTemplateName =
'alongtemplatenamethatincludessliightlymoredetailsandiscertainlyalargerunonstnencewithevenworsestylisticconcernsandpreposterouslyeliminatespunctuation';

test('ensurePodNamePrefixLength', () => {
let expected = `${shortWfName}-${shortTemplateName}`;
expect(ensurePodNamePrefixLength(expected)).toEqual(expected);

expected = `${longWfName}-${longTemplateName}`;
const actual = ensurePodNamePrefixLength(expected);
expect(actual.length).toEqual(maxK8sResourceNameLength - k8sNamingHashLength - 1);
});

test('getPodName', () => {
expect(getPodName(shortWfName, nodeName, shortTemplateName, nodeID)).toEqual('wfname-templatename-1454367246');

const name = getPodName(longWfName, nodeName, longTemplateName, nodeID);
expect(name.length).toEqual(maxK8sResourceNameLength);
});
});
39 changes: 39 additions & 0 deletions ui/src/app/shared/pod-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const maxK8sResourceNameLength = 253;
export const k8sNamingHashLength = 10;

// getPodName returns a deterministic pod name
export const getPodName = (workflowName: string, nodeName: string, templateName: string, nodeID: string): string => {
if (workflowName === nodeName) {
return workflowName;
}

let prefix = `${workflowName}-${templateName}`;
prefix = ensurePodNamePrefixLength(prefix);

const hash = createFNVHash(nodeName);
return `${prefix}-${hash}`;
};

export const ensurePodNamePrefixLength = (prefix: string): string => {
const maxPrefixLength = maxK8sResourceNameLength - k8sNamingHashLength;

if (prefix.length > maxPrefixLength - 1) {
return prefix.substring(0, maxPrefixLength - 1);
}

return prefix;
};

export const createFNVHash = (input: string): number => {
const data = Buffer.from(input);

let hashint = 2166136261;

/* tslint:disable:no-bitwise */
for (const character of data) {
hashint = hashint ^ character;
hashint += (hashint << 1) + (hashint << 4) + (hashint << 7) + (hashint << 8) + (hashint << 24);
}

return hashint >>> 0;
};
39 changes: 0 additions & 39 deletions ui/src/app/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {NODE_PHASE} from '../../models';

const managedNamespaceKey = 'managedNamespace';
const currentNamespaceKey = 'current_namespace';
const maxK8sResourceNameLength = 253;
const k8sNamingHashLength = 10;

export const Utils = {
statusIconClasses(status: string): string {
Expand Down Expand Up @@ -120,42 +118,5 @@ export const Utils = {
// return a namespace, never return null/undefined, defaults to "default"
getNamespaceWithDefault(namespace: string) {
return this.managedNamespace || namespace || this.currentNamespace || 'default';
},

ensurePodNamePrefixLength(prefix: string): string {
const maxPrefixLength = maxK8sResourceNameLength - k8sNamingHashLength;

if (prefix.length > maxPrefixLength - 1) {
return prefix.substring(0, maxPrefixLength - 1);
}

return prefix;
},

// getPodName returns a deterministic pod name
getPodName(workflowName: string, nodeName: string, templateName: string, nodeID: string): string {
if (workflowName === nodeName) {
return workflowName;
}

let prefix = `${workflowName}-${templateName}`;
prefix = this.ensurePodNamePrefixLength(prefix);

const hash = createFNVHash(nodeName);
return `${prefix}-${hash}`;
}
};

const createFNVHash = (input: string): number => {
const data = new Buffer(input);

let hashint = 2166136261;

/* tslint:disable:no-bitwise */
for (const character of data) {
hashint = hashint ^ character;
hashint += (hashint << 1) + (hashint << 4) + (hashint << 7) + (hashint << 8) + (hashint << 24);
}

return hashint >>> 0;
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import {SecurityNudge} from '../../../shared/components/security-nudge';
import {hasWarningConditionBadge} from '../../../shared/conditions-panel';
import {Context} from '../../../shared/context';
import {historyUrl} from '../../../shared/history';
import {getPodName} from '../../../shared/pod-name';
import {RetryWatch} from '../../../shared/retry-watch';
import {services} from '../../../shared/services';
import {useQueryParams} from '../../../shared/use-query-params';
import {Utils} from '../../../shared/utils';
import * as Operations from '../../../shared/workflow-operations-map';
import {WorkflowOperations} from '../../../shared/workflow-operations-map';
import {WidgetGallery} from '../../../widgets/widget-gallery';
Expand Down Expand Up @@ -233,16 +233,16 @@ export const WorkflowDetails = ({history, location, match}: RouteComponentProps<
});
};

const getPodName = (wf: Workflow, node: NodeStatus, nodeID: string): string => {
const ensurePodName = (wf: Workflow, node: NodeStatus, nodeID: string): string => {
if (workflow && node) {
return Utils.getPodName(wf.metadata.name, node.name, node.templateName, node.id);
return getPodName(wf.metadata.name, node.name, node.templateName, node.id);
}

return nodeID;
};

const selectedNode = workflow && workflow.status && workflow.status.nodes && workflow.status.nodes[nodeId];
const podName = getPodName(workflow, selectedNode, nodeId);
const podName = ensurePodName(workflow, selectedNode, nodeId);

return (
<Page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {execSpec} from '../../../../models';
import {ErrorNotice} from '../../../shared/components/error-notice';
import {InfoIcon, WarningIcon} from '../../../shared/components/fa-icons';
import {Links} from '../../../shared/components/links';
import {getPodName} from '../../../shared/pod-name';
import {services} from '../../../shared/services';
import {Utils} from '../../../shared/utils';
import {FullHeightLogsViewer} from './full-height-logs-viewer';

interface WorkflowLogsViewerProps {
Expand Down Expand Up @@ -63,7 +63,7 @@ export const WorkflowLogsViewer = ({workflow, nodeId, initialPodName, container,
.filter(x => x.type === 'Pod')
.map(targetNode => {
const {name, id, templateName, displayName} = targetNode;
const targetPodName = Utils.getPodName(workflow.metadata.name, name, templateName, id);
const targetPodName = getPodName(workflow.metadata.name, name, templateName, id);
return {value: targetPodName, label: (displayName || name) + ' (' + targetPodName + ')'};
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {InlineTable} from '../../../shared/components/inline-table/inline-table'
import {Links} from '../../../shared/components/links';
import {Phase} from '../../../shared/components/phase';
import {Timestamp} from '../../../shared/components/timestamp';
import {getPodName} from '../../../shared/pod-name';
import {ResourcesDuration} from '../../../shared/resources-duration';
import {services} from '../../../shared/services';
import {getResolvedTemplates} from '../../../shared/template-resolution';
import {Utils} from '../../../shared/utils';

require('./workflow-node-info.scss');

Expand Down Expand Up @@ -82,7 +82,7 @@ const AttributeRows = (props: {attributes: {title: string; value: any}[]}) => (
const WorkflowNodeSummary = (props: Props) => {
const {workflow, node} = props;

const podName = Utils.getPodName(workflow.metadata.name, node.name, node.templateName, node.id);
const podName = getPodName(workflow.metadata.name, node.name, node.templateName, node.id);

const attributes = [
{title: 'NAME', value: <ClipboardText text={props.node.name} />},
Expand Down