NIFI-3695 - created the nifi admin toolkit which includes shell scripts and classes to support notification and basic node management in standalone and clustered nifi.
This closes #1669. Signed-off-by: Andy LoPresto <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/c0f0462e Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/c0f0462e Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/c0f0462e Branch: refs/heads/master Commit: c0f0462e8bc829d5e4ac2415a9fa77d3f5d6bdc3 Parents: ba2bdf8 Author: Yolanda M. Davis <[email protected]> Authored: Tue Feb 7 10:28:15 2017 -0500 Committer: Andy LoPresto <[email protected]> Committed: Mon Apr 24 23:08:37 2017 -0700 ---------------------------------------------------------------------- .../nifi/web/api/entity/BulletinEntity.java | 2 + .../nifi/authorization/FileAuthorizer.java | 4 + .../nifi/cluster/manager/BulletinMerger.java | 12 +- .../org/apache/nifi/web/NiFiServiceFacade.java | 11 + .../nifi/web/StandardNiFiServiceFacade.java | 7 + .../apache/nifi/web/api/ControllerResource.java | 68 +++ .../web/StandardNiFiServiceFacadeSpec.groovy | 56 ++- nifi-toolkit/nifi-toolkit-admin/pom.xml | 186 +++++++++ .../nifi/toolkit/admin/AbstractAdminTool.groovy | 110 +++++ .../toolkit/admin/client/ClientFactory.groovy | 27 ++ .../admin/client/NiFiClientFactory.groovy | 172 ++++++++ .../toolkit/admin/client/NiFiClientUtil.groovy | 144 +++++++ .../admin/nodemanager/NodeManagerTool.groovy | 289 +++++++++++++ .../admin/notify/NotificationTool.groovy | 181 ++++++++ .../nifi/toolkit/admin/util/AdminUtil.groovy | 69 ++++ .../nifi/toolkit/admin/util/Version.groovy | 82 ++++ .../admin/client/NiFiClientFactorySpec.groovy | 247 +++++++++++ .../admin/client/NiFiClientUtilSpec.groovy | 109 +++++ .../nodemanager/NodeManagerToolSpec.groovy | 414 +++++++++++++++++++ .../admin/notify/NotificationToolSpec.groovy | 171 ++++++++ .../toolkit/admin/util/AdminUtilSpec.groovy | 54 +++ .../src/test/resources/conf/bootstrap.conf | 32 ++ .../resources/conf/login-identity-providers.xml | 112 +++++ .../src/test/resources/conf/nifi.properties | 28 ++ .../test/resources/external/conf/bootstrap.conf | 32 ++ .../external/conf/login-identity-providers.xml | 112 +++++ .../resources/external/conf/nifi.properties | 28 ++ .../test/resources/filemanager/bootstrap.conf | 32 ++ .../src/test/resources/filemanager/myid | 1 + .../filemanager/nifi-test-archive.tar.gz | Bin 0 -> 27167 bytes .../resources/filemanager/nifi-test-archive.zip | Bin 0 -> 32415 bytes .../test/resources/filemanager/nifi.properties | 32 ++ .../resources/lib/nifi-framework-nar-1.2.0.nar | Bin 0 -> 1385 bytes .../test/resources/no_rules/conf/bootstrap.conf | 21 + .../no_rules/conf/login-identity-providers.xml | 112 +++++ .../resources/no_rules/conf/nifi.properties | 29 ++ .../test/resources/notify/conf/bootstrap.conf | 74 ++++ .../notify/conf/nifi-secured.properties | 107 +++++ .../test/resources/notify/conf/nifi.properties | 204 +++++++++ .../src/test/resources/overlay.properties | 41 ++ .../test/resources/upgrade/conf/bootstrap.conf | 21 + .../upgrade/conf/login-identity-providers.xml | 112 +++++ .../test/resources/upgrade/conf/nifi.properties | 28 ++ .../upgrade/lib/nifi-framework-nar-1.2.0.nar | Bin 0 -> 1385 bytes nifi-toolkit/nifi-toolkit-assembly/pom.xml | 4 + .../src/main/resources/bin/node-manager.bat | 39 ++ .../src/main/resources/bin/node-manager.sh | 119 ++++++ .../src/main/resources/bin/notify.bat | 39 ++ .../src/main/resources/bin/notify.sh | 120 ++++++ nifi-toolkit/pom.xml | 4 + pom.xml | 5 + 51 files changed, 3900 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/BulletinEntity.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/BulletinEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/BulletinEntity.java index 9e93a24..83f45d1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/BulletinEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/BulletinEntity.java @@ -21,12 +21,14 @@ import org.apache.nifi.web.api.dto.BulletinDTO; import org.apache.nifi.web.api.dto.ReadablePermission; import org.apache.nifi.web.api.dto.util.TimeAdapter; +import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.util.Date; /** * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a BulletinDTO. */ +@XmlRootElement(name = "bulletinEntity") public class BulletinEntity extends Entity implements ReadablePermission { private Long id; http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java index 9a310a2..c7440e2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java @@ -361,6 +361,10 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer { // grant access to the proxy resource addAccessPolicy(authorizations, ResourceType.Proxy.getValue(), jaxbNodeUser.getIdentifier(), WRITE_CODE); + //grant access to controller resource + addAccessPolicy(authorizations, ResourceType.Controller.getValue(), jaxbNodeUser.getIdentifier(), READ_CODE); + addAccessPolicy(authorizations, ResourceType.Controller.getValue(), jaxbNodeUser.getIdentifier(), WRITE_CODE); + // grant the user read/write access data of the root group if (rootGroupId != null) { addAccessPolicy(authorizations, ResourceType.Data.getValue() + ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, jaxbNodeUser.getIdentifier(), READ_CODE); http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/BulletinMerger.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/BulletinMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/BulletinMerger.java index 79b1447..952edab 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/BulletinMerger.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/BulletinMerger.java @@ -24,6 +24,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; + +import com.google.common.collect.Lists; public final class BulletinMerger { @@ -71,7 +74,12 @@ public final class BulletinMerger { } } - Collections.sort(bulletinEntities, (BulletinEntity o1, BulletinEntity o2) -> { + final List<BulletinEntity> entities = Lists.newArrayList(); + + final Map<String,List<BulletinEntity>> groupingEntities = bulletinEntities.stream().collect(Collectors.groupingBy(b -> b.getBulletin().getMessage())); + groupingEntities.values().stream().map(e -> e.get(0)).forEach(entities::add); + + Collections.sort(entities, (BulletinEntity o1, BulletinEntity o2) -> { final int timeComparison = o1.getTimestamp().compareTo(o2.getTimestamp()); if (timeComparison != 0) { return timeComparison; @@ -80,6 +88,6 @@ public final class BulletinMerger { return o1.getNodeAddress().compareTo(o2.getNodeAddress()); }); - return bulletinEntities; + return entities; } } http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java index 039cbf8..6f9ea98 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java @@ -25,6 +25,7 @@ import org.apache.nifi.controller.service.ControllerServiceState; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.web.api.dto.AccessPolicyDTO; import org.apache.nifi.web.api.dto.BulletinBoardDTO; +import org.apache.nifi.web.api.dto.BulletinDTO; import org.apache.nifi.web.api.dto.BulletinQueryDTO; import org.apache.nifi.web.api.dto.ClusterDTO; import org.apache.nifi.web.api.dto.ComponentHistoryDTO; @@ -66,6 +67,7 @@ import org.apache.nifi.web.api.dto.search.SearchResultsDTO; import org.apache.nifi.web.api.dto.status.ControllerStatusDTO; import org.apache.nifi.web.api.entity.AccessPolicyEntity; import org.apache.nifi.web.api.entity.ActionEntity; +import org.apache.nifi.web.api.entity.BulletinEntity; import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ConnectionStatusEntity; import org.apache.nifi.web.api.entity.ControllerBulletinsEntity; @@ -1049,6 +1051,15 @@ public interface NiFiServiceFacade { */ RemoteProcessGroupEntity deleteRemoteProcessGroup(Revision revision, String remoteProcessGroupId); + + /** + * Create a system bulletin + * + * @param bulletinDTO bulletin to send to users + * @param canRead allow users to read bulletin + */ + BulletinEntity createBulletin(final BulletinDTO bulletinDTO, final Boolean canRead); + // ---------------------------------------- // Funnel methods // ---------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index 4179745..b9b208e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -78,6 +78,7 @@ import org.apache.nifi.controller.service.ControllerServiceReference; import org.apache.nifi.controller.service.ControllerServiceState; import org.apache.nifi.controller.status.ProcessGroupStatus; import org.apache.nifi.diagnostics.SystemDiagnostics; +import org.apache.nifi.events.BulletinFactory; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.groups.ProcessGroupCounts; import org.apache.nifi.groups.RemoteProcessGroup; @@ -1380,6 +1381,12 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { }); } + @Override + public BulletinEntity createBulletin(final BulletinDTO bulletinDTO, final Boolean canRead){ + final Bulletin bulletin = BulletinFactory.createBulletin(bulletinDTO.getCategory(),bulletinDTO.getLevel(),bulletinDTO.getMessage()); + bulletinRepository.addBulletin(bulletin); + return entityFactory.createBulletinEntity(dtoFactory.createBulletinDto(bulletin),canRead); + } @Override public FunnelEntity createFunnel(final Revision revision, final String groupId, final FunnelDTO funnelDTO) { http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index 98400f2..cb87ca2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -40,10 +40,12 @@ import org.apache.nifi.controller.FlowController; import org.apache.nifi.web.IllegalClusterResourceRequestException; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.Revision; +import org.apache.nifi.web.api.dto.BulletinDTO; import org.apache.nifi.web.api.dto.ClusterDTO; import org.apache.nifi.web.api.dto.ControllerServiceDTO; import org.apache.nifi.web.api.dto.NodeDTO; import org.apache.nifi.web.api.dto.ReportingTaskDTO; +import org.apache.nifi.web.api.entity.BulletinEntity; import org.apache.nifi.web.api.entity.ClusterEntity; import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; @@ -261,6 +263,7 @@ public class ControllerResource extends ApplicationResource { @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") } ) + public Response createReportingTask( @Context final HttpServletRequest httpServletRequest, @ApiParam( @@ -330,6 +333,71 @@ public class ControllerResource extends ApplicationResource { ); } + /** + * Creates a Bulletin. + * + * @param httpServletRequest request + * @param requestBulletinEntity A bulletinEntity. + * @return A bulletinEntity. + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("bulletin") + @ApiOperation( + value = "Creates a new bulletin", + response = BulletinEntity.class, + authorizations = { + @Authorization(value = "Write - /controller", type = "") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response createBulletin( + @Context final HttpServletRequest httpServletRequest, + @ApiParam( + value = "The reporting task configuration details.", + required = true + ) final BulletinEntity requestBulletinEntity) { + + if (requestBulletinEntity == null || requestBulletinEntity.getBulletin() == null) { + throw new IllegalArgumentException("Bulletin details must be specified."); + } + + final BulletinDTO requestBulletin = requestBulletinEntity.getBulletin(); + if (requestBulletin.getId() != null) { + throw new IllegalArgumentException("A bulletin ID cannot be specified."); + } + + if (StringUtils.isBlank(requestBulletin.getMessage())) { + throw new IllegalArgumentException("The bulletin message must be specified."); + } + + if (isReplicateRequest()) { + return replicate(HttpMethod.POST, requestBulletinEntity); + } + + return withWriteLock( + serviceFacade, + requestBulletinEntity, + lookup -> { + authorizeController(RequestAction.WRITE); + }, + null, + (bulletinEntity) -> { + final BulletinDTO bulletin = bulletinEntity.getBulletin(); + final BulletinEntity entity = serviceFacade.createBulletin(bulletin,true); + return generateOkResponse(entity).build(); + } + ); + } + // ------------------- // controller services // ------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy index 677b25d..29ab83a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy @@ -23,7 +23,11 @@ import org.apache.nifi.authorization.user.NiFiUser import org.apache.nifi.authorization.user.StandardNiFiUser import org.apache.nifi.authorization.user.NiFiUserDetails import org.apache.nifi.controller.service.ControllerServiceProvider +import org.apache.nifi.reporting.Bulletin +import org.apache.nifi.reporting.BulletinRepository +import org.apache.nifi.reporting.ComponentType import org.apache.nifi.web.api.dto.* +import org.apache.nifi.web.api.entity.BulletinEntity import org.apache.nifi.web.api.entity.UserEntity import org.apache.nifi.web.controller.ControllerFacade import org.apache.nifi.web.dao.AccessPolicyDAO @@ -36,7 +40,7 @@ import spock.lang.Ignore import spock.lang.Specification import spock.lang.Unroll -@Ignore + class StandardNiFiServiceFacadeSpec extends Specification { def setup() { @@ -49,6 +53,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { SecurityContextHolder.getContext().setAuthentication(null); } + @Ignore @Unroll def "CreateUser: isAuthorized: #isAuthorized"() { given: @@ -87,6 +92,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { createUserDTO() | null | ResourceFactory.usersResource | false | AuthorizationResult.denied() } + @Ignore @Unroll def "GetUser: isAuthorized: #isAuthorized"() { given: @@ -134,6 +140,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { createUserDTO() | false | AuthorizationResult.denied() } + @Ignore @Unroll def "UpdateUser: isAuthorized: #isAuthorized, policy exists: #userExists"() { given: @@ -188,6 +195,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { true | new Revision(1L, 'client1', 'root') | createUserDTO() | false | AuthorizationResult.denied() } + @Ignore @Unroll def "DeleteUser: isAuthorized: #isAuthorized, user exists: #userExists"() { given: @@ -239,6 +247,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { false | null | createUserDTO() | false | AuthorizationResult.denied() } + @Ignore @Unroll def "CreateUserGroup: isAuthorized: #isAuthorized"() { given: @@ -307,6 +316,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { createUserGroupDTO() | false | [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] } + @Ignore @Unroll def "GetUserGroup: isAuthorized: #isAuthorized"() { given: @@ -363,6 +373,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | false | AuthorizationResult.denied() } + @Ignore @Unroll def "UpdateUserGroup: isAuthorized: #isAuthorized, userGroupExists exists: #userGroupExists"() { given: @@ -444,6 +455,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] } + @Ignore @Unroll def "DeleteUserGroup: isAuthorized: #isAuthorized, userGroup exists: #userGroupExists"() { given: @@ -521,6 +533,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] } + @Ignore @Unroll def "CreateAccessPolicy: #isAuthorized"() { given: @@ -589,6 +602,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | AuthorizationResult.denied() } + @Ignore @Unroll def "GetAccessPolicy: isAuthorized: #isAuthorized"() { given: @@ -654,6 +668,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | AuthorizationResult.denied() } + @Ignore @Unroll def "UpdateAccessPolicy: isAuthorized: #isAuthorized, policy exists: #hasPolicy"() { given: @@ -741,6 +756,7 @@ class StandardNiFiServiceFacadeSpec extends Specification { AuthorizationResult.denied() } + @Ignore @Unroll def "DeleteAccessPolicy: isAuthorized: #isAuthorized, hasPolicy: #hasPolicy"() { given: @@ -828,6 +844,44 @@ class StandardNiFiServiceFacadeSpec extends Specification { AuthorizationResult.denied() } + + def "CreateBulletin Successfully"() { + given: + + def entityFactory = new EntityFactory() + def dtoFactory = new DtoFactory() + dtoFactory.setEntityFactory entityFactory + def authorizableLookup = Mock AuthorizableLookup + def controllerFacade = Mock ControllerFacade + def niFiServiceFacade = new StandardNiFiServiceFacade() + def bulletinRepository = Mock BulletinRepository + niFiServiceFacade.setAuthorizableLookup authorizableLookup + niFiServiceFacade.setDtoFactory dtoFactory + niFiServiceFacade.setEntityFactory entityFactory + niFiServiceFacade.setControllerFacade controllerFacade + niFiServiceFacade.setBulletinRepository bulletinRepository + + def bulletinDto = new BulletinDTO() + bulletinDto.category = "SYSTEM" + bulletinDto.message = "test system message" + bulletinDto.level = "WARN" + def bulletinEntity + def retBulletinEntity = new BulletinEntity() + retBulletinEntity.bulletin = bulletinDto + + when: + + bulletinEntity = niFiServiceFacade.createBulletin(bulletinDto,true) + + + then: + 1 * bulletinRepository.addBulletin(_ as Bulletin) + bulletinEntity + bulletinEntity.bulletin.message == bulletinDto.message + + + } + private UserGroupDTO createUserGroupDTO() { new UserGroupDTO(id: 'group-1', name: 'test group', users: [createUserEntity()] as Set) } http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/pom.xml b/nifi-toolkit/nifi-toolkit-admin/pom.xml new file mode 100644 index 0000000..37500c6 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/pom.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-toolkit</artifactId> + <version>1.2.0-SNAPSHOT</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + + <artifactId>nifi-toolkit-admin</artifactId> + + <dependencies> + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-toolkit-tls</artifactId> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-client-dto</artifactId> + <version>${client.version}</version> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-properties</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-properties-loader</artifactId> + <exclusions> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-mapper-asl</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.jaxrs</groupId> + <artifactId>jackson-jaxrs-json-provider</artifactId> + <version>RELEASE</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-bundle</artifactId> + <version>RELEASE</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-json</artifactId> + <version>RELEASE</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + </dependency> + <!-- Spock testing dependencies--> + <dependency> + <groupId>com.github.stefanbirkner</groupId> + <artifactId>system-rules</artifactId> + <version>1.16.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.spockframework</groupId> + <artifactId>spock-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib-nodep</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>compile</goal> + <goal>testCompile</goal> + </goals> + <configuration> + <compilerId>groovy-eclipse-compiler</compilerId> + </configuration> + </execution> + </executions> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + <dependencies> + <dependency> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy-eclipse-compiler</artifactId> + <version>2.9.2-01</version> + </dependency> + <dependency> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy-eclipse-batch</artifactId> + <version>2.4.3-01</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.5</version> + <executions> + <execution> + <id>add-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>src/main/groovy</source> + </sources> + </configuration> + </execution> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src/test/groovy</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes combine.children="append"> + <exclude>src/test/resources/filemanager/myid</exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </build> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/AbstractAdminTool.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/AbstractAdminTool.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/AbstractAdminTool.groovy new file mode 100644 index 0000000..aed3027 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/AbstractAdminTool.groovy @@ -0,0 +1,110 @@ +/* + * 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.admin + +import org.apache.nifi.toolkit.admin.util.AdminUtil +import org.apache.commons.cli.HelpFormatter +import org.apache.commons.cli.Options +import org.apache.commons.lang3.SystemUtils +import org.apache.nifi.toolkit.admin.util.Version +import org.apache.nifi.util.StringUtils +import org.slf4j.Logger +import java.nio.file.Path +import java.nio.file.Paths + +public abstract class AbstractAdminTool { + + protected static final String JAVA_HOME = "JAVA_HOME" + protected static final String NIFI_TOOLKIT_HOME = "NIFI_TOOLKIT_HOME" + protected static final String SEP = System.lineSeparator() + protected Options options + protected String header + protected String footer + protected Boolean isVerbose + protected Logger logger + + protected void setup(){ + options = getOptions() + footer = buildFooter() + logger = getLogger() + } + + protected String buildHeader(final String description ) { + "${SEP}${description}${SEP * 2}" + } + + protected String buildFooter() { + "${SEP}Java home: ${System.getenv(JAVA_HOME)}${SEP}NiFi Toolkit home: ${System.getenv(NIFI_TOOLKIT_HOME)}" + } + + public void printUsage(final String errorMessage) { + if (errorMessage) { + System.out.println(errorMessage) + System.out.println() + } + final HelpFormatter helpFormatter = new HelpFormatter() + helpFormatter.setWidth(160) + helpFormatter.printHelp(this.class.getCanonicalName(), this.header, options, footer, true) + } + + protected abstract Options getOptions() + + protected abstract Logger getLogger() + + Properties getBootstrapConf(Path bootstrapConfFileName) { + Properties bootstrapProperties = new Properties() + File bootstrapConf = bootstrapConfFileName.toFile() + bootstrapProperties.load(new FileInputStream(bootstrapConf)) + return bootstrapProperties + } + + String getRelativeDirectory(String directory, String rootDirectory) { + if (directory.startsWith("./")) { + final String directoryUpdated = SystemUtils.IS_OS_WINDOWS ? File.separator + directory.substring(2,directory.length()) : directory.substring(1,directory.length()) + rootDirectory + directoryUpdated + } else { + directory + } + } + + Boolean supportedNiFiMinimumVersion(final String nifiConfDirName, final String nifiLibDirName, final String supportedMinimumVersion){ + final File nifiConfDir = new File(nifiConfDirName) + final File nifiLibDir = new File (nifiLibDirName) + final String versionStr = AdminUtil.getNiFiVersion(nifiConfDir,nifiLibDir) + + if(!StringUtils.isEmpty(versionStr)){ + Version version = new Version(versionStr,".") + Version minVersion = new Version(supportedMinimumVersion,".") + Version.VERSION_COMPARATOR.compare(version,minVersion) >= 0 + }else{ + return false + } + + } + + Boolean supportedNiFiMinimumVersion(final String nifiCurrentDirName, final String supportedMinimumVersion){ + final String bootstrapConfFileName = Paths.get(nifiCurrentDirName,"conf","bootstrap.conf").toString() + final File bootstrapConf = new File(bootstrapConfFileName) + final Properties bootstrapProperties = getBootstrapConf(Paths.get(bootstrapConfFileName)) + final String parentPathName = bootstrapConf.getCanonicalFile().getParentFile().getParentFile().getCanonicalPath() + final String nifiConfDir = getRelativeDirectory(bootstrapProperties.getProperty("conf.dir"),parentPathName) + final String nifiLibDir = getRelativeDirectory(bootstrapProperties.getProperty("lib.dir"),parentPathName) + return supportedNiFiMinimumVersion(nifiConfDir,nifiLibDir,supportedMinimumVersion) + } + + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/ClientFactory.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/ClientFactory.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/ClientFactory.groovy new file mode 100644 index 0000000..960ac6a --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/ClientFactory.groovy @@ -0,0 +1,27 @@ +/* + * 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.admin.client + +import com.sun.jersey.api.client.Client +import org.apache.nifi.util.NiFiProperties + +interface ClientFactory { + + Client getClient(NiFiProperties niFiProperties, String nifiInstallDir) throws Exception + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactory.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactory.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactory.groovy new file mode 100644 index 0000000..5c0333a --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientFactory.groovy @@ -0,0 +1,172 @@ +/* + * 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.admin.client + +import com.sun.jersey.api.client.Client +import com.sun.jersey.api.client.config.ClientConfig +import com.sun.jersey.api.client.config.DefaultClientConfig +import com.sun.jersey.client.urlconnection.HTTPSProperties +import org.apache.commons.lang3.StringUtils +import org.apache.nifi.security.util.CertificateUtils +import org.apache.nifi.util.NiFiProperties +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import javax.naming.ldap.LdapName +import javax.naming.ldap.Rdn +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLPeerUnverifiedException +import javax.net.ssl.SSLSession +import javax.net.ssl.TrustManagerFactory +import java.security.KeyManagementException +import java.security.KeyStore +import java.security.KeyStoreException +import java.security.NoSuchAlgorithmException +import java.security.SecureRandom +import java.security.UnrecoverableKeyException +import java.security.cert.Certificate +import java.security.cert.CertificateException +import java.security.cert.CertificateParsingException +import java.security.cert.X509Certificate + +class NiFiClientFactory implements ClientFactory{ + + private static final Logger logger = LoggerFactory.getLogger(NiFiClientFactory.class) + static enum NiFiAuthType{ NONE, SSL } + + public Client getClient(NiFiProperties niFiProperties, String nifiInstallDir) throws Exception { + + final String authTypeStr = StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_HOST)) && StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_PORT)) ? NiFiAuthType.NONE : NiFiAuthType.SSL; + final NiFiAuthType authType = NiFiAuthType.valueOf(authTypeStr); + + SSLContext sslContext = null; + + if (NiFiAuthType.SSL.equals(authType)) { + String keystore = niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE); + final String keystoreType = niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE); + final String keystorePassword = niFiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD); + String truststore = niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE); + final String truststoreType = niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE); + final String truststorePassword = niFiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD); + + if(keystore.startsWith("./")){ + keystore = keystore.replace("./",nifiInstallDir+"/") + } + if(truststore.startsWith("./")){ + truststore = truststore.replace("./",nifiInstallDir+"/") + } + + sslContext = createSslContext( + keystore.trim(), + keystorePassword.trim().toCharArray(), + keystoreType.trim(), + truststore.trim(), + truststorePassword.trim().toCharArray(), + truststoreType.trim(), + "TLS"); + } + + final ClientConfig config = new DefaultClientConfig(); + + if (sslContext != null) { + config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,new HTTPSProperties(new NiFiHostnameVerifier(), sslContext)) + } + + return Client.create(config) + + } + + + static SSLContext createSslContext( + final String keystore, final char[] keystorePasswd, final String keystoreType, + final String truststore, final char[] truststorePasswd, final String truststoreType, + final String protocol) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, + UnrecoverableKeyException, KeyManagementException { + + // prepare the keystore + final KeyStore keyStore = KeyStore.getInstance(keystoreType); + final InputStream keyStoreStream = new FileInputStream(keystore) + keyStore.load(keyStoreStream, keystorePasswd); + + + final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, keystorePasswd); + + // prepare the truststore + final KeyStore trustStore = KeyStore.getInstance(truststoreType); + final InputStream trustStoreStream = new FileInputStream(truststore) + trustStore.load(trustStoreStream, truststorePasswd); + + final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + + // initialize the ssl context + final SSLContext sslContext = SSLContext.getInstance(protocol); + sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); + return sslContext; + } + + static class NiFiHostnameVerifier implements HostnameVerifier { + + @Override + public boolean verify(final String hostname, final SSLSession ssls) { + + if (ssls.getPeerCertificates() != null && ssls.getPeerCertificates().length > 0) { + + try { + final Certificate peerCertificate = ssls.getPeerCertificates()[0] + final X509Certificate x509Cert = CertificateUtils.convertAbstractX509Certificate(peerCertificate) + final String dn = x509Cert.getSubjectDN().getName().trim() + + final LdapName ln = new LdapName(dn) + final boolean match = ln.getRdns().any { Rdn rdn -> rdn.getType().equalsIgnoreCase("CN") && rdn.getValue().toString().equalsIgnoreCase(hostname)} + return match || getSubjectAlternativeNames(x509Cert).any { String san -> san.equalsIgnoreCase(hostname) } + + } catch (final SSLPeerUnverifiedException | CertificateParsingException ex ) { + logger.warn("Hostname Verification encountered exception verifying hostname due to: " + ex, ex); + } + + }else{ + logger.warn("Peer certificates not found on ssl session "); + } + + return false + } + + private List<String> getSubjectAlternativeNames(final X509Certificate certificate) throws CertificateParsingException { + final Collection<List<?>> altNames = certificate.getSubjectAlternativeNames() + + if (altNames == null) { + return new ArrayList<>() + } + + final List<String> result = new ArrayList<>() + for (final List<?> generalName : altNames) { + final Object value = generalName.get(1) + if (value instanceof String) { + result.add(((String) value).toLowerCase()) + } + } + + return result + } + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientUtil.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientUtil.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientUtil.groovy new file mode 100644 index 0000000..d4e5ff6 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/client/NiFiClientUtil.groovy @@ -0,0 +1,144 @@ +/* + * 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.admin.client + +import com.fasterxml.jackson.databind.ObjectMapper +import com.google.common.collect.Lists +import com.sun.jersey.api.client.Client +import com.sun.jersey.api.client.ClientResponse +import com.sun.jersey.api.client.WebResource +import org.apache.nifi.util.NiFiProperties +import org.apache.nifi.util.StringUtils +import org.apache.nifi.web.api.dto.NodeDTO +import org.apache.nifi.web.api.dto.util.DateTimeAdapter +import org.apache.nifi.web.api.entity.ClusterEntity +import org.apache.nifi.web.api.entity.NodeEntity +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import java.text.SimpleDateFormat + +public class NiFiClientUtil { + + private static final Logger logger = LoggerFactory.getLogger(NiFiClientUtil.class) + private final static String GET_CLUSTER_ENDPOINT ="/nifi-api/controller/cluster" + + public static Boolean isCluster(final NiFiProperties niFiProperties){ + String clusterNode = niFiProperties.getProperty(NiFiProperties.CLUSTER_IS_NODE) + return Boolean.valueOf(clusterNode) + } + + public static String getUrl(NiFiProperties niFiProperties, String endpoint){ + + final StringBuilder urlBuilder = new StringBuilder(); + + if(!StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_PORT))){ + urlBuilder.append("https://") + urlBuilder.append(StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_HOST)) ? "localhost": niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_HOST)) + urlBuilder.append(":") + urlBuilder.append(StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_PORT)) ? "8081" : niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_PORT)) + }else{ + urlBuilder.append("http://") + urlBuilder.append(StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTP_HOST)) ? "localhost": niFiProperties.getProperty(NiFiProperties.WEB_HTTP_HOST)) + urlBuilder.append(":") + urlBuilder.append(StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_PORT)) ? "8080": niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_PORT)) + } + + if(!StringUtils.isEmpty(endpoint)) { + urlBuilder.append(endpoint) + } + + urlBuilder.toString() + } + + public static String getUrl(NiFiProperties niFiProperties, NodeDTO nodeDTO, String endpoint){ + + final StringBuilder urlBuilder = new StringBuilder(); + if(!StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_PORT))){ + urlBuilder.append("https://") + + }else{ + urlBuilder.append("http://") + } + urlBuilder.append(nodeDTO.address) + urlBuilder.append(":") + urlBuilder.append(nodeDTO.apiPort) + + if(!StringUtils.isEmpty(endpoint)) { + urlBuilder.append(endpoint) + } + + urlBuilder.toString() + } + + public static ClusterEntity getCluster(final Client client, NiFiProperties niFiProperties, List<String> activeUrls){ + + if(activeUrls.isEmpty()){ + final String url = getUrl(niFiProperties,null) + activeUrls.add(url) + } + + for(String activeUrl: activeUrls) { + + try { + + String url = activeUrl + GET_CLUSTER_ENDPOINT + final WebResource webResource = client.resource(url) + final ClientResponse response = webResource.type("application/json").get(ClientResponse.class) + + Integer status = response.getStatus() + + if (status != 200) { + if (status == 404) { + logger.warn("This node is not attached to a cluster. Please connect to a node that is attached to the cluster for information") + } else { + logger.warn("Failed with HTTP error code: {}, message: {}", status, response.getStatusInfo().getReasonPhrase()) + } + } else if (status == 200) { + return response.getEntity(ClusterEntity.class) + } + + }catch(Exception ex){ + logger.warn("Exception occurred during connection attempt: {}",ex.localizedMessage) + } + + } + + throw new RuntimeException("Unable to obtain cluster information") + + } + + public static List<String> getActiveClusterUrls(final Client client, NiFiProperties niFiProperties){ + + final ClusterEntity clusterEntity = getCluster(client, niFiProperties, Lists.newArrayList()) + final List<NodeDTO> activeNodes = clusterEntity.cluster.nodes.findAll{ it.status == "CONNECTED" } + final List<String> activeUrls = Lists.newArrayList() + + activeNodes.each { + activeUrls.add(getUrl(niFiProperties,it, null)) + } + activeUrls + } + + public static String convertToJson(NodeDTO nodeDTO){ + ObjectMapper om = new ObjectMapper() + om.setDateFormat(new SimpleDateFormat(DateTimeAdapter.DEFAULT_DATE_TIME_FORMAT)); + NodeEntity ne = new NodeEntity() + ne.setNode(nodeDTO) + return om.writeValueAsString(ne) + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy new file mode 100644 index 0000000..3a1c4df --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/nodemanager/NodeManagerTool.groovy @@ -0,0 +1,289 @@ +/* + * 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.admin.nodemanager + +import com.sun.jersey.api.client.Client +import com.sun.jersey.api.client.ClientResponse +import com.sun.jersey.api.client.WebResource +import org.apache.nifi.toolkit.admin.AbstractAdminTool +import org.apache.nifi.toolkit.admin.client.NiFiClientUtil +import org.apache.commons.cli.CommandLine +import org.apache.commons.cli.DefaultParser +import org.apache.commons.cli.Option +import org.apache.commons.cli.Options +import org.apache.commons.cli.ParseException +import org.apache.nifi.properties.NiFiPropertiesLoader +import org.apache.nifi.toolkit.admin.client.ClientFactory +import org.apache.nifi.toolkit.admin.client.NiFiClientFactory +import org.apache.nifi.util.NiFiProperties +import org.apache.nifi.util.StringUtils +import org.apache.nifi.web.api.dto.NodeDTO +import org.apache.nifi.web.api.entity.ClusterEntity +import org.apache.nifi.web.api.entity.NodeEntity +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import java.nio.file.Paths + +public class NodeManagerTool extends AbstractAdminTool { + + private static final String DEFAULT_DESCRIPTION = "This tool is used to manage nodes within a cluster. Supported functionality will remove node from cluster. " + private static final String HELP_ARG = "help" + private static final String VERBOSE_ARG = "verbose" + private static final String BOOTSTRAP_CONF = "bootstrapConf" + private static final String NIFI_INSTALL_DIR = "nifiInstallDir" + private static final String CLUSTER_URLS = "clusterUrls" + private static final String REMOVE = "remove" + private static final String DISCONNECT = "disconnect" + private static final String CONNECT = "connect" + private static final String OPERATION = "operation" + private final static String NODE_ENDPOINT = "/nifi-api/controller/cluster/nodes" + private final static String SUPPORTED_MINIMUM_VERSION = "1.0.0" + static enum STATUS {DISCONNECTING,CONNECTING,CONNECTED} + + NodeManagerTool() { + header = buildHeader(DEFAULT_DESCRIPTION) + setup() + } + + NodeManagerTool(final String description){ + this.header = buildHeader(description) + setup() + } + + @Override + protected Logger getLogger() { + LoggerFactory.getLogger(NodeManagerTool.class) + } + + protected Options getOptions(){ + final Options options = new Options() + options.addOption(Option.builder("h").longOpt(HELP_ARG).desc("Print help info").build()) + options.addOption(Option.builder("v").longOpt(VERBOSE_ARG).desc("Set mode to verbose (default is false)").build()) + options.addOption(Option.builder("b").longOpt(BOOTSTRAP_CONF).hasArg().desc("Existing Bootstrap Configuration file").build()) + options.addOption(Option.builder("d").longOpt(NIFI_INSTALL_DIR).hasArg().desc("NiFi Installation Directory").build()) + options.addOption(Option.builder("o").longOpt(OPERATION).hasArg().desc("Operation to connect, disconnect or remove node from cluster").build()) + options.addOption(Option.builder("u").longOpt(CLUSTER_URLS).hasArg().desc("List of active urls for the cluster").build()) + options + } + + NodeDTO getCurrentNode(ClusterEntity clusterEntity, NiFiProperties niFiProperties){ + final List<NodeDTO> nodeDTOs = clusterEntity.cluster.nodes + final String nodeHost = StringUtils.isEmpty(niFiProperties.getProperty(NiFiProperties.CLUSTER_NODE_ADDRESS)) ? + "localhost":niFiProperties.getProperty(NiFiProperties.CLUSTER_NODE_ADDRESS) + return nodeDTOs.find{ it.address == nodeHost } + } + + NodeEntity updateNode(final String url, final Client client, final NodeDTO nodeDTO, final STATUS nodeStatus){ + final WebResource webResource = client.resource(url) + nodeDTO.status = nodeStatus + String json = NiFiClientUtil.convertToJson(nodeDTO) + + if(isVerbose){ + logger.info("Sending node info for update: " + json) + } + + final ClientResponse response = webResource.type("application/json").put(ClientResponse.class,json) + + if(response.getStatus() != 200){ + throw new RuntimeException("Failed with HTTP error code: " + response.getStatus()) + }else{ + response.getEntity(NodeEntity.class) + } + } + + void deleteNode(final String url, final Client client){ + final WebResource webResource = client.resource(url) + + if(isVerbose){ + logger.info("Attempting to delete node" ) + } + + final ClientResponse response = webResource.type("application/json").delete(ClientResponse.class) + + if(response.getStatus() != 200){ + throw new RuntimeException("Failed with HTTP error code: " + response.getStatus()) + } + } + + void disconnectNode(final Client client, NiFiProperties niFiProperties, List<String> activeUrls){ + final ClusterEntity clusterEntity = NiFiClientUtil.getCluster(client, niFiProperties, activeUrls) + NodeDTO currentNode = getCurrentNode(clusterEntity,niFiProperties) + for(String activeUrl: activeUrls) { + try { + final String url = activeUrl + NODE_ENDPOINT + File.separator + currentNode.nodeId + updateNode(url, client, currentNode, STATUS.DISCONNECTING) + return + } catch (Exception ex){ + logger.warn("Could not connect to node on "+activeUrl+". Exception: "+ex.toString()) + } + } + throw new RuntimeException("Could not successfully complete request") + } + + void connectNode(final Client client, NiFiProperties niFiProperties,List<String> activeUrls){ + final ClusterEntity clusterEntity = NiFiClientUtil.getCluster(client, niFiProperties, activeUrls) + NodeDTO currentNode = getCurrentNode(clusterEntity,niFiProperties) + for(String activeUrl: activeUrls) { + try { + final String url = activeUrl + NODE_ENDPOINT + File.separator + currentNode.nodeId + updateNode(url, client, currentNode, STATUS.CONNECTING) + return + } catch (Exception ex){ + logger.warn("Could not connect to node on "+activeUrl+". Exception: "+ex.toString()) + } + } + throw new RuntimeException("Could not successfully complete request") + } + + void removeNode(final Client client, NiFiProperties niFiProperties, List<String> activeUrls){ + + final ClusterEntity clusterEntity = NiFiClientUtil.getCluster(client, niFiProperties, activeUrls) + NodeDTO currentNode = getCurrentNode(clusterEntity,niFiProperties) + + if(currentNode != null) { + + for (String activeUrl : activeUrls) { + + try { + + final String url = activeUrl + NODE_ENDPOINT + File.separator + currentNode.nodeId + + if(isVerbose){ + logger.info("Attempting to connect to cluster with url:" + url) + } + + if(currentNode.status == "CONNECTED") { + currentNode = updateNode(url, client, currentNode, STATUS.DISCONNECTING).node + } + + if(currentNode.status == "DISCONNECTED") { + deleteNode(url, client) + } + + if(isVerbose){ + logger.info("Node removed from cluster successfully.") + } + + return + + }catch (Exception ex){ + logger.warn("Could not connect to node on "+activeUrl+". Exception: "+ex.toString()) + } + + } + throw new RuntimeException("Could not successfully complete request") + + }else{ + throw new RuntimeException("Current node could not be found in the cluster") + } + + } + + void parse(final ClientFactory clientFactory, final String[] args) throws ParseException, UnsupportedOperationException, IllegalArgumentException { + + final CommandLine commandLine = new DefaultParser().parse(options,args) + + if (commandLine.hasOption(HELP_ARG)){ + printUsage(null) + }else{ + + if(commandLine.hasOption(BOOTSTRAP_CONF) && commandLine.hasOption(NIFI_INSTALL_DIR) && commandLine.hasOption(OPERATION)) { + + if(commandLine.hasOption(VERBOSE_ARG)){ + this.isVerbose = true; + } + + final String bootstrapConfFileName = commandLine.getOptionValue(BOOTSTRAP_CONF) + final File bootstrapConf = new File(bootstrapConfFileName) + Properties bootstrapProperties = getBootstrapConf(Paths.get(bootstrapConfFileName)) + String nifiConfDir = getRelativeDirectory(bootstrapProperties.getProperty("conf.dir"), bootstrapConf.getCanonicalFile().getParentFile().getParentFile().getCanonicalPath()) + String nifiLibDir = getRelativeDirectory(bootstrapProperties.getProperty("lib.dir"), bootstrapConf.getCanonicalFile().getParentFile().getParentFile().getCanonicalPath()) + String nifiPropertiesFileName = nifiConfDir + File.separator +"nifi.properties" + final String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile(bootstrapConfFileName) + final NiFiProperties niFiProperties = NiFiPropertiesLoader.withKey(key).load(nifiPropertiesFileName) + + final String nifiInstallDir = commandLine.getOptionValue(NIFI_INSTALL_DIR) + + if(supportedNiFiMinimumVersion(nifiConfDir,nifiLibDir,SUPPORTED_MINIMUM_VERSION) && NiFiClientUtil.isCluster(niFiProperties)){ + + final Client client = clientFactory.getClient(niFiProperties,nifiInstallDir) + final String operation = commandLine.getOptionValue(OPERATION) + + if(isVerbose){ + logger.info("Starting {} request",operation) + } + + List<String> activeUrls + + if(commandLine.hasOption(CLUSTER_URLS)){ + final String urlList = commandLine.getOptionValue(CLUSTER_URLS) + activeUrls = urlList.tokenize(',') + }else{ + activeUrls = NiFiClientUtil.getActiveClusterUrls(client,niFiProperties) + } + + if(isVerbose){ + logger.info("Using active urls {} for communication.",activeUrls) + } + + if(operation.toLowerCase().equals(REMOVE)){ + removeNode(client,niFiProperties,activeUrls) + } + else if(operation.toLowerCase().equals(DISCONNECT)){ + disconnectNode(client,niFiProperties,activeUrls) + } + else if(operation.toLowerCase().equals(CONNECT)){ + connectNode(client,niFiProperties,activeUrls) + } + else{ + throw new ParseException("Invalid operation provided: " + operation) + } + + }else{ + throw new UnsupportedOperationException("Node Manager Tool only supports clustered instance of NiFi running versions 1.0.0 or higher.") + } + + }else if(!commandLine.hasOption(BOOTSTRAP_CONF)){ + throw new ParseException("Missing -b option") + }else if(!commandLine.hasOption(NIFI_INSTALL_DIR)){ + throw new ParseException("Missing -d option") + }else{ + throw new ParseException("Missing -o option") + } + } + + } + + public static void main(String[] args) { + final NodeManagerTool tool = new NodeManagerTool() + final ClientFactory clientFactory = new NiFiClientFactory() + + try{ + tool.parse(clientFactory,args) + } catch (ParseException | RuntimeException e ) { + tool.printUsage(e.getLocalizedMessage()); + System.exit(1) + } + + System.exit(0) + } + + + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy new file mode 100644 index 0000000..ce87499 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/notify/NotificationTool.groovy @@ -0,0 +1,181 @@ +/* + * 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.admin.notify + +import com.sun.jersey.api.client.Client +import com.sun.jersey.api.client.ClientResponse +import com.sun.jersey.api.client.WebResource +import org.apache.commons.lang3.StringUtils +import org.apache.nifi.toolkit.admin.client.NiFiClientUtil +import org.apache.commons.cli.CommandLine +import org.apache.commons.cli.DefaultParser +import org.apache.commons.cli.Option +import org.apache.commons.cli.Options +import org.apache.commons.cli.ParseException +import org.apache.nifi.properties.NiFiPropertiesLoader +import org.apache.nifi.toolkit.admin.AbstractAdminTool +import org.apache.nifi.toolkit.admin.client.ClientFactory +import org.apache.nifi.toolkit.admin.client.NiFiClientFactory +import org.apache.nifi.util.NiFiProperties +import org.apache.nifi.web.api.dto.BulletinDTO +import org.apache.nifi.web.api.entity.BulletinEntity +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import java.nio.file.Paths + +public class NotificationTool extends AbstractAdminTool { + + private static final String DEFAULT_DESCRIPTION = "This tool is used to send notifications (bulletins) to a NiFi cluster. " + private static final String HELP_ARG = "help" + private static final String VERBOSE_ARG = "verbose" + private static final String BOOTSTRAP_CONF = "bootstrapConf" + private static final String NIFI_INSTALL_DIR = "nifiInstallDir" + private static final String NOTIFICATION_MESSAGE = "message" + private static final String NOTIFICATION_LEVEL = "level" + private final static String NOTIFICATION_ENDPOINT ="/nifi-api/controller/bulletin" + private final static String SUPPORTED_MINIMUM_VERSION = "1.2.0" + + NotificationTool() { + header = buildHeader(DEFAULT_DESCRIPTION) + setup() + } + + NotificationTool(final String description){ + header = buildHeader(description) + setup() + } + + @Override + protected Logger getLogger() { + LoggerFactory.getLogger(NotificationTool.class) + } + + protected Options getOptions(){ + final Options options = new Options() + options.addOption(Option.builder("h").longOpt(HELP_ARG).desc("Print help info").build()) + options.addOption(Option.builder("v").longOpt(VERBOSE_ARG).desc("Set mode to verbose (default is false)").build()) + options.addOption(Option.builder("b").longOpt(BOOTSTRAP_CONF).hasArg().desc("Existing Bootstrap Configuration file").build()) + options.addOption(Option.builder("d").longOpt(NIFI_INSTALL_DIR).hasArg().desc("NiFi Installation Directory").build()) + options.addOption(Option.builder("m").longOpt(NOTIFICATION_MESSAGE).hasArg().desc("Notification message for nifi instance or cluster").build()) + options.addOption(Option.builder("l").longOpt(NOTIFICATION_LEVEL).required(false).hasArg().desc("Level for notification bulletin INFO,WARN,ERROR").build()) + options + } + + void notifyCluster(final ClientFactory clientFactory, final String nifiPropertiesFile, final String bootstrapConfFile, final String nifiInstallDir, final String message, final String level){ + + if(isVerbose){ + logger.info("Loading nifi properties for host information") + } + + final String key = NiFiPropertiesLoader.extractKeyFromBootstrapFile(bootstrapConfFile) + final NiFiProperties niFiProperties = NiFiPropertiesLoader.withKey(key).load(nifiPropertiesFile) + final Client client = clientFactory.getClient(niFiProperties,nifiInstallDir) + final String url = NiFiClientUtil.getUrl(niFiProperties,NOTIFICATION_ENDPOINT) + final WebResource webResource = client.resource(url) + + if(isVerbose){ + logger.info("Contacting node at url:" + url) + } + + final BulletinEntity bulletinEntity = new BulletinEntity() + final BulletinDTO bulletinDTO = new BulletinDTO() + bulletinDTO.message = message + bulletinDTO.category = "NOTICE" + bulletinDTO.level = StringUtils.isEmpty(level) ? "INFO" : level + bulletinEntity.bulletin = bulletinDTO + final ClientResponse response = webResource.type("application/json").post(ClientResponse.class, bulletinEntity) + + Integer status = response.getStatus() + + if(status != 200){ + if(status == 404){ + throw new RuntimeException("The notification feature is not supported by each node in the cluster") + }else{ + throw new RuntimeException("Failed with HTTP error code: " + status) + } + } + + } + + void parse(final ClientFactory clientFactory, final String[] args) throws ParseException, UnsupportedOperationException { + + final CommandLine commandLine = new DefaultParser().parse(options,args) + + if (commandLine.hasOption(HELP_ARG)){ + printUsage(null) + }else{ + + if(commandLine.hasOption(BOOTSTRAP_CONF) && commandLine.hasOption(NOTIFICATION_MESSAGE) && commandLine.hasOption(NIFI_INSTALL_DIR)) { + + if(commandLine.hasOption(VERBOSE_ARG)){ + this.isVerbose = true; + } + + final String bootstrapConfFileName = commandLine.getOptionValue(BOOTSTRAP_CONF) + final File bootstrapConf = new File(bootstrapConfFileName) + final Properties bootstrapProperties = getBootstrapConf(Paths.get(bootstrapConfFileName)) + final String parentPathName = bootstrapConf.getCanonicalFile().getParentFile().getParentFile().getCanonicalPath() + final String nifiConfDir = getRelativeDirectory(bootstrapProperties.getProperty("conf.dir"),parentPathName) + final String nifiLibDir = getRelativeDirectory(bootstrapProperties.getProperty("lib.dir"),parentPathName) + final String nifiPropertiesFileName = nifiConfDir + File.separator +"nifi.properties" + final String notificationMessage = commandLine.getOptionValue(NOTIFICATION_MESSAGE) + final String notificationLevel = commandLine.getOptionValue(NOTIFICATION_LEVEL) + final String nifiInstallDir = commandLine.getOptionValue(NIFI_INSTALL_DIR) + + if(supportedNiFiMinimumVersion(nifiConfDir, nifiLibDir, SUPPORTED_MINIMUM_VERSION)){ + if(isVerbose){ + logger.info("Attempting to connect with nifi using properties:", nifiPropertiesFileName) + } + + notifyCluster(clientFactory, nifiPropertiesFileName, bootstrapConfFileName,nifiInstallDir,notificationMessage,notificationLevel) + + if(isVerbose) { + logger.info("Message sent successfully to NiFi.") + } + }else{ + throw new UnsupportedOperationException("Notification Tool only supports NiFi versions 1.2.0 and above") + } + + }else if(!commandLine.hasOption(BOOTSTRAP_CONF)){ + throw new ParseException("Missing -b option") + }else if(!commandLine.hasOption(NIFI_INSTALL_DIR)){ + throw new ParseException("Missing -d option") + }else{ + throw new ParseException("Missing -m option") + } + } + + } + + public static void main(String[] args) { + final NotificationTool tool = new NotificationTool() + final ClientFactory clientFactory = new NiFiClientFactory() + + try{ + tool.parse(clientFactory,args) + } catch (ParseException | UnsupportedOperationException e) { + tool.printUsage(e.message); + System.exit(1) + } + + System.exit(0) + } + + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/AdminUtil.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/AdminUtil.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/AdminUtil.groovy new file mode 100644 index 0000000..9dc0090 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/AdminUtil.groovy @@ -0,0 +1,69 @@ +/* + * 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.admin.util + +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry +import org.apache.commons.compress.archivers.zip.ZipFile +import org.apache.commons.lang3.StringUtils + +class AdminUtil { + + protected static String getNiFiVersionFromNar(final File nifiLibDir){ + + if(nifiLibDir.isDirectory()){ + File[] files = nifiLibDir.listFiles(new FilenameFilter() { + @Override + boolean accept(File dir, String name) { + name.startsWith("nifi-framework-nar") + } + }) + + if(files.length == 1){ + final ZipFile zipFile = new ZipFile(files[0]) + final ZipArchiveEntry archiveEntry = zipFile.getEntry("META-INF/MANIFEST.MF") + final InputStream is = zipFile.getInputStream(archiveEntry) + final Properties manifestProperties = new Properties() + manifestProperties.load(is) + String version = manifestProperties.get("Nar-Version") + zipFile.close() + return StringUtils.isEmpty(version)? null : version + + } + } + + null + } + + protected static String getNiFiVersionFromProperties(final File nifiConfDir) { + final String nifiPropertiesFileName = nifiConfDir.getAbsolutePath() + File.separator +"nifi.properties" + final File nifiPropertiesFile = new File(nifiPropertiesFileName) + final Properties nifiProperties = new Properties() + nifiProperties.load(new FileInputStream(nifiPropertiesFile)) + nifiProperties.getProperty("nifi.version") + } + + public static String getNiFiVersion(final File nifiConfDir, final File nifiLibDir){ + + String nifiVersion = getNiFiVersionFromProperties(nifiConfDir) + if(StringUtils.isEmpty(nifiVersion)){ + nifiVersion = getNiFiVersionFromNar(nifiLibDir) + } + return nifiVersion.replace("-SNAPSHOT","") + + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/c0f0462e/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/Version.groovy ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/Version.groovy b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/Version.groovy new file mode 100644 index 0000000..db5dc04 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-admin/src/main/groovy/org/apache/nifi/toolkit/admin/util/Version.groovy @@ -0,0 +1,82 @@ +/* + * 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.admin.util + +import org.apache.commons.lang3.StringUtils + +class Version { + + private String[] versionNumber + private String delimeter + + Version(String version, String delimeter) { + this.versionNumber = version.tokenize(delimeter) + this.delimeter = delimeter + } + + String[] getVersionNumber() { + return versionNumber + } + + void setVersionNumber(String[] versionNumber) { + this.versionNumber = versionNumber + } + + String getDelimeter() { + return delimeter + } + + void setDelimeter(String delimeter) { + this.delimeter = delimeter + } + + boolean equals(o) { + if (this.is(o)) return true + if (getClass() != o.class) return false + Version version = (Version) o + if (!Arrays.equals(versionNumber, version.versionNumber)) return false + return true + } + + int hashCode() { + return (versionNumber != null ? Arrays.hashCode(versionNumber) : 0) + } + + public final static Comparator<Version> VERSION_COMPARATOR = new Comparator<Version>() { + @Override + int compare(Version o1, Version o2) { + String[] o1V = o1.versionNumber + String[] o2V = o2.versionNumber + + for(int i = 0; i < o1V.length; i++) { + Integer val1 = Integer.parseInt(o1V[i]) + Integer val2 = Integer.parseInt(o2V[i]) + if (val1.compareTo(val2) != 0) { + return val1.compareTo(val2) + } + } + return 0 + } + } + + + @Override + public String toString() { + StringUtils.join(versionNumber,delimeter) + } +}
