From 9bb20d2b0ddd0cd1a4c041634d86ef97c8d6c0d4 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 19:49:08 +0200 Subject: [PATCH] Add initial code Signed-off-by: Tomas Dabasinskas --- .../config.d.ts | 58 +++++ .../package.json | 6 + .../src/index.ts | 24 ++ .../src/providers/PuppetDBEntityProvider.ts | 236 ++++++++++++++++++ .../providers/PuppetDBEntityProviderConfig.ts | 99 ++++++++ .../src/providers/constants.ts | 29 +++ .../src/providers/index.ts | 19 ++ .../src/puppet/constants.ts | 32 +++ .../src/puppet/index.ts | 24 ++ .../src/puppet/read.ts | 75 ++++++ .../src/puppet/transformers.ts | 65 +++++ .../src/puppet/types.ts | 102 ++++++++ yarn.lock | 6 + 13 files changed, 775 insertions(+) create mode 100644 plugins/catalog-backend-module-puppetdb/config.d.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/index.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/constants.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/index.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/index.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/read.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/types.ts diff --git a/plugins/catalog-backend-module-puppetdb/config.d.ts b/plugins/catalog-backend-module-puppetdb/config.d.ts new file mode 100644 index 0000000000..6514629251 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/config.d.ts @@ -0,0 +1,58 @@ +/* + * 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 { + TaskScheduleDefinition, + TaskScheduleDefinitionConfig, +} from '@backstage/backend-tasks'; + +/** + * Represents the configuration for the Backstage. + */ +export interface Config { + /** + * Configuration for the catalog. + */ + catalog?: { + /** + * Configuration for the providers. + */ + providers?: { + /** + * Puppet entity provider configuration. Uses "default" as default ID for the single config variant. + */ + puppet?: ProviderConfig | Record; + }; + }; +} + +/** + * Configuration of {@link PuppetDBEntityProvider}. + */ +interface ProviderConfig { + /** + * (Required) The host of PuppetDB API instance. + */ + host: string; + /** + * (Optional) PQL query to filter PuppetDB nodes. + */ + query?: string; + /** + * (Optional) Task schedule definition for the refresh. + */ + schedule?: TaskScheduleDefinition; +} diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 5f6a39f73d..fa7f08da1a 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -35,8 +35,14 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/types": "workspace:^", "lodash": "^4.17.21", + "luxon": "^3.0.0", "node-fetch": "^2.6.7", "uuid": "^8.0.0", "winston": "^3.2.1" diff --git a/plugins/catalog-backend-module-puppetdb/src/index.ts b/plugins/catalog-backend-module-puppetdb/src/index.ts new file mode 100644 index 0000000000..b5c5f9d209 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/index.ts @@ -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. + */ + +/** + * A Backstage catalog backend module that helps integrate towards PuppetDB + * + * @packageDocumentation + */ + +export * from './providers'; +export * from './puppet'; diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts new file mode 100644 index 0000000000..a887d4778c --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts @@ -0,0 +1,236 @@ +/* + * 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 { + EntityProvider, + EntityProviderConnection, +} from '@backstage/plugin-catalog-backend'; +import { Logger } from 'winston'; +import { + PuppetDBEntityProviderConfig, + readProviderConfigs, +} from './PuppetDBEntityProviderConfig'; +import { Config } from '@backstage/config'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; +import * as uuid from 'uuid'; +import { ResourceTransformer, defaultResourceTransformer } from '../puppet'; +import { + ANNOTATION_LOCATION, + ANNOTATION_ORIGIN_LOCATION, + Entity, +} from '@backstage/catalog-model/'; +import { merge } from 'lodash'; +import { readPuppetNodes } from '../puppet/read'; +import { ENDPOINT_NODES } from '../puppet/constants'; + +/** + * Reads nodes from [PuppetDB](https://www.puppet.com/docs/puppet/6/puppetdb_overview.html) + * based on the provided query and registers them as Resource entities in the catalog. + * + * @public + */ +export class PuppetDBEntityProvider implements EntityProvider { + private readonly config: PuppetDBEntityProviderConfig; + private readonly logger: Logger; + private readonly scheduleFn: () => Promise; + private readonly transformer: ResourceTransformer; + private connection?: EntityProviderConnection; + + /** + * Creates instances of {@link PuppetDBEntityProvider} from a configuration. + * + * @param config - The configuration to read provider information from. + * @param deps - The dependencies for {@link PuppetDBEntityProvider}. + * + * @returns A list of {@link PuppetDBEntityProvider} instances. + */ + static fromConfig( + config: Config, + deps: { + logger: Logger; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + transformer?: ResourceTransformer; + }, + ): PuppetDBEntityProvider[] { + if (!deps.schedule && !deps.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + + return readProviderConfigs(config).map(providerConfig => { + if (!deps.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for puppet-provider:${providerConfig.id}.`, + ); + } + + const taskRunner = + deps.schedule ?? + deps.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + + const transformer = deps.transformer ?? defaultResourceTransformer; + + return new PuppetDBEntityProvider( + providerConfig, + deps.logger, + taskRunner, + transformer, + ); + }); + } + + /** + * Creates an instance of {@link PuppetDBEntityProvider}. + * + * @param config - Configuration of the provider. + * @param logger - The instance of a {@link Logger}. + * @param taskRunner - The instance of {@link TaskRunner}. + * @param transformer - A {@link ResourceTransformer} function. + * + * @private + */ + private constructor( + config: PuppetDBEntityProviderConfig, + logger: Logger, + taskRunner: TaskRunner, + transformer: ResourceTransformer, + ) { + this.config = config; + this.logger = logger.child({ + target: this.getProviderName(), + }); + this.scheduleFn = this.createScheduleFn(taskRunner); + this.transformer = transformer; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + async connect(connection: EntityProviderConnection): Promise { + this.connection = connection; + await this.scheduleFn(); + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + getProviderName(): string { + return `puppetdb-provider:${this.config.id}`; + } + + /** + * Creates a function that can be used to schedule a refresh of the catalog. + * + * @param taskRunner - The instance of {@link TaskRunner}. + * + * @private + */ + private createScheduleFn(taskRunner: TaskRunner): () => Promise { + return async () => { + const taskId = `${this.getProviderName()}:refresh`; + return taskRunner.run({ + id: taskId, + fn: async () => { + const logger = this.logger.child({ + class: PuppetDBEntityProvider.prototype.constructor.name, + taskId, + taskInstanceId: uuid.v4(), + }); + try { + await this.refresh(logger); + } catch (error) { + logger.error(`${this.getProviderName()} refresh failed`, error); + } + }, + }); + }; + } + + /** + * Refreshes the catalog by reading nodes from PuppetDB and registering them as Resource Entities. + * + * @param logger - The instance of a {@link Logger}. + */ + async refresh(logger: Logger) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + const { markReadComplete } = trackProgress(logger); + const entities = await readPuppetNodes(this.config, { + logger, + transformer: this.transformer, + }); + const { markCommitComplete } = markReadComplete(entities); + + await this.connection.applyMutation({ + type: 'full', + entities: [...entities].map(entity => ({ + locationKey: this.getProviderName(), + entity: withLocations(this.config.host, entity), + })), + }); + markCommitComplete(entities); + } +} + +/** + * Ensures the entities have required annotation data. + * + * @param host - The host of the PuppetDB instance. + * @param entity - The entity to add the annotations to. + * + * @returns Entity with @{@link ANNOTATION_LOCATION} and @{@link ANNOTATION_ORIGIN_LOCATION} annotations. + */ +function withLocations(host: string, entity: Entity): Entity { + const location = new URL(host); + location.pathname = `${ENDPOINT_NODES}/${entity.metadata?.name}`; + + return merge( + { + metadata: { + annotations: { + [ANNOTATION_LOCATION]: `url:${location.toString()}`, + [ANNOTATION_ORIGIN_LOCATION]: `url:${location.toString()}`, + }, + }, + }, + entity, + ) as Entity; +} + +/** + * Tracks the progress of the PuppetDB read and commit operations. + * + * @param logger - The instance of a {@link Logger}. + */ +function trackProgress(logger: Logger) { + let timestamp = Date.now(); + + function markReadComplete(entities: Entity[]) { + const readDuration = ((Date.now() - timestamp) / 1000).toFixed(1); + timestamp = Date.now(); + logger.info( + `Read ${entities?.length ?? 0} in ${readDuration} seconds. Committing...`, + ); + return { markCommitComplete }; + } + + function markCommitComplete(entities: Entity[]) { + const commitDuration = ((Date.now() - timestamp) / 1000).toFixed(1); + logger.info( + `Committed ${entities?.length ?? 0} in ${commitDuration} seconds.`, + ); + } + + return { markReadComplete }; +} diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts new file mode 100644 index 0000000000..e96a4a11e9 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts @@ -0,0 +1,99 @@ +/* + * 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 { + readTaskScheduleDefinitionFromConfig, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { Config } from '@backstage/config'; +import { DEFAULT_PROVIDER_ID } from './constants'; + +/** + * Configuration of {@link PuppetDBEntityProvider}. + * + * @public + */ +export type PuppetDBEntityProviderConfig = { + /** + * ID of the provider. + */ + id: string; + /** + * (Required) The host of PuppetDB API instance. + */ + host: string; + /** + * (Optional) PQL query to filter PuppetDB nodes. + */ + query?: string; + /** + * (Optional) Task schedule definition for the refresh. + */ + schedule?: TaskScheduleDefinition; +}; + +/** + * Reads the configuration of the PuppetDB Entity Providers. + * + * @param config - The application configuration. + * + * @returns PuppetDB Entity Provider configurations list. + */ +export function readProviderConfigs( + config: Config, +): PuppetDBEntityProviderConfig[] { + const providersConfig = config.getOptionalConfig( + 'catalog.providers.puppetdb', + ); + if (!providersConfig) { + return []; + } + + if (providersConfig.has('host')) { + return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)]; + } + + return providersConfig.keys().map(id => { + return readProviderConfig(id, providersConfig.getConfig(id)); + }); +} + +/** + * Reads the configuration for the PuppetDB Entity Provider. + * + * @param id - ID of the provider. + * @param config - The application configuration. + * + * @returns The PuppetDB Entity Provider configuration. + */ +function readProviderConfig( + id: string, + config: Config, +): PuppetDBEntityProviderConfig { + const host = config.getString('host').replace(/\/+$/, ''); + const query = config.getOptionalString('query'); + + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + + return { + id, + host, + query, + schedule, + }; +} diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts b/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts new file mode 100644 index 0000000000..02b333c613 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * Name of the default provider when a using a simple configuration. + * + * @public + */ +export const DEFAULT_PROVIDER_ID = 'default'; + +/** + * Default owner for entities created by the PuppetDB provider. + * + * @public + */ +export const DEFAULT_OWNER = 'unknown'; diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/index.ts b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts new file mode 100644 index 0000000000..4e220395ba --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +export { PuppetDBEntityProvider } from './PuppetDBEntityProvider'; +export type { PuppetDBEntityProviderConfig } from './PuppetDBEntityProviderConfig'; +export { DEFAULT_PROVIDER_ID, DEFAULT_OWNER } from './constants'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts new file mode 100644 index 0000000000..ef355f22d3 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts @@ -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. + */ + +/** + * Annotation for specifying the certificate name of a node in PuppetDB. + * + * @public + */ +export const ANNOTATION_PUPPET_CERTNAME = 'puppet.com/certname'; + +/** + * Path of PuppetDB FactSets endpoint. + */ +export const ENDPOINT_FACTSETS = '/pdb/query/v4/factsets'; + +/** + * Path of PuppetDB Nodes endpoint. + */ +export const ENDPOINT_NODES = '/pdb/query/v4/nodes'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/index.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/index.ts new file mode 100644 index 0000000000..644d111ac1 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/index.ts @@ -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. + */ + +export { ANNOTATION_PUPPET_CERTNAME } from './constants'; +export { defaultResourceTransformer } from './transformers'; +export type { + PuppetNode, + PuppetFactSet, + PuppetFact, + ResourceTransformer, +} from './types'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts new file mode 100644 index 0000000000..edd33bc76a --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts @@ -0,0 +1,75 @@ +/* + * 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 { PuppetDBEntityProviderConfig } from '../providers'; +import { PuppetNode, ResourceTransformer } from './types'; +import { ResourceEntity } from '@backstage/catalog-model/'; +import { defaultResourceTransformer } from './transformers'; +import { ResponseError } from '@backstage/errors'; +import { ENDPOINT_FACTSETS } from './constants'; +import { Logger } from 'winston'; + +/** + * Reads nodes and their facts from PuppetDB. + * + * @param config - The provider configuration. + * @param opts - Additional options. + */ +export async function readPuppetNodes( + config: PuppetDBEntityProviderConfig, + opts?: { + transformer?: ResourceTransformer; + logger?: Logger; + }, +): Promise { + const transformFn = opts?.transformer ?? defaultResourceTransformer; + const url = new URL(config.host); + url.pathname = ENDPOINT_FACTSETS; + + if (config.query) { + url.searchParams.set('query', config.query); + } + + if (opts?.logger) { + opts.logger + .child({ url: url.toString() }) + .debug('Reading nodes from PuppetDB'); + } + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + const nodes = (await response.json()) as PuppetNode[]; + const entities: ResourceEntity[] = []; + + for (const node of nodes) { + const entity = await transformFn(node, config); + if (entity) { + entities.push(entity); + } + } + + return entities; +} diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts new file mode 100644 index 0000000000..ca0d2a4ccc --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts @@ -0,0 +1,65 @@ +/* + * 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 { ResourceTransformer } from './types'; +import { DEFAULT_NAMESPACE, ResourceEntity } from '@backstage/catalog-model'; +import { DEFAULT_OWNER } from '../providers'; +import { ANNOTATION_PUPPET_CERTNAME } from './constants'; + +/** + * A default implementation of the {@link ResourceTransformer}. + * + * @param node - The found PuppetDB node entry in its source format. This is the entry that you want to transform. + * @param _config - The configuration for the entity provider. + * + * @returns A `ResourceEntity`. + * + * @public + */ +export const defaultResourceTransformer: ResourceTransformer = async ( + node, + _config, +) => { + const certName = node.certname.toLowerCase(); + const type = node.facts?.data?.find(e => e.name === 'is_virtual')?.value + ? 'virtual-machine' + : 'physical-server'; + const kernel = node.facts?.data?.find(e => e.name === 'kernel')?.value; + + const entity: ResourceEntity = { + apiVersion: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: certName, + annotations: { + [ANNOTATION_PUPPET_CERTNAME]: certName, + }, + namespace: DEFAULT_NAMESPACE, + description: node.facts?.data + ?.find(e => e.name === 'ipaddress') + ?.value?.toString(), + tags: kernel ? [kernel.toString().toLowerCase()] : [], + }, + spec: { + type: type, + owner: DEFAULT_OWNER, + dependsOn: [], + dependencyOf: [], + }, + }; + + return entity; +}; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts new file mode 100644 index 0000000000..7dc951f49f --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts @@ -0,0 +1,102 @@ +/* + * 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 { ResourceEntity } from '@backstage/catalog-model'; +import { JsonValue } from '@backstage/types'; +import { PuppetDBEntityProviderConfig } from '../providers/PuppetDBEntityProviderConfig'; + +/** + * Customize the ingested Resource entity. + * + * @param node - The found PuppetDB node entry in its source format. This is the entry that you want to transform. + * @param config - The configuration for the entity provider. + * + * @returns A `ResourceEntity` or `undefined` if you want to ignore the found group for being ingested by the catalog. + * + * @public + */ +export type ResourceTransformer = ( + node: PuppetNode, + config: PuppetDBEntityProviderConfig, +) => Promise; + +/** + * A node in PuppetDB. + * + * @public + */ +export type PuppetNode = { + /** + * The most recent time of fact submission from the associated certname. + */ + timestamp: string; + /** + * The certname associated with the factset. + */ + certname: string; + /** + * A hash of the factset's certname, environment, timestamp, facts, and producer_timestamp. + */ + hash: string; + /** + * The most recent time of fact submission for the relevant certname from the Puppet Server. + */ + producer_timestamp: string; + /** + * The certname of the Puppet Server that sent the factset to PuppetDB. + */ + producer: string; + /** + * The environment associated with the fact. + */ + environment: string; + /** + * The facts associated with the factset. + */ + facts: PuppetFactSet; +}; + +/** + * The set of all facts for a single certname in PuppetDB. + * + * @public + */ +export type PuppetFactSet = { + /** + * The array of facts. + */ + data: PuppetFact[]; + /** + * The URL to retrieve more information about the facts. + */ + href: string; +}; + +/** + * A fact in PuppetDB. + * + * @public + */ +export type PuppetFact = { + /** + * The name of the fact. + */ + name: string; + /** + * The value of the fact, in JSON format. + */ + value: JsonValue; +}; diff --git a/yarn.lock b/yarn.lock index 33261fc7da..eb17085b2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5211,10 +5211,16 @@ __metadata: resolution: "@backstage/plugin-catalog-backend-module-puppetdb-backend@workspace:plugins/catalog-backend-module-puppetdb" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-tasks": "workspace:^" + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-catalog-backend": "workspace:^" + "@backstage/types": "workspace:^" "@types/lodash": ^4.14.151 lodash: ^4.17.21 + luxon: ^3.0.0 msw: ^0.49.0 node-fetch: ^2.6.7 uuid: ^8.0.0