Show kubernetes services

This commit is contained in:
Kévin Gomez
2021-01-13 23:23:00 +01:00
parent b9cef17f79
commit d014185dbd
8 changed files with 376 additions and 4 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes': patch
---
Show Kubernetes Service manifests
@@ -47,6 +47,7 @@ import { DeploymentsAccordions } from '../DeploymentsAccordions';
import { ErrorReporting } from '../ErrorReporting';
import { groupResponses } from '../../utils/response';
import { DetectedError, detectErrors } from '../../error-detection';
import { ServicesAccordions } from '../ServicesAccordions';
type KubernetesContentProps = { entity: Entity; children?: React.ReactNode };
@@ -187,10 +188,18 @@ const Cluster = ({ clusterObjects, detectedErrors }: ClusterProps) => {
/>
</AccordionSummary>
<AccordionDetails>
<DeploymentsAccordions
deploymentResources={groupedResponses}
clusterPodNamesWithErrors={podsWithErrors}
/>
<Grid container direction="column">
<Grid item>
<DeploymentsAccordions
deploymentResources={groupedResponses}
clusterPodNamesWithErrors={podsWithErrors}
/>
</Grid>
<Grid item>
<ServicesAccordions deploymentResources={groupedResponses} />
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
</>
@@ -0,0 +1,39 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { render } from '@testing-library/react';
import * as services from './__fixtures__/2-services.json';
import { wrapInTestApp } from '@backstage/test-utils';
import { ServiceDrawer } from './ServiceDrawer';
describe('ServiceDrawer', () => {
it('should render deployment drawer', async () => {
const { getByText, getAllByText } = render(
wrapInTestApp(
<ServiceDrawer service={(services as any).services[0]} expanded />,
),
);
expect(getAllByText('awesome-service-grpc')).toHaveLength(2);
expect(getAllByText('Service')).toHaveLength(2);
expect(getByText('YAML')).toBeInTheDocument();
expect(getByText('Cluster IP')).toBeInTheDocument();
expect(getByText('Ports')).toBeInTheDocument();
expect(getByText('Target Port: 1997')).toBeInTheDocument();
expect(getByText('App: awesome-service')).toBeInTheDocument();
});
});
@@ -0,0 +1,60 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { V1Service } from '@kubernetes/client-node';
import { KubernetesDrawer } from '../KubernetesDrawer/KubernetesDrawer';
import { Typography, Grid } from '@material-ui/core';
export const ServiceDrawer = ({
service,
expanded,
}: {
service: V1Service;
expanded?: boolean;
}) => {
return (
<KubernetesDrawer
object={service}
expanded={expanded}
kind="Service"
renderObject={(service: V1Service) => {
return {
...service.spec,
};
}}
>
<Grid
container
direction="column"
justify="flex-start"
alignItems="flex-start"
spacing={0}
>
<Grid item>
<Typography variant="h5">
{service.metadata?.name ?? 'unknown object'}
</Typography>
</Grid>
<Grid item>
<Typography color="textSecondary" variant="body1">
Service
</Typography>
</Grid>
</Grid>
</KubernetesDrawer>
);
};
@@ -0,0 +1,37 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { render } from '@testing-library/react';
import * as twoDeployFixture from './__fixtures__/2-services.json';
import { wrapInTestApp } from '@backstage/test-utils';
import { ServicesAccordions } from './ServicesAccordions';
describe('ServicesAccordions', () => {
it('should render 2 services', async () => {
const { getByText } = render(
wrapInTestApp(
<ServicesAccordions deploymentResources={twoDeployFixture as any} />,
),
);
expect(getByText('awesome-service-grpc')).toBeInTheDocument();
expect(getByText('Type: ClusterIP')).toBeInTheDocument();
expect(getByText('awesome-service-pg')).toBeInTheDocument();
expect(getByText('Type: ExternalName')).toBeInTheDocument();
});
});
@@ -0,0 +1,123 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { GroupedResponses } from '../../types/types';
import React from 'react';
import {
Accordion,
AccordionDetails,
AccordionSummary,
Divider,
Grid,
Typography,
} from '@material-ui/core';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { V1Service } from '@kubernetes/client-node';
import { StructuredMetadataTable } from '@backstage/core';
import { ServiceDrawer } from './ServiceDrawer';
type ServicesAccordionsProps = {
deploymentResources: GroupedResponses;
};
export const ServicesAccordions = ({
deploymentResources,
}: ServicesAccordionsProps) => {
return (
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
{deploymentResources.services.map((service, i) => (
<Grid item key={i} xs>
<ServiceAccordion service={service} />
</Grid>
))}
</Grid>
);
};
type ServiceAccordionProps = {
service: V1Service;
};
const ServiceAccordion = ({ service }: ServiceAccordionProps) => {
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<ServiceSummary service={service} />
</AccordionSummary>
<AccordionDetails>
<ServiceCard service={service} />
</AccordionDetails>
</Accordion>
);
};
type ServiceSummaryProps = {
service: V1Service;
};
const ServiceSummary = ({ service }: ServiceSummaryProps) => {
return (
<Grid container direction="row" justify="flex-start" alignItems="center">
<Grid xs={3} item>
<ServiceDrawer service={service} />
</Grid>
<Grid item xs={1}>
<Divider style={{ height: '5em' }} orientation="vertical" />
</Grid>
<Grid item>
<Typography variant="subtitle2">
Type: {service.spec?.type ?? '?'}
</Typography>
</Grid>
</Grid>
);
};
type ServiceCardProps = {
service: V1Service;
};
const ServiceCard = ({ service }: ServiceCardProps) => {
const metadata: any = {};
if (service.status?.loadBalancer?.ingress?.length ?? -1 > 0) {
metadata.loadbalancer = service.status?.loadBalancer;
}
if (service.spec?.type === 'ClusterIP') {
metadata.clusterIP = service.spec.clusterIP;
}
if (service.spec?.type === 'ExternalName') {
metadata.externalName = service.spec.externalName;
}
return (
<StructuredMetadataTable
metadata={{
type: service.spec?.type,
ports: service.spec?.ports,
...metadata,
}}
/>
);
};
@@ -0,0 +1,83 @@
{
"services": [
{
"metadata": {
"annotations": {
"artifact.spinnaker.io/location": "default",
"artifact.spinnaker.io/name": "awesome-service-grpc",
"artifact.spinnaker.io/type": "kubernetes/service",
"moniker.spinnaker.io/application": "awesome-service",
"moniker.spinnaker.io/cluster": "service awesome-service-grpc"
},
"creationTimestamp": "2021-01-04T16:35:04.000Z",
"labels": {
"app": "awesome-service",
"app.kubernetes.io/managed-by": "spinnaker",
"app.kubernetes.io/name": "awesome-service"
},
"name": "awesome-service-grpc",
"namespace": "default",
"resourceVersion": "548901649",
"selfLink": "/api/v1/namespaces/default/services/awesome-service-grpc",
"uid": "461cdcd7-8c61-4125-91f9-e03d745f2f2c"
},
"spec": {
"clusterIP": "None",
"ports": [
{
"name": "grpc",
"port": 1997,
"protocol": "TCP",
"targetPort": 1997
}
],
"selector": {
"app": "awesome-service"
},
"sessionAffinity": "None",
"type": "ClusterIP"
},
"status": {
"loadBalancer": {}
}
},
{
"metadata": {
"annotations": {
"artifact.spinnaker.io/location": "default",
"artifact.spinnaker.io/name": "awesome-service-pg",
"artifact.spinnaker.io/type": "kubernetes/service",
"moniker.spinnaker.io/application": "awesome-service",
"moniker.spinnaker.io/cluster": "service awesome-service-pg"
},
"creationTimestamp": "2021-01-04T16:35:02.000Z",
"labels": {
"app": "awesome-service",
"app.kubernetes.io/managed-by": "spinnaker",
"app.kubernetes.io/name": "awesome-service"
},
"name": "awesome-service-pg",
"namespace": "default",
"resourceVersion": "548901625",
"selfLink": "/api/v1/namespaces/default/services/awesome-service-pg",
"uid": "7d7ff8f2-6caa-4888-ae55-b6d41833ab92"
},
"spec": {
"externalName": "10.244.0.5",
"ports": [
{
"name": "pg",
"port": 5432,
"protocol": "TCP",
"targetPort": 5432
}
],
"sessionAffinity": "None",
"type": "ExternalName"
},
"status": {
"loadBalancer": {}
}
}
]
}
@@ -0,0 +1,16 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { ServicesAccordions } from './ServicesAccordions';