Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIFI-3785: Added feature to move controller services between process … #8965

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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.web.api.entity;

import io.swagger.v3.oas.annotations.media.Schema;

import jakarta.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "processGroupOptionEntity")
public class ProcessGroupOptionEntity extends Entity {

private String text;
private String value;
private String description;
private boolean disabled;

/**
* The name for this ProcessGroup.
*
* @return The name
*/
@Schema(description = "The name of this ProcessGroup.")
public String getText() {
return text;
}

public void setText(String id) {
this.text = id;
}

/**
* The id for this ProcessGroup.
*
* @return The id
*/
@Schema(description = "The id of this ProcessGroup.")
public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

/**
* Text containing any conflicts for this ProcessGroup.
*
* @return The text
*/
@Schema(description = "Text containing any conflicts for this ProcessGroup.")
public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

/**
* Boolean showing whether this option is disabled.
*
* @return The disabled boolean
*/
@Schema(description = "Boolean showing whether this option is disabled.")
public boolean isDisabled() {
return disabled;
}

public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public class StandardControllerServiceNode extends AbstractComponentNode impleme
private volatile LogLevel bulletinLevel = LogLevel.WARN;

private final AtomicBoolean active;
private final AtomicBoolean moving;

public StandardControllerServiceNode(final LoggableComponent<ControllerService> implementation, final LoggableComponent<ControllerService> proxiedControllerService,
final ControllerServiceInvocationHandler invocationHandler, final String id, final ValidationContextFactory validationContextFactory,
Expand All @@ -134,6 +135,7 @@ public StandardControllerServiceNode(final LoggableComponent<ControllerService>
super(id, validationContextFactory, serviceProvider, componentType, componentCanonicalClass, reloadComponent, extensionManager, validationTrigger, isExtensionMissing);
this.serviceProvider = serviceProvider;
this.active = new AtomicBoolean();
this.moving = new AtomicBoolean(false);
setControllerServiceAndProxy(implementation, proxiedControllerService, invocationHandler);
stateTransition = new ServiceStateTransition(this);
this.comment = "";
Expand Down Expand Up @@ -445,7 +447,14 @@ public ControllerServiceState getState() {
public boolean isActive() {
return this.active.get();
}

@Override
public boolean isMoving() {
return this.moving.get();
}
@Override
public void setMoving(boolean moving) {
this.moving.set(moving);
}
@Override
public boolean awaitEnabled(final long timePeriod, final TimeUnit timeUnit) throws InterruptedException {
LOG.debug("Waiting up to {} {} for {} to be enabled", timePeriod, timeUnit, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2588,7 +2588,9 @@ public void removeControllerService(final ControllerServiceNode service) {
throw new IllegalStateException("ControllerService " + service.getIdentifier() + " is not a member of this Process Group");
}

service.verifyCanDelete();
if (!service.isMoving()) {
service.verifyCanDelete();
}

try (final NarCloseable x = NarCloseable.withComponentNarLoader(extensionManager, service.getControllerServiceImplementation().getClass(), service.getIdentifier())) {
final ConfigurationContext configurationContext = new StandardConfigurationContext(service, controllerServiceProvider, null);
Expand Down Expand Up @@ -2635,6 +2637,7 @@ public void removeControllerService(final ControllerServiceNode service) {
} catch (Throwable t) {
}
}
service.setMoving(false);
writeLock.unlock();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ public interface ControllerServiceNode extends ComponentNode, VersionedComponent
*/
boolean isActive();

boolean isMoving();

void setMoving(boolean moving);

/**
* Waits up to the given amount of time for the Controller Service to transition to an ENABLED state.
* @param timePeriod maximum amount of time to wait
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2171,6 +2171,16 @@ ControllerServiceReferencingComponentsEntity updateControllerServiceReferencingC
*/
ControllerServiceEntity updateControllerService(Revision revision, ControllerServiceDTO controllerServiceDTO);

/**
* Moves the specified controller service.
*
* @param revision Revision to compare with current base revision
* @param controllerServiceDTO The controller service DTO
* @param newProcessGroupID The id of the process group the controller service is being moved to
* @return The controller service DTO
*/
ControllerServiceEntity moveControllerService(final Revision revision, final ControllerServiceDTO controllerServiceDTO, final String newProcessGroupID);

/**
* Performs verification of the given Configuration for the Controller Service with the given ID
* @param controllerServiceId the id of the controller service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3009,6 +3009,41 @@ public ControllerServiceEntity updateControllerService(final Revision revision,
return entityFactory.createControllerServiceEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, operatePermissions, bulletinEntities);
}

@Override
public ControllerServiceEntity moveControllerService(final Revision revision, final ControllerServiceDTO controllerServiceDTO, final String newProcessGroupID) {
// get the component, ensure we have access to it, and perform the move request
final ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(controllerServiceDTO.getId());
final RevisionUpdate<ControllerServiceDTO> snapshot = updateComponent(revision,
controllerService,
() -> {
final ProcessGroup oldParentGroup = controllerService.getProcessGroup();
controllerService.setMoving(true);
oldParentGroup.removeControllerService(controllerService);
if (!oldParentGroup.isRootGroup() && oldParentGroup.getParent().getIdentifier().equals(newProcessGroupID)) {
// move to parent process group
oldParentGroup.getParent().addControllerService(controllerService);
} else {
// move to child process group
oldParentGroup.getProcessGroup(newProcessGroupID).addControllerService(controllerService);
}
return controllerService;
},
cs -> {
final ControllerServiceDTO dto = dtoFactory.createControllerServiceDto(cs);
final ControllerServiceReference ref = controllerService.getReferences();
final ControllerServiceReferencingComponentsEntity referencingComponentsEntity = createControllerServiceReferencingComponentsEntity(ref);
dto.setReferencingComponents(referencingComponentsEntity.getControllerServiceReferencingComponents());
return dto;
});

final PermissionsDTO permissions = dtoFactory.createPermissionsDto(controllerService);
final PermissionsDTO operatePermissions = dtoFactory.createPermissionsDto(new OperationAuthorizable(controllerService));
final List<BulletinDTO> bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(controllerServiceDTO.getId()));
final List<BulletinEntity> bulletinEntities = bulletins.stream().map(bulletin -> entityFactory.createBulletinEntity(bulletin, permissions.getCanRead())).collect(Collectors.toList());
controllerService.performValidation();
return entityFactory.createControllerServiceEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, operatePermissions, bulletinEntities);
}

@Override
public List<ConfigVerificationResultDTO> performControllerServiceConfigVerification(final String controllerServiceId, final Map<String, String> properties, final Map<String, String> variables) {
return controllerServiceDAO.verifyConfiguration(controllerServiceId, properties, variables);
Expand Down
Loading