Skip to content

Commit

Permalink
feat(ui): Add cost optimisation nudges. (#3089)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexec authored May 28, 2020
1 parent e88124d commit 9229165
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 1 deletion.
41 changes: 41 additions & 0 deletions ui/src/app/shared/components/cost-optimisation-nudge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react';
import {Notice} from './notice';

interface Props {
name: string;
}
interface State {
closed: boolean;
}

export class CostOptimisationNudge extends React.Component<Props, State> {
constructor(props: Readonly<Props>) {
super(props);
this.state = {closed: localStorage.getItem(this.key) !== null};
}

public render() {
return (
!this.state.closed && (
<Notice>
<i className='fa fa-money-bill-alt status-icon--pending' /> {this.props.children}{' '}
<a href='https://github.com/argoproj/argo/blob/master/docs/cost-optimisation.md'>Learn more</a>
<span className='fa-pull-right'>
<a onClick={() => this.close()}>
<i className='fa fa-times' />
</a>{' '}
</span>
</Notice>
)
);
}

private get key() {
return 'cost-optimization-nude/' + this.props.name;
}

private close() {
this.setState({closed: true});
localStorage.setItem(this.key, '{}');
}
}
13 changes: 13 additions & 0 deletions ui/src/app/shared/components/notice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from 'react';

export class Notice extends React.Component {
public render() {
return (
<div style={{marginTop: 20, marginBottom: 20}}>
<div className='white-box' style={{padding: 20}}>
{this.props.children}
</div>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {uiUrl} from '../../../shared/base';
import {services} from '../../../shared/services';

import {WorkflowArtifacts, WorkflowDag, WorkflowLogsViewer, WorkflowNodeInfo, WorkflowSummaryPanel, WorkflowTimeline, WorkflowYamlViewer} from '..';
import {CostOptimisationNudge} from '../../../shared/components/cost-optimisation-nudge';
import {hasWarningConditionBadge} from '../../../shared/conditions-panel';
import {Consumer, ContextApis} from '../../../shared/context';
import {Utils} from '../../../shared/utils';
Expand Down Expand Up @@ -360,6 +361,27 @@ export class WorkflowDetails extends React.Component<RouteComponentProps<any>, W
this.appContext.router.history.push(`${this.props.match.url}?${params.toString()}`);
}

private renderCostOptimisations() {
const recommendations: string[] = [];
if (!this.state.workflow.spec.activeDeadlineSeconds) {
recommendations.push('activeDeadlineSeconds');
}
if (!this.state.workflow.spec.ttlStrategy) {
recommendations.push('ttlStrategy');
}
if (!this.state.workflow.spec.podGC) {
recommendations.push('podGC');
}
if (recommendations.length === 0) {
return;
}
return (
<CostOptimisationNudge name='workflow'>
You do not have {recommendations.join('/')} enabled for this workflow. Enabling these will reduce your costs.
</CostOptimisationNudge>
);
}

private renderSummaryTab() {
if (!this.state.workflow) {
return <div>Loading...</div>;
Expand All @@ -368,6 +390,7 @@ export class WorkflowDetails extends React.Component<RouteComponentProps<any>, W
<div className='argo-container'>
<div className='workflow-details__content'>
<WorkflowSummaryPanel workflow={this.state.workflow} />
{this.renderCostOptimisations()}
{this.state.workflow.spec.arguments && this.state.workflow.spec.arguments.parameters && (
<React.Fragment>
<h6>Parameters</h6>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Subscription} from 'rxjs';

import {Autocomplete, Page, SlidingPanel} from 'argo-ui';
import * as models from '../../../../models';
import {Workflow} from '../../../../models';
import {labels, Workflow} from '../../../../models';
import {uiUrl} from '../../../shared/base';
import {Consumer} from '../../../shared/context';
import {services} from '../../../shared/services';
Expand All @@ -19,6 +19,7 @@ import {Utils} from '../../../shared/utils';

import {Ticker} from 'argo-ui/src/index';
import * as classNames from 'classnames';
import {CostOptimisationNudge} from '../../../shared/components/cost-optimisation-nudge';
import {PaginationPanel} from '../../../shared/components/pagination-panel';
import {Timestamp} from '../../../shared/components/timestamp';
import {formatDuration, wfDuration} from '../../../shared/duration';
Expand Down Expand Up @@ -225,6 +226,18 @@ export class WorkflowsList extends BasePage<RouteComponentProps<any>, State> {
this.fetchWorkflows(namespace, selectedPhases, selectedLabels, pagination);
}

private countsByCompleted() {
const counts = {complete: 0, incomplete: 0};
this.state.workflows.forEach(wf => {
if (wf.metadata.labels && wf.metadata.labels[labels.completed] === 'true') {
counts.complete++;
} else {
counts.incomplete++;
}
});
return counts;
}

private renderWorkflows() {
if (!this.state.workflows) {
return <Loading />;
Expand All @@ -238,8 +251,15 @@ export class WorkflowsList extends BasePage<RouteComponentProps<any>, State> {
);
}

const counts = this.countsByCompleted();

return (
<>
{(counts.complete > 100 || counts.incomplete > 100) && (
<CostOptimisationNudge name='workflow-list'>
You have at least {counts.incomplete} incomplete, and {counts.complete} complete workflows. Reducing these amounts will reduce your costs.
</CostOptimisationNudge>
)}
<div className='argo-table-list'>
<div className='row argo-table-list__head'>
<div className='columns small-1' />
Expand Down
21 changes: 21 additions & 0 deletions ui/src/models/workflows.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import * as kubernetes from 'argo-ui/src/models/kubernetes';

export const labels = {
completed: 'workflows.argoproj.io/completed'
};

/**
* Arguments to a template
*/
Expand Down Expand Up @@ -804,6 +808,23 @@ export interface WorkflowList {
* WorkflowSpec is the specification of a Workflow.
*/
export interface WorkflowSpec {
/**
* Optional duration in seconds relative to the workflow start time which the workflow is
* allowed to run before the controller terminates the workflow. A value of zero is used to
* terminate a Running workflow
*/
activeDeadlineSeconds?: number;
/**
* TTLStrategy limits the lifetime of a Workflow that has finished execution depending on if it
* Succeeded or Failed. If this struct is set, once the Workflow finishes, it will be
* deleted after the time to live expires. If this field is unset,
* the controller config map will hold the default values.
*/
ttlStrategy?: {};
/**
* PodGC describes the strategy to use when to deleting completed pods
*/
podGC?: {};
/**
* Affinity sets the scheduling constraints for all pods in the workflow. Can be overridden by an affinity specified in the template
*/
Expand Down

0 comments on commit 9229165

Please sign in to comment.