diff --git a/bubble-server/src/main/java/bubble/ApiConstants.java b/bubble-server/src/main/java/bubble/ApiConstants.java index 2b4d04b7..f3281e7e 100644 --- a/bubble-server/src/main/java/bubble/ApiConstants.java +++ b/bubble-server/src/main/java/bubble/ApiConstants.java @@ -44,6 +44,10 @@ public class ApiConstants { public static final String[] ROLES_SAGE = {"common", "nginx", "bubble", "bubble_finalizer"}; public static final String[] ROLES_NODE = {"common", "nginx", "algo", "mitmproxy", "bubble", "bubble_finalizer"}; + public static final String PACKER_IMAGE_TAG = "packer_bubble"; + public static final String PACKER_IMAGE_TAG_SAGE = "packer_bubble_sage"; + public static final String PACKER_IMAGE_TAG_NODE = "packer_bubble_node"; + private static final AtomicReference bubbleDefaultDomain = new AtomicReference<>(); public static final ObjectMapper DB_JSON_MAPPER = COMPACT_MAPPER; @@ -166,6 +170,7 @@ public class ApiConstants { public static final String EP_RECEIVED_NOTIFICATIONS = "/notifications/inbox"; public static final String EP_REFERRAL_CODES = "/referralCodes"; public static final String EP_STORAGE = "/storage"; + public static final String EP_PACKER = "/packer"; public static final String EP_DNS = "/dns"; public static final String EP_BACKUPS = "/backups"; public static final String EP_FIND_DNS = "/find"; diff --git a/bubble-server/src/main/java/bubble/cloud/CloudRegion.java b/bubble-server/src/main/java/bubble/cloud/CloudRegion.java index 48b1b2b4..15ad8e1d 100644 --- a/bubble-server/src/main/java/bubble/cloud/CloudRegion.java +++ b/bubble-server/src/main/java/bubble/cloud/CloudRegion.java @@ -14,6 +14,8 @@ import static java.util.UUID.randomUUID; @Accessors(chain=true) public class CloudRegion { + public static final CloudRegion[] EMPTY_REGIONS = new CloudRegion[0]; + @Getter @Setter private String uuid = randomUUID().toString(); @Getter @Setter private String cloud; diff --git a/bubble-server/src/main/java/bubble/cloud/NoopCloud.java b/bubble-server/src/main/java/bubble/cloud/NoopCloud.java index 6f0abfdf..68416b29 100644 --- a/bubble-server/src/main/java/bubble/cloud/NoopCloud.java +++ b/bubble-server/src/main/java/bubble/cloud/NoopCloud.java @@ -9,6 +9,7 @@ import bubble.cloud.auth.RenderedMessage; import bubble.cloud.compute.ComputeNodeSize; import bubble.cloud.compute.ComputeNodeSizeType; import bubble.cloud.compute.ComputeServiceDriver; +import bubble.cloud.compute.PackerImage; import bubble.cloud.dns.DnsServiceDriver; import bubble.cloud.email.EmailServiceDriver; import bubble.cloud.email.RenderedEmail; @@ -38,6 +39,7 @@ 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; @Slf4j @@ -69,6 +71,9 @@ public class NoopCloud implements return false; } + @Override public List getPackerImages() { return Collections.emptyList(); } + @Override public List writePackerImages() { return Collections.emptyList(); } + @Override public boolean _write(String fromNode, String key, InputStream data, StorageMetadata metadata, String requestId) throws IOException { if (log.isDebugEnabled()) log.debug("_write(fromNode=" + fromNode + ")"); return false; diff --git a/bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriver.java b/bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriver.java index 43dff0a6..14c189da 100644 --- a/bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/compute/ComputeServiceDriver.java @@ -25,4 +25,7 @@ public interface ComputeServiceDriver extends CloudServiceDriver, RegionalServic @Override default boolean test () { return true; } + List getPackerImages(); + List writePackerImages(); + } diff --git a/bubble-server/src/main/java/bubble/cloud/compute/PackerImage.java b/bubble-server/src/main/java/bubble/cloud/compute/PackerImage.java new file mode 100644 index 00000000..2c60a9ef --- /dev/null +++ b/bubble-server/src/main/java/bubble/cloud/compute/PackerImage.java @@ -0,0 +1,17 @@ +package bubble.cloud.compute; + +import bubble.cloud.CloudRegion; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; + +@NoArgsConstructor @Accessors(chain=true) @EqualsAndHashCode(of={"id"}) +public class PackerImage { + + @Getter @Setter private String id; + @Getter @Setter private String name; + @Getter @Setter private CloudRegion[] regions; + +} diff --git a/bubble-server/src/main/java/bubble/cloud/compute/delegate/DelegatedComputeDriver.java b/bubble-server/src/main/java/bubble/cloud/compute/delegate/DelegatedComputeDriver.java index b378b601..197569d2 100644 --- a/bubble-server/src/main/java/bubble/cloud/compute/delegate/DelegatedComputeDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/compute/delegate/DelegatedComputeDriver.java @@ -9,6 +9,7 @@ import bubble.cloud.DelegatedCloudServiceDriverBase; import bubble.cloud.compute.ComputeNodeSize; import bubble.cloud.compute.ComputeNodeSizeType; import bubble.cloud.compute.ComputeServiceDriver; +import bubble.cloud.compute.PackerImage; import bubble.model.cloud.BubbleNode; import bubble.model.cloud.CloudService; import bubble.notify.compute.ComputeDriverNotification; @@ -17,6 +18,7 @@ import java.util.Arrays; import java.util.List; import static bubble.model.cloud.notify.NotificationType.*; +import static org.cobbzilla.util.daemon.ZillaRuntime.notSupported; public class DelegatedComputeDriver extends DelegatedCloudServiceDriverBase implements ComputeServiceDriver { @@ -70,4 +72,8 @@ public class DelegatedComputeDriver extends DelegatedCloudServiceDriverBase impl final BubbleNode delegate = getDelegateNode(); return notificationService.notifySync(delegate, compute_driver_status, notification(node)); } + + @Override public List getPackerImages() { return notSupported("getPackerImages"); } + @Override public List writePackerImages() { return notSupported("writePackerImages"); } + } diff --git a/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java b/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java index 59fd77dc..d8ef69b0 100644 --- a/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java @@ -4,9 +4,11 @@ */ package bubble.cloud.compute.digitalocean; +import bubble.ApiConstants; import bubble.cloud.CloudRegion; import bubble.cloud.compute.ComputeNodeSize; import bubble.cloud.compute.ComputeServiceDriverBase; +import bubble.cloud.compute.PackerImage; import bubble.model.cloud.BubbleNode; import bubble.model.cloud.BubbleNodeState; import com.fasterxml.jackson.databind.JsonNode; @@ -44,34 +46,33 @@ public class DigitalOceanDriver extends ComputeServiceDriverBase { public static final String TAG_PREFIX_CLOUD = "cloud_"; public static final String TAG_PREFIX_NODE = "node_"; + public static final String PACKER_IMAGES_URI = "images?tag=" + ApiConstants.PACKER_IMAGE_TAG; @Getter(lazy=true) private final Set regionSlugs = getResourceSlugs("regions"); @Getter(lazy=true) private final Set sizeSlugs = getResourceSlugs("sizes"); @Getter(lazy=true) private final Set imageSlugs = getResourceSlugs("images?type=distribution"); - private Set getResourceSlugs(String uri) { + private Set getResourceSlugs(String uri) { return getResources(uri, new ResourceSlugParser(uri)); } + + private > C getResources(String uri, ResourceParser parser) { final int qPos = uri.indexOf('?'); final String type = qPos == -1 ? uri : uri.substring(0, qPos); final JsonNode found = doGet(uri, JsonNode.class); - final Set slugs = new HashSet<>(); + final C results = parser.newResults(); JsonNode page = found; do { final JsonNode items = page.has(type) ? page.get(type) : null; if (empty(items) || !items.isArray()) return die("getResourceSlugs("+uri+"): expected "+type+" property to contain a (non-empty) array"); - for (int i=0; i getPackerImages() { + return getResources(PACKER_IMAGES_URI, new ImageParser()); + } + + @Override public List writePackerImages() { + return null; + } + } diff --git a/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/ImageParser.java b/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/ImageParser.java new file mode 100644 index 00000000..2de8d06e --- /dev/null +++ b/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/ImageParser.java @@ -0,0 +1,32 @@ +package bubble.cloud.compute.digitalocean; + +import bubble.cloud.CloudRegion; +import bubble.cloud.compute.PackerImage; +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.ArrayList; +import java.util.List; + +public class ImageParser implements ResourceParser> { + + @Override public List newResults() { return new ArrayList<>(); } + + @Override public PackerImage parse(JsonNode item) { + final PackerImage image = new PackerImage(); + if (item.has("id")) image.setId(item.get("id").textValue()); + if (item.has("name")) image.setName(item.get("name").textValue()); + if (item.has("regions")) { + final JsonNode regionsNode = item.get("regions"); + if (regionsNode.isArray()) { + final List regions = new ArrayList<>(); + for (int i=0; i> { + + C newResults(); + + E parse(JsonNode item); + +} diff --git a/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/ResourceSlugParser.java b/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/ResourceSlugParser.java new file mode 100644 index 00000000..7be8fdf1 --- /dev/null +++ b/bubble-server/src/main/java/bubble/cloud/compute/digitalocean/ResourceSlugParser.java @@ -0,0 +1,24 @@ +package bubble.cloud.compute.digitalocean; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; + +import java.util.HashSet; +import java.util.Set; + +import static org.cobbzilla.util.daemon.ZillaRuntime.die; +import static org.cobbzilla.util.json.JsonUtil.json; + +@AllArgsConstructor +class ResourceSlugParser implements ResourceParser> { + + private final String uri; + + @Override public Set newResults() { return new HashSet<>(); } + + @Override public String parse(JsonNode item) { + final JsonNode slug = item.get("slug"); + if (slug == null) return die("getResourceSlugs("+ uri +"): no 'slug' found in item: "+json(item)); + return slug.textValue(); + } +} diff --git a/bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java b/bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java index e11df04b..294519f0 100644 --- a/bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java +++ b/bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java @@ -6,6 +6,7 @@ package bubble.cloud.compute.ec2; import bubble.cloud.compute.ComputeNodeSize; import bubble.cloud.compute.ComputeServiceDriverBase; +import bubble.cloud.compute.PackerImage; import bubble.cloud.shared.aws.BubbleAwsCredentialsProvider; import bubble.model.cloud.BubbleNode; import bubble.model.cloud.BubbleNodeState; @@ -31,8 +32,8 @@ import java.util.concurrent.TimeUnit; import static bubble.model.cloud.BubbleNode.*; import static org.cobbzilla.util.daemon.Await.awaitAll; import static org.cobbzilla.util.daemon.DaemonThreadFactory.fixedPool; -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.daemon.ZillaRuntime.notSupported; import static org.cobbzilla.util.http.HttpStatusCodes.OK; import static org.cobbzilla.util.security.RsaKeyPair.newRsaKeyPair; import static org.cobbzilla.wizard.resources.ResourceUtil.notFoundEx; @@ -308,4 +309,9 @@ public class AmazonEC2Driver extends ComputeServiceDriverBase { } return importKeyPairResult.getKeyFingerprint(); } + + // todo + @Override public List getPackerImages() { return notSupported("getPackerImages"); } + @Override public List writePackerImages() { return notSupported("writePackerImages"); } + } diff --git a/bubble-server/src/main/java/bubble/cloud/compute/local/LocalComputeDriver.java b/bubble-server/src/main/java/bubble/cloud/compute/local/LocalComputeDriver.java index 3afa5651..9fd24db5 100644 --- a/bubble-server/src/main/java/bubble/cloud/compute/local/LocalComputeDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/compute/local/LocalComputeDriver.java @@ -6,10 +6,7 @@ package bubble.cloud.compute.local; import bubble.cloud.CloudRegion; import bubble.cloud.CloudServiceDriverBase; -import bubble.cloud.compute.ComputeConfig; -import bubble.cloud.compute.ComputeNodeSize; -import bubble.cloud.compute.ComputeNodeSizeType; -import bubble.cloud.compute.ComputeServiceDriver; +import bubble.cloud.compute.*; import bubble.model.cloud.BubbleNode; import java.util.List; @@ -29,4 +26,7 @@ public class LocalComputeDriver extends CloudServiceDriverBase 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 getPackerImages() { return notSupported("getPackerImages"); } + @Override public List writePackerImages() { return notSupported("writePackerImages"); } + } diff --git a/bubble-server/src/main/java/bubble/cloud/compute/mock/MockComputeDriver.java b/bubble-server/src/main/java/bubble/cloud/compute/mock/MockComputeDriver.java index 5385d698..99bc66ff 100644 --- a/bubble-server/src/main/java/bubble/cloud/compute/mock/MockComputeDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/compute/mock/MockComputeDriver.java @@ -8,6 +8,7 @@ import bubble.cloud.CloudRegion; import bubble.cloud.compute.ComputeNodeSize; import bubble.cloud.compute.ComputeNodeSizeType; import bubble.cloud.compute.ComputeServiceDriverBase; +import bubble.cloud.compute.PackerImage; import bubble.cloud.geoLocation.mock.MockGeoLocationDriver; import bubble.model.cloud.BubbleNode; import bubble.model.cloud.BubbleNodeState; @@ -68,4 +69,7 @@ public class MockComputeDriver extends ComputeServiceDriverBase { return node; } + @Override public List getPackerImages() { return Collections.emptyList(); } + @Override public List writePackerImages() { return Collections.emptyList(); } + } diff --git a/bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java b/bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java index bd7f3b66..6e02eeff 100644 --- a/bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java @@ -7,6 +7,7 @@ package bubble.cloud.compute.vultr; import bubble.cloud.CloudRegion; import bubble.cloud.compute.ComputeNodeSize; import bubble.cloud.compute.ComputeServiceDriverBase; +import bubble.cloud.compute.PackerImage; import bubble.model.cloud.BubbleNode; import bubble.model.cloud.BubbleNodeState; import com.fasterxml.jackson.databind.JsonNode; @@ -366,4 +367,9 @@ public class VultrDriver extends ComputeServiceDriverBase { } } } + + // todo + @Override public List getPackerImages() { return notSupported("getPackerImages"); } + @Override public List writePackerImages() { return notSupported("writePackerImages"); } + } diff --git a/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java b/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java index 575b0dda..9ba9ba8f 100644 --- a/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java +++ b/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java @@ -18,8 +18,7 @@ import javax.ws.rs.core.Context; import java.util.List; import java.util.Map; -import static bubble.ApiConstants.EP_DATA; -import static bubble.ApiConstants.EP_STORAGE; +import static bubble.ApiConstants.*; import static org.cobbzilla.util.daemon.ZillaRuntime.empty; import static org.cobbzilla.util.http.URIUtil.queryParams; import static org.cobbzilla.wizard.resources.ResourceUtil.*; @@ -61,4 +60,14 @@ public class CloudServicesResource extends AccountOwnedResource