Procházet zdrojové kódy

WIP. packer basics working for ec2

cobbzilla/introduce_packer
Jonathan Cobb před 4 roky
rodič
revize
828cdcf736
38 změnil soubory, kde provedl 654 přidání a 243 odebrání
  1. +2
    -0
      bin/aws/config.template
  2. +16
    -0
      bin/aws/delete_subnets.sh
  3. +0
    -0
      bin/aws/delete_test_instances.sh
  4. +6
    -0
      bin/aws/init_aws_configs.sh
  5. +3
    -0
      bin/aws/list_regions.sh
  6. +0
    -0
      bin/aws/list_test_instances.sh
  7. +12
    -0
      bin/aws/set_aws_region.sh
  8. +1
    -2
      bubble-server/pom.xml
  9. +3
    -2
      bubble-server/src/main/java/bubble/cloud/NoopCloud.java
  10. +7
    -0
      bubble-server/src/main/java/bubble/cloud/compute/ComputeDiskType.java
  11. +2
    -2
      bubble-server/src/main/java/bubble/cloud/compute/ComputeNodeSize.java
  12. +8
    -2
      bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriver.java
  13. +36
    -1
      bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriverBase.java
  14. +3
    -1
      bubble-server/src/main/java/bubble/cloud/compute/OsImage.java
  15. +1
    -1
      bubble-server/src/main/java/bubble/cloud/compute/PackerImage.java
  16. +1
    -4
      bubble-server/src/main/java/bubble/cloud/compute/PackerImageParserBase.java
  17. +3
    -1
      bubble-server/src/main/java/bubble/cloud/compute/delegate/DelegatedComputeDriver.java
  18. +3
    -1
      bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanComputeNodeSizeParser.java
  19. +7
    -5
      bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java
  20. +2
    -2
      bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanOsImageParser.java
  21. +2
    -2
      bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanPackerImageParser.java
  22. +380
    -119
      bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java
  23. +2
    -1
      bubble-server/src/main/java/bubble/cloud/compute/local/LocalComputeDriver.java
  24. +2
    -1
      bubble-server/src/main/java/bubble/cloud/compute/mock/MockComputeDriver.java
  25. +3
    -1
      bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrComputeNodeSizeParser.java
  26. +19
    -38
      bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java
  27. +2
    -2
      bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrOsImageParser.java
  28. +2
    -2
      bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrPackerImageParser.java
  29. +7
    -13
      bubble-server/src/main/java/bubble/cloud/shared/aws/BubbleAwsCredentialsProvider.java
  30. +3
    -3
      bubble-server/src/main/java/bubble/main/http/BubbleHttpEntityOptions.java
  31. +1
    -1
      bubble-server/src/main/java/bubble/resources/cloud/ComputePackerResource.java
  32. +18
    -17
      bubble-server/src/main/java/bubble/service/packer/PackerJob.java
  33. +2
    -2
      bubble-server/src/main/resources/ansible/playbook.yml.hbs
  34. +11
    -0
      bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties
  35. +77
    -10
      bubble-server/src/main/resources/models/defaults/cloudService.json
  36. +2
    -2
      bubble-server/src/main/resources/packer/packer-playbook.yml.hbs
  37. +3
    -3
      bubble-server/src/main/resources/packer/packer.json.hbs
  38. +2
    -2
      bubble-server/src/main/resources/packer/roles/algo/files/config.cfg.hbs

+ 2
- 0
bin/aws/config.template Zobrazit soubor

@@ -0,0 +1,2 @@
[default]
region = __REGION__

+ 16
- 0
bin/aws/delete_subnets.sh Zobrazit soubor

@@ -0,0 +1,16 @@
#!/bin/bash

function die {
echo 1>&2 "${1}"
exit 1
}

THISDIR=$(cd $(dirname ${0}) && pwd)
for region in $(${THISDIR}/list_regions.sh) ; do
echo "Deleting subnets in region ${region}"
${THISDIR}/set_aws_region.sh ${region} || die "Error setting aws region ${region}"
for subnet in $(aws ec2 describe-subnets --filters "Name=default-for-az,Values=false" | grep SubnetId | cut -d\" -f4) ; do
echo "Deleting subnet ${subnet} in region ${region}"
aws ec2 delete-subnet --subnet-id ${subnet} || echo "WARNING: Error deleting subnet ${subnet} in region ${region}"
done
done

bin/compute/ec2/delete_test_instances.sh → bin/aws/delete_test_instances.sh Zobrazit soubor


+ 6
- 0
bin/aws/init_aws_configs.sh Zobrazit soubor

@@ -0,0 +1,6 @@
#!/bin/bash

THISDIR=$(cd $(dirname ${0}) && pwd)
for region in $(${THISDIR}/list_regions.sh) ; do
cat ${THISDIR}/config.template > ~/.aws/config.${region} && echo "created config for region ${region}"
done

+ 3
- 0
bin/aws/list_regions.sh Zobrazit soubor

@@ -0,0 +1,3 @@
#!/bin/bash

aws ec2 describe-regions --filters "Name=opt-in-status,Values=opt-in-not-required" | grep RegionName | cut -d\" -f4 | sort

bin/compute/ec2/list_test_instances.sh → bin/aws/list_test_instances.sh Zobrazit soubor


+ 12
- 0
bin/aws/set_aws_region.sh Zobrazit soubor

@@ -0,0 +1,12 @@
#!/bin/bash

function die {
echo 1>&2 "${1}"
exit 1
}

region=${1:?no region specified}
if [[ ! -f ~/.aws/config.${region} ]] ; then
die "Region not found: ${region}"
fi
rm -f ~/.aws/config && ln -s ~/.aws/config.${region} ~/.aws/config

+ 1
- 2
bubble-server/pom.xml Zobrazit soubor

@@ -106,11 +106,10 @@
<artifactId>jetty-proxy</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-ec2 -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-ec2</artifactId>
<version>1.11.762</version>
<version>1.11.797</version>
</dependency>

<!--&lt;!&ndash; https://mvnrepository.com/artifact/org.atmosphere/atmosphere-jersey &ndash;&gt;-->


+ 3
- 2
bubble-server/src/main/java/bubble/cloud/NoopCloud.java Zobrazit soubor

@@ -36,7 +36,6 @@ import org.cobbzilla.util.dns.DnsRecordMatch;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@@ -69,7 +68,9 @@ public class NoopCloud implements
return false;
}

@Override public List<PackerImage> getPackerImages() { return Collections.emptyList(); }
@Override public List<PackerImage> getAllPackerImages() { return null; }

@Override public List<PackerImage> getPackerImagesForRegion(String region) { return null; }

@Override public boolean _write(String fromNode, String key, InputStream data, StorageMetadata metadata, String requestId) throws IOException {
if (log.isDebugEnabled()) log.debug("_write(fromNode=" + fromNode + ")");


+ 7
- 0
bubble-server/src/main/java/bubble/cloud/compute/ComputeDiskType.java Zobrazit soubor

@@ -0,0 +1,7 @@
package bubble.cloud.compute;

public enum ComputeDiskType {

ssd, hdd, ebs_gp2, ebs_magnetic;

}

+ 2
- 2
bubble-server/src/main/java/bubble/cloud/compute/ComputeNodeSize.java Zobrazit soubor

@@ -23,8 +23,8 @@ public class ComputeNodeSize {
@Getter @Setter private String description;
@Getter @Setter private int vcpu;
@Getter @Setter private int memoryMB;
@Getter @Setter private int ssdGB;
@Getter @Setter private int hddGB;
@Getter @Setter private int diskGB;
@Getter @Setter private ComputeDiskType diskType;
@Getter @Setter private Integer networkMbps;
@Getter @Setter private Integer transferGB;



+ 8
- 2
bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriver.java Zobrazit soubor

@@ -4,6 +4,7 @@
*/
package bubble.cloud.compute;

import bubble.cloud.CloudRegion;
import bubble.cloud.CloudServiceDriver;
import bubble.cloud.CloudServiceType;
import bubble.model.cloud.AnsibleInstallType;
@@ -35,7 +36,12 @@ public interface ComputeServiceDriver extends CloudServiceDriver, RegionalServic

@Override default boolean test () { return true; }

List<PackerImage> getPackerImages();
default List<PackerImage> finalizeIncompletePackerRun(CommandResult commandResult, AnsibleInstallType installType, String jarSha) { return null; }
List<PackerImage> getAllPackerImages();
List<PackerImage> getPackerImagesForRegion(String region);
default List<PackerImage> finalizeIncompletePackerRun(CommandResult commandResult, AnsibleInstallType installType) { return null; }

default Map<String, Object> getPackerRegionContext(CloudRegion region) { return null; }

default int getPackerParallelBuilds() { return 1; }

}

+ 36
- 1
bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriverBase.java Zobrazit soubor

@@ -7,6 +7,7 @@ package bubble.cloud.compute;
import bubble.cloud.CloudRegion;
import bubble.cloud.CloudServiceDriverBase;
import bubble.dao.cloud.BubbleNodeDAO;
import bubble.model.cloud.AnsibleInstallType;
import bubble.model.cloud.BubbleNode;
import bubble.service.packer.PackerService;
import lombok.Getter;
@@ -18,13 +19,19 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.cobbzilla.util.daemon.ZillaRuntime.die;
import static org.cobbzilla.util.daemon.ZillaRuntime.now;
import static org.cobbzilla.util.system.Sleep.sleep;

@Slf4j
public abstract class ComputeServiceDriverBase
extends CloudServiceDriverBase<ComputeConfig>
implements ComputeServiceDriver {

public static final long PACKER_TIMEOUT = MINUTES.toMillis(60);

private final AtomicReference<NodeReaper> reaper = new AtomicReference<>();

@Override public void postSetup() {
@@ -83,7 +90,7 @@ public abstract class ComputeServiceDriverBase
}

@Getter(lazy=true) private final OsImage os = initOs();
private OsImage initOs() {
protected OsImage initOs() {
final OsImage os = getCloudOsImages().stream()
.filter(s -> s.getName().equals(config.getOs()))
.findFirst()
@@ -104,4 +111,32 @@ public abstract class ComputeServiceDriverBase
.findAny().orElse(null);
}

public PackerImage getPackerImage(BubbleNode node) {
PackerImage packerImage = getPackerImage(node.getInstallType(), node.getRegion());
if (packerImage == null) {
final AtomicReference<List<PackerImage>> imagesRef = new AtomicReference<>();
packerService.writePackerImages(cloud, node.getInstallType(), imagesRef);
long start = now();
while (imagesRef.get() == null && now() - start < PACKER_TIMEOUT) {
sleep(SECONDS.toMillis(1), "getPackerImage: waiting for packer image creation");
}
if (imagesRef.get() == null) {
return die("getPackerImage: timeout creating packer image");
}
packerImage = getPackerImage(node.getInstallType(), node.getRegion());
if (packerImage == null) {
return die("getPackerImage: error creating packer image");
}
}
return packerImage;
}

public PackerImage getPackerImage(AnsibleInstallType installType, String region) {
final List<PackerImage> images = getPackerImagesForRegion(region);
return images == null ? null : images.stream()
.filter(i -> i.getName().contains("_"+installType.name()+"_"))
.findFirst()
.orElse(null);
}

}

+ 3
- 1
bubble-server/src/main/java/bubble/cloud/compute/OsImage.java Zobrazit soubor

@@ -1,12 +1,14 @@
package bubble.cloud.compute;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import lombok.experimental.Accessors;

@NoArgsConstructor @Accessors(chain=true)
public class OsImage {

@Getter @Setter private Long id;
@Getter @Setter private String id;
@Getter @Setter private String name;
@Getter @Setter private String region;

}

+ 1
- 1
bubble-server/src/main/java/bubble/cloud/compute/PackerImage.java Zobrazit soubor

@@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;

@NoArgsConstructor @Accessors(chain=true) @EqualsAndHashCode(of={"id"})
@NoArgsConstructor @Accessors(chain=true) @EqualsAndHashCode(of={"id", "regions"})
public class PackerImage {

@Getter @Setter private String id;


+ 1
- 4
bubble-server/src/main/java/bubble/cloud/compute/PackerImageParserBase.java Zobrazit soubor

@@ -6,19 +6,16 @@ public abstract class PackerImageParserBase extends ListResourceParser<PackerIma

private String bubbleVersion;
private String keyHash;
private String jarSha;

public PackerImageParserBase(String bubbleVersion, String keyHash, String jarSha) {
public PackerImageParserBase(String bubbleVersion, String keyHash) {
this.bubbleVersion = bubbleVersion;
this.keyHash = keyHash;
this.jarSha = jarSha;
}

public boolean isValidPackerImage(String name) {
if (!name.startsWith(PACKER_IMAGE_PREFIX)) return false;
if (!name.contains("_"+bubbleVersion+"_")) return false;
if (!name.contains("_"+keyHash+"_")) return false;
// if (!name.endsWith("_"+jarSha)) return false;
return true;
}



+ 3
- 1
bubble-server/src/main/java/bubble/cloud/compute/delegate/DelegatedComputeDriver.java Zobrazit soubor

@@ -75,6 +75,8 @@ public class DelegatedComputeDriver extends DelegatedCloudServiceDriverBase impl
return notificationService.notifySync(delegate, compute_driver_status, notification(node));
}

@Override public List<PackerImage> getPackerImages() { return notSupported("getPackerImages"); }
@Override public List<PackerImage> getAllPackerImages() { return notSupported("getPackerImages"); }
@Override public List<PackerImage> getPackerImagesForRegion(String region) { return notSupported("getPackerImagesForRegion"); }


}

+ 3
- 1
bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanComputeNodeSizeParser.java Zobrazit soubor

@@ -1,5 +1,6 @@
package bubble.cloud.compute.digitalocean;

import bubble.cloud.compute.ComputeDiskType;
import bubble.cloud.compute.ComputeNodeSize;
import bubble.cloud.compute.ListResourceParser;
import com.fasterxml.jackson.databind.JsonNode;
@@ -18,7 +19,8 @@ public class DigitalOceanComputeNodeSizeParser extends ListResourceParser<Comput
.setInternalName(item.get("slug").textValue())
.setVcpu(item.get("vcpus").intValue())
.setMemoryMB(item.get("memory").intValue())
.setSsdGB(item.get("disk").intValue())
.setDiskGB(item.get("disk").intValue())
.setDiskType(ComputeDiskType.ssd)
.setTransferGB(1024 * item.get("transfer").intValue());
}



+ 7
- 5
bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java Zobrazit soubor

@@ -170,14 +170,13 @@ public class DigitalOceanDriver extends ComputeServiceDriverBase {

final ComputeNodeSize size = config.getSize(node.getSize());

// todo: lookup image based on node installType and region
final String os = getOs().getName();
final PackerImage packerImage = getPackerImage(node);

final CreateDropletRequest createRequest = new CreateDropletRequest()
.setName(node.getFqdn())
.setRegion(region.getInternalName())
.setSize(size.getInternalName())
.setImage(os)
.setImage(packerImage.getId())
.setIpv6(true)
.setBackups(false)
.setMonitoring(false)
@@ -232,8 +231,11 @@ public class DigitalOceanDriver extends ComputeServiceDriverBase {
return node.setState(found.get(0).getState());
}

@Override public List<PackerImage> getPackerImages() {
final List<PackerImage> images = getResources(PACKER_IMAGES_URI, new DigitalOceanPackerImageParser(configuration.getVersion(), packerService.getPackerPublicKeyHash(), configuration.getJarSha()));
@Override public List<PackerImage> getAllPackerImages() { return getPackerImages(); }
@Override public List<PackerImage> getPackerImagesForRegion(String region) { return getPackerImages(); }

public List<PackerImage> getPackerImages () {
final List<PackerImage> images = getResources(PACKER_IMAGES_URI, new DigitalOceanPackerImageParser(configuration.getVersion(), packerService.getPackerPublicKeyHash()));
return images == null ? Collections.emptyList() : images;
}



+ 2
- 2
bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanOsImageParser.java Zobrazit soubor

@@ -13,7 +13,7 @@ public class DigitalOceanOsImageParser extends ListResourceParser<OsImage> {
if (item.has("id")) {
final JsonNode id = item.get("id");
if (id.isNumber()) {
image.setId(id.numberValue().longValue());
image.setId(id.asText());
} else {
return die("parse: id was not numeric");
}
@@ -22,7 +22,7 @@ public class DigitalOceanOsImageParser extends ListResourceParser<OsImage> {
}
if (item.has("name")) {
final JsonNode name = item.get("slug");
image.setName(name.textValue());
image.setName(name.asText());
} else {
return die("parse: name not found");
}


+ 2
- 2
bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanPackerImageParser.java Zobrazit soubor

@@ -10,8 +10,8 @@ import java.util.List;

public class DigitalOceanPackerImageParser extends PackerImageParserBase {

public DigitalOceanPackerImageParser (String bubbleVersion, String keyHash, String jarSha) {
super(bubbleVersion, keyHash, jarSha);
public DigitalOceanPackerImageParser (String bubbleVersion, String keyHash) {
super(bubbleVersion, keyHash);
}

@Override public boolean allowEmpty() { return true; }


+ 380
- 119
bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java Zobrazit soubor

@@ -18,109 +18,391 @@ import com.amazonaws.regions.Regions;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2ClientBuilder;
import com.amazonaws.services.ec2.model.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.collection.ExpirationMap;
import org.cobbzilla.util.collection.SingletonList;
import org.cobbzilla.util.daemon.AwaitResult;
import org.cobbzilla.util.reflect.ReflectionUtil;
import org.cobbzilla.wizard.cache.redis.RedisService;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static bubble.model.cloud.BubbleNode.TAG_INSTANCE_ID;
import static bubble.model.cloud.BubbleNode.TAG_TEST;
import static java.util.Comparator.comparing;
import static java.util.concurrent.TimeUnit.DAYS;
import static org.cobbzilla.util.daemon.Await.awaitAll;
import static org.cobbzilla.util.daemon.DaemonThreadFactory.fixedPool;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.http.HttpStatusCodes.OK;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.wizard.cache.redis.RedisService.EX;
import static org.cobbzilla.wizard.resources.ResourceUtil.notFoundEx;

@Slf4j
public class AmazonEC2Driver extends ComputeServiceDriverBase {

public static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10);
public static final long PARALLEL_TIMEOUT = TimeUnit.SECONDS.toMillis(20);
public static final String TAG_CLOUD_UUID = "cloudUUID";
public static final String TAG_NODE_UUID = "nodeUUID";
public static final String KEY_NAME_PREFIX = "keyName_";
public static final int MIN_COUNT = 1;
public static final int MAX_COUNT = 1;

@Getter(lazy=true) private final AWSCredentialsProvider ec2credentials = new BubbleAwsCredentialsProvider(cloud, getCredentials());
@Getter(lazy=true) private final Map<String, AmazonEC2> ec2ClientMap = new HashMap<>();
public static final String VPC_CIDR_BLOCK = "10.0.0.0/16";
public static final String VPC_IPV6_CIDR_BLOCK = "fdb0:bb00::/48";
public static final String TAG_BUBBLE_CLASS = "BUBBLE_CLASS";
public static final String TAG_BUBBLE_CLASS_PACKER_VPC = "BUBBLE_PACKER_VPC";
public static final String TAG_BUBBLE_CLASS_PACKER_SUBNET = "BUBBLE_PACKER_SUBNET";

public static final Filter[] VPC_FILTERS = new Filter[]{
new Filter("tag:" + TAG_BUBBLE_CLASS, new SingletonList<>(TAG_BUBBLE_CLASS_PACKER_VPC))
};
public static final Filter[] SUBNET_FILTERS = new Filter[]{
new Filter("tag:" + TAG_BUBBLE_CLASS, new SingletonList<>(TAG_BUBBLE_CLASS_PACKER_SUBNET))
};
public static final String IP4_CIDR_ALL = "0.0.0.0/0";
public static final String IP6_CIDR_ALL = "::/0";

@Autowired private RedisService redis;

@Override protected List<CloudRegion> getCloudRegions() {
// todo
return null;
@Getter(lazy=true) private final String securityGroup = config.getConfig("securityGroup");

@Getter(lazy=true) private final AWSCredentialsProvider ec2credentials = new BubbleAwsCredentialsProvider(cloud, getCredentials());
@Getter(lazy=true) private final Map<String, AmazonEC2> ec2ClientMap = initClientMap();

private Map<String, AmazonEC2> initClientMap() {
final Map<String, AmazonEC2> clients = new HashMap<>();
final AWSCredentialsProvider ec2credentials = getEc2credentials();
for (CloudRegion region : getCloudRegions()) {
final AmazonEC2 ec2 = AmazonEC2ClientBuilder.standard()
.withRegion(Regions.fromName(region.getInternalName()))
.withCredentials(ec2credentials).build();
clients.put(region.getInternalName(), ec2);
}
return clients;
}

@Override protected List<ComputeNodeSize> getCloudSizes() {
// todo
return null;
@Getter(lazy=true) private final List<CloudRegion> cloudRegions = driverConfig("regions");
@Getter(lazy=true) private final List<ComputeNodeSize> cloudSizes = driverConfig("sizes");

private <T> List<T> driverConfig(String field) { return Arrays.asList((T[]) ReflectionUtil.get(config, field)); }

@Getter(lazy=true) private final RedisService imageCache = redis.prefixNamespace(getClass().getSimpleName()+".ec2_ubuntu_image");
public static final long IMAGE_CACHE_TIME = DAYS.toSeconds(30);

@Getter(lazy=true) private final ExecutorService perRegionExecutor = fixedPool(getRegions().size());

@Getter(lazy=true) private final List<OsImage> cloudOsImages = initImages();
private List<OsImage> initImages() {
final ArrayList<Filter> filters = new ArrayList<>();
filters.add(new Filter("root-device-type", new SingletonList<>("ebs")));
filters.add(new Filter("state", new SingletonList<>("available")));
filters.add(new Filter("name", new SingletonList<>(config.getOs())));
final List<Future<?>> futures = new ArrayList<>();
for (CloudRegion region : getCloudRegions()) {
futures.add(getPerRegionExecutor().submit(() -> {
final String internalName = region.getInternalName();
final String cachedJson = getImageCache().get(internalName);
if (cachedJson != null) {
return json(cachedJson, OsImage.class);
}
final AmazonEC2 ec2 = getEc2Client(region);
final DescribeImagesRequest imageRequest = new DescribeImagesRequest().withFilters(filters);
final DescribeImagesResult imagesResult = ec2.describeImages(imageRequest);
if (empty(imagesResult.getImages())) die("no images found");
final List<Image> sorted = new ArrayList<>(imagesResult.getImages());
sorted.sort(comparing(Image::getCreationDate));
final Image first = sorted.get(0);
final OsImage image = new OsImage()
.setName(first.getName())
.setId(first.getImageId())
.setRegion(internalName);
getImageCache().set(internalName, json(image), EX, IMAGE_CACHE_TIME);
return image;
}));
}
final AwaitResult<Object> awaitResult = awaitAll(futures, PARALLEL_TIMEOUT);
if (!awaitResult.allSucceeded()) return die("initImages: "+awaitResult.getFailures().values());
return awaitResult.getSuccesses().values().stream().map(o -> (OsImage) o).collect(Collectors.toList());
}

@Override protected List<OsImage> getCloudOsImages() {
// todo
return null;
@Getter(lazy=true) private final Map<String, OsImage> imagesByRegion = getCloudOsImages().stream()
.collect(Collectors.toMap(OsImage::getRegion, Function.identity()));

public Map<String, Object> getPackerRegionContext(CloudRegion region) {
final Map<String, Object> ctx = new HashMap<>();
final String internalName = region.getInternalName();

final Map<String, OsImage> imagesByRegion = getImagesByRegion();
if (empty(imagesByRegion)) return die("getPackerRegionContext: getImagesByRegion returned empty map");
final OsImage imageForRegion = imagesByRegion.get(internalName);
if (imageForRegion == null) return die("getPackerRegionContext: no image found for region: "+internalName);
ctx.put("imageForRegion", imageForRegion);

final Map<String, Vpc> vpcsByRegion = getVpcsByRegion();
if (empty(vpcsByRegion)) return die("getPackerRegionContext: getVpcsByRegion returned empty map");
final Vpc vpc = vpcsByRegion.get(internalName);
if (vpc == null) return die("getPackerRegionContext: no vpc found for region: "+internalName);
ctx.put("vpcForRegion", vpc);

final Map<String, Map<String, Subnet>> subnetsByRegion = getSubnetsByRegion();
if (empty(subnetsByRegion)) return die("getPackerRegionContext: getSubnetsByRegion returned empty map");
final Map<String, Subnet> subnets = subnetsByRegion.get(internalName);
if (subnets == null) return die("getPackerRegionContext: no subnets found for region: "+internalName);

// use the last az/subnet in the region
final List<String> azNames = new ArrayList<>(new TreeSet<>(subnets.keySet()));
final String az = azNames.get(azNames.size() - 1);
ctx.put("availabilityZoneForRegion", az);
ctx.put("subnetForRegion", subnets.get(az));

return ctx;
}

private static final ExecutorService perRegionExecutor = fixedPool(8);

private AmazonEC2 getEC2Client(final String regionName) {
Map<String, AmazonEC2> ec2ClientMap = getEc2ClientMap();
AmazonEC2 ec2client;
if (!ec2ClientMap.containsKey(regionName)) {
final Regions region;
try {
region = Regions.valueOf(regionName);
} catch (Exception e) {
return die("initEC2Client: invalid region: " + regionName);
}
ec2client = AmazonEC2ClientBuilder.standard()
.withRegion(region)
.withCredentials(getEc2credentials())
.build();
ec2ClientMap.put(regionName, ec2client);
@Override protected OsImage initOs() { return null; }

@Override public List<PackerImage> getAllPackerImages() {
final List<Future<?>> futures = new ArrayList<>();
for (CloudRegion region : getRegions()) {
futures.add(getPerRegionExecutor().submit(() -> getPackerImagesForRegion(region.getInternalName())));
}
ec2client = ec2ClientMap.get(regionName);
return ec2client;
final AwaitResult<Object> awaitResult = awaitAll(futures, PARALLEL_TIMEOUT);
if (!awaitResult.allSucceeded()) return die("initImages: "+awaitResult.getFailures().values());

final List<PackerImage> images = new ArrayList<>();
for (Object o : awaitResult.getSuccesses().values()) images.addAll((List<PackerImage>) o);
return images;
}

@Override public List<BubbleNode> listNodes() throws IOException {
List<Future<?>> listNodeJobs = new ArrayList<>();
for (final String regionName : getEc2ClientMap().keySet()) {
listNodeJobs.add(perRegionExecutor.submit(new listNodesHelper(regionName)));
@Override public List<PackerImage> getPackerImagesForRegion(String region) {
final ArrayList<Filter> filters = new ArrayList<>();
filters.add(new Filter("root-device-type", new SingletonList<>("ebs")));
filters.add(new Filter("state", new SingletonList<>("available")));
filters.add(new Filter("name", new SingletonList<>("packer_*_"+packerService.getPackerPublicKeyHash()+"_"+configuration.getVersion()+"_*")));
final AmazonEC2 ec2 = getEc2Client(region);
final DescribeImagesRequest imageRequest = new DescribeImagesRequest().withFilters(filters);
final DescribeImagesResult imagesResult = ec2.describeImages(imageRequest);
if (empty(imagesResult.getImages())) return Collections.emptyList();
return imagesResult.getImages().stream().map(i -> new PackerImage()
.setName(i.getName())
.setId(i.getImageId())
.setRegions(new CloudRegion[]{getRegion(region)})
).collect(Collectors.toList());
}

@Override public int getPackerParallelBuilds() { return getRegions().size(); }

private AmazonEC2 getEc2Client(CloudRegion region) { return getEc2Client(region.getInternalName()); }

private AmazonEC2 getEc2Client(String internalName) {
final AmazonEC2 ec2 = getEc2ClientMap().get(internalName);
if (ec2 == null) return die("getEc2Client: invalid region: "+internalName);
return ec2;
}

private final ExpirationMap<String, Vpc> vpcCache = new ExpirationMap<>(DAYS.toMillis(1));

public Map<String, Vpc> getVpcsByRegion() {
final List<Future<?>> futures = new ArrayList<>();
final Map<String, Vpc> vpcsByRegion = new ConcurrentHashMap<>();
for (CloudRegion region : getRegions()) {
final String internalName = region.getInternalName();
final AmazonEC2 ec2 = getEc2Client(internalName);
futures.add(getPerRegionExecutor().submit(() -> {
Vpc vpc = vpcCache.get(internalName);
if (vpc != null) {
vpcsByRegion.put(internalName, vpc);
return;
}
final DescribeVpcsRequest describeRequest = new DescribeVpcsRequest().withFilters(VPC_FILTERS);
final DescribeVpcsResult describeVpcsResult = ec2.describeVpcs(describeRequest);
if (empty(describeVpcsResult.getVpcs())) {
final CreateVpcRequest createRequest = new CreateVpcRequest()
.withCidrBlock(VPC_CIDR_BLOCK)
.withAmazonProvidedIpv6CidrBlock(true);
final CreateVpcResult createVpcResult;
try {
createVpcResult = ec2.createVpc(createRequest);
} catch (Exception e) {
die("createVpcs("+internalName+"): "+e, e);
return;
}

vpc = createVpcResult.getVpc();
ec2.createTags(new CreateTagsRequest()
.withResources(vpc.getVpcId())
.withTags(new Tag(TAG_BUBBLE_CLASS, TAG_BUBBLE_CLASS_PACKER_VPC)));
} else {
final List<Vpc> vpcs = describeVpcsResult.getVpcs();
if (vpcs.size() > 1) die("createVpcs: more than 1 vpc found for region: "+internalName);
vpc = vpcs.get(0);
}
vpcsByRegion.put(internalName, vpc);
vpcCache.put(internalName, vpc);
}));
}
AwaitResult awaitResult = awaitAll(listNodeJobs, TIMEOUT);
final AwaitResult awaitResult = awaitAll(futures, PARALLEL_TIMEOUT);
if (!awaitResult.allSucceeded()) {
return die("listNodes: error listing nodes");
return die("createVpcs: "+awaitResult.getFailures().values());
}
final List<BubbleNode> nodes = new ArrayList<>();
return vpcsByRegion;
}

nodes.addAll((Collection<? extends BubbleNode>) awaitResult.getSuccesses().values());
private final ExpirationMap<String, List<String>> azCache = new ExpirationMap<>(DAYS.toMillis(30));
private final ExpirationMap<String, Map<String, Subnet>> subnetCache = new ExpirationMap<>(DAYS.toMillis(1));

public Map<String, Map<String, Subnet>> getSubnetsByRegion() {
final Map<String, Vpc> vpcsByRegion = getVpcsByRegion();
final List<Future<?>> futures = new ArrayList<>();
final Map<String, Map<String, Subnet>> subnetsByRegion = new ConcurrentHashMap<>();
for (CloudRegion region : getRegions()) {
final String internalName = region.getInternalName();
final AmazonEC2 ec2 = getEc2Client(internalName);
futures.add(getPerRegionExecutor().submit(() -> {
final List<String> availZones = azCache.computeIfAbsent(internalName, k -> {
final DescribeAvailabilityZonesResult azResult = ec2.describeAvailabilityZones(new DescribeAvailabilityZonesRequest().withFilters(new Filter().withName("region-name").withValues(k)));
return azResult.getAvailabilityZones().stream().map(AvailabilityZone::getZoneName).collect(Collectors.toList());
});

Map<String, Subnet> subnets = subnetCache.get(internalName);
if (subnets == null) {
subnets = new HashMap<>();
} else if (!empty(subnets) && subnets.size() == availZones.size()) {
subnetsByRegion.put(internalName, subnets);
return;
}

return nodes;
final DescribeSubnetsResult subnetsResult = ec2.describeSubnets(new DescribeSubnetsRequest().withFilters(SUBNET_FILTERS));
for (String az : availZones) {
final Vpc vpc = vpcsByRegion.get(internalName);
Subnet subnet = subnetsResult.getSubnets().stream().filter(sn -> sn.getAvailabilityZone().equals(az)).findFirst().orElse(null);
final String subnetId = subnet.getSubnetId();
if (subnet == null) {
final CreateSubnetResult createSubnetResult;
try {
final CreateSubnetRequest createRequest = new CreateSubnetRequest()
.withVpcId(vpc.getVpcId())
.withAvailabilityZone(az)
.withCidrBlock(ip4Slash28(vpc, az));
createSubnetResult = ec2.createSubnet(createRequest);
} catch (Exception e) {
die("createSubnets("+internalName+"/"+az+"): " + e, e);
return;
}

subnet = createSubnetResult.getSubnet();
ec2.createTags(new CreateTagsRequest()
.withResources(subnetId)
.withTags(new Tag(TAG_BUBBLE_CLASS, TAG_BUBBLE_CLASS_PACKER_SUBNET)));
}

final DescribeInternetGatewaysResult gatewaysResult = ec2.describeInternetGateways();
final InternetGateway gateway = gatewaysResult.getInternetGateways().stream()
.filter(g -> g.getAttachments().stream().anyMatch(a -> a.getVpcId().equals(vpc.getVpcId())))
.findFirst()
.or(() -> Optional.ofNullable(ec2.createInternetGateway().getInternetGateway()))
.get();
final String gatewayId = gateway.getInternetGatewayId();
if (gateway.getAttachments().stream().noneMatch(a -> a.getVpcId().equals(vpc.getVpcId()))) {
ec2.attachInternetGateway(new AttachInternetGatewayRequest()
.withVpcId(vpc.getVpcId())
.withInternetGatewayId(gatewayId));
}

final DescribeRouteTablesResult routes = ec2.describeRouteTables();
final RouteTable routeTable = routes.getRouteTables().stream().filter(rt -> rt.getVpcId().equals(vpc.getVpcId())).findFirst().orElse(null);
if (routeTable == null) {
die("createSubnets("+internalName+"/"+az+"): no route table found for vpc "+vpc.getVpcId());
return;
}
final List<RouteTableAssociation> associations = routeTable.getAssociations();
try {
// if (empty(associations) || associations.stream()
// .noneMatch(a -> a.getGatewayId() != null && a.getGatewayId().equals(gatewayId))) {
// ec2.associateRouteTable(new AssociateRouteTableRequest()
// .withGatewayId(gatewayId)
// .withRouteTableId(routeTable.getRouteTableId()));
// }
if (empty(associations) || associations.stream()
.noneMatch(a -> a.getSubnetId() != null && a.getSubnetId().equals(subnetId))) {
ec2.associateRouteTable(new AssociateRouteTableRequest()
.withSubnetId(subnetId)
.withRouteTableId(routeTable.getRouteTableId()));
}
if (routeTable.getRoutes().stream()
.noneMatch(r -> r.getDestinationCidrBlock() != null && r.getDestinationCidrBlock().equals(IP4_CIDR_ALL))) {
ec2.createRoute(new CreateRouteRequest()
.withDestinationCidrBlock(IP4_CIDR_ALL)
.withGatewayId(gatewayId)
.withRouteTableId(routeTable.getRouteTableId()));
}
if (routeTable.getRoutes().stream()
.noneMatch(r -> r.getDestinationIpv6CidrBlock() != null && r.getDestinationIpv6CidrBlock().equals(IP6_CIDR_ALL))) {
ec2.createRoute(new CreateRouteRequest()
.withDestinationIpv6CidrBlock(IP6_CIDR_ALL)
.withGatewayId(gatewayId)
.withRouteTableId(routeTable.getRouteTableId()));
}
} catch (Exception e) {
die("createSubnets("+internalName+"/"+az+"): error adding gateway route vpc "+vpc.getVpcId());
return;
}

subnets.put(az, subnet);
}
subnetsByRegion.put(internalName, subnets);
subnetCache.put(internalName, subnets);
}));
}
final AwaitResult awaitResult = awaitAll(futures, PARALLEL_TIMEOUT);
if (!awaitResult.allSucceeded()) {
return die("createSubnets: "+awaitResult.getFailures().values());
}
return subnetsByRegion;
}

private class listNodesHelper implements Callable<List<BubbleNode>> {
private String regionName;
private String ip4Slash28(Vpc vpc, String az) {
final char zoneId = az.toLowerCase().charAt(az.length()-1);
final int subnetNumber = zoneId - 'a';
final String[] parts = vpc.getCidrBlock().split("[./]");
final String subnet = parts[0] + "." + parts[1] + "." + parts[2] + "." + (subnetNumber * 16) + "/28";
return subnet;
}

public listNodesHelper(String regionName) {
this.regionName = regionName;
@Override public List<BubbleNode> listNodes() throws IOException {
final List<Future<?>> listNodeJobs = new ArrayList<>();
for (final AmazonEC2 ec2 : getEc2ClientMap().values()) {
listNodeJobs.add(getPerRegionExecutor().submit(new ListNodesHelper(ec2)));
}
final AwaitResult awaitResult = awaitAll(listNodeJobs, PARALLEL_TIMEOUT);
if (!awaitResult.allSucceeded()) {
return die("listNodes: error listing nodes: "+awaitResult.getFailures().values());
}
final List<BubbleNode> nodes = new ArrayList<>();
for (Object o : awaitResult.getSuccesses().values()) nodes.addAll((List<BubbleNode>) o);
return nodes;
}

@AllArgsConstructor
private class ListNodesHelper implements Callable<List<BubbleNode>> {
private AmazonEC2 ec2;

public List<BubbleNode> listNodesForSingleRegion(String regionName) {
AmazonEC2 ec2Client = getEC2Client(regionName);
@Override public List<BubbleNode> call() {
final List<BubbleNode> nodes = new ArrayList<>();
final DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest()
.withFilters(
new Filter("instance.group-id").withValues(config.getConfig("group")),
new Filter("instance.group-id").withValues(getSecurityGroup()),
new Filter("tag:" + TAG_CLOUD_UUID).withValues(cloud.getUuid()),
new Filter("instance-state-name").withValues("running", "pending")
);

final DescribeInstancesResult result = ec2Client.describeInstances(describeInstancesRequest);
final DescribeInstancesResult result = ec2.describeInstances(describeInstancesRequest);
if (result.getSdkHttpMetadata().getHttpStatusCode() == OK) {
for (Reservation reservation : result.getReservations()) {
for (Instance instance : reservation.getInstances()) {
@@ -131,50 +413,38 @@ public class AmazonEC2Driver extends ComputeServiceDriverBase {
}
}
} else {
return die("listNodesForRegion: error describe EC2 instances for region " + regionName
return die("list: error describe EC2 instances: "
+ result.getSdkHttpMetadata().getHttpStatusCode()
+ ": " + result.getSdkHttpMetadata().getAllHttpHeaders());
}
return nodes;
}

@Override public List<BubbleNode> call() throws Exception {
try {
return listNodesForSingleRegion(this.regionName);
} catch (Exception e) {
log.error("listNodesHelper.call: " + e, e);
throw e;
}
}
}

@Override public BubbleNode start(BubbleNode node) throws Exception {
final ComputeNodeSize size = config.getSize(node.getSize());
final AmazonEC2 ec2Client = getEC2Client(node.getRegion());

final DescribeSubnetsRequest describeSubnetsRequest = new DescribeSubnetsRequest()
.withFilters(new Filter().withName("state").withValues("available"));

final String subnetId = ec2Client.describeSubnets(describeSubnetsRequest).getSubnets().stream()
.filter(s -> s.getAvailableIpAddressCount() != 0)
.findAny().orElseThrow(() -> new NoSuchElementException("Subnet not found")).getSubnetId();
final AmazonEC2 ec2Client = getEc2Client(node.getRegion());
final PackerImage packerImage = getPackerImage(node);

final EbsBlockDevice ebs = null; // todo
final RunInstancesRequest runInstancesRequest = new RunInstancesRequest().withImageId(config.getConfig("imageId"))
.withInstanceType(size.getInternalName())
.withMinCount(MIN_COUNT)
.withMaxCount(MAX_COUNT)
.withImageId(packerImage.getId())
.withMinCount(1)
.withMaxCount(1)
.withKeyName(KEY_NAME_PREFIX+node.getUuid())
.withBlockDeviceMappings(new BlockDeviceMapping().withEbs(ebs))
.withNetworkInterfaces(new InstanceNetworkInterfaceSpecification()
.withAssociatePublicIpAddress(true)
.withDeviceIndex(0)
.withSubnetId(subnetId)
.withGroups(config.getConfig("group")));
.withIpv6AddressCount(1)
.withGroups(getSecurityGroup()));

final RunInstancesResult runInstancesResult = ec2Client.runInstances(runInstancesRequest);

if (runInstancesResult.getSdkHttpMetadata().getHttpStatusCode() != OK)
return die("start: error running instance: "
+ runInstancesResult.getSdkHttpMetadata().getAllHttpHeaders());
if (runInstancesResult.getSdkHttpMetadata().getHttpStatusCode() != OK) {
return die("start: error running instance");
}

final String instanceId = runInstancesResult.getReservation().getInstances().get(0).getInstanceId();

@@ -185,52 +455,47 @@ public class AmazonEC2Driver extends ComputeServiceDriverBase {
final DescribeInstancesResult result = ec2Client.describeInstances(
new DescribeInstancesRequest().withInstanceIds(instanceId));

if (result.getSdkHttpMetadata().getHttpStatusCode() == OK) {
for (final Reservation reservation : result.getReservations()) {
for (final Instance i : reservation.getInstances()) {
if (i.getInstanceId().equals(instanceId)) {
final String ip4 = i.getPrivateIpAddress();
if (ip4 != null && ip4.length() > 0 && !ip4.equals("0.0.0.0")) {
node.setIp4(ip4);
nodeDAO.update(node);
}
final String ip6 = i.getPublicIpAddress();
if (ip6 != null && ip6.length() > 0) {
node.setIp6(ip6);
nodeDAO.update(node);
}
break;
if (result.getSdkHttpMetadata().getHttpStatusCode() != OK) {
return die("start: error describing instance");
}
for (final Reservation reservation : result.getReservations()) {
for (final Instance i : reservation.getInstances()) {
if (i.getInstanceId().equals(instanceId)) {
final String ip4 = i.getPublicIpAddress();
if (ip4 != null && ip4.length() > 0 && !ip4.equals("0.0.0.0")) {
node.setIp4(ip4);
nodeDAO.update(node);
}
final String ip6 = i.getPublicIpAddress();
if (ip6 != null && ip6.length() > 0) {
node.setIp6(ip6);
nodeDAO.update(node);
}
break;
}
}
} else {
log.error("start: error describe instance, status: " + result.getSdkHttpMetadata().getHttpStatusCode()
+ result.getSdkHttpMetadata().getAllHttpHeaders());
}

// Setting up the tags for the instance
try {
ec2Client.createTags(new CreateTagsRequest()
.withResources(instanceId)
.withTags(new Tag(TAG_NODE_UUID, node.getUuid()),
new Tag(TAG_CLOUD_UUID, cloud.getUuid())));
} catch (AmazonServiceException e) {
log.warn("start: error creating tags: " + e.getErrorMessage() + e.getErrorCode());
}

// Setting up the tag for the test instance
final List<Tag> tags = new ArrayList<>();
tags.add(new Tag(TAG_NODE_UUID, node.getUuid()));
tags.add(new Tag(TAG_CLOUD_UUID, cloud.getUuid()));
if (configuration.testMode()) {
final String testTag = configuration.getEnvironment().get("TEST_TAG_CLOUD");
if (empty(testTag)) return die("TEST_TAG_CLOUD env var is not defined or is empty");

try {
ec2Client.createTags(new CreateTagsRequest()
.withResources(instanceId)
.withTags(new Tag(TAG_TEST, configuration.getEnvironment().get("TEST_TAG_CLOUD"))));
} catch (AmazonServiceException e) {
log.warn("start: error creating test tag: " + e.getErrorMessage() + e.getErrorCode());
tags.add(new Tag(TAG_TEST, testTag));
}
try {
final CreateTagsResult tagsResult = ec2Client.createTags(new CreateTagsRequest()
.withResources(instanceId)
.withTags(tags));
if (tagsResult.getSdkHttpMetadata().getHttpStatusCode() != OK) {
return die("start: error setting tags on instance");
}
} catch (AmazonServiceException e) {
return die("start: error setting tags on instance: "+shortError(e), e);
}

return node;
}

@@ -246,8 +511,7 @@ public class AmazonEC2Driver extends ComputeServiceDriverBase {
final StopInstancesRequest stopInstancesRequest = new StopInstancesRequest()
.withInstanceIds(instanceID);

final AmazonEC2 ec2Client = getEC2Client(node.getRegion());

final AmazonEC2 ec2Client = getEc2Client(node.getRegion());
try {
ec2Client.stopInstances(stopInstancesRequest);
} catch (AmazonServiceException e) {
@@ -274,7 +538,4 @@ public class AmazonEC2Driver extends ComputeServiceDriverBase {
return node;
}

// todo
@Override public List<PackerImage> getPackerImages() { return notSupported("getPackerImages"); }

}

+ 2
- 1
bubble-server/src/main/java/bubble/cloud/compute/local/LocalComputeDriver.java Zobrazit soubor

@@ -27,6 +27,7 @@ public class LocalComputeDriver extends CloudServiceDriverBase<ComputeConfig> im
@Override public BubbleNode stop(BubbleNode node) throws Exception { return notSupported("stop"); }
@Override public BubbleNode status(BubbleNode node) throws Exception { return notSupported("status"); }

@Override public List<PackerImage> getPackerImages() { return notSupported("getPackerImages"); }
@Override public List<PackerImage> getAllPackerImages() { return notSupported("getPackerImages"); }
@Override public List<PackerImage> getPackerImagesForRegion(String region) { return notSupported("getPackerImagesForRegion"); }

}

+ 2
- 1
bubble-server/src/main/java/bubble/cloud/compute/mock/MockComputeDriver.java Zobrazit soubor

@@ -62,6 +62,7 @@ public class MockComputeDriver extends ComputeServiceDriverBase {
return node;
}

@Override public List<PackerImage> getPackerImages() { return Collections.emptyList(); }
@Override public List<PackerImage> getAllPackerImages() { return Collections.emptyList(); }
@Override public List<PackerImage> getPackerImagesForRegion(String region) { return Collections.emptyList(); }

}

+ 3
- 1
bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrComputeNodeSizeParser.java Zobrazit soubor

@@ -1,5 +1,6 @@
package bubble.cloud.compute.vultr;

import bubble.cloud.compute.ComputeDiskType;
import bubble.cloud.compute.ComputeNodeSize;
import bubble.cloud.compute.ListResourceParser;
import com.fasterxml.jackson.databind.JsonNode;
@@ -19,7 +20,8 @@ public class VultrComputeNodeSizeParser extends ListResourceParser<ComputeNodeSi
.setInternalName(item.get("name").textValue())
.setVcpu(item.get("vcpu_count").asInt())
.setMemoryMB(item.get("ram").asInt())
.setSsdGB(item.get("disk").asInt())
.setDiskGB(item.get("disk").asInt())
.setDiskType(ComputeDiskType.ssd)
.setTransferGB(item.get("bandwidth_gb").asInt());
}



+ 19
- 38
bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java Zobrazit soubor

@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

import static bubble.model.cloud.BubbleNode.TAG_INSTANCE_ID;
@@ -56,8 +55,6 @@ public class VultrDriver extends ComputeServiceDriverBase {
public static final String VULTR_V4_IP = "main_ip";
public static final String VULTR_V6_IP = "v6_main_ip";

public static final String CREATE_SSH_KEY_URL = VULTR_API_BASE + "sshkey/create";
public static final String DESTROY_SSH_KEY_URL = VULTR_API_BASE + "sshkey/destroy";
public static final String CREATE_SERVER_URL = VULTR_API_BASE + "server/create";
public static final String DESTROY_SERVER_URL = VULTR_API_BASE + "server/destroy";
public static final String LIST_SERVERS_URL = VULTR_API_BASE + "server/list";
@@ -73,7 +70,7 @@ public class VultrDriver extends ComputeServiceDriverBase {
.filter(i -> i.getName().equals("Snapshot"))
.findFirst()
.orElse(null);
return snapshot == null ? die("initSnapshotOsId: no snapshot OS found") : snapshot.getId().intValue();
return snapshot == null ? die("initSnapshotOsId: no snapshot OS found") : Integer.parseInt(snapshot.getId());
}

public static final long SERVER_START_INITIAL_INTERVAL = SECONDS.toMillis(30);
@@ -108,8 +105,6 @@ public class VultrDriver extends ComputeServiceDriverBase {
super.postSetup();
}

public static final long PACKER_TIMEOUT = MINUTES.toMillis(60);

@Override public BubbleNode start(BubbleNode node) throws Exception {

final CloudRegion region = config.getRegion(node.getRegion());
@@ -121,20 +116,7 @@ public class VultrDriver extends ComputeServiceDriverBase {
final Long planId = getSize(size.getType()).getId();
if (planId == null) return die("start: plan not found: "+size.getInternalName());

PackerImage packerImage = getPackerImage(node.getInstallType());
if (packerImage == null) {
final AtomicReference<List<PackerImage>> imagesRef = new AtomicReference<>();
packerService.writePackerImages(cloud, node.getInstallType(), imagesRef);
long start = now();
while (imagesRef.get() == null && now() - start < PACKER_TIMEOUT) {
sleep(SECONDS.toMillis(1), "start: waiting for packer image creation");
}
if (imagesRef.get() == null) return die("start: timeout creating packer image");
packerImage = getPackerImage(node.getInstallType());
if (packerImage == null) {
return die("start: error creating packer image");
}
}
final PackerImage packerImage = getPackerImage(node);

// prepare to create server
final String data = "DCID=" + regionId +
@@ -392,56 +374,55 @@ public class VultrDriver extends ComputeServiceDriverBase {
}
}

@Override public List<PackerImage> getPackerImages() {
final List<PackerImage> images = loadCloudResources(SNAPSHOT_URL, new VultrPackerImageParser(configuration.getVersion(), packerService.getPackerPublicKeyHash(), configuration.getJarSha()));
return images == null ? Collections.emptyList() : images;
}
@Override public List<PackerImage> getAllPackerImages() { return getPackerImages(); }
@Override public List<PackerImage> getPackerImagesForRegion(String region) { return getPackerImages(); }

public PackerImage getPackerImage(AnsibleInstallType installType) {
final List<PackerImage> images = getPackerImages();
return images == null ? null : images.stream()
.filter(i -> i.getName().contains("_"+installType.name()+"_"))
.findFirst()
.orElse(null);
public List<PackerImage> getPackerImages () {
final List<PackerImage> images = loadCloudResources(SNAPSHOT_URL, new VultrPackerImageParser(configuration.getVersion(), packerService.getPackerPublicKeyHash()));
return images == null ? Collections.emptyList() : images;
}

public static final long SNAPSHOT_TIMEOUT = MINUTES.toMillis(60);
@Override public List<PackerImage> finalizeIncompletePackerRun(CommandResult commandResult, AnsibleInstallType installType, String jarSha) {
@Override public List<PackerImage> finalizeIncompletePackerRun(CommandResult commandResult, AnsibleInstallType installType) {
if (!commandResult.getStdout().contains("Waiting 300s for snapshot")
|| !commandResult.getStdout().contains("Error waiting for snapshot")
|| !commandResult.getStdout().contains("Unable to destroy server: Unable to remove VM: Server is currently locked")) {
stopImageServer(installType, jarSha);
stopImageServer(installType);
return null;
}

// wait longer for the snapshot...
final String keyHash = packerService.getPackerPublicKeyHash();
final long start = now();
PackerImage snapshot = null;
while (now() - start < SNAPSHOT_TIMEOUT) {
snapshot = getPackerImages().stream()
.filter(i -> i.getName().contains("_"+installType.name()+"_") && i.getName().endsWith(jarSha))
.filter(i -> i.getName().contains("_"+installType.name()+"_") && i.getName().contains(keyHash))
.findFirst()
.orElse(null);
if (snapshot != null) break;
sleep(SECONDS.toMillis(20), "finalizeIncompletePackerRun: waiting for snapshot: "+jarSha);
sleep(SECONDS.toMillis(20), "finalizeIncompletePackerRun: waiting for snapshot: "+keyHash);
}
if (snapshot == null) {
log.error("finalizeIncompletePackerRun: timeout waiting for snapshot");
}

if (!stopImageServer(installType, jarSha)) return null;
if (!stopImageServer(installType)) return null;
if (snapshot == null) return null;

return new SingletonList<>(snapshot);
}

public boolean stopImageServer(AnsibleInstallType installType, String jarSha) {
// find the server
public boolean stopImageServer(AnsibleInstallType installType) {

final String keyHash = packerService.getPackerPublicKeyHash();
final List<BubbleNode> servers;

// find the server(s)
try {
servers = listNodes(server -> {
final String tag = server.has("tag") ? server.get("tag").textValue() : null;
return tag != null && tag.contains("_"+installType.name()+"_") && tag.endsWith(jarSha);
return tag != null && tag.contains("_"+installType.name()+"_") && tag.contains(keyHash);
});
} catch (IOException e) {
log.error("finalizeIncompletePackerRun: error listing servers: "+shortError(e), e);


+ 2
- 2
bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrOsImageParser.java Zobrazit soubor

@@ -12,8 +12,8 @@ public class VultrOsImageParser extends ListResourceParser<OsImage> {
if (!item.has("OSID")) return die("parse: OSID not found");
if (!item.has("name")) return die("parse: name not found");
return new OsImage()
.setId(item.get("OSID").asLong())
.setName(item.get("name").textValue());
.setId(item.get("OSID").asText())
.setName(item.get("name").asText());
}

}

+ 2
- 2
bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrPackerImageParser.java Zobrazit soubor

@@ -8,8 +8,8 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.die;

public class VultrPackerImageParser extends PackerImageParserBase {

public VultrPackerImageParser(String bubbleVersion, String keyHash, String jarSha) {
super(bubbleVersion, keyHash, jarSha);
public VultrPackerImageParser(String bubbleVersion, String keyHash) {
super(bubbleVersion, keyHash);
}

@Override public PackerImage parse(JsonNode item) {


+ 7
- 13
bubble-server/src/main/java/bubble/cloud/shared/aws/BubbleAwsCredentialsProvider.java Zobrazit soubor

@@ -15,23 +15,17 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.empty;

public class BubbleAwsCredentialsProvider implements AWSCredentialsProvider {

public static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID";
public static final String AWS_SECRET_KEY = "AWS_SECRET_KEY";

private final CloudService cloud;
private final CloudCredentials awsCredentials;
private String accessKeyParam = "AWS_ACCESS_KEY_ID";
private String secretKeyParam = "AWS_SECRET_KEY";

public BubbleAwsCredentialsProvider (CloudService cloud, CloudCredentials credentials) {
this.cloud = cloud;
this.awsCredentials = credentials;
}

public BubbleAwsCredentialsProvider (CloudService cloud, CloudCredentials credentials, String accessKeyParam, String secretKeyParam) {
this.cloud = cloud;
this.awsCredentials = credentials;
this.accessKeyParam = accessKeyParam;
this.secretKeyParam = secretKeyParam;
}

@Getter(lazy=true) private final AWSCredentials credentials = new BubbleAwsCredentials();

@Override public void refresh() {}
@@ -39,17 +33,17 @@ public class BubbleAwsCredentialsProvider implements AWSCredentialsProvider {
private class BubbleAwsCredentials implements AWSCredentials {

@Override public String getAWSAccessKeyId() {
final String key = awsCredentials.getParam(accessKeyParam);
final String key = awsCredentials.getParam(AWS_ACCESS_KEY_ID);
return empty(key)
? die("getAWSAccessKeyId: no accessKeyParam ("+accessKeyParam+") defined in credentials for cloud: "+cloud.getUuid())
? die("getAWSAccessKeyId: no AWS_ACCESS_KEY_ID defined in credentials for cloud: "+cloud.getUuid())
: key;

}

@Override public String getAWSSecretKey() {
final String key = awsCredentials.getParam(secretKeyParam);
final String key = awsCredentials.getParam(AWS_SECRET_KEY);
return empty(key)
? die("getAWSSecretKey: no secretKeyParam ("+secretKeyParam+") defined in credentials for cloud: "+cloud.getUuid())
? die("getAWSSecretKey: no AWS_SECRET_KEY defined in credentials for cloud: "+cloud.getUuid())
: key;
}
}


+ 3
- 3
bubble-server/src/main/java/bubble/main/http/BubbleHttpEntityOptions.java Zobrazit soubor

@@ -14,9 +14,9 @@ import org.kohsuke.args4j.Option;
import java.io.InputStream;

import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON;
import static org.cobbzilla.util.http.HttpContentTypes.MULTIPART_FORM_DATA;
import static org.cobbzilla.util.http.HttpContentTypes.*;
import static org.cobbzilla.util.json.JsonUtil.*;
import static org.cobbzilla.util.string.StringUtil.UTF8cs;

@Slf4j
public class BubbleHttpEntityOptions extends BubbleHttpOptions {
@@ -57,6 +57,6 @@ public class BubbleHttpEntityOptions extends BubbleHttpOptions {
return APPLICATION_JSON;
}

public ContentType contentType() { return ContentType.create(getContentType()); }
public ContentType contentType() { return ContentType.create(getContentType(), UTF8cs); }

}

+ 1
- 1
bubble-server/src/main/java/bubble/resources/cloud/ComputePackerResource.java Zobrazit soubor

@@ -36,7 +36,7 @@ public class ComputePackerResource {
public Response listImages(@Context Request req,
@Context ContainerRequest ctx) {
final ComputeServiceDriver driver = cloud.getComputeDriver(configuration);
return ok(driver.getPackerImages());
return ok(driver.getAllPackerImages());
}

@PUT @Path("/{type}")


+ 18
- 17
bubble-server/src/main/java/bubble/service/packer/PackerJob.java Zobrazit soubor

@@ -2,10 +2,7 @@ package bubble.service.packer;

import bubble.cloud.CloudRegion;
import bubble.cloud.CloudRegionRelative;
import bubble.cloud.compute.ComputeConfig;
import bubble.cloud.compute.ComputeServiceDriver;
import bubble.cloud.compute.PackerConfig;
import bubble.cloud.compute.PackerImage;
import bubble.cloud.compute.*;
import bubble.cloud.geoLocation.GeoLocation;
import bubble.dao.account.AccountDAO;
import bubble.model.account.Account;
@@ -25,6 +22,7 @@ import org.cobbzilla.util.io.TempDir;
import org.cobbzilla.util.system.Command;
import org.cobbzilla.util.system.CommandResult;
import org.cobbzilla.util.system.CommandShell;
import org.cobbzilla.util.time.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
@@ -36,14 +34,15 @@ import java.util.stream.Collectors;
import static bubble.ApiConstants.copyScripts;
import static bubble.model.cloud.RegionalServiceDriver.findClosestRegions;
import static bubble.service.packer.PackerService.*;
import static org.cobbzilla.util.daemon.ZillaRuntime.die;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.io.FileUtil.*;
import static org.cobbzilla.util.io.StreamUtil.copyClasspathDirectory;
import static org.cobbzilla.util.io.StreamUtil.stream2string;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.util.network.NetworkUtil.getExternalIp;
import static org.cobbzilla.util.string.StringUtil.truncate;
import static org.cobbzilla.util.system.CommandShell.hostname;
import static org.cobbzilla.util.time.TimeUtil.DATE_FORMAT_YYYYMMDDHHMMSS;

@Slf4j
public class PackerJob implements Callable<List<PackerImage>> {
@@ -54,15 +53,15 @@ public class PackerJob implements Callable<List<PackerImage>> {
public static final String PACKER_IMAGE_PREFIX = "packer_bubble_";

public static final String INSTALL_TYPE_VAR = "@@TYPE@@";
public static final String BUBBLE_VERSION_VAR = "@@BUBBLE_VERSION@@";
public static final String SAGE_HOST_VAR = "@@SAGE_HOST@@";
public static final String PACKER_KEY_VAR = "@@PACKER_KEY_HASH@@";
public static final String JAR_SHA_VAR = "@@JAR_SHA256@@";
public static final String BUBBLE_VERSION_VAR = "@@BUBBLE_VERSION@@";
public static final String TIMESTAMP_VAR = "@@TIMESTAMP@@";
public static final String PACKER_IMAGE_NAME_TEMPLATE = PACKER_IMAGE_PREFIX + INSTALL_TYPE_VAR
+ "_" + SAGE_HOST_VAR
+ "_" + PACKER_KEY_VAR
+ "_" + BUBBLE_VERSION_VAR
+ "_" + JAR_SHA_VAR;
+ "_" + TIMESTAMP_VAR;

public static final String VARIABLES_VAR = "packerVariables";
public static final String BUILD_REGION_VAR = "buildRegion";
@@ -103,8 +102,6 @@ public class PackerJob implements Callable<List<PackerImage>> {
}

public List<PackerImage> _call() throws Exception {
final String jarSha = configuration.getJarSha();

final ComputeConfig computeConfig = json(cloud.getDriverConfigJson(), ComputeConfig.class);
final ComputeServiceDriver computeDriver = cloud.getComputeDriver(configuration);
final PackerConfig packerConfig = computeConfig.getPacker();
@@ -146,7 +143,7 @@ public class PackerJob implements Callable<List<PackerImage>> {
copyScripts(bubbleFilesDir);

// check to see if we have packer images for all regions
final List<PackerImage> existingImages = computeDriver.getPackerImages();
final List<PackerImage> existingImages = computeDriver.getAllPackerImages();
if (!empty(existingImages)) {
final List<PackerImage> existingForInstallType = existingImages.stream()
.filter(i -> i.getName().startsWith(PACKER_IMAGE_PREFIX+installType.name()))
@@ -190,10 +187,11 @@ public class PackerJob implements Callable<List<PackerImage>> {

final String imageName = PACKER_IMAGE_NAME_TEMPLATE
.replace(INSTALL_TYPE_VAR, installType.name())
.replace(BUBBLE_VERSION_VAR, configuration.getVersion())
.replace(SAGE_HOST_VAR, truncate(hostname(), 19))
.replace(PACKER_KEY_VAR, packerService.getPackerPublicKeyHash())
.replace(SAGE_HOST_VAR, hostname())
.replace(JAR_SHA_VAR, jarSha);
.replace(BUBBLE_VERSION_VAR, configuration.getVersion())
.replace(TIMESTAMP_VAR, TimeUtil.format(now(), DATE_FORMAT_YYYYMMDDHHMMSS));
if (imageName.length() > 128) return die("imageName.length > 128: "+imageName); // sanity check
ctx.put(PACKER_IMAGE_NAME_VAR, imageName);

final String packerConfigTemplate = stream2string(PACKER_TEMPLATE);
@@ -204,6 +202,8 @@ public class PackerJob implements Callable<List<PackerImage>> {
if (packerConfig.iterateRegions()) {
for (CloudRegion region : computeDriver.getRegions()) {
ctx.put("region", region);
final Map<String, Object> perRegionCtx = computeDriver.getPackerRegionContext(region);
if (perRegionCtx != null) ctx.putAll(perRegionCtx);
builderJsons.add(generateBuilder(packerConfig, ctx));
}
} else {
@@ -221,9 +221,10 @@ public class PackerJob implements Callable<List<PackerImage>> {

// run packer, return handle to running packer
log.info("running packer for " + installType + "...");
final int packerParallelBuilds = computeDriver.getPackerParallelBuilds();
final CommandResult commandResult = CommandShell.exec(new Command(new CommandLine(PACKER_BINARY)
.addArgument("build")
.addArgument("-parallel-builds=2")
.addArgument("-parallel-builds="+packerParallelBuilds)
.addArgument("-color=false")
.addArgument("packer.json"))
.setDir(tempDir)
@@ -244,7 +245,7 @@ public class PackerJob implements Callable<List<PackerImage>> {
images.addAll(Arrays.stream(builds).map(b -> b.toPackerImage(imageName)).collect(Collectors.toList()));

} else {
final List<PackerImage> finalizedImages = computeDriver.finalizeIncompletePackerRun(commandResult, installType, jarSha);
final List<PackerImage> finalizedImages = computeDriver.finalizeIncompletePackerRun(commandResult, installType);
if (empty(finalizedImages)) {
return die("Error executing packer: exit status " + commandResult.getExitStatus());
}


+ 2
- 2
bubble-server/src/main/resources/ansible/playbook.yml.hbs Zobrazit soubor

@@ -1,8 +1,8 @@
---

- name: Create new bubble node
- name: Configure new bubble node
hosts: bubble
remote_user: root
become: yes
roles:
{{#each roles}} - {{this}}
{{/each}}

+ 11
- 0
bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties Zobrazit soubor

@@ -400,6 +400,17 @@ driver_config_description_sizes_bubble.cloud.compute.digitalocean.DigitalOceanDr
driver_config_config_bubble.cloud.compute.digitalocean.DigitalOceanDriver=Additional Config JSON
driver_config_description_config_bubble.cloud.compute.digitalocean.DigitalOceanDriver=Array of configuration options in the form [ {name: "...", "value": ...} ] There must be one option named 'os' that contains the Operating System image to use when launching Bubbles.

driver_bubble.cloud.compute.ec2.AmazonEC2Driver=Amazon EC2
description_bubble.cloud.compute.ec2.AmazonEC2Driver=Use the <a href="https://aws.amazon.com/ec2/">Elastic Compute Cloud (EC2)</a> from Amazon AWS to launch new Bubbles
driver_credential_AWS_ACCESS_KEY_ID.bubble.cloud.compute.ec2.AmazonEC2Driver=Amazon Access Key
driver_credential_AWS_SECRET_KEY.bubble.cloud.compute.ec2.AmazonEC2Driver=Amazon Secret Key
driver_config_regions_bubble.cloud.compute.ec2.AmazonEC2Driver=Regions JSON
driver_config_description_regions_bubble.cloud.compute.ec2.AmazonEC2Driver=Array of bubble.cloud.CloudRegion objects. Determines which regions are supported, and defines latitude/longitude to enable geo-aware decisions.
driver_config_sizes_bubble.cloud.compute.ec2.AmazonEC2Driver=Instance Sizes JSON
driver_config_description_sizes_bubble.cloud.compute.ec2.AmazonEC2Driver=Array of bubble.cloud.compute.ComputeNodeSize objects. Determines which instance sizes are supported.
driver_config_config_bubble.cloud.compute.ec2.AmazonEC2Driver=Additional Config JSON
driver_config_description_config_bubble.cloud.compute.ec2.AmazonEC2Driver=Array of configuration options in the form [ {name: "...", "value": ...} ] There must be one option named 'os' that contains the Operating System image to use when launching Bubbles.

driver_bubble.cloud.dns.godaddy.GoDaddyDnsDriver=GoDaddy DNS
description_bubble.cloud.dns.godaddy.GoDaddyDnsDriver=Use <a href="https://www.godaddy.com/">GoDaddy</a> to manage DNS records for Bubbles
driver_credential_GODADDY_API_KEY_bubble.cloud.dns.godaddy.GoDaddyDnsDriver=GoDaddy API Key


+ 77
- 10
bubble-server/src/main/resources/models/defaults/cloudService.json Zobrazit soubor

@@ -300,23 +300,90 @@
},

{
"name": "AmazonEC2Driver",
"name": "AmazonEC2Compute",
"type": "compute",
"driverClass": "bubble.cloud.compute.ec2.AmazonEC2Driver",
"driverConfig": {
"regions": [{
"name": "US_WEST_2", "description": "US West (Oregon)",
"location": {"country": "US", "lat": "45.5272", "lon": "122.9361"}
"name": "Amazon - N. Virginia", "description": "US East 1 (N. Virginia)", "internalName": "us-east-1",
"location": {"city": "Arlington", "region": "VA", "country": "US", "lat": "38.880278", "lon": "-77.108333"}
}, {
"name": "Amazon - Ohio", "description": "US East 2 (Ohio)", "internalName": "us-east-2",
"location": {"region": "OH", "country": "US", "lat": "40.3416167", "lon": "-84.9180579"}
}, {
"name": "Amazon - N. California", "description": "US West 1 (N. California)", "internalName": "us-west-1",
"location": {"city": "San Jose", "country": "US", "region": "CA", "lat": "37.333333", "lon": "-121.9"}
}, {
"name": "Amazon - Oregon", "description": "US West 2 (Oregon)", "internalName": "us-west-2",
"location": {"city": "Hermiston", "region": "OR", "country": "US", "lat": "45.841111", "lon": "-119.291667"}
}, {
"name": "Amazon - Canada", "description": "Canada (Central)", "internalName": "ca-central-1",
"location": {"region": "QC", "country": "CA", "lat": "46.813889", "lon": "-71.208056"}
}, {
"name": "Amazon - Stockholm", "description": "EU (Stockholm)", "internalName": "eu-north-1",
"location": {"city": "Stockholm", "region": "Södermanland", "country": "SE", "lat": "59.329444", "lon": "18.068611"}
}, {
"name": "Amazon - Ireland", "description": "EU (Ireland)", "internalName": "eu-west-1",
"location": {"city": "Dublin", "region": "Leinster", "country": "IE", "lat": "53.35", "lon": "-6.266667"}
}, {
"name": "Amazon - London", "description": "EU (London)", "internalName": "eu-west-2",
"location": {"city": "London", "country": "GB", "region": "London", "lat": "51.507222", "lon": "-0.1275"}
}, {
"name": "Amazon - Milan", "description": "EU (Milan)", "internalName": "eu-west-3",
"location": {"city": "Milan", "country": "IT", "region": "Lombardy", "lat": "45.466944", "lon": "9.19"}
}, {
"name": "Amazon - Frankfurt", "description": "EU (Frankfurt)", "internalName": "eu-central-1",
"location": {"city": "Frankfurt", "country": "DE", "region": "Hesse", "lat": "50.116667", "lon": "8.683333"}
}, {
"name": "Amazon - Tokyo", "description": "Asia Pacific (Tokyo)", "internalName": "ap-northeast-1",
"location": {"city": "Tokyo", "country": "JP", "region": "Kantō", "lat": "35.689722", "lon": "139.692222"}
}, {
"name": "Amazon - Seoul", "description": "Asia Pacific (Seoul)", "internalName": "ap-northeast-2",
"location": {"city": "Seoul", "country": "KR", "region": "Sudogwon", "lat": "37.566667", "lon": "126.966667"}
}, {
"name": "Amazon - Singapore", "description": "Asia Pacific (Singapore)", "internalName": "ap-southeast-1",
"location": {"city": "Singapore", "country": "SG", "region": "Singapore", "lat": "1.283333", "lon": "103.833333"}
}, {
"name": "Amazon - Sydney", "description": "Asia Pacific (Sydney)", "internalName": "ap-southeast-2",
"location": {"city": "Sydney", "country": "AU", "region": "NSW", "lat": "-33.865", "lon": "151.209444"}
}, {
"name": "Amazon - Mumbai", "description": "Asia Pacific (Mumbai)", "internalName": "ap-south-1",
"location": {"city": "Mumbai", "country": "IN", "region": "Konkan", "lat": "18.975", "lon": "72.825833"}
}, {
"name": "Amazon - São Paulo", "description": "South America (São Paulo)", "internalName": "sa-east-1",
"location": {"city": "São Paulo", "country": "BR", "region": "São Paulo", "lat": "-23.55", "lon": "-46.633333"}
}],
"sizes": [
{"name": "small", "type": "small", "internalName": "t2.micro", "vcpu": 1, "memoryMB": 1024, "ssdGB": 0},
{"name": "medium", "type": "medium", "internalName": "t2.small", "vcpu": 1, "memoryMB": 2048, "ssdGB": 0},
{"name": "large", "type": "large", "internalName": "t2.medium", "vcpu": 2, "memoryMB": 4096, "ssdGB": 80}
{"name": "small", "type": "small", "internalName": "t2.micro", "vcpu": 1, "memoryMB": 1024, "diskGB": 10, "diskType": "ebs_magnetic"},
{"name": "medium", "type": "medium", "internalName": "t2.small", "vcpu": 1, "memoryMB": 2048, "diskGB": 20, "diskType": "ebs_magnetic"},
{"name": "large", "type": "large", "internalName": "t2.medium", "vcpu": 2, "memoryMB": 4096, "diskGB": 40, "diskType": "ebs_magnetic"}
],
"os": "ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-????????",
"config": [
{"name": "imageId", "value": "{{AWS_EC2_IMAGE_ID}}"},
{"name": "group", "value": "{{EC2_SECURITY_GROUP}}"}
]
{"name": "securityGroup", "value": "{{AWS_EC2_SECURITY_GROUP}}"}
],
"packer": {
"vars": [
{"name": "AWS_ACCESS_KEY_ID", "value": "[[credentials.AWS_ACCESS_KEY_ID]]"},
{"name": "AWS_SECRET_KEY", "value": "[[credentials.AWS_SECRET_KEY]]"}
],
"iterateRegions": true,
"builder": {
"type": "amazon-ebs",
"name": "amazon-ebs-<<region.internalName>>",
"access_key": "[[user `AWS_ACCESS_KEY_ID`]]",
"secret_key": "[[user `AWS_SECRET_KEY`]]",
"region": "<<region.internalName>>",
"ami_name": "<<packerImageName>>",
"ssh_username": "ubuntu",
"source_ami": "<<imageForRegion.id>>",
"instance_type": "<<sizes.small.internalName>>",
"vpc_id": "<<vpcForRegion.vpcId>>",
"availability_zone": "<<availabilityZoneForRegion>>",
"subnet_id": "<<subnetForRegion.subnetId>>",
"associate_public_ip_address": true
}
}
},
"credentials": {
"params": [
@@ -326,4 +393,4 @@
},
"template": true
}
]
]

+ 2
- 2
bubble-server/src/main/resources/packer/packer-playbook.yml.hbs Zobrazit soubor

@@ -1,8 +1,8 @@
---

- name: Create new bubble sage node
- name: Create new bubble [[installType]]
hosts: bubble
remote_user: root
become: yes
vars:
install_type: [[installType]]
roles:


+ 3
- 3
bubble-server/src/main/resources/packer/packer.json.hbs Zobrazit soubor

@@ -12,9 +12,9 @@
"type": "shell",
"inline": [
"sleep 30",
"sudo apt-get -y update",
"sudo apt-get -y upgrade",
"sudo apt-get -y install python3 python3-pip virtualenv",
"sudo bash -c 'DEBIAN_FRONTEND=noninteractive apt-get -y update'",
"sudo bash -c 'DEBIAN_FRONTEND=noninteractive apt-get -y upgrade'",
"sudo bash -c 'DEBIAN_FRONTEND=noninteractive apt-get -y install python3 python3-pip virtualenv'",
"sudo pip3 install setuptools psycopg2-binary ansible"
]
},


+ 2
- 2
bubble-server/src/main/resources/packer/roles/algo/files/config.cfg.hbs Zobrazit soubor

@@ -26,7 +26,7 @@ strongswan_log_level: 2

# rightsourceip for ipsec
# ipv4
strongswan_network: 10.19.48.0/24
strongswan_network: 172.16.0.0/16
# ipv6
strongswan_network_ipv6: 'fd9d:bc11:4020::/48'

@@ -42,7 +42,7 @@ wireguard_port: 51820
wireguard_PersistentKeepalive: 0

# WireGuard network configuration
wireguard_network_ipv4: 10.19.49.0/24
wireguard_network_ipv4: 172.16.1.0/16
wireguard_network_ipv6: fd9d:bc11:4021::/48

# Reduce the MTU of the VPN tunnel


Načítá se…
Zrušit
Uložit