From c4a9a16af2427b7968010d8e6663703a2670ad5d Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Fri, 10 Feb 2023 09:10:58 +0200 Subject: [PATCH] Add initial code to get PuppetDB node data Signed-off-by: Tomas Dabasinskas --- app-config.yaml | 3 + plugins/puppetdb/package.json | 4 +- plugins/puppetdb/src/api/PuppetDbClient.ts | 66 +++++++++++++ .../{config.d.ts => src/api/index.ts} | 20 +--- plugins/puppetdb/src/api/types.ts | 94 +++++++++++++++++++ .../PuppetDbNodeCard/PuppetDbNodeCard.tsx | 49 ++++++++++ .../src/components/PuppetDbNodeCard/index.ts | 16 ++++ .../components/PuppetDbTab/PuppetDbTab.tsx | 6 +- plugins/puppetdb/src/plugin.ts | 21 ++++- yarn.lock | 1 + 10 files changed, 258 insertions(+), 22 deletions(-) create mode 100644 plugins/puppetdb/src/api/PuppetDbClient.ts rename plugins/puppetdb/{config.d.ts => src/api/index.ts} (63%) create mode 100644 plugins/puppetdb/src/api/types.ts create mode 100644 plugins/puppetdb/src/components/PuppetDbNodeCard/PuppetDbNodeCard.tsx create mode 100644 plugins/puppetdb/src/components/PuppetDbNodeCard/index.ts diff --git a/app-config.yaml b/app-config.yaml index 385c9c2e0e..e2b03786c4 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -123,6 +123,9 @@ proxy: headers: St2-Api-Key: ${ST2_API_KEY} + '/puppetdb': + target: https://your.puppetdb.instance.com + organization: name: My Company diff --git a/plugins/puppetdb/package.json b/plugins/puppetdb/package.json index 4ac841233d..c731998d1f 100644 --- a/plugins/puppetdb/package.json +++ b/plugins/puppetdb/package.json @@ -36,6 +36,7 @@ "dependencies": { "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -58,8 +59,7 @@ "msw": "^0.49.0" }, "files": [ - "dist", - "config.d.ts" + "dist" ], "configSchema": "config.d.ts" } diff --git a/plugins/puppetdb/src/api/PuppetDbClient.ts b/plugins/puppetdb/src/api/PuppetDbClient.ts new file mode 100644 index 0000000000..f1653613e5 --- /dev/null +++ b/plugins/puppetdb/src/api/PuppetDbClient.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2022 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 { PuppetDbApi, PuppetDbNode } from './types'; +import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'; +import { ResponseError } from '@backstage/errors'; + +export class PuppetDbClient implements PuppetDbApi { + discoveryApi: DiscoveryApi; + fetchApi: FetchApi; + + constructor({ + discoveryApi, + fetchApi, + }: { + discoveryApi: DiscoveryApi; + fetchApi: FetchApi; + }) { + this.discoveryApi = discoveryApi; + this.fetchApi = fetchApi; + } + + private async callApi( + path: string, + query: { [key in string]: any }, + ): Promise { + const apiUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/puppetdb`; + const response = await this.fetchApi.fetch( + `${apiUrl}/${path}?${new URLSearchParams(query).toString()}`, + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + if (response.ok) { + return (await response.json()) as T; + } + throw await ResponseError.fromResponse(response); + } + + async getPuppetDbNode( + puppetDbCertName: string, + ): Promise { + if (!puppetDbCertName) { + throw new Error('PuppetDB certname is required'); + } + + return this.callApi( + `/pdb/query/v4/nodes/${encodeURIComponent(puppetDbCertName)}`, + {}, + ); + } +} diff --git a/plugins/puppetdb/config.d.ts b/plugins/puppetdb/src/api/index.ts similarity index 63% rename from plugins/puppetdb/config.d.ts rename to plugins/puppetdb/src/api/index.ts index 530f651af7..905db526c7 100644 --- a/plugins/puppetdb/config.d.ts +++ b/plugins/puppetdb/src/api/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2022 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. @@ -13,18 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** - * Represents the configuration for the Backstage. - */ -export interface Config { - /** - * Configuration of PuppetDB frontend plugin. - */ - puppetdb?: { - /** - * (Required) The host of PuppetDB API instance. - * @visibility frontend - */ - host: string; - }; -} +export type { PuppetDbNode } from './types'; +export { puppetDbApiRef } from './types'; +export { PuppetDbClient } from './PuppetDbClient'; diff --git a/plugins/puppetdb/src/api/types.ts b/plugins/puppetdb/src/api/types.ts new file mode 100644 index 0000000000..e709b403ac --- /dev/null +++ b/plugins/puppetdb/src/api/types.ts @@ -0,0 +1,94 @@ +/* + * Copyright 2022 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 { createApiRef } from '@backstage/core-plugin-api'; + +/** + * A node in PuppetDB. + */ +export type PuppetDbNode = { + /** + * The name of the node that the report was received from. + */ + certname: string; + /** + * The environment for the last received catalog. + */ + catalog_environment: string; + /** + * The environment for the last received fact set. + */ + facts_environment: string; + /** + * The environment for the last received report. + */ + report_environment: string; + /** + * The last time a catalog was received. Timestamps are always ISO-8601 compatible date/time strings. + */ + catalog_timestamp: string; + /** + * The last time a fact set was received. Timestamps are always ISO-8601 compatible date/time strings. + */ + facts_timestamp: string; + /** + * The last time a report run was complete. Timestamps are always ISO-8601 compatible date/time strings. + */ + report_timestamp: string; + /** + * The status of the latest report. Possible values come from Puppet's report status. + */ + latest_report_status: string; + /** + * Indicates whether the most recent report for the node was a noop run. + */ + latest_report_noop: boolean; + /** + * Indicates whether the most recent report for the node contained noop events. + */ + latest_report_noop_pending: boolean; + /** + * A flag indicating whether the latest report for the node included events that remediated configuration drift. + */ + latest_report_corrective_change: boolean; + /** + * Cached catalog status of the last puppet run for the node. Possible values are explicitly_requested, on_failure, not_used or null. + */ + cached_catalog_status: string; + /** + * A hash of the latest report for the node. + */ + latest_report_hash: string; + /** + * The job id associated with the latest report. + */ + latest_report_job_id?: string; + /** + * Indicates whether the node is currently in the deactivated state. + */ + deactivated?: boolean; + /** + * Indicates whether the node is currently in the expired state. + */ + expired?: boolean; +}; + +export const puppetDbApiRef = createApiRef({ + id: 'plugin.puppetdb.service', +}); + +export type PuppetDbApi = { + getPuppetDbNode(puppetDbCertName: string): Promise; +}; diff --git a/plugins/puppetdb/src/components/PuppetDbNodeCard/PuppetDbNodeCard.tsx b/plugins/puppetdb/src/components/PuppetDbNodeCard/PuppetDbNodeCard.tsx new file mode 100644 index 0000000000..64d068c5c7 --- /dev/null +++ b/plugins/puppetdb/src/components/PuppetDbNodeCard/PuppetDbNodeCard.tsx @@ -0,0 +1,49 @@ +/* + * Copyright 2022 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 useAsync from 'react-use/lib/useAsync'; +import { Progress, ResponseErrorPanel } from '@backstage/core-components'; +import { InfoCard } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; +import { puppetDbApiRef } from '../../api'; + +type PuppetDbNodeCardProps = { + certName: string; +}; + +export const PuppetDbNodeCard = (props: PuppetDbNodeCardProps) => { + const { certName } = props; + const puppetDbApi = useApi(puppetDbApiRef); + + const { value, loading, error } = useAsync(async () => { + return puppetDbApi.getPuppetDbNode(certName); + }, [puppetDbApi, certName]); + + if (loading) { + return ; + } else if (error) { + return ; + } + + return ( + + ${JSON.stringify(value)} + + ); +}; diff --git a/plugins/puppetdb/src/components/PuppetDbNodeCard/index.ts b/plugins/puppetdb/src/components/PuppetDbNodeCard/index.ts new file mode 100644 index 0000000000..b5f8184ce1 --- /dev/null +++ b/plugins/puppetdb/src/components/PuppetDbNodeCard/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 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 { PuppetDbNodeCard } from './PuppetDbNodeCard'; diff --git a/plugins/puppetdb/src/components/PuppetDbTab/PuppetDbTab.tsx b/plugins/puppetdb/src/components/PuppetDbTab/PuppetDbTab.tsx index 9f143fd5e6..e9b55ad7c5 100644 --- a/plugins/puppetdb/src/components/PuppetDbTab/PuppetDbTab.tsx +++ b/plugins/puppetdb/src/components/PuppetDbTab/PuppetDbTab.tsx @@ -22,6 +22,7 @@ import { import { useEntity } from '@backstage/plugin-catalog-react'; import { isPuppetDbAvailable } from '../../plugin'; import { ANNOTATION_PUPPET_CERTNAME } from '../../constants'; +import { PuppetDbNodeCard } from '../PuppetDbNodeCard'; export const PuppetDbTab = () => { const { entity } = useEntity(); @@ -32,10 +33,13 @@ export const PuppetDbTab = () => { ); } + // @ts-ignore + const certName = entity.metadata.annotations[ANNOTATION_PUPPET_CERTNAME]; + return ( - TODO: Implement PuppetDB plugin + ); diff --git a/plugins/puppetdb/src/plugin.ts b/plugins/puppetdb/src/plugin.ts index 131ec5a449..a819bf74ba 100644 --- a/plugins/puppetdb/src/plugin.ts +++ b/plugins/puppetdb/src/plugin.ts @@ -14,13 +14,17 @@ * limitations under the License. */ import { + createApiFactory, createPlugin, createRoutableExtension, + discoveryApiRef, + fetchApiRef, } from '@backstage/core-plugin-api'; import { rootRouteRef } from './routes'; import { Entity } from '@backstage/catalog-model'; import { ANNOTATION_PUPPET_CERTNAME } from './constants'; +import { puppetDbApiRef, PuppetDbClient } from './api'; /** * Create the PuppetDB frontend plugin. @@ -28,9 +32,20 @@ import { ANNOTATION_PUPPET_CERTNAME } from './constants'; */ export const puppetdbPlugin = createPlugin({ id: 'puppetdb', - routes: { - root: rootRouteRef, - }, + apis: [ + createApiFactory({ + api: puppetDbApiRef, + deps: { + discoveryApi: discoveryApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ discoveryApi, fetchApi }) => + new PuppetDbClient({ + discoveryApi, + fetchApi, + }), + }), + ], }); /** diff --git a/yarn.lock b/yarn.lock index 346dae8ea8..1ba930f017 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7933,6 +7933,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.12.2