utils.gce.GoogleComputeEngineClient.java Source code

Java tutorial

Introduction

Here is the source code for utils.gce.GoogleComputeEngineClient.java

Source

/*
 * Copyright 2014 Ricardo Lorenzo<unshakablespirit@gmail.com>
 *
 *    Licensed 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 utils.gce;

import com.fasterxml.jackson.databind.JsonNode;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.compute.Compute;
import com.google.api.services.compute.model.*;
import conf.PlayConfiguration;
import play.libs.Json;
import utils.CappedList;
import utils.file.FileUtils;
import utils.gce.auth.GoogleComputeEngineAuth;
import utils.security.GoogleComputeEngineFingerprint;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Created by ricardolorenzo on 18/07/2014.
 */
public class GoogleComputeEngineClient {
    private static final String APPLICATION_NAME = "Ricardo Lorenzo GCE Client/1.0";
    private static final Long OPERATIONS_PER_PAGE = 50L;
    private static final JsonFactory JSON_FACTORY;
    private static final HttpTransport HTTP_TRANSPORT;
    private static final List<String> IMAGE_PROJECTS;
    private static final String projectId;
    private static Optional<String> zoneName;
    private Compute compute;
    private GoogleComputeEngineAuth auth;

    static {
        JSON_FACTORY = new JacksonFactory();
        HTTP_TRANSPORT = new NetHttpTransport();
        //IMAGE_PROJECTS = Arrays.asList(new String[]{ "debian-cloud", "centos-cloud" });
        IMAGE_PROJECTS = Arrays.asList(new String[] { "debian-cloud" });
        projectId = PlayConfiguration.getProperty("google.projectId");
        if (PlayConfiguration.hasProperty("google.zoneName")) {
            zoneName = Optional.ofNullable(PlayConfiguration.getProperty("google.zoneName"));
        }
    }

    public GoogleComputeEngineClient(final GoogleComputeEngineAuth auth) {
        this.auth = auth;
        compute = new Compute.Builder(HTTP_TRANSPORT, JSON_FACTORY, null).setApplicationName(APPLICATION_NAME)
                .setHttpRequestInitializer(auth.getCredential()).build();
    }

    public String getProjectId() {
        return projectId;
    }

    public String getZone() {
        return zoneName.get();
    }

    public void setZone(String zoneName) {
        GoogleComputeEngineClient.zoneName = Optional.ofNullable(zoneName);
    }

    public List<Disk> getDisks() throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.Disks.List list = compute.disks().list(projectId, zoneName.get());
            return list.execute().getItems();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public List<DiskType> getDiskTypes() throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.DiskTypes.List list = compute.diskTypes().list(projectId, zoneName.get());
            return list.execute().getItems();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public List<Instance> getInstances(List<String> tags) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.Instances.List list = compute.instances().list(projectId, zoneName.get());
            if (tags != null && !tags.isEmpty()) {
                List<Instance> instances = new ArrayList<>();
                for (Instance i : list.execute().getItems()) {
                    int tag_match = 0;
                    Optional<List<String>> tagsItems = Optional
                            .ofNullable(i.getTags() != null ? i.getTags().getItems() : null);
                    for (String tag : tags) {
                        if (tagsItems.isPresent() && tagsItems.get().contains(tag)) {
                            tag_match++;
                        }
                    }
                    if (tag_match == tags.size()) {
                        instances.add(i);
                    }
                }
                return instances;
            } else {
                return list.execute().getItems();
            }
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public List<Zone> getZones() throws GoogleComputeEngineException {
        try {
            Compute.Zones.List list = compute.zones().list(projectId);
            return list.execute().getItems();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public List<Image> getImages() throws GoogleComputeEngineException {
        try {
            List<Image> images = new ArrayList<>();
            for (String project : IMAGE_PROJECTS) {
                Compute.Images.List list = compute.images().list(project);
                List<Image> res_images = list.execute().getItems();
                if (res_images != null) {
                    images.addAll(res_images);
                }
            }
            return images;
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public List<MachineType> getMachineTypes() throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.MachineTypes.List list = compute.machineTypes().list(projectId, zoneName.get());
            return list.execute().getItems();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public List<Network> getNetworks() throws GoogleComputeEngineException {
        try {
            Compute.Networks.List list = compute.networks().list(projectId);
            return list.execute().getItems();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public List<Operation> getOperations() throws GoogleComputeEngineException {
        try {
            List<Operation> operations = getLastOperationsPage();
            return operations;
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    private List<Operation> getLastOperationsPage() throws IOException, GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        Compute.ZoneOperations.List list = compute.zoneOperations().list(projectId, zoneName.get());
        /**
         * TODO get the maximum from the configuration
         */
        list.setMaxResults(OPERATIONS_PER_PAGE);

        CappedList<Operation> operations = new CappedList<>(OPERATIONS_PER_PAGE.intValue());
        OperationList pageList = list.execute();
        operations.addAll(pageList.getItems());
        while (pageList.getNextPageToken() != null) {
            list.setPageToken(pageList.getNextPageToken());
            pageList = list.execute();
            operations.addAll(pageList.getItems());
        }
        return new ArrayList<>(operations);
    }

    public Disk getDisk(String diskName) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.Disks.Get get = compute.disks().get(projectId, zoneName.get(), diskName);
            return get.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }

    }

    public void attachDiskToInstance(String instanceName, String diskName) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        Disk disk = getDisk(diskName);
        AttachedDisk attachedDisk = new AttachedDisk();
        attachedDisk.setBoot(false);
        attachedDisk.setAutoDelete(true);
        attachedDisk.setDeviceName(diskName);
        attachedDisk.setMode("READ_WRITE");
        attachedDisk.setSource(disk.getSelfLink());

        try {
            Compute.Instances.AttachDisk attachDisk = compute.instances().attachDisk(projectId, zoneName.get(),
                    instanceName, attachedDisk);
            attachDisk.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public String createDisk(String name, String diskType, Integer sizeGb) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Disk disk = new Disk();
            disk.setName(name);
            disk.setZone(zoneName.get());
            disk.setSizeGb(sizeGb.longValue());
            disk.setType(diskType);

            Compute.Disks.Insert insert = compute.disks().insert(projectId, zoneName.get(), disk);
            return insert.execute().getTargetLink();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public void deleteDisk(String diskName) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.Disks.Delete delete = compute.disks().delete(projectId, zoneName.get(), diskName);
            delete.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    private static AttachedDisk createAttachedDisk(String name, Integer sizeGb, String diskType,
            String sourceImage) {
        AttachedDiskInitializeParams paramaters = new AttachedDiskInitializeParams();
        paramaters.setDiskName(name);
        paramaters.setDiskSizeGb(sizeGb.longValue());
        if (diskType != null && !diskType.isEmpty()) {
            paramaters.setDiskType(diskType);
        }
        if (sourceImage != null && !sourceImage.isEmpty()) {
            paramaters.setSourceImage(sourceImage);
        }

        AttachedDisk disk = new AttachedDisk();
        disk.setBoot(true);
        //disk.setType("PERSISTENT");
        disk.setMode("READ_WRITE");
        disk.setAutoDelete(true);
        disk.setInitializeParams(paramaters);

        return disk;
    }

    public void setInstanceTags(String instanceName, Map<String, String> tags) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        Tags t = new Tags();
        if (tags != null && !tags.isEmpty()) {
            for (Map.Entry<String, String> e : tags.entrySet()) {
                t.set(e.getKey(), e.getValue());
            }
        }
        t = t.encodeFingerprint(GoogleComputeEngineFingerprint.getSha1Hash(t.toString()));

        try {
            Compute.Instances.SetTags setTags = compute.instances().setTags(projectId, zoneName.get(), instanceName,
                    t);
            setTags.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public void setInstanceMetaData(String instanceName, Map<String, String> metadata)
            throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        Metadata m = new Metadata();
        if (metadata != null && !metadata.isEmpty()) {
            for (Map.Entry<String, String> e : metadata.entrySet()) {
                m.set(e.getKey(), e.getValue());
            }
        }
        m = m.encodeFingerprint(GoogleComputeEngineFingerprint.getSha1Hash(m.toString()));

        try {
            Compute.Instances.SetMetadata setMetadata = compute.instances().setMetadata(projectId, zoneName.get(),
                    instanceName, m);
            setMetadata.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public Network getNetwork(String networkName) throws GoogleComputeEngineException {
        try {
            Compute.Networks.Get get = compute.networks().get(projectId, networkName);
            return get.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public String createNetwork(String name, String description, String ip4Range, String ip4Gateway)
            throws GoogleComputeEngineException {
        Network net = new Network();
        net.setName(name);
        if (Optional.ofNullable(description).isPresent()) {
            net.setDescription(description);
        }
        net.setIPv4Range(ip4Range);
        if (Optional.ofNullable(ip4Gateway).isPresent()) {
            net.setGatewayIPv4(ip4Gateway);
        }
        try {
            Compute.Networks.Insert insert = compute.networks().insert(projectId, net);
            return insert.execute().getTargetLink();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public void deleteNetwork(String networkName) throws GoogleComputeEngineException {
        try {
            Compute.Networks.Delete delete = compute.networks().delete(projectId, networkName);
            delete.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public Instance getInstance(String instanceName) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.Instances.Get get = compute.instances().get(projectId, zoneName.get(), instanceName);
            return get.execute();
        } catch (IOException e) {
            String message = e.getMessage();
            if (message != null && message.contains("{") && message.contains("}")) {
                message = message.substring(message.indexOf("{"));
                message = message.substring(0, message.lastIndexOf("}") + 1);
                JsonNode node = Json.parse(message).get("code");
                if (node != null && node.asInt() == 404) {
                    return null;
                }
            }
            throw new GoogleComputeEngineException(e);
        }
    }

    public boolean instanceExists(String instanceName) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.Instances.Get get = compute.instances().get(projectId, zoneName.get(), instanceName);
            if (get.execute() != null) {
                return true;
            }
            return false;
        } catch (IOException e) {
            String message = e.getMessage();
            if (message != null && message.contains("{") && message.contains("}")) {
                message = message.substring(message.indexOf("{"));
                message = message.substring(0, message.lastIndexOf("}") + 1);
                JsonNode node = Json.parse(message).get("code");
                if (node != null && node.asInt() == 404) {
                    return false;
                }
            }
            throw new GoogleComputeEngineException(e);
        }
    }

    public String createInstance(String name, String machineType, List<String> network, Integer sizeGb,
            String sourceImage, Map<String, String> dataDisks, List<String> tags, List<String> sshKeys,
            String startupScript, boolean publicAddress) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Instance instance = new Instance();
            instance.setFactory(JSON_FACTORY);
            instance.setMachineType(machineType);
            instance.setName(name);
            instance.setZone(zoneName.get());
            instance.setCanIpForward(publicAddress);
            instance.setServiceAccounts(auth.getInstanceServiceAccounts());
            if (network != null && !network.isEmpty()) {
                int count = 0;
                List<NetworkInterface> networkInterfaces = new ArrayList<>();
                for (String n : network) {
                    NetworkInterface iface = new NetworkInterface();
                    iface.setFactory(JSON_FACTORY);
                    iface.setName("eth" + count);
                    iface.setNetwork(n);
                    if (publicAddress) {
                        AccessConfig ac = new AccessConfig();
                        ac.setType("ONE_TO_ONE_NAT");
                        iface.setAccessConfigs(Arrays.asList(ac));
                    }
                    networkInterfaces.add(iface);
                    count++;
                }
                instance.setNetworkInterfaces(networkInterfaces);
            }
            List<AttachedDisk> attachedDisks = new ArrayList<>();
            attachedDisks.add(createAttachedDisk(name + "-disk-root", sizeGb, null, sourceImage));
            if (dataDisks != null && !dataDisks.isEmpty()) {
                for (Map.Entry<String, String> dataDisk : dataDisks.entrySet()) {
                    AttachedDisk attachedDisk = new AttachedDisk();
                    attachedDisk.setBoot(false);
                    attachedDisk.setAutoDelete(true);
                    attachedDisk.setDeviceName(dataDisk.getKey());
                    attachedDisk.setMode("READ_WRITE");
                    attachedDisk.setSource(dataDisk.getValue());
                    attachedDisks.add(attachedDisk);
                }
            }
            instance.setDisks(attachedDisks);
            if (tags != null && !tags.isEmpty()) {
                Tags t = new Tags();
                t.setItems(tags);
                instance.setTags(t.encodeFingerprint(GoogleComputeEngineFingerprint.getSha1Hash(t.toString())));
            }
            if (Optional.ofNullable(startupScript).isPresent() || (sshKeys != null && !sshKeys.isEmpty())) {
                Metadata m = new Metadata();
                List<Metadata.Items> itemList = new ArrayList<>();
                if (Optional.ofNullable(startupScript).isPresent()) {
                    Metadata.Items items = new Metadata.Items();
                    if (startupScript
                            .matches("\\b(https?|ftp)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]")) {
                        items.setKey("startup-script-url");
                        items.setValue(startupScript);
                    } else {
                        items.setKey("startup-script");
                        File f = new File(startupScript);
                        if (f.exists()) {
                            items.setValue(FileUtils.readFileAsString(f));
                        } else {
                            items.setValue("file not found [" + startupScript + "]");
                        }
                    }
                    itemList.add(items);
                }
                if (sshKeys != null && !sshKeys.isEmpty()) {
                    for (String key : sshKeys) {
                        Metadata.Items items = new Metadata.Items();
                        items.setKey("sshKeys");
                        items.setValue(key);
                        itemList.add(items);
                    }
                }
                m.setItems(itemList);
                instance.setMetadata(m.encodeFingerprint(GoogleComputeEngineFingerprint.getSha1Hash(m.toString())));
            }

            Compute.Instances.Insert insert = compute.instances().insert(projectId, zoneName.get(), instance);
            return insert.execute().getTargetLink();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public void deleteInstance(String instanceName) throws GoogleComputeEngineException {
        if (!zoneName.isPresent()) {
            throw new GoogleComputeEngineException("zone name not specified");
        }

        try {
            Compute.Instances.Delete delete = compute.instances().delete(projectId, zoneName.get(), instanceName);
            delete.execute();
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }
}