Merge pull request #14584 from AmericanAirlines/3574-catalog-import-filter

adding filters to catalog import
This commit is contained in:
Patrik Oldsberg
2022-12-13 11:27:56 +01:00
committed by GitHub
6 changed files with 126 additions and 9 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Added a new `catalog.rules[].location` configuration that makes it possible to configure catalog rules to only apply to specific locations, either via exact match or a glob pattern.
+31
View File
@@ -38,6 +38,37 @@ export interface Config {
* E.g. ["Component", "API", "Template", "Location"]
*/
allow: Array<string>;
/**
* Limit this rule to a specific location
*
* Example with a fixed location
* { "type": "url", "exact": "https://github.com/a/b/blob/file.yaml"}
*
* Example using a Regex
* { "type": "url", "pattern": "https://github.com/org/*\/blob/master/*.yaml"}
*
* Using both exact and pattern will result in an error starting the application
*/
locations?: Array<{
/**
* The type of location, e.g. "url".
*/
type: string;
/**
* The exact location, e.g.
* "https://github.com/org/repo/blob/master/users.yaml".
*
* The exact location can also be used to match on locations
* that contain glob characters themselves, e.g.
* "https://github.com/org/*\/blob/master/*.yaml".
*/
exact?: string;
/**
* The pattern allowed for the location, e.g.
* "https://github.com/org/*\/blob/master/*.yaml".
*/
pattern?: string;
}>;
}>;
/**
+1
View File
@@ -59,6 +59,7 @@
"knex": "^2.0.0",
"lodash": "^4.17.21",
"luxon": "^3.0.0",
"minimatch": "^5.0.0",
"node-fetch": "^2.6.7",
"p-limit": "^3.0.2",
"prom-client": "^14.0.1",
@@ -35,6 +35,10 @@ const entity = {
};
const location: Record<string, LocationSpec> = {
w: {
type: 'url',
target: 'https://github.com/b/c/blob/master/w.yaml',
},
x: {
type: 'url',
target: 'https://github.com/a/b/blob/master/x.yaml',
@@ -50,6 +54,28 @@ const location: Record<string, LocationSpec> = {
};
describe('DefaultCatalogRulesEnforcer', () => {
it('should throw an error if both pattern and exact are used', () => {
expect(() =>
DefaultCatalogRulesEnforcer.fromConfig(
new ConfigReader({
catalog: {
rules: [
{
allow: ['Component'],
locations: [
{
type: 'url',
pattern: 'https://github.com/b/**',
exact: 'https://github.com/a/b/blob/master/w.yaml',
},
],
},
],
},
}),
),
).toThrow(/cannot have both exact and pattern values/i);
});
it('should deny by default', () => {
const enforcer = new DefaultCatalogRulesEnforcer([]);
expect(enforcer.isAllowed(entity.user, location.x)).toBe(false);
@@ -205,7 +231,7 @@ describe('DefaultCatalogRulesEnforcer', () => {
const enforcer = DefaultCatalogRulesEnforcer.fromConfig(
new ConfigReader({
catalog: {
rules: [{ allow: ['Group'], locations: [{ type: 'url' }] }],
rules: [{ allow: ['Group'] }],
},
}),
);
@@ -216,5 +242,25 @@ describe('DefaultCatalogRulesEnforcer', () => {
expect(enforcer.isAllowed(entity.component, location.z)).toBe(false);
expect(enforcer.isAllowed(entity.location, location.z)).toBe(false);
});
it('should only allow locations that match a given pattern', () => {
const enforcer = DefaultCatalogRulesEnforcer.fromConfig(
new ConfigReader({
catalog: {
rules: [
{
allow: ['Component'],
locations: [
{ type: 'url', pattern: 'https://github.com/b/**' },
],
},
],
},
}),
);
expect(enforcer.isAllowed(entity.component, location.w)).toBe(true);
expect(enforcer.isAllowed(entity.component, location.y)).toBe(false);
expect(enforcer.isAllowed(entity.component, location.z)).toBe(false);
});
});
});
@@ -18,6 +18,7 @@ import { Config } from '@backstage/config';
import { Entity } from '@backstage/catalog-model';
import path from 'path';
import { LocationSpec } from '@backstage/plugin-catalog-common';
import minimatch from 'minimatch';
/**
* Rules to apply to catalog entities.
@@ -29,8 +30,9 @@ export type CatalogRule = {
kind: string;
}>;
locations?: Array<{
target?: string;
exact?: string;
type: string;
pattern?: string;
}>;
};
@@ -76,6 +78,14 @@ export class DefaultCatalogRulesEnforcer implements CatalogRulesEnforcer {
* catalog:
* rules:
* - allow: [Component, API]
* - allow: [Template]
* locations:
* - type: url
* pattern: https://github.com/org/*\/blob/master/template.yaml
* - allow: [Location]
* locations:
* - type: url
* pattern: https://github.com/org/repo/blob/master/location.yaml
*
* locations:
* - type: url
@@ -92,9 +102,26 @@ export class DefaultCatalogRulesEnforcer implements CatalogRulesEnforcer {
const rules = new Array<CatalogRule>();
if (config.has('catalog.rules')) {
const globalRules = config.getConfigArray('catalog.rules').map(sub => ({
allow: sub.getStringArray('allow').map(kind => ({ kind })),
}));
const globalRules = config
.getConfigArray('catalog.rules')
.map(ruleConf => ({
allow: ruleConf.getStringArray('allow').map(kind => ({ kind })),
locations: ruleConf
.getOptionalConfigArray('locations')
?.map(locationConfig => {
const location = {
pattern: locationConfig.getOptionalString('pattern'),
type: locationConfig.getString('type'),
exact: locationConfig.getOptionalString('exact'),
};
if (location.pattern && location.exact) {
throw new Error(
'A catalog rule location cannot have both exact and pattern values',
);
}
return location;
}),
}));
rules.push(...globalRules);
} else {
rules.push(...DefaultCatalogRulesEnforcer.defaultRules);
@@ -108,11 +135,11 @@ export class DefaultCatalogRulesEnforcer implements CatalogRulesEnforcer {
return [];
}
const type = locConf.getString('type');
const target = resolveTarget(type, locConf.getString('target'));
const exact = resolveTarget(type, locConf.getString('target'));
return locConf.getConfigArray('rules').map(ruleConf => ({
allow: ruleConf.getStringArray('allow').map(kind => ({ kind })),
locations: [{ type, target }],
locations: [{ type, exact }],
}));
});
@@ -144,7 +171,7 @@ export class DefaultCatalogRulesEnforcer implements CatalogRulesEnforcer {
private matchLocation(
location: LocationSpec,
matchers?: { target?: string; type: string }[],
matchers?: { exact?: string; type: string; pattern?: string }[],
): boolean {
if (!matchers) {
return true;
@@ -154,7 +181,13 @@ export class DefaultCatalogRulesEnforcer implements CatalogRulesEnforcer {
if (matcher.type !== location?.type) {
continue;
}
if (matcher.target && matcher.target !== location?.target) {
if (matcher.exact && matcher.exact !== location?.target) {
continue;
}
if (
matcher.pattern &&
!minimatch(location?.target, matcher.pattern, { nocase: true })
) {
continue;
}
return true;
+1
View File
@@ -5263,6 +5263,7 @@ __metadata:
knex: ^2.0.0
lodash: ^4.17.21
luxon: ^3.0.0
minimatch: ^5.0.0
msw: ^0.49.0
node-fetch: ^2.6.7
p-limit: ^3.0.2