com.dtolabs.rundeck.plugin.resources.gcp.GCPResourceModelSource.java Source code

Java tutorial

Introduction

Here is the source code for com.dtolabs.rundeck.plugin.resources.gcp.GCPResourceModelSource.java

Source

/*
 * Copyright 2011 DTO Solutions, Inc. (http://dtosolutions.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.
 */

/*
* GCPResourceModelSource.java
* 
* User: James Coppens <a href="mailto:jameshcoppens@gmail.com">jameshcoppens@gmail.com</a>
* Created: 9/1/11 4:34 PM
* 
*/
package com.dtolabs.rundeck.plugin.resources.gcp;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential.Builder;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.compute.Compute;
import com.google.api.services.compute.ComputeScopes;
import com.google.api.services.compute.model.Instance;

import com.dtolabs.rundeck.core.common.*;
import com.dtolabs.rundeck.core.plugins.configuration.ConfigurationException;
import com.dtolabs.rundeck.core.resources.ResourceModelSource;
import com.dtolabs.rundeck.core.resources.ResourceModelSourceException;
import org.apache.log4j.Logger;

import java.io.*;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * GCPResourceModelSource produces nodes by querying the GCP Compute Engine API to list instances.
 * <p/>
 * The RunDeck node definitions are created from the instances on a mapping system to convert properties of the amazon
 * instances to attributes defined on the nodes.
 * <p/>
 * The Compute Engine requests are performed asynchronously, so the first request to {@link #getNodes()} will return null, and
 * subsequent requests may return the data when it's available.
 *
 * @author James Coppens <a href="mailto:jameshcoppens@gmail.com">jameshcoppens@gmail.com</a>
 */
public class GCPResourceModelSource implements ResourceModelSource {
    static Logger logger = Logger.getLogger(GCPResourceModelSource.class);
    private String projectId;
    long refreshInterval = 30000;
    long lastRefresh = 0;
    String filterParams;
    String endpoint;
    String httpProxyHost;
    int httpProxyPort = 80;
    String httpProxyUser;
    String httpProxyPass;
    String mappingParams;
    File mappingFile;
    boolean useDefaultMapping = true;
    boolean runningOnly = true;
    boolean queryAsync = true;
    Future<INodeSet> futureResult = null;
    final Properties mapping = new Properties();

    GoogleCredential credential;

    INodeSet iNodeSet;
    static final Properties defaultMapping = new Properties();
    InstanceToNodeMapper mapper;

    static {
        final String mapping = "nodename.selector=name,id\n" + "hostname.selector=name\n" + "sshport.default=22\n"
                + "sshport.selector=tags/ssh_config_Port\n" + "description.default=GCE node instance\n"
                + "osArch.selector=architecture\n" + "osFamily.selector=platform\n" + "osFamily.default=unix\n"
                + "osName.selector=platform\n" + "osName.default=Linux\n" + "username.selector=tags/Rundeck-User\n"
                + "username.default=ec2-user\n"
                + "editUrl.default=https://console.cloud.google.com/compute/instances?project=${node.projectId}\n"
                + "privateIpAddress.selector=networkInterfaces\n" + "privateDnsName.selector=privateDnsName\n"
                + "tags.selector=tags/Rundeck-Tags\n" + "instanceId.selector=instanceId\n"
                + "tag.running.selector=status=running\n" + "tag.stopped.selector=status=stopped\n"
                + "tag.stopping.selector=status=stopping\n" + "tag.shutting-down.selector=status=shutting-down\n"
                + "tag.terminated.selector=status=terminated\n" + "tag.pending.selector=status=pending\n"
                + "state.selector=status\n" + "tags.default=gce\n";
        try {

            final InputStream resourceAsStream = GCPResourceModelSource.class.getClassLoader()
                    .getResourceAsStream("defaultMapping.properties");
            if (null != resourceAsStream) {
                try {
                    defaultMapping.load(resourceAsStream);
                } finally {
                    resourceAsStream.close();
                }
            } else {
                //fallback in case class loader is misbehaving
                final StringReader stringReader = new StringReader(mapping);
                try {
                    defaultMapping.load(stringReader);
                } finally {
                    stringReader.close();
                }
            }

        } catch (IOException e) {
            e.printStackTrace(System.err);
        }
    }

    public GCPResourceModelSource(final Properties configuration) {
        logger.error("GCPResourceModelSource Constructor");
        //this.clientId = configuration.getProperty(GCPResourceModelSourceFactory.CLIENT_ID);
        //this.clientSecret = configuration.getProperty(GCPResourceModelSourceFactory.CLIENT_SECRET);
        //this.endpoint = configuration.getProperty(EC2ResourceModelSourceFactory.ENDPOINT);
        //this.httpProxyHost = configuration.getProperty(GCPResourceModelSourceFactory.HTTP_PROXY_HOST);
        this.projectId = configuration.getProperty(GCPResourceModelSourceFactory.PROJECT_ID);
        int proxyPort = 80;

        //final String proxyPortStr = configuration.getProperty(GCPResourceModelSourceFactory.HTTP_PROXY_PORT);
        /*if (null != proxyPortStr && !"".equals(proxyPortStr)) {
        try {
            proxyPort = Integer.parseInt(proxyPortStr);
        } catch (NumberFormatException e) {
            logger.warn(GCPResourceModelSourceFactory.HTTP_PROXY_PORT + " value is not valid: " + proxyPortStr);
        }
        }
        this.httpProxyPort = proxyPort;
        this.httpProxyUser = configuration.getProperty(GCPResourceModelSourceFactory.HTTP_PROXY_USER);
        this.httpProxyPass = configuration.getProperty(GCPResourceModelSourceFactory.HTTP_PROXY_PASS);
        */
        this.filterParams = configuration.getProperty(GCPResourceModelSourceFactory.FILTER_PARAMS);
        this.mappingParams = configuration.getProperty(GCPResourceModelSourceFactory.MAPPING_PARAMS);
        final String mappingFilePath = configuration.getProperty(GCPResourceModelSourceFactory.MAPPING_FILE);
        if (null != mappingFilePath) {
            mappingFile = new File(mappingFilePath);
        }
        int refreshSecs = 30;
        final String refreshStr = configuration.getProperty(GCPResourceModelSourceFactory.REFRESH_INTERVAL);
        if (null != refreshStr && !"".equals(refreshStr)) {
            try {
                refreshSecs = Integer.parseInt(refreshStr);
            } catch (NumberFormatException e) {
                logger.warn(GCPResourceModelSourceFactory.REFRESH_INTERVAL + " value is not valid: " + refreshStr);
            }
        }
        refreshInterval = refreshSecs * 1000;
        if (configuration.containsKey(GCPResourceModelSourceFactory.USE_DEFAULT_MAPPING)) {
            useDefaultMapping = Boolean
                    .parseBoolean(configuration.getProperty(GCPResourceModelSourceFactory.USE_DEFAULT_MAPPING));
        }
        if (configuration.containsKey(GCPResourceModelSourceFactory.RUNNING_ONLY)) {
            runningOnly = Boolean
                    .parseBoolean(configuration.getProperty(GCPResourceModelSourceFactory.RUNNING_ONLY));
        }
        //if (null != clientId && null != clientSecret) {
        try {
            credential = GoogleCredential
                    .fromStream(new FileInputStream("/etc/rundeck/rundeck-gcp-nodes-plugin.json"))
                    .createScoped(Collections.singleton(ComputeScopes.COMPUTE_READONLY));
            logger.error("Google Crendential created successfully");
        } catch (FileNotFoundException e) {
            logger.error("Google Crendential failed creation");
            System.err.println(e.getMessage());
        } catch (Throwable t) {
            t.printStackTrace();
        }
        //}

        /*if (null != httpProxyHost && !"".equals(httpProxyHost)) {
        clientConfiguration.setProxyHost(httpProxyHost);
        clientConfiguration.setProxyPort(httpProxyPort);
        clientConfiguration.setProxyUsername(httpProxyUser);
        clientConfiguration.setProxyPassword(httpProxyPass);
        }*/

        initialize();
    }

    private void initialize() {
        logger.error("initialize call");
        final ArrayList<String> params = new ArrayList<String>();
        if (null != filterParams) {
            Collections.addAll(params, filterParams.split(";"));
        }
        loadMapping();
        mapper = new InstanceToNodeMapper(credential, mapping);
        mapper.setProjectId(projectId);
        mapper.setFilterParams(params);
        mapper.setEndpoint(endpoint);
        mapper.setRunningStateOnly(runningOnly);
    }

    public synchronized INodeSet getNodes() throws ResourceModelSourceException {
        logger.error("getNodes call");
        checkFuture();
        if (!needsRefresh()) {
            if (null != iNodeSet) {
                logger.info("Returning " + iNodeSet.getNodeNames().size() + " cached nodes from GCP");
            }
            return iNodeSet;
        }
        if (lastRefresh > 0 && queryAsync && null == futureResult) {
            futureResult = mapper.performQueryAsync();
            lastRefresh = System.currentTimeMillis();
        } else if (!queryAsync || lastRefresh < 1) {
            //always perform synchronous query the first time
            iNodeSet = mapper.performQuery();
            lastRefresh = System.currentTimeMillis();
        }
        if (null != iNodeSet) {
            logger.info("Read " + iNodeSet.getNodeNames().size() + " nodes from GCP");
        }
        return iNodeSet;
    }

    /**
     * if any future results are pending, check if they are done and retrieve the results
     */
    private void checkFuture() {
        logger.error("checkFuture call");
        if (null != futureResult && futureResult.isDone()) {
            try {
                iNodeSet = futureResult.get();
            } catch (InterruptedException e) {
                logger.debug(e);
            } catch (ExecutionException e) {
                logger.warn("Error performing query: " + e.getMessage(), e);
            }
            futureResult = null;
        }
    }

    /**
     * Returns true if the last refresh time was longer ago than the refresh interval
     */
    private boolean needsRefresh() {
        logger.error("needsRefresh call");
        return refreshInterval < 0 || (System.currentTimeMillis() - lastRefresh > refreshInterval);
    }

    private void loadMapping() {
        logger.error("loadMapping call");
        if (useDefaultMapping) {
            mapping.putAll(defaultMapping);
        }
        if (null != mappingFile) {
            try {
                final FileInputStream fileInputStream = new FileInputStream(mappingFile);
                try {
                    mapping.load(fileInputStream);
                } finally {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                logger.warn(e);
            }
        }
        if (null != mappingParams) {
            for (final String s : mappingParams.split(";")) {
                if (s.contains("=")) {
                    final String[] split = s.split("=", 2);
                    if (2 == split.length) {
                        mapping.put(split[0], split[1]);
                    }
                }
            }
        }
        if (mapping.size() < 1) {
            mapping.putAll(defaultMapping);
        }
    }

    public void validate() throws ConfigurationException {
        logger.error("validate call");
        /*if (null != clientId && null == clientSecret) {
        throw new ConfigurationException("clientSecret is required for use with clientID");
        }*/

    }
}