Skip to content

Commit 1f7bd84

Browse files
authored
Merge pull request redhat-developer#1 from gabemontero/rhdh-rhoai-catalogprocessor-prototype
RHDHPAI-506: custom catalog process with new location type for rhdh/rhoai 'bridge'
2 parents b477c08 + a878dc5 commit 1f7bd84

File tree

8 files changed

+175
-1
lines changed

8 files changed

+175
-1
lines changed

workspaces/rhdh-ai/app-config.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ backend:
1212
# auth:
1313
# keys:
1414
# - secret: ${BACKEND_SECRET}
15+
auth:
16+
externalAccess:
17+
- type: static
18+
options:
19+
token: '${RHDH_TOKEN}'
20+
subject: admin-curl-access
1521
baseUrl: http://localhost:7007
1622
listen:
1723
port: 7007
@@ -31,6 +37,11 @@ backend:
3137
client: better-sqlite3
3238
connection: ':memory:'
3339
# workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir
40+
reading:
41+
allow:
42+
- host: example.com
43+
- host: '*.mozilla.org'
44+
- host: '${RHDH_BRIDGE_HOST}'
3445

3546
integrations:
3647
github:

workspaces/rhdh-ai/packages/backend/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
*/
1616

1717
import { createBackend } from '@backstage/backend-defaults';
18+
import {
19+
catalogModuleRHDHRHOAIReaderProcessor,
20+
catalogModuleRHDHRHOAILocationsExtensionPoint,
21+
} from '@red-hat-developer-hub/backstage-plugin-catalog-backend-module-rhdh-ai';
1822

1923
const backend = createBackend();
2024

@@ -63,4 +67,6 @@ backend.add(
6367
'@red-hat-developer-hub/backstage-plugin-catalog-backend-module-rhdh-ai'
6468
),
6569
);
70+
backend.add(catalogModuleRHDHRHOAILocationsExtensionPoint);
71+
backend.add(catalogModuleRHDHRHOAIReaderProcessor);
6672
backend.start();

workspaces/rhdh-ai/plugins/catalog-backend-module-rhdh-ai/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@backstage/backend-plugin-api": "^1.1.1",
2828
"@backstage/catalog-model": "^1.7.2",
2929
"@backstage/errors": "^1.2.7",
30+
"@backstage/plugin-catalog-common": "^1.1.3",
3031
"@backstage/plugin-catalog-node": "^1.15.1"
3132
},
3233
"devDependencies": {

workspaces/rhdh-ai/plugins/catalog-backend-module-rhdh-ai/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,8 @@
2222

2323
// export * from './clients';
2424
export { catalogModuleModelCatalogResourceEntityProvider as default } from './module';
25+
export { catalogModuleRHDHRHOAIReaderProcessor } from './module';
26+
export { catalogModuleRHDHRHOAILocationsExtensionPoint } from './module';
2527
export * from './providers';
28+
export * from './processors';
2629
export * from './clients';

workspaces/rhdh-ai/plugins/catalog-backend-module-rhdh-ai/src/module.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ import {
1717
coreServices,
1818
createBackendModule,
1919
} from '@backstage/backend-plugin-api';
20-
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
20+
import {
21+
catalogProcessingExtensionPoint,
22+
catalogLocationsExtensionPoint,
23+
} from '@backstage/plugin-catalog-node/alpha';
2124

2225
import { ModelCatalogResourceEntityProvider } from './providers';
26+
import { RHDHRHOAIReaderProcessor } from './processors';
2327

2428
export const catalogModuleModelCatalogResourceEntityProvider =
2529
createBackendModule({
@@ -50,3 +54,42 @@ export const catalogModuleModelCatalogResourceEntityProvider =
5054
});
5155
},
5256
});
57+
58+
export const catalogModuleRHDHRHOAIReaderProcessor = createBackendModule({
59+
pluginId: 'catalog',
60+
moduleId: 'rhdh-rhoai-bridge-reader-processor',
61+
register(env) {
62+
env.registerInit({
63+
deps: {
64+
catalog: catalogProcessingExtensionPoint,
65+
reader: coreServices.urlReader,
66+
},
67+
async init({ catalog, reader }) {
68+
catalog.addProcessor(new RHDHRHOAIReaderProcessor(reader));
69+
},
70+
});
71+
},
72+
});
73+
74+
// so a `CatalogProcessor` does not need to also provide a `CatalogLocationsExtensionPoint` if it only supports imports of locations
75+
// from the app-config.yaml on startup, but if you want to dynamically add Locations via the catalog's REST API (like what the UI does for import
76+
// of catalog-info.yaml from git repos) then you need to also provide a `CatalogLocationsExtension` point to add your type to the default list of 'url' and 'file';
77+
// fwiw in examining the core Backstage code, none of the default `CatalogProcessors` bother to also provide a CatalogLocationsExtension`; however,
78+
// we want to allow our RHDH bridge to import new locations dynamically
79+
export const catalogModuleRHDHRHOAILocationsExtensionPoint =
80+
createBackendModule({
81+
pluginId: 'catalog',
82+
moduleId: 'rhdh-rhoai-bridge-location-extension-point',
83+
register(env) {
84+
env.registerInit({
85+
deps: {
86+
catalog: catalogLocationsExtensionPoint,
87+
},
88+
async init({ catalog }) {
89+
// setAllowedLocationTypes does not add to the list but replaces it, so we preserve the default options of 'file' and 'url'
90+
const allowedLocationTypes = ['file', 'url', 'rhdh-rhoai-bridge'];
91+
catalog.setAllowedLocationTypes(allowedLocationTypes);
92+
},
93+
});
94+
},
95+
});
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright Red Hat, Inc.
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+
import {
17+
processingResult,
18+
CatalogProcessor,
19+
CatalogProcessorEmit,
20+
CatalogProcessorParser,
21+
CatalogProcessorResult,
22+
} from '@backstage/plugin-catalog-node';
23+
import { UrlReaderService } from '@backstage/backend-plugin-api';
24+
25+
import { LocationSpec } from '@backstage/plugin-catalog-common';
26+
27+
// A processor that reads from the RHDH RHOAI Bridge
28+
export class RHDHRHOAIReaderProcessor implements CatalogProcessor {
29+
constructor(private readonly reader: UrlReaderService) {}
30+
31+
getProcessorName(): string {
32+
return 'RHDHRHOAIReaderProcessor';
33+
}
34+
35+
async readLocation(
36+
location: LocationSpec,
37+
_optional: boolean,
38+
emit: CatalogProcessorEmit,
39+
parser: CatalogProcessorParser,
40+
): Promise<boolean> {
41+
// Pick a custom location type string. A location will be
42+
// registered later with this type.
43+
if (location.type !== 'rhdh-rhoai-bridge') {
44+
return false;
45+
}
46+
47+
try {
48+
// Use the builtin reader facility to grab data from the
49+
// API. If you prefer, you can just use plain fetch here
50+
// (from the node-fetch package), or any other method of
51+
// your choosing.
52+
// TODO eventually we will want to take the k8s credentials provided to
53+
// backstage and its k8s plugin and supply them to the bridge
54+
// for potential auth and access control checks (i.e. SARs) with the kubeflow MR
55+
const data = await this.reader.readUrl(location.target);
56+
const response = [{ url: location.target, data: await data.buffer() }];
57+
// Repeatedly call emit(processingResult.entity(location, <entity>))
58+
const parseResults: CatalogProcessorResult[] = [];
59+
for (const item of response) {
60+
for await (const parseResult of parser({
61+
data: item.data,
62+
location: { type: location.type, target: item.url },
63+
})) {
64+
parseResults.push(parseResult);
65+
emit(parseResult);
66+
}
67+
}
68+
emit(processingResult.refresh(`${location.type}:${location.target}`));
69+
} catch (error) {
70+
const message = `Unable to read ${location.type}, ${error}`.substring(
71+
0,
72+
5000,
73+
);
74+
// TODO when we enable cache this is how UrlReaderPRocessor.ts in core backstage handles errors
75+
// if (error.name === 'NotModifiedError' && cacheItem) {
76+
// for (const parseResult of cacheItem.value) {
77+
// emit(parseResult);
78+
// }
79+
// emit(processingResult.refresh(`${location.type}:${location.target}`));
80+
// await cache.set(CACHE_KEY, cacheItem);
81+
// } else if (error.name === 'NotFoundError') {
82+
// if (!optional) {
83+
// emit(processingResult.notFoundError(location, message));
84+
// }
85+
// } else {
86+
// emit(processingResult.generalError(location, message));
87+
// }
88+
emit(processingResult.generalError(location, message));
89+
}
90+
91+
return true;
92+
}
93+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright Red Hat, Inc.
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+
export { RHDHRHOAIReaderProcessor } from './RHDHRHOAIReaderProcessor';

workspaces/rhdh-ai/yarn.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10175,6 +10175,7 @@ __metadata:
1017510175
"@backstage/catalog-model": ^1.7.2
1017610176
"@backstage/cli": ^0.29.5
1017710177
"@backstage/errors": ^1.2.7
10178+
"@backstage/plugin-catalog-common": ^1.1.3
1017810179
"@backstage/plugin-catalog-node": ^1.15.1
1017910180
languageName: unknown
1018010181
linkType: soft

0 commit comments

Comments
 (0)