@backstage/plugin-catalog-backend-module-puppetdb 0.0.0-nightly-20230304023150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # @backstage/plugin-catalog-backend-module-puppetdb
2
+
3
+ ## 0.0.0-nightly-20230304023150
4
+
5
+ ### Minor Changes
6
+
7
+ - a1efcf9a658: Initial version of the plugin.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @backstage/plugin-catalog-backend@0.0.0-nightly-20230304023150
13
+ - @backstage/errors@0.0.0-nightly-20230304023150
14
+ - @backstage/backend-tasks@0.0.0-nightly-20230304023150
15
+ - @backstage/backend-common@0.0.0-nightly-20230304023150
16
+ - @backstage/catalog-model@0.0.0-nightly-20230304023150
17
+ - @backstage/config@0.0.0-nightly-20230304023150
18
+ - @backstage/types@1.0.2
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Catalog Backend Module for Puppet
2
+
3
+ This is an extension module to the `plugin-catalog-backend` plugin, providing an `PuppetDbEntityProvider` that can be used to ingest
4
+ [Resource entities](https://backstage.io/docs/features/software-catalog/descriptor-format#kind-resource) from a
5
+ [PuppetDB](https://www.puppet.com/docs/puppet/6/puppetdb_overview.html) instance(s). This provider is useful if you want to import nodes
6
+ from your PuppetDB into Backstage.
7
+
8
+ ## Installation
9
+
10
+ The provider is not installed by default, therefore you have to add a dependency to `@backstage/plugin-catalog-backend-module-puppetdb`
11
+ to your backend package:
12
+
13
+ ```bash
14
+ # From your Backstage root directory
15
+ yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-puppetdb
16
+ ```
17
+
18
+ Update the catalog plugin initialization in your backend to add the provider and schedule it:
19
+
20
+ ```diff
21
+ + import { PuppetDbEntityProvider } from '@backstage/plugin-catalog-backend-module-puppetdb';
22
+
23
+ export default async function createPlugin(
24
+ env: PluginEnvironment,
25
+ ): Promise<Router> {
26
+ const builder = await CatalogBuilder.create(env);
27
+
28
+ + builder.addEntityProvider(
29
+ + PuppetDbEntityProvider.fromConfig(env.config, {
30
+ + logger: env.logger,
31
+ + schedule: env.scheduler.createScheduledTaskRunner({
32
+ + frequency: { minutes: 10 },
33
+ + timeout: { minutes: 50 },
34
+ + initialDelay: { seconds: 15}
35
+ + }),
36
+ + });
37
+ + );
38
+ ```
39
+
40
+ After this, you also have to add some configuration in your app-config that describes what you want to import for that target.
41
+
42
+ ## Configuration
43
+
44
+ The following configuration is an example of how a setup could look for importing nodes from an internal PuppetDB instance:
45
+
46
+ ```yaml
47
+ catalog:
48
+ providers:
49
+ puppetdb:
50
+ default:
51
+ # (Required) The base URL of PuppetDB API instance:
52
+ baseUrl: https://puppetdb.example.com
53
+
54
+ # (Optional) Query to filter PuppetDB nodes:
55
+ #query: '["=","certname","example.com"]'
56
+ ```
57
+
58
+ ## Customize the Provider
59
+
60
+ The default ingestion behaviour will likely not work for all use cases - you will want to set proper `Owner`, `System` and other fields for the
61
+ ingested resources. In case you want to customize the ingested entities, the provider allows to pass a transformer for resources. Here we will show an example
62
+ of overriding the default transformer.
63
+
64
+ 1. Create a transformer:
65
+
66
+ ```ts
67
+ export const customResourceTransformer: ResourceTransformer = async (
68
+ node,
69
+ config,
70
+ ): Promise<GroupEntity | undefined> => {
71
+ // Transformations may change namespace, owner, change entity naming pattern, add labels, annotations, etc.
72
+
73
+ // Create the Resource Entity on your own, or wrap the default transformer
74
+ return await defaultResourceTransformer(node, config);
75
+ };
76
+ ```
77
+
78
+ 2. Configure the provider with the transformer:
79
+
80
+ ```ts
81
+ const puppetDbEntityProvider = PuppetDbEntityProvider.fromConfig(env.config, {
82
+ logger: env.logger,
83
+ schedule: env.scheduler.createScheduledTaskRunner({
84
+ frequency: { minutes: 10 },
85
+ timeout: { minutes: 50 },
86
+ initialDelay: { seconds: 15 },
87
+ }),
88
+ transformer: customResourceTransformer,
89
+ });
90
+ ```
package/config.d.ts ADDED
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Copyright 2020 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { TaskScheduleDefinition } from '@backstage/backend-tasks';
18
+
19
+ /**
20
+ * Represents the configuration for the Backstage.
21
+ */
22
+ export interface Config {
23
+ /**
24
+ * Configuration for the catalog.
25
+ */
26
+ catalog?: {
27
+ /**
28
+ * Configuration for the providers.
29
+ */
30
+ providers?: {
31
+ /**
32
+ * PuppetDB Entity Provider configuration. Uses "default" as default ID for the single config variant.
33
+ */
34
+ puppetdb?:
35
+ | {
36
+ /**
37
+ * (Required) The base URL of PuppetDB API instance.
38
+ */
39
+ baseUrl: string;
40
+ /**
41
+ * (Optional) PQL query to filter PuppetDB nodes.
42
+ */
43
+ query?: string;
44
+ /**
45
+ * (Optional) Task schedule definition for the refresh.
46
+ */
47
+ schedule?: TaskScheduleDefinition;
48
+ }
49
+ | Record<
50
+ string,
51
+ {
52
+ /**
53
+ * (Required) The base URL of PuppetDB API instance.
54
+ */
55
+ baseUrl: string;
56
+ /**
57
+ * (Optional) PQL query to filter PuppetDB nodes.
58
+ */
59
+ query?: string;
60
+ /**
61
+ * (Optional) Task schedule definition for the refresh.
62
+ */
63
+ schedule?: TaskScheduleDefinition;
64
+ }
65
+ >;
66
+ };
67
+ };
68
+ }
@@ -0,0 +1,275 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var backendTasks = require('@backstage/backend-tasks');
6
+ var uuid = require('uuid');
7
+ var catalogModel = require('@backstage/catalog-model');
8
+ var _ = require('@backstage/catalog-model/');
9
+ var lodash = require('lodash');
10
+ var fetch = require('node-fetch');
11
+ var errors = require('@backstage/errors');
12
+
13
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
+
15
+ function _interopNamespace(e) {
16
+ if (e && e.__esModule) return e;
17
+ var n = Object.create(null);
18
+ if (e) {
19
+ Object.keys(e).forEach(function (k) {
20
+ if (k !== 'default') {
21
+ var d = Object.getOwnPropertyDescriptor(e, k);
22
+ Object.defineProperty(n, k, d.get ? d : {
23
+ enumerable: true,
24
+ get: function () { return e[k]; }
25
+ });
26
+ }
27
+ });
28
+ }
29
+ n["default"] = e;
30
+ return Object.freeze(n);
31
+ }
32
+
33
+ var uuid__namespace = /*#__PURE__*/_interopNamespace(uuid);
34
+ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
35
+
36
+ const DEFAULT_PROVIDER_ID = "default";
37
+
38
+ function readProviderConfigs(config) {
39
+ const providersConfig = config.getOptionalConfig(
40
+ "catalog.providers.puppetdb"
41
+ );
42
+ if (!providersConfig) {
43
+ return [];
44
+ }
45
+ if (providersConfig.has("baseUrl")) {
46
+ return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)];
47
+ }
48
+ return providersConfig.keys().map((id) => {
49
+ return readProviderConfig(id, providersConfig.getConfig(id));
50
+ });
51
+ }
52
+ function readProviderConfig(id, config) {
53
+ const baseUrl = config.getString("baseUrl").replace(/\/+$/, "");
54
+ const query = config.getOptionalString("query");
55
+ const schedule = config.has("schedule") ? backendTasks.readTaskScheduleDefinitionFromConfig(config.getConfig("schedule")) : void 0;
56
+ return {
57
+ id,
58
+ baseUrl,
59
+ query,
60
+ schedule
61
+ };
62
+ }
63
+
64
+ const ANNOTATION_PUPPET_CERTNAME = "puppet.com/certname";
65
+ const ENDPOINT_FACTSETS = "pdb/query/v4/factsets";
66
+ const ENDPOINT_NODES = "pdb/query/v4/nodes";
67
+ const DEFAULT_ENTITY_OWNER = "unknown";
68
+
69
+ const defaultResourceTransformer = async (node, _config) => {
70
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
71
+ const certName = node.certname.toLocaleLowerCase("en-US");
72
+ const type = ((_c = (_b = (_a = node.facts) == null ? void 0 : _a.data) == null ? void 0 : _b.find((e) => e.name === "is_virtual")) == null ? void 0 : _c.value) ? "virtual-machine" : "physical-server";
73
+ const kernel = (_f = (_e = (_d = node.facts) == null ? void 0 : _d.data) == null ? void 0 : _e.find((e) => e.name === "kernel")) == null ? void 0 : _f.value;
74
+ return {
75
+ apiVersion: "backstage.io/v1beta1",
76
+ kind: "Resource",
77
+ metadata: {
78
+ name: certName,
79
+ annotations: {
80
+ [ANNOTATION_PUPPET_CERTNAME]: certName
81
+ },
82
+ namespace: catalogModel.DEFAULT_NAMESPACE,
83
+ description: (_j = (_i = (_h = (_g = node.facts) == null ? void 0 : _g.data) == null ? void 0 : _h.find((e) => e.name === "ipaddress")) == null ? void 0 : _i.value) == null ? void 0 : _j.toString(),
84
+ tags: kernel ? [kernel.toString().toLocaleLowerCase("en-US")] : []
85
+ },
86
+ spec: {
87
+ type,
88
+ owner: DEFAULT_ENTITY_OWNER,
89
+ dependsOn: [],
90
+ dependencyOf: []
91
+ }
92
+ };
93
+ };
94
+
95
+ async function readPuppetNodes(config, opts) {
96
+ var _a;
97
+ const transformFn = (_a = opts == null ? void 0 : opts.transformer) != null ? _a : defaultResourceTransformer;
98
+ const url = new URL(ENDPOINT_FACTSETS, config.baseUrl);
99
+ if (config.query) {
100
+ url.searchParams.set("query", config.query);
101
+ }
102
+ if (opts == null ? void 0 : opts.logger) {
103
+ opts.logger.debug("Reading nodes from PuppetDB", { url: url.toString() });
104
+ }
105
+ const response = await fetch__default["default"](url.toString(), {
106
+ method: "GET",
107
+ headers: {
108
+ "Content-Type": "application/json",
109
+ Accept: "application/json"
110
+ }
111
+ });
112
+ if (!response.ok) {
113
+ throw await errors.ResponseError.fromResponse(response);
114
+ }
115
+ const nodes = await response.json();
116
+ const entities = [];
117
+ for (const node of nodes) {
118
+ const entity = await transformFn(node, config);
119
+ if (entity) {
120
+ entities.push(entity);
121
+ }
122
+ }
123
+ return entities;
124
+ }
125
+
126
+ class PuppetDbEntityProvider {
127
+ /**
128
+ * Creates instances of {@link PuppetDbEntityProvider} from a configuration.
129
+ *
130
+ * @param config - The configuration to read provider information from.
131
+ * @param deps - The dependencies for {@link PuppetDbEntityProvider}.
132
+ *
133
+ * @returns A list of {@link PuppetDbEntityProvider} instances.
134
+ */
135
+ static fromConfig(config, deps) {
136
+ if (!deps.schedule && !deps.scheduler) {
137
+ throw new Error("Either schedule or scheduler must be provided.");
138
+ }
139
+ return readProviderConfigs(config).map((providerConfig) => {
140
+ var _a, _b;
141
+ if (!deps.schedule && !providerConfig.schedule) {
142
+ throw new Error(
143
+ `No schedule provided neither via code nor config for puppet-provider:${providerConfig.id}.`
144
+ );
145
+ }
146
+ const taskRunner = (_a = deps.schedule) != null ? _a : deps.scheduler.createScheduledTaskRunner(providerConfig.schedule);
147
+ const transformer = (_b = deps.transformer) != null ? _b : defaultResourceTransformer;
148
+ return new PuppetDbEntityProvider(
149
+ providerConfig,
150
+ deps.logger,
151
+ taskRunner,
152
+ transformer
153
+ );
154
+ });
155
+ }
156
+ /**
157
+ * Creates an instance of {@link PuppetDbEntityProvider}.
158
+ *
159
+ * @param config - Configuration of the provider.
160
+ * @param logger - The instance of a {@link Logger}.
161
+ * @param taskRunner - The instance of {@link TaskRunner}.
162
+ * @param transformer - A {@link ResourceTransformer} function.
163
+ *
164
+ * @private
165
+ */
166
+ constructor(config, logger, taskRunner, transformer) {
167
+ this.config = config;
168
+ this.logger = logger.child({
169
+ target: this.getProviderName()
170
+ });
171
+ this.scheduleFn = this.createScheduleFn(taskRunner);
172
+ this.transformer = transformer;
173
+ }
174
+ /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */
175
+ async connect(connection) {
176
+ this.connection = connection;
177
+ await this.scheduleFn();
178
+ }
179
+ /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */
180
+ getProviderName() {
181
+ return `puppetdb-provider:${this.config.id}`;
182
+ }
183
+ /**
184
+ * Creates a function that can be used to schedule a refresh of the catalog.
185
+ *
186
+ * @param taskRunner - The instance of {@link TaskRunner}.
187
+ *
188
+ * @private
189
+ */
190
+ createScheduleFn(taskRunner) {
191
+ return async () => {
192
+ const taskId = `${this.getProviderName()}:refresh`;
193
+ return taskRunner.run({
194
+ id: taskId,
195
+ fn: async () => {
196
+ const logger = this.logger.child({
197
+ class: PuppetDbEntityProvider.prototype.constructor.name,
198
+ taskId,
199
+ taskInstanceId: uuid__namespace.v4()
200
+ });
201
+ try {
202
+ await this.refresh(logger);
203
+ } catch (error) {
204
+ logger.error(`${this.getProviderName()} refresh failed`, error);
205
+ }
206
+ }
207
+ });
208
+ };
209
+ }
210
+ /**
211
+ * Refreshes the catalog by reading nodes from PuppetDB and registering them as Resource Entities.
212
+ *
213
+ * @param logger - The instance of a Logger.
214
+ */
215
+ async refresh(logger) {
216
+ if (!this.connection) {
217
+ throw new Error("Not initialized");
218
+ }
219
+ const { markReadComplete } = trackProgress(logger);
220
+ const entities = await readPuppetNodes(this.config, {
221
+ logger,
222
+ transformer: this.transformer
223
+ });
224
+ const { markCommitComplete } = markReadComplete(entities);
225
+ await this.connection.applyMutation({
226
+ type: "full",
227
+ entities: [...entities].map((entity) => ({
228
+ locationKey: this.getProviderName(),
229
+ entity: withLocations(this.config.baseUrl, entity)
230
+ }))
231
+ });
232
+ markCommitComplete(entities);
233
+ }
234
+ }
235
+ function withLocations(baseUrl, entity) {
236
+ var _a;
237
+ const location = `${baseUrl}/${ENDPOINT_NODES}/${(_a = entity.metadata) == null ? void 0 : _a.name}`;
238
+ return lodash.merge(
239
+ {
240
+ metadata: {
241
+ annotations: {
242
+ [_.ANNOTATION_LOCATION]: `url:${location}`,
243
+ [_.ANNOTATION_ORIGIN_LOCATION]: `url:${location}`
244
+ }
245
+ }
246
+ },
247
+ entity
248
+ );
249
+ }
250
+ function trackProgress(logger) {
251
+ let timestamp = Date.now();
252
+ function markReadComplete(entities) {
253
+ var _a;
254
+ const readDuration = ((Date.now() - timestamp) / 1e3).toFixed(1);
255
+ timestamp = Date.now();
256
+ logger.info(
257
+ `Read ${(_a = entities == null ? void 0 : entities.length) != null ? _a : 0} in ${readDuration} seconds. Committing...`
258
+ );
259
+ return { markCommitComplete };
260
+ }
261
+ function markCommitComplete(entities) {
262
+ var _a;
263
+ const commitDuration = ((Date.now() - timestamp) / 1e3).toFixed(1);
264
+ logger.info(
265
+ `Committed ${(_a = entities == null ? void 0 : entities.length) != null ? _a : 0} in ${commitDuration} seconds.`
266
+ );
267
+ }
268
+ return { markReadComplete };
269
+ }
270
+
271
+ exports.ANNOTATION_PUPPET_CERTNAME = ANNOTATION_PUPPET_CERTNAME;
272
+ exports.DEFAULT_PROVIDER_ID = DEFAULT_PROVIDER_ID;
273
+ exports.PuppetDbEntityProvider = PuppetDbEntityProvider;
274
+ exports.defaultResourceTransformer = defaultResourceTransformer;
275
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/providers/constants.ts","../src/providers/PuppetDbEntityProviderConfig.ts","../src/puppet/constants.ts","../src/puppet/transformers.ts","../src/puppet/read.ts","../src/providers/PuppetDbEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Name of the default provider when a using a simple configuration.\n *\n * @public\n */\nexport const DEFAULT_PROVIDER_ID = 'default';\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n readTaskScheduleDefinitionFromConfig,\n TaskScheduleDefinition,\n} from '@backstage/backend-tasks';\nimport { Config } from '@backstage/config';\nimport { DEFAULT_PROVIDER_ID } from './constants';\n\n/**\n * Configuration of {@link PuppetDbEntityProvider}.\n *\n * @public\n */\nexport type PuppetDbEntityProviderConfig = {\n /**\n * ID of the provider.\n */\n id: string;\n /**\n * (Required) The base URL of PuppetDB API instance.\n */\n baseUrl: string;\n /**\n * (Optional) PQL query to filter PuppetDB nodes.\n */\n query?: string;\n /**\n * (Optional) Task schedule definition for the refresh.\n */\n schedule?: TaskScheduleDefinition;\n};\n\n/**\n * Reads the configuration of the PuppetDB Entity Providers.\n *\n * @param config - The application configuration.\n *\n * @returns PuppetDB Entity Provider configurations list.\n */\nexport function readProviderConfigs(\n config: Config,\n): PuppetDbEntityProviderConfig[] {\n const providersConfig = config.getOptionalConfig(\n 'catalog.providers.puppetdb',\n );\n if (!providersConfig) {\n return [];\n }\n\n if (providersConfig.has('baseUrl')) {\n return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)];\n }\n\n return providersConfig.keys().map(id => {\n return readProviderConfig(id, providersConfig.getConfig(id));\n });\n}\n\n/**\n * Reads the configuration for the PuppetDB Entity Provider.\n *\n * @param id - ID of the provider.\n * @param config - The application configuration.\n *\n * @returns The PuppetDB Entity Provider configuration.\n */\nfunction readProviderConfig(\n id: string,\n config: Config,\n): PuppetDbEntityProviderConfig {\n const baseUrl = config.getString('baseUrl').replace(/\\/+$/, '');\n const query = config.getOptionalString('query');\n\n const schedule = config.has('schedule')\n ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule'))\n : undefined;\n\n return {\n id,\n baseUrl,\n query,\n schedule,\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Annotation for specifying the certificate name of a node in PuppetDB.\n *\n * @public\n */\nexport const ANNOTATION_PUPPET_CERTNAME = 'puppet.com/certname';\n\n/**\n * Path of PuppetDB FactSets endpoint.\n */\nexport const ENDPOINT_FACTSETS = 'pdb/query/v4/factsets';\n\n/**\n * Path of PuppetDB Nodes endpoint.\n */\nexport const ENDPOINT_NODES = 'pdb/query/v4/nodes';\n\n/**\n * Default owner for entities created by the PuppetDB provider.\n *\n * @public\n */\nexport const DEFAULT_ENTITY_OWNER = 'unknown';\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ResourceTransformer } from './types';\nimport { DEFAULT_NAMESPACE, ResourceEntity } from '@backstage/catalog-model';\nimport { ANNOTATION_PUPPET_CERTNAME, DEFAULT_ENTITY_OWNER } from './constants';\n\n/**\n * A default implementation of the {@link ResourceTransformer}.\n *\n * @param node - The found PuppetDB node entry in its source format. This is the entry that you want to transform.\n * @param _config - The configuration for the entity provider.\n *\n * @returns A `ResourceEntity`.\n *\n * @public\n */\nexport const defaultResourceTransformer: ResourceTransformer = async (\n node,\n _config,\n): Promise<ResourceEntity | undefined> => {\n const certName = node.certname.toLocaleLowerCase('en-US');\n const type = node.facts?.data?.find(e => e.name === 'is_virtual')?.value\n ? 'virtual-machine'\n : 'physical-server';\n const kernel = node.facts?.data?.find(e => e.name === 'kernel')?.value;\n\n return {\n apiVersion: 'backstage.io/v1beta1',\n kind: 'Resource',\n metadata: {\n name: certName,\n annotations: {\n [ANNOTATION_PUPPET_CERTNAME]: certName,\n },\n namespace: DEFAULT_NAMESPACE,\n description: node.facts?.data\n ?.find(e => e.name === 'ipaddress')\n ?.value?.toString(),\n tags: kernel ? [kernel.toString().toLocaleLowerCase('en-US')] : [],\n },\n spec: {\n type: type,\n owner: DEFAULT_ENTITY_OWNER,\n dependsOn: [],\n dependencyOf: [],\n },\n };\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { PuppetDbEntityProviderConfig } from '../providers';\nimport { PuppetNode, ResourceTransformer } from './types';\nimport { ResourceEntity } from '@backstage/catalog-model/';\nimport { defaultResourceTransformer } from './transformers';\nimport fetch from 'node-fetch';\nimport { ResponseError } from '@backstage/errors';\nimport { ENDPOINT_FACTSETS } from './constants';\nimport { Logger } from 'winston';\n\n/**\n * Reads nodes and their facts from PuppetDB.\n *\n * @param config - The provider configuration.\n * @param opts - Additional options.\n */\nexport async function readPuppetNodes(\n config: PuppetDbEntityProviderConfig,\n opts?: {\n transformer?: ResourceTransformer;\n logger?: Logger;\n },\n): Promise<ResourceEntity[]> {\n const transformFn = opts?.transformer ?? defaultResourceTransformer;\n const url = new URL(ENDPOINT_FACTSETS, config.baseUrl);\n\n if (config.query) {\n url.searchParams.set('query', config.query);\n }\n\n if (opts?.logger) {\n opts.logger.debug('Reading nodes from PuppetDB', { url: url.toString() });\n }\n\n const response = await fetch(url.toString(), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const nodes = (await response.json()) as PuppetNode[];\n const entities: ResourceEntity[] = [];\n\n for (const node of nodes) {\n const entity = await transformFn(node, config);\n if (entity) {\n entities.push(entity);\n }\n }\n\n return entities;\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-backend';\nimport { Logger } from 'winston';\nimport {\n PuppetDbEntityProviderConfig,\n readProviderConfigs,\n} from './PuppetDbEntityProviderConfig';\nimport { Config } from '@backstage/config';\nimport { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks';\nimport * as uuid from 'uuid';\nimport { ResourceTransformer, defaultResourceTransformer } from '../puppet';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n} from '@backstage/catalog-model/';\nimport { merge } from 'lodash';\nimport { readPuppetNodes } from '../puppet/read';\nimport { ENDPOINT_NODES } from '../puppet/constants';\n\n/**\n * Reads nodes from [PuppetDB](https://www.puppet.com/docs/puppet/6/puppetdb_overview.html)\n * based on the provided query and registers them as Resource entities in the catalog.\n *\n * @public\n */\nexport class PuppetDbEntityProvider implements EntityProvider {\n private readonly config: PuppetDbEntityProviderConfig;\n private readonly logger: Logger;\n private readonly scheduleFn: () => Promise<void>;\n private readonly transformer: ResourceTransformer;\n private connection?: EntityProviderConnection;\n\n /**\n * Creates instances of {@link PuppetDbEntityProvider} from a configuration.\n *\n * @param config - The configuration to read provider information from.\n * @param deps - The dependencies for {@link PuppetDbEntityProvider}.\n *\n * @returns A list of {@link PuppetDbEntityProvider} instances.\n */\n static fromConfig(\n config: Config,\n deps: {\n logger: Logger;\n schedule?: TaskRunner;\n scheduler?: PluginTaskScheduler;\n transformer?: ResourceTransformer;\n },\n ): PuppetDbEntityProvider[] {\n if (!deps.schedule && !deps.scheduler) {\n throw new Error('Either schedule or scheduler must be provided.');\n }\n\n return readProviderConfigs(config).map(providerConfig => {\n if (!deps.schedule && !providerConfig.schedule) {\n throw new Error(\n `No schedule provided neither via code nor config for puppet-provider:${providerConfig.id}.`,\n );\n }\n\n const taskRunner =\n deps.schedule ??\n deps.scheduler!.createScheduledTaskRunner(providerConfig.schedule!);\n\n const transformer = deps.transformer ?? defaultResourceTransformer;\n\n return new PuppetDbEntityProvider(\n providerConfig,\n deps.logger,\n taskRunner,\n transformer,\n );\n });\n }\n\n /**\n * Creates an instance of {@link PuppetDbEntityProvider}.\n *\n * @param config - Configuration of the provider.\n * @param logger - The instance of a {@link Logger}.\n * @param taskRunner - The instance of {@link TaskRunner}.\n * @param transformer - A {@link ResourceTransformer} function.\n *\n * @private\n */\n private constructor(\n config: PuppetDbEntityProviderConfig,\n logger: Logger,\n taskRunner: TaskRunner,\n transformer: ResourceTransformer,\n ) {\n this.config = config;\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n this.scheduleFn = this.createScheduleFn(taskRunner);\n this.transformer = transformer;\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n await this.scheduleFn();\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */\n getProviderName(): string {\n return `puppetdb-provider:${this.config.id}`;\n }\n\n /**\n * Creates a function that can be used to schedule a refresh of the catalog.\n *\n * @param taskRunner - The instance of {@link TaskRunner}.\n *\n * @private\n */\n private createScheduleFn(taskRunner: TaskRunner): () => Promise<void> {\n return async () => {\n const taskId = `${this.getProviderName()}:refresh`;\n return taskRunner.run({\n id: taskId,\n fn: async () => {\n const logger = this.logger.child({\n class: PuppetDbEntityProvider.prototype.constructor.name,\n taskId,\n taskInstanceId: uuid.v4(),\n });\n try {\n await this.refresh(logger);\n } catch (error) {\n logger.error(`${this.getProviderName()} refresh failed`, error);\n }\n },\n });\n };\n }\n\n /**\n * Refreshes the catalog by reading nodes from PuppetDB and registering them as Resource Entities.\n *\n * @param logger - The instance of a Logger.\n */\n async refresh(logger: Logger) {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n\n const { markReadComplete } = trackProgress(logger);\n const entities = await readPuppetNodes(this.config, {\n logger,\n transformer: this.transformer,\n });\n const { markCommitComplete } = markReadComplete(entities);\n\n await this.connection.applyMutation({\n type: 'full',\n entities: [...entities].map(entity => ({\n locationKey: this.getProviderName(),\n entity: withLocations(this.config.baseUrl, entity),\n })),\n });\n markCommitComplete(entities);\n }\n}\n\n/**\n * Ensures the entities have required annotation data.\n *\n * @param baseUrl - The base URL of the PuppetDB instance.\n * @param entity - The entity to add the annotations to.\n *\n * @returns Entity with @{@link ANNOTATION_LOCATION} and @{@link ANNOTATION_ORIGIN_LOCATION} annotations.\n */\nfunction withLocations(baseUrl: string, entity: Entity): Entity {\n const location = `${baseUrl}/${ENDPOINT_NODES}/${entity.metadata?.name}`;\n\n return merge(\n {\n metadata: {\n annotations: {\n [ANNOTATION_LOCATION]: `url:${location}`,\n [ANNOTATION_ORIGIN_LOCATION]: `url:${location}`,\n },\n },\n },\n entity,\n ) as Entity;\n}\n\n/**\n * Tracks the progress of the PuppetDB read and commit operations.\n *\n * @param logger - The instance of a {@link Logger}.\n */\nfunction trackProgress(logger: Logger) {\n let timestamp = Date.now();\n\n function markReadComplete(entities: Entity[]) {\n const readDuration = ((Date.now() - timestamp) / 1000).toFixed(1);\n timestamp = Date.now();\n logger.info(\n `Read ${entities?.length ?? 0} in ${readDuration} seconds. Committing...`,\n );\n return { markCommitComplete };\n }\n\n function markCommitComplete(entities: Entity[]) {\n const commitDuration = ((Date.now() - timestamp) / 1000).toFixed(1);\n logger.info(\n `Committed ${entities?.length ?? 0} in ${commitDuration} seconds.`,\n );\n }\n\n return { markReadComplete };\n}\n"],"names":["readTaskScheduleDefinitionFromConfig","DEFAULT_NAMESPACE","fetch","ResponseError","uuid","merge","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION"],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ import { EntityProvider, EntityProviderConnection } from '@backstage/plugin-catalog-backend';
2
+ import { Logger } from 'winston';
3
+ import { Config } from '@backstage/config';
4
+ import { TaskScheduleDefinition, TaskRunner, PluginTaskScheduler } from '@backstage/backend-tasks';
5
+ import { ResourceEntity } from '@backstage/catalog-model';
6
+ import { JsonValue } from '@backstage/types';
7
+
8
+ /**
9
+ * Annotation for specifying the certificate name of a node in PuppetDB.
10
+ *
11
+ * @public
12
+ */
13
+ declare const ANNOTATION_PUPPET_CERTNAME = "puppet.com/certname";
14
+
15
+ /**
16
+ * Configuration of {@link PuppetDbEntityProvider}.
17
+ *
18
+ * @public
19
+ */
20
+ declare type PuppetDbEntityProviderConfig = {
21
+ /**
22
+ * ID of the provider.
23
+ */
24
+ id: string;
25
+ /**
26
+ * (Required) The base URL of PuppetDB API instance.
27
+ */
28
+ baseUrl: string;
29
+ /**
30
+ * (Optional) PQL query to filter PuppetDB nodes.
31
+ */
32
+ query?: string;
33
+ /**
34
+ * (Optional) Task schedule definition for the refresh.
35
+ */
36
+ schedule?: TaskScheduleDefinition;
37
+ };
38
+
39
+ /**
40
+ * Customize the ingested Resource entity.
41
+ *
42
+ * @param node - The found PuppetDB node entry in its source format. This is the entry that you want to transform.
43
+ * @param config - The configuration for the entity provider.
44
+ *
45
+ * @returns A `ResourceEntity` or `undefined` if you want to ignore the found group for being ingested by the catalog.
46
+ *
47
+ * @public
48
+ */
49
+ declare type ResourceTransformer = (node: PuppetNode, config: PuppetDbEntityProviderConfig) => Promise<ResourceEntity | undefined>;
50
+ /**
51
+ * A node in PuppetDB.
52
+ *
53
+ * @public
54
+ */
55
+ declare type PuppetNode = {
56
+ /**
57
+ * The most recent time of fact submission from the associated certname.
58
+ */
59
+ timestamp: string;
60
+ /**
61
+ * The certname associated with the factset.
62
+ */
63
+ certname: string;
64
+ /**
65
+ * A hash of the factset's certname, environment, timestamp, facts, and producer_timestamp.
66
+ */
67
+ hash: string;
68
+ /**
69
+ * The most recent time of fact submission for the relevant certname from the Puppet Server.
70
+ */
71
+ producer_timestamp: string;
72
+ /**
73
+ * The certname of the Puppet Server that sent the factset to PuppetDB.
74
+ */
75
+ producer: string;
76
+ /**
77
+ * The environment associated with the fact.
78
+ */
79
+ environment: string;
80
+ /**
81
+ * The facts associated with the factset.
82
+ */
83
+ facts: PuppetFactSet;
84
+ };
85
+ /**
86
+ * The set of all facts for a single certname in PuppetDB.
87
+ *
88
+ * @public
89
+ */
90
+ declare type PuppetFactSet = {
91
+ /**
92
+ * The array of facts.
93
+ */
94
+ data: PuppetFact[];
95
+ /**
96
+ * The URL to retrieve more information about the facts.
97
+ */
98
+ href: string;
99
+ };
100
+ /**
101
+ * A fact in PuppetDB.
102
+ *
103
+ * @public
104
+ */
105
+ declare type PuppetFact = {
106
+ /**
107
+ * The name of the fact.
108
+ */
109
+ name: string;
110
+ /**
111
+ * The value of the fact.
112
+ */
113
+ value: JsonValue;
114
+ };
115
+
116
+ /**
117
+ * A default implementation of the {@link ResourceTransformer}.
118
+ *
119
+ * @param node - The found PuppetDB node entry in its source format. This is the entry that you want to transform.
120
+ * @param _config - The configuration for the entity provider.
121
+ *
122
+ * @returns A `ResourceEntity`.
123
+ *
124
+ * @public
125
+ */
126
+ declare const defaultResourceTransformer: ResourceTransformer;
127
+
128
+ /**
129
+ * Reads nodes from [PuppetDB](https://www.puppet.com/docs/puppet/6/puppetdb_overview.html)
130
+ * based on the provided query and registers them as Resource entities in the catalog.
131
+ *
132
+ * @public
133
+ */
134
+ declare class PuppetDbEntityProvider implements EntityProvider {
135
+ private readonly config;
136
+ private readonly logger;
137
+ private readonly scheduleFn;
138
+ private readonly transformer;
139
+ private connection?;
140
+ /**
141
+ * Creates instances of {@link PuppetDbEntityProvider} from a configuration.
142
+ *
143
+ * @param config - The configuration to read provider information from.
144
+ * @param deps - The dependencies for {@link PuppetDbEntityProvider}.
145
+ *
146
+ * @returns A list of {@link PuppetDbEntityProvider} instances.
147
+ */
148
+ static fromConfig(config: Config, deps: {
149
+ logger: Logger;
150
+ schedule?: TaskRunner;
151
+ scheduler?: PluginTaskScheduler;
152
+ transformer?: ResourceTransformer;
153
+ }): PuppetDbEntityProvider[];
154
+ /**
155
+ * Creates an instance of {@link PuppetDbEntityProvider}.
156
+ *
157
+ * @param config - Configuration of the provider.
158
+ * @param logger - The instance of a {@link Logger}.
159
+ * @param taskRunner - The instance of {@link TaskRunner}.
160
+ * @param transformer - A {@link ResourceTransformer} function.
161
+ *
162
+ * @private
163
+ */
164
+ private constructor();
165
+ /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */
166
+ connect(connection: EntityProviderConnection): Promise<void>;
167
+ /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */
168
+ getProviderName(): string;
169
+ /**
170
+ * Creates a function that can be used to schedule a refresh of the catalog.
171
+ *
172
+ * @param taskRunner - The instance of {@link TaskRunner}.
173
+ *
174
+ * @private
175
+ */
176
+ private createScheduleFn;
177
+ /**
178
+ * Refreshes the catalog by reading nodes from PuppetDB and registering them as Resource Entities.
179
+ *
180
+ * @param logger - The instance of a Logger.
181
+ */
182
+ refresh(logger: Logger): Promise<void>;
183
+ }
184
+
185
+ /**
186
+ * Name of the default provider when a using a simple configuration.
187
+ *
188
+ * @public
189
+ */
190
+ declare const DEFAULT_PROVIDER_ID = "default";
191
+
192
+ export { ANNOTATION_PUPPET_CERTNAME, DEFAULT_PROVIDER_ID, PuppetDbEntityProvider, PuppetDbEntityProviderConfig, PuppetFact, PuppetFactSet, PuppetNode, ResourceTransformer, defaultResourceTransformer };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@backstage/plugin-catalog-backend-module-puppetdb",
3
+ "description": "A Backstage catalog backend module that helps integrate towards PuppetDB",
4
+ "version": "0.0.0-nightly-20230304023150",
5
+ "main": "dist/index.cjs.js",
6
+ "types": "dist/index.d.ts",
7
+ "license": "Apache-2.0",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "main": "dist/index.cjs.js",
11
+ "types": "dist/index.d.ts"
12
+ },
13
+ "backstage": {
14
+ "role": "backend-plugin-module"
15
+ },
16
+ "homepage": "https://backstage.io",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/backstage/backstage",
20
+ "directory": "plugins/catalog-backend-module-puppetdb"
21
+ },
22
+ "keywords": [
23
+ "backstage",
24
+ "puppetdb",
25
+ "puppet"
26
+ ],
27
+ "scripts": {
28
+ "start": "backstage-cli package start",
29
+ "build": "backstage-cli package build",
30
+ "lint": "backstage-cli package lint",
31
+ "test": "backstage-cli package test",
32
+ "clean": "backstage-cli package clean",
33
+ "prepack": "backstage-cli package prepack",
34
+ "postpack": "backstage-cli package postpack"
35
+ },
36
+ "dependencies": {
37
+ "@backstage/backend-common": "^0.0.0-nightly-20230304023150",
38
+ "@backstage/backend-tasks": "^0.0.0-nightly-20230304023150",
39
+ "@backstage/catalog-model": "^0.0.0-nightly-20230304023150",
40
+ "@backstage/config": "^0.0.0-nightly-20230304023150",
41
+ "@backstage/errors": "^0.0.0-nightly-20230304023150",
42
+ "@backstage/plugin-catalog-backend": "^0.0.0-nightly-20230304023150",
43
+ "@backstage/types": "^1.0.2",
44
+ "lodash": "^4.17.21",
45
+ "luxon": "^3.0.0",
46
+ "node-fetch": "^2.6.7",
47
+ "uuid": "^8.0.0",
48
+ "winston": "^3.2.1"
49
+ },
50
+ "devDependencies": {
51
+ "@backstage/backend-test-utils": "^0.0.0-nightly-20230304023150",
52
+ "@backstage/cli": "^0.0.0-nightly-20230304023150",
53
+ "@types/lodash": "^4.14.151",
54
+ "msw": "^0.49.0"
55
+ },
56
+ "files": [
57
+ "dist",
58
+ "config.d.ts"
59
+ ],
60
+ "configSchema": "config.d.ts"
61
+ }