org.apache.ignite.yarn.IgniteApplicationMasterSelfTest.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ignite.yarn.IgniteApplicationMasterSelfTest.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.ignite.yarn;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;
import junit.framework.TestCase;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.util.Progressable;
import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterResponse;
import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
import org.apache.hadoop.yarn.api.records.ContainerStatus;
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
import org.apache.hadoop.yarn.api.records.NodeId;
import org.apache.hadoop.yarn.api.records.Priority;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.client.api.AMRMClient;
import org.apache.hadoop.yarn.client.api.NMClient;
import org.apache.hadoop.yarn.client.api.async.AMRMClientAsync;
import org.apache.hadoop.yarn.exceptions.YarnException;

/**
 * Application master tests.
 */
public class IgniteApplicationMasterSelfTest extends TestCase {
    /** */
    private ApplicationMaster appMaster;

    /** */
    private ClusterProperties props;

    /** */
    private RMMock rmMock = new RMMock();

    /**
     * @throws Exception If failed.
     */
    @Override
    protected void setUp() throws Exception {
        super.setUp();

        props = new ClusterProperties();
        appMaster = new ApplicationMaster("test", props);

        appMaster.setSchedulerTimeout(10000);

        rmMock.clear();
    }

    /**
     * @throws Exception If failed.
     */
    public void testContainerAllocate() throws Exception {
        appMaster.setRmClient(rmMock);
        appMaster.setNmClient(new NMMock());

        props.cpusPerNode(2);
        props.memoryPerNode(1024);
        props.instances(3);

        Thread thread = runAppMaster(appMaster);

        List<AMRMClient.ContainerRequest> contRequests = collectRequests(rmMock, 2, 1000);

        interruptedThread(thread);

        assertEquals(3, contRequests.size());

        for (AMRMClient.ContainerRequest req : contRequests) {
            assertEquals(2, req.getCapability().getVirtualCores());
            assertEquals(1024, req.getCapability().getMemory());
        }
    }

    /**
     * Tests whether memory overhead is allocated within container memory.
     *
     * @throws Exception If failed.
     */
    public void testMemoryOverHeadAllocation() throws Exception {
        appMaster.setRmClient(rmMock);
        appMaster.setNmClient(new NMMock());

        props.cpusPerNode(2);
        props.memoryPerNode(1024);
        props.memoryOverHeadPerNode(512);
        props.instances(3);

        Thread thread = runAppMaster(appMaster);

        List<AMRMClient.ContainerRequest> contRequests = collectRequests(rmMock, 1, 1000);

        interruptedThread(thread);

        assertEquals(3, contRequests.size());

        for (AMRMClient.ContainerRequest req : contRequests) {
            assertEquals(2, req.getCapability().getVirtualCores());
            assertEquals(1024 + 512, req.getCapability().getMemory());
        }
    }

    /**
     * Tests whether memory overhead prevents from allocating container.
     *
     * @throws Exception If failed.
     */
    public void testMemoryOverHeadPreventAllocation() throws Exception {
        rmMock.availableRes(new MockResource(1024, 2));
        appMaster.setRmClient(rmMock);
        appMaster.setNmClient(new NMMock());

        props.cpusPerNode(2);
        props.memoryPerNode(1024);
        props.memoryOverHeadPerNode(512);
        props.instances(3);

        Thread thread = runAppMaster(appMaster);

        List<AMRMClient.ContainerRequest> contRequests = collectRequests(rmMock, 1, 1000);

        interruptedThread(thread);

        assertEquals(0, contRequests.size());
    }

    /**
     * @throws Exception If failed.
     */
    public void testClusterResource() throws Exception {
        rmMock.availableRes(new MockResource(1024, 2));

        appMaster.setRmClient(rmMock);
        appMaster.setNmClient(new NMMock());

        props.cpusPerNode(8);
        props.memoryPerNode(10240);
        props.instances(3);

        Thread thread = runAppMaster(appMaster);

        List<AMRMClient.ContainerRequest> contRequests = collectRequests(rmMock, 1, 1000);

        interruptedThread(thread);

        assertEquals(0, contRequests.size());
    }

    /**
     * @throws Exception If failed.
     */
    public void testClusterAllocatedResource() throws Exception {
        rmMock.availableRes(new MockResource(1024, 2));

        appMaster.setRmClient(rmMock);
        appMaster.setNmClient(new NMMock());

        appMaster.setFs(new MockFileSystem());

        props.cpusPerNode(8);
        props.memoryPerNode(5000);
        props.instances(3);

        // Check that container resources
        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 5, 2000)));
        assertEquals(0, appMaster.getContainers().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 10, 2000)));
        assertEquals(0, appMaster.getContainers().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 1, 7000)));
        assertEquals(0, appMaster.getContainers().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 8, 5000)));
        assertEquals(1, appMaster.getContainers().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 10, 7000)));
        assertEquals(2, appMaster.getContainers().size());
    }

    /**
     * @throws Exception If failed.
     */
    public void testStartReleaseContainer() throws Exception {
        rmMock.availableRes(new MockResource(1024, 2));

        NMMock nmClient = new NMMock();

        appMaster.setRmClient(rmMock);
        appMaster.setNmClient(nmClient);

        appMaster.setFs(new MockFileSystem());

        props.cpusPerNode(8);
        props.memoryPerNode(5000);
        props.instances(3);

        // Check that container resources
        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 5, 2000)));
        assertEquals(1, rmMock.releasedResources().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 5, 7000)));
        assertEquals(2, rmMock.releasedResources().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 9, 2000)));
        assertEquals(3, rmMock.releasedResources().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 8, 5000)));
        assertEquals(3, rmMock.releasedResources().size());
        assertEquals(1, nmClient.startedContainer().size());
    }

    /**
     * @throws Exception If failed.
     */
    public void testHostnameConstraint() throws Exception {
        rmMock.availableRes(new MockResource(1024, 2));

        NMMock nmClient = new NMMock();

        appMaster.setRmClient(rmMock);
        appMaster.setNmClient(nmClient);

        appMaster.setFs(new MockFileSystem());

        props.cpusPerNode(8);
        props.memoryPerNode(5000);
        props.instances(3);
        props.hostnameConstraint(Pattern.compile("ignoreHost"));

        // Check that container resources
        appMaster.onContainersAllocated(Collections.singletonList(createContainer("simple", 8, 5000)));
        assertEquals(0, rmMock.releasedResources().size());
        assertEquals(1, nmClient.startedContainer().size());

        appMaster.onContainersAllocated(Collections.singletonList(createContainer("ignoreHost", 8, 5000)));
        assertEquals(1, rmMock.releasedResources().size());
        assertEquals(1, nmClient.startedContainer().size());
    }

    /**
     * @throws Exception If failed.
     */
    public void testContainerEnvironment() throws Exception {
        props.memoryPerNode(1001);
        props.memoryOverHeadPerNode(2002);

        // Properties are used to initialize AM container environment
        Map<String, String> result = props.toEnvs();
        assertEquals(1001, (int) Double.parseDouble(result.get(ClusterProperties.IGNITE_MEMORY_PER_NODE)));
        assertEquals(2002, (int) Double.parseDouble(result.get(ClusterProperties.IGNITE_MEMORY_OVERHEAD_PER_NODE)));
    }

    /**
     * @param host Host.
     * @param cpu Cpu count.
     * @param mem Memory.
     * @return Container.
     */
    private Container createContainer(String host, int cpu, int mem) {
        return Container.newInstance(
                ContainerId.newContainerId(ApplicationAttemptId.newInstance(ApplicationId.newInstance(0l, 0), 0),
                        ThreadLocalRandom.current().nextLong()),
                NodeId.newInstance(host, 0), "example.com", new MockResource(mem, cpu), Priority.newInstance(0),
                null);
    }

    /**
     * @param rmMock RM mock.
     * @param expectedCnt Expected cnt.
     * @param timeOut Timeout.
     * @return Requests.
     */
    private List<AMRMClient.ContainerRequest> collectRequests(RMMock rmMock, int expectedCnt, int timeOut) {
        long startTime = System.currentTimeMillis();

        List<AMRMClient.ContainerRequest> requests = rmMock.requests();

        while (requests.size() < expectedCnt && (System.currentTimeMillis() - startTime) < timeOut)
            requests = rmMock.requests();

        return requests;
    }

    /**
     * Runs appMaster other thread.
     *
     * @param appMaster Application master.
     * @return Thread.
     */
    private static Thread runAppMaster(final ApplicationMaster appMaster) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    appMaster.run();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });

        thread.start();

        return thread;
    }

    /**
     * Interrupt thread and join.
     *
     * @param thread Thread.
     */
    private static void interruptedThread(Thread thread) throws InterruptedException {
        thread.interrupt();

        thread.join();
    }

    /**
     * Resource manager mock.
     */
    private static class RMMock extends AMRMClientAsync {
        /** */
        private List<AMRMClient.ContainerRequest> contRequests = new ArrayList<>();

        /** */
        private List<ContainerId> releasedConts = new ArrayList<>();

        /** */
        private Resource availableRes;

        /** */
        public RMMock() {
            super(0, null);
        }

        /**
         * @return Requests.
         */
        public List<AMRMClient.ContainerRequest> requests() {
            return contRequests;
        }

        /**
         * @return Released resources.
         */
        public List<ContainerId> releasedResources() {
            return releasedConts;
        }

        /**
         * Sets resource.
         *
         * @param availableRes Available resource.
         */
        public void availableRes(Resource availableRes) {
            this.availableRes = availableRes;
        }

        /**
         * Clear internal state.
         */
        public void clear() {
            contRequests.clear();
            releasedConts.clear();
            availableRes = null;
        }

        /** {@inheritDoc} */
        @Override
        public List<? extends Collection> getMatchingRequests(Priority priority, String resourceName,
                Resource capability) {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public RegisterApplicationMasterResponse registerApplicationMaster(String appHostName, int appHostPort,
                String appTrackingUrl) throws YarnException, IOException {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public void unregisterApplicationMaster(FinalApplicationStatus appStatus, String appMessage,
                String appTrackingUrl) throws YarnException, IOException {
            // No-op.
        }

        /** {@inheritDoc} */
        @Override
        public void addContainerRequest(AMRMClient.ContainerRequest req) {
            contRequests.add(req);
        }

        /** {@inheritDoc} */
        @Override
        public void removeContainerRequest(AMRMClient.ContainerRequest req) {
            // No-op.
        }

        /** {@inheritDoc} */
        @Override
        public void releaseAssignedContainer(ContainerId containerId) {
            releasedConts.add(containerId);
        }

        /** {@inheritDoc} */
        @Override
        public Resource getAvailableResources() {
            return availableRes;
        }

        /** {@inheritDoc} */
        @Override
        public int getClusterNodeCount() {
            return 0;
        }

        /**
         * Update application's blacklist with addition or removal resources.
         *
         * @param blacklistAdditions list of resources which should be added to the
         *        application blacklist
         * @param blacklistRemovals list of resources which should be removed from the
         *        application blacklist
         */
        public void updateBlacklist(List blacklistAdditions, List blacklistRemovals) {
            // No-op.
        }
    }

    /**
     * Network manager mock.
     */
    public static class NMMock extends NMClient {
        /** */
        private List<ContainerLaunchContext> startedContainer = new ArrayList<>();

        /** */
        public NMMock() {
            super("name");
        }

        /**
         * @return Started containers.
         */
        public List<ContainerLaunchContext> startedContainer() {
            return startedContainer;
        }

        /** {@inheritDoc} */
        @Override
        public Map<String, ByteBuffer> startContainer(Container container,
                ContainerLaunchContext containerLaunchContext) throws YarnException, IOException {

            startedContainer.add(containerLaunchContext);

            return null;
        }

        /** {@inheritDoc} */
        @Override
        public void stopContainer(ContainerId containerId, NodeId nodeId) throws YarnException, IOException {
            // No-op.
        }

        /** {@inheritDoc} */
        @Override
        public ContainerStatus getContainerStatus(ContainerId containerId, NodeId nodeId)
                throws YarnException, IOException {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public void cleanupRunningContainersOnStop(boolean enabled) {
            // No-op.
        }
    }

    /**
     * Resource.
     */
    public static class MockResource extends Resource {
        /** Memory. */
        private int mem;

        /** CPU. */
        private int cpu;

        /**
         * @param mem Memory.
         * @param cpu CPU.
         */
        public MockResource(int mem, int cpu) {
            this.mem = mem;
            this.cpu = cpu;
        }

        /** {@inheritDoc} */
        @Override
        public int getMemory() {
            return mem;
        }

        /** {@inheritDoc} */
        @Override
        public void setMemory(int memory) {
            this.mem = memory;
        }

        /** {@inheritDoc} */
        @Override
        public int getVirtualCores() {
            return cpu;
        }

        /** {@inheritDoc} */
        @Override
        public void setVirtualCores(int vCores) {
            this.cpu = vCores;
        }

        /** {@inheritDoc} */
        @Override
        public int compareTo(Resource resource) {
            return 0;
        }
    }

    /**
     * Mock file system.
     */
    public static class MockFileSystem extends FileSystem {
        /** */
        public MockFileSystem() {
        }

        /** {@inheritDoc} */
        @Override
        public Path makeQualified(Path path) {
            return new Path("/test/path");
        }

        /** {@inheritDoc} */
        @Override
        public FileStatus getFileStatus(Path f) throws IOException {
            return new FileStatus();
        }

        /** {@inheritDoc} */
        @Override
        public boolean mkdirs(Path f, FsPermission permission) throws IOException {
            return false;
        }

        /** {@inheritDoc} */
        @Override
        public Path getWorkingDirectory() {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public void setWorkingDirectory(Path new_dir) {
            // No-op.
        }

        /** {@inheritDoc} */
        @Override
        public FileStatus[] listStatus(Path f) throws FileNotFoundException, IOException {
            return new FileStatus[0];
        }

        /** {@inheritDoc} */
        @Override
        public boolean delete(Path f, boolean recursive) throws IOException {
            return false;
        }

        /** {@inheritDoc} */
        @Override
        public boolean rename(Path src, Path dst) throws IOException {
            return false;
        }

        /** {@inheritDoc} */
        @Override
        public FSDataOutputStream append(Path f, int bufferSize, Progressable progress) throws IOException {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize,
                short replication, long blockSize, Progressable progress) throws IOException {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public FSDataInputStream open(Path f, int bufferSize) throws IOException {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public URI getUri() {
            return null;
        }
    }
}