NIFI-8069 Set invalid status when Controller Services are enabling

This closes #4710.

Signed-off-by: Mark Payne <markap14@hotmail.com>
This commit is contained in:
exceptionfactory 2020-12-04 07:18:56 -06:00 committed by Mark Payne
parent 84b561ad33
commit 5f7558cecf
4 changed files with 100 additions and 4 deletions

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.nifi.components.validation;
import org.apache.nifi.components.ValidationResult;
public class EnablingServiceValidationResult extends ValidationResult {
private static final String EXPLANATION = "Controller Service [%s] Identifier [%s] state is Enabling";
public EnablingServiceValidationResult(final String subject, final String serviceId) {
super(new Builder()
.input(serviceId)
.subject(subject)
.valid(false)
.explanation(String.format(EXPLANATION, subject, serviceId)));
}
}

View File

@ -25,12 +25,14 @@ import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.validation.DisabledServiceValidationResult;
import org.apache.nifi.components.validation.EnablingServiceValidationResult;
import org.apache.nifi.components.validation.ValidationState;
import org.apache.nifi.components.validation.ValidationStatus;
import org.apache.nifi.components.validation.ValidationTrigger;
import org.apache.nifi.controller.service.ControllerServiceDisabledException;
import org.apache.nifi.controller.service.ControllerServiceNode;
import org.apache.nifi.controller.service.ControllerServiceProvider;
import org.apache.nifi.controller.service.ControllerServiceState;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.NarCloseable;
import org.apache.nifi.parameter.ExpressionLanguageAgnosticParameterParser;
@ -723,6 +725,8 @@ public abstract class AbstractComponentNode implements ComponentNode {
if (!controllerServiceNode.isActive()) {
validationResults.add(new DisabledServiceValidationResult(descriptor.getDisplayName(), controllerServiceId));
} else if (ControllerServiceState.ENABLING == controllerServiceNode.getState()) {
validationResults.add(new EnablingServiceValidationResult(descriptor.getDisplayName(), controllerServiceId));
}
}

View File

@ -22,11 +22,15 @@ import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.bundle.BundleCoordinate;
import org.apache.nifi.components.ConfigurableComponent;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.validation.EnablingServiceValidationResult;
import org.apache.nifi.components.validation.ValidationStatus;
import org.apache.nifi.components.validation.ValidationTrigger;
import org.apache.nifi.controller.service.ControllerServiceNode;
import org.apache.nifi.controller.service.ControllerServiceProvider;
import org.apache.nifi.controller.service.ControllerServiceState;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.parameter.Parameter;
import org.apache.nifi.parameter.ParameterContext;
@ -44,11 +48,13 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class TestAbstractComponentNode {
@ -136,14 +142,65 @@ public class TestAbstractComponentNode {
assertEquals(1L, validationCount.get());
}
@Test
public void testValidateControllerServicesValid() {
final ControllerServiceProvider serviceProvider = Mockito.mock(ControllerServiceProvider.class);
final ValidationContext context = getServiceValidationContext(ControllerServiceState.ENABLED, serviceProvider);
final ValidationControlledAbstractComponentNode componentNode = new ValidationControlledAbstractComponentNode(0, Mockito.mock(ValidationTrigger.class), serviceProvider);
final Collection<ValidationResult> results = componentNode.validateReferencedControllerServices(context);
assertTrue(String.format("Validation Failed %s", results), results.isEmpty());
}
@Test
public void testValidateControllerServicesEnablingInvalid() {
final ControllerServiceProvider serviceProvider = Mockito.mock(ControllerServiceProvider.class);
final ValidationContext context = getServiceValidationContext(ControllerServiceState.ENABLING, serviceProvider);
final ValidationControlledAbstractComponentNode componentNode = new ValidationControlledAbstractComponentNode(0, Mockito.mock(ValidationTrigger.class), serviceProvider);
final Collection<ValidationResult> results = componentNode.validateReferencedControllerServices(context);
final Optional<ValidationResult> firstResult = results.stream().findFirst();
assertTrue("Validation Result not found", firstResult.isPresent());
final ValidationResult validationResult = firstResult.get();
assertTrue("Enabling Service Validation Result not found", validationResult instanceof EnablingServiceValidationResult);
}
private ValidationContext getServiceValidationContext(final ControllerServiceState serviceState, final ControllerServiceProvider serviceProvider) {
final ValidationContext context = Mockito.mock(ValidationContext.class);
final String serviceIdentifier = MockControllerService.class.getName();
final ControllerServiceNode serviceNode = Mockito.mock(ControllerServiceNode.class);
Mockito.when(serviceProvider.getControllerServiceNode(serviceIdentifier)).thenReturn(serviceNode);
Mockito.when(serviceNode.getState()).thenReturn(serviceState);
Mockito.when(serviceNode.isActive()).thenReturn(true);
final PropertyDescriptor property = new PropertyDescriptor.Builder()
.name(MockControllerService.class.getSimpleName())
.identifiesControllerService(ControllerService.class)
.required(true)
.build();
final Map<PropertyDescriptor, String> properties = Collections.singletonMap(property, serviceIdentifier);
Mockito.when(context.getProperties()).thenReturn(properties);
final PropertyValue propertyValue = Mockito.mock(PropertyValue.class);
Mockito.when(propertyValue.getValue()).thenReturn(serviceIdentifier);
Mockito.when(context.getProperty(Mockito.eq(property))).thenReturn(propertyValue);
return context;
}
private static class ValidationControlledAbstractComponentNode extends AbstractComponentNode {
private final long pauseMillis;
private volatile ParameterContext paramContext = null;
public ValidationControlledAbstractComponentNode(final long pauseMillis, final ValidationTrigger validationTrigger) {
super("id", Mockito.mock(ValidationContextFactory.class), Mockito.mock(ControllerServiceProvider.class), "unit test component",
ValidationControlledAbstractComponentNode.class.getCanonicalName(), Mockito.mock(ComponentVariableRegistry.class), Mockito.mock(ReloadComponent.class),
Mockito.mock(ExtensionManager.class), validationTrigger, false);
this(pauseMillis, validationTrigger, Mockito.mock(ControllerServiceProvider.class));
}
public ValidationControlledAbstractComponentNode(final long pauseMillis, final ValidationTrigger validationTrigger, final ControllerServiceProvider controllerServiceProvider) {
super("id", Mockito.mock(ValidationContextFactory.class), controllerServiceProvider, "unit test component",
ValidationControlledAbstractComponentNode.class.getCanonicalName(), Mockito.mock(ComponentVariableRegistry.class), Mockito.mock(ReloadComponent.class),
Mockito.mock(ExtensionManager.class), validationTrigger, false);
this.pauseMillis = pauseMillis;
}
@ -288,4 +345,8 @@ public class TestAbstractComponentNode {
return sensitive;
}
}
private interface MockControllerService extends ControllerService {
}
}

View File

@ -231,7 +231,6 @@ public class TestStandardControllerServiceProvider {
provider.enableControllerService(serviceNodeB);
serviceNodeA.performValidation();
assertSame(ValidationStatus.VALID, serviceNodeA.getValidationStatus(5, TimeUnit.SECONDS));
final long maxTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(10);
// Wait for Service A to become ENABLED. This will happen in a background thread after approximately 5 seconds, now that Service A is valid.