diff --git a/.changeset/olive-zebras-itch.md b/.changeset/olive-zebras-itch.md
new file mode 100644
index 0000000000..6a12e35c87
--- /dev/null
+++ b/.changeset/olive-zebras-itch.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-kubernetes': patch
+---
+
+Show Kubernetes Service manifests.
+Show Kubernetes Ingress manifests.
diff --git a/plugins/kubernetes/src/components/IngressesAccordions/IngressDrawer.test.tsx b/plugins/kubernetes/src/components/IngressesAccordions/IngressDrawer.test.tsx
new file mode 100644
index 0000000000..cf37eb3e79
--- /dev/null
+++ b/plugins/kubernetes/src/components/IngressesAccordions/IngressDrawer.test.tsx
@@ -0,0 +1,38 @@
+/*
+ * 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 ingresses from './__fixtures__/2-ingresses.json';
+import { wrapInTestApp } from '@backstage/test-utils';
+import { IngressDrawer } from './IngressDrawer';
+
+describe('IngressDrawer', () => {
+ it('should render ingress drawer', async () => {
+ const { getByText, getAllByText } = render(
+ wrapInTestApp(
+ ,
+ ),
+ );
+
+ expect(getAllByText('awesome-service')).toHaveLength(2);
+ expect(getByText('YAML')).toBeInTheDocument();
+ expect(getByText('Rules')).toBeInTheDocument();
+ expect(getByText('Host: api.awesome-host.io')).toBeInTheDocument();
+ expect(getAllByText('Service Port: 80')).toHaveLength(2);
+ expect(getAllByText('Service Name: awesome-service')).toHaveLength(2);
+ });
+});
diff --git a/plugins/kubernetes/src/components/IngressesAccordions/IngressDrawer.tsx b/plugins/kubernetes/src/components/IngressesAccordions/IngressDrawer.tsx
new file mode 100644
index 0000000000..9e63f22fda
--- /dev/null
+++ b/plugins/kubernetes/src/components/IngressesAccordions/IngressDrawer.tsx
@@ -0,0 +1,58 @@
+/*
+ * 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 { ExtensionsV1beta1Ingress } from '@kubernetes/client-node';
+import { KubernetesDrawer } from '../KubernetesDrawer/KubernetesDrawer';
+import { Typography, Grid } from '@material-ui/core';
+
+export const IngressDrawer = ({
+ ingress,
+ expanded,
+}: {
+ ingress: ExtensionsV1beta1Ingress;
+ expanded?: boolean;
+}) => {
+ return (
+ {
+ return ingress.spec || {};
+ }}
+ >
+
+
+
+ {ingress.metadata?.name ?? 'unknown object'}
+
+
+
+
+ Ingress
+
+
+
+
+ );
+};
diff --git a/plugins/kubernetes/src/components/IngressesAccordions/IngressesAccordions.test.tsx b/plugins/kubernetes/src/components/IngressesAccordions/IngressesAccordions.test.tsx
new file mode 100644
index 0000000000..580561095e
--- /dev/null
+++ b/plugins/kubernetes/src/components/IngressesAccordions/IngressesAccordions.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * 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 oneIngressFixture from './__fixtures__/2-ingresses.json';
+import { wrapInTestApp } from '@backstage/test-utils';
+import { IngressesAccordions } from './IngressesAccordions';
+
+describe('IngressesAccordions', () => {
+ it('should render 1 ingress', async () => {
+ const { getByText } = render(
+ wrapInTestApp(
+ ,
+ ),
+ );
+
+ expect(getByText('awesome-service')).toBeInTheDocument();
+ expect(getByText('Ingress')).toBeInTheDocument();
+ });
+});
diff --git a/plugins/kubernetes/src/components/IngressesAccordions/IngressesAccordions.tsx b/plugins/kubernetes/src/components/IngressesAccordions/IngressesAccordions.tsx
new file mode 100644
index 0000000000..a49f966ffd
--- /dev/null
+++ b/plugins/kubernetes/src/components/IngressesAccordions/IngressesAccordions.tsx
@@ -0,0 +1,101 @@
+/*
+ * 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,
+} from '@material-ui/core';
+import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+import { ExtensionsV1beta1Ingress } from '@kubernetes/client-node';
+import { StructuredMetadataTable } from '@backstage/core';
+import { IngressDrawer } from './IngressDrawer';
+
+type IngressesAccordionsProps = {
+ deploymentResources: GroupedResponses;
+};
+
+export const IngressesAccordions = ({
+ deploymentResources,
+}: IngressesAccordionsProps) => {
+ return (
+
+ {deploymentResources.ingresses.map((ingress, i) => (
+
+
+
+ ))}
+
+ );
+};
+
+type IngressAccordionProps = {
+ ingress: ExtensionsV1beta1Ingress;
+};
+
+const IngressAccordion = ({ ingress }: IngressAccordionProps) => {
+ return (
+
+ }>
+
+
+
+
+
+
+ );
+};
+
+type IngressSummaryProps = {
+ ingress: ExtensionsV1beta1Ingress;
+};
+
+const IngressSummary = ({ ingress }: IngressSummaryProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+type IngressCardProps = {
+ ingress: ExtensionsV1beta1Ingress;
+};
+
+const IngressCard = ({ ingress }: IngressCardProps) => {
+ return (
+
+ );
+};
diff --git a/plugins/kubernetes/src/components/IngressesAccordions/__fixtures__/2-ingresses.json b/plugins/kubernetes/src/components/IngressesAccordions/__fixtures__/2-ingresses.json
new file mode 100644
index 0000000000..136c70697d
--- /dev/null
+++ b/plugins/kubernetes/src/components/IngressesAccordions/__fixtures__/2-ingresses.json
@@ -0,0 +1,57 @@
+{
+ "ingresses": [
+ {
+ "metadata": {
+ "annotations": {
+ "artifact.spinnaker.io/location": "default",
+ "artifact.spinnaker.io/name": "awesome-service",
+ "artifact.spinnaker.io/type": "kubernetes/ingress",
+ "kubernetes.io/ingress.class": "traefik",
+ "kubernetes.io/ingress.global-static-ip-name": "traefik-tcp-lb",
+ "moniker.spinnaker.io/application": "awesome-service",
+ "moniker.spinnaker.io/cluster": "ingress awesome-service"
+ },
+ "creationTimestamp": "2018-11-16T14:00:13.000Z",
+ "generation": 11,
+ "labels": {
+ "app": "awesome-service",
+ "app.kubernetes.io/managed-by": "spinnaker",
+ "app.kubernetes.io/name": "awesome-service"
+ },
+ "name": "awesome-service",
+ "namespace": "default",
+ "resourceVersion": "564824116",
+ "selfLink": "/apis/networking.k8s.io/v1beta1/namespaces/default/ingresses/awesome-service",
+ "uid": "f072e0b4-e9a7-11e8-af65-42010a9c0022"
+ },
+ "spec": {
+ "rules": [
+ {
+ "host": "api.awesome-host.io",
+ "http": {
+ "paths": [
+ {
+ "backend": {
+ "serviceName": "awesome-service",
+ "servicePort": 80
+ },
+ "path": "/v1/awesome-service"
+ },
+ {
+ "backend": {
+ "serviceName": "awesome-service",
+ "servicePort": 80
+ },
+ "path": "/v1/awesome-services"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "status": {
+ "loadBalancer": {}
+ }
+ }
+ ]
+}
diff --git a/plugins/kubernetes/src/components/IngressesAccordions/index.ts b/plugins/kubernetes/src/components/IngressesAccordions/index.ts
new file mode 100644
index 0000000000..a71f53fac7
--- /dev/null
+++ b/plugins/kubernetes/src/components/IngressesAccordions/index.ts
@@ -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 { IngressesAccordions } from './IngressesAccordions';
diff --git a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx
index 39e73782f1..092ada0724 100644
--- a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx
+++ b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx
@@ -47,6 +47,8 @@ import { DeploymentsAccordions } from '../DeploymentsAccordions';
import { ErrorReporting } from '../ErrorReporting';
import { groupResponses } from '../../utils/response';
import { DetectedError, detectErrors } from '../../error-detection';
+import { IngressesAccordions } from '../IngressesAccordions';
+import { ServicesAccordions } from '../ServicesAccordions';
type KubernetesContentProps = { entity: Entity; children?: React.ReactNode };
@@ -187,10 +189,22 @@ const Cluster = ({ clusterObjects, detectedErrors }: ClusterProps) => {
/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
>
diff --git a/plugins/kubernetes/src/components/ServicesAccordions/ServiceDrawer.test.tsx b/plugins/kubernetes/src/components/ServicesAccordions/ServiceDrawer.test.tsx
new file mode 100644
index 0000000000..d9082ff632
--- /dev/null
+++ b/plugins/kubernetes/src/components/ServicesAccordions/ServiceDrawer.test.tsx
@@ -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(
+ ,
+ ),
+ );
+
+ 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();
+ });
+});
diff --git a/plugins/kubernetes/src/components/ServicesAccordions/ServiceDrawer.tsx b/plugins/kubernetes/src/components/ServicesAccordions/ServiceDrawer.tsx
new file mode 100644
index 0000000000..d48f17813a
--- /dev/null
+++ b/plugins/kubernetes/src/components/ServicesAccordions/ServiceDrawer.tsx
@@ -0,0 +1,58 @@
+/*
+ * 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 (
+ {
+ return service.spec || {};
+ }}
+ >
+
+
+
+ {service.metadata?.name ?? 'unknown object'}
+
+
+
+
+ Service
+
+
+
+
+ );
+};
diff --git a/plugins/kubernetes/src/components/ServicesAccordions/ServicesAccordions.test.tsx b/plugins/kubernetes/src/components/ServicesAccordions/ServicesAccordions.test.tsx
new file mode 100644
index 0000000000..46172b4150
--- /dev/null
+++ b/plugins/kubernetes/src/components/ServicesAccordions/ServicesAccordions.test.tsx
@@ -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(
+ ,
+ ),
+ );
+
+ expect(getByText('awesome-service-grpc')).toBeInTheDocument();
+ expect(getByText('Type: ClusterIP')).toBeInTheDocument();
+
+ expect(getByText('awesome-service-pg')).toBeInTheDocument();
+ expect(getByText('Type: ExternalName')).toBeInTheDocument();
+ });
+});
diff --git a/plugins/kubernetes/src/components/ServicesAccordions/ServicesAccordions.tsx b/plugins/kubernetes/src/components/ServicesAccordions/ServicesAccordions.tsx
new file mode 100644
index 0000000000..6484f2211d
--- /dev/null
+++ b/plugins/kubernetes/src/components/ServicesAccordions/ServicesAccordions.tsx
@@ -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 (
+
+ {deploymentResources.services.map((service, i) => (
+
+
+
+ ))}
+
+ );
+};
+
+type ServiceAccordionProps = {
+ service: V1Service;
+};
+
+const ServiceAccordion = ({ service }: ServiceAccordionProps) => {
+ return (
+
+ }>
+
+
+
+
+
+
+ );
+};
+
+type ServiceSummaryProps = {
+ service: V1Service;
+};
+
+const ServiceSummary = ({ service }: ServiceSummaryProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ Type: {service.spec?.type ?? '?'}
+
+
+
+ );
+};
+
+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 (
+
+ );
+};
diff --git a/plugins/kubernetes/src/components/ServicesAccordions/__fixtures__/2-services.json b/plugins/kubernetes/src/components/ServicesAccordions/__fixtures__/2-services.json
new file mode 100644
index 0000000000..f982ebf164
--- /dev/null
+++ b/plugins/kubernetes/src/components/ServicesAccordions/__fixtures__/2-services.json
@@ -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": {}
+ }
+ }
+ ]
+}
diff --git a/plugins/kubernetes/src/components/ServicesAccordions/index.ts b/plugins/kubernetes/src/components/ServicesAccordions/index.ts
new file mode 100644
index 0000000000..a3c392c743
--- /dev/null
+++ b/plugins/kubernetes/src/components/ServicesAccordions/index.ts
@@ -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';