services.ConfigurationService.java Source code

Java tutorial

Introduction

Here is the source code for services.ConfigurationService.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 services;

import conf.PlayConfiguration;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import utils.file.FileLockException;
import utils.file.FileUtils;
import utils.gce.GoogleComputeEngineException;
import utils.gce.storage.GoogleCloudStorageClient;
import utils.gce.storage.GoogleCloudStorageException;
import utils.puppet.PuppetConfiguration;
import utils.puppet.PuppetConfigurationException;
import utils.ssh.FilePermissions;
import utils.ssh.SSHClient;
import utils.ssh.SSHException;
import utils.test.TestConfiguration;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Created by ricardolorenzo on 27/07/2014.
 */
@Service(value = "conf-service")
@Configurable
public class ConfigurationService {
    public static final String CLUSTER_USER = "mongodb@localhost";
    public static final String TEST_USER = "test@localhost";
    public static final String NODE_NAME_PUPPET = "puppetmaster";
    public static final String NODE_NAME_CONF = "conf";
    public static final String NODE_NAME_SHARD = "shard";
    public static final String NODE_NAME_TEST = "test";
    public static final String NODE_NAME_TEST_JUMP = "test-jump";
    public static final String NODE_TAG_PUPPET = "puppet";
    public static final String NODE_TAG_CONF = "config";
    public static final String NODE_TAG_SHARD = "shard";
    public static final String NODE_TAG_TEST = "test";
    public static final String NODE_TAG_TEST_JUMP = "test-jump";
    private static GoogleAuthenticationService authService;
    private static GoogleComputeEngineService googleComputeService;
    private static GoogleCloudStorageClient googleStorageClient;
    private static final String projectId;
    private static final String bucketId;
    private static final String applicationDirectory;

    @Inject
    void setConfigurationService(@Qualifier("gce-service") GoogleComputeEngineService googleService) {
        ConfigurationService.googleComputeService = googleService;
    }

    @Inject
    void setConfigurationService(@Qualifier("gauth-service") GoogleAuthenticationService googleService) {
        ConfigurationService.authService = googleService;
    }

    static {
        applicationDirectory = PlayConfiguration.getProperty("application.directory");
        projectId = PlayConfiguration.getProperty("google.projectId");
        bucketId = PlayConfiguration.getProperty("google.bucketId");
    }

    private static void checkGoogleAuthentication() throws GoogleComputeEngineException {
        if (googleStorageClient == null) {
            if (GoogleAuthenticationService.getCredential() == null) {
                throw new GoogleComputeEngineException("not authenticated on Google");
            }
            googleStorageClient = new GoogleCloudStorageClient(GoogleAuthenticationService.getAuthentication());
        }
    }

    private static File getClusterNameFile() {
        return new File(applicationDirectory + "/cluster.name");
    }

    private static File getClusterNodeProcessesFile() {
        return new File(applicationDirectory + "/cluster.processes");
    }

    private static File getStartupScriptFile() throws IOException {
        return File.createTempFile("startup-script", ".sh");
    }

    public static String getInternalServerName(String instanceName) {
        StringBuilder name = new StringBuilder();
        name.append(instanceName);
        name.append(".c.");
        name.append(ConfigurationService.projectId);
        name.append(".internal");
        return name.toString();
    }

    public static String getServerName(String clusterName, String nodeName) {
        StringBuilder name = new StringBuilder();
        name.append(clusterName);
        name.append("-");
        name.append(nodeName);
        return name.toString();
    }

    public static String generatePuppetMasterStartupScript(String clusterName, String networkName,
            Integer processes, String diskRaid, String dataFileSystem)
            throws IOException, GoogleComputeEngineException, GoogleCloudStorageException {
        checkGoogleAuthentication();
        if (bucketId == null || bucketId.isEmpty()) {
            throw new GoogleCloudStorageException("parameter 'google.bucketId' not specified in the configuration");
        }
        StringBuilder scriptPath = new StringBuilder();
        scriptPath.append("autostart/");
        scriptPath.append(clusterName);
        scriptPath.append("/puppetmaster_autostart.sh");
        /**
         * There is a limit of 32K for the metadata in Google Compute Engine. To avoid any size problems
         * the application upload the file to Google Storage and store the link in the metadata.
         */
        String puppetScriptContent = PuppetConfiguration.getPuppetStartupScriptContent(clusterName,
                googleComputeService.getNetworkRange(networkName), processes, diskRaid, dataFileSystem);
        return googleStorageClient.putFile(bucketId, scriptPath.toString(), "plain/text",
                puppetScriptContent.getBytes());
    }

    public static String getClusterName() throws GoogleComputeEngineException {
        File f = getClusterNameFile();
        if (!f.exists()) {
            return null;
        }
        try {
            return FileUtils.readFileAsString(f);
        } catch (IOException e) {
            throw new GoogleComputeEngineException("cannot read cluster name file: " + e.getMessage());
        }
    }

    public static Integer getClusterNodeProcesses() throws GoogleComputeEngineException {
        File f = getClusterNodeProcessesFile();
        if (!f.exists()) {
            return null;
        }
        try {
            return Integer.parseInt(FileUtils.readFileAsString(f).trim());
        } catch (IOException e) {
            throw new GoogleComputeEngineException("cannot read cluster node processes file: " + e.getMessage());
        }
    }

    public static File getPuppetNodeStartupScriptFile(String clusterName) throws GoogleComputeEngineException {
        try {
            File f = getStartupScriptFile();
            FileUtils.writeFile(f, PuppetConfiguration.getNodeStartupScriptContent(
                    getInternalServerName(getServerName(clusterName, NODE_NAME_PUPPET))));
            return f;
        } catch (IOException e) {
            throw new GoogleComputeEngineException("cannot write the startup script: " + e.toString());
        } catch (FileLockException e) {
            throw new GoogleComputeEngineException("cannot write the startup script: " + e.toString());
        }
    }

    public static String getPuppetFile(final Integer type, String name)
            throws PuppetConfigurationException, GoogleComputeEngineException {
        String serverName = googleComputeService.getClusterPublicAddress();
        try {
            if (serverName == null || serverName.isEmpty()) {
                return "";
            }
            StringBuilder destinationPath = new StringBuilder();
            SSHClient client = new SSHClient(serverName, 22);
            switch (type) {
            case PuppetConfiguration.PUPPET_MANIFEST:
                destinationPath.append(PuppetConfiguration.getPuppetManifestsDirectory());
                break;
            case PuppetConfiguration.PUPPET_FILE:
                destinationPath.append(PuppetConfiguration.getPuppetFilesDirectory());
                break;
            default:
                throw new PuppetConfigurationException("incorrect puppet file type");
            }
            destinationPath.append("/");
            destinationPath.append(name);
            try {
                client.connect(CLUSTER_USER);
                for (Map.Entry<String, byte[]> e : client.getFiles(destinationPath.toString()).entrySet()) {
                    return new String(e.getValue());
                }
                return null;
            } catch (SSHException e) {
                throw new GoogleComputeEngineException(e);
            } finally {
                client.disconnect();
            }
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public static File getTestJumpNodeStartupScriptFile(String clusterName, String networkName)
            throws GoogleComputeEngineException, GoogleCloudStorageException {
        try {
            String scriptUrl;
            File f = getStartupScriptFile();

            try {
                checkGoogleAuthentication();
                if (bucketId == null || bucketId.isEmpty()) {
                    throw new GoogleCloudStorageException(
                            "parameter 'google.bucketId' not specified in the configuration");
                }
                StringBuilder scriptPath = new StringBuilder();
                scriptPath.append("autostart/");
                scriptPath.append(clusterName);
                scriptPath.append("/test_autostart.sh");
                /**
                 * There is a limit of 32K for the metadata in Google Compute Engine. To avoid any size problems
                 * the application upload the file to Google Storage and store the link in the metadata.
                 *
                 * Limit can be exceeded in the case of a lot of cluster nodes.
                 */
                String scriptContent = TestConfiguration.getNodeRemoteStartupScriptContent(
                        getInternalServerName(getServerName(clusterName, NODE_NAME_TEST_JUMP)),
                        googleComputeService
                                .getInstancesNames(Arrays.asList(ConfigurationService.NODE_TAG_CONF, clusterName)),
                        googleComputeService.getInstancesNames(
                                Arrays.asList(ConfigurationService.NODE_TAG_SHARD, clusterName)));
                scriptUrl = googleStorageClient.putFile(bucketId, scriptPath.toString(), "plain/text",
                        scriptContent.getBytes());

                FileUtils.writeFile(f, TestConfiguration.getJumpServerStartupScriptContent(
                        googleComputeService.getNetworkRange(networkName), scriptUrl));
            } catch (IOException e) {
                throw new GoogleComputeEngineException("cannot create the startup script: " + e.toString());
            }

            return f;
        } catch (IOException e) {
            throw new GoogleComputeEngineException("cannot write the startup script: " + e.toString());
        } catch (FileLockException e) {
            throw new GoogleComputeEngineException("cannot write the startup script: " + e.toString());
        }
    }

    public static File getTestNodeStartupScriptFile(String clusterName)
            throws GoogleComputeEngineException, GoogleCloudStorageException {
        try {
            File f = getStartupScriptFile();

            FileUtils.writeFile(f,
                    TestConfiguration.getNodeStartupScriptContent(
                            getInternalServerName(getServerName(clusterName, NODE_NAME_TEST_JUMP)),
                            getTestNodeStartupScriptUrl(clusterName)));

            return f;
        } catch (IOException e) {
            throw new GoogleComputeEngineException("cannot write the startup script: " + e.toString());
        } catch (FileLockException e) {
            throw new GoogleComputeEngineException("cannot write the startup script: " + e.toString());
        }
    }

    private static String getTestNodeStartupScriptUrl(String clusterName) {
        StringBuilder sb = new StringBuilder();
        sb.append("http://");
        sb.append(getInternalServerName(getServerName(clusterName, NODE_NAME_TEST_JUMP)));
        sb.append("/startup.sh");
        return sb.toString();
    }

    public static List<String> listPuppetFiles(final Integer type)
            throws PuppetConfigurationException, GoogleComputeEngineException {
        String serverName = googleComputeService.getClusterPublicAddress();
        List<String> files = new ArrayList<String>();
        try {
            if (serverName == null || serverName.isEmpty()) {
                return files;
            }
            StringBuilder destinationPath = new StringBuilder();
            SSHClient client = new SSHClient(serverName, 22);
            switch (type) {
            case PuppetConfiguration.PUPPET_MANIFEST:
                destinationPath.append(PuppetConfiguration.getPuppetManifestsDirectory());
                break;
            case PuppetConfiguration.PUPPET_FILE:
                destinationPath.append(PuppetConfiguration.getPuppetFilesDirectory());
                break;
            default:
                throw new PuppetConfigurationException("incorrect puppet file type");
            }
            try {
                client.connect(CLUSTER_USER);
                if (client.sendCommand("ls", destinationPath.toString()) > 0) {
                    throw new GoogleComputeEngineException(
                            "cannot list the files for directory: " + destinationPath.toString());
                }
                StringTokenizer st = new StringTokenizer(client.getStringOutput());
                while (st.hasMoreTokens()) {
                    files.add(st.nextToken());
                }
                return files;
            } catch (SSHException e) {
                throw new GoogleComputeEngineException(e);
            } finally {
                client.disconnect();
            }
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public static void uploadPuppetFile(final Integer type, String fileName, File file)
            throws PuppetConfigurationException, GoogleComputeEngineException {
        String serverName = googleComputeService.getClusterPublicAddress();
        try {
            StringBuilder temporaryFile = new StringBuilder();
            temporaryFile.append("/tmp/");
            temporaryFile.append(fileName);
            StringBuilder destinationPath = new StringBuilder();
            SSHClient client = new SSHClient(serverName, 22);
            FilePermissions permissions = new FilePermissions(FilePermissions.READ, FilePermissions.READ,
                    FilePermissions.READ);
            switch (type) {
            case PuppetConfiguration.PUPPET_MANIFEST:
                destinationPath.append(PuppetConfiguration.getPuppetManifestsDirectory());
                break;
            case PuppetConfiguration.PUPPET_FILE:
                destinationPath.append(PuppetConfiguration.getPuppetFilesDirectory());
                break;
            default:
                throw new PuppetConfigurationException("incorrect puppet file type");
            }
            destinationPath.append("/");
            destinationPath.append(fileName);
            try {
                client.connect(CLUSTER_USER);
                client.sendFile(file, temporaryFile.toString(), permissions);
                client.sendCommand("sudo", "mv", temporaryFile.toString(), destinationPath.toString(), "&&",
                        "puppet", "kick", "--all");
            } catch (SSHException e) {
                throw new GoogleComputeEngineException(e);
            } finally {
                client.disconnect();
            }
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public static void deletePuppetFile(final Integer type, String fileName)
            throws PuppetConfigurationException, GoogleComputeEngineException {
        String serverName = googleComputeService.getClusterPublicAddress();
        if (fileName == null || fileName.isEmpty()) {
            throw new PuppetConfigurationException("file to be deleted is not defined");
        }
        try {
            StringBuilder destinationPath = new StringBuilder();
            SSHClient client = new SSHClient(serverName, 22);
            switch (type) {
            case PuppetConfiguration.PUPPET_MANIFEST:
                destinationPath.append(PuppetConfiguration.getPuppetManifestsDirectory());
                break;
            case PuppetConfiguration.PUPPET_FILE:
                destinationPath.append(PuppetConfiguration.getPuppetFilesDirectory());
                break;
            default:
                throw new PuppetConfigurationException("incorrect puppet file type");
            }
            destinationPath.append("/");
            destinationPath.append(fileName);
            try {
                client.connect(CLUSTER_USER);
                System.out.println("Delete: " + destinationPath.toString());
                client.sendCommand("sudo", "rm", "-f", destinationPath.toString(), "&&", "puppet", "kick", "--all");
            } catch (SSHException e) {
                throw new GoogleComputeEngineException(e);
            } finally {
                client.disconnect();
            }
        } catch (IOException e) {
            throw new GoogleComputeEngineException(e);
        }
    }

    public static void setClusterName(String name) throws GoogleComputeEngineException {
        File f = getClusterNameFile();
        if (name == null) {
            f.delete();
        } else {
            try {
                FileUtils.writeFile(f, name);
            } catch (IOException e) {
                throw new GoogleComputeEngineException("cannot write cluster name file: " + e.getMessage());
            } catch (FileLockException e) {
                throw new GoogleComputeEngineException("cannot write cluster name file: " + e.getMessage());
            }
        }
    }

    public static void setClusterNodeProcesses(Integer processes) throws GoogleComputeEngineException {
        File f = getClusterNodeProcessesFile();
        if (processes == null) {
            f.delete();
        } else {
            try {
                FileUtils.writeFile(f, String.valueOf(processes));
            } catch (IOException e) {
                throw new GoogleComputeEngineException(
                        "cannot write cluster node processes file: " + e.getMessage());
            } catch (FileLockException e) {
                throw new GoogleComputeEngineException(
                        "cannot write cluster node processes file: " + e.getMessage());
            }
        }
    }
}