All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.finra.herd.service.helper.EmrClusterDefinitionHelper Maven / Gradle / Ivy

Go to download

This project contains the business service code. This is a classic service tier where business logic is defined along with it's associated transaction management configuration.

There is a newer version: 0.160.0
Show newest version
/*
* Copyright 2015 herd contributors
*
* 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 org.finra.herd.service.helper;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import org.finra.herd.core.helper.ConfigurationHelper;
import org.finra.herd.dao.helper.EmrHelper;
import org.finra.herd.dao.helper.HerdStringHelper;
import org.finra.herd.model.api.xml.EmrClusterDefinition;
import org.finra.herd.model.api.xml.EmrClusterDefinitionKey;
import org.finra.herd.model.api.xml.InstanceDefinition;
import org.finra.herd.model.api.xml.MasterInstanceDefinition;
import org.finra.herd.model.api.xml.NodeTag;
import org.finra.herd.model.dto.ConfigurationValue;

/**
 * A helper class for EmrClusterDefinition related code.
 */
@Component
public class EmrClusterDefinitionHelper
{
    @Autowired
    private AlternateKeyHelper alternateKeyHelper;

    @Autowired
    private ConfigurationHelper configurationHelper;

    @Autowired
    private EmrHelper emrHelper;

    @Autowired
    private HerdStringHelper herdStringHelper;

    /**
     * Validates an EMR cluster definition configuration.
     *
     * @param emrClusterDefinition the EMR cluster definition configuration
     *
     * @throws IllegalArgumentException if any validation errors were found
     */
    public void validateEmrClusterDefinitionConfiguration(EmrClusterDefinition emrClusterDefinition) throws IllegalArgumentException
    {
        Assert.notNull(emrClusterDefinition, "An EMR cluster definition configuration must be specified.");

        Assert.isTrue(StringUtils.isNotBlank(emrClusterDefinition.getSubnetId()), "Subnet ID must be specified");
        for (String token : emrClusterDefinition.getSubnetId().split(","))
        {
            Assert.isTrue(StringUtils.isNotBlank(token), "No blank is allowed in the list of subnet IDs");
        }

        Assert.isTrue(!emrHelper.isInstanceDefinitionsEmpty(emrClusterDefinition.getInstanceDefinitions()) ||
            CollectionUtils.isNotEmpty(emrClusterDefinition.getInstanceFleets()), "Instance group definitions or instance fleets must be specified.");

        if (!emrHelper.isInstanceDefinitionsEmpty(emrClusterDefinition.getInstanceDefinitions()))
        {
            // Check master instances.
            Assert.notNull(emrClusterDefinition.getInstanceDefinitions().getMasterInstances(), "Master instances must be specified.");
            validateMasterInstanceDefinition(emrClusterDefinition.getInstanceDefinitions().getMasterInstances());

            // Check core instances.
            if (emrClusterDefinition.getInstanceDefinitions().getCoreInstances() != null)
            {
                validateInstanceDefinition("core", emrClusterDefinition.getInstanceDefinitions().getCoreInstances(), 0);
                // If instance count is <= 0, remove the entire core instance definition since it is redundant.
                if (emrClusterDefinition.getInstanceDefinitions().getCoreInstances().getInstanceCount() <= 0)
                {
                    emrClusterDefinition.getInstanceDefinitions().setCoreInstances(null);
                }
            }

            // Check task instances
            if (emrClusterDefinition.getInstanceDefinitions().getTaskInstances() != null)
            {
                validateInstanceDefinition("task", emrClusterDefinition.getInstanceDefinitions().getTaskInstances(), 1);
            }

            // Check that total number of instances does not exceed the max allowed.
            int maxEmrInstanceCount = configurationHelper.getProperty(ConfigurationValue.MAX_EMR_INSTANCES_COUNT, Integer.class);
            if (maxEmrInstanceCount > 0)
            {
                int instancesRequested = emrClusterDefinition.getInstanceDefinitions().getMasterInstances().getInstanceCount();
                if (emrClusterDefinition.getInstanceDefinitions().getCoreInstances() != null)
                {
                    instancesRequested += emrClusterDefinition.getInstanceDefinitions().getCoreInstances().getInstanceCount();
                }
                if (emrClusterDefinition.getInstanceDefinitions().getTaskInstances() != null)
                {
                    instancesRequested += emrClusterDefinition.getInstanceDefinitions().getTaskInstances().getInstanceCount();
                }

                Assert.isTrue((maxEmrInstanceCount >= instancesRequested), "Total number of instances requested can not exceed : " + maxEmrInstanceCount);
            }
        }

        // Validate node tags including checking for required tags and detecting any duplicate node tag names in case sensitive manner.
        Assert.notEmpty(emrClusterDefinition.getNodeTags(), "Node tags must be specified.");
        HashSet nodeTagNameValidationSet = new HashSet<>();
        for (NodeTag nodeTag : emrClusterDefinition.getNodeTags())
        {
            Assert.hasText(nodeTag.getTagName(), "A node tag name must be specified.");
            Assert.hasText(nodeTag.getTagValue(), "A node tag value must be specified.");
            Assert.isTrue(!nodeTagNameValidationSet.contains(nodeTag.getTagName()), String.format("Duplicate node tag \"%s\" is found.", nodeTag.getTagName()));
            nodeTagNameValidationSet.add(nodeTag.getTagName());
        }

        // Validate the mandatory AWS tags are there
        for (String mandatoryTag : herdStringHelper.splitStringWithDefaultDelimiter(configurationHelper.getProperty(ConfigurationValue.MANDATORY_AWS_TAGS)))
        {
            Assert.isTrue(nodeTagNameValidationSet.contains(mandatoryTag), String.format("Mandatory AWS tag not specified: \"%s\"", mandatoryTag));
        }

        emrClusterDefinition.setAdditionalMasterSecurityGroups(
            assertNotBlankAndTrim(emrClusterDefinition.getAdditionalMasterSecurityGroups(), "additionalMasterSecurityGroup"));

        emrClusterDefinition
            .setAdditionalSlaveSecurityGroups(assertNotBlankAndTrim(emrClusterDefinition.getAdditionalSlaveSecurityGroups(), "additionalSlaveSecurityGroup"));

        // Fail if security configuration is specified for EMR version less than 4.8.0.
        if (StringUtils.isNotBlank(emrClusterDefinition.getSecurityConfiguration()))
        {
            final DefaultArtifactVersion securityConfigurationMinEmrVersion = new DefaultArtifactVersion("4.8.0");
            Assert.isTrue(StringUtils.isNotBlank(emrClusterDefinition.getReleaseLabel()) &&
                securityConfigurationMinEmrVersion.compareTo(new DefaultArtifactVersion(emrClusterDefinition.getReleaseLabel().replaceFirst("^(emr-)", ""))) <=
                    0, "EMR security configuration is not supported prior to EMR release 4.8.0.");
        }
    }

    /**
     * Validates the EMR cluster definition key. This method also trims the key parameters.
     *
     * @param key the EMR cluster definition key
     *
     * @throws IllegalArgumentException if any validation errors were found
     */
    public void validateEmrClusterDefinitionKey(EmrClusterDefinitionKey key) throws IllegalArgumentException
    {
        Assert.notNull(key, "An EMR cluster definition key must be specified.");
        key.setNamespace(alternateKeyHelper.validateStringParameter("namespace", key.getNamespace()));
        key.setEmrClusterDefinitionName(alternateKeyHelper.validateStringParameter("An", "EMR cluster definition name", key.getEmrClusterDefinitionName()));
    }

    /**
     * Asserts that the given list of string contains no blank string and returns a copy of the list with all the values trimmed. If the given list is null, a
     * null is returned.
     *
     * @param list The list of string
     * @param displayName The display name of the element in the list used to construct the error message
     *
     * @return Copy of list with trimmed values
     */
    private List assertNotBlankAndTrim(List list, String displayName)
    {
        List trimmed = null;
        if (list != null)
        {
            for (String string : list)
            {
                Assert.hasText(string, displayName + " must not be blank");
            }
            trimmed = new ArrayList<>();
            for (String string : list)
            {
                trimmed.add(string.trim());
            }
        }
        return trimmed;
    }

    /**
     * Validates the given instance definition. Generates an appropriate error message using the given name. The name specified is one of "master", "core", or
     * "task".
     *
     * @param name name of instance group
     * @param instanceDefinition the instance definition to validate
     * @param minimumInstanceCount The minimum instance count.
     *
     * @throws IllegalArgumentException when any validation error occurs
     */
    private void validateInstanceDefinition(String name, InstanceDefinition instanceDefinition, Integer minimumInstanceCount)
    {
        String capitalizedName = StringUtils.capitalize(name);

        Assert.isTrue(instanceDefinition.getInstanceCount() >= minimumInstanceCount,
            String.format("At least %d %s instance must be specified.", minimumInstanceCount, name));
        Assert.hasText(instanceDefinition.getInstanceType(), "An instance type for " + name + " instances must be specified.");

        if (instanceDefinition.getInstanceSpotPrice() != null)
        {
            Assert.isNull(instanceDefinition.getInstanceMaxSearchPrice(),
                capitalizedName + " instance max search price must not be specified when instance spot price is specified.");

            Assert.isTrue(instanceDefinition.getInstanceSpotPrice().compareTo(BigDecimal.ZERO) > 0,
                capitalizedName + " instance spot price must be greater than 0");
        }

        if (instanceDefinition.getInstanceMaxSearchPrice() != null)
        {
            Assert.isNull(instanceDefinition.getInstanceSpotPrice(),
                capitalizedName + " instance spot price must not be specified when max search price is specified.");

            Assert.isTrue(instanceDefinition.getInstanceMaxSearchPrice().compareTo(BigDecimal.ZERO) > 0,
                capitalizedName + " instance max search price must be greater than 0");

            if (instanceDefinition.getInstanceOnDemandThreshold() != null)
            {
                Assert.isTrue(instanceDefinition.getInstanceOnDemandThreshold().compareTo(BigDecimal.ZERO) > 0,
                    capitalizedName + " instance on-demand threshold must be greater than 0");
            }
        }
        else
        {
            Assert.isNull(instanceDefinition.getInstanceOnDemandThreshold(),
                capitalizedName + " instance on-demand threshold must not be specified when instance max search price is not specified.");
        }
    }

    /**
     * Converts the given master instance definition to a generic instance definition and delegates to validateInstanceDefinition(). Generates an appropriate
     * error message using the name "master".
     *
     * @param masterInstanceDefinition the master instance definition to validate
     *
     * @throws IllegalArgumentException when any validation error occurs
     */
    private void validateMasterInstanceDefinition(MasterInstanceDefinition masterInstanceDefinition)
    {
        InstanceDefinition instanceDefinition = new InstanceDefinition();
        instanceDefinition.setInstanceCount(masterInstanceDefinition.getInstanceCount());
        instanceDefinition.setInstanceMaxSearchPrice(masterInstanceDefinition.getInstanceMaxSearchPrice());
        instanceDefinition.setInstanceOnDemandThreshold(masterInstanceDefinition.getInstanceOnDemandThreshold());
        instanceDefinition.setInstanceSpotPrice(masterInstanceDefinition.getInstanceSpotPrice());
        instanceDefinition.setInstanceType(masterInstanceDefinition.getInstanceType());
        validateInstanceDefinition("master", instanceDefinition, 1);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy