@Override
 public Set<String> getSSHFingerprints(Stack stack, String gateway) {
   Set<String> result = new HashSet<>();
   LOGGER.debug("Get SSH fingerprints of gateway instance for stack: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   InstanceMetaData gatewayMetaData =
       stack.getGatewayInstanceGroup().getInstanceMetaData().iterator().next();
   CloudInstance gatewayInstance = metadataConverter.convert(gatewayMetaData);
   GetSSHFingerprintsRequest<GetSSHFingerprintsResult> sSHFingerprintReq =
       new GetSSHFingerprintsRequest<>(cloudContext, cloudCredential, gatewayInstance);
   LOGGER.info("Triggering GetSSHFingerprintsRequest stack event: {}", sSHFingerprintReq);
   eventBus.notify(sSHFingerprintReq.selector(), Event.wrap(sSHFingerprintReq));
   try {
     GetSSHFingerprintsResult res = sSHFingerprintReq.await();
     LOGGER.info("Get SSH fingerprints of gateway instance for stack result: {}", res);
     if (res.getStatus().equals(EventStatus.FAILED)) {
       LOGGER.error("Failed to get SSH fingerprint", res.getErrorDetails());
       throw new OperationException(res.getErrorDetails());
     }
     result.addAll(res.getSshFingerprints());
   } catch (InterruptedException e) {
     LOGGER.error(
         format("Failed to get SSH fingerprints of gateway instance stack: %s", cloudContext), e);
     throw new OperationException(e);
   }
   return result;
 }
 @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 void stopAll(Stack stack) {
   LOGGER.info("Assembling stop request for stack: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   List<CloudInstance> instances = metadataConverter.convert(stack.getInstanceMetaDataAsList());
   List<CloudResource> resources = cloudResourceConverter.convert(stack.getResources());
   StopInstancesRequest<StopInstancesResult> stopRequest =
       new StopInstancesRequest<>(cloudContext, cloudCredential, resources, instances);
   LOGGER.info("Triggering event: {}", stopRequest);
   eventBus.notify(stopRequest.selector(), Event.wrap(stopRequest));
   try {
     StopInstancesResult res = stopRequest.await();
     LOGGER.info("Result: {}", res);
     if (res.isFailed()) {
       Exception exception = res.getException();
       LOGGER.error("Failed to stop the stack", exception);
       throw new OperationException(exception);
     } else {
       for (CloudVmInstanceStatus instanceStatus : res.getResults().getResults()) {
         if (instanceStatus.getStatus().equals(InstanceStatus.FAILED)) {
           throw new OperationException(
               "Failed to stop the following instance: " + instanceStatus.getCloudInstance());
         }
       }
     }
   } catch (InterruptedException e) {
     LOGGER.error("Error while stopping the stack", e);
     throw new OperationException(e);
   }
 }
 @Override
 public PlatformParameters getPlatformParameters(Stack stack) {
   LOGGER.debug("Get platform parameters for: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   PlatformParameterRequest parameterRequest =
       new PlatformParameterRequest(cloudContext, cloudCredential);
   eventBus.notify(parameterRequest.selector(), Event.wrap(parameterRequest));
   try {
     PlatformParameterResult res = parameterRequest.await();
     LOGGER.info("Platform parameter result: {}", res);
     if (res.getStatus().equals(EventStatus.FAILED)) {
       LOGGER.error("Failed to get platform parameters", res.getErrorDetails());
       throw new OperationException(res.getErrorDetails());
     }
     return res.getPlatformParameters();
   } catch (InterruptedException e) {
     LOGGER.error("Error while getting platform parameters: " + cloudContext, e);
     throw new OperationException(e);
   }
 }
 @Override
 public String checkAndGetPlatformVariant(Stack stack) {
   LOGGER.debug("Get platform variant for: {}", stack);
   CloudContext cloudContext = new CloudContext(stack);
   CloudCredential cloudCredential = credentialConverter.convert(stack.getCredential());
   CheckPlatformVariantRequest checkPlatformVariantRequest =
       new CheckPlatformVariantRequest(cloudContext, cloudCredential);
   eventBus.notify(
       checkPlatformVariantRequest.selector(), Event.wrap(checkPlatformVariantRequest));
   try {
     CheckPlatformVariantResult res = checkPlatformVariantRequest.await();
     LOGGER.info("Platform variant result: {}", res);
     if (res.getStatus().equals(EventStatus.FAILED)) {
       LOGGER.error("Failed to get platform variant", res.getErrorDetails());
       throw new OperationException(res.getErrorDetails());
     }
     return res.getDefaultPlatformVariant();
   } catch (InterruptedException e) {
     LOGGER.error("Error while getting the platform variant: " + cloudContext, 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);
   }
 }