From e3c99d72f1f3c5907cad6c400cd074e2d05b457f Mon Sep 17 00:00:00 2001 From: Matthew Clarke Date: Tue, 22 Sep 2020 15:47:27 +0100 Subject: [PATCH] Kubernetes plugins boilerplate (#2559) * Kubernetes plugins boilerplate * pr feedback; check for k8s annotation --- packages/app/package.json | 1 + .../app/src/components/catalog/EntityPage.tsx | 11 +++ packages/app/src/plugins.ts | 1 + packages/backend/package.json | 1 + packages/backend/src/index.ts | 3 + packages/backend/src/plugins/kubernetes.ts | 22 ++++++ plugins/kubernetes-backend/.eslintrc.js | 3 + plugins/kubernetes-backend/README.md | 11 +++ plugins/kubernetes-backend/package.json | 42 +++++++++++ plugins/kubernetes-backend/src/index.test.ts | 23 ++++++ plugins/kubernetes-backend/src/index.ts | 17 +++++ plugins/kubernetes-backend/src/run.ts | 33 +++++++++ .../kubernetes-backend/src/service/router.ts | 45 ++++++++++++ .../src/service/standaloneApplication.ts | 52 ++++++++++++++ .../src/service/standaloneServer.ts | 50 +++++++++++++ plugins/kubernetes-backend/src/setupTests.ts | 19 +++++ plugins/kubernetes/.eslintrc.js | 3 + plugins/kubernetes/README.md | 13 ++++ plugins/kubernetes/dev/index.tsx | 19 +++++ plugins/kubernetes/package.json | 47 ++++++++++++ plugins/kubernetes/src/Router.tsx | 47 ++++++++++++ .../src/api/KubernetesBackendClient.ts | 43 +++++++++++ plugins/kubernetes/src/api/types.ts | 27 +++++++ .../KubernetesContent/KubernetesContent.tsx | 72 +++++++++++++++++++ .../src/components/KubernetesContent/index.ts | 16 +++++ plugins/kubernetes/src/index.ts | 17 +++++ plugins/kubernetes/src/plugin.test.ts | 22 ++++++ plugins/kubernetes/src/plugin.ts | 40 +++++++++++ plugins/kubernetes/src/setupTests.ts | 18 +++++ 29 files changed, 718 insertions(+) create mode 100644 packages/backend/src/plugins/kubernetes.ts create mode 100644 plugins/kubernetes-backend/.eslintrc.js create mode 100644 plugins/kubernetes-backend/README.md create mode 100644 plugins/kubernetes-backend/package.json create mode 100644 plugins/kubernetes-backend/src/index.test.ts create mode 100644 plugins/kubernetes-backend/src/index.ts create mode 100644 plugins/kubernetes-backend/src/run.ts create mode 100644 plugins/kubernetes-backend/src/service/router.ts create mode 100644 plugins/kubernetes-backend/src/service/standaloneApplication.ts create mode 100644 plugins/kubernetes-backend/src/service/standaloneServer.ts create mode 100644 plugins/kubernetes-backend/src/setupTests.ts create mode 100644 plugins/kubernetes/.eslintrc.js create mode 100644 plugins/kubernetes/README.md create mode 100644 plugins/kubernetes/dev/index.tsx create mode 100644 plugins/kubernetes/package.json create mode 100644 plugins/kubernetes/src/Router.tsx create mode 100644 plugins/kubernetes/src/api/KubernetesBackendClient.ts create mode 100644 plugins/kubernetes/src/api/types.ts create mode 100644 plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx create mode 100644 plugins/kubernetes/src/components/KubernetesContent/index.ts create mode 100644 plugins/kubernetes/src/index.ts create mode 100644 plugins/kubernetes/src/plugin.test.ts create mode 100644 plugins/kubernetes/src/plugin.ts create mode 100644 plugins/kubernetes/src/setupTests.ts diff --git a/packages/app/package.json b/packages/app/package.json index cb61e1ed8c..1262c954cc 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -16,6 +16,7 @@ "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.22", "@backstage/plugin-graphiql": "^0.1.1-alpha.22", "@backstage/plugin-jenkins": "^0.1.1-alpha.22", + "@backstage/plugin-kubernetes": "^0.1.1-alpha.22", "@backstage/plugin-lighthouse": "^0.1.1-alpha.22", "@backstage/plugin-newrelic": "^0.1.1-alpha.22", "@backstage/plugin-register-component": "^0.1.1-alpha.22", diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index c382abc3a8..d96fe9841f 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -29,6 +29,7 @@ import { import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs'; import { Router as SentryRouter } from '@backstage/plugin-sentry'; import { EmbeddedDocsRouter as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as KubernetesRouter } from '@backstage/plugin-kubernetes'; import React from 'react'; import { AboutCard, @@ -99,6 +100,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( title="Docs" element={} /> + } + /> ); @@ -124,6 +130,11 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( title="Docs" element={} /> + } + /> ); const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index b36d0323e0..aa68bc5fad 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -33,3 +33,4 @@ export { plugin as Jenkins } from '@backstage/plugin-jenkins'; export { plugin as ApiDocs } from '@backstage/plugin-api-docs'; export { plugin as GithubPullRequests } from '@roadiehq/backstage-plugin-github-pull-requests'; export { plugin as GcpProjects } from '@backstage/plugin-gcp-projects'; +export { plugin as Kubernetes } from '@backstage/plugin-kubernetes'; diff --git a/packages/backend/package.json b/packages/backend/package.json index aa6f0ad0bf..147ea630c2 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -26,6 +26,7 @@ "@backstage/plugin-catalog-backend": "^0.1.1-alpha.22", "@backstage/plugin-graphql-backend": "^0.1.1-alpha.22", "@backstage/plugin-identity-backend": "^0.1.1-alpha.22", + "@backstage/plugin-kubernetes-backend": "^0.1.1-alpha.22", "@backstage/plugin-proxy-backend": "^0.1.1-alpha.22", "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.22", "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.22", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index ab32822769..7c52175d76 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -34,6 +34,7 @@ import healthcheck from './plugins/healthcheck'; import auth from './plugins/auth'; import catalog from './plugins/catalog'; import identity from './plugins/identity'; +import kubernetes from './plugins/kubernetes'; import rollbar from './plugins/rollbar'; import scaffolder from './plugins/scaffolder'; import sentry from './plugins/sentry'; @@ -73,6 +74,7 @@ async function main() { const rollbarEnv = useHotMemoize(module, () => createEnv('rollbar')); const sentryEnv = useHotMemoize(module, () => createEnv('sentry')); const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); + const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes')); const graphqlEnv = useHotMemoize(module, () => createEnv('graphql')); const appEnv = useHotMemoize(module, () => createEnv('app')); @@ -86,6 +88,7 @@ async function main() { .addRouter('/auth', await auth(authEnv)) .addRouter('/identity', await identity(identityEnv)) .addRouter('/techdocs', await techdocs(techdocsEnv)) + .addRouter('/kubernetes', await kubernetes(kubernetesEnv)) .addRouter('/proxy', await proxy(proxyEnv, '/proxy')) .addRouter('/graphql', await graphql(graphqlEnv)) .addRouter('', await app(appEnv)); diff --git a/packages/backend/src/plugins/kubernetes.ts b/packages/backend/src/plugins/kubernetes.ts new file mode 100644 index 0000000000..42b7722ff6 --- /dev/null +++ b/packages/backend/src/plugins/kubernetes.ts @@ -0,0 +1,22 @@ +/* + * 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 { createRouter } from '@backstage/plugin-kubernetes-backend'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ logger }: PluginEnvironment) { + return await createRouter({ logger }); +} diff --git a/plugins/kubernetes-backend/.eslintrc.js b/plugins/kubernetes-backend/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/kubernetes-backend/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/kubernetes-backend/README.md b/plugins/kubernetes-backend/README.md new file mode 100644 index 0000000000..0246da24ed --- /dev/null +++ b/plugins/kubernetes-backend/README.md @@ -0,0 +1,11 @@ +# Kubernetes Backend + +WORK IN PROGRESS + +This is the backend part of the Kubernetes plugin. + +It responds to Kubernetes requests from the frontend. + +## Links + +- [The Backstage homepage](https://backstage.io) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json new file mode 100644 index 0000000000..2e59aaeec0 --- /dev/null +++ b/plugins/kubernetes-backend/package.json @@ -0,0 +1,42 @@ +{ + "name": "@backstage/plugin-kubernetes-backend", + "version": "0.1.1-alpha.22", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.1.1-alpha.22", + "@types/express": "^4.17.6", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.17.1", + "express-promise-router": "^3.0.3", + "fs-extra": "^9.0.0", + "helmet": "^4.0.0", + "morgan": "^1.10.0", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.22", + "jest-fetch-mock": "^3.0.3" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/kubernetes-backend/src/index.test.ts b/plugins/kubernetes-backend/src/index.test.ts new file mode 100644 index 0000000000..4fca4ca746 --- /dev/null +++ b/plugins/kubernetes-backend/src/index.test.ts @@ -0,0 +1,23 @@ +/* + * 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 '@backstage/backend-common'; + +describe('test', () => { + it('unbreaks the test runner', () => { + expect(true).toBeTruthy(); + }); +}); diff --git a/plugins/kubernetes-backend/src/index.ts b/plugins/kubernetes-backend/src/index.ts new file mode 100644 index 0000000000..7612c392a2 --- /dev/null +++ b/plugins/kubernetes-backend/src/index.ts @@ -0,0 +1,17 @@ +/* + * 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 * from './service/router'; diff --git a/plugins/kubernetes-backend/src/run.ts b/plugins/kubernetes-backend/src/run.ts new file mode 100644 index 0000000000..995adaff10 --- /dev/null +++ b/plugins/kubernetes-backend/src/run.ts @@ -0,0 +1,33 @@ +/* + * 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 yn from 'yn'; +import { getRootLogger } from '@backstage/backend-common'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 3004; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts new file mode 100644 index 0000000000..b200896887 --- /dev/null +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -0,0 +1,45 @@ +/* + * 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 express from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; + +export interface RouterOptions { + logger: Logger; +} + +const makeRouter = (logger: Logger): express.Router => { + const router = Router(); + router.use(express.json()); + + router.get('/services/:serviceId', async (req, res) => { + const serviceId = req.params.serviceId; + logger.info(`HERE ${serviceId}`); + res.send({ serviceId }); + }); + + return router; +}; + +export async function createRouter( + options: RouterOptions, +): Promise { + const logger = options.logger; + + logger.info('Initializing Kubernetes backend'); + return makeRouter(logger); +} diff --git a/plugins/kubernetes-backend/src/service/standaloneApplication.ts b/plugins/kubernetes-backend/src/service/standaloneApplication.ts new file mode 100644 index 0000000000..5eea4fb8f5 --- /dev/null +++ b/plugins/kubernetes-backend/src/service/standaloneApplication.ts @@ -0,0 +1,52 @@ +/* + * 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 { + errorHandler, + notFoundHandler, + requestLoggingHandler, +} from '@backstage/backend-common'; +import compression from 'compression'; +import cors from 'cors'; +import express from 'express'; +import helmet from 'helmet'; +import { Logger } from 'winston'; +import { createRouter } from './router'; + +export interface ApplicationOptions { + enableCors: boolean; + logger: Logger; +} + +export async function createStandaloneApplication( + options: ApplicationOptions, +): Promise { + const { enableCors, logger } = options; + const app = express(); + + app.use(helmet()); + if (enableCors) { + app.use(cors()); + } + app.use(compression()); + app.use(express.json()); + app.use(requestLoggingHandler()); + app.use('/', await createRouter({ logger })); + app.use(notFoundHandler()); + app.use(errorHandler()); + + return app; +} diff --git a/plugins/kubernetes-backend/src/service/standaloneServer.ts b/plugins/kubernetes-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..9831bc986b --- /dev/null +++ b/plugins/kubernetes-backend/src/service/standaloneServer.ts @@ -0,0 +1,50 @@ +/* + * 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 { Server } from 'http'; +import { Logger } from 'winston'; +import { createStandaloneApplication } from './standaloneApplication'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'kubernetes-backend' }); + + logger.debug('Creating application...'); + const app = await createStandaloneApplication({ + enableCors: options.enableCors, + logger, + }); + + logger.debug('Starting application server...'); + return await new Promise((resolve, reject) => { + const server = app.listen(options.port, (err?: Error) => { + if (err) { + reject(err); + return; + } + + logger.info(`Listening on port ${options.port}`); + resolve(server); + }); + }); +} diff --git a/plugins/kubernetes-backend/src/setupTests.ts b/plugins/kubernetes-backend/src/setupTests.ts new file mode 100644 index 0000000000..f7b6ca962d --- /dev/null +++ b/plugins/kubernetes-backend/src/setupTests.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +require('jest-fetch-mock').enableMocks(); + +export {}; diff --git a/plugins/kubernetes/.eslintrc.js b/plugins/kubernetes/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/kubernetes/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/kubernetes/README.md b/plugins/kubernetes/README.md new file mode 100644 index 0000000000..678c9a96a6 --- /dev/null +++ b/plugins/kubernetes/README.md @@ -0,0 +1,13 @@ +# kubernetes + +Welcome to the kubernetes plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/kubernetes](http://localhost:3000/kubernetes). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. diff --git a/plugins/kubernetes/dev/index.tsx b/plugins/kubernetes/dev/index.tsx new file mode 100644 index 0000000000..f352fb9c34 --- /dev/null +++ b/plugins/kubernetes/dev/index.tsx @@ -0,0 +1,19 @@ +/* + * 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 { createDevApp } from '@backstage/dev-utils'; +import { plugin } from '../src'; + +createDevApp().registerPlugin(plugin).render(); diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json new file mode 100644 index 0000000000..aa8a7f61e4 --- /dev/null +++ b/plugins/kubernetes/package.json @@ -0,0 +1,47 @@ +{ + "name": "@backstage/plugin-kubernetes", + "version": "0.1.1-alpha.22", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/catalog-model": "^0.1.1-alpha.22", + "@backstage/core": "^0.1.1-alpha.22", + "@backstage/theme": "^0.1.1-alpha.22", + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "react": "^16.13.1", + "react-router-dom": "6.0.0-beta.0", + "react-dom": "^16.13.1", + "react-use": "^15.3.3" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.22", + "@backstage/dev-utils": "^0.1.1-alpha.22", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^10.4.1", + "@testing-library/user-event": "^12.0.7", + "@types/jest": "^26.0.7", + "@types/node": "^12.0.0", + "jest-fetch-mock": "^3.0.3" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/kubernetes/src/Router.tsx b/plugins/kubernetes/src/Router.tsx new file mode 100644 index 0000000000..994788bf72 --- /dev/null +++ b/plugins/kubernetes/src/Router.tsx @@ -0,0 +1,47 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { Route, Routes } from 'react-router-dom'; + +import { rootCatalogKubernetesRouteRef } from './plugin'; +import { KubernetesContent } from './components/KubernetesContent'; +import { WarningPanel } from '@backstage/core'; + +const KUBERNETES_ANNOTATION = 'backstage.io/kubernetes'; + +export const Router = ({ entity }: { entity: Entity }) => { + const kubernetesAnnotationValue = + entity.metadata.annotations?.[KUBERNETES_ANNOTATION]; + + if (!kubernetesAnnotationValue) { + return ( + +
{KUBERNETES_ANNOTATION}
annotation is missing on the entity. +
+ ); + } + + return ( + + } + /> + + ); +}; diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts new file mode 100644 index 0000000000..fed3cd2ed0 --- /dev/null +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts @@ -0,0 +1,43 @@ +/* + * 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 { DiscoveryApi } from '@backstage/core'; +import { KubernetesApi } from './types'; + +export class KubernetesBackendClient implements KubernetesApi { + private readonly discoveryApi: DiscoveryApi; + + constructor(options: { discoveryApi: DiscoveryApi }) { + this.discoveryApi = options.discoveryApi; + } + + private async getRequired(path: string): Promise { + const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}${path}`; + const response = await fetch(url); + + if (!response.ok) { + const payload = await response.text(); + const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`; + throw new Error(message); + } + + return await response.json(); + } + + async getObjectsByServiceId(serviceId: String): Promise<{}> { + return await this.getRequired(`/services/${serviceId}`); + } +} diff --git a/plugins/kubernetes/src/api/types.ts b/plugins/kubernetes/src/api/types.ts new file mode 100644 index 0000000000..2ad6e567f9 --- /dev/null +++ b/plugins/kubernetes/src/api/types.ts @@ -0,0 +1,27 @@ +/* + * 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 { createApiRef } from '@backstage/core'; + +export const kubernetesApiRef = createApiRef({ + id: 'plugin.kubernetes.service', + description: + 'Used by the Kubernetes plugin to make requests to accompanying backend', +}); + +export interface KubernetesApi { + getObjectsByServiceId(serviceId: String): Promise<{}>; +} diff --git a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx new file mode 100644 index 0000000000..d79454a5d1 --- /dev/null +++ b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx @@ -0,0 +1,72 @@ +/* + * 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, { FC, useEffect, useState } from 'react'; +import { Typography, Grid } from '@material-ui/core'; +import { + InfoCard, + Page, + pageTheme, + Content, + ContentHeader, + useApi, +} from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { kubernetesApiRef } from '../../api/types'; + +// TODO this is a temporary component used to construct the Kubernetes plugin boilerplate + +export const KubernetesContent: FC<{ entity: Entity }> = ({ entity }) => { + const kubernetesApi = useApi(kubernetesApiRef); + const [kubernetesObjects, setKubernetesObjects] = useState<{} | undefined>( + undefined, + ); + const [error, setError] = useState(undefined); + + useEffect(() => { + kubernetesApi + .getObjectsByServiceId(entity.metadata.name) + .then(result => { + setKubernetesObjects(result); + }) + .catch(e => { + setError(e.message); + }); + }, [entity.metadata.name, kubernetesApi]); + + return ( + + + + + + + + {kubernetesObjects === undefined &&
loading....
} + {error !== undefined &&
{error}
} + {kubernetesObjects !== undefined && ( +
+ backend response: {JSON.stringify(kubernetesObjects)} +
+ )} +
+
+
+
+
+
+ ); +}; diff --git a/plugins/kubernetes/src/components/KubernetesContent/index.ts b/plugins/kubernetes/src/components/KubernetesContent/index.ts new file mode 100644 index 0000000000..dc10b08927 --- /dev/null +++ b/plugins/kubernetes/src/components/KubernetesContent/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 { KubernetesContent } from './KubernetesContent'; diff --git a/plugins/kubernetes/src/index.ts b/plugins/kubernetes/src/index.ts new file mode 100644 index 0000000000..8b5969666c --- /dev/null +++ b/plugins/kubernetes/src/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { plugin } from './plugin'; +export { Router } from './Router'; diff --git a/plugins/kubernetes/src/plugin.test.ts b/plugins/kubernetes/src/plugin.test.ts new file mode 100644 index 0000000000..20c50a813a --- /dev/null +++ b/plugins/kubernetes/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * 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 { plugin } from './plugin'; + +describe('kubernetes', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/plugins/kubernetes/src/plugin.ts b/plugins/kubernetes/src/plugin.ts new file mode 100644 index 0000000000..b7ad9615f5 --- /dev/null +++ b/plugins/kubernetes/src/plugin.ts @@ -0,0 +1,40 @@ +/* + * 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 { + createApiFactory, + createPlugin, + createRouteRef, + discoveryApiRef, +} from '@backstage/core'; +import { KubernetesBackendClient } from './api/KubernetesBackendClient'; +import { kubernetesApiRef } from './api/types'; + +export const rootCatalogKubernetesRouteRef = createRouteRef({ + path: '*', + title: 'Kubernetes', +}); + +export const plugin = createPlugin({ + id: 'kubernetes', + apis: [ + createApiFactory({ + api: kubernetesApiRef, + deps: { discoveryApi: discoveryApiRef }, + factory: ({ discoveryApi }) => + new KubernetesBackendClient({ discoveryApi }), + }), + ], +}); diff --git a/plugins/kubernetes/src/setupTests.ts b/plugins/kubernetes/src/setupTests.ts new file mode 100644 index 0000000000..4b4cdbdaaf --- /dev/null +++ b/plugins/kubernetes/src/setupTests.ts @@ -0,0 +1,18 @@ +/* + * 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 '@testing-library/jest-dom'; + +require('jest-fetch-mock').enableMocks();