Skip to content

Commit

Permalink
feat: UI enhancement for Cluster Workflow Template (argoproj#2525)
Browse files Browse the repository at this point in the history
  • Loading branch information
sarabala1979 committed Apr 4, 2020
1 parent 0801a42 commit 59f746e
Show file tree
Hide file tree
Showing 17 changed files with 534 additions and 11 deletions.
81 changes: 81 additions & 0 deletions docs/cluster-workflow-templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Cluster Workflow Templates

> v2.8 and after
## Introduction

`ClusterWorkflowTemplates` are cluster scoped `WorkflowTemplates`. `ClusterWorkflowTemplate`
can be created cluster scoped like `ClusterRole` and can be accessed all namespaces in the cluster.

`WorkflowTemplate` documentation [link](./workflow-template.md)

## Defining `ClusterWorkflowTemplate`

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ClusterWorkflowTemplate
metadata:
name: cluster-workflow-template-whalesay-template
spec:
templates:
- name: whalesay-template
inputs:
parameters:
- name: message
container:
image: docker/whalesay
command: [cowsay]
args: ["{{inputs.parameters.message}}"]
```

## Referencing other `ClusterWorkflowTemplates`

You can reference `templates` from another `ClusterWorkflowTemplates` using a `templateRef` field with `clusterScope: true` .
Just as how you reference other `templates` within the same `Workflow`, you should do so from a `steps` or `dag` template.

Here is an example:
More examples []()
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: workflow-template-hello-world-
spec:
entrypoint: whalesay
templates:
- name: whalesay
steps: # You should only reference external "templates" in a "steps" or "dag" "template".
- - name: call-whalesay-template
templateRef: # You can reference a "template" from another "WorkflowTemplate or ClusterWorkflowTemplate" using this field
name: cluster-workflow-template-whalesay-template # This is the name of the "WorkflowTemplate or ClusterWorkflowTempalte" CRD that contains the "template" you want
template: whalesay-template # This is the name of the "template" you want to reference
clusterScope: true # This field indicates this templateRef is pointing ClusterWorkflowTemplate
arguments: # You can pass in arguments as normal
parameters:
- name: message
value: "hello world"
```

## Managing `ClusterWorkflowTemplates`

### CLI

You can create some example templates as follows:

```
argo cluster-template create https://raw.githubusercontent.com/argoproj/argo/master/examples/cluster-workflow-template/clustertemplates.yaml
```

The submit a workflow using one of those templates:

```
argo submit https://raw.githubusercontent.com/argoproj/argo/master/examples/cluster-workflow-template/cluster-wftmpl-dag.yaml
```

### `kubectl`

Using `kubectl apply -f` and `kubectl get cwft`

### UI

`ClusterWorkflowTemplate` resources can also be managed by the UI
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ require (
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/tools v0.0.0-20200401192744-099440627f01 // indirect
golang.org/x/tools v0.0.0-20200402223321-bcf690261a44 // indirect
google.golang.org/api v0.20.0
google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24
google.golang.org/grpc v1.28.0
Expand Down
5 changes: 3 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
Expand Down Expand Up @@ -613,8 +614,8 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200401192744-099440627f01 h1:ysQJ/fU6laLOZJseIeOqXl6Mo+lw5z6b7QHnmUKjW+k=
golang.org/x/tools v0.0.0-20200401192744-099440627f01/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200402223321-bcf690261a44 h1:bMm0eoDiGkM5VfIyKjxDvoflW5GLp7+VCo+60n8F+TE=
golang.org/x/tools v0.0.0-20200402223321-bcf690261a44/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
9 changes: 9 additions & 0 deletions ui/src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {ContextApis, Provider} from './shared/context';

import archivedWorkflows from './archived-workflows';
import cronWorkflows from './cron-workflows';
import clusterWorkflowTemplates from './cluster-workflow-templates'
import help from './help';
import login from './login';
import ErrorBoundary from './shared/components/error-boundary';
Expand All @@ -17,6 +18,8 @@ import workflows from './workflows';

const workflowsUrl = uiUrl('workflows');
const workflowTemplatesUrl = uiUrl('workflow-templates');
const clusterWorkflowTemplatesUrl = uiUrl('cluster-workflow-templates');

const cronWorkflowUrl = uiUrl('cron-workflows');
const archivedWorkflowUrl = uiUrl('archived-workflows');
const helpUrl = uiUrl('help');
Expand All @@ -27,6 +30,7 @@ const routes: {
} = {
[workflowsUrl]: {component: workflows.component},
[workflowTemplatesUrl]: {component: workflowTemplates.component},
[clusterWorkflowTemplatesUrl]: {component: clusterWorkflowTemplates.component},
[cronWorkflowUrl]: {component: cronWorkflows.component},
[archivedWorkflowUrl]: {component: archivedWorkflows.component},
[helpUrl]: {component: help.component},
Expand All @@ -44,6 +48,11 @@ const navItems = [
{
title: 'Workflow Templates',
path: workflowTemplatesUrl,
iconClassName: 'fas fa-object-group'
},
{
title: 'Cluster Workflow Templates',
path: clusterWorkflowTemplatesUrl,
iconClassName: 'fa fa-clone'
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from 'react';
import {Route, RouteComponentProps, Switch} from 'react-router';
import {ClusterWorkflowTemplateDetails} from './cluster-workflow-template-details/cluster-workflow-template-details';
import {ClusterWorkflowTemplateList} from './cluster-workflow-template-list/cluster-workflow-template-list';

export const ClusterWorkflowTemplateContainer = (props: RouteComponentProps<any>) => (
<Switch>
<Route exact={true} path={`${props.match.path}`} component={ClusterWorkflowTemplateList} />
<Route exact={true} path={`${props.match.path}/:name`} component={ClusterWorkflowTemplateDetails} />
</Switch>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import {NotificationType, Page} from 'argo-ui';
import {SlidingPanel} from 'argo-ui/src/index';
import * as React from 'react';
import {RouteComponentProps} from 'react-router';
import * as models from '../../../../models';
import {uiUrl} from '../../../shared/base';
import {BasePage} from '../../../shared/components/base-page';
import {Loading} from '../../../shared/components/loading';
import {ResourceSubmit} from '../../../shared/components/resource-submit';
import {Consumer} from '../../../shared/context';
import {services} from '../../../shared/services';
import {ClusterWorkflowTemplateSummaryPanel} from '../cluster-workflow-template-summary-panel';

require('../../../workflows/components/workflow-details/workflow-details.scss');

interface State {
template?: models.ClusterWorkflowTemplate;
error?: Error;
}

export class ClusterWorkflowTemplateDetails extends BasePage<RouteComponentProps<any>, State> {


private get name() {
return this.props.match.params.name;
}

private get sidePanel() {
return this.queryParam('sidePanel');
}

private set sidePanel(sidePanel) {
this.setQueryParams({sidePanel});
}

constructor(props: RouteComponentProps<any>, context: any) {
super(props, context);
this.state = {};
}

public componentDidMount(): void {
services.clusterWorkflowTemplate
.get(this.name)
.then(template => {
this.setState({template});
})
.catch(error => this.setState({error}));
}

public render() {
if (this.state.error !== undefined) {
throw this.state.error;
}
return (
<Consumer>
{ctx => (
<Page
title='Cluster Workflow Template Details'
toolbar={{
actionMenu: {
items: [
{
title: 'Submit',
iconClassName: 'fa fa-plus',
action: () => (this.sidePanel = 'new')
},
{
title: 'Delete',
iconClassName: 'fa fa-trash',
action: () => this.deleteClusterWorkflowTemplate()
}
]
},
breadcrumbs: [
{
title: 'Cluster Workflow Template',
path: uiUrl('cluster-workflow-templates')
},
{title: this.name}
]
}}>
<div className='argo-container'>
<div className='workflow-details__content'>{this.renderClusterWorkflowTemplate()}</div>
</div>
{this.state.template && (
<SlidingPanel isShown={this.sidePanel !== null} onClose={() => (this.sidePanel = null)}>
<ResourceSubmit<models.Workflow>
resourceName={'Workflow'}
defaultResource={this.getWorkflow(this.state.template)}
onSubmit={wfValue => {
return services.workflows
.create(wfValue, wfValue.metadata.namespace)
.then(workflow => ctx.navigation.goto(uiUrl(`workflows/${workflow.metadata.namespace}/${workflow.metadata.name}`)));
}}
/>
</SlidingPanel>
)}
</Page>
)}
</Consumer>
);
}

private renderClusterWorkflowTemplate() {
if (!this.state.template) {
return <Loading />;
}
return <ClusterWorkflowTemplateSummaryPanel template={this.state.template} onChange={template => this.setState({template})} onError={error => this.setState({error})} />;
}

private deleteClusterWorkflowTemplate() {
if (!confirm('Are you sure you want to delete this cluster workflow template?\nThere is no undo.')) {
return;
}
services.clusterWorkflowTemplate
.delete(this.name)
.catch(e => {
this.appContext.apis.notifications.show({
content: 'Failed to delete cluster workflow template ' + e,
type: NotificationType.Error
});
})
.then(() => {
document.location.href = uiUrl('cluster-workflow-templates');
});
}

private getWorkflow(template: models.ClusterWorkflowTemplate): models.Workflow {
return {
metadata: {
generateName: template.metadata.name + '-',
namespace: '<enter the namespace>'
},
spec: {
entrypoint: template.spec.templates[0].name,
templates: template.spec.templates.map(t => ({
name: t.name,
templateRef: {
name: template.metadata.name,
template: t.name,
clusterscope: true,
}
}))
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@import 'node_modules/argo-ui/src/styles/config';

.argo-table-list {
margin: 1em;
&__row:hover {
box-shadow: 1px 2px 3px rgba($argo-color-gray-9, .1), 0 0 0 1px rgba($argo-color-teal-5, .5);
}
}
Loading

0 comments on commit 59f746e

Please sign in to comment.