diff --git a/.changeset/hip-experts-dance.md b/.changeset/hip-experts-dance.md new file mode 100644 index 0000000000..f88fe62cd4 --- /dev/null +++ b/.changeset/hip-experts-dance.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +An entity A, that exists in the catalog, can no longer be overwritten by registering a different location that also tries to supply an entity with the same kind+namespace+name. Writes of that new entity will instead be rejected with a log message similar to `Rejecting write of entity Component:default/artist-lookup from file:/Users/freben/dev/github/backstage/packages/catalog-model/examples/components/artist-lookup-component.yaml because entity existed from github:https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml` diff --git a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts index 00898b33a6..8d2138b3e5 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.test.ts @@ -15,7 +15,7 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import type { Entity } from '@backstage/catalog-model'; +import { Entity, LOCATION_ANNOTATION } from '@backstage/catalog-model'; import { Database, DatabaseManager, Transaction } from '../database'; import { EntityFilters } from '../service/EntityFilters'; import { DatabaseEntitiesCatalog } from './DatabaseEntitiesCatalog'; @@ -127,6 +127,9 @@ describe('DatabaseEntitiesCatalog', () => { metadata: { name: 'c', namespace: 'd', + annotations: { + [LOCATION_ANNOTATION]: 'mock', + }, }, }; const dbEntity: Entity = { @@ -135,7 +138,11 @@ describe('DatabaseEntitiesCatalog', () => { metadata: { name: 'c', namespace: 'd', + description: 'changes', uid: 'u', + annotations: { + [LOCATION_ANNOTATION]: 'mock', + }, }, }; db.entities.mockResolvedValue([ diff --git a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts index 5c55a30a55..f40e327eaa 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseEntitiesCatalog.ts @@ -205,9 +205,11 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { // TODO(Rugvip): We currently always update relations, but we // likely want to figure out a way to avoid that for (const { entity, relations } of toIgnore) { - const entityId = entity.metadata.uid!; - await this.setRelations(entityId, relations, tx); - modifiedEntityIds.push({ entityId }); + const entityId = entity.metadata.uid; + if (entityId) { + await this.setRelations(entityId, relations, tx); + modifiedEntityIds.push({ entityId }); + } } break; @@ -300,12 +302,22 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { for (const request of requests) { const newEntity = request.entity; const oldEntity = oldEntitiesByName.get(newEntity.metadata.name); + const newLocation = newEntity.metadata.annotations?.[LOCATION_ANNOTATION]; + const oldLocation = + oldEntity?.metadata.annotations?.[LOCATION_ANNOTATION]; if (!oldEntity) { toAdd.push(request); + } else if (oldLocation !== newLocation) { + this.logger.warn( + `Rejecting write of entity ${serializeEntityRef( + newEntity, + )} from ${newLocation} because entity existed from ${oldLocation}`, + ); + toIgnore.push(request); } else if (entityHasChanges(oldEntity, newEntity)) { // TODO(freben): This currently uses addOrUpdateEntity under the hood, // but should probably calculate the end result entity right here - // instead and call a dedicated batch update database method instead + // instead and call a dedicated batch update database method toUpdate.push(request); } else { toIgnore.push(request);