Add authuser param to GKE cluster link formatter in k8s plugin

Signed-off-by: Tomasz Szuba <tszuba@box.com>
This commit is contained in:
Tomasz Szuba
2023-12-13 17:36:30 +01:00
parent 8b13af849a
commit d5d2c67ca8
5 changed files with 212 additions and 157 deletions
+8
View File
@@ -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(),
};
+1 -1
View File
@@ -103,7 +103,7 @@ export const kubernetesPlugin = createPlugin({
}),
createApiFactory({
api: kubernetesClusterLinkFormatterApiRef,
deps: {},
deps: { googleAuthApi: googleAuthApiRef },
factory: deps => {
const formatters = getDefaultFormatters(deps);
return new KubernetesClusterLinkFormatter({