@Override
 public Set<Resource> buildStack(
     Stack stack,
     String gateWayUserData,
     String coreUserData,
     Map<String, Object> setupProperties) {
   LOGGER.info("Assembling launch request for stack: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   CloudStack cloudStack = cloudStackConverter.convert(stack, coreUserData, gateWayUserData);
   instanceMetadataService.saveInstanceRequests(stack, cloudStack.getGroups());
   LaunchStackRequest launchRequest =
       new LaunchStackRequest(cloudContext, cloudCredential, cloudStack);
   LOGGER.info("Triggering event: {}", launchRequest);
   eventBus.notify(launchRequest.selector(), Event.wrap(launchRequest));
   try {
     LaunchStackResult res = launchRequest.await();
     LOGGER.info("Result: {}", res);
     validateResourceResults(cloudContext, res);
     List<CloudResourceStatus> results = res.getResults();
     updateNodeCount(stack.getId(), cloudStack.getGroups(), results, true);
     return transformResults(results, stack);
   } catch (InterruptedException e) {
     LOGGER.error("Error while launching stack", e);
     throw new OperationException(
         "Unexpected exception occurred during build stack: " + e.getMessage());
   }
 }
 @Override
 public void updateAllowedSubnets(Stack stack, String gateWayUserData, String coreUserData) {
   LOGGER.debug("Assembling update subnet event for: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   CloudStack cloudStack = cloudStackConverter.convert(stack, coreUserData, gateWayUserData);
   List<CloudResource> resources = cloudResourceConverter.convert(stack.getResources());
   UpdateStackRequest<UpdateStackResult> updateRequest =
       new UpdateStackRequest<>(cloudContext, cloudCredential, cloudStack, resources);
   eventBus.notify(updateRequest.selector(), Event.wrap(updateRequest));
   try {
     UpdateStackResult res = updateRequest.await();
     LOGGER.info("Update stack result: {}", res);
     if (res.isFailed()) {
       if (res.getException() != null) {
         LOGGER.error("Failed to update the stack", res.getException());
         throw new OperationException(res.getException());
       }
       throw new OperationException(
           format(
               "Failed to update the stack: %s due to: %s", cloudContext, res.getStatusReason()));
     }
   } catch (InterruptedException e) {
     LOGGER.error("Error while updating the stack: " + cloudContext, e);
     throw new OperationException(e);
   }
 }
 @Override
 public void deleteStack(Stack stack, Credential credential) {
   LOGGER.debug("Assembling terminate stack event for stack: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   List<CloudResource> resources = cloudResourceConverter.convert(stack.getResources());
   CloudStack cloudStack = cloudStackConverter.convert(stack, "", "");
   TerminateStackRequest<TerminateStackResult> terminateRequest =
       new TerminateStackRequest<>(cloudContext, cloudStack, cloudCredential, resources);
   LOGGER.info("Triggering terminate stack event: {}", terminateRequest);
   eventBus.notify(terminateRequest.selector(), Event.wrap(terminateRequest));
   try {
     TerminateStackResult res = terminateRequest.await();
     LOGGER.info("Terminate stack result: {}", res);
     if (res.getStatus().equals(EventStatus.FAILED)) {
       if (res.getErrorDetails() != null) {
         LOGGER.error("Failed to terminate the stack", res.getErrorDetails());
         throw new OperationException(res.getErrorDetails());
       }
       throw new OperationException(
           format(
               "Failed to terminate the stack: %s due to %s",
               cloudContext, res.getStatusReason()));
     }
   } catch (InterruptedException e) {
     LOGGER.error("Error while terminating the stack", e);
     throw new OperationException(e);
   }
 }
 @Override
 public Set<String> removeInstances(
     Stack stack,
     String gateWayUserData,
     String coreUserData,
     Set<String> instanceIds,
     String instanceGroup) {
   LOGGER.debug("Assembling downscale stack event for stack: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   List<CloudResource> resources = cloudResourceConverter.convert(stack.getResources());
   List<CloudInstance> instances = new ArrayList<>();
   InstanceGroup group = stack.getInstanceGroupByInstanceGroupName(instanceGroup);
   for (InstanceMetaData metaData : group.getAllInstanceMetaData()) {
     if (instanceIds.contains(metaData.getInstanceId())) {
       CloudInstance cloudInstance = metadataConverter.convert(metaData);
       instances.add(cloudInstance);
     }
   }
   CloudStack cloudStack =
       cloudStackConverter.convert(stack, coreUserData, gateWayUserData, instanceIds);
   DownscaleStackRequest<DownscaleStackResult> downscaleRequest =
       new DownscaleStackRequest<>(
           cloudContext, cloudCredential, cloudStack, resources, instances);
   LOGGER.info("Triggering downscale stack event: {}", downscaleRequest);
   eventBus.notify(downscaleRequest.selector(), Event.wrap(downscaleRequest));
   try {
     DownscaleStackResult res = downscaleRequest.await();
     LOGGER.info("Downscale stack result: {}", res);
     if (res.getStatus().equals(EventStatus.FAILED)) {
       LOGGER.error("Failed to downscale the stack", res.getErrorDetails());
       throw new OperationException(res.getErrorDetails());
     }
     return instanceIds;
   } catch (InterruptedException e) {
     LOGGER.error("Error while downscaling the stack", e);
     throw new OperationException(e);
   }
 }
 @Override
 public Set<Resource> addInstances(
     Stack stack,
     String gateWayUserData,
     String coreUserData,
     Integer adjustment,
     String instanceGroup) {
   LOGGER.debug("Assembling upscale stack event for stack: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   InstanceGroup group = stack.getInstanceGroupByInstanceGroupName(instanceGroup);
   group.setNodeCount(group.getNodeCount() + adjustment);
   CloudStack cloudStack = cloudStackConverter.convert(stack, coreUserData, gateWayUserData);
   instanceMetadataService.saveInstanceRequests(stack, cloudStack.getGroups());
   List<CloudResource> resources = cloudResourceConverter.convert(stack.getResources());
   UpscaleStackRequest<UpscaleStackResult> upscaleRequest =
       new UpscaleStackRequest<>(cloudContext, cloudCredential, cloudStack, resources);
   LOGGER.info("Triggering upscale stack event: {}", upscaleRequest);
   eventBus.notify(upscaleRequest.selector(), Event.wrap(upscaleRequest));
   try {
     UpscaleStackResult res = upscaleRequest.await();
     LOGGER.info("Upscale stack result: {}", res);
     List<CloudResourceStatus> results = res.getResults();
     updateNodeCount(stack.getId(), cloudStack.getGroups(), results, false);
     validateResourceResults(cloudContext, res);
     Set<Resource> resourceSet = transformResults(results, stack);
     if (resourceSet.isEmpty()) {
       throw new OperationException(
           "Failed to upscale the cluster since all create request failed: "
               + results.get(0).getStatusReason());
     }
     return resourceSet;
   } catch (InterruptedException e) {
     LOGGER.error("Error while upscaling the stack", e);
     throw new OperationException(e);
   }
 }