blob: 13d1b75d9a708655f78f701ca137f3b0a659b5e3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.googlecomputeengine.config;
import static org.jclouds.googlecomputeengine.domain.Firewall.Rule;
import java.beans.ConstructorProperties;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import javax.inject.Singleton;
import org.jclouds.googlecomputeengine.domain.Firewall;
import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.InstanceTemplate;
import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.googlecomputeengine.domain.Project;
import org.jclouds.googlecomputeengine.options.FirewallOptions;
import org.jclouds.json.config.GsonModule;
import org.jclouds.oauth.v2.domain.ClaimSet;
import org.jclouds.oauth.v2.domain.Header;
import org.jclouds.oauth.v2.json.ClaimSetTypeAdapter;
import org.jclouds.oauth.v2.json.HeaderTypeAdapter;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Range;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
/**
* @author David Alves
*/
public class GoogleComputeEngineParserModule extends AbstractModule {
@Override
protected void configure() {
bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
}
@Provides
@Singleton
public Map<Type, Object> provideCustomAdapterBindings() {
return new ImmutableMap.Builder<Type, Object>()
.put(Metadata.class, new MetadataTypeAdapter())
.put(Operation.class, new OperationTypeAdapter())
.put(Header.class, new HeaderTypeAdapter())
.put(ClaimSet.class, new ClaimSetTypeAdapter())
.put(Project.class, new ProjectTypeAdapter())
.put(Instance.class, new InstanceTypeAdapter())
.put(InstanceTemplate.class, new InstanceTemplateTypeAdapter())
.put(FirewallOptions.class, new FirewallOptionsTypeAdapter())
.put(Rule.class, new RuleTypeAdapter())
.build();
}
/**
* Parser for operations that unwraps errors avoiding an extra intermediate object.
*
* @see <a href="https://developers.google.com/compute/docs/reference/v1beta15/operations"/>
*/
@Singleton
private static class OperationTypeAdapter implements JsonDeserializer<Operation> {
@Override
public Operation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws
JsonParseException {
Operation.Builder operationBuilder = ((Operation) context.deserialize(json,
OperationInternal.class)).toBuilder();
JsonObject error = json.getAsJsonObject().getAsJsonObject("error");
if (error != null) {
JsonArray array = error.getAsJsonArray("errors");
if (array != null) {
for (JsonElement element : array) {
operationBuilder.addError((Operation.Error) context.deserialize(element, Operation.Error.class));
}
}
}
return operationBuilder.build();
}
private static class OperationInternal extends Operation {
@ConstructorProperties({
"id", "creationTimestamp", "selfLink", "name", "description", "targetLink", "targetId",
"clientOperationId", "status", "statusMessage", "user", "progress", "insertTime", "startTime",
"endTime", "httpErrorStatusCode", "httpErrorMessage", "operationType", "region", "zone"
})
private OperationInternal(String id, Date creationTimestamp, URI selfLink, String name,
String description, URI targetLink, String targetId, String clientOperationId,
Status status, String statusMessage, String user, int progress, Date insertTime,
Date startTime, Date endTime, int httpErrorStatusCode, String httpErrorMessage,
String operationType, URI region, URI zone) {
super(id, creationTimestamp, selfLink, name, description, targetLink, targetId, clientOperationId,
status, statusMessage, user, progress, insertTime, startTime, endTime, httpErrorStatusCode,
httpErrorMessage, operationType, null, region, zone);
}
}
}
@Singleton
private static class InstanceTemplateTypeAdapter implements JsonSerializer<InstanceTemplate> {
@Override
public JsonElement serialize(InstanceTemplate src, Type typeOfSrc, JsonSerializationContext context) {
InstanceTemplateInternal template = new InstanceTemplateInternal(src);
JsonObject instance = (JsonObject) context.serialize(template, InstanceTemplateInternal.class);
// deal with network
JsonArray networkInterfaces = new JsonArray();
for (InstanceTemplate.NetworkInterface networkInterface : template.getNetworkInterfaces()){
networkInterfaces.add(context.serialize(networkInterface, InstanceTemplate.NetworkInterface.class));
}
instance.add("networkInterfaces", networkInterfaces);
// deal with persistent disks
if (src.getDisks() != null && !src.getDisks().isEmpty()) {
JsonArray disks = new JsonArray();
for (InstanceTemplate.PersistentDisk persistentDisk : src.getDisks()) {
JsonObject disk = (JsonObject) context.serialize(persistentDisk, InstanceTemplate.PersistentDisk.class);
disk.addProperty("type", "PERSISTENT");
disks.add(disk);
}
instance.add("disks", disks);
}
// deal with metadata
if (src.getMetadata() != null && !src.getMetadata().isEmpty()) {
JsonObject metadata = (JsonObject) context.serialize(new Metadata(src.getMetadata()));
instance.add("metadata", metadata);
return instance;
}
return instance;
}
private static class InstanceTemplateInternal extends InstanceTemplate {
private InstanceTemplateInternal(InstanceTemplate template) {
super(template.getMachineType());
name(template.getName());
description(template.getDescription());
image(template.getImage());
serviceAccounts(template.getServiceAccounts());
networkInterfaces(template.getNetworkInterfaces());
}
}
}
@Singleton
private static class InstanceTypeAdapter implements JsonDeserializer<Instance> {
@Override
public Instance deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws
JsonParseException {
Instance.Builder instanceBuilder = ((Instance) context.deserialize(json,
InstanceInternal.class)).toBuilder();
JsonObject object = (JsonObject) json;
if (object.get("disks") != null) {
JsonArray disks = (JsonArray) object.get("disks");
for (JsonElement element : disks) {
JsonObject disk = (JsonObject) element;
if (disk.get("type").getAsString().equals("PERSISTENT")) {
instanceBuilder.addDisk((Instance.PersistentAttachedDisk) context.deserialize(disk,
Instance.PersistentAttachedDisk.class));
} else {
instanceBuilder.addDisk((Instance.AttachedDisk) context.deserialize(disk,
Instance.AttachedDisk.class));
}
}
}
return Instance.builder().fromInstance(instanceBuilder.build()).build();
}
private static class InstanceInternal extends Instance {
@ConstructorProperties({
"id", "creationTimestamp", "selfLink", "name", "description", "tags", "image", "machineType",
"status", "statusMessage", "zone", "networkInterfaces", "metadata", "serviceAccounts"
})
private InstanceInternal(String id, Date creationTimestamp, URI selfLink, String name, String description,
Tags tags, URI image, URI machineType, Status status, String statusMessage,
URI zone, Set<NetworkInterface> networkInterfaces, Metadata metadata,
Set<ServiceAccount> serviceAccounts) {
super(id, creationTimestamp, selfLink, name, description, tags, image, machineType,
status, statusMessage, zone, networkInterfaces, null, metadata, serviceAccounts);
}
}
}
/**
* Parser for Metadata.
*/
@Singleton
private static class MetadataTypeAdapter implements JsonDeserializer<Metadata>, JsonSerializer<Metadata> {
@Override
public Metadata deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws
JsonParseException {
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
JsonObject metadata = json.getAsJsonObject();
JsonArray items = metadata.getAsJsonArray("items");
if (items != null) {
for (JsonElement element : items) {
JsonObject object = element.getAsJsonObject();
builder.put(object.get("key").getAsString(), object.get("value").getAsString());
}
}
return new Metadata(builder.build());
}
@Override
public JsonElement serialize(Metadata src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject metadataObject = new JsonObject();
metadataObject.add("kind", new JsonPrimitive("compute#metadata"));
JsonArray items = new JsonArray();
for (Map.Entry<String, String> entry : src.entrySet()) {
JsonObject object = new JsonObject();
object.addProperty("key", entry.getKey());
object.addProperty("value", entry.getValue());
items.add(object);
}
metadataObject.add("items", items);
return metadataObject;
}
}
public static class Metadata extends ForwardingMap<String, String> {
private final Map<String, String> delegate;
public Metadata(Map<String, String> delegate) {
this.delegate = delegate;
}
@Override
protected Map<String, String> delegate() {
return delegate;
}
}
@Singleton
private static class ProjectTypeAdapter implements JsonDeserializer<Project> {
@Override
public Project deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws
JsonParseException {
return Project.builder().fromProject((Project) context.deserialize(json, ProjectInternal.class)).build();
}
private static class ProjectInternal extends Project {
@ConstructorProperties({
"id", "creationTimestamp", "selfLink", "name", "description", "commonInstanceMetadata", "quotas",
"externalIpAddresses"
})
private ProjectInternal(String id, Date creationTimestamp, URI selfLink, String name, String description,
Metadata commonInstanceMetadata, Set<Quota> quotas, Set<String> externalIpAddresses) {
super(id, creationTimestamp, selfLink, name, description, commonInstanceMetadata, quotas,
externalIpAddresses);
}
}
}
@Singleton
private static class FirewallOptionsTypeAdapter implements JsonSerializer<FirewallOptions> {
@Override
public JsonElement serialize(FirewallOptions src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject firewall = new JsonObject();
if (src.getName() != null) {
firewall.addProperty("name", src.getName());
}
if (src.getNetwork() != null) {
firewall.addProperty("network", src.getNetwork().toString());
}
if (!src.getSourceRanges().isEmpty()) {
firewall.add("sourceRanges", buildArrayOfStrings(src.getSourceRanges()));
}
if (!src.getSourceTags().isEmpty()) {
firewall.add("sourceTags", buildArrayOfStrings(src.getSourceTags()));
}
if (!src.getTargetTags().isEmpty()) {
firewall.add("targetTags", buildArrayOfStrings(src.getTargetTags()));
}
if (!src.getAllowed().isEmpty()) {
JsonArray rules = new JsonArray();
for (Rule rule : src.getAllowed()) {
rules.add(context.serialize(rule, Firewall.Rule.class));
}
firewall.add("allowed", rules);
}
return firewall;
}
}
private static JsonArray buildArrayOfStrings(Set<String> strings) {
JsonArray array = new JsonArray();
for (String string : strings) {
array.add(new JsonPrimitive(string));
}
return array;
}
private static class RuleTypeAdapter implements JsonDeserializer<Firewall.Rule>, JsonSerializer<Firewall.Rule> {
@Override
public Firewall.Rule deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws
JsonParseException {
JsonObject rule = json.getAsJsonObject();
Rule.Builder builder = Rule.builder();
builder.IPProtocol(Rule.IPProtocol.fromValue(rule.get("IPProtocol").getAsString()));
if (rule.get("ports") != null) {
JsonArray ports = (JsonArray) rule.get("ports");
for (JsonElement port : ports) {
String portAsString = port.getAsString();
if (portAsString.contains("-")) {
String[] split = portAsString.split("-");
builder.addPortRange(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
} else {
builder.addPort(Integer.parseInt(portAsString));
}
}
}
return builder.build();
}
@Override
public JsonElement serialize(Firewall.Rule src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject ruleObject = new JsonObject();
ruleObject.addProperty("IPProtocol", src.getIPProtocol().value());
if (src.getPorts() != null && !src.getPorts().isEmpty()) {
JsonArray ports = new JsonArray();
for (Range<Integer> range : src.getPorts().asRanges()) {
ports.add(new JsonPrimitive(range.lowerEndpoint() == range.upperEndpoint() ? range.lowerEndpoint() + "" :
range.lowerEndpoint() + "-" + range.upperEndpoint()));
}
ruleObject.add("ports", ports);
}
return ruleObject;
}
}
}