mirror of
https://github.com/argoproj/argo-cd
synced 2026-05-24 09:50:08 +00:00
* feat: Support applications with cluster name in the ui * Retrigger CI pipeline
This commit is contained in:
parent
7a348f786b
commit
275daa7976
10 changed files with 49 additions and 20 deletions
|
|
@ -13,14 +13,15 @@ import (
|
|||
|
||||
func TestClusterList(t *testing.T) {
|
||||
output := FailOnErr(RunCli("cluster", "list")).(string)
|
||||
assert.Equal(t, fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE
|
||||
https://kubernetes.default.svc %v Successful `, GetVersions().ServerVersion), output)
|
||||
assert.Equal(t, fmt.Sprintf(`SERVER NAME VERSION STATUS MESSAGE
|
||||
https://kubernetes.default.svc in-cluster %v Successful `, GetVersions().ServerVersion), output)
|
||||
}
|
||||
|
||||
func TestClusterGet(t *testing.T) {
|
||||
output := FailOnErr(RunCli("cluster", "get", "https://kubernetes.default.svc")).(string)
|
||||
|
||||
assert.Contains(t, output, fmt.Sprintf(`
|
||||
name: in-cluster
|
||||
server: https://kubernetes.default.svc
|
||||
serverVersion: "%v"`, GetVersions().ServerVersion))
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const DEFAULT_APP: Partial<models.Application> = {
|
|||
},
|
||||
spec: {
|
||||
destination: {
|
||||
name: '',
|
||||
namespace: '',
|
||||
server: ''
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ function swap(array: any[], a: number, b: number) {
|
|||
export const ApplicationSummary = (props: {app: models.Application; 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 isDestName = app.spec.destination.server === undefined;
|
||||
const fieldDest = isDestName ? 'spec.destination.name' : 'spec.destination.server';
|
||||
|
||||
const attributes = [
|
||||
{
|
||||
|
|
@ -59,18 +61,18 @@ export const ApplicationSummary = (props: {app: models.Application; updateApp: (
|
|||
},
|
||||
{
|
||||
title: 'CLUSTER',
|
||||
view: <Cluster server={app.spec.destination.server} showUrl={true} />,
|
||||
view: <Cluster server={app.spec.destination.server} name={app.spec.destination.name} showUrl={true} />,
|
||||
edit: (formApi: FormApi) => (
|
||||
<DataLoader
|
||||
load={() =>
|
||||
services.clusters.list().then(clusters =>
|
||||
clusters.map(cluster => ({
|
||||
title: clusterTitle(cluster),
|
||||
value: cluster.server
|
||||
value: isDestName ? cluster.name : cluster.server
|
||||
}))
|
||||
)
|
||||
}>
|
||||
{clusters => <FormField formApi={formApi} field='spec.destination.server' componentProps={{options: clusters}} component={FormSelect} />}
|
||||
{clusters => <FormField formApi={formApi} field={fieldDest} componentProps={{options: clusters}} component={FormSelect} />}
|
||||
</DataLoader>
|
||||
)
|
||||
},
|
||||
|
|
@ -326,10 +328,17 @@ Default is 10.
|
|||
<div className='application-summary'>
|
||||
<EditablePanel
|
||||
save={props.updateApp}
|
||||
validate={input => ({
|
||||
'spec.project': !input.spec.project && 'Project name is required',
|
||||
'spec.destination.server': !input.spec.destination.server && 'Cluster is required'
|
||||
})}
|
||||
validate={input =>
|
||||
isDestName
|
||||
? {
|
||||
'spec.project': !input.spec.project && 'Project name is required',
|
||||
'spec.destination.name': !input.spec.destination.name && 'Cluster is required'
|
||||
}
|
||||
: {
|
||||
'spec.project': !input.spec.project && 'Project name is required',
|
||||
'spec.destination.server': !input.spec.destination.server && 'Cluster is required'
|
||||
}
|
||||
}
|
||||
values={app}
|
||||
title={app.metadata.name.toLocaleUpperCase()}
|
||||
items={attributes}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ export interface ApplicationTilesProps {
|
|||
deleteApplication: (appName: string) => any;
|
||||
}
|
||||
|
||||
function getDestination(dest: models.ApplicationDestination) {
|
||||
if (dest.server === undefined) {
|
||||
return dest.name;
|
||||
}
|
||||
return dest.server;
|
||||
}
|
||||
|
||||
export const ApplicationTiles = ({applications, syncApplication, refreshApplication, deleteApplication}: ApplicationTilesProps) => (
|
||||
<Consumer>
|
||||
{ctx => (
|
||||
|
|
@ -102,7 +109,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
|
|||
)}
|
||||
<div className='row'>
|
||||
<div className='columns small-3'>Destination:</div>
|
||||
<div className='columns small-9'>{app.spec.destination.server}</div>
|
||||
<div className='columns small-9'>{getDestination(app.spec.destination)}</div>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='columns small-3'>Namespace:</div>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const ClusterDetails = (props: RouteComponentProps<{server: string}>) =>
|
|||
const loaderRef = React.useRef<DataLoader>();
|
||||
const [updating, setUpdating] = React.useState(false);
|
||||
return (
|
||||
<DataLoader ref={loaderRef} input={server} load={(url: string) => Observable.interval(1000).flatMap(() => Observable.fromPromise(services.clusters.get(url)))}>
|
||||
<DataLoader ref={loaderRef} input={server} load={(url: string) => Observable.interval(1000).flatMap(() => Observable.fromPromise(services.clusters.get(url, '')))}>
|
||||
{(cluster: Cluster) => (
|
||||
<Page
|
||||
title={server}
|
||||
|
|
@ -55,7 +55,7 @@ export const ClusterDetails = (props: RouteComponentProps<{server: string}>) =>
|
|||
<EditablePanel
|
||||
values={cluster}
|
||||
save={async updated => {
|
||||
const item = await services.clusters.get(updated.server);
|
||||
const item = await services.clusters.get(updated.server, '');
|
||||
item.name = updated.name;
|
||||
item.namespaces = updated.namespaces;
|
||||
loaderRef.current.setData(await services.clusters.update(item));
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ const clusterHTML = (cluster: models.Cluster, showUrl: boolean) => {
|
|||
);
|
||||
};
|
||||
|
||||
async function getCluster(clusters: Promise<models.Cluster[]>, server: string): Promise<models.Cluster> {
|
||||
async function getCluster(clusters: Promise<models.Cluster[]>, server: string, name: string): Promise<models.Cluster> {
|
||||
let cluster: models.Cluster;
|
||||
if (clusters) {
|
||||
cluster = await clusters.then(items => items.find(item => item.server === server));
|
||||
cluster = await clusters.then(items => items.find(item => item.server === server || item.name === name));
|
||||
} else {
|
||||
try {
|
||||
cluster = await services.clusters.get(server);
|
||||
cluster = await services.clusters.get(server, name);
|
||||
} catch {
|
||||
cluster = null;
|
||||
}
|
||||
|
|
@ -43,10 +43,10 @@ async function getCluster(clusters: Promise<models.Cluster[]>, server: string):
|
|||
|
||||
export const ClusterCtx = React.createContext<Promise<Array<models.Cluster>>>(null);
|
||||
|
||||
export const Cluster = (props: {server: string; showUrl?: boolean}) => (
|
||||
export const Cluster = (props: {server: string; name?: string; showUrl?: boolean}) => (
|
||||
<ClusterCtx.Consumer>
|
||||
{clusters => (
|
||||
<DataLoader input={props.server} loadingRenderer={() => <span>{props.server}</span>} load={server => getCluster(clusters, server)}>
|
||||
<DataLoader input={props} loadingRenderer={() => <span>{props.server}</span>} load={input => getCluster(clusters, input.server, input.name)}>
|
||||
{(cluster: models.Cluster) => clusterHTML(cluster, props.showUrl)}
|
||||
</DataLoader>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,10 @@ export interface ApplicationDestination {
|
|||
* Namespace overrides the environment namespace value in the ksonnet app.yaml
|
||||
*/
|
||||
namespace: string;
|
||||
/**
|
||||
* Name of the destination cluster which can be used instead of server (url) field
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface OrphanedResource {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,14 @@ export class ClustersService {
|
|||
.then(list => list.items || []);
|
||||
}
|
||||
|
||||
public get(url: string): Promise<models.Cluster> {
|
||||
return requests.get(`/clusters/${encodeURIComponent(url)}`).then(res => res.body as models.Cluster);
|
||||
public get(url: string, name: string): Promise<models.Cluster> {
|
||||
let queryName = '';
|
||||
if (url === undefined) {
|
||||
url = '';
|
||||
queryName = `?name=${name}`;
|
||||
}
|
||||
const requestUrl = `/clusters/${encodeURIComponent(url)}` + queryName;
|
||||
return requests.get(requestUrl).then(res => res.body as models.Cluster);
|
||||
}
|
||||
|
||||
public update(cluster: models.Cluster): Promise<models.Cluster> {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
var (
|
||||
localCluster = appv1.Cluster{
|
||||
Name: "in-cluster",
|
||||
Server: common.KubernetesInternalAPIServerAddr,
|
||||
ConnectionState: appv1.ConnectionState{Status: appv1.ConnectionStatusSuccessful},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ func TestWatchClusters_LocalClusterModifications(t *testing.T) {
|
|||
},
|
||||
func(old *v1alpha1.Cluster, new *v1alpha1.Cluster) {
|
||||
assert.Equal(t, new.Server, common.KubernetesInternalAPIServerAddr)
|
||||
assert.Equal(t, new.Name, "")
|
||||
assert.Equal(t, new.Name, "in-cluster")
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue