Skip to content

Commit

Permalink
feat(console): support helm history manifest (#756)
Browse files Browse the repository at this point in the history
* fix(console): update app resource list

* fix(console): update app resource list

* fix(application): support app history manifest

* fix(console): support helm history manifest

Co-authored-by: jianzhuang <[email protected]>
  • Loading branch information
jianzzz and jianzhuang committed Sep 25, 2020
1 parent 067166b commit 0ad32bf
Show file tree
Hide file tree
Showing 19 changed files with 637 additions and 143 deletions.
1 change: 1 addition & 0 deletions api/application/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ type History struct {
Chart string
AppVersion string
Description string
Manifest string
}

// +genclient
Expand Down
218 changes: 129 additions & 89 deletions api/application/v1/generated.pb.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions api/application/v1/generated.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/application/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ type History struct {
Chart string `json:"chart,omitempty" protobuf:"bytes,4,opt,name=chart"`
AppVersion string `json:"appVersion,omitempty" protobuf:"bytes,5,opt,name=appVersion"`
Description string `json:"description,omitempty" protobuf:"bytes,6,opt,name=description"`
Manifest string `json:"manifest,omitempty" protobuf:"bytes,7,opt,name=manifest"`
}

// +genclient
Expand Down
2 changes: 2 additions & 0 deletions api/application/v1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions api/openapi/zz_generated.openapi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pkg/application/helm/action/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type ReleaseInfo struct {
Chart string `json:"chart"`
AppVersion string `json:"app_version"`
Description string `json:"description"`
Manifest string `json:"manifest"`
}

type ReleaseHistory []ReleaseInfo
Expand Down Expand Up @@ -87,13 +88,14 @@ func getReleaseHistory(rls []*release.Release) (history ReleaseHistory) {
v := r.Version
d := r.Info.Description
a := formatAppVersion(r.Chart)

m := r.Manifest
rInfo := ReleaseInfo{
Revision: v,
Status: s,
Chart: c,
AppVersion: a,
Description: d,
Manifest: m,
}
if !r.Info.LastDeployed.IsZero() {
rInfo.Updated = r.Info.LastDeployed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func (rs *HistoryREST) Get(ctx context.Context, name string, options *metav1.Get
Chart: h.Chart,
AppVersion: h.AppVersion,
Description: h.Description,
Manifest: h.Manifest,
}
}
return appHistory, nil
Expand Down
13 changes: 9 additions & 4 deletions web/console/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"d3-time-format": "^2.1.3",
"dagre-d3": "^0.6.3",
"dayjs": "^1.7.7",
"diff": "^4.0.2",
"express": "=4.16.3",
"fetch-jsonp": "^1.1.3",
"final-form": "^4.19.1",
Expand Down
24 changes: 20 additions & 4 deletions web/console/src/modules/application/actions/app/resourceActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,25 @@ const fetchAppResourceActions = createFFObjectActions<AppResource, AppResourceFi
return getState().appResource;
},
onFinish: (record, dispatch: Redux.Dispatch, getState: GetState) => {
let resources = [];
let resources = new Map<string, Resource[]>();
if (record.data) {
try {
Object.keys(record.data.spec.resources).forEach(k => {
record.data.spec.resources[k].forEach(item => {
let json = JsYAML.safeLoad(item);
resources.push({
if (!resources.get(k)) {
resources.set(k, []);
}
resources.get(k).push({
id: uuid(),
metadata: {
namespace: json.metadata.namespace,
name: json.metadata.name
},
kind: json.kind,
cluster: record.data.spec.targetCluster,
yaml: JsYAML.safeDump(json)
yaml: JsYAML.safeDump(json),
object: json
});
});
});
Expand All @@ -53,6 +57,18 @@ const fetchAppResourceActions = createFFObjectActions<AppResource, AppResourceFi
}
});

const restActions = {};
const restActions = {
/** 轮询操作 */
poll: (filter: AppResourceFilter) => {
return async (dispatch: Redux.Dispatch, getState: GetState) => {
dispatch(
resourceActions.polling({
delayTime: 5000,
filter: filter
})
);
};
}
};

export const resourceActions = extend({}, fetchAppResourceActions, restActions);
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ export class AppDetail extends React.Component<RootProps, {}> {
actions.app.detail.updateAppWorkflow.reset();
actions.app.detail.clearEditorState();
actions.app.detail.clearValidatorState();

actions.app.resource.clearPolling();
}

componentDidMount() {
const { actions, route, appEditor } = this.props;
let urlParam = router.resolve(route);
let tab = 'detail';
let tab = 'resource';
if (urlParam['tab']) tab = urlParam['tab'];
this.changeTab(tab);
}
Expand All @@ -36,6 +38,8 @@ export class AppDetail extends React.Component<RootProps, {}> {
const { actions, route, appEditor } = this.props;
switch (tab) {
case 'detail': {
actions.app.resource.clearPolling();

/** 拉取仓库列表 */
actions.chartGroup.list.applyFilter({});
/** 查询具体应用,从而Detail可以用到 */
Expand All @@ -47,14 +51,16 @@ export class AppDetail extends React.Component<RootProps, {}> {
break;
}
case 'resource': {
actions.app.resource.applyFilter({
actions.app.resource.poll({
cluster: route.queries['cluster'],
namespace: route.queries['namespace'],
name: route.queries['app']
});
break;
}
case 'history': {
actions.app.resource.clearPolling();

actions.app.history.applyFilter({
cluster: route.queries['cluster'],
namespace: route.queries['namespace'],
Expand All @@ -67,14 +73,14 @@ export class AppDetail extends React.Component<RootProps, {}> {

render() {
const tabs = [
{ id: 'detail', label: t('应用详情') },
{ id: 'resource', label: t('资源列表') },
{ id: 'detail', label: t('应用详情') },
{ id: 'history', label: t('版本历史') }
];

let { actions, route } = this.props,
urlParam = router.resolve(route);
let tab = 'detail';
let tab = 'resource';
if (urlParam['tab']) tab = urlParam['tab'];
return (
<React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class HeaderPanel extends React.Component<RootProps, {}> {

render() {
let { route } = this.props;
let title = route.queries['app'];
let title = route.queries['appName'] + '(' + route.queries['namespace'] + ')';

return (
<Justify
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,61 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { LinkButton, emptyTips } from '../../../../common/components';
import { Table, TableColumn, Text, Modal, Card, Bubble, Icon, ContentView } from '@tea/component';
import { bindActionCreators } from '@tencent/ff-redux';
import { LinkButton, YamlEditorPanel } from '../../../../common/components';
import { Table, TableColumn, Text, Modal, Card, Justify, Button, ContentView } from '@tea/component';
import { bindActionCreators, uuid } from '@tencent/ff-redux';
import { t, Trans } from '@tencent/tea-app/lib/i18n';
import { router } from '../../../router';
import { allActions } from '../../../actions';
import { History } from '../../../models';
import { RootProps } from '../AppContainer';
import { UnControlled as CodeMirror } from 'react-codemirror2';
import { selectable } from '@tea/component/table/addons/selectable';
import { dateFormat } from '../../../../../../helpers/dateUtil';
// @ts-ignore
const tips = seajs.require('tips');
const jsDiff = require('diff');

const mapDispatchToProps = dispatch =>
Object.assign({}, bindActionCreators({ actions: allActions }, dispatch), {
dispatch
});

interface State {
showYamlDialog?: boolean;
yaml?: string;
revisions?: string[];
selectedHistories?: History[];
}
@connect(state => state, mapDispatchToProps)
export class HistoryTablePanel extends React.Component<RootProps, {}> {
export class HistoryTablePanel extends React.Component<RootProps, State> {
state = {
showYamlDialog: false,
yaml: '',
revisions: [],
selectedHistories: []
};
showYaml(yaml, revisions) {
this.setState({
showYamlDialog: true,
yaml,
revisions
});
}

_renderYamlDialog() {
const cancel = () => this.setState({ showYamlDialog: false, yaml: '' });
const title =
this.state.revisions && this.state.revisions.length === 1
? '版本: ' + this.state.revisions[0]
: '版本比对: ' + this.state.revisions[0] + '和' + this.state.revisions[1];
return (
<Modal visible={true} caption={t(title)} onClose={cancel} size={700} disableEscape={true}>
<Modal.Body>
<YamlEditorPanel readOnly={true} config={this.state.yaml} />
</Modal.Body>
</Modal>
);
}

render() {
let { actions, historyList, route } = this.props;
const columns: TableColumn<History>[] = [
Expand Down Expand Up @@ -73,15 +111,58 @@ export class HistoryTablePanel extends React.Component<RootProps, {}> {
<ContentView.Body>
<Card>
<Card.Body>
<Table.ActionPanel>
<Justify
left={
<Button
type="primary"
onClick={e => {
e.preventDefault();
if (!this.state.selectedHistories || this.state.selectedHistories.length !== 2) {
tips.error('请选择两个比对版本', 2000);
return;
}
//context的数目会影响比对行数的显示
const diff = jsDiff.createTwoFilesPatch(
t('版本: ') + this.state.selectedHistories[1].revision,
t('版本: ') + this.state.selectedHistories[0].revision,
this.state.selectedHistories[1].manifest,
this.state.selectedHistories[0].manifest,
'',
'',
{ context: 0 }
);
this.showYaml(diff, [
this.state.selectedHistories[0].revision,
this.state.selectedHistories[1].revision
]);
}}
>
{t('参数比对')}
</Button>
}
/>
</Table.ActionPanel>
<Table
recordKey={record => {
return record.id.toString();
}}
records={historyList.histories}
columns={columns}
addons={[
selectable({
value: this.state.selectedHistories.map(item => item.id as string),
onChange: keys => {
this.setState({
selectedHistories: historyList.histories.filter(item => keys.indexOf(item.id as string) > -1)
});
}
})
]}
/>
</Card.Body>
</Card>
{this.state.showYamlDialog && this._renderYamlDialog()}
</ContentView.Body>
</ContentView>
);
Expand All @@ -90,10 +171,27 @@ export class HistoryTablePanel extends React.Component<RootProps, {}> {
/** 渲染操作按钮 */
_renderOperationCell = (app: History) => {
if (app.status === 'deployed') {
return false;
return (
<React.Fragment>
<LinkButton
onClick={() => {
this.showYaml(app.manifest, [app.revision]);
}}
>
{t('参数')}
</LinkButton>
</React.Fragment>
);
}
return (
<React.Fragment>
<LinkButton
onClick={() => {
this.showYaml(app.manifest, [app.revision]);
}}
>
{t('参数')}
</LinkButton>
<LinkButton onClick={() => this._rollbackApp(app)}>{t('回滚')}</LinkButton>
</React.Fragment>
);
Expand Down
Loading

0 comments on commit 0ad32bf

Please sign in to comment.