Issue #2407 - Improve Helm/Git app version rendering (#2410)

This commit is contained in:
Alexander Matyushentsev 2019-10-03 14:47:36 -07:00 committed by GitHub
parent 9df130938e
commit c3479b886a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 12 deletions

View file

@ -131,7 +131,7 @@ export const ApplicationCreatePanel = (props: {
'metadata.name': !a.metadata.name && 'Application name is required',
'spec.project': !a.spec.project && 'Project name is required',
'spec.source.repoURL': !a.spec.source.repoURL && 'Repository URL is required',
'spec.source.targetRevision': !a.spec.source.targetRevision && 'Revision is required',
'spec.source.targetRevision': !a.spec.source.targetRevision && a.spec.source.hasOwnProperty('chart') && 'Version is required',
'spec.source.path': !a.spec.source.path && !a.spec.source.chart && 'Path is required',
'spec.source.chart': !a.spec.source.path && !a.spec.source.chart && 'Chart is required',
'spec.destination.server': !a.spec.destination.server && 'Cluster is required',

View file

@ -43,4 +43,19 @@
border: 1px solid #dbdcdc;
border-radius: 2px;
}
.argo-field {
line-height: 1.15;
}
.white-box__details-row .row {
padding-left: 1em;
padding-right: 1em;
}
.white-box__details-row .row .columns:last-child {
padding-left: 1em;
}
.select {
padding-bottom: 0;
}
}

View file

@ -4,7 +4,7 @@ import { FormApi, Text } from 'react-form';
require('./application-summary.scss');
import { Cluster, clusterTitle, DataLoader, EditablePanel, EditablePanelItem } from '../../../shared/components';
import { AutocompleteField, Cluster, clusterTitle, DataLoader, EditablePanel, EditablePanelItem } from '../../../shared/components';
import { Repo, Revision } from '../../../shared/components';
import { Consumer } from '../../../shared/context';
import * as models from '../../../shared/models';
@ -26,6 +26,7 @@ export const ApplicationSummary = (props: {
updateApp: (app: models.Application) => Promise<any>,
}) => {
const app = JSON.parse(JSON.stringify(props.app)) as models.Application;
const isHelm = app.spec.source.hasOwnProperty('chart');
const attributes = [
{
@ -63,16 +64,43 @@ export const ApplicationSummary = (props: {
view: <Repo url={app.spec.source.repoURL}/>,
edit: (formApi: FormApi) => <FormField formApi={formApi} field='spec.source.repoURL' component={Text}/>,
},
{
...(isHelm ? [{
title: 'CHART',
view: <span>{app.spec.source.chart}:{app.spec.source.targetRevision}</span>,
edit: (formApi: FormApi) => (
<DataLoader input={{repoURL: formApi.getFormState().values.spec.source.repoURL, chart: formApi.getFormState().values.spec.source.chart}}
load={(src) => services.repos.charts(src.repoURL).catch(() => new Array<models.HelmChart>()).then((charts) => {
const chartInfo = charts.find((chart) => chart.name === src.chart);
return {
charts: charts.map((chart) => chart.name),
versions: chartInfo && chartInfo.versions || new Array<string>(),
};
})}>
{(data: {charts: string[], versions: string[] }) => (
<div className='row'>
<div className='columns small-10'>
<FormField formApi={formApi} field='spec.source.chart' component={AutocompleteField} componentProps={{
items: data.charts, filterSuggestions: true,
}}/>
</div>
<div className='columns small-2'>
<FormField formApi={formApi} field='spec.source.targetRevision' component={AutocompleteField} componentProps={{
items: data.versions,
}}/>
</div>
</div>
)}
</DataLoader>
),
}] : [{
title: 'TARGET REVISION',
view: <Revision repoUrl={app.spec.source.repoURL} revision={app.spec.source.targetRevision}/>,
edit: (formApi: FormApi) => <FormField formApi={formApi} field='spec.source.targetRevision' component={Text}/>,
},
{
view: <Revision repoUrl={app.spec.source.repoURL} revision={app.spec.source.targetRevision || 'HEAD'}/>,
edit: (formApi: FormApi) => <FormField formApi={formApi} field='spec.source.targetRevision' component={Text} componentProps={{placeholder: 'HEAD'}} />,
}, {
title: 'PATH',
view: app.spec.source.path,
edit: (formApi: FormApi) => <FormField formApi={formApi} field='spec.source.path' component={Text}/>,
},
}]),
{title: 'STATUS', view: (
<span><ComparisonStatusIcon status={app.status.sync.status}/> {app.status.sync.status} {syncStatusMessage(app)}
</span>
@ -169,7 +197,7 @@ export const ApplicationSummary = (props: {
const appURL = `${location.protocol}//${location.host}/applications/${props.app.metadata.name}`;
return (
<React.Fragment>
<div className='application-summary'>
<EditablePanel
save={props.updateApp}
validate={(input) => ({
@ -263,6 +291,6 @@ export const ApplicationSummary = (props: {
)}
</DataLoader>
<EditablePanel save={(props.updateApp)} values={app} title='Info' items={infoItems} onModeSwitch={() => setAdjustedCount(0)}/>
</React.Fragment>
</div>
);
};

View file

@ -35,7 +35,7 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {
{isVisible && (
<Form
defaultValues={{
revision: application.spec.source.targetRevision,
revision: application.spec.source.targetRevision || 'HEAD',
resources: appResources.filter((item) => !item.hook).map((_, i) => i === syncResIndex || syncResIndex === -1),
}}
validateError={(values) => ({

View file

@ -36,7 +36,7 @@ export class ApplicationsService {
}
public revisionMetadata(name: string, revision: string): Promise<models.RevisionMetadata> {
return requests.get(`/applications/${name}/revisions/${revision}/metadata`)
return requests.get(`/applications/${name}/revisions/${revision || 'HEAD'}/metadata`)
.then((res) => res.body as models.RevisionMetadata);
}

View file

@ -270,6 +270,13 @@ func ValidatePermissions(ctx context.Context, spec *argoappv1.ApplicationSpec, p
})
return conditions, nil
}
if spec.Source.Chart != "" && spec.Source.TargetRevision == "" {
conditions = append(conditions, argoappv1.ApplicationCondition{
Type: argoappv1.ApplicationConditionInvalidSpecError,
Message: "spec.source.targetRevision is required if the manifest source is a helm chart",
})
return conditions, nil
}
if !proj.IsSourcePermitted(spec.Source) {
conditions = append(conditions, argoappv1.ApplicationCondition{

View file

@ -148,6 +148,23 @@ func TestValidatePermissionsEmptyDestination(t *testing.T) {
assert.ElementsMatch(t, conditions, []argoappv1.ApplicationCondition{{Type: argoappv1.ApplicationConditionInvalidSpecError, Message: "Destination server and/or namespace missing from app spec"}})
}
func TestValidateChartWithoutRevision(t *testing.T) {
conditions, err := ValidatePermissions(context.Background(), &argoappv1.ApplicationSpec{
Source: argoappv1.ApplicationSource{RepoURL: "https://kubernetes-charts-incubator.storage.googleapis.com/", Chart: "myChart", TargetRevision: ""},
Destination: argoappv1.ApplicationDestination{
Server: "https://kubernetes.default.svc", Namespace: "default",
},
}, &argoappv1.AppProject{
Spec: argoappv1.AppProjectSpec{
SourceRepos: []string{"*"},
Destinations: []argoappv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
},
}, nil)
assert.NoError(t, err)
assert.ElementsMatch(t, conditions, []argoappv1.ApplicationCondition{{
Type: argoappv1.ApplicationConditionInvalidSpecError, Message: "spec.source.targetRevision is required if the manifest source is a helm chart"}})
}
func Test_enrichSpec(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
spec := &argoappv1.ApplicationSpec{}