in src/main/java/com/awslabs/aws/greengrass/provisioner/implementations/helpers/BasicDeploymentHelper.java [450:1097]
public void execute(DeploymentArguments deploymentArguments) {
// Make the directories for build, if necessary
ioHelper.createDirectoryIfNecessary(ggConstants.getBuildDirectory());
// Get the Greengrass group name
GreengrassGroupName greengrassGroupName = ImmutableGreengrassGroupName.builder().groupName(deploymentArguments.groupName).build();
// Get the core thing name
ImmutableThingName coreThingName = ggVariables.getCoreThingName(greengrassGroupName);
///////////////////////////////////////
// Load the deployment configuration //
///////////////////////////////////////
DeploymentConf deploymentConf;
if (isEmptyDeployment(deploymentArguments)) {
deploymentConf = getEmptyDeploymentConf(deploymentArguments, greengrassGroupName);
} else {
deploymentConf = Try.of(() -> getDeploymentConf(coreThingName, deploymentArguments.deploymentConfigFilename, greengrassGroupName)).get();
}
///////////////////////////////////////////////////
// Create an AWS Greengrass Group and get its ID //
///////////////////////////////////////////////////
if (v2GreengrassHelper.groupExists(greengrassGroupName) && (deploymentArguments.ec2LinuxVersion != null)) {
throw new RuntimeException(String.join("", "Group [", deploymentArguments.groupName, "] already exists, cannot launch another EC2 instance for this group. You can update the group configuration by not specifying the EC2 launch option."));
}
log.info("Creating a Greengrass group, if necessary");
String groupId = greengrassHelper.createGroupIfNecessary(greengrassGroupName);
GreengrassGroupId greengrassGroupId = ImmutableGreengrassGroupId.builder().groupId(groupId).build();
///////////////////////
// Create core thing //
///////////////////////
log.info("Creating core thing");
ThingArn coreThingArn = v2IotHelper.createThing(coreThingName);
///////////////////////////////////////////////////
// Determine the default function isolation mode //
///////////////////////////////////////////////////
FunctionIsolationMode defaultFunctionIsolationMode;
if (isEmptyDeployment(deploymentArguments)) {
// If we're doing an empty deployment default to no container mode
defaultFunctionIsolationMode = FunctionIsolationMode.NO_CONTAINER;
} else if ((deploymentArguments.dockerLaunch) || (deploymentArguments.buildContainer)) {
// If we're doing a Docker launch we always use no container
log.warn("Setting default function isolation mode to no container because we're doing a Docker launch");
defaultFunctionIsolationMode = FunctionIsolationMode.NO_CONTAINER;
} else {
// If we're not doing a Docker launch use the default values in the configuration file
defaultFunctionIsolationMode = ggVariables.getDefaultFunctionIsolationMode();
}
////////////////////////////////////////////////////////////
// Build the default environment for all Lambda functions //
////////////////////////////////////////////////////////////
Map<String, String> defaultEnvironment = environmentHelper.getDefaultEnvironment(greengrassGroupId, coreThingName, coreThingArn, greengrassGroupName);
// Get a config object with the default environment values (eg. "${AWS_IOT_THING_NAME}" used in the function and connector configuration)
Config defaultConfig = typeSafeConfigHelper.addDefaultValues(defaultEnvironment, Optional.empty());
//////////////////////////////////////////////////////////////////////
// Find enabled functions and create function conf objects for them //
//////////////////////////////////////////////////////////////////////
List<FunctionConf> functionConfs = getFunctionConfs(deploymentConf, defaultFunctionIsolationMode, defaultConfig);
///////////////////////////
// Create the connectors //
///////////////////////////
List<ConnectorConf> connectorConfs = connectorHelper.getConnectorConfObjects(defaultConfig, deploymentConf.getConnectors());
log.info("Creating connector definition");
Optional<String> optionalConnectionDefinitionVersionArn = greengrassHelper.createConnectorDefinitionVersion(connectorConfs);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Merge any additional permissions that the functions need into the core and service role configurations //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
List<Map> additionalCoreRoleIamPolicies = getRoleIamPolicies(functionConfs.stream()
.map(FunctionConf::getCoreRoleIamPolicy));
List<String> additionalCoreRoleIamManagedPolicies = getRoleIamManagedPolicies(functionConfs.stream()
.map(FunctionConf::getCoreRoleIamManagedPolicies));
RoleConf mergedCoreRoleConf = mergeRoleConf(deploymentConf.getCoreRoleConf(), additionalCoreRoleIamPolicies, additionalCoreRoleIamManagedPolicies);
// Use the merged core role conf
deploymentConf = ImmutableDeploymentConf.builder().from(deploymentConf)
.coreRoleConf(mergedCoreRoleConf)
.build();
if (deploymentConf.getServiceRoleConf().isPresent()) {
List<Map> additionalServiceRoleIamPolicies = getRoleIamPolicies(functionConfs.stream()
.map(FunctionConf::getServiceRoleIamPolicy));
List<String> additionalServiceRoleIamManagedPolicies = getRoleIamManagedPolicies(functionConfs.stream()
.map(FunctionConf::getServiceRoleIamManagedPolicies));
RoleConf mergedServiceRoleConf = mergeRoleConf(deploymentConf.getServiceRoleConf().get(), additionalServiceRoleIamPolicies, additionalServiceRoleIamManagedPolicies);
// Use the merged service role conf
deploymentConf = ImmutableDeploymentConf.builder().from(deploymentConf)
.serviceRoleConf(mergedServiceRoleConf)
.build();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Merge any additional permissions that the connectors need into the core and service role configurations //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
additionalCoreRoleIamPolicies = getRoleIamPolicies(connectorConfs.stream()
.map(ConnectorConf::getCoreRoleIamPolicy));
additionalCoreRoleIamManagedPolicies = getRoleIamManagedPolicies(connectorConfs.stream()
.map(ConnectorConf::getCoreRoleIamManagedPolicies));
mergedCoreRoleConf = mergeRoleConf(deploymentConf.getCoreRoleConf(), additionalCoreRoleIamPolicies, additionalCoreRoleIamManagedPolicies);
// Use the merged core role conf
deploymentConf = ImmutableDeploymentConf.builder().from(deploymentConf)
.coreRoleConf(mergedCoreRoleConf)
.build();
if (deploymentConf.getServiceRoleConf().isPresent()) {
List<Map> additionalServiceRoleIamPolicies = getRoleIamPolicies(connectorConfs.stream()
.map(ConnectorConf::getServiceRoleIamPolicy));
List<String> additionalServiceRoleIamManagedPolicies = getRoleIamManagedPolicies(connectorConfs.stream()
.map(ConnectorConf::getServiceRoleIamManagedPolicies));
RoleConf mergedServiceRoleConf = mergeRoleConf(deploymentConf.getServiceRoleConf().get(), additionalServiceRoleIamPolicies, additionalServiceRoleIamManagedPolicies);
// Use the merged service role conf
deploymentConf = ImmutableDeploymentConf.builder().from(deploymentConf)
.serviceRoleConf(mergedServiceRoleConf)
.build();
}
//////////////////////////////////////////////////////////
// Create the service role and role alias, if necessary //
//////////////////////////////////////////////////////////
Optional<Role> optionalGreengrassServiceRole;
Optional<CreateRoleAliasResponse> optionalCreateRoleAliasResponse;
// Create the role for the core, if necessary
Role coreRole;
if (deploymentArguments.coreRoleName != null) {
RoleName coreRoleName = ImmutableRoleName.builder().name(deploymentArguments.coreRoleName).build();
Optional<Role> optionalCoreRole = iamHelper.getRole(coreRoleName);
if (!optionalCoreRole.isPresent()) {
throw new RuntimeException(String.join("", "Greengrass core role is not present or GetRole failed due to insufficient permissions on [", deploymentArguments.coreRoleName, "]"));
}
coreRole = optionalCoreRole.get();
} else {
coreRole = createCoreRole(deploymentConf.getCoreRoleConf());
}
if (!deploymentArguments.serviceRoleExists) {
// If the service role does not exist we should create it
Role serviceRole = createServiceRole(deploymentConf.getServiceRoleConf().get());
optionalGreengrassServiceRole = Optional.of(serviceRole);
} else {
// The service role exists already, do not try to create or modify it
optionalGreengrassServiceRole = Optional.empty();
}
// Create the role alias so we can use IoT as a credentials provider with certificate based authentication
if (!deploymentConf.getCoreRoleConf().getAlias().isPresent()) {
throw new RuntimeException(String.join("", "No role alias specified for the Greengrass core role [", deploymentConf.getCoreRoleConf().getName(), "]"));
}
ImmutableRoleAlias coreRoleAlias = ImmutableRoleAlias.builder().name(deploymentConf.getCoreRoleConf().getAlias().get()).build();
log.info(String.join("", "Creating core role alias [", coreRoleAlias.getName(), "]"));
optionalCreateRoleAliasResponse = Optional.of(v2IotHelper.forceCreateRoleAlias(coreRole, coreRoleAlias));
//////////////////////////////////
// Create or reuse certificates //
//////////////////////////////////
Optional<GroupVersion> optionalGroupVersion = v2GreengrassHelper.getLatestGroupVersionByNameOrId(groupId);
Optional<KeysAndCertificate> optionalCoreKeysAndCertificate = Optional.empty();
Optional<CertificateArn> optionalCoreCertificateArn = Optional.empty();
if (deploymentArguments.certificateArn != null) {
// Use the certificate ARN supplied by the user, new or existing group
log.info(String.join("", "Using user supplied certificate ARN for core certificate [", deploymentArguments.certificateArn, "]"));
optionalCoreCertificateArn = Optional.of(ImmutableCertificateArn.builder().arn(deploymentArguments.certificateArn).build());
} else if (deploymentArguments.csr != null) {
// Sign the CSR supplied by the user, new or existing group
log.info("Using user supplied CSR for core certificate");
optionalCoreCertificateArn = Optional.of(v2IotHelper.signCsrAndReturnCertificateArn(ImmutableCertificateSigningRequest.builder().request(deploymentArguments.csr).build()));
} else if (!optionalGroupVersion.isPresent()) {
// New group, create new keys
log.info("Group is new, no certificate ARN or CSR supplied, creating new keys");
KeysAndCertificate coreKeysAndCertificate = iotHelper.createKeysAndCertificateForCore(greengrassGroupName);
iotHelper.writePublicSignedCertificateFileForCore(coreKeysAndCertificate, greengrassGroupName);
iotHelper.writePrivateKeyFileForCore(coreKeysAndCertificate, greengrassGroupName);
iotHelper.writeRootCaFile(greengrassGroupName);
iotHelper.writeIotCpPropertiesFile(greengrassGroupName, coreThingName, coreRoleAlias);
optionalCoreKeysAndCertificate = Optional.of(coreKeysAndCertificate);
} else {
GroupVersion groupVersion = optionalGroupVersion.get();
// Existing group, can we find the existing keys?
optionalCoreKeysAndCertificate = iotHelper.loadKeysAndCertificateForCore(greengrassGroupName);
if (optionalCoreKeysAndCertificate.isPresent()) {
// Found keys, we'll reuse them
log.info("Group is not new, loaded keys from credentials directory");
} else if (deploymentArguments.forceCreateNewKeysOption) {
// Didn't find keys but the user has requested that they be recreated
log.info("Group is not new, user forcing new keys to be created");
KeysAndCertificate coreKeysAndCertificate = iotHelper.createKeysAndCertificateForCore(greengrassGroupName);
optionalCoreKeysAndCertificate = Optional.of(coreKeysAndCertificate);
} else {
log.info("Group is not new, keys could not be found, but user not forcing new keys to be created");
log.info("Attempting to get the core certificate ARN from the latest group version information");
optionalCoreCertificateArn = v2GreengrassHelper.getCoreCertificateArn(groupVersion);
}
}
if (optionalCoreKeysAndCertificate.isPresent()) {
// If we have keys and certificate then get the certificate ARN
optionalCoreCertificateArn = optionalCoreKeysAndCertificate.map(KeysAndCertificate::getCertificateArn);
}
if (!optionalCoreCertificateArn.isPresent()) {
// We need the certificate ARN at this point, fail if we don't have it
StringBuilder message = new StringBuilder();
message.append("Core certificate information/ARN could not be found. ");
message.append(String.join("", "If you would like to recreate the keys you must specify the [", DeploymentArguments.LONG_FORCE_CREATE_NEW_KEYS_OPTION, "] option. "));
message.append(String.join("", "If you'd like to reuse an existing certificate you must specify the [", DeploymentArguments.LONG_CERTIFICATE_ARN_OPTION, "] option."));
throw new RuntimeException(message.toString());
}
CertificateArn coreCertificateArn = optionalCoreCertificateArn.get();
////////////////////////////////////////////////////
// IoT policy creation for the core, if necessary //
////////////////////////////////////////////////////
PolicyName corePolicyName;
if (deploymentArguments.corePolicyName == null) {
if (!deploymentConf.getCoreRoleConf().getIotPolicy().isPresent()) {
throw new RuntimeException(String.join("", "No IoT policy specified for core role [", deploymentConf.getCoreRoleConf().getName(), "]"));
}
log.info("Creating policy for core");
v2IotHelper.createPolicyIfNecessary(ggVariables.getCorePolicyName(greengrassGroupName),
ImmutablePolicyDocument.builder().document(deploymentConf.getCoreRoleConf().getIotPolicy().get()).build());
corePolicyName = ggVariables.getCorePolicyName(greengrassGroupName);
} else {
corePolicyName = ImmutablePolicyName.builder().name(deploymentArguments.corePolicyName).build();
}
//////////////////////////////////
// Attach policy to certificate //
//////////////////////////////////
v2IotHelper.attachPrincipalPolicy(corePolicyName, coreCertificateArn);
/////////////////////////////////
// Attach thing to certificate //
/////////////////////////////////
v2IotHelper.attachThingPrincipal(ggVariables.getCoreThingName(greengrassGroupName), coreCertificateArn);
////////////////////////////////////////////////
// Associate the Greengrass role to the group //
////////////////////////////////////////////////
associateRoleToGroup(coreRole, greengrassGroupId);
////////////////////////////////////////////
// Create a core definition and a version //
////////////////////////////////////////////
log.info("Creating core definition");
String coreDefinitionVersionArn = greengrassHelper.createCoreDefinitionAndVersion(ggVariables.getCoreDefinitionName(greengrassGroupName), coreCertificateArn, coreThingArn, deploymentConf.isSyncShadow());
//////////////////////////////////////////////
// Create a logger definition and a version //
//////////////////////////////////////////////
log.info("Creating logger definition");
String loggerDefinitionVersionArn;
if (!deploymentConf.getLoggers().isPresent()) {
log.warn("No loggers section defined in configuration files, using default logger configuration");
loggerDefinitionVersionArn = greengrassHelper.createDefaultLoggerDefinitionAndVersion();
} else {
loggerDefinitionVersionArn = greengrassHelper.createLoggerDefinitionAndVersion(deploymentConf.getLoggers().get());
}
//////////////////////////////////////////////
// Create the Lambda role for the functions //
//////////////////////////////////////////////
Optional<Role> optionalLambdaRole = Optional.empty();
if (!deploymentConf.getFunctions().isEmpty()) {
log.info("Creating Lambda role");
RoleConf lambdaRoleConf = deploymentConf.getLambdaRoleConf().get();
requireAssumeRolePolicy(lambdaRoleConf, "Lambda");
optionalLambdaRole = Optional.of(createRoleFromRoleConf(lambdaRoleConf));
}
////////////////////////////////////////////////////////
// Start building the subscription and function lists //
////////////////////////////////////////////////////////
List<Subscription> subscriptions = new ArrayList<>();
////////////////////////////////////////////////////
// Determine if any functions need to run as root //
////////////////////////////////////////////////////
boolean functionsRunningAsRoot = functionConfs.stream()
.anyMatch(functionConf -> ((functionConf.getUid().isPresent() && (functionConf.getUid().get() == 0))
|| (functionConf.getGid().isPresent() && (functionConf.getGid().get() == 0))));
if (functionsRunningAsRoot) {
log.warn("At least one function was detected that is configured to run as root");
}
////////////////////////////////////////////////////////////////////////////
// Determine if any functions are running inside the Greengrass container //
////////////////////////////////////////////////////////////////////////////
List<FunctionName> functionsRunningInGreengrassContainer = functionConfs.stream()
.filter(FunctionConf::isGreengrassContainer)
.map(FunctionConf::getFunctionName)
.collect(Collectors.toList());
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Check if launching or building a Docker container and functions are running in the Greengrass container //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
if ((deploymentArguments.dockerLaunch || deploymentArguments.buildContainer) && !functionsRunningInGreengrassContainer.isEmpty()) {
log.error("The following functions are marked to run in the Greengrass container:");
functionsRunningInGreengrassContainer
.forEach(name -> log.error(String.join("", " ", name.getName())));
log.error("When running in Docker all functions must be running without the Greengrass container.");
log.error("Set the greengrassContainer option to false in the functions.default.conf and/or the individual function configurations and try again.");
System.exit(1);
}
/////////////////////////////////////////////////////
// Launch any CloudFormation templates we've found //
/////////////////////////////////////////////////////
List<String> cloudFormationStacksLaunched = functionConfs.stream()
.map(functionConf -> cloudFormationHelper.deployCloudFormationTemplate(defaultEnvironment, deploymentArguments.groupName, functionConf))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
/////////////////////////
// Build the functions //
/////////////////////////
Map<Function, FunctionConf> functionToConfMap = new HashMap<>();
// Only try to create functions if we have a Lambda role
if (optionalLambdaRole.isPresent()) {
Role lambdaRole = optionalLambdaRole.get();
// Verify that all of the functions in the list are supported
functionHelper.verifyFunctionsAreSupported(functionConfs);
// Get the map of functions to function configuration (builds functions and publishes them to Lambda)
functionToConfMap = functionHelper.buildFunctionsAndGenerateMap(deploymentArguments.s3Bucket, deploymentArguments.s3Directory, functionConfs, lambdaRole);
}
////////////////////////////
// Set up local resources //
////////////////////////////
log.info("Creating resource definition");
String resourceDefinitionVersionArn = greengrassHelper.createResourceDefinitionFromFunctionConfs(functionConfs);
/////////////////////////////////////////////////////////////////////////
// Build the function definition for the Lambda function and a version //
/////////////////////////////////////////////////////////////////////////
log.info("Creating function definition");
String functionDefinitionVersionArn = greengrassHelper.createFunctionDefinitionVersion(ImmutableSet.copyOf(functionToConfMap.keySet()), defaultFunctionIsolationMode);
///////////////////////////////////////////////
// Connection functions to cloud and shadows //
///////////////////////////////////////////////
subscriptions.addAll(functionToConfMap.entrySet().stream()
.flatMap(entry -> subscriptionHelper.createCloudSubscriptionsForArn(
entry.getValue().getFromCloudSubscriptions(),
entry.getValue().getToCloudSubscriptions(),
entry.getKey().functionArn()).stream())
.collect(Collectors.toList()));
subscriptions.addAll(subscriptionHelper.connectFunctionsToShadows(functionToConfMap));
////////////////////////////////////////
// Connection functions to each other //
////////////////////////////////////////
subscriptions.addAll(subscriptionHelper.connectFunctions(functionToConfMap));
//////////////////////////////////////////////////////
// Get a list of all of the connected thing shadows //
//////////////////////////////////////////////////////
Set<ThingName> connectedShadowThings = new HashSet<>();
for (FunctionConf functionConf : functionToConfMap.values()) {
connectedShadowThings.addAll(functionConf.getConnectedShadows().stream()
.map(connectedShadow -> ImmutableThingName.builder().name(connectedShadow).build())
.collect(Collectors.toList()));
for (String connectedShadow : functionConf.getConnectedShadows()) {
// Make sure all of the connected shadows exist
v2IotHelper.createThing(ImmutableThingName.builder().name(connectedShadow).build());
}
}
//////////////////////////////////////////////////////
// Create the subscription definition from our list //
//////////////////////////////////////////////////////
log.info("Creating subscription definition");
String subscriptionDefinitionVersionArn = greengrassHelper.createSubscriptionDefinitionAndVersion(subscriptions);
////////////////////////////////////
// Create a minimal group version //
////////////////////////////////////
log.info("Creating group version");
GroupVersion.Builder groupVersionBuilder = GroupVersion.builder();
// Connector definition can not be empty or the cloud service will reject it
optionalConnectionDefinitionVersionArn.ifPresent(groupVersionBuilder::connectorDefinitionVersionArn);
groupVersionBuilder.coreDefinitionVersionArn(coreDefinitionVersionArn);
groupVersionBuilder.functionDefinitionVersionArn(functionDefinitionVersionArn);
groupVersionBuilder.loggerDefinitionVersionArn(loggerDefinitionVersionArn);
groupVersionBuilder.resourceDefinitionVersionArn(resourceDefinitionVersionArn);
groupVersionBuilder.subscriptionDefinitionVersionArn(subscriptionDefinitionVersionArn);
GroupVersion groupVersion = groupVersionBuilder.build();
String groupVersionId = greengrassHelper.createGroupVersion(greengrassGroupId, groupVersion);
/////////////////////////////////////////////
// Do all of the output file related stuff //
/////////////////////////////////////////////
buildOutputFiles(deploymentArguments,
greengrassGroupName,
optionalCreateRoleAliasResponse,
greengrassGroupId,
coreThingName,
coreThingArn,
optionalCoreKeysAndCertificate,
coreCertificateArn,
functionsRunningAsRoot);
//////////////////////////////////////////////////
// Start building the EC2 instance if necessary //
//////////////////////////////////////////////////
Optional<String> optionalInstanceId = Optional.empty();
if (deploymentArguments.ec2LinuxVersion != null) {
log.info("Launching EC2 instance");
Set<Integer> openPorts = functionConfs.stream()
// Get all of the environment variables from each function
.map(FunctionConf::getEnvironmentVariables)
// Extract the PORT variables
.map(environmentVariables -> Optional.ofNullable(environmentVariables.get("PORT")))
// Filter out missing values
.filter(Optional::isPresent)
.map(Optional::get)
// Parse the string into an integer
.map(Integer::parseInt)
.collect(Collectors.toSet());
optionalInstanceId = launchEc2Instance(deploymentArguments.groupName, deploymentArguments.architecture, deploymentArguments.ec2LinuxVersion, deploymentArguments.mqttPort, openPorts);
if (!optionalInstanceId.isPresent()) {
// Something went wrong, bail out
throw new RuntimeException("Couldn't obtain EC2 instance ID, bailing out");
}
}
///////////////////////////////////////////////////
// Start the Docker container build if necessary //
///////////////////////////////////////////////////
if (deploymentArguments.buildContainer) {
log.info("Configuring container build");
ecrDockerHelper.setEcrRepositoryName(Optional.ofNullable(deploymentArguments.ecrRepositoryNameString));
ecrDockerHelper.setEcrImageName(Optional.ofNullable(deploymentArguments.ecrImageNameString));
String imageName = ecrDockerHelper.getImageName();
String currentDirectory = System.getProperty(USER_DIR);
File dockerfile = officialGreengrassImageDockerHelper.getDockerfileForArchitecture(deploymentArguments.architecture);
String dockerfileTemplate = ioHelper.readFileAsString(dockerfile);
dockerfileTemplate = dockerfileTemplate.replaceAll("GROUP_NAME", deploymentArguments.groupName);
// Add the group name and UUID so we don't accidentally overwrite an existing file
File tempDockerfile = dockerfile.toPath().getParent().resolve(
String.join(".", "Dockerfile", deploymentArguments.groupName, ioHelper.getUuid())).toFile();
ioHelper.writeFile(tempDockerfile.toString(), dockerfileTemplate.getBytes());
tempDockerfile.deleteOnExit();
try (DockerClient dockerClient = officialGreengrassImageDockerClientProvider.get()) {
log.info("Building container");
// Pull the official Greengrass Docker container first
String officialGreengrassDockerImage = ggConstants.getOfficialGreengrassDockerImage();
officialGreengrassImageDockerHelper.pullImage(officialGreengrassDockerImage);
String imageId = dockerClient.build(new File(currentDirectory).toPath(),
basicProgressHandler,
DockerClient.BuildParam.dockerfile(tempDockerfile.toPath()));
dockerClient.tag(imageId, imageName);
pushContainerIfNecessary(deploymentArguments, imageId);
} catch (DockerException | InterruptedException | IOException e) {
log.error("Container build failed");
throw new RuntimeException(e);
}
}
// Create a deployment and wait for it to succeed. Return if it fails.
Try.run(() -> createAndWaitForDeployment(optionalGreengrassServiceRole, Optional.of(coreRole), greengrassGroupId, groupVersionId))
.get();
//////////////////////////////////////////////
// Launch the Docker container if necessary //
//////////////////////////////////////////////
if (deploymentArguments.dockerLaunch) {
log.info("Launching Docker container");
String officialGreengrassDockerImage = ggConstants.getOfficialGreengrassDockerImage();
officialGreengrassImageDockerHelper.pullImage(officialGreengrassDockerImage);
officialGreengrassImageDockerHelper.createAndStartContainer(officialGreengrassDockerImage, greengrassGroupName);
}
///////////////////////////////////////////////////////
// Wait for the EC2 instance to launch, if necessary //
///////////////////////////////////////////////////////
if (optionalInstanceId.isPresent()) {
String instanceId = optionalInstanceId.get();
DescribeInstancesRequest describeInstancesRequest = DescribeInstancesRequest.builder()
.instanceIds(instanceId)
.build();
// Describe instances retry policy
RetryPolicy<DescribeInstancesResponse> describeInstancesRetryPolicy = new RetryPolicy<DescribeInstancesResponse>()
.handleIf(throwable -> throwable.getMessage().contains(DOES_NOT_EXIST))
.withDelay(Duration.ofSeconds(5))
.withMaxRetries(3)
.onRetry(failure -> log.warn("Waiting for the instance to become visible..."))
.onRetriesExceeded(failure -> log.error("Instance never became visible. Cannot continue."));
DescribeInstancesResponse describeInstancesResponse = Failsafe.with(describeInstancesRetryPolicy).get(() ->
ec2Client.describeInstances(describeInstancesRequest));
Optional<Reservation> optionalReservation = describeInstancesResponse.reservations().stream().findFirst();
if (!optionalReservation.isPresent()) {
throw new RuntimeException("Error finding the EC2 reservation to wait for the instance to finish launching, this should never happen");
}
Reservation reservation = optionalReservation.get();
Optional<Instance> optionalInstance = reservation.instances().stream().findFirst();
if (!optionalInstance.isPresent()) {
throw new RuntimeException("Error finding the EC2 instance to wait for it to finish launching, this should never happen");
}
Instance instance = optionalInstance.get();
String publicIpAddress = instance.publicIpAddress();
if (publicIpAddress == null) {
throw new RuntimeException("Public IP address returned from EC2 was NULL, skipping EC2 setup");
}
Optional<String> username = Optional.empty();
if (deploymentArguments.ec2LinuxVersion.equals(EC2LinuxVersion.Ubuntu1804)) {
username = Optional.of("ubuntu");
}
if (deploymentArguments.ec2LinuxVersion.equals(EC2LinuxVersion.AmazonLinux2)) {
username = Optional.of("ec2-user");
}
if (!username.isPresent()) {
throw new RuntimeException(String.join("", "Unexpected EC2 Linux version requested [", deploymentArguments.ec2LinuxVersion.name(), "], this is a bug 2 [couldn't determine SSH username]"));
}
attemptBootstrap(deploymentArguments, publicIpAddress, username.get());
}
///////////////////////////////////////////
// Launch a non-EC2 system, if necessary //
///////////////////////////////////////////
if (deploymentArguments.launch != null) {
attemptBootstrap(deploymentArguments, deploymentArguments.launchHost, deploymentArguments.launchUser);
}
//////////////////////////////////////////////////////////////////////////
// Wait for the CloudFormation stacks to finish launching, if necessary //
//////////////////////////////////////////////////////////////////////////
if (cloudFormationStacksLaunched.size() != 0) {
waitForStacksToLaunch(cloudFormationStacksLaunched);
}
}