private Optional launchEc2Instance()

in src/main/java/com/awslabs/aws/greengrass/provisioner/implementations/helpers/BasicDeploymentHelper.java [1351:1471]


    private Optional<String> launchEc2Instance(String groupName, Architecture architecture, EC2LinuxVersion ec2LinuxVersion, int mqttPort, Set<Integer> openPorts) {
        String instanceTagName = String.join("-", GREENGRASS_EC2_INSTANCE_TAG_PREFIX, groupName);

        Optional<String> optionalAccountId = getAccountId(ec2LinuxVersion);

        Optional<InstanceType> instanceType = getInstanceType(architecture);

        Optional<String> optionalNameFilter = getNameFilter(architecture, ec2LinuxVersion);

        if (!optionalAccountId.isPresent()) {
            throw new RuntimeException(String.join("", "Unexpected EC2 Linux version requested [", ec2LinuxVersion.name(), "], this is a bug 1 [couldn't determine which AMI to use]"));
        }

        if (!optionalNameFilter.isPresent() || !instanceType.isPresent()) {
            throw new RuntimeException(String.join("", "Unexpected architecture [", architecture.toString(), "] for EC2 launch"));
        }

        Optional<Image> optionalImage = getImage(optionalNameFilter.get(), optionalAccountId.get());

        if (!optionalImage.isPresent()) {
            log.error(String.join("", "No [", ec2LinuxVersion.name(), "] image found in this region, not launching the instance"));
            return Optional.empty();
        }

        DescribeKeyPairsResponse describeKeyPairsResponse = ec2Client.describeKeyPairs();

        // Find the first keypair
        Optional<KeyPairInfo> optionalKeyPairInfo = describeKeyPairsResponse.keyPairs().stream().min(Comparator.comparing(KeyPairInfo::keyName));

        if (!optionalKeyPairInfo.isPresent()) {
            log.error("No SSH keys found in your account, not launching the instance");
            return Optional.empty();
        }

        KeyPairInfo keyPairInfo = optionalKeyPairInfo.get();

        log.warn(String.join("", "Automatically chose the first key pair available [", keyPairInfo.keyName(), "]"));

        Image image = optionalImage.get();

        String securityGroupName = String.join("-", instanceTagName, ioHelper.getUuid());

        CreateSecurityGroupRequest createSecurityGroupRequest = CreateSecurityGroupRequest.builder()
                .groupName(securityGroupName)
                .description(String.join("", "Security group for Greengrass Core [", instanceTagName, "]"))
                .build();

        ec2Client.createSecurityGroup(createSecurityGroupRequest);

        // Sometimes the security group isn't immediately visible so we need retries
        RetryPolicy<AuthorizeSecurityGroupIngressResponse> securityGroupRetryPolicy = new RetryPolicy<AuthorizeSecurityGroupIngressResponse>()
                .handleIf(throwable -> throwable.getMessage().contains(DOES_NOT_EXIST))
                .withDelay(Duration.ofSeconds(5))
                .withMaxRetries(6)
                .onRetry(failure -> log.warn("Waiting for security group to become visible..."))
                .onRetriesExceeded(failure -> log.error("Security group never became visible. Cannot continue."));

        List<IpPermission> ipPermissions = openPorts.stream()
                .map(this::openTcpPortToWorld)
                .collect(Collectors.toList());

        IpPermission sshPermission = openTcpPortToWorld(SSH_PORT);
        IpPermission mqttPermission = openTcpPortToWorld(mqttPort);
        IpPermission moshPermission = openUdpRangeToWorld(MOSH_START_PORT, MOSH_END_PORT);

        ipPermissions.add(sshPermission);
        ipPermissions.add(moshPermission);
        ipPermissions.add(mqttPermission);

        AuthorizeSecurityGroupIngressRequest authorizeSecurityGroupIngressRequest = AuthorizeSecurityGroupIngressRequest.builder()
                .groupName(securityGroupName)
                .ipPermissions(ipPermissions)
                .build();

        Failsafe.with(securityGroupRetryPolicy).get(() ->
                ec2Client.authorizeSecurityGroupIngress(authorizeSecurityGroupIngressRequest));

        RunInstancesRequest runInstancesRequest = RunInstancesRequest.builder()
                .imageId(image.imageId())
                .instanceType(instanceType.get())
                .maxCount(1)
                .minCount(1)
                .keyName(keyPairInfo.keyName())
                .securityGroups(securityGroupName)
                .build();

        RunInstancesResponse runInstancesResponse = ec2Client.runInstances(runInstancesRequest);

        Optional<String> optionalInstanceId = runInstancesResponse.instances().stream().findFirst().map(Instance::instanceId);

        if (!optionalInstanceId.isPresent()) {
            log.error("Couldn't find an instance ID, this should never happen, not launching the instance");
            return Optional.empty();
        }

        String instanceId = optionalInstanceId.get();

        Tag tag = Tag.builder()
                .key("Name")
                .value(instanceTagName)
                .build();

        CreateTagsRequest createTagsRequest = CreateTagsRequest.builder()
                .tags(tag)
                .resources(instanceId)
                .build();

        RetryPolicy<CreateTagsResponse> createTagsResponseRetryPolicy = new RetryPolicy<CreateTagsResponse>()
                .handleIf(throwable -> throwable.getMessage().contains(DOES_NOT_EXIST))
                .withDelay(Duration.ofSeconds(5))
                .withMaxRetries(3)
                .onRetry(failure -> log.warn("Instance may still be starting, trying again..."))
                .onRetriesExceeded(failure -> log.error("Failed to find the instance in EC2, it was not launched"));

        Failsafe.with(createTagsResponseRetryPolicy).get(() ->
                ec2Client.createTags(createTagsRequest));

        log.info(String.join("", "Launched instance [", instanceId, "] with tag [", instanceTagName, "]"));

        return Optional.of(instanceId);
    }