diff --git a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx index 58c023a113..82e852652d 100644 --- a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx +++ b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx @@ -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', diff --git a/ui/src/app/applications/components/application-summary/application-summary.scss b/ui/src/app/applications/components/application-summary/application-summary.scss index b557d70710..28ebab8b31 100644 --- a/ui/src/app/applications/components/application-summary/application-summary.scss +++ b/ui/src/app/applications/components/application-summary/application-summary.scss @@ -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; + } } diff --git a/ui/src/app/applications/components/application-summary/application-summary.tsx b/ui/src/app/applications/components/application-summary/application-summary.tsx index 4fcb7c0272..ebb6d7fa9f 100644 --- a/ui/src/app/applications/components/application-summary/application-summary.tsx +++ b/ui/src/app/applications/components/application-summary/application-summary.tsx @@ -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, }) => { 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: , edit: (formApi: FormApi) => , }, - { + ...(isHelm ? [{ + title: 'CHART', + view: {app.spec.source.chart}:{app.spec.source.targetRevision}, + edit: (formApi: FormApi) => ( + services.repos.charts(src.repoURL).catch(() => new Array()).then((charts) => { + const chartInfo = charts.find((chart) => chart.name === src.chart); + return { + charts: charts.map((chart) => chart.name), + versions: chartInfo && chartInfo.versions || new Array(), + }; + })}> + {(data: {charts: string[], versions: string[] }) => ( +
+
+ +
+
+ +
+
+ )} +
+ ), + }] : [{ title: 'TARGET REVISION', - view: , - edit: (formApi: FormApi) => , - }, - { + view: , + edit: (formApi: FormApi) => , + }, { title: 'PATH', view: app.spec.source.path, edit: (formApi: FormApi) => , - }, + }]), {title: 'STATUS', view: ( {app.status.sync.status} {syncStatusMessage(app)} @@ -169,7 +197,7 @@ export const ApplicationSummary = (props: { const appURL = `${location.protocol}//${location.host}/applications/${props.app.metadata.name}`; return ( - +
({ @@ -263,6 +291,6 @@ export const ApplicationSummary = (props: { )} setAdjustedCount(0)}/> - +
); }; diff --git a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx index 711ee0d64b..519e03035a 100644 --- a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx +++ b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx @@ -35,7 +35,7 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: { {isVisible && (
!item.hook).map((_, i) => i === syncResIndex || syncResIndex === -1), }} validateError={(values) => ({ diff --git a/ui/src/app/shared/services/applications-service.ts b/ui/src/app/shared/services/applications-service.ts index 50d9d3a466..67de742ca3 100644 --- a/ui/src/app/shared/services/applications-service.ts +++ b/ui/src/app/shared/services/applications-service.ts @@ -36,7 +36,7 @@ export class ApplicationsService { } public revisionMetadata(name: string, revision: string): Promise { - return requests.get(`/applications/${name}/revisions/${revision}/metadata`) + return requests.get(`/applications/${name}/revisions/${revision || 'HEAD'}/metadata`) .then((res) => res.body as models.RevisionMetadata); } diff --git a/util/argo/argo.go b/util/argo/argo.go index 29c2793393..13cce17da5 100644 --- a/util/argo/argo.go +++ b/util/argo/argo.go @@ -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{ diff --git a/util/argo/argo_test.go b/util/argo/argo_test.go index fc07449e66..3d7b181258 100644 --- a/util/argo/argo_test.go +++ b/util/argo/argo_test.go @@ -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{}