From fe595aca4c03df2bc38ccaa0e8f3c470557116bc Mon Sep 17 00:00:00 2001 From: Pierre Villard Date: Wed, 15 Apr 2026 18:19:38 +0200 Subject: [PATCH] NIFI-15841 - Relax versioning of a parent PG with locally modified child PGs --- .../nifi/groups/StandardProcessGroup.java | 2 - .../system/registry/RegistryClientIT.java | 45 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java index 1dda6c268432..1ae2fb63f280 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java +++ b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java @@ -4087,8 +4087,6 @@ private VersionedFlowSynchronizationContext createGroupSynchronizationContext(fi @Override public void verifyCanSaveToFlowRegistry(final String registryId, final FlowLocation flowLocation, final String saveAction) { - verifyNoDescendantsWithLocalModifications("be saved to a Flow Registry"); - final StandardVersionControlInformation vci = versionControlInfo.get(); if (vci != null) { final String flowId = flowLocation.getFlowId(); diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/RegistryClientIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/RegistryClientIT.java index d9876ceb7257..4903372c5b29 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/RegistryClientIT.java +++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/RegistryClientIT.java @@ -550,6 +550,51 @@ public void testNewComponentsNotStartedWhenGroupStopped() throws NiFiClientExcep } } + /** + * Test that a parent Process Group can be committed to a Flow Registry even when a child Process Group + * that is separately under Version Control has local modifications. The parent's snapshot only stores + * version coordinate references for child versioned PGs, so uncommitted changes in the child are + * irrelevant to the parent's save operation. + * + * Scenario: + * 1. Create parent PG with a processor and child PG with a processor + * 2. Commit child PG (v1), then commit parent PG (v1) + * 3. Modify a processor in the child PG so the child becomes LOCALLY_MODIFIED + * 4. Commit the parent PG as v2 -- this should succeed + */ + @Test + public void testSaveParentFlowVersionWithLocallyModifiedChild() throws NiFiClientException, IOException, InterruptedException { + final FlowRegistryClientEntity clientEntity = registerClient(); + final NiFiClientUtil util = getClientUtil(); + + final ProcessGroupEntity parent = util.createProcessGroup("Parent", "root"); + final ProcessorEntity parentProcessor = util.createProcessor("GenerateFlowFile", parent.getId()); + + final ProcessGroupEntity child = util.createProcessGroup("Child", parent.getId()); + final ProcessorEntity childProcessor = util.createProcessor("TerminateFlowFile", child.getId()); + + final VersionControlInformationEntity childVci = util.startVersionControl(child, clientEntity, TEST_FLOWS_BUCKET, "Child"); + assertEquals("1", childVci.getVersionControlInformation().getVersion()); + + final VersionControlInformationEntity parentVci = util.startVersionControl(parent, clientEntity, TEST_FLOWS_BUCKET, "Parent"); + assertEquals("1", parentVci.getVersionControlInformation().getVersion()); + + util.assertFlowUpToDate(parent.getId()); + util.assertFlowUpToDate(child.getId()); + + util.updateProcessorSchedulingPeriod(childProcessor, "2 min"); + waitFor(() -> VersionControlInformationDTO.LOCALLY_MODIFIED.equals(util.getVersionControlState(child.getId()))); + + util.updateProcessorProperties(parentProcessor, Collections.singletonMap("Text", "Modified")); + + final VersionControlInformationEntity parentV2 = util.saveFlowVersion(parent, clientEntity, parentVci); + assertEquals("2", parentV2.getVersionControlInformation().getVersion()); + + util.assertFlowUpToDate(parent.getId()); + + assertEquals(VersionControlInformationDTO.LOCALLY_MODIFIED, util.getVersionControlState(child.getId())); + } + @Test public void testStopVersionControlThenSetVersionControlInfo() throws NiFiClientException, IOException, InterruptedException { final FlowRegistryClientEntity clientEntity = registerClient();