Add a managed-by-origin-location annotation

This commit is contained in:
Dominik Henneke
2021-01-19 14:05:52 +01:00
parent 342eccc25a
commit def2307f33
8 changed files with 139 additions and 11 deletions
+29
View File
@@ -0,0 +1,29 @@
---
'@backstage/catalog-model': patch
'@backstage/plugin-catalog-backend': patch
---
Adds a `backstage.io/managed-by-origin-location` annotation to all entities. It links to the
location that was registered to the catalog and which emitted this entity. It has a different
semantic than the existing `backstage.io/managed-by-location` annotation, which tells the direct
parent location that created this entity.
Consider this example: The Backstage operator adds a location of type `github-org` in the
`app-config.yaml`. This setting will be added to a `bootstrap:boostrap` location. The processor
discovers the entities in the following branch
`Location bootstrap:bootstrap -> Location github-org:… -> User xyz`. The user `xyz` will be:
```yaml
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: xyz
annotations:
# This entity was added by the 'github-org:…' location
backstage.io/managed-by-location: github-org:…
# The entity was added because the 'bootstrap:boostrap' was added to the catalog
backstage.io/managed-by-origin-location: bootstrap:bootstrap
# ...
spec:
# ...
```
@@ -40,6 +40,23 @@ expecting a two-item array out of it. The format of the target part is
type-dependent and could conceivably even be an empty string, but the separator
colon is always present.
### backstage.io/managed-by-origin-location
```yaml
# Example:
metadata:
annotations:
backstage.io/managed-by-origin-location: github:http://github.com/backstage/backstage/catalog-info.yaml
```
The value of this annotation is a location reference string (see above). It
points to the location, which registration lead to the creation of the entity.
In most cases, the `backstage.io/managed-by-location` and
`backstage.io/managed-by-origin-location` will be equal. It will be different if
the original location delegates to another location. A common case is, that a
location is registered via the `bootstrap:boostrap` which means that is part of
the `app-config.yml` of a backstage installation.
### backstage.io/techdocs-ref
```yaml
@@ -15,3 +15,5 @@
*/
export const LOCATION_ANNOTATION = 'backstage.io/managed-by-location';
export const ORIGIN_LOCATION_ANNOTATION =
'backstage.io/managed-by-origin-location';
+1 -1
View File
@@ -20,4 +20,4 @@ export {
locationSpecSchema,
analyzeLocationSchema,
} from './validation';
export { LOCATION_ANNOTATION } from './annotation';
export { LOCATION_ANNOTATION, ORIGIN_LOCATION_ANNOTATION } from './annotation';
@@ -78,13 +78,17 @@ export class LocationReaders implements LocationReader {
if (rulesEnforcer.isAllowed(item.entity, item.location)) {
const relations = Array<EntityRelationSpec>();
const entity = await this.handleEntity(item, emitResult => {
if (emitResult.type === 'relation') {
relations.push(emitResult.relation);
return;
}
emit(emitResult);
});
const entity = await this.handleEntity(
item,
emitResult => {
if (emitResult.type === 'relation') {
relations.push(emitResult.relation);
return;
}
emit(emitResult);
},
location,
);
if (entity) {
output.entities.push({
@@ -165,6 +169,7 @@ export class LocationReaders implements LocationReader {
private async handleEntity(
item: CatalogProcessorEntityResult,
emit: CatalogProcessorEmit,
originLocation: LocationSpec,
): Promise<Entity | undefined> {
const { processors, logger } = this.options;
@@ -185,6 +190,7 @@ export class LocationReaders implements LocationReader {
current,
item.location,
emit,
originLocation,
);
} catch (e) {
const message = `Processor ${processor.constructor.name} threw an error while preprocessing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`;
@@ -0,0 +1,62 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { Entity, LocationSpec } from '@backstage/catalog-model';
import { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcessor';
describe('AnnotateLocationEntityProcessor', () => {
describe('preProcessEntity', () => {
it('adds annotations', async () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'my-component',
},
};
const location: LocationSpec = {
type: 'url',
target: 'my-location',
};
const originLocation: LocationSpec = {
type: 'url',
target: 'my-origin-location',
};
const processor = new AnnotateLocationEntityProcessor();
expect(
await processor.preProcessEntity(
entity,
location,
() => {},
originLocation,
),
).toEqual({
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'my-component',
annotations: {
'backstage.io/managed-by-location': 'url:my-location',
'backstage.io/managed-by-origin-location': 'url:my-origin-location',
},
},
});
});
});
});
@@ -14,20 +14,28 @@
* limitations under the License.
*/
import { Entity, LocationSpec } from '@backstage/catalog-model';
import {
Entity,
LOCATION_ANNOTATION,
LocationSpec,
ORIGIN_LOCATION_ANNOTATION,
} from '@backstage/catalog-model';
import lodash from 'lodash';
import { CatalogProcessor } from './types';
import { CatalogProcessor, CatalogProcessorEmit } from './types';
export class AnnotateLocationEntityProcessor implements CatalogProcessor {
async preProcessEntity(
entity: Entity,
location: LocationSpec,
_: CatalogProcessorEmit,
originLocation: LocationSpec,
): Promise<Entity> {
return lodash.merge(
{
metadata: {
annotations: {
'backstage.io/managed-by-location': `${location.type}:${location.target}`,
[LOCATION_ANNOTATION]: `${location.type}:${location.target}`,
[ORIGIN_LOCATION_ANNOTATION]: `${originLocation.type}:${originLocation.target}`,
},
},
},
@@ -46,12 +46,16 @@ export type CatalogProcessor = {
* @param entity The (possibly partial) entity to process
* @param location The location that the entity came from
* @param emit A sink for auxiliary items resulting from the processing
* @param originLocation The location that the entity originally came from.
* While location resolves to the direct parent location, originLocation
* tells which location was used to start the ingestion loop.
* @returns The same entity or a modified version of it
*/
preProcessEntity?(
entity: Entity,
location: LocationSpec,
emit: CatalogProcessorEmit,
originLocation: LocationSpec,
): Promise<Entity>;
/**