Merge pull request #8182 from livetocode/feature/addOpenshiftUrlFormatter
implement openshiftFormatter
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
---
|
||||
|
||||
Implement support for formatting OpenShift dashboard url links
|
||||
@@ -189,6 +189,7 @@ oidc
|
||||
Okta
|
||||
onboarding
|
||||
Onboarding
|
||||
OpenShift
|
||||
orgs
|
||||
pagerduty
|
||||
pageview
|
||||
|
||||
@@ -78,21 +78,6 @@ describe('clusterLinks', () => {
|
||||
'https://k8s.foo.com/#/service/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment properly url encoded', () => {
|
||||
const url = formatClusterLink({
|
||||
dashboardUrl: 'https://k8s.foo.com/',
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(url).toBe(
|
||||
'https://k8s.foo.com/#/deployment/bar%20bar/foobar?namespace=bar+bar',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('standard app', () => {
|
||||
|
||||
@@ -40,8 +40,5 @@ export function formatClusterLink(options: FormatClusterLinkOptions) {
|
||||
object: options.object,
|
||||
kind: options.kind,
|
||||
});
|
||||
// Note that we can't rely on 'url.href' since it will put the search before the hash
|
||||
// and this won't be properly recognized by SPAs such as Angular in the standard dashboard.
|
||||
// Note also that pathname, hash and search will be properly url encoded.
|
||||
return `${url.origin}${url.pathname}${url.hash}${url.search}`;
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
@@ -17,19 +17,126 @@ import { openshiftFormatter } from './openshift';
|
||||
|
||||
describe('clusterLinks - OpenShift formatter', () => {
|
||||
it('should return an url on the workloads when there is a namespace only', () => {
|
||||
expect(() =>
|
||||
openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
namespace: 'bar',
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).toThrowError(
|
||||
'OpenShift formatter is not yet implemented. Please, contribute!',
|
||||
},
|
||||
kind: 'foo',
|
||||
});
|
||||
expect(url.href).toBe('https://k8s.foo.com/k8s/cluster/projects/bar');
|
||||
});
|
||||
it('should return an url on the workloads when the kind is not recognizeed', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'UnknownKind',
|
||||
});
|
||||
expect(url.href).toBe('https://k8s.foo.com/k8s/cluster/projects/bar');
|
||||
});
|
||||
it('should return an url on the deployment', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(url.href).toBe('https://k8s.foo.com/k8s/ns/bar/deployments/foobar');
|
||||
});
|
||||
it('should return an url on the deployment and keep the path prefix 1', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/some/prefix/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://k8s.foo.com/some/prefix/k8s/ns/bar/deployments/foobar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment and keep the path prefix 2', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/some/prefix'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://k8s.foo.com/some/prefix/k8s/ns/bar/deployments/foobar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the service', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Service',
|
||||
});
|
||||
expect(url.href).toBe('https://k8s.foo.com/k8s/ns/bar/services/foobar');
|
||||
});
|
||||
it('should return an url on the ingress', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Ingress',
|
||||
});
|
||||
expect(url.href).toBe('https://k8s.foo.com/k8s/ns/bar/ingresses/foobar');
|
||||
});
|
||||
it('should return an url on the deployment for a hpa', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'HorizontalPodAutoscaler',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://k8s.foo.com/k8s/ns/bar/horizontalpodautoscalers/foobar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the PV', () => {
|
||||
const url = openshiftFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
},
|
||||
},
|
||||
kind: 'PersistentVolume',
|
||||
});
|
||||
expect(url.href).toBe(
|
||||
'https://k8s.foo.com/k8s/cluster/persistentvolumes/foobar',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,10 +15,40 @@
|
||||
*/
|
||||
import { ClusterLinksFormatterOptions } from '../../../types/types';
|
||||
|
||||
export function openshiftFormatter(
|
||||
_options: ClusterLinksFormatterOptions,
|
||||
): URL {
|
||||
throw new Error(
|
||||
'OpenShift formatter is not yet implemented. Please, contribute!',
|
||||
const kindMappings: Record<string, string> = {
|
||||
deployment: 'deployments',
|
||||
ingress: 'ingresses',
|
||||
service: 'services',
|
||||
horizontalpodautoscaler: 'horizontalpodautoscalers',
|
||||
persistentvolume: 'persistentvolumes',
|
||||
};
|
||||
|
||||
export function openshiftFormatter(options: ClusterLinksFormatterOptions): URL {
|
||||
const basePath = new URL(options.dashboardUrl.href);
|
||||
const name = encodeURIComponent(options.object.metadata?.name ?? '');
|
||||
const namespace = encodeURIComponent(
|
||||
options.object.metadata?.namespace ?? '',
|
||||
);
|
||||
const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')];
|
||||
if (!basePath.pathname.endsWith('/')) {
|
||||
// a dashboard url with a path should end with a slash otherwise
|
||||
// the new combined URL will replace the last segment with the appended path!
|
||||
// https://foobar.com/abc/def + k8s/cluster/projects/test --> https://foobar.com/abc/k8s/cluster/projects/test
|
||||
// https://foobar.com/abc/def/ + k8s/cluster/projects/test --> https://foobar.com/abc/def/k8s/cluster/projects/test
|
||||
basePath.pathname += '/';
|
||||
}
|
||||
let path = '';
|
||||
if (namespace) {
|
||||
if (name && validKind) {
|
||||
path = `k8s/ns/${namespace}/${validKind}/${name}`;
|
||||
} else {
|
||||
path = `k8s/cluster/projects/${namespace}`;
|
||||
}
|
||||
} else if (validKind) {
|
||||
path = `k8s/cluster/${validKind}`;
|
||||
if (name) {
|
||||
path += `/${name}`;
|
||||
}
|
||||
}
|
||||
return new URL(path, basePath);
|
||||
}
|
||||
|
||||
@@ -23,14 +23,24 @@ const kindMappings: Record<string, string> = {
|
||||
};
|
||||
|
||||
export function rancherFormatter(options: ClusterLinksFormatterOptions): URL {
|
||||
const result = new URL(options.dashboardUrl.href);
|
||||
const name = options.object.metadata?.name;
|
||||
const namespace = options.object.metadata?.namespace;
|
||||
const basePath = new URL(options.dashboardUrl.href);
|
||||
const name = encodeURIComponent(options.object.metadata?.name ?? '');
|
||||
const namespace = encodeURIComponent(
|
||||
options.object.metadata?.namespace ?? '',
|
||||
);
|
||||
const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')];
|
||||
if (validKind && name && namespace) {
|
||||
result.pathname += `explorer/${validKind}/${namespace}/${name}`;
|
||||
} else if (namespace) {
|
||||
result.pathname += 'explorer/workload';
|
||||
if (!basePath.pathname.endsWith('/')) {
|
||||
// a dashboard url with a path should end with a slash otherwise
|
||||
// the new combined URL will replace the last segment with the appended path!
|
||||
// https://foobar.com/abc/def + explorer/service/test --> https://foobar.com/abc/explorer/service/test
|
||||
// https://foobar.com/abc/def/ + explorer/service/test --> https://foobar.com/abc/def/explorer/service/test
|
||||
basePath.pathname += '/';
|
||||
}
|
||||
return result;
|
||||
let path = '';
|
||||
if (validKind && name && namespace) {
|
||||
path = `explorer/${validKind}/${namespace}/${name}`;
|
||||
} else if (namespace) {
|
||||
path = 'explorer/workload';
|
||||
}
|
||||
return new URL(path, basePath);
|
||||
}
|
||||
|
||||
@@ -67,6 +67,51 @@ describe('clusterLinks - standard formatter', () => {
|
||||
'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment with a prefix 1', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/some/prefix'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/some/prefix/#/deployment/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment with a prefix 2', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/some/prefix/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/some/prefix/#/deployment/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment properly url encoded', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/#/deployment/bar%20bar/foobar?namespace=bar%20bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the service', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
|
||||
@@ -24,16 +24,22 @@ const kindMappings: Record<string, string> = {
|
||||
|
||||
export function standardFormatter(options: ClusterLinksFormatterOptions) {
|
||||
const result = new URL(options.dashboardUrl.href);
|
||||
const name = options.object.metadata?.name;
|
||||
const namespace = options.object.metadata?.namespace;
|
||||
const name = encodeURIComponent(options.object.metadata?.name ?? '');
|
||||
const namespace = encodeURIComponent(
|
||||
options.object.metadata?.namespace ?? '',
|
||||
);
|
||||
const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')];
|
||||
if (namespace) {
|
||||
result.searchParams.set('namespace', namespace);
|
||||
if (!result.pathname.endsWith('/')) {
|
||||
result.pathname += '/';
|
||||
}
|
||||
if (validKind && name && namespace) {
|
||||
result.hash = `/${validKind}/${namespace}/${name}`;
|
||||
} else if (namespace) {
|
||||
result.hash = '/workloads';
|
||||
}
|
||||
if (namespace) {
|
||||
// Note that Angular SPA requires a hash and the query parameter should be part of it
|
||||
result.hash += `?namespace=${namespace}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user