Merge pull request #6782 from livetocode/feature/k8s-dashboard-links
provide access to the Kubernetes dashboard when viewing a specific resource
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
'@backstage/plugin-kubernetes-common': patch
|
||||
---
|
||||
|
||||
Provide access to the Kubernetes dashboard when viewing a specific resource
|
||||
@@ -27,6 +27,8 @@ kubernetes:
|
||||
authProvider: 'serviceAccount'
|
||||
skipTLSVerify: false
|
||||
serviceAccountToken: ${K8S_MINIKUBE_TOKEN}
|
||||
dashboardUrl: http://127.0.0.1:64713 # url copied from running the command: minikube service kubernetes-dashboard -n kubernetes-dashboard
|
||||
dashboardApp: standard
|
||||
- url: http://127.0.0.2:9999
|
||||
name: aws-cluster-1
|
||||
authProvider: 'aws'
|
||||
@@ -98,6 +100,41 @@ kubectl -n <NAMESPACE> get secret $(kubectl -n <NAMESPACE> get sa <SERVICE_ACCOU
|
||||
| base64 --decode
|
||||
```
|
||||
|
||||
##### `clusters.\*.dashboardUrl` (optional)
|
||||
|
||||
Specifies the link to the Kubernetes dashboard managing this cluster.
|
||||
|
||||
Note that you should specify the app used for the dashboard using the
|
||||
**dashboardApp property**, in order to properly format links to kubernetes
|
||||
resources, otherwise it will assume that you're running the standard one.
|
||||
|
||||
##### `clusters.\*.dashboardApp` (optional)
|
||||
|
||||
Specifies the app that provides the Kubernetes dashboard.
|
||||
|
||||
This will be used for formatting links to kubernetes objects inside the
|
||||
dashboard.
|
||||
|
||||
The supported dashboards are: `standard`, `rancher`, `openshift`, `gke`, `aks`,
|
||||
`eks` However, not all of them are implemented yet, so please contribute!
|
||||
|
||||
Note that it will default to the regular dashboard provided by the Kubernetes
|
||||
project (`standard`), that can run in any Kubernetes cluster.
|
||||
|
||||
Note that you can add your own formatter by registering it to the
|
||||
`clusterLinksFormatters` dictionary, in the app project.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { clusterLinksFormatters } from '@backstage/plugin-kubernetes';
|
||||
clusterLinksFormatters.myDashboard = (options) => ...;
|
||||
```
|
||||
|
||||
See also
|
||||
https://github.com/backstage/backstage/tree/master/plugins/kubernetes/src/utils/clusterLinks/formatters
|
||||
for real examples.
|
||||
|
||||
#### `gke`
|
||||
|
||||
This cluster locator is designed to work with Kubernetes clusters running in
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { ClusterObjects } from '@backstage/plugin-kubernetes-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import express from 'express';
|
||||
import { FetchResponse } from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesFetchError } from '@backstage/plugin-kubernetes-common';
|
||||
import type { FetchResponse } from '@backstage/plugin-kubernetes-common';
|
||||
import type { KubernetesFetchError } from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import { Logger as Logger_2 } from 'winston';
|
||||
|
||||
@@ -26,7 +27,8 @@ export interface AWSClusterDetails extends ClusterDetails {
|
||||
export interface ClusterDetails {
|
||||
// (undocumented)
|
||||
authProvider: string;
|
||||
// (undocumented)
|
||||
dashboardApp?: string;
|
||||
dashboardUrl?: string;
|
||||
name: string;
|
||||
// (undocumented)
|
||||
serviceAccountToken?: string | undefined;
|
||||
|
||||
@@ -66,6 +66,7 @@ describe('ConfigClusterLocator', () => {
|
||||
url: 'http://localhost:8080',
|
||||
authProvider: 'serviceAccount',
|
||||
skipTLSVerify: false,
|
||||
dashboardUrl: 'https://k8s.foo.com',
|
||||
},
|
||||
{
|
||||
name: 'cluster2',
|
||||
@@ -83,6 +84,7 @@ describe('ConfigClusterLocator', () => {
|
||||
expect(result).toStrictEqual([
|
||||
{
|
||||
name: 'cluster1',
|
||||
dashboardUrl: 'https://k8s.foo.com',
|
||||
serviceAccountToken: 'token',
|
||||
url: 'http://localhost:8080',
|
||||
authProvider: 'serviceAccount',
|
||||
|
||||
@@ -30,13 +30,21 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier {
|
||||
return new ConfigClusterLocator(
|
||||
config.getConfigArray('clusters').map(c => {
|
||||
const authProvider = c.getString('authProvider');
|
||||
const clusterDetails = {
|
||||
const clusterDetails: ClusterDetails = {
|
||||
name: c.getString('name'),
|
||||
url: c.getString('url'),
|
||||
serviceAccountToken: c.getOptionalString('serviceAccountToken'),
|
||||
skipTLSVerify: c.getOptionalBoolean('skipTLSVerify') ?? false,
|
||||
authProvider: authProvider,
|
||||
};
|
||||
const dashboardUrl = c.getOptionalString('dashboardUrl');
|
||||
if (dashboardUrl) {
|
||||
clusterDetails.dashboardUrl = dashboardUrl;
|
||||
}
|
||||
const dashboardApp = c.getOptionalString('dashboardApp');
|
||||
if (dashboardApp) {
|
||||
clusterDetails.dashboardApp = dashboardApp;
|
||||
}
|
||||
|
||||
switch (authProvider) {
|
||||
case 'google': {
|
||||
|
||||
@@ -211,6 +211,7 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
{
|
||||
name: 'test-cluster',
|
||||
authProvider: 'serviceAccount',
|
||||
dashboardUrl: 'https://k8s.foo.coom',
|
||||
},
|
||||
{
|
||||
name: 'other-cluster',
|
||||
@@ -260,6 +261,7 @@ describe('handleGetKubernetesObjectsForService', () => {
|
||||
items: [
|
||||
{
|
||||
cluster: {
|
||||
dashboardUrl: 'https://k8s.foo.coom',
|
||||
name: 'test-cluster',
|
||||
},
|
||||
errors: [],
|
||||
|
||||
@@ -22,7 +22,10 @@ import {
|
||||
KubernetesObjectTypes,
|
||||
KubernetesServiceLocator,
|
||||
} from '../types/types';
|
||||
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import {
|
||||
ClusterObjects,
|
||||
KubernetesRequestBody,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types';
|
||||
import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator';
|
||||
|
||||
@@ -111,13 +114,20 @@ export class KubernetesFanOutHandler {
|
||||
customResources: this.customResources,
|
||||
})
|
||||
.then(result => {
|
||||
return {
|
||||
const objects: ClusterObjects = {
|
||||
cluster: {
|
||||
name: clusterDetailsItem.name,
|
||||
},
|
||||
resources: result.responses,
|
||||
errors: result.errors,
|
||||
};
|
||||
if (clusterDetailsItem.dashboardUrl) {
|
||||
objects.cluster.dashboardUrl = clusterDetailsItem.dashboardUrl;
|
||||
}
|
||||
if (clusterDetailsItem.dashboardApp) {
|
||||
objects.cluster.dashboardApp = clusterDetailsItem.dashboardApp;
|
||||
}
|
||||
return objects;
|
||||
});
|
||||
}),
|
||||
).then(r => ({
|
||||
|
||||
@@ -86,6 +86,7 @@ export const makeRouter = (
|
||||
res.json({
|
||||
items: clusterDetails.map(cd => ({
|
||||
name: cd.name,
|
||||
dashboardUrl: cd.dashboardUrl,
|
||||
authProvider: cd.authProvider,
|
||||
})),
|
||||
});
|
||||
|
||||
@@ -75,11 +75,39 @@ export interface KubernetesServiceLocator {
|
||||
export type ServiceLocatorMethod = 'multiTenant' | 'http'; // TODO implement http
|
||||
|
||||
export interface ClusterDetails {
|
||||
/**
|
||||
* Specifies the name of the Kubernetes cluster.
|
||||
*/
|
||||
name: string;
|
||||
url: string;
|
||||
authProvider: string;
|
||||
serviceAccountToken?: string | undefined;
|
||||
skipTLSVerify?: boolean;
|
||||
/**
|
||||
* Specifies the link to the Kubernetes dashboard managing this cluster.
|
||||
* @remarks
|
||||
* Note that you should specify the app used for the dashboard
|
||||
* using the dashboardApp property, in order to properly format
|
||||
* links to kubernetes resources, otherwise it will assume that you're running the standard one.
|
||||
* @see dashboardApp
|
||||
*/
|
||||
dashboardUrl?: string;
|
||||
/**
|
||||
* Specifies the app that provides the Kubernetes dashboard.
|
||||
* This will be used for formatting links to kubernetes objects inside the dashboard.
|
||||
* @remarks
|
||||
* The existing apps are: standard, rancher, openshift, gke, aks, eks
|
||||
* Note that it will default to the regular dashboard provided by the Kubernetes project (standard).
|
||||
* Note that you can add your own formatter by registering it to the clusterLinksFormatters dictionary.
|
||||
* @defaultValue standard
|
||||
* @see dashboardUrl
|
||||
* @example
|
||||
* ```ts
|
||||
* import { clusterLinksFormatters } from '@backstage/plugin-kubernetes';
|
||||
* clusterLinksFormatters.myDashboard = (options) => ...;
|
||||
* ```
|
||||
*/
|
||||
dashboardApp?: string;
|
||||
}
|
||||
|
||||
export interface GKEClusterDetails extends ClusterDetails {}
|
||||
|
||||
@@ -17,14 +17,21 @@ import { V1Service } from '@kubernetes/client-node';
|
||||
// @public (undocumented)
|
||||
export type AuthProviderType = 'google' | 'serviceAccount' | 'aws';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ClusterAttributes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface ClusterAttributes {
|
||||
dashboardApp?: string;
|
||||
dashboardUrl?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ClusterObjects" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface ClusterObjects {
|
||||
// (undocumented)
|
||||
cluster: {
|
||||
name: string;
|
||||
};
|
||||
cluster: ClusterAttributes;
|
||||
// (undocumented)
|
||||
errors: KubernetesFetchError[];
|
||||
// (undocumented)
|
||||
|
||||
@@ -32,8 +32,40 @@ export interface KubernetesRequestBody {
|
||||
entity: Entity;
|
||||
}
|
||||
|
||||
export interface ClusterAttributes {
|
||||
/**
|
||||
* Specifies the name of the Kubernetes cluster.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Specifies the link to the Kubernetes dashboard managing this cluster.
|
||||
* @remarks
|
||||
* Note that you should specify the app used for the dashboard
|
||||
* using the dashboardApp property, in order to properly format
|
||||
* links to kubernetes resources, otherwise it will assume that you're running the standard one.
|
||||
* @see dashboardApp
|
||||
*/
|
||||
dashboardUrl?: string;
|
||||
/**
|
||||
* Specifies the app that provides the Kubernetes dashboard.
|
||||
* This will be used for formatting links to kubernetes objects inside the dashboard.
|
||||
* @remarks
|
||||
* The supported dashboards are: standard, rancher, openshift, gke, aks, eks
|
||||
* Note that it will default to the regular dashboard provided by the Kubernetes project (standard).
|
||||
* Note that you can add your own formatter by registering it to the clusterLinksFormatters dictionary.
|
||||
* @defaultValue standard
|
||||
* @see dashboardUrl
|
||||
* @example
|
||||
* ```ts
|
||||
* import { clusterLinksFormatters } from '@backstage/plugin-kubernetes';
|
||||
* clusterLinksFormatters.myDashboard = (options) => ...;
|
||||
* ```
|
||||
*/
|
||||
dashboardApp?: string;
|
||||
}
|
||||
|
||||
export interface ClusterObjects {
|
||||
cluster: { name: string };
|
||||
cluster: ClusterAttributes;
|
||||
resources: FetchResponse[];
|
||||
errors: KubernetesFetchError[];
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@ import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
|
||||
import { OAuthApi } from '@backstage/core-plugin-api';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "ClusterLinksFormatter" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "clusterLinksFormatters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const clusterLinksFormatters: Record<string, ClusterLinksFormatter>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "EntityKubernetesContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -19,6 +25,14 @@ export const EntityKubernetesContent: (_props: {
|
||||
entity?: Entity | undefined;
|
||||
}) => JSX.Element;
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "FormatClusterLinkOptions" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "formatClusterLink" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function formatClusterLink(
|
||||
options: FormatClusterLinkOptions,
|
||||
): string | undefined;
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "KubernetesAuthProvidersApi" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "KubernetesAuthProviders" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
||||
@@ -36,6 +36,7 @@ import { ServicesAccordions } from '../ServicesAccordions';
|
||||
import { CustomResources } from '../CustomResources';
|
||||
import EmptyStateImage from '../../assets/emptystate.svg';
|
||||
import {
|
||||
ClusterContext,
|
||||
GroupedResponsesContext,
|
||||
PodNamesWithErrorsContext,
|
||||
useKubernetesObjects,
|
||||
@@ -119,35 +120,37 @@ type ClusterProps = {
|
||||
const Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => {
|
||||
const groupedResponses = groupResponses(clusterObjects.resources);
|
||||
return (
|
||||
<GroupedResponsesContext.Provider value={groupedResponses}>
|
||||
<PodNamesWithErrorsContext.Provider value={podsWithErrors}>
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<ClusterSummary
|
||||
clusterName={clusterObjects.cluster.name}
|
||||
totalNumberOfPods={groupedResponses.pods.length}
|
||||
numberOfPodsWithErrors={podsWithErrors.size}
|
||||
/>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container direction="column">
|
||||
<Grid item>
|
||||
<CustomResources />
|
||||
<ClusterContext.Provider value={clusterObjects.cluster}>
|
||||
<GroupedResponsesContext.Provider value={groupedResponses}>
|
||||
<PodNamesWithErrorsContext.Provider value={podsWithErrors}>
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<ClusterSummary
|
||||
clusterName={clusterObjects.cluster.name}
|
||||
totalNumberOfPods={groupedResponses.pods.length}
|
||||
numberOfPodsWithErrors={podsWithErrors.size}
|
||||
/>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container direction="column">
|
||||
<Grid item>
|
||||
<CustomResources />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<DeploymentsAccordions />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<IngressesAccordions />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<ServicesAccordions />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<DeploymentsAccordions />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<IngressesAccordions />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<ServicesAccordions />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</PodNamesWithErrorsContext.Provider>
|
||||
</GroupedResponsesContext.Provider>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</PodNamesWithErrorsContext.Provider>
|
||||
</GroupedResponsesContext.Provider>
|
||||
</ClusterContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ChangeEvent, useState } from 'react';
|
||||
import React, { ChangeEvent, useContext, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
@@ -28,13 +28,20 @@ import {
|
||||
Grid,
|
||||
} from '@material-ui/core';
|
||||
import Close from '@material-ui/icons/Close';
|
||||
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
||||
import { V1ObjectMeta } from '@kubernetes/client-node';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import jsYaml from 'js-yaml';
|
||||
import {
|
||||
Button as BackstageButton,
|
||||
CodeSnippet,
|
||||
StructuredMetadataTable,
|
||||
WarningPanel,
|
||||
} from '@backstage/core-components';
|
||||
import { ClusterContext } from '../../hooks';
|
||||
import { formatClusterLink } from '../../utils/clusterLinks';
|
||||
import { ClusterAttributes } from '@backstage/plugin-kubernetes-common';
|
||||
import { FormatClusterLinkOptions } from '../../utils/clusterLinks/formatClusterLink';
|
||||
|
||||
const useDrawerStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -53,10 +60,14 @@ const useDrawerContentStyles = makeStyles((_: Theme) =>
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
errorMessage: {
|
||||
marginTop: '1em',
|
||||
marginBottom: '1em',
|
||||
},
|
||||
options: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
icon: {
|
||||
fontSize: 20,
|
||||
@@ -76,6 +87,27 @@ const PodDrawerButton = withStyles({
|
||||
},
|
||||
})(Button);
|
||||
|
||||
type ErrorPanelProps = {
|
||||
cluster: ClusterAttributes;
|
||||
errorMessage?: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const ErrorPanel = ({ cluster, errorMessage }: ErrorPanelProps) => (
|
||||
<WarningPanel
|
||||
title="There was a problem formatting the link to the Kubernetes dashboard"
|
||||
message={`Could not format the link to the dashboard of your cluster named '${
|
||||
cluster.name
|
||||
}'. Its dashboardApp property has been set to '${
|
||||
cluster.dashboardApp || 'standard'
|
||||
}.'`}
|
||||
>
|
||||
{errorMessage && (
|
||||
<Typography variant="body2">Errors: {errorMessage}</Typography>
|
||||
)}
|
||||
</WarningPanel>
|
||||
);
|
||||
|
||||
interface KubernetesDrawerable {
|
||||
metadata?: V1ObjectMeta;
|
||||
}
|
||||
@@ -96,6 +128,20 @@ function replaceNullsWithUndefined(someObj: any) {
|
||||
return JSON.parse(JSON.stringify(someObj, replacer));
|
||||
}
|
||||
|
||||
function tryFormatClusterLink(options: FormatClusterLinkOptions) {
|
||||
try {
|
||||
return {
|
||||
clusterLink: formatClusterLink(options),
|
||||
errorMessage: '',
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
clusterLink: '',
|
||||
errorMessage: err.message || err.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const KubernetesDrawerContent = <T extends KubernetesDrawerable>({
|
||||
toggleDrawer,
|
||||
object,
|
||||
@@ -105,6 +151,13 @@ const KubernetesDrawerContent = <T extends KubernetesDrawerable>({
|
||||
const [isYaml, setIsYaml] = useState<boolean>(false);
|
||||
|
||||
const classes = useDrawerContentStyles();
|
||||
const cluster = useContext(ClusterContext);
|
||||
const { clusterLink, errorMessage } = tryFormatClusterLink({
|
||||
dashboardUrl: cluster.dashboardUrl,
|
||||
dashboardApp: cluster.dashboardApp,
|
||||
object,
|
||||
kind,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -135,7 +188,25 @@ const KubernetesDrawerContent = <T extends KubernetesDrawerable>({
|
||||
<Close className={classes.icon} />
|
||||
</IconButton>
|
||||
</div>
|
||||
{errorMessage && (
|
||||
<div className={classes.errorMessage}>
|
||||
<ErrorPanel cluster={cluster} errorMessage={errorMessage} />
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.options}>
|
||||
<div>
|
||||
{clusterLink && (
|
||||
<BackstageButton
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
size="small"
|
||||
to={clusterLink}
|
||||
endIcon={<OpenInNewIcon />}
|
||||
>
|
||||
Open Kubernetes Dashboard
|
||||
</BackstageButton>
|
||||
)}
|
||||
</div>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ClusterAttributes } from '@backstage/plugin-kubernetes-common';
|
||||
|
||||
export const ClusterContext = React.createContext<ClusterAttributes>({
|
||||
name: '',
|
||||
});
|
||||
@@ -17,3 +17,4 @@
|
||||
export * from './useKubernetesObjects';
|
||||
export * from './PodNamesWithErrors';
|
||||
export * from './GroupedResponses';
|
||||
export * from './Cluster';
|
||||
|
||||
@@ -27,3 +27,4 @@ export {
|
||||
} from './plugin';
|
||||
export { Router } from './Router';
|
||||
export * from './kubernetes-auth-provider';
|
||||
export * from './utils/clusterLinks';
|
||||
|
||||
@@ -37,3 +37,13 @@ export interface GroupedResponses extends DeploymentResources {
|
||||
ingresses: ExtensionsV1beta1Ingress[];
|
||||
customResources: any[];
|
||||
}
|
||||
|
||||
export interface ClusterLinksFormatterOptions {
|
||||
dashboardUrl: URL;
|
||||
object: any;
|
||||
kind: string;
|
||||
}
|
||||
|
||||
export type ClusterLinksFormatter = (
|
||||
options: ClusterLinksFormatterOptions,
|
||||
) => URL;
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { formatClusterLink } from './formatClusterLink';
|
||||
|
||||
describe('clusterLinks', () => {
|
||||
describe('formatClusterLink', () => {
|
||||
it('should not return an url when there is no dashboard url', () => {
|
||||
const url = formatClusterLink({ object: {}, kind: 'foo' });
|
||||
expect(url).toBeUndefined();
|
||||
});
|
||||
it('should return an url even when there is no object', () => {
|
||||
const url = formatClusterLink({
|
||||
dashboardUrl: 'https://k8s.foo.com',
|
||||
object: undefined,
|
||||
kind: 'foo',
|
||||
});
|
||||
expect(url).toBe('https://k8s.foo.com');
|
||||
});
|
||||
it('should throw when the app is not recognized', () => {
|
||||
expect(() =>
|
||||
formatClusterLink({
|
||||
dashboardUrl: 'https://k8s.foo.com',
|
||||
dashboardApp: 'unknownapp',
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).toThrowError(
|
||||
"Could not find Kubernetes dashboard app named 'unknownapp'",
|
||||
);
|
||||
});
|
||||
|
||||
describe('default app', () => {
|
||||
it('should return an url on the deployment', () => {
|
||||
const url = formatClusterLink({
|
||||
dashboardUrl: 'https://k8s.foo.com/',
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(url).toBe(
|
||||
'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the service', () => {
|
||||
const url = formatClusterLink({
|
||||
dashboardUrl: 'https://k8s.foo.com/',
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Service',
|
||||
});
|
||||
expect(url).toBe(
|
||||
'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', () => {
|
||||
it('should return an url on the deployment', () => {
|
||||
const url = formatClusterLink({
|
||||
dashboardUrl: 'https://k8s.foo.com/',
|
||||
dashboardApp: 'standard',
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(url).toBe(
|
||||
'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the service', () => {
|
||||
const url = formatClusterLink({
|
||||
dashboardUrl: 'https://k8s.foo.com/',
|
||||
dashboardApp: 'standard',
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Service',
|
||||
});
|
||||
expect(url).toBe(
|
||||
'https://k8s.foo.com/#/service/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defaultFormatterName, clusterLinksFormatters } from './formatters';
|
||||
|
||||
export type FormatClusterLinkOptions = {
|
||||
dashboardUrl?: string;
|
||||
dashboardApp?: string;
|
||||
object: any;
|
||||
kind: string;
|
||||
};
|
||||
|
||||
export function formatClusterLink(options: FormatClusterLinkOptions) {
|
||||
if (!options.dashboardUrl) {
|
||||
return undefined;
|
||||
}
|
||||
if (!options.object) {
|
||||
return options.dashboardUrl;
|
||||
}
|
||||
const app = options.dashboardApp || defaultFormatterName;
|
||||
const formatter = clusterLinksFormatters[app];
|
||||
if (!formatter) {
|
||||
throw new Error(`Could not find Kubernetes dashboard app named '${app}'`);
|
||||
}
|
||||
const url = formatter({
|
||||
dashboardUrl: new URL(options.dashboardUrl),
|
||||
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}`;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { aksFormatter } from './aks';
|
||||
|
||||
describe('clusterLinks - AKS formatter', () => {
|
||||
it('should return an url on the workloads when there is a namespace only', () => {
|
||||
expect(() =>
|
||||
aksFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).toThrowError('AKS formatter is not yet implemented. Please, contribute!');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClusterLinksFormatterOptions } from '../../../types/types';
|
||||
|
||||
export function aksFormatter(_options: ClusterLinksFormatterOptions): URL {
|
||||
throw new Error('AKS formatter is not yet implemented. Please, contribute!');
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { eksFormatter } from './eks';
|
||||
|
||||
describe('clusterLinks - EKS formatter', () => {
|
||||
it('should return an url on the workloads when there is a namespace only', () => {
|
||||
expect(() =>
|
||||
eksFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).toThrowError('EKS formatter is not yet implemented. Please, contribute!');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClusterLinksFormatterOptions } from '../../../types/types';
|
||||
|
||||
export function eksFormatter(_options: ClusterLinksFormatterOptions): URL {
|
||||
throw new Error('EKS formatter is not yet implemented. Please, contribute!');
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { gkeFormatter } from './gke';
|
||||
|
||||
describe('clusterLinks - GKE formatter', () => {
|
||||
it('should return an url on the workloads when there is a namespace only', () => {
|
||||
expect(() =>
|
||||
gkeFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).toThrowError('GKE formatter is not yet implemented. Please, contribute!');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClusterLinksFormatterOptions } from '../../../types/types';
|
||||
|
||||
export function gkeFormatter(_options: ClusterLinksFormatterOptions): URL {
|
||||
throw new Error('GKE formatter is not yet implemented. Please, contribute!');
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClusterLinksFormatter } from '../../../types/types';
|
||||
import { standardFormatter } from './standard';
|
||||
import { rancherFormatter } from './rancher';
|
||||
import { openshiftFormatter } from './openshift';
|
||||
import { aksFormatter } from './aks';
|
||||
import { eksFormatter } from './eks';
|
||||
import { gkeFormatter } from './gke';
|
||||
|
||||
export const clusterLinksFormatters: Record<string, ClusterLinksFormatter> = {
|
||||
standard: standardFormatter,
|
||||
rancher: rancherFormatter,
|
||||
openshift: openshiftFormatter,
|
||||
aks: aksFormatter,
|
||||
eks: eksFormatter,
|
||||
gke: gkeFormatter,
|
||||
};
|
||||
export const defaultFormatterName = 'standard';
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
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',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).toThrowError(
|
||||
'OpenShift formatter is not yet implemented. Please, contribute!',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClusterLinksFormatterOptions } from '../../../types/types';
|
||||
|
||||
export function openshiftFormatter(
|
||||
_options: ClusterLinksFormatterOptions,
|
||||
): URL {
|
||||
throw new Error(
|
||||
'OpenShift formatter is not yet implemented. Please, contribute!',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { rancherFormatter } from './rancher';
|
||||
|
||||
describe('clusterLinks - Rancher formatter', () => {
|
||||
it('should return an url on the workloads when there is a namespace only', () => {
|
||||
expect(() =>
|
||||
rancherFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
}),
|
||||
).toThrowError(
|
||||
'Rancher formatter is not yet implemented. Please, contribute!',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClusterLinksFormatterOptions } from '../../../types/types';
|
||||
|
||||
export function rancherFormatter(_options: ClusterLinksFormatterOptions): URL {
|
||||
throw new Error(
|
||||
'Rancher formatter is not yet implemented. Please, contribute!',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { standardFormatter } from './standard';
|
||||
|
||||
function formatUrl(url: URL) {
|
||||
// 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}`;
|
||||
}
|
||||
|
||||
describe('clusterLinks - standard formatter', () => {
|
||||
it('should return an url on the workloads when there is a namespace only', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'foo',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/#/workloads?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the workloads when the kind is not recognizeed', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'UnknownKind',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/#/workloads?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Deployment',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the service', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Service',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/#/service/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the ingress', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'Ingress',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/#/ingress/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
it('should return an url on the deployment for a hpa', () => {
|
||||
const url = standardFormatter({
|
||||
dashboardUrl: new URL('https://k8s.foo.com/'),
|
||||
object: {
|
||||
metadata: {
|
||||
name: 'foobar',
|
||||
namespace: 'bar',
|
||||
},
|
||||
},
|
||||
kind: 'HorizontalPodAutoscaler',
|
||||
});
|
||||
expect(formatUrl(url)).toBe(
|
||||
'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClusterLinksFormatterOptions } from '../../../types/types';
|
||||
|
||||
const kindMappings: Record<string, string> = {
|
||||
deployment: 'deployment',
|
||||
ingress: 'ingress',
|
||||
service: 'service',
|
||||
horizontalpodautoscaler: 'deployment',
|
||||
};
|
||||
|
||||
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 validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')];
|
||||
if (namespace) {
|
||||
result.searchParams.set('namespace', namespace);
|
||||
}
|
||||
if (validKind && name && namespace) {
|
||||
result.hash = `/${validKind}/${namespace}/${name}`;
|
||||
} else if (namespace) {
|
||||
result.hash = '/workloads';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { formatClusterLink } from './formatClusterLink';
|
||||
export { clusterLinksFormatters } from './formatters';
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './clusterLinks';
|
||||
Reference in New Issue
Block a user