diff --git a/src/layouts/Base/index.less b/src/layouts/Base/index.less index 9fe505d..221eb74 100644 --- a/src/layouts/Base/index.less +++ b/src/layouts/Base/index.less @@ -101,6 +101,11 @@ font-size: @size-normal; margin-right: 20px; } + .ant-menu-submenu-title .anticon { + // Ant Design adds 10px to an icon immediately followed by a title span. + // Keep submenu titles aligned with ordinary menu items. + margin-right: 10px; + } & .ant-menu-item-selected { background-color: @primary-color; border-radius: @border-radius; diff --git a/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/LeftSteps.jsx b/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/LeftSteps.jsx index 31145d1..bec4d87 100644 --- a/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/LeftSteps.jsx +++ b/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/LeftSteps.jsx @@ -40,7 +40,7 @@ const StepItem = observer((props) => { } }, []); - const handleStepClick = () => activeByStep(step, index); + const handleStepClick = () => activeByStep(step, index, true); const stepItem = (
{
{isExpand ? : } - {`${nodes.name || nodes.ipv4 || nodes.id}`} + {nodes.ipv4 ? `${nodes.id}(${nodes.ipv4})` : nodes.id} {runtime || ''} {stateIcons(false, nodeStatus)} diff --git a/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/index.jsx b/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/index.jsx index 722beac..82598cd 100644 --- a/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/index.jsx +++ b/src/pages/cluster/containers/Cluster/Detail/Operation/actions/PipelineLogDialog/index.jsx @@ -53,6 +53,7 @@ export default class PipelineLog extends ViewAction { this.taskTimer = null; this.refreshing = false; this.disposer = null; + this.manuallySelectedStepID = null; } componentDidMount() { @@ -139,6 +140,12 @@ export default class PipelineLog extends ViewAction { updateOperation = async (operation, tasks) => { if (!operation) return; + if ( + this.rawOperation?.metadata?.uid && + this.rawOperation.metadata.uid !== operation.metadata?.uid + ) { + this.manuallySelectedStepID = null; + } this.rawOperation = operation; const operationTasks = tasks || (await this.fetchTasks(operation)); const result = ObjectMapper.operations(operation, operationTasks); @@ -148,12 +155,31 @@ export default class PipelineLog extends ViewAction { this.store.currentOperation = result; this.store.operationSteps = operationSteps; - const activeIndex = operationSteps.findIndex((step) => !step.isComplete); - const index = activeIndex === -1 ? operationSteps.length - 1 : activeIndex; - this.activeByStep(operationSteps[index], index); + let index = this.manuallySelectedStepID + ? operationSteps.findIndex( + (step) => step.stepID === this.manuallySelectedStepID + ) + : -1; + + if (index === -1) { + const activeIndex = operationSteps.findIndex((step) => !step.isComplete); + index = activeIndex === -1 ? operationSteps.length - 1 : activeIndex; + if (this.manuallySelectedStepID) { + this.manuallySelectedStepID = null; + } + } + + if (index >= 0) { + this.activeByStep(operationSteps[index], index); + } }; - activeByStep = async (step, index) => { + activeByStep = async (step, index, manuallySelected = false) => { + if (!step) return; + + if (manuallySelected) { + this.manuallySelectedStepID = step.stepID; + } this.store.currentNodesByStep = step; this.store.activeStepIndex = index; }; diff --git a/src/utils/object.mapper.js b/src/utils/object.mapper.js index 5127f93..ff6a435 100644 --- a/src/utils/object.mapper.js +++ b/src/utils/object.mapper.js @@ -272,8 +272,7 @@ const mapOperationStep = (step, tasks) => { id: target.name, name: target.name, uid: target.uid, - // Operation V2 exposes the stable node name, not the node IP. - ipv4: target.name, + ipv4: target.ip || get(task, 'spec.nodeRef.ip', ''), taskName: get(task, 'metadata.name'), status: phase, startAt: get(task, 'status.startedAt'), diff --git a/src/utils/object.mapper.test.js b/src/utils/object.mapper.test.js index 56266fe..a154138 100644 --- a/src/utils/object.mapper.test.js +++ b/src/utils/object.mapper.test.js @@ -45,7 +45,7 @@ describe('operation mapper', () => { steps: [ { id: 'step-1', - targets: [{ name: 'node-1', uid: 'node-uid' }], + targets: [{ name: 'node-1', uid: 'node-uid', ip: '10.0.0.1' }], payload: { step: { name: 'installRuntime' } }, }, ], @@ -57,6 +57,7 @@ describe('operation mapper', () => { const result = [operation].map(ObjectMapper.operations)[0]; expect(result.operationSteps[0].nodes[0].status).toBe('Pending'); + expect(result.operationSteps[0].nodes[0].ipv4).toBe('10.0.0.1'); expect(result.operationSteps[0].name).toBe('installRuntime'); }); @@ -75,4 +76,31 @@ describe('operation mapper', () => { expect(result.operationSteps[0].nodes[0].status).toBe('Succeeded'); expect(result.operationSteps[0].nodes[0].taskName).toBe('task-1'); }); + + it('uses the task node IP when the operation target omits it', () => { + const operationWithoutTargetIP = { + ...operation, + spec: { + ...operation.spec, + steps: [ + { + ...operation.spec.steps[0], + targets: [{ name: 'node-1', uid: 'node-uid' }], + }, + ], + }, + }; + const result = ObjectMapper.operations(operationWithoutTargetIP, [ + { + metadata: { name: 'task-1' }, + spec: { + stepID: 'step-1', + nodeRef: { name: 'node-1', uid: 'node-uid', ip: '10.0.0.2' }, + }, + status: { phase: 'Running' }, + }, + ]); + + expect(result.operationSteps[0].nodes[0].ipv4).toBe('10.0.0.2'); + }); });