alluxio.yarn.ContainerAllocatorTest.java Source code

Java tutorial

Introduction

Here is the source code for alluxio.yarn.ContainerAllocatorTest.java

Source

/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.yarn;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import alluxio.exception.ExceptionMessage;

import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.NodeId;
import org.apache.hadoop.yarn.api.records.NodeReport;
import org.apache.hadoop.yarn.api.records.NodeState;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.client.api.AMRMClient.ContainerRequest;
import org.apache.hadoop.yarn.client.api.YarnClient;
import org.apache.hadoop.yarn.client.api.async.AMRMClientAsync;
import org.apache.hadoop.yarn.util.Records;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentMatcher;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Unit tests for {@link ContainerAllocator}.
 */
public final class ContainerAllocatorTest {
    private static final String CONTAINER_NAME = "test";

    private Resource mResource;
    private YarnClient mYarnClient;
    private AMRMClientAsync<ContainerRequest> mRMClient;

    @Rule
    public ExpectedException mThrown = ExpectedException.none();

    @Before
    @SuppressWarnings("unchecked")
    public void before() {
        mResource = mock(Resource.class);
        mYarnClient = mock(YarnClient.class);
        mRMClient = (AMRMClientAsync<ContainerRequest>) mock(AMRMClientAsync.class);
    }

    @Test(timeout = 10000)
    public void oneContainerPerHostFullAllocation() throws Exception {
        int numHosts = 10;
        int maxContainersPerHost = 1;
        testFullAllocation(numHosts, maxContainersPerHost);
    }

    @Test(timeout = 10000)
    public void fiveContainersPerHostFullAllocation() throws Exception {
        int numHosts = 10;
        int maxContainersPerHost = 5;
        testFullAllocation(numHosts, maxContainersPerHost);
    }

    @Test(timeout = 10000)
    public void fiveContainersPerHostHalfAllocation() throws Exception {
        int numHosts = 10;
        int maxContainersPerHost = 5;
        int numContainers = numHosts * maxContainersPerHost / 2;
        ContainerAllocator containerAllocator = setup(numHosts, maxContainersPerHost, numContainers);
        List<Container> containers = containerAllocator.allocateContainers();

        assertEquals(numContainers, containers.size());
        checkMaxHostsLimitNotExceeded(containers, maxContainersPerHost);
    }

    @Test(timeout = 10000)
    public void notEnoughHosts() throws Exception {
        int numHosts = 10;
        int maxContainersPerHost = 5;
        int numContainers = numHosts * maxContainersPerHost + 1; // one container too many
        ContainerAllocator containerAllocator = setup(numHosts, maxContainersPerHost, numContainers);
        mThrown.expect(RuntimeException.class);
        mThrown.expectMessage(
                ExceptionMessage.YARN_NOT_ENOUGH_HOSTS.getMessage(numContainers, CONTAINER_NAME, numHosts));
        containerAllocator.allocateContainers();
    }

    @Test(timeout = 1000)
    public void allocateMasterInAnyHost() throws Exception {
        ContainerAllocator containerAllocator = new ContainerAllocator(CONTAINER_NAME, 1, 1, mResource, mYarnClient,
                mRMClient, "any");
        doAnswer(allocateFirstHostAnswer(containerAllocator)).when(mRMClient)
                .addContainerRequest(Matchers.argThat(new ArgumentMatcher<ContainerRequest>() {
                    @Override
                    public boolean matches(Object o) {
                        ContainerRequest request = (ContainerRequest) o;
                        if (request.getRelaxLocality() == true && request.getNodes().size() == 1
                                && request.getNodes().get(0).equals("any")) {
                            return true;
                        }
                        return false;
                    }
                }));
        containerAllocator.allocateContainers();
    }

    /*
     * Creates a container allocator for allocating the specified numContainers with the specified
     * maxContainersPerHost.
     *
     * The yarn client is mocked to make it look like there are numHosts different hosts in the
     * system, and the resource manager client is mocked to allocate containers when they are
     * requested.
     */
    private ContainerAllocator setup(int numHosts, int maxContainersPerHost, int numContainers) throws Exception {
        ContainerAllocator containerAllocator = new ContainerAllocator(CONTAINER_NAME, numContainers,
                maxContainersPerHost, mResource, mYarnClient, mRMClient);
        List<NodeReport> nodeReports = new ArrayList<>();
        for (int i = 0; i < numHosts; i++) {
            NodeReport nodeReport = Records.newRecord(NodeReport.class);
            nodeReport.setNodeId(NodeId.newInstance("host" + i, 0));
            nodeReports.add(nodeReport);
        }
        when(mYarnClient.getNodeReports(Matchers.<NodeState[]>anyVararg())).thenReturn(nodeReports);
        doAnswer(allocateFirstHostAnswer(containerAllocator)).when(mRMClient)
                .addContainerRequest(any(ContainerRequest.class));
        return containerAllocator;
    }

    /*
     * Tests that when there are the specified number of hosts and the specified max containers per
     * host, a ContainerAllocator can fully allocate all hosts so that every host has
     * maxContainersPerHost containers.
     */
    private void testFullAllocation(int numHosts, int maxContainersPerHost) throws Exception {
        int numContainers = numHosts * maxContainersPerHost;
        ContainerAllocator containerAllocator = setup(numHosts, maxContainersPerHost, numContainers);
        List<Container> containers = containerAllocator.allocateContainers();

        Set<String> containerHosts = new HashSet<>();
        for (Container container : containers) {
            containerHosts.add(container.getNodeId().getHost());
        }
        assertEquals("All hosts are allocated", numHosts, containerHosts.size());
        assertEquals("All containers are allocated", numContainers, containers.size());
        checkMaxHostsLimitNotExceeded(containers, maxContainersPerHost);
    }

    /*
     * Creates an Answer to an addContainerRequest method. The Answer picks the first node requested
     * and allocates a container on it, sending a callback to the specified container allocator
     */
    private Answer<Void> allocateFirstHostAnswer(final ContainerAllocator containerAllocator) {
        return new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                ContainerRequest containerRequest = invocation.getArgumentAt(0, ContainerRequest.class);
                Container container = Records.newRecord(Container.class);
                container.setNodeId(NodeId.newInstance(containerRequest.getNodes().get(0), 0));
                containerAllocator.allocateContainer(container);
                return null;
            }
        };
    }

    private void checkMaxHostsLimitNotExceeded(List<Container> containers, int maxContainersPerHost) {
        ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
        for (Container container : containers) {
            String host = container.getNodeId().getHost();
            counts.putIfAbsent(host, 0);
            int newCount = counts.get(host) + 1;
            assertTrue(newCount <= maxContainersPerHost);
            counts.put(host, newCount);
        }
    }
}