EUREKA-838 Spike - Evaluate handling of application composition changes

EUREKA-838 Spike - Evaluate handling of application composition changes

Spike Overview

https://folio-org.atlassian.net/browse/EUREKA-838

Objective: There seems to be some confusion about how moving of modules from one application to another is handled (or whether it’s supported at all). The purpose of this spike is to review the relevant code, understand which validation is happening in which places, and to try moving modules from one application to another to uncover potential problems. The process should be documented along the way. The idea is to clarify the situation so we can get everyone aligned and dispel rumor/misunderstandings.

Scope

  • Clarify all open questions regarding module transfer between applications

  • Review the current behavior and clearly document the findings (including the validation processes within components and the procedures for transferring modules between applications etc)

  • Suggest improvements or changes needed to support module transferring

mgr-applications validation

Existing validators

ApplicationIdValidator

  • Purpose: Ensures that the Application ID follows the strict convention of {name}-{version}.

  • Validation Logic: It gets ApplicationDescriptor’s ID and validates that the prefix matches the application name and the suffix matches the application version. It fails if the ID format does not strictly equal 'name-version' or if the ID is empty.

  • Usage: Used by ApplicationValidatorService to validate an application during creation or when validating an application descriptor.

ApplicationModuleValidator

  • Purpose: Validates the list of back-end modules contained within an ApplicationDescriptor.

  • Validation Logic: It iterates through the modules list in the descriptor. It verifies that each module entry (consisting of module name and version) appears in another registered application (if any) with the same name as in the provided application descriptor. It fails if a module with the same version exists in another registered application with the different name .

  • Usage: Used by ApplicationValidatorService to validate an application during creation or when validating an application descriptor.

InterfaceNameValidator

  • Purpose: Validates the format of interface names defined in provides sections.

  • Validation Logic: It uses regular expressions to ensure an interface name consist only of alphanumeric, dots, underscores characters and a version consist only of numeric, dot characters. It fails if an interface name or a version contains characters not allowed by the schema.

  • Usage: Used by ApplicationValidatorService to validate an application during creation or when validating an application descriptor.

ModuleDescriptorsSourceValidator

  • Purpose: Validates module descriptors explicitly included in the application descriptor or referenced by URL.

  • Validation Logic: It validates that each back-end or UI module's descriptor is either included in the application descriptor or referenced by a URL. It fails if both the module descriptor and URL are present or if neither is present.

  • Usage: Used by ApplicationValidatorService to validate an application during creation only.

ModuleDescriptorValidator

  • Purpose: Validates back-end modules listed in the modules section of the application descriptor against the module descriptors obtained from the moduleDescriptors section or by URL.

  • Validation Logic: It extracts module IDs from the modules section and matches them against module IDs from the moduleDescriptors section or from module descriptors retrieved via URL. It fails if any module ID does not match.

  • Usage: Used by ApplicationValidatorService to validate an application during creation or when validating an application descriptor.

UiModuleDescriptorValidator

  • Purpose: Validates UI modules listed in the uiModules section of the application descriptor against the module descriptors obtained from the uiModuleDescriptors section or by URL.

  • Validation Logic: It extracts module IDs from the uiModules section and matches them against module IDs from the uiModuleDescriptors section or from module descriptors retrieved via URL. It fails if any module ID does not match.

  • Usage: Used by ApplicationValidatorService to validate an application during creation or when validating an application descriptor.

DependenciesValidator

  • Purpose: Validates the dependency graph to ensure all required interfaces can be satisfied.

  • Validation Logic: It analyzes the 'requires' list of an application/module and checks against the 'provides' list of other available applications. It fails if a required interface (and version range) cannot be resolved by any known application in the system (missing dependency).

  • Usage: Used by ApplicationReferencesValidationService to validate module interface integrity for given applications.

Validators related to application composition

Only ApplicationModuleValidator performs application composition related checks.

  • Module Ownership Validator: Ensures that modules specified in an application descriptor are not already owned by a different application (prevents module conflicts between applications)

  • Uniqueness Enforcement: Validates that module artifact IDs are unique across applications with different names, allowing only the same application name to reuse modules.

Validation Flow:

  • Retrieves all module artifact IDs from the application descriptor being validated

  • Calls ApplicationService.findApplicationsByModuleIds() to find any existing applications that already use these modules

  • Excludes applications with the same name (allowing different versions of the same application to share modules)

  • For each conflicting application, builds a map of application name → list of conflicting module IDs. Fails with error message "Modules belong to other Applications" and parameters listing each conflicting application name with its conflicting module IDs

By-passing validation

It is possible to bypass the validators that are normally applied during the application registration process by using the POST /applications method. To do this, you need to add the check=false query parameter to the request. By default, this parameter is set to true, which means validation is enforced to ensure the application data meets all required criteria. More detailed information and guidance can be found in Register Application API method documentation page.

mgr-tenant-entitlements validation

Existing validators

ApplicationFlowValidator

  • Purpose: Ensures only one entitlement operation (Entitle, Revoke, or Upgrade) can be executed at a time for a given application within a tenant by validating the current flow state before proceeding.

  • Validation Logic: Retrieves the last application flow for each requested application and validates that no conflicting operations are in progress, queued, or in incompatible finished states. Rejects requests if flows are actively running, queued, or have failed/cancelled statuses for different operation types.

  • Usage: Registered as the first validation stage in Entitle, Revoke and Upgrade flows to fail fast before processing begins.

ExistingEntitlementValidator

  • Purpose: Ensures the requested Revoke operation is valid regarding the current installation state of the application for the tenant.

  • Validation Logic: Queries the database to check if the application is currently installed. Fails if the application(s) is NOT currently installed.

  • Usage: Used by Revoke flow to prevent operations on non-existent entitlements.

UpgradeRequestValidator

  • Purpose: Validates that an upgrade request targets a valid version progression.

  • Validation Logic: Compares the requested version against the currently installed version. It fails if the requested version is not strictly greater (according to SemVer) than the installed version (downgrades are typically not allowed).

  • Usage: Used by Upgrade flow to prevent incorrect operations on existent entitlements.

InterfaceIntegrityValidator

  • Purpose: Ensures that the resulting environment satisfies all interface dependencies.

  • Validation Logic: Constructs a graph of all modules to be installed/retained. It checks the 'requires' section of every module against the 'provides' section of all other modules. It fails if any module requires an interface (and version) that is not provided by the rest of the entitlement set.

  • Usage: Used flow validators to ensure the final state is consistent.

DesiredStateApplicationFlowValidator

  • Purpose: Ensures only one entitlement operation (Entitle, Revoke, or Upgrade) can be executed at a time for a given application within a tenant by validating the current flow state before proceeding. Similar in purpose to ApplicationFlowValidator.

  • Validation Logic: Retrieves the last application flow for each requested application and validates that no conflicting operations are in progress, queued, or in incompatible finished states. Rejects requests if flows are actively running, queued, or have failed/cancelled statuses for different operation types.

  • Usage: Desired State flow uses applies the validator to application state transition buckets.

DesiredStateWithRevokeValidator

  • Purpose: Ensures the requested Revoke operation is valid regarding the current installation state of the application for the tenant. Similar in purpose to ExistingEntitlementValidator.

  • Validation Logic: Queries the database to check if the application is currently installed. Fails if the application(s) is NOT currently installed.

  • Usage: Used during the Revoke flow validation in scope of Desired State request.

DesiredStateWithUpgradeValidator

  • Purpose: Validates that an upgrade request targets a valid version progression. Similar in purpose to UpgradeRequestValidator.

  • Validation Logic: Compares the requested version against the currently installed version. It fails if the requested version is not strictly greater (according to SemVer) than the installed version (downgrades are typically not allowed).

  • Usage: Used during the Upgrade flow validation in scope of Desired State request.

Validators related to application composition

There are no validators related to application composition in mgr-tenant-entitlements.

folio-application-generator validation

Existing validators

ApplicationDependencyValidator

  • Purpose: Validates dependency declarations in application descriptor templates before generation. Ensures semantic versioning compliance for all modules and application dependencies. Validates pre-release filter consistency with version specifications

  • Validation Logic: Validates different module constraints, like module name should not be black, version is valid SemVer etc. Checks application dependencies to make sure their names are not black, versions satisfy SemVer range syntax.

  • Usage: Called by ApplicationDescriptorGenerator.generate() before creating application descriptor

ApplicationModulesIntegrityValidator

  • Purpose: Ensures modules integrity by delegating to centralized validation service in mgr-applications: /applications/validate-descriptors

  • Validation Logic: Validates complete application descriptor with module descriptors via HTTP

  • Usage: Called by IntegrityValidatorMojo.execute() (validateIntegrity Maven goal)

Validators related to application composition

There are no validators related to application composition in folio-application-generator.

Moving a module from one application to another

At the moment it’s not possible to move a module of the same version from one application to another w/o disabling the validation check. You can only have the same module version within different versions of the same applications.

This constraint is enforced by ApplicationModuleValidatorduring a new application registration. The validator will report a problem with the “Modules belong to other Applications“ message.

Let’s consider an example when mod-notes module with 7.1.0-SNAPSHOT.302 version is a part of registered in mgr-applications. If an attempt is made to register a new application with mod-notes-7.1.0-SNAPSHOT.302 then mgr-applications will return an error:

{ "errors": [ { "message": "Modules belong to other Applications", "type": "RequestValidationException", "code": "validation_error", "parameters": [ { "key": "app-platform-minimal", "value": "[mod-notes-7.1.0-SNAPSHOT.302]" } ] } ], "total_records": 1 }

At the same time if app-notes-1.0.0-SNAPSHOT.0001 contains a different version of mod-notes, 7.1.0-SNAPSHOT.303, then the attempt will be successful:

image-20251207-162038.png

Once discovery information for the new module version is created, a new gateway service with routes will be added to Kong and there will be two services: for mod-notes-7.1.0-SNAPSHOT.302 and for mod-notes-7.1.0-SNAPSHOT.303

image-20251207-171753.png
image-20251207-171646.png

Note, if both the initial and the new versions of mod-notes contain identical endpoints, they will create the same route expressions, causing collisions when routing requests. Kong will be unable to differentiate the target module by the given URL.

Partially, it can be resolved by adding a tenant-based condition to the expression, enabling one module version for certain tenants and another version for different tenants.

Sample use cases where multiple module versions coexist in the system, causing collisions with Kong services/routes:

  • The test environment is not recreated from scratch. module-A has previously installed version 1.0.1-snapshot.101.

    • During regular env update, the module receives a new snapshot version 1.0.1-snapshot.102. The old application is not removed, and the old module discovery is not deleted. Consequently, a new service is created in Kong with routes duplicating those from the previous snapshot version. Repeating this process multiple times (for example, daily over several months) results in dozens of duplicated routes in Kong

  • an environment is upgraded. module-A has previously installed version 1.0.1

    • During the upgrade, a new application is registered with updated module-A-1.0.2. The old application is not removed, because it’s required during the upgrade, and the old module discovery is not deleted also. Consequently, a new service is created in Kong with routes duplicating those from the previous snapshot version.

    • To bring the system into a consistent state, remove the old application or at least the old module discovery. This can be done manually or during an automated upgrade process.

Main issue – Kong services and routes are managed outside of tenant context, but it should be the opposite: tenant entitlement events should control when and how the necessary Kong services and routes modified.

Stories concerning the transfer of route management from mgr-tenant-entitlement to mgr-applications: