Add authuser param to GKE cluster link formatter in k8s plugin
Signed-off-by: Tomasz Szuba <tszuba@box.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-react': patch
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
---
|
||||
|
||||
Add `authuser` search parameter to GKE cluster link formatter in k8s plugin
|
||||
|
||||
Thanks to this, people with multiple simultaneously logged-in accounts to be automatically picked.
|
||||
@@ -17,25 +17,120 @@
|
||||
import { GkeClusterLinksFormatter } from './GkeClusterLinksFormatter';
|
||||
|
||||
describe('clusterLinks - GKE formatter', () => {
|
||||
const formatter = new GkeClusterLinksFormatter();
|
||||
describe('without googleAuthApi provided', () => {
|
||||
const formatter = new GkeClusterLinksFormatter(undefined);
|
||||
|
||||
it('should provide a dashboardParameters in the options', async () => {
|
||||
await expect(
|
||||
formatter.formatClusterLink({
|
||||
it('should provide a dashboardParameters in the options', async () => {
|
||||
await expect(
|
||||
formatter.formatClusterLink({
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow('GKE dashboard requires a dashboardParameters option');
|
||||
});
|
||||
it('should provide a projectId in the dashboardParameters options', async () => {
|
||||
await expect(
|
||||
formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
'GKE dashboard requires a "projectId" of type string in the dashboardParameters option',
|
||||
);
|
||||
});
|
||||
it('should provide a region in the dashboardParameters options', async () => {
|
||||
await expect(
|
||||
formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
'GKE dashboard requires a "region" of type string in the dashboardParameters option',
|
||||
);
|
||||
});
|
||||
it('should provide a clusterName in the dashboardParameters options', async () => {
|
||||
await expect(() =>
|
||||
formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
'GKE dashboard requires a "clusterName" of type string in the dashboardParameters option',
|
||||
);
|
||||
});
|
||||
it('should return an url on the cluster when there is a namespace only', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'foo',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/clusters/details/us-east1-c/cluster-1/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the cluster when the kind is not recognized', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow('GKE dashboard requires a dashboardParameters option');
|
||||
});
|
||||
it('should provide a projectId in the dashboardParameters options', async () => {
|
||||
await expect(
|
||||
formatter.formatClusterLink({
|
||||
kind: 'UnknownKind',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/clusters/details/us-east1-c/cluster-1/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
@@ -46,16 +141,17 @@ describe('clusterLinks - GKE formatter', () => {
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
'GKE dashboard requires a "projectId" of type string in the dashboardParameters option',
|
||||
);
|
||||
});
|
||||
it('should provide a region in the dashboardParameters options', async () => {
|
||||
await expect(
|
||||
formatter.formatClusterLink({
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/deployment/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return an url on the service', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
@@ -64,18 +160,18 @@ describe('clusterLinks - GKE formatter', () => {
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
'GKE dashboard requires a "region" of type string in the dashboardParameters option',
|
||||
);
|
||||
});
|
||||
it('should provide a clusterName in the dashboardParameters options', async () => {
|
||||
await expect(() =>
|
||||
formatter.formatClusterLink({
|
||||
kind: 'Service',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/service/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the pod', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
@@ -83,14 +179,53 @@ describe('clusterLinks - GKE formatter', () => {
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
'GKE dashboard requires a "clusterName" of type string in the dashboardParameters option',
|
||||
);
|
||||
kind: 'Pod',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/pod/us-east1-c/cluster-1/bar/foobar/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the ingress', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Ingress',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/ingress/us-east1-c/cluster-1/bar/foobar/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment for a hpa', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'HorizontalPodAutoscaler',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/deployment/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
});
|
||||
it('should return an url on the cluster when there is a namespace only', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
describe('with googleAuthApi provided', () => {
|
||||
const object = {
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
@@ -102,124 +237,28 @@ describe('clusterLinks - GKE formatter', () => {
|
||||
},
|
||||
},
|
||||
kind: 'foo',
|
||||
};
|
||||
it('should not fail, when undefined is returned as profile', async () => {
|
||||
const formatter = new GkeClusterLinksFormatter({
|
||||
getProfile: async _ => undefined,
|
||||
});
|
||||
const url = await formatter.formatClusterLink(object);
|
||||
expect(url.searchParams.has('authuser')).toBeFalsy();
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/clusters/details/us-east1-c/cluster-1/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the cluster when the kind is not recognizeed', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'UnknownKind',
|
||||
it('should not fail, when profile returns no email', async () => {
|
||||
const formatter = new GkeClusterLinksFormatter({
|
||||
getProfile: async _ => ({ email: undefined }),
|
||||
});
|
||||
const url = await formatter.formatClusterLink(object);
|
||||
expect(url.searchParams.has('authuser')).toBeFalsy();
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/clusters/details/us-east1-c/cluster-1/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
it('should add authuser with email when provided', async () => {
|
||||
const email = 'email@example.com';
|
||||
const formatter = new GkeClusterLinksFormatter({
|
||||
getProfile: async _ => ({ email }),
|
||||
});
|
||||
const url = await formatter.formatClusterLink(object);
|
||||
expect(url.searchParams.get('authuser')).toBe(email);
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/deployment/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return an url on the service', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Service',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/service/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the pod', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Pod',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/pod/us-east1-c/cluster-1/bar/foobar/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the ingress', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Ingress',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/ingress/us-east1-c/cluster-1/bar/foobar/details?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment for a hpa', async () => {
|
||||
const url = await formatter.formatClusterLink({
|
||||
dashboardParameters: {
|
||||
projectId: 'foobar-333614',
|
||||
region: 'us-east1-c',
|
||||
clusterName: 'cluster-1',
|
||||
},
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'HorizontalPodAutoscaler',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://console.cloud.google.com/kubernetes/deployment/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
ClusterLinksFormatter,
|
||||
ClusterLinksFormatterOptions,
|
||||
} from '../../types';
|
||||
import { ProfileInfoApi } from '@backstage/core-plugin-api';
|
||||
|
||||
const kindMappings: Record<string, string> = {
|
||||
deployment: 'deployment',
|
||||
@@ -28,6 +29,7 @@ const kindMappings: Record<string, string> = {
|
||||
|
||||
/** @public */
|
||||
export class GkeClusterLinksFormatter implements ClusterLinksFormatter {
|
||||
constructor(private readonly googleAuthApi: ProfileInfoApi | undefined) {}
|
||||
async formatClusterLink(options: ClusterLinksFormatterOptions): Promise<URL> {
|
||||
if (!options.dashboardParameters) {
|
||||
throw new Error('GKE dashboard requires a dashboardParameters option');
|
||||
@@ -68,6 +70,12 @@ export class GkeClusterLinksFormatter implements ClusterLinksFormatter {
|
||||
}
|
||||
const result = new URL(path, basePath);
|
||||
result.searchParams.set('project', args.projectId);
|
||||
if (this.googleAuthApi) {
|
||||
const profile = await this.googleAuthApi.getProfile({ optional: true });
|
||||
if (profile?.email) {
|
||||
result.searchParams.set('authuser', profile.email);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { GkeClusterLinksFormatter } from './GkeClusterLinksFormatter';
|
||||
import { StandardClusterLinksFormatter } from './StandardClusterLinksFormatter';
|
||||
import { OpenshiftClusterLinksFormatter } from './OpenshiftClusterLinksFormatter';
|
||||
import { RancherClusterLinksFormatter } from './RancherClusterLinksFormatter';
|
||||
import { ProfileInfoApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export {
|
||||
StandardClusterLinksFormatter,
|
||||
@@ -35,15 +36,14 @@ export {
|
||||
export const DEFAULT_FORMATTER_NAME = 'standard';
|
||||
|
||||
/** @public */
|
||||
export function getDefaultFormatters(_deps: {}): Record<
|
||||
string,
|
||||
ClusterLinksFormatter
|
||||
> {
|
||||
export function getDefaultFormatters(deps: {
|
||||
googleAuthApi: ProfileInfoApi;
|
||||
}): Record<string, ClusterLinksFormatter> {
|
||||
return {
|
||||
standard: new StandardClusterLinksFormatter(),
|
||||
aks: new AksClusterLinksFormatter(),
|
||||
eks: new EksClusterLinksFormatter(),
|
||||
gke: new GkeClusterLinksFormatter(),
|
||||
gke: new GkeClusterLinksFormatter(deps.googleAuthApi),
|
||||
openshift: new OpenshiftClusterLinksFormatter(),
|
||||
rancher: new RancherClusterLinksFormatter(),
|
||||
};
|
||||
|
||||
@@ -103,7 +103,7 @@ export const kubernetesPlugin = createPlugin({
|
||||
}),
|
||||
createApiFactory({
|
||||
api: kubernetesClusterLinkFormatterApiRef,
|
||||
deps: {},
|
||||
deps: { googleAuthApi: googleAuthApiRef },
|
||||
factory: deps => {
|
||||
const formatters = getDefaultFormatters(deps);
|
||||
return new KubernetesClusterLinkFormatter({
|
||||
|
||||
Reference in New Issue
Block a user