com.hazelcast.jclouds.ComputeServiceBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.hazelcast.jclouds.ComputeServiceBuilder.java

Source

/*
 * Copyright (c) 2008-2015, Hazelcast, Inc. All Rights Reserved.
 *
 * 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 com.hazelcast.jclouds;

import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.io.Files;
import com.hazelcast.config.InvalidConfigurationException;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.config.properties.PropertyDefinition;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import org.jclouds.Constants;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope;
import org.jclouds.googlecloud.GoogleCredentialsFromJson;
import org.jclouds.location.reference.LocationConstants;

import java.io.File;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;

import static com.hazelcast.util.Preconditions.checkNotNull;
import static com.hazelcast.util.Preconditions.isNotNull;

/**
 * ComputeServiceBuilder is the responsible class for building jclouds compute service provider.
 * Also parses config and applies necessary filters on cluster nodes.
 */
public class ComputeServiceBuilder {

    private static final String GOOGLE_COMPUTE_ENGINE = "google-compute-engine";
    private static final String AWS_EC2 = "aws-ec2";
    private static final String JCLOUD_CONNECTION_TIMEOUT = "10000";
    private static final ILogger LOGGER = Logger.getLogger(ComputeServiceBuilder.class);

    private final Map<String, Comparable> properties;
    private Set<String> regionsSet = new LinkedHashSet<String>();
    private Set<String> zonesSet = new LinkedHashSet<String>();
    private List<AbstractMap.SimpleImmutableEntry> tagPairs = new ArrayList<AbstractMap.SimpleImmutableEntry>();
    private Predicate<ComputeMetadata> nodesFilter;
    private ComputeService computeService;

    /**
     * Instantiates a new Compute service builder.
     *
     * @param properties the properties
     */
    ComputeServiceBuilder(Map<String, Comparable> properties) {
        checkNotNull(properties, "Props cannot be null");
        this.properties = properties;
    }

    public Map<String, Comparable> getProperties() {
        return properties;
    }

    public Set<String> getRegionsSet() {
        return regionsSet;
    }

    public Set<String> getZonesSet() {
        return zonesSet;
    }

    public List<AbstractMap.SimpleImmutableEntry> getTagPairs() {
        return tagPairs;
    }

    /**
     * Injects already built ComputeService
     * @param computeService
     */
    public void setComputeService(ComputeService computeService) {
        this.computeService = computeService;
    }

    /**
     * Gets filtered nodes.
     *
     * @return the filtered nodes
     */
    public Iterable<? extends NodeMetadata> getFilteredNodes() {
        final String group = getOrNull(JCloudsProperties.GROUP);
        Set<? extends NodeMetadata> result = computeService.listNodesDetailsMatching(nodesFilter);
        Iterable<? extends NodeMetadata> filteredResult = new HashSet<NodeMetadata>();
        for (NodeMetadata metadata : result) {
            if (group != null && !group.equals(metadata.getGroup())) {
                continue;
            }
            if (!isNodeInsideZones(metadata) || !isNodeInsideRegions(metadata)) {
                continue;
            }
            ((HashSet<NodeMetadata>) filteredResult).add(metadata);
        }
        return filteredResult;
    }

    public int getServicePort() {
        return getOrDefault(JCloudsProperties.HZ_PORT, NetworkConfig.DEFAULT_PORT);
    }

    public boolean isNodeInsideZones(NodeMetadata metadata) {
        Location location = metadata.getLocation();
        while (location != null) {
            String id = location.getId();
            if (location.getScope().equals(LocationScope.ZONE)) {
                if (id != null && !zonesSet.isEmpty() && !zonesSet.contains(id)) {
                    return false;
                }
            }
            location = location.getParent();
        }
        return true;
    }

    public boolean isNodeInsideRegions(NodeMetadata metadata) {
        Location location = metadata.getLocation();
        while (location != null) {
            String id = location.getId();
            if (location.getScope().equals(LocationScope.REGION)) {
                if (id != null && !regionsSet.isEmpty() && !regionsSet.contains(id)) {
                    return false;
                }
            }
            location = location.getParent();
        }
        return true;
    }

    /**
     */
    public void destroy() {
        if (computeService != null) {
            this.computeService.getContext().close();
        }
    }

    /**
     * Build compute service.
     *
     * @return the compute service
     */
    ComputeService build() {
        final String cloudProvider = getOrNull(JCloudsProperties.PROVIDER);
        final String identity = getOrNull(JCloudsProperties.IDENTITY);
        String credential = getOrNull(JCloudsProperties.CREDENTIAL);
        final String credentialPath = getOrNull(JCloudsProperties.CREDENTIAL_PATH);
        isNotNull(cloudProvider, "Cloud Provider");

        if (credential != null && credentialPath != null) {
            throw new UnsupportedOperationException(
                    "Both credential and credentialPath are set. Use only one method.");
        }
        if (credentialPath != null) {
            credential = getCredentialFromFile(credential, credentialPath);
        }

        if (LOGGER.isFinestEnabled()) {
            LOGGER.finest("Using CLOUD_PROVIDER: " + cloudProvider);
        }

        final String roleName = getOrNull(JCloudsProperties.ROLE_NAME);
        ContextBuilder contextBuilder = newContextBuilder(cloudProvider, identity, credential, roleName);

        Properties jcloudsProperties = buildRegionZonesConfig();
        buildTagConfig();
        buildNodeFilter();

        computeService = contextBuilder.overrides(jcloudsProperties).buildView(ComputeServiceContext.class)
                .getComputeService();

        return computeService;
    }

    public Properties buildRegionZonesConfig() {
        final String regions = getOrNull(JCloudsProperties.REGIONS);
        final String zones = getOrNull(JCloudsProperties.ZONES);

        Properties jcloudsProperties = newOverrideProperties();
        if (regions != null) {
            List<String> regionList = Arrays.asList(regions.split(","));
            for (String region : regionList) {
                regionsSet.add(region);
            }
            jcloudsProperties.setProperty(LocationConstants.PROPERTY_REGIONS, regions);
        }
        if (zones != null) {
            List<String> zoneList = Arrays.asList(zones.split(","));
            for (String zone : zoneList) {
                zonesSet.add(zone);
            }
            jcloudsProperties.setProperty(LocationConstants.PROPERTY_ZONES, zones);
        }
        return jcloudsProperties;
    }

    public void buildTagConfig() {
        final String tagKeys = getOrNull(JCloudsProperties.TAG_KEYS);
        final String tagValues = getOrNull(JCloudsProperties.TAG_VALUES);
        if (tagKeys != null && tagValues != null) {
            List<String> keysList = Arrays.asList(tagKeys.split(","));
            List<String> valueList = Arrays.asList(tagValues.split(","));
            if (keysList.size() != valueList.size()) {
                throw new InvalidConfigurationException("Tags keys and value count does not match.");
            }
            for (int i = 0; i < keysList.size(); i++) {
                tagPairs.add(new AbstractMap.SimpleImmutableEntry(keysList.get(i), valueList.get(i)));
            }
        }
    }

    public Predicate<ComputeMetadata> buildNodeFilter() {
        nodesFilter = new Predicate<ComputeMetadata>() {
            @Override
            public boolean apply(ComputeMetadata nodeMetadata) {
                if (nodeMetadata == null) {
                    return false;
                }
                if (tagPairs.size() > nodeMetadata.getUserMetadata().size()) {
                    return false;
                }
                for (AbstractMap.SimpleImmutableEntry entry : tagPairs) {
                    String value = nodeMetadata.getUserMetadata().get(entry.getKey());
                    if (value == null || !value.equals(entry.getValue())) {
                        return false;
                    }
                }
                return true;
            }
        };
        return nodesFilter;
    }

    public String getCredentialFromFile(String provider, String credentialPath) throws IllegalArgumentException {
        try {
            String fileContents = Files.toString(new File(credentialPath), Charsets.UTF_8);

            if (provider.equals(GOOGLE_COMPUTE_ENGINE)) {
                Supplier<Credentials> credentialSupplier = new GoogleCredentialsFromJson(fileContents);
                return credentialSupplier.get().credential;
            }

            return fileContents;
        } catch (IOException e) {
            throw new InvalidConfigurationException(
                    "Failed to retrieve the private key from the file: " + credentialPath, e);
        }
    }

    public ContextBuilder newContextBuilder(final String cloudProvider, final String identity,
            final String credential, final String roleName) {
        try {
            if (roleName != null && (identity != null || credential != null)) {
                throw new InvalidConfigurationException(
                        "IAM role is configured, identity " + "or credential propery is not allowed.");
            }
            if (roleName != null && !cloudProvider.equals(AWS_EC2)) {
                throw new InvalidConfigurationException(
                        "IAM role is only supported with aws-ec2, your cloud " + "provider is " + cloudProvider);
            }
            if (cloudProvider.equals(AWS_EC2) && roleName != null) {
                Supplier<Credentials> credentialsSupplier = new Supplier<Credentials>() {
                    @Override
                    public Credentials get() {
                        return new IAMRoleCredentialSupplierBuilder().withRoleName(roleName).build();
                    }
                };
                return ContextBuilder.newBuilder(cloudProvider).credentialsSupplier(credentialsSupplier);
            } else {
                checkNotNull(identity, "Cloud provider identity is not set");
                checkNotNull(credential, "Cloud provider credential is not set");
                return ContextBuilder.newBuilder(cloudProvider).credentials(identity, credential);
            }
        } catch (NoSuchElementException e) {
            throw new InvalidConfigurationException("Unrecognized cloud-provider [" + cloudProvider + "]");
        }
    }

    private Properties newOverrideProperties() {
        Properties properties = new Properties();
        properties.setProperty(Constants.PROPERTY_SO_TIMEOUT, JCLOUD_CONNECTION_TIMEOUT);
        properties.setProperty(Constants.PROPERTY_CONNECTION_TIMEOUT, JCLOUD_CONNECTION_TIMEOUT);
        return properties;
    }

    private <T extends Comparable> T getOrNull(PropertyDefinition property) {
        return getOrDefault(property, null);
    }

    private <T extends Comparable> T getOrDefault(PropertyDefinition property, T defaultValue) {

        if (properties == null || property == null) {
            return defaultValue;
        }

        Comparable value = properties.get(property.key());
        if (value == null) {
            return defaultValue;
        }

        return (T) value;
    }

}