You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by pv...@apache.org on 2018/04/04 21:26:33 UTC
nifi git commit: NIFI-5027 Adding commands pg-get-services,
pg-enable-services,
and pg-disable-services - Improving response when service is stuck enabling,
and improving response when some services couldn't be enabled - Throwing
exception when a service
Repository: nifi
Updated Branches:
refs/heads/master 1913b1e2a -> 7abb02fff
NIFI-5027 Adding commands pg-get-services, pg-enable-services, and pg-disable-services
- Improving response when service is stuck enabling, and improving response when some services couldn't be enabled
- Throwing exception when a service is stuck enabling or can't be enabled so that standalone mode gets a non-zero status code, also allowing use of -verbose so stand-alone can decide if output is desired
- Improving information provided by pg-disable-services
Signed-off-by: Pierre Villard <pi...@gmail.com>
This closes #2604.
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/7abb02ff
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/7abb02ff
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/7abb02ff
Branch: refs/heads/master
Commit: 7abb02fff000e70523c8b7afb6d3b9a7df5c929f
Parents: 1913b1e
Author: Bryan Bende <bb...@apache.org>
Authored: Tue Apr 3 09:30:30 2018 -0400
Committer: Pierre Villard <pi...@gmail.com>
Committed: Wed Apr 4 23:26:24 2018 +0200
----------------------------------------------------------------------
.../cli/impl/client/nifi/FlowClient.java | 19 ++
.../impl/client/nifi/impl/JerseyFlowClient.java | 40 ++++
.../cli/impl/command/AbstractCommand.java | 8 +
.../cli/impl/command/nifi/NiFiCommandGroup.java | 6 +
.../nifi/pg/PGDisableControllerServices.java | 176 +++++++++++++++
.../nifi/pg/PGEnableControllerServices.java | 221 +++++++++++++++++++
.../nifi/pg/PGGetControllerServices.java | 60 +++++
.../pg/cs/ControllerServiceStateCounts.java | 58 +++++
.../nifi/pg/cs/ControllerServiceStates.java | 28 +++
.../nifi/pg/cs/ControllerServiceUtil.java | 68 ++++++
.../impl/result/ControllerServicesResult.java | 81 +++++++
11 files changed, 765 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
index 5e56cd3..bc56ece 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/FlowClient.java
@@ -16,6 +16,8 @@
*/
package org.apache.nifi.toolkit.cli.impl.client.nifi;
+import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
import org.apache.nifi.web.api.entity.CurrentUserEntity;
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
@@ -78,4 +80,21 @@ public interface FlowClient {
VersionedFlowSnapshotMetadataSetEntity getVersions(String registryId, String bucketId, String flowId)
throws NiFiClientException, IOException;
+
+ /**
+ * Retrieves the controller services for the given group.
+ *
+ * @param groupId the process group id
+ * @return tje controller services entity
+ */
+ ControllerServicesEntity getControllerServices(String groupId) throws NiFiClientException, IOException;
+
+ /**
+ * Enable or disable controller services in the given process group.
+ *
+ * @param activateControllerServicesEntity the entity indicating the process group to enable/disable services for
+ * @return the activate response
+ */
+ ActivateControllerServicesEntity activateControllerServices(ActivateControllerServicesEntity activateControllerServicesEntity) throws NiFiClientException, IOException;
+
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
index 36a74bf..4489998 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyFlowClient.java
@@ -23,7 +23,9 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupBox;
import org.apache.nifi.web.api.dto.PositionDTO;
import org.apache.nifi.web.api.dto.flow.FlowDTO;
import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO;
+import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
import org.apache.nifi.web.api.entity.ComponentEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
import org.apache.nifi.web.api.entity.CurrentUserEntity;
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
@@ -177,4 +179,42 @@ public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient
return getRequestBuilder(target).get(VersionedFlowSnapshotMetadataSetEntity.class);
});
}
+
+ @Override
+ public ControllerServicesEntity getControllerServices(final String groupId) throws NiFiClientException, IOException {
+ if (StringUtils.isBlank(groupId)) {
+ throw new IllegalArgumentException("Group Id cannot be null or blank");
+ }
+
+ return executeAction("Error retrieving controller services", () -> {
+ final WebTarget target = flowTarget
+ .path("process-groups/{id}/controller-services")
+ .resolveTemplate("id", groupId);
+
+ return getRequestBuilder(target).get(ControllerServicesEntity.class);
+ });
+ }
+
+ @Override
+ public ActivateControllerServicesEntity activateControllerServices(final ActivateControllerServicesEntity activateControllerServicesEntity)
+ throws NiFiClientException, IOException {
+
+ if (activateControllerServicesEntity == null) {
+ throw new IllegalArgumentException("Entity cannot be null");
+ }
+
+ if (StringUtils.isBlank(activateControllerServicesEntity.getId())) {
+ throw new IllegalArgumentException("Entity must contain a process group id");
+ }
+
+ return executeAction("Error enabling or disabling controlling services", () -> {
+ final WebTarget target = flowTarget
+ .path("process-groups/{id}/controller-services")
+ .resolveTemplate("id", activateControllerServicesEntity.getId());
+
+ return getRequestBuilder(target).put(
+ Entity.entity(activateControllerServicesEntity, MediaType.APPLICATION_JSON_TYPE),
+ ActivateControllerServicesEntity.class);
+ });
+ }
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
index 1742243..b692bb0 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java
@@ -205,4 +205,12 @@ public abstract class AbstractCommand<R extends Result> implements Command<R> {
}
}
+ protected boolean isVerbose(final Properties properties) {
+ return properties.containsKey(CommandOption.VERBOSE.getLongName());
+ }
+
+ protected boolean isInteractive() {
+ return getContext().isInteractive();
+ }
+
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
index 8f2206a..ac5ec62 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
@@ -21,7 +21,10 @@ import org.apache.nifi.toolkit.cli.impl.command.AbstractCommandGroup;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.CurrentUser;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetRootId;
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGChangeVersion;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGDisableControllerServices;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGEnableControllerServices;
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetAllVersions;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetControllerServices;
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVars;
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVersion;
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGImport;
@@ -68,6 +71,9 @@ public class NiFiCommandGroup extends AbstractCommandGroup {
commands.add(new PGGetAllVersions());
commands.add(new PGList());
commands.add(new PGStatus());
+ commands.add(new PGGetControllerServices());
+ commands.add(new PGEnableControllerServices());
+ commands.add(new PGDisableControllerServices());
return new ArrayList<>(commands);
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGDisableControllerServices.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGDisableControllerServices.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGDisableControllerServices.java
new file mode 100644
index 0000000..26552db
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGDisableControllerServices.java
@@ -0,0 +1,176 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.cs.ControllerServiceStateCounts;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.cs.ControllerServiceStates;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.cs.ControllerServiceUtil;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
+import org.apache.nifi.web.api.entity.BulletinEntity;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class PGDisableControllerServices extends AbstractNiFiCommand<VoidResult> {
+
+ public static final int MAX_DISABLING_ITERATIONS = 20;
+ public static final long DELAY_MS = 2000;
+
+ public PGDisableControllerServices() {
+ super("pg-disable-services", VoidResult.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Disables the controller services in the given process group. Any services that are in use by a running component " +
+ "will fail to be disabled and will need to be stopped first using pg-stop.";
+ }
+
+ @Override
+ protected void doInitialize(final Context context) {
+ addOption(CommandOption.PG_ID.createOption());
+ }
+
+ @Override
+ public VoidResult doExecute(NiFiClient client, Properties properties)
+ throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+ final FlowClient flowClient = client.getFlowClient();
+ final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
+
+ final ControllerServiceStateCounts initialServiceStates = getControllerServiceStates(flowClient, pgId);
+
+ // if we have no enabled or enabling services then there is nothing to do so return
+ if (initialServiceStates.getEnabled() == 0 && initialServiceStates.getEnabling() == 0) {
+ if (shouldPrint(properties)) {
+ println();
+ println("No services are currently enabled/enabling, nothing to do...");
+ println();
+ }
+ return VoidResult.getInstance();
+ }
+
+ if (shouldPrint(properties)) {
+ println();
+ println("Starting states:");
+ printControllerServiceStates(initialServiceStates);
+ println();
+ println("Attempting to disable services...");
+ }
+
+ // send the request to disable services and wait a second
+ final ActivateControllerServicesEntity disableEntity = new ActivateControllerServicesEntity();
+ disableEntity.setId(pgId);
+ disableEntity.setState(ActivateControllerServicesEntity.STATE_DISABLED);
+
+ flowClient.activateControllerServices(disableEntity);
+ sleep(1000);
+
+ // wait for disabling services to become disabled, or until we've waited up to max number of waits
+ int disablingWaitCount = 1;
+ ControllerServiceStateCounts serviceStates = getControllerServiceStates(flowClient, pgId);
+ while (serviceStates.getDisabling() > 0 && disablingWaitCount < MAX_DISABLING_ITERATIONS) {
+ if (shouldPrint(properties)) {
+ println("Currently " + serviceStates.getDisabling() + " services are disabling, waiting to finish before proceeding ("
+ + disablingWaitCount + " of " + MAX_DISABLING_ITERATIONS + ")...");
+ }
+ sleep(DELAY_MS);
+ disablingWaitCount++;
+ serviceStates = getControllerServiceStates(flowClient, pgId);
+ }
+
+ // if we have still have disabling services then we may have a stuck service so throw an exception
+ if (serviceStates.getDisabling() > 0) {
+ if (shouldPrint(properties)) {
+ printServicesStillDisabling(flowClient, pgId);
+ }
+ throw new CommandException("One or more services may be stuck disabling, run command with -verbose to obtain more details");
+ }
+
+ // otherwise the command was successful so print the final states and return
+ if (shouldPrint(properties)) {
+ println();
+ println("Finished States:");
+ printControllerServiceStates(serviceStates);
+ println();
+ }
+
+ return VoidResult.getInstance();
+ }
+
+ private void printControllerServiceStates(final ControllerServiceStateCounts serviceStates) {
+ println(" - " + serviceStates.getEnabled() + " enabled");
+ println(" - " + serviceStates.getEnabling() + " enabling");
+ println(" - " + serviceStates.getDisabled() + " disabled");
+ println(" - " + serviceStates.getDisabling() + " disabling");
+ }
+
+ private void printServicesStillDisabling(final FlowClient flowClient, final String pgId)
+ throws NiFiClientException, IOException {
+
+ final ControllerServicesEntity servicesEntity = flowClient.getControllerServices(pgId);
+ if (servicesEntity == null || servicesEntity.getControllerServices() == null) {
+ return;
+ }
+
+ println();
+ println("One or more services appear to be stuck disabling: ");
+
+ for (final ControllerServiceEntity serviceEntity : servicesEntity.getControllerServices()) {
+ if (ControllerServiceStates.STATE_DISABLING.equals(serviceEntity.getComponent().getState())) {
+ println();
+ println("Service: " + serviceEntity.getId() + " - " + serviceEntity.getComponent().getName());
+
+ if (serviceEntity.getBulletins() != null) {
+ println();
+ println("Reasons: ");
+ for (final BulletinEntity bulletinEntity : serviceEntity.getBulletins()) {
+ println("- " + bulletinEntity.getBulletin().getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ private void sleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ Thread.interrupted();
+ }
+ }
+
+ private boolean shouldPrint(final Properties properties) {
+ return isInteractive() || isVerbose(properties);
+ }
+
+ private ControllerServiceStateCounts getControllerServiceStates(final FlowClient flowClient, final String pgId)
+ throws NiFiClientException, IOException {
+ return ControllerServiceUtil.getControllerServiceStates(flowClient, pgId);
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGEnableControllerServices.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGEnableControllerServices.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGEnableControllerServices.java
new file mode 100644
index 0000000..98689c5
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGEnableControllerServices.java
@@ -0,0 +1,221 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.cs.ControllerServiceStateCounts;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.cs.ControllerServiceStates;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.cs.ControllerServiceUtil;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
+import org.apache.nifi.web.api.entity.BulletinEntity;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class PGEnableControllerServices extends AbstractNiFiCommand<VoidResult> {
+
+ public static final int MAX_ATTEMPTS = 180;
+ public static final int MAX_ENABLING_ITERATIONS = 20;
+ public static final long ENABLING_DELAY_MS = 2000;
+
+ public PGEnableControllerServices() {
+ super("pg-enable-services", VoidResult.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Attempts to enable all controller services in the given PG. In stand-alone mode this command will not " +
+ "produce all of the output seen in interactive mode unless the --verbose argument is specified.";
+ }
+
+ @Override
+ protected void doInitialize(final Context context) {
+ addOption(CommandOption.PG_ID.createOption());
+ }
+
+ @Override
+ public VoidResult doExecute(final NiFiClient client, final Properties properties)
+ throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+ final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
+ final FlowClient flowClient = client.getFlowClient();
+
+ if (shouldPrint(properties)) {
+ println();
+ }
+
+ int count = 0;
+ int prevNumEnabled = -1;
+ int enablingIterations = 1;
+
+ // request to enable services until the number of enabled services is no longer changing, which means either all
+ // services have been enabled, or the rest of the services are invalid and can't be enabled
+ while (count < MAX_ATTEMPTS) {
+
+ // retrieve the current states of the services in the given pg
+ final ControllerServiceStateCounts states = getControllerServiceStates(flowClient, pgId);
+
+ // if any services are currently enabling then sleep and loop again
+ if (states.getEnabling() > 0) {
+ if (enablingIterations < MAX_ENABLING_ITERATIONS) {
+ if (shouldPrint(properties)) {
+ println("Currently " + states.getEnabling() + " services are enabling, waiting to finish before " +
+ "proceeding (" + enablingIterations + " of " + MAX_ENABLING_ITERATIONS + ")");
+ }
+ try {
+ Thread.sleep(ENABLING_DELAY_MS);
+ } catch (InterruptedException e) {
+ Thread.interrupted();
+ }
+
+ enablingIterations++;
+ continue;
+ } else {
+ if (shouldPrint(properties)) {
+ printServicesStillEnabling(flowClient, pgId);
+ }
+
+ // throw an exception so stand-alone mode will exit with a non-zero status code
+ throw new CommandException("One or more services are stuck enabling, run command with -verbose to obtain more details");
+ }
+ }
+
+ // reset the enabling iteration count since we got past the above block without breaking
+ enablingIterations = 1;
+
+ // if no services are enabling and the number of enabled services equals the number of enabled services from
+ // last iteration, then we know there are no more that we can enable so break
+ if (states.getEnabled() == prevNumEnabled && states.getEnabling() == 0) {
+ if (shouldPrint(properties)) {
+ println();
+ println("Finished with " + states.getEnabled() + " enabled services and " + states.getDisabled() + " disabled services");
+ }
+
+ if (states.getDisabled() > 0 || states.getDisabling() > 0) {
+ if (shouldPrint(properties)) {
+ printServicesNotEnabled(flowClient, pgId);
+ println();
+ }
+
+ // throw an exception so stand-alone mode will exit with a non-zero status code
+ throw new CommandException("One or more services could not be enabled, run command with -verbose to obtain more details");
+ } else {
+ if (shouldPrint(properties)) {
+ println();
+ }
+
+ // just break here to proceed with normal completion (i.e. will have a zero status code)
+ break;
+ }
+ }
+
+ // if we didn't break then store the number that were enabled to compare with next time
+ prevNumEnabled = states.getEnabled();
+
+ if (shouldPrint(properties)) {
+ println("Currently " + states.getEnabled() + " enabled services and " + states.getDisabled()
+ + " disabled services, attempting to enable services...");
+ }
+
+ // send the request to enable services
+ final ActivateControllerServicesEntity enableEntity = new ActivateControllerServicesEntity();
+ enableEntity.setId(pgId);
+ enableEntity.setState(ActivateControllerServicesEntity.STATE_ENABLED);
+
+ flowClient.activateControllerServices(enableEntity);
+ count++;
+ }
+
+ return VoidResult.getInstance();
+ }
+
+ private boolean shouldPrint(final Properties properties) {
+ return isInteractive() || isVerbose(properties);
+ }
+
+ private ControllerServiceStateCounts getControllerServiceStates(final FlowClient flowClient, final String pgId)
+ throws NiFiClientException, IOException {
+ return ControllerServiceUtil.getControllerServiceStates(flowClient, pgId);
+ }
+
+ private void printServicesStillEnabling(final FlowClient flowClient, final String pgId)
+ throws NiFiClientException, IOException {
+
+ final ControllerServicesEntity servicesEntity = flowClient.getControllerServices(pgId);
+ if (servicesEntity == null || servicesEntity.getControllerServices() == null) {
+ return;
+ }
+
+ println();
+ println("One or more services appear to be stuck enabling: ");
+
+ for (final ControllerServiceEntity serviceEntity : servicesEntity.getControllerServices()) {
+ if (ControllerServiceStates.STATE_ENABLING.equals(serviceEntity.getComponent().getState())) {
+ println();
+ println("Service: " + serviceEntity.getId() + " - " + serviceEntity.getComponent().getName());
+
+ if (serviceEntity.getBulletins() != null) {
+ println();
+ println("Reasons: ");
+ for (final BulletinEntity bulletinEntity : serviceEntity.getBulletins()) {
+ println("- " + bulletinEntity.getBulletin().getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ private void printServicesNotEnabled(final FlowClient flowClient, final String pgId)
+ throws NiFiClientException, IOException {
+
+ final ControllerServicesEntity servicesEntity = flowClient.getControllerServices(pgId);
+ if (servicesEntity == null || servicesEntity.getControllerServices() == null) {
+ return;
+ }
+
+ println();
+ println("The following services could not be enabled: ");
+
+ for (final ControllerServiceEntity serviceEntity : servicesEntity.getControllerServices()) {
+ if (!ControllerServiceStates.STATE_ENABLED.equals(serviceEntity.getComponent().getState())) {
+ println();
+ println("Service: " + serviceEntity.getId() + " - " + serviceEntity.getComponent().getName());
+
+ final ControllerServiceDTO serviceDTO = serviceEntity.getComponent();
+ if (serviceDTO.getValidationErrors() != null) {
+ println();
+ println("Validation Errors: ");
+ for (final String validationError : serviceDTO.getValidationErrors()) {
+ println("- " + validationError);
+ }
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetControllerServices.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetControllerServices.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetControllerServices.java
new file mode 100644
index 0000000..f4178aa
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGGetControllerServices.java
@@ -0,0 +1,60 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.ControllerServicesResult;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Command to get the list of controller services for a given process group.
+ */
+public class PGGetControllerServices extends AbstractNiFiCommand<ControllerServicesResult> {
+
+ public PGGetControllerServices() {
+ super("pg-get-services", ControllerServicesResult.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Retrieves the list of controller services for the given process group.";
+ }
+
+ @Override
+ protected void doInitialize(final Context context) {
+ addOption(CommandOption.PG_ID.createOption());
+ }
+
+ @Override
+ public ControllerServicesResult doExecute(NiFiClient client, Properties properties)
+ throws NiFiClientException, IOException, MissingOptionException, CommandException {
+ final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
+ final FlowClient flowClient = client.getFlowClient();
+ final ControllerServicesEntity servicesEntity = flowClient.getControllerServices(pgId);
+ return new ControllerServicesResult(getResultType(properties), servicesEntity);
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStateCounts.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStateCounts.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStateCounts.java
new file mode 100644
index 0000000..d6fb3f9
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStateCounts.java
@@ -0,0 +1,58 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg.cs;
+
+public class ControllerServiceStateCounts {
+
+ private int enabled;
+ private int enabling;
+ private int disabled;
+ private int disabling;
+
+ public int getEnabled() {
+ return enabled;
+ }
+
+ public void incrementEnabled() {
+ enabled++;
+ }
+
+ public int getEnabling() {
+ return enabling;
+ }
+
+ public void incrementEnabling() {
+ enabling++;
+ }
+
+ public int getDisabled() {
+ return disabled;
+ }
+
+ public void incrementDisabled() {
+ disabled++;
+ }
+
+ public int getDisabling() {
+ return disabling;
+ }
+
+ public void incrementDisabling() {
+ disabling++;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStates.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStates.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStates.java
new file mode 100644
index 0000000..f1b01c1
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceStates.java
@@ -0,0 +1,28 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg.cs;
+
+/**
+ * Possible states for a controller service.
+ */
+public interface ControllerServiceStates {
+
+ String STATE_ENABLED = "ENABLED";
+ String STATE_ENABLING = "ENABLING";
+ String STATE_DISABLED = "DISABLED";
+ String STATE_DISABLING = "DISABLING";
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceUtil.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceUtil.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceUtil.java
new file mode 100644
index 0000000..ccf6227
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/cs/ControllerServiceUtil.java
@@ -0,0 +1,68 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.pg.cs;
+
+import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
+
+import java.io.IOException;
+
+/**
+ * Utility methods for controller service commands.
+ */
+public class ControllerServiceUtil {
+
+ public static ControllerServiceStateCounts getControllerServiceStates(final FlowClient flowClient, final String pgId)
+ throws NiFiClientException, IOException {
+ final ControllerServicesEntity servicesEntity = flowClient.getControllerServices(pgId);
+ return getControllerServiceStates(servicesEntity);
+ }
+
+ public static ControllerServiceStateCounts getControllerServiceStates(final ControllerServicesEntity servicesEntity)
+ throws NiFiClientException {
+
+ final ControllerServiceStateCounts states = new ControllerServiceStateCounts();
+ if (servicesEntity == null || servicesEntity.getControllerServices() == null || servicesEntity.getControllerServices().isEmpty()) {
+ return states;
+ }
+
+ for (final ControllerServiceEntity serviceEntity : servicesEntity.getControllerServices()) {
+ final String state = serviceEntity.getComponent().getState();
+ switch(state) {
+ case ControllerServiceStates.STATE_ENABLED:
+ states.incrementEnabled();
+ break;
+ case ControllerServiceStates.STATE_ENABLING:
+ states.incrementEnabling();
+ break;
+ case ControllerServiceStates.STATE_DISABLED:
+ states.incrementDisabled();
+ break;
+ case ControllerServiceStates.STATE_DISABLING:
+ states.incrementDisabling();
+ break;
+ default:
+ throw new NiFiClientException("Unexpected controller service state: " + state);
+ }
+ }
+
+ return states;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/7abb02ff/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ControllerServicesResult.java
----------------------------------------------------------------------
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ControllerServicesResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ControllerServicesResult.java
new file mode 100644
index 0000000..c25bc73
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ControllerServicesResult.java
@@ -0,0 +1,81 @@
+/*
+ * 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.toolkit.cli.impl.result;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter;
+import org.apache.nifi.toolkit.cli.impl.result.writer.Table;
+import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Result for ControllerServicesEntity.
+ */
+public class ControllerServicesResult extends AbstractWritableResult<ControllerServicesEntity> {
+
+ private final ControllerServicesEntity controllerServicesEntity;
+
+ public ControllerServicesResult(final ResultType resultType, final ControllerServicesEntity controllerServicesEntity) {
+ super(resultType);
+ this.controllerServicesEntity = controllerServicesEntity;
+ Validate.notNull(this.controllerServicesEntity);
+ }
+
+ @Override
+ protected void writeSimpleResult(final PrintStream output) throws IOException {
+ final Set<ControllerServiceEntity> serviceEntities = controllerServicesEntity.getControllerServices();
+ if (serviceEntities == null) {
+ return;
+ }
+
+ final List<ControllerServiceDTO> serviceDTOS = serviceEntities.stream()
+ .map(s -> s.getComponent())
+ .collect(Collectors.toList());
+
+ Collections.sort(serviceDTOS, Comparator.comparing(ControllerServiceDTO::getName));
+
+ final Table table = new Table.Builder()
+ .column("#", 3, 3, false)
+ .column("Name", 5, 40, false)
+ .column("State", 5, 40, false)
+ .build();
+
+ for (int i=0; i < serviceDTOS.size(); i++) {
+ final ControllerServiceDTO serviceDTO = serviceDTOS.get(i);
+ table.addRow(String.valueOf(i+1), serviceDTO.getName(), serviceDTO.getState());
+ }
+
+ final TableWriter tableWriter = new DynamicTableWriter();
+ tableWriter.write(table, output);
+ }
+
+ @Override
+ public ControllerServicesEntity getResult() {
+ return controllerServicesEntity;
+ }
+}