rfellows commented on code in PR #11175:
URL: https://github.com/apache/nifi/pull/11175#discussion_r3250856181


##########
nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/components/connector/StandardConnectorNode.java:
##########
@@ -637,12 +663,155 @@ public void verifyCanDelete() {
 
     @Override
     public void verifyCanStart() {
+        if (getCurrentState() == ConnectorState.TROUBLESHOOTING) {
+            throw new IllegalStateException("Cannot start " + this + " because 
it is in Troubleshooting mode.");
+        }
         final ValidationState state = performValidation();
         if (state.getStatus() != ValidationStatus.VALID) {
             throw new IllegalStateException("Cannot start " + this + " because 
it is not valid: " + state.getValidationErrors());
         }
     }
 
+    @Override
+    public void verifyCanEnterTroubleshooting() {
+        if (isExtensionMissing()) {
+            throw new IllegalStateException("Cannot enter Troubleshooting mode 
for " + this + " because it is a Ghost Connector (its underlying extension is 
missing).");
+        }
+
+        final ConnectorState currentState = getCurrentState();
+        switch (currentState) {
+            case TROUBLESHOOTING -> throw new IllegalStateException("Cannot 
enter Troubleshooting mode for " + this + " because it is already in 
Troubleshooting mode.");
+            case STARTING, STOPPING, DRAINING, PURGING, PREPARING_FOR_UPDATE, 
UPDATING ->
+                throw new IllegalStateException("Cannot enter Troubleshooting 
mode for " + this + " because its state is currently "
+                    + currentState + "; it must be in a stable state before 
entering Troubleshooting.");
+            default -> {
+                // STOPPED, RUNNING, UPDATED, UPDATE_FAILED are all acceptable 
to enter Troubleshooting from
+            }
+        }
+    }
+
+    @Override
+    public void verifyCanEndTroubleshooting() {
+        if (getCurrentState() != ConnectorState.TROUBLESHOOTING) {
+            throw new IllegalStateException("Cannot end Troubleshooting mode 
for " + this + " because it is not currently in Troubleshooting mode; current 
state is " + getCurrentState());
+        }
+
+        // Verify that every component inside the managed flow is 
stopped/disabled BEFORE doing any other validation.
+        // The flow-update check below relies on components being 
stopped/disabled in order to produce meaningful results. Otherwise,
+        // the update check may succeed and then the running flow puts it into 
a bad state.
+        final ProcessGroup managedGroup = 
getActiveFlowContext().getManagedProcessGroup();
+        verifyAllComponentsStoppedAndDisabled(managedGroup);
+
+        // After confirming all components are stopped or disabled, check if 
the managed flow can be safely reverted.
+        // Connector's authoritative flow would succeed. This mirrors exactly 
what endTroubleshooting() will do so that
+        // any problem (e.g. a Connection whose contents cannot be preserved 
or a component that cannot be replaced)
+        // is reported synchronously at the REST verify-phase rather than 
surfacing halfway through the state change.
+        final VersionedExternalFlow authoritativeFlow = 
resolveAuthoritativeFlow();
+        try {
+            initializationContext.verifyUpdateFlow(activeFlowContext, 
authoritativeFlow, BundleCompatibility.RESOLVE_BUNDLE);
+        } catch (final FlowUpdateException e) {
+            throw new IllegalStateException("Cannot end Troubleshooting mode 
for " + this
+                + " because the Managed Process Group cannot be reverted to 
the Connector's authoritative flow: " + e.getMessage(), e);
+        }
+    }
+
+    private VersionedExternalFlow resolveAuthoritativeFlow() {
+        final VersionedExternalFlow authoritativeFlow;
+        try (final NarCloseable ignored = 
NarCloseable.withComponentNarLoader(extensionManager, 
getConnector().getClass(), getIdentifier())) {
+            authoritativeFlow = 
getConnector().getActiveFlow(activeFlowContext);
+        }
+
+        if (authoritativeFlow == null || authoritativeFlow.getFlowContents() 
== null) {
+            logger.warn("Connector {} returned a null authoritative flow from 
getActiveFlow; using an empty flow.", this);
+            final VersionedExternalFlow empty = new VersionedExternalFlow();
+            empty.setFlowContents(new VersionedProcessGroup());
+            return empty;
+        }
+
+        return authoritativeFlow;
+    }
+
+    private void verifyAllComponentsStoppedAndDisabled(final ProcessGroup 
group) {
+        for (final ProcessorNode processor : group.getProcessors()) {
+            if (!STOPPED_STATES.contains(processor.getScheduledState())) {
+                throw new IllegalStateException("Cannot end Troubleshooting 
mode for " + this + " because Processor " + processor.getIdentifier()
+                    + " is in state " + processor.getScheduledState() + "; it 
must be STOPPED or DISABLED.");
+            }

Review Comment:
   The way this works currently is a bit confusing. You can enter 
troubleshooting mode while the processor is running, but you can't end 
troubleshooting with it still running. This forces the user to have to manually 
stop all processors before they would be allowed to end troubleshooting. For 
large connectors, this will be a tedious process of manually stopping 
everything.
   
   It might be more palatable if STOP was a valid action on the connector as a 
whole while in troubleshooting mode. But it is explicitly not allowed in the 
current state.
   
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to