Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 5845D200C5C for ; Thu, 20 Apr 2017 17:53:37 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 56AE3160B9F; Thu, 20 Apr 2017 15:53:37 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id DF11D160BB0 for ; Thu, 20 Apr 2017 17:53:34 +0200 (CEST) Received: (qmail 13533 invoked by uid 500); 20 Apr 2017 15:53:31 -0000 Mailing-List: contact common-commits-help@hadoop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Delivered-To: mailing list common-commits@hadoop.apache.org Received: (qmail 13054 invoked by uid 99); 20 Apr 2017 15:53:31 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 20 Apr 2017 15:53:31 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id C0A6AF4A29; Thu, 20 Apr 2017 15:53:30 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jianhe@apache.org To: common-commits@hadoop.apache.org Date: Thu, 20 Apr 2017 15:53:36 -0000 Message-Id: <89cc0abf60a240acaa71e6a5635a9495@git.apache.org> In-Reply-To: <9ba65d655bc9475a91489a2e35890af3@git.apache.org> References: <9ba65d655bc9475a91489a2e35890af3@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [07/11] hadoop git commit: YARN-6335. Port slider's groovy unit tests to yarn native services. Contributed by Billie Rinaldi archived-at: Thu, 20 Apr 2017 15:53:37 -0000 http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAOvercapacity.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAOvercapacity.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAOvercapacity.java new file mode 100644 index 0000000..e339a0a --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAOvercapacity.java @@ -0,0 +1,112 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.hadoop.yarn.api.records.Container; +import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.slider.core.main.LauncherExitCodes; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.model.mock.MockYarnEngine; +import org.apache.slider.server.appmaster.operations.AbstractRMOperation; +import org.apache.slider.server.appmaster.state.AppState; +import org.apache.slider.server.appmaster.state.NodeInstance; +import org.apache.slider.server.appmaster.state.NodeMap; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.apache.slider.server.appmaster.state.RoleStatus; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test Anti-affine placement with a cluster of size 1. + */ +public class TestMockAppStateAAOvercapacity extends BaseMockAppStateAATest + implements MockRoles { + + private static final int NODES = 1; + + @Override + public MockYarnEngine createYarnEngine() { + return new MockYarnEngine(NODES, 1); + } + + void assertAllContainersAA() { + assertAllContainersAA(getAaRole().getKey()); + } + + /** + * + * @throws Throwable + */ + @Test + public void testOvercapacityRecovery() throws Throwable { + RoleStatus aaRole = getAaRole(); + + describe("Ask for 1 more than the no of available nodes;" + + "verify the state. kill the allocated container and review"); + //more than expected + int desired = 3; + aaRole.setDesired(desired); + assertTrue(appState.getRoleHistory().canPlaceAANodes()); + + //first request + List operations = + appState.reviewRequestAndReleaseNodes(); + assertTrue(aaRole.isAARequestOutstanding()); + assertEquals(1, aaRole.getRequested()); + assertEquals(desired - 1, aaRole.getAAPending()); + List operationsOut = new ArrayList<>(); + // allocate and re-submit + List instances = submitOperations(operations, + EMPTY_ID_LIST, operationsOut); + assertEquals(1, instances.size()); + assertAllContainersAA(); + + // expect an outstanding AA request to be unsatisfied + assertTrue(aaRole.getRunning() < aaRole.getDesired()); + assertEquals(0, aaRole.getRequested()); + assertFalse(aaRole.isAARequestOutstanding()); + assertEquals(desired - 1, aaRole.getAAPending()); + List allocatedContainers = engine.execute(operations, + EMPTY_ID_LIST); + assertEquals(0, allocatedContainers.size()); + + // now lets trigger a failure + NodeMap nodemap = cloneNodemap(); + assertEquals(1, nodemap.size()); + + RoleInstance instance = instances.get(0); + ContainerId cid = instance.getContainerId(); + + AppState.NodeCompletionResult result = appState.onCompletedContainer( + containerStatus(cid, LauncherExitCodes.EXIT_TASK_LAUNCH_FAILURE)); + assertTrue(result.containerFailed); + + assertEquals(1, aaRole.getFailed()); + assertEquals(0, aaRole.getRunning()); + List availablePlacements = appState.getRoleHistory() + .findNodeForNewAAInstance(aaRole); + assertEquals(1, availablePlacements.size()); + describe("expecting a successful review with available placements of " + + availablePlacements); + operations = appState.reviewRequestAndReleaseNodes(); + assertEquals(1, operations.size()); + } +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAPlacement.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAPlacement.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAPlacement.java new file mode 100644 index 0000000..eb25b40 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateAAPlacement.java @@ -0,0 +1,380 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.hadoop.yarn.api.records.Container; +import org.apache.hadoop.yarn.api.records.NodeState; +import org.apache.hadoop.yarn.client.api.AMRMClient; +import org.apache.hadoop.yarn.client.api.AMRMClient.ContainerRequest; +import org.apache.slider.api.types.NodeInformation; +import org.apache.slider.common.tools.SliderUtils; +import org.apache.slider.providers.PlacementPolicy; +import org.apache.slider.server.appmaster.model.mock.MockAppState; +import org.apache.slider.server.appmaster.model.mock.MockFactory; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.model.mock.MockYarnEngine; +import org.apache.slider.server.appmaster.operations.AbstractRMOperation; +import org.apache.slider.server.appmaster.state.AppState; +import org.apache.slider.server.appmaster.state.AppState.NodeUpdatedOutcome; +import org.apache.slider.server.appmaster.state.AppStateBindingInfo; +import org.apache.slider.server.appmaster.state.ContainerAssignment; +import org.apache.slider.server.appmaster.state.NodeInstance; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.apache.slider.server.appmaster.state.RoleStatus; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.apache.slider.api.ResourceKeys.COMPONENT_PLACEMENT_POLICY; +import static org.apache.slider.server.appmaster.model.mock.MockFactory.AAROLE_2; + +/** + * Test Anti-affine placement. + */ +public class TestMockAppStateAAPlacement extends BaseMockAppStateAATest + implements MockRoles { + private static final Logger LOG = + LoggerFactory.getLogger(TestMockAppStateAAPlacement.class); + + private static final int NODES = 3; + + /** + * The YARN engine has a cluster with very few nodes (3) and lots of + * containers, so if AA placement isn't working, there will be affine + * placements surfacing. + * @return + */ + @Override + public MockYarnEngine createYarnEngine() { + return new MockYarnEngine(NODES, 8); + } + + /** + * This is the simplest AA allocation: no labels, so allocate anywhere. + * @throws Throwable + */ + @Test + public void testAllocateAANoLabel() throws Throwable { + RoleStatus aaRole = getAaRole(); + + assertTrue(cloneNodemap().size() > 0); + + // want multiple instances, so there will be iterations + aaRole.setDesired(2); + + List ops = appState.reviewRequestAndReleaseNodes(); + AMRMClient.ContainerRequest request = getSingleRequest(ops); + assertFalse(request.getRelaxLocality()); + assertEquals(request.getNodes().size(), engine.getCluster() + .getClusterSize()); + assertNull(request.getRacks()); + assertNotNull(request.getCapability()); + + Container allocated = engine.allocateContainer(request); + + // notify the container ane expect + List assignments = new ArrayList<>(); + List operations = new ArrayList<>(); + appState.onContainersAllocated(Arrays.asList(allocated), assignments, + operations); + + String host = allocated.getNodeId().getHost(); + NodeInstance hostInstance = cloneNodemap().get(host); + assertEquals(1, hostInstance.get(aaRole.getKey()).getStarting()); + assertFalse(hostInstance.canHost(aaRole.getKey(), "")); + assertFalse(hostInstance.canHost(aaRole.getKey(), null)); + + // assignment + assertEquals(1, assignments.size()); + + // verify the release matches the allocation + assertEquals(2, operations.size()); + assertNotNull(getCancel(operations, 0).getCapability().equals(allocated + .getResource())); + + // we also expect a new allocation request to have been issued + + ContainerRequest req2 = getRequest(operations, 1); + assertEquals(req2.getNodes().size(), engine.getCluster() + .getClusterSize() - 1); + + assertFalse(req2.getNodes().contains(host)); + assertFalse(request.getRelaxLocality()); + + // verify the pending couner is down + assertEquals(0L, aaRole.getAAPending()); + Container allocated2 = engine.allocateContainer(req2); + + // placement must be on a different host + assertNotEquals(allocated2.getNodeId(), allocated.getNodeId()); + + ContainerAssignment assigned = assignments.get(0); + Container container = assigned.container; + RoleInstance ri = roleInstance(assigned); + //tell the app it arrived + appState.containerStartSubmitted(container, ri); + assertNotNull(appState.onNodeManagerContainerStarted(container.getId())); + ops = appState.reviewRequestAndReleaseNodes(); + assertEquals(0, ops.size()); + assertAllContainersAA(); + + // identify those hosts with an aa role on + Map naming = appState.buildNamingMap(); + assertEquals(3, naming.size()); + + String name = aaRole.getName(); + assertEquals(name, naming.get(aaRole.getKey())); + Map info = + appState.getRoleHistory().getNodeInformationSnapshot(naming); + assertTrue(SliderUtils.isNotEmpty(info)); + + NodeInformation nodeInformation = info.get(host); + assertNotNull(nodeInformation); + assertTrue(SliderUtils.isNotEmpty(nodeInformation.entries)); + assertNotNull(nodeInformation.entries.get(name)); + assertEquals(1, nodeInformation.entries.get(name).live); + } + + @Test + public void testAllocateFlexUp() throws Throwable { + RoleStatus aaRole = getAaRole(); + + // want multiple instances, so there will be iterations + aaRole.setDesired(2); + List ops = appState.reviewRequestAndReleaseNodes(); + getSingleRequest(ops); + assertEquals(1, aaRole.getRequested()); + assertEquals(1, aaRole.getAAPending()); + assertEquals(aaRole.getActualAndRequested() + aaRole + .getAAPending(), aaRole.getDesired()); + + // now trigger that flex up + aaRole.setDesired(3); + + // expect: no new reqests, pending count ++ + List ops2 = appState.reviewRequestAndReleaseNodes(); + assertTrue(ops2.isEmpty()); + assertEquals(aaRole.getRunning() + aaRole.getAAPending() + + aaRole.getOutstandingAARequestCount(), aaRole.getDesired()); + + // 1 outstanding + assertEquals(0, aaRole.getRunning()); + assertTrue(aaRole.isAARequestOutstanding()); + // and one AA + assertEquals(2, aaRole.getAAPending()); + assertAllContainersAA(); + + // next iter + assertEquals(1, submitOperations(ops, EMPTY_ID_LIST, ops2).size()); + assertEquals(2, ops2.size()); + assertEquals(1, aaRole.getAAPending()); + assertAllContainersAA(); + + assertEquals(0, appState.reviewRequestAndReleaseNodes().size()); + // now trigger the next execution cycle + List ops3 = new ArrayList<>(); + assertEquals(1, submitOperations(ops2, EMPTY_ID_LIST, ops3).size()); + assertEquals(2, ops3.size()); + assertEquals(0, aaRole.getAAPending()); + assertAllContainersAA(); + + } + + @Test + public void testAllocateFlexDownDecrementsPending() throws Throwable { + RoleStatus aaRole = getAaRole(); + + // want multiple instances, so there will be iterations + aaRole.setDesired(2); + List ops = appState.reviewRequestAndReleaseNodes(); + getSingleRequest(ops); + assertEquals(1, aaRole.getAAPending()); + assertTrue(aaRole.isAARequestOutstanding()); + + // flex down so that the next request should be cancelled + aaRole.setDesired(1); + + // expect: no new requests, pending count -- + List ops2 = appState.reviewRequestAndReleaseNodes(); + assertTrue(ops2.isEmpty()); + assertTrue(aaRole.isAARequestOutstanding()); + assertEquals(0, aaRole.getAAPending()); + assertAllContainersAA(); + + // next iter + submitOperations(ops, EMPTY_ID_LIST, ops2).size(); + assertEquals(1, ops2.size()); + assertAllContainersAA(); + } + + /** + * Here flex down while there is only one outstanding request. + * The outstanding flex should be cancelled + * @throws Throwable + */ + @Test + public void testAllocateFlexDownForcesCancel() throws Throwable { + RoleStatus aaRole = getAaRole(); + + // want multiple instances, so there will be iterations + aaRole.setDesired(1); + List ops = appState.reviewRequestAndReleaseNodes(); + getSingleRequest(ops); + assertEquals(1, aaRole.getRequested()); + assertEquals(0, aaRole.getAAPending()); + assertTrue(aaRole.isAARequestOutstanding()); + + // flex down so that the next request should be cancelled + aaRole.setDesired(0); + // expect: no new requests, pending count -- + List ops2 = appState.reviewRequestAndReleaseNodes(); + assertEquals(0, aaRole.getRequested()); + assertEquals(0, aaRole.getAAPending()); + assertFalse(aaRole.isAARequestOutstanding()); + assertEquals(1, ops2.size()); + getSingleCancel(ops2); + + // next iter + submitOperations(ops, EMPTY_ID_LIST, ops2).size(); + getSingleRelease(ops2); + } + + void assertAllContainersAA() { + assertAllContainersAA(getAaRole().getKey()); + } + + /** + * + * @throws Throwable + */ + @Test + public void testAskForTooMany() throws Throwable { + RoleStatus aaRole = getAaRole(); + + describe("Ask for 1 more than the no of available nodes;" + + " expect the final request to be unsatisfied until the cluster " + + "changes size"); + //more than expected + aaRole.setDesired(NODES + 1); + List operations = appState + .reviewRequestAndReleaseNodes(); + assertTrue(aaRole.isAARequestOutstanding()); + assertEquals(NODES, aaRole.getAAPending()); + for (int i = 0; i < NODES; i++) { + String iter = "Iteration " + i + " role = " + aaRole; + LOG.info(iter); + List operationsOut = new ArrayList<>(); + assertEquals(1, submitOperations(operations, EMPTY_ID_LIST, + operationsOut).size()); + operations = operationsOut; + if (i + 1 < NODES) { + assertEquals(2, operations.size()); + } else { + assertEquals(1, operations.size()); + } + assertAllContainersAA(); + } + // expect an outstanding AA request to be unsatisfied + assertTrue(aaRole.getRunning() < aaRole.getDesired()); + assertEquals(0, aaRole.getRequested()); + assertFalse(aaRole.isAARequestOutstanding()); + List allocatedContainers = engine.execute(operations, + EMPTY_ID_LIST); + assertEquals(0, allocatedContainers.size()); + // in a review now, no more requests can be generated, as there is no + // space for AA placements, even though there is cluster capacity + assertEquals(0, appState.reviewRequestAndReleaseNodes().size()); + + // now do a node update (this doesn't touch the YARN engine; the node + // isn't really there) + NodeUpdatedOutcome outcome = addNewNode(); + assertEquals(cloneNodemap().size(), NODES + 1); + assertTrue(outcome.clusterChanged); + // no active calls to empty + assertTrue(outcome.operations.isEmpty()); + assertEquals(1, appState.reviewRequestAndReleaseNodes().size()); + } + + protected AppState.NodeUpdatedOutcome addNewNode() { + return updateNodes(MockFactory.INSTANCE.newNodeReport("4", NodeState + .RUNNING, "gpu")); + } + + @Test + public void testClusterSizeChangesDuringRequestSequence() throws Throwable { + RoleStatus aaRole = getAaRole(); + describe("Change the cluster size where the cluster size changes during " + + "a test sequence."); + aaRole.setDesired(NODES + 1); + appState.reviewRequestAndReleaseNodes(); + assertTrue(aaRole.isAARequestOutstanding()); + assertEquals(NODES, aaRole.getAAPending()); + NodeUpdatedOutcome outcome = addNewNode(); + assertTrue(outcome.clusterChanged); + // one call to cancel + assertEquals(1, outcome.operations.size()); + // and on a review, one more to rebuild + assertEquals(1, appState.reviewRequestAndReleaseNodes().size()); + } + + @Test + public void testBindingInfoMustHaveNodeMap() throws Throwable { + AppStateBindingInfo bindingInfo = buildBindingInfo(); + bindingInfo.nodeReports = null; + try { + MockAppState state = new MockAppState(bindingInfo); + fail("Expected an exception, got " + state); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testAMRestart() throws Throwable { + int desiredAA = 3; + getAaRole().setDesired(desiredAA); + List instances = createAndStartNodes(); + List containers = new ArrayList<>(); + for (RoleInstance instance : instances) { + containers.add(instance.container); + } + + // now destroy the app state + AppStateBindingInfo bindingInfo = buildBindingInfo(); + bindingInfo.application = factory.newApplication(0, 0, desiredAA).name( + getTestName()); + bindingInfo.application.getComponent(ROLE2) + .getConfiguration().setProperty(COMPONENT_PLACEMENT_POLICY, + Integer.toString(PlacementPolicy.ANTI_AFFINITY_REQUIRED)); + bindingInfo.liveContainers = containers; + appState = new MockAppState(bindingInfo); + + RoleStatus aaRole = lookupRole(AAROLE_2.name); + RoleStatus gpuRole = lookupRole(MockFactory.AAROLE_1_GPU.name); + appState.reviewRequestAndReleaseNodes(); + assertTrue(aaRole.isAntiAffinePlacement()); + assertTrue(aaRole.isAARequestOutstanding()); + + } + +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateContainerFailure.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateContainerFailure.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateContainerFailure.java new file mode 100644 index 0000000..ea0dcf4 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateContainerFailure.java @@ -0,0 +1,387 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.slider.api.ResourceKeys; +import org.apache.slider.api.resource.Application; +import org.apache.slider.core.exceptions.SliderException; +import org.apache.slider.core.exceptions.TriggerClusterTeardownException; +import org.apache.slider.server.appmaster.actions.ResetFailureWindow; +import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest; +import org.apache.slider.server.appmaster.model.mock.MockAM; +import org.apache.slider.server.appmaster.model.mock.MockAppState; +import org.apache.slider.server.appmaster.model.mock.MockRMOperationHandler; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.model.mock.MockYarnEngine; +import org.apache.slider.server.appmaster.state.AppState; +import org.apache.slider.server.appmaster.state.AppStateBindingInfo; +import org.apache.slider.server.appmaster.state.ContainerOutcome; +import org.apache.slider.server.appmaster.state.NodeEntry; +import org.apache.slider.server.appmaster.state.NodeInstance; +import org.apache.slider.server.appmaster.state.RoleHistory; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.apache.slider.server.appmaster.state.RoleStatus; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Test that if you have >1 role, the right roles are chosen for release. + */ +public class TestMockAppStateContainerFailure extends BaseMockAppStateTest + implements MockRoles { + private static final Logger LOG = + LoggerFactory.getLogger(TestMockAppStateContainerFailure.class); + + private MockRMOperationHandler operationHandler = new + MockRMOperationHandler(); + private MockAM mockAM = new MockAM(); + + @Override + public String getTestName() { + return "TestMockAppStateContainerFailure"; + } + + /** + * Small cluster with multiple containers per node, + * to guarantee many container allocations on each node. + * @return + */ + @Override + public MockYarnEngine createYarnEngine() { + return new MockYarnEngine(4, 8000); + } + + @Override + public Application buildApplication() { + Application application = super.buildApplication(); + application.getConfiguration().setProperty( + ResourceKeys.CONTAINER_FAILURE_THRESHOLD, "10"); + return application; + } + + @Test + public void testShortLivedFail() throws Throwable { + + getRole0Status().setDesired(1); + List instances = createAndStartNodes(); + assertEquals(1, instances.size()); + + RoleInstance instance = instances.get(0); + long created = instance.createTime; + long started = instance.startTime; + assertTrue(created > 0); + assertTrue(started >= created); + List ids = extractContainerIds(instances, ROLE0); + + ContainerId cid = ids.get(0); + assertTrue(appState.isShortLived(instance)); + AppState.NodeCompletionResult result = appState.onCompletedContainer( + containerStatus(cid, 1)); + assertNotNull(result.roleInstance); + assertTrue(result.containerFailed); + RoleStatus status = getRole0Status(); + assertEquals(1, status.getFailed()); +// assertEquals(1, status.getStartFailed()); + + //view the world + appState.getRoleHistory().dump(); + List queue = appState.getRoleHistory().cloneRecentNodeList( + getRole0Status().getKey()); + assertEquals(0, queue.size()); + + } + + @Test + public void testLongLivedFail() throws Throwable { + + getRole0Status().setDesired(1); + List instances = createAndStartNodes(); + assertEquals(1, instances.size()); + + RoleInstance instance = instances.get(0); + instance.startTime = System.currentTimeMillis() - 60 * 60 * 1000; + assertFalse(appState.isShortLived(instance)); + List ids = extractContainerIds(instances, ROLE0); + + ContainerId cid = ids.get(0); + AppState.NodeCompletionResult result = appState.onCompletedContainer( + containerStatus(cid, 1)); + assertNotNull(result.roleInstance); + assertTrue(result.containerFailed); + RoleStatus status = getRole0Status(); + assertEquals(1, status.getFailed()); +// assertEquals(0, status.getStartFailed()); + + //view the world + appState.getRoleHistory().dump(); + List queue = appState.getRoleHistory().cloneRecentNodeList( + getRole0Status().getKey()); + assertEquals(1, queue.size()); + + } + + @Test + public void testNodeStartFailure() throws Throwable { + + getRole0Status().setDesired(1); + List instances = createAndSubmitNodes(); + assertEquals(1, instances.size()); + + RoleInstance instance = instances.get(0); + + List ids = extractContainerIds(instances, ROLE0); + + ContainerId cid = ids.get(0); + appState.onNodeManagerContainerStartFailed(cid, new SliderException( + "oops")); + RoleStatus status = getRole0Status(); + assertEquals(1, status.getFailed()); +// assertEquals(1, status.getStartFailed()); + + + RoleHistory history = appState.getRoleHistory(); + history.dump(); + List queue = history.cloneRecentNodeList(getRole0Status() + .getKey()); + assertEquals(0, queue.size()); + + NodeInstance ni = history.getOrCreateNodeInstance(instance.container); + NodeEntry re = ni.get(getRole0Status().getKey()); + assertEquals(1, re.getFailed()); + assertEquals(1, re.getStartFailed()); + } + + @Test + public void testRecurrentStartupFailure() throws Throwable { + + getRole0Status().setDesired(1); + try { + for (int i = 0; i< 100; i++) { + List instances = createAndSubmitNodes(); + assertEquals(1, instances.size()); + + List ids = extractContainerIds(instances, ROLE0); + + ContainerId cid = ids.get(0); + LOG.info("{} instance {} {}", i, instances.get(0), cid); + assertNotNull(cid); + appState.onNodeManagerContainerStartFailed(cid, + new SliderException("failure #" + i)); + AppState.NodeCompletionResult result = appState.onCompletedContainer( + containerStatus(cid)); + assertTrue(result.containerFailed); + } + fail("Cluster did not fail from too many startup failures"); + } catch (TriggerClusterTeardownException teardown) { + LOG.info("Exception {} : {}", teardown.getExitCode(), teardown); + } + } + + @Test + public void testRecurrentStartupFailureWithUnlimitedFailures() throws + Throwable { + // Update instance definition to allow containers to fail any number of + // times + AppStateBindingInfo bindingInfo = buildBindingInfo(); + bindingInfo.application.getConfiguration().setProperty( + ResourceKeys.CONTAINER_FAILURE_THRESHOLD, "0"); + appState = new MockAppState(bindingInfo); + + getRole0Status().setDesired(1); + try { + for (int i = 0; i < 100; i++) { + List instances = createAndSubmitNodes(); + assertEquals(1, instances.size()); + + List ids = extractContainerIds(instances, ROLE0); + + ContainerId cid = ids.get(0); + LOG.info("{} instance {} {}", i, instances.get(0), cid); + assertNotNull(cid); + appState.onNodeManagerContainerStartFailed(cid, + new SliderException("failure #" + i)); + AppState.NodeCompletionResult result = appState.onCompletedContainer( + containerStatus(cid)); + assertTrue(result.containerFailed); + } + } catch (TriggerClusterTeardownException teardown) { + LOG.info("Exception {} : {}", teardown.getExitCode(), teardown); + fail("Cluster failed despite " + ResourceKeys + .CONTAINER_FAILURE_THRESHOLD + " = 0"); + } + } + + @Test + public void testRoleStatusFailureWindow() throws Throwable { + + ResetFailureWindow resetter = new ResetFailureWindow(operationHandler); + + // initial reset + resetter.execute(mockAM, null, appState); + + getRole0Status().setDesired(1); + for (int i = 0; i < 100; i++) { + resetter.execute(mockAM, null, appState); + List instances = createAndSubmitNodes(); + assertEquals(1, instances.size()); + + List ids = extractContainerIds(instances, ROLE0); + + ContainerId cid = ids.get(0); + LOG.info("{} instance {} {}", i, instances.get(0), cid); + assertNotNull(cid); + appState.onNodeManagerContainerStartFailed( + cid, + new SliderException("failure #" + i)); + AppState.NodeCompletionResult result = appState.onCompletedContainer( + containerStatus(cid)); + assertTrue(result.containerFailed); + } + } + + @Test + public void testRoleStatusFailed() throws Throwable { + RoleStatus status = getRole0Status(); + // limits exceeded + appState.incFailedContainers(status, ContainerOutcome.Failed); + assertEquals(1, status.getFailed()); + assertEquals(1L, status.getFailedRecently()); + assertEquals(0L, status.getLimitsExceeded()); + assertEquals(0L, status.getPreempted()); + assertEquals(0L, status.getDiskFailed()); + + ResetFailureWindow resetter = new ResetFailureWindow(operationHandler); + resetter.execute(mockAM, null, appState); + assertEquals(1, status.getFailed()); + assertEquals(0L, status.getFailedRecently()); + } + + @Test + public void testRoleStatusFailedLimitsExceeded() throws Throwable { + RoleStatus status = getRole0Status(); + // limits exceeded + appState.incFailedContainers(status, ContainerOutcome + .Failed_limits_exceeded); + assertEquals(1, status.getFailed()); + assertEquals(1L, status.getFailedRecently()); + assertEquals(1L, status.getLimitsExceeded()); + assertEquals(0L, status.getPreempted()); + assertEquals(0L, status.getDiskFailed()); + + ResetFailureWindow resetter = new ResetFailureWindow(operationHandler); + resetter.execute(mockAM, null, appState); + assertEquals(1, status.getFailed()); + assertEquals(0L, status.getFailedRecently()); + assertEquals(1L, status.getLimitsExceeded()); + } + + + @Test + public void testRoleStatusFailedPrempted() throws Throwable { + RoleStatus status = getRole0Status(); + // limits exceeded + appState.incFailedContainers(status, ContainerOutcome.Preempted); + assertEquals(0, status.getFailed()); + assertEquals(1L, status.getPreempted()); + assertEquals(0L, status.getFailedRecently()); + assertEquals(0L, status.getDiskFailed()); + + ResetFailureWindow resetter = new ResetFailureWindow(operationHandler); + resetter.execute(mockAM, null, appState); + assertEquals(1L, status.getPreempted()); + } + + + @Test + public void testRoleStatusFailedNode() throws Throwable { + RoleStatus status = getRole0Status(); + // limits exceeded + appState.incFailedContainers(status, ContainerOutcome.Disk_failure); + assertEquals(1, status.getFailed()); + assertEquals(0L, status.getFailedRecently()); + assertEquals(0L, status.getLimitsExceeded()); + assertEquals(0L, status.getPreempted()); + assertEquals(1L, status.getDiskFailed()); + } + + @Test + public void testNodeEntryCompleted() throws Throwable { + NodeEntry nodeEntry = new NodeEntry(1); + nodeEntry.containerCompleted(true, ContainerOutcome.Completed); + assertEquals(0, nodeEntry.getFailed()); + assertEquals(0, nodeEntry.getFailedRecently()); + assertEquals(0, nodeEntry.getStartFailed()); + assertEquals(0, nodeEntry.getPreempted()); + assertEquals(0, nodeEntry.getActive()); + assertTrue(nodeEntry.isAvailable()); + } + + @Test + public void testNodeEntryFailed() throws Throwable { + NodeEntry nodeEntry = new NodeEntry(1); + nodeEntry.containerCompleted(false, ContainerOutcome.Failed); + assertEquals(1, nodeEntry.getFailed()); + assertEquals(1, nodeEntry.getFailedRecently()); + assertEquals(0, nodeEntry.getStartFailed()); + assertEquals(0, nodeEntry.getPreempted()); + assertEquals(0, nodeEntry.getActive()); + assertTrue(nodeEntry.isAvailable()); + nodeEntry.resetFailedRecently(); + assertEquals(1, nodeEntry.getFailed()); + assertEquals(0, nodeEntry.getFailedRecently()); + } + + @Test + public void testNodeEntryLimitsExceeded() throws Throwable { + NodeEntry nodeEntry = new NodeEntry(1); + nodeEntry.containerCompleted(false, ContainerOutcome + .Failed_limits_exceeded); + assertEquals(0, nodeEntry.getFailed()); + assertEquals(0, nodeEntry.getFailedRecently()); + assertEquals(0, nodeEntry.getStartFailed()); + assertEquals(0, nodeEntry.getPreempted()); + } + + @Test + public void testNodeEntryPreempted() throws Throwable { + NodeEntry nodeEntry = new NodeEntry(1); + nodeEntry.containerCompleted(false, ContainerOutcome.Preempted); + assertEquals(0, nodeEntry.getFailed()); + assertEquals(0, nodeEntry.getFailedRecently()); + assertEquals(0, nodeEntry.getStartFailed()); + assertEquals(1, nodeEntry.getPreempted()); + } + + @Test + public void testNodeEntryNodeFailure() throws Throwable { + NodeEntry nodeEntry = new NodeEntry(1); + nodeEntry.containerCompleted(false, ContainerOutcome.Disk_failure); + assertEquals(1, nodeEntry.getFailed()); + assertEquals(1, nodeEntry.getFailedRecently()); + assertEquals(0, nodeEntry.getStartFailed()); + assertEquals(0, nodeEntry.getPreempted()); + } + + + +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.java new file mode 100644 index 0000000..da2ed0d --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicHistory.java @@ -0,0 +1,212 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.hadoop.yarn.client.api.AMRMClient.ContainerRequest; +import org.apache.slider.api.ResourceKeys; +import org.apache.slider.api.resource.Application; +import org.apache.slider.api.resource.Component; +import org.apache.slider.common.tools.SliderUtils; +import org.apache.slider.core.exceptions.BadConfigException; +import org.apache.slider.providers.PlacementPolicy; +import org.apache.slider.providers.ProviderRole; +import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest; +import org.apache.slider.server.appmaster.model.mock.MockRoleHistory; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.model.mock.MockYarnEngine; +import org.apache.slider.server.appmaster.operations.AbstractRMOperation; +import org.apache.slider.server.appmaster.operations.ContainerRequestOperation; +import org.apache.slider.server.appmaster.state.AppState; +import org.apache.slider.server.appmaster.state.NodeEntry; +import org.apache.slider.server.appmaster.state.NodeInstance; +import org.apache.slider.server.appmaster.state.RoleHistory; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.apache.slider.server.appmaster.state.RoleStatus; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Test that if you have >1 role, the right roles are chosen for release. + */ +public class TestMockAppStateDynamicHistory extends BaseMockAppStateTest + implements MockRoles { + private static final Logger LOG = + LoggerFactory.getLogger(TestMockAppStateDynamicHistory.class); + + /** + * Small cluster with multiple containers per node, + * to guarantee many container allocations on each node. + * @return + */ + @Override + public MockYarnEngine createYarnEngine() { + return new MockYarnEngine(8, 1); + } + + @Test + public void testDynamicRoleHistory() throws Throwable { + + String dynamic = "dynamicRole"; + long desired = 1; + int placementPolicy = PlacementPolicy.DEFAULT; + // snapshot and patch existing spec + Application application = appState.getClusterStatus(); + Component component = new Component().name(dynamic).numberOfContainers( + desired); + component.getConfiguration().setProperty(ResourceKeys + .COMPONENT_PLACEMENT_POLICY, "" + placementPolicy); + application.getComponents().add(component); + + // write the definitions + List updates = appState.updateComponents( + Collections.singletonMap(dynamic, desired)); + assertEquals(1, updates.size()); + ProviderRole updatedRole = updates.get(0); + assertEquals(updatedRole.placementPolicy, placementPolicy); + + // now look at the role map + assertNotNull(appState.getRoleMap().get(dynamic)); + ProviderRole mappedRole = appState.getRoleMap().get(dynamic); + int rolePriority = mappedRole.id; + + Map priorityMap = appState.getRolePriorityMap(); + assertEquals(priorityMap.size(), 4); + ProviderRole dynamicProviderRole = priorityMap.get(rolePriority); + assertNotNull(dynamicProviderRole); + assertEquals(dynamicProviderRole.id, rolePriority); + + assertNotNull(appState.getRoleStatusMap().get(rolePriority)); + RoleStatus dynamicRoleStatus = + appState.getRoleStatusMap().get(rolePriority); + assertEquals(dynamicRoleStatus.getDesired(), desired); + + + // before allocating the nodes, fill up the capacity of some of the + // hosts + engine.getAllocator().nextIndex(); + + int targetNode = 2; + assertEquals(targetNode, engine.getAllocator().nextIndex()); + String targetHostname = engine.getCluster().nodeAt(targetNode) + .getHostname(); + + // clock is set to a small value + appState.setTime(100000); + + // allocate the nodes + List actions = appState.reviewRequestAndReleaseNodes(); + assertEquals(1, actions.size()); + ContainerRequestOperation action0 = (ContainerRequestOperation)actions + .get(0); + + ContainerRequest request = action0.getRequest(); + assertTrue(SliderUtils.isEmpty(request.getNodes())); + + List released = new ArrayList<>(); + List allocations = submitOperations(actions, released); + processSubmissionOperations(allocations, new ArrayList<>(), released); + assertEquals(1, allocations.size()); + RoleInstance ri = allocations.get(0); + + assertEquals(ri.role, dynamic); + assertEquals(ri.roleId, rolePriority); + assertEquals(ri.host, targetHostname); + + // now look at the role history + + RoleHistory roleHistory = appState.getRoleHistory(); + List activeNodes = roleHistory.listActiveNodes( + rolePriority); + assertEquals(activeNodes.size(), 1); + NodeInstance activeNode = activeNodes.get(0); + assertNotNull(activeNode.get(rolePriority)); + NodeEntry entry8 = activeNode.get(rolePriority); + assertEquals(entry8.getActive(), 1); + + assertEquals(activeNode.hostname, targetHostname); + + NodeInstance activeNodeInstance = + roleHistory.getOrCreateNodeInstance(ri.container); + + assertEquals(activeNode, activeNodeInstance); + NodeEntry entry = activeNodeInstance.get(rolePriority); + assertNotNull(entry); + assertTrue(entry.getActive() > 0); + assertTrue(entry.getLive() > 0); + + + // now trigger a termination event on that role + + // increment time for a long-lived failure event + appState.incTime(100000); + + LOG.debug("Triggering failure"); + ContainerId cid = ri.getContainerId(); + AppState.NodeCompletionResult result = appState.onCompletedContainer( + containerStatus(cid, 1)); + assertEquals(result.roleInstance, ri); + assertTrue(result.containerFailed); + + roleHistory.dump(); + // values should have changed + assertEquals(1, entry.getFailed()); + assertEquals(0, entry.getStartFailed()); + assertEquals(0, entry.getActive()); + assertEquals(0, entry.getLive()); + + + List nodesForRoleId = + roleHistory.getRecentNodesForRoleId(rolePriority); + assertNotNull(nodesForRoleId); + + // make sure new nodes will default to a different host in the engine + assertTrue(targetNode < engine.getAllocator().nextIndex()); + + actions = appState.reviewRequestAndReleaseNodes(); + assertEquals(1, actions.size()); + ContainerRequestOperation action1 = (ContainerRequestOperation) actions + .get(0); + ContainerRequest request1 = action1.getRequest(); + assertTrue(SliderUtils.isNotEmpty(request1.getNodes())); + } + + @Test(expected = BadConfigException.class) + public void testRoleHistoryRoleAdditions() throws Throwable { + MockRoleHistory roleHistory = new MockRoleHistory(new ArrayList<>()); + roleHistory.addNewRole(new RoleStatus(new ProviderRole("one", 1))); + roleHistory.addNewRole(new RoleStatus(new ProviderRole("two", 1))); + roleHistory.dump(); + } + + @Test(expected = BadConfigException.class) + public void testRoleHistoryRoleStartupConflict() throws Throwable { + MockRoleHistory roleHistory = new MockRoleHistory(Arrays.asList( + new ProviderRole("one", 1), new ProviderRole("two", 1) + )); + roleHistory.dump(); + } +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicRoles.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicRoles.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicRoles.java new file mode 100644 index 0000000..2c695fd --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateDynamicRoles.java @@ -0,0 +1,243 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.slider.api.ResourceKeys; +import org.apache.slider.api.resource.Application; +import org.apache.slider.api.resource.Component; +import org.apache.slider.providers.PlacementPolicy; +import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.model.mock.MockYarnEngine; +import org.apache.slider.server.appmaster.operations.AbstractRMOperation; +import org.apache.slider.server.appmaster.operations.ContainerRequestOperation; +import org.apache.slider.server.appmaster.state.AppState.NodeCompletionResult; +import org.apache.slider.server.appmaster.state.ContainerPriority; +import org.apache.slider.server.appmaster.state.RoleHistoryUtils; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.apache.slider.server.appmaster.state.RoleStatus; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.apache.slider.server.appmaster.model.mock.MockFactory.NODE_FAILURE_THRESHOLD; + +/** + * Test that if you have >1 role, the right roles are chosen for release. + */ +public class TestMockAppStateDynamicRoles extends BaseMockAppStateTest + implements MockRoles { + private static final Logger LOG = + LoggerFactory.getLogger(TestMockAppStateDynamicRoles.class); + private static final String ROLE4 = "4"; + private static final String ROLE5 = "5"; + + @Override + public String getTestName() { + return "TestMockAppStateDynamicRoles"; + } + + /** + * Small cluster with multiple containers per node, + * to guarantee many container allocations on each node. + * @return + */ + @Override + public MockYarnEngine createYarnEngine() { + return new MockYarnEngine(8, 2); + } + + @Override + public Application buildApplication() { + Application application = super.buildApplication(); + + Component component = new Component().name(ROLE4).numberOfContainers(1L); + component.getConfiguration().setProperty(ResourceKeys + .NODE_FAILURE_THRESHOLD, Integer.toString(3)); + application.getComponents().add(component); + + component = new Component().name(ROLE5).numberOfContainers(1L); + component.getConfiguration().setProperty(ResourceKeys + .COMPONENT_PLACEMENT_POLICY, Integer.toString(PlacementPolicy.STRICT)); + application.getComponents().add(component); + + return application; + } + + @Test + public void testAllocateReleaseRealloc() throws Throwable { + + createAndStartNodes(); + appState.reviewRequestAndReleaseNodes(); + appState.getRoleHistory().dump(); + } + + /** + * Find all allocations for a specific role. + * @param role role Id/priority + * @param actions source list + * @return found list + */ + List findAllocationsForRole(int role, + List actions) { + List ops = new ArrayList<>(); + for (AbstractRMOperation op : actions) { + if (op instanceof ContainerRequestOperation && role == + ContainerPriority.extractRole(((ContainerRequestOperation) op) + .getRequest().getPriority())) { + ops.add((ContainerRequestOperation) op); + } + } + return ops; + } + + @Test + public void testStrictPlacementInitialRequest() throws Throwable { + LOG.info("Initial engine state = {}", engine); + List actions = appState.reviewRequestAndReleaseNodes(); + assertEquals(2, actions.size()); + + // neither have locality at this point + assertRelaxLocalityFlag(appState.lookupRoleStatus(ROLE4).getKey(), null, + true, actions); + assertRelaxLocalityFlag(appState.lookupRoleStatus(ROLE5).getKey(), null, + true, actions); + } + + @Test + public void testPolicyPropagation() throws Throwable { + assertEquals(0, (appState.lookupRoleStatus(ROLE4).getPlacementPolicy() & + PlacementPolicy.STRICT)); + assertNotEquals(0, (appState.lookupRoleStatus(ROLE5).getPlacementPolicy() & + PlacementPolicy.STRICT)); + + } + + @Test + public void testNodeFailureThresholdPropagation() throws Throwable { + assertEquals(3, appState.lookupRoleStatus(ROLE4).getNodeFailureThreshold()); + assertEquals(NODE_FAILURE_THRESHOLD, appState.lookupRoleStatus(ROLE5) + .getNodeFailureThreshold()); + } + + @Test + public void testLaxPlacementSecondRequestRole4() throws Throwable { + LOG.info("Initial engine state = {}", engine); + RoleStatus role4 = appState.lookupRoleStatus(ROLE4); + RoleStatus role5 = appState.lookupRoleStatus(ROLE5); + role4.setDesired(1); + role5.setDesired(0); + + List instances = createStartAndStopNodes(new ArrayList<>()); + assertEquals(1, instances.size()); + + int id = appState.lookupRoleStatus(ROLE4).getKey(); + RoleInstance instanceA = null; + for (RoleInstance instance : instances) { + if (instance.roleId == id) { + instanceA = instance; + } + } + assertNotNull(instanceA); + String hostname = RoleHistoryUtils.hostnameOf(instanceA.container); + + LOG.info("Allocated engine state = {}", engine); + assertEquals(1, engine.containerCount()); + + assertEquals(1, role4.getRunning()); + // shrinking cluster + + role4.setDesired(0); + appState.lookupRoleStatus(ROLE4).setDesired(0); + List completionResults = new ArrayList<>(); + createStartAndStopNodes(completionResults); + assertEquals(0, engine.containerCount()); + assertEquals(1, completionResults.size()); + + // expanding: expect hostnames now + role4.setDesired(1); + List actions = appState.reviewRequestAndReleaseNodes(); + assertEquals(1, actions.size()); + + ContainerRequestOperation cro = (ContainerRequestOperation) actions.get(0); + List nodes = cro.getRequest().getNodes(); + assertEquals(1, nodes.size()); + assertEquals(hostname, nodes.get(0)); + } + + @Test + public void testStrictPlacementSecondRequestRole5() throws Throwable { + LOG.info("Initial engine state = {}", engine); + RoleStatus role4 = appState.lookupRoleStatus(ROLE4); + RoleStatus role5 = appState.lookupRoleStatus(ROLE5); + role4.setDesired(0); + role5.setDesired(1); + + List instances = createStartAndStopNodes(new ArrayList<>()); + assertEquals(1, instances.size()); + + int id = appState.lookupRoleStatus(ROLE5).getKey(); + RoleInstance instanceA = null; + for (RoleInstance instance : instances) { + if (instance.roleId == id) { + instanceA = instance; + } + } + assertNotNull(instanceA); + String hostname = RoleHistoryUtils.hostnameOf(instanceA.container); + + LOG.info("Allocated engine state = {}", engine); + assertEquals(1, engine.containerCount()); + + assertEquals(1, role5.getRunning()); + + // shrinking cluster + role5.setDesired(0); + List completionResults = new ArrayList<>(); + createStartAndStopNodes(completionResults); + assertEquals(0, engine.containerCount()); + assertEquals(1, completionResults.size()); + assertEquals(0, role5.getRunning()); + + role5.setDesired(1); + List actions = appState.reviewRequestAndReleaseNodes(); + assertEquals(1, actions.size()); + assertRelaxLocalityFlag(id, "", false, actions); + ContainerRequestOperation cro = (ContainerRequestOperation) actions.get(0); + List nodes = cro.getRequest().getNodes(); + assertEquals(1, nodes.size()); + assertEquals(hostname, nodes.get(0)); + } + + public void assertRelaxLocalityFlag( + int role, + String expectedHost, + boolean expectedRelaxFlag, + List actions) { + List requests = findAllocationsForRole( + role, actions); + assertEquals(1, requests.size()); + ContainerRequestOperation req = requests.get(0); + assertEquals(expectedRelaxFlag, req.getRequest().getRelaxLocality()); + } + +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexDynamicRoles.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexDynamicRoles.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexDynamicRoles.java new file mode 100644 index 0000000..01bf9bd --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexDynamicRoles.java @@ -0,0 +1,160 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.hadoop.fs.Path; +import org.apache.slider.api.resource.Application; +import org.apache.slider.api.resource.Component; +import org.apache.slider.core.exceptions.SliderInternalStateException; +import org.apache.slider.core.exceptions.TriggerClusterTeardownException; +import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest; +import org.apache.slider.server.appmaster.model.mock.MockAppState; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.model.mock.MockYarnEngine; +import org.apache.slider.server.appmaster.state.AppStateBindingInfo; +import org.apache.slider.server.appmaster.state.MostRecentContainerReleaseSelector; +import org.apache.slider.server.appmaster.state.RoleHistory; +import org.apache.slider.server.avro.LoadedRoleHistory; +import org.apache.slider.server.avro.RoleHistoryWriter; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.Collections; + +/** + * Test that if you have more than one role, the right roles are chosen for + * release. + */ +public class TestMockAppStateFlexDynamicRoles extends BaseMockAppStateTest + implements MockRoles { + private static final Logger LOG = + LoggerFactory.getLogger(TestMockAppStateFlexDynamicRoles.class); + + @Override + public String getTestName() { + return "TestMockAppStateFlexDynamicRoles"; + } + + /** + * Small cluster with multiple containers per node, + * to guarantee many container allocations on each node. + * @return + */ + @Override + public MockYarnEngine createYarnEngine() { + return new MockYarnEngine(4, 4); + } + + @Override + public AppStateBindingInfo buildBindingInfo() { + AppStateBindingInfo bindingInfo = super.buildBindingInfo(); + bindingInfo.releaseSelector = new MostRecentContainerReleaseSelector(); + return bindingInfo; + } + + @Override + public Application buildApplication() { + Application application = super.buildApplication(); + Component component = new Component().name("dynamic-6") + .numberOfContainers(1L); + application.getComponents().add(component); + + return application; + } + + @Before + public void init() + throws TriggerClusterTeardownException, SliderInternalStateException { + createAndStartNodes(); + } + + @Test + public void testDynamicFlexAddRole() throws Throwable { + Application application = appState.getClusterStatus(); + Component component = new Component().name("dynamicAdd7") + .numberOfContainers(1L); + application.getComponents().add(component); + appState.updateComponents(Collections.singletonMap(component.getName(), + component.getNumberOfContainers())); + createAndStartNodes(); + dumpClusterDescription("updated CD", appState.getClusterStatus()); + appState.lookupRoleStatus("dynamicAdd7"); + } + + @Test + public void testDynamicFlexDropRole() throws Throwable { + appState.updateComponents(Collections.singletonMap("dynamic-6", 0L)); + + Application getCD = appState.getClusterStatus(); + dumpClusterDescription("updated CD", getCD); + //status is retained for future + appState.lookupRoleStatus("dynamic-6"); + } + + + @Test + public void testHistorySaveFlexLoad() throws Throwable { + Application application = appState.getClusterStatus(); + RoleHistory roleHistory = appState.getRoleHistory(); + Path history = roleHistory.saveHistory(0x0001); + RoleHistoryWriter historyWriter = new RoleHistoryWriter(); + Component component = new Component().name("HistorySaveFlexLoad") + .numberOfContainers(1L); + application.getComponents().add(component); + + appState.updateComponents(Collections.singletonMap(component.getName(), + component.getNumberOfContainers())); + createAndStartNodes(); + LoadedRoleHistory loadedRoleHistory = + historyWriter.read(fs, history); + assertEquals(0, appState.getRoleHistory().rebuild(loadedRoleHistory)); + } + + @Test + public void testHistoryFlexSaveResetLoad() throws Throwable { + Application application = appState.getClusterStatus(); + Component component = new Component().name("HistoryFlexSaveLoad") + .numberOfContainers(1L); + application.getComponents().add(component); + + appState.updateComponents(Collections.singletonMap(component.getName(), + component.getNumberOfContainers())); + createAndStartNodes(); + RoleHistoryWriter historyWriter = new RoleHistoryWriter(); + RoleHistory roleHistory = appState.getRoleHistory(); + Path history = roleHistory.saveHistory(0x0002); + //now reset the app state + File historyWorkDir2 = new File("target/history" + getTestName() + + "-0002"); + Path historyPath2 = new Path(historyWorkDir2.toURI()); + appState = new MockAppState(); + AppStateBindingInfo binding2 = buildBindingInfo(); + binding2.application = factory.newApplication(0, 0, 0) + .name(getTestName()); + binding2.historyPath = historyPath2; + appState.buildInstance(binding2); + // on this read there won't be the right number of roles + LoadedRoleHistory loadedRoleHistory = historyWriter.read(fs, history); + assertEquals(0, appState.getRoleHistory().rebuild(loadedRoleHistory)); + } + +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexing.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexing.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexing.java new file mode 100644 index 0000000..9b5e532 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateFlexing.java @@ -0,0 +1,201 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.hadoop.yarn.api.records.Container; +import org.apache.slider.api.resource.Application; +import org.apache.slider.api.types.ApplicationLivenessInformation; +import org.apache.slider.core.exceptions.TriggerClusterTeardownException; +import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.operations.AbstractRMOperation; +import org.apache.slider.server.appmaster.operations.CancelSingleRequest; +import org.apache.slider.server.appmaster.state.AppState; +import org.apache.slider.server.appmaster.state.ContainerAssignment; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test app state flexing. + */ +public class TestMockAppStateFlexing extends BaseMockAppStateTest implements + MockRoles { + private static final Logger LOG = + LoggerFactory.getLogger(BaseMockAppStateTest.class); + + @Override + public String getTestName() { + return "TestMockAppStateFlexing"; + } + + @Test + public void testFlexDuringLaunchPhase() throws Throwable { + + // ask for one instance of role0 + getRole0Status().setDesired(1); + + List ops = appState.reviewRequestAndReleaseNodes(); + + // at this point there's now one request in the list + assertEquals(1, ops.size()); + // and in a liveness check, one outstanding + ApplicationLivenessInformation liveness = + appState.getApplicationLivenessInformation(); + assertEquals(1, liveness.requestsOutstanding); + assertFalse(liveness.allRequestsSatisfied); + + List allocations = engine.execute(ops); + List assignments = new ArrayList<>(); + List releases = new ArrayList<>(); + appState.onContainersAllocated(allocations, assignments, releases); + assertEquals(1, assignments.size()); + ContainerAssignment assigned = assignments.get(0); + Container target = assigned.container; + RoleInstance ri = roleInstance(assigned); + + ops = appState.reviewRequestAndReleaseNodes(); + assertTrue(ops.isEmpty()); + + liveness = appState.getApplicationLivenessInformation(); + assertEquals(0, liveness.requestsOutstanding); + assertTrue(liveness.allRequestsSatisfied); + + //now this is the start point. + appState.containerStartSubmitted(target, ri); + + ops = appState.reviewRequestAndReleaseNodes(); + assertTrue(ops.isEmpty()); + + appState.innerOnNodeManagerContainerStarted(target.getId()); + } + + @Test + public void testFlexBeforeAllocationPhase() throws Throwable { + getRole0Status().setDesired(1); + + List ops = appState.reviewRequestAndReleaseNodes(); + assertFalse(ops.isEmpty()); + + // second scan will find the first run outstanding, so not re-issue + // any more container requests + List ops2 = appState.reviewRequestAndReleaseNodes(); + assertTrue(ops2.isEmpty()); + + // and in a liveness check, one outstanding + ApplicationLivenessInformation liveness = appState + .getApplicationLivenessInformation(); + assertEquals(1, liveness.requestsOutstanding); + assertFalse(liveness.allRequestsSatisfied); + + appState.refreshClusterStatus(); + Application application = appState.getClusterStatus(); + // TODO cluster status returns liveness info +// assertEquals(1, cd.liveness.requestsOutstanding); + + } + + + @Test + public void testFlexDownTwice() throws Throwable { + int r0 = 6; + int r1 = 0; + int r2 = 0; + getRole0Status().setDesired(r0); + getRole1Status().setDesired(r1); + getRole2Status().setDesired(r2); + List instances = createAndStartNodes(); + + int clusterSize = r0 + r1 + r2; + assertEquals(instances.size(), clusterSize); + LOG.info("shrinking cluster"); + r0 = 4; + getRole0Status().setDesired(r0); + List completionResults = new ArrayList<>(); + instances = createStartAndStopNodes(completionResults); + assertEquals(0, instances.size()); + // assert two nodes were released + assertEquals(2, completionResults.size()); + + // no-op review + completionResults = new ArrayList<>(); + instances = createStartAndStopNodes(completionResults); + assertEquals(0, instances.size()); + // assert two nodes were released + assertEquals(0, completionResults.size()); + + + // now shrink again + getRole0Status().setDesired(1); + completionResults = new ArrayList<>(); + instances = createStartAndStopNodes(completionResults); + assertEquals(0, instances.size()); + // assert two nodes were released + assertEquals(3, completionResults.size()); + + } + + @Test + public void testFlexNegative() throws Throwable { + int r0 = 6; + int r1 = 0; + int r2 = 0; + getRole0Status().setDesired(r0); + getRole1Status().setDesired(r1); + getRole2Status().setDesired(r2); + List instances = createAndStartNodes(); + + int clusterSize = r0 + r1 + r2; + assertEquals(instances.size(), clusterSize); + LOG.info("shrinking cluster"); + getRole0Status().setDesired(-2); + List completionResults = new ArrayList<>(); + try { + createStartAndStopNodes(completionResults); + fail("expected an exception"); + } catch (TriggerClusterTeardownException e) { + } + + } + + @Test + public void testCancelWithRequestsOutstanding() throws Throwable { + // flex cluster size before the original set were allocated + + + getRole0Status().setDesired(6); + // build the ops + List ops = appState.reviewRequestAndReleaseNodes(); + // here the data structures exist + + // go down + getRole0Status().setDesired(3); + List ops2 = appState.reviewRequestAndReleaseNodes(); + assertEquals(3, ops2.size()); + for (AbstractRMOperation op : ops2) { + assertTrue(op instanceof CancelSingleRequest); + } + + } + +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/b1fe5461/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateRMOperations.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateRMOperations.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateRMOperations.java new file mode 100644 index 0000000..2d87be6 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/appstate/TestMockAppStateRMOperations.java @@ -0,0 +1,382 @@ +/* + * 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.slider.server.appmaster.model.appstate; + +import org.apache.hadoop.yarn.api.records.Container; +import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.hadoop.yarn.client.api.AMRMClient; +import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest; +import org.apache.slider.server.appmaster.model.mock.MockRMOperationHandler; +import org.apache.slider.server.appmaster.model.mock.MockRoles; +import org.apache.slider.server.appmaster.model.mock.MockYarnEngine; +import org.apache.slider.server.appmaster.operations.AbstractRMOperation; +import org.apache.slider.server.appmaster.operations.CancelSingleRequest; +import org.apache.slider.server.appmaster.operations.ContainerReleaseOperation; +import org.apache.slider.server.appmaster.operations.ContainerRequestOperation; +import org.apache.slider.server.appmaster.state.AppState; +import org.apache.slider.server.appmaster.state.ContainerAssignment; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.apache.slider.server.appmaster.state.RoleStatus; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.apache.slider.server.appmaster.state.ContainerPriority.buildPriority; +import static org.apache.slider.server.appmaster.state.ContainerPriority.extractRole; + +/** + * Test app state RM operations. + */ +public class TestMockAppStateRMOperations extends BaseMockAppStateTest + implements MockRoles { + private static final Logger LOG = + LoggerFactory.getLogger(BaseMockAppStateTest.class); + + @Override + public String getTestName() { + return "TestMockAppStateRMOperations"; + } + + @Test + public void testPriorityOnly() throws Throwable { + assertEquals(5, extractRole(buildPriority(5, false))); + } + + @Test + public void testPriorityRoundTrip() throws Throwable { + assertEquals(5, extractRole(buildPriority(5, false))); + } + + @Test + public void testPriorityRoundTripWithRequest() throws Throwable { + int priority = buildPriority(5, false); + assertEquals(5, extractRole(priority)); + } + + @Test + public void testMockAddOp() throws Throwable { + getRole0Status().setDesired(1); + List ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 1); + ContainerRequestOperation operation = (ContainerRequestOperation)ops.get(0); + int priority = operation.getRequest().getPriority().getPriority(); + assertEquals(extractRole(priority), getRole0Status().getKey()); + MockRMOperationHandler handler = new MockRMOperationHandler(); + handler.execute(ops); + + AbstractRMOperation op = handler.getFirstOp(); + assertTrue(op instanceof ContainerRequestOperation); + } + + /** + * Test of a flex up and down op which verifies that outstanding + * requests are cancelled first. + *
    + *
  1. request 5 nodes, assert 5 request made
  2. + *
  3. allocate 1 of them
  4. + *
  5. flex cluster size to 3
  6. + *
  7. assert this generates 2 cancel requests
  8. + *
+ */ + @Test + public void testRequestThenCancelOps() throws Throwable { + RoleStatus role0 = getRole0Status(); + role0.setDesired(5); + List ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 5); + // now 5 outstanding requests. + assertEquals(5, role0.getRequested()); + + // allocate one + List processed = new ArrayList<>(); + processed.add(ops.get(0)); + List released = new ArrayList<>(); + List completionResults = new ArrayList<>(); + submitOperations(processed, released); + List instances = createAndSubmitNodes(released); + processSubmissionOperations(instances, completionResults, released); + + + // four outstanding + assertEquals(4, role0.getRequested()); + + // flex cluster to 3 + role0.setDesired(3); + ops = appState.reviewRequestAndReleaseNodes(); + + // expect two cancel operation from review + assertListLength(ops, 2); + for (AbstractRMOperation op : ops) { + assertTrue(op instanceof CancelSingleRequest); + } + + MockRMOperationHandler handler = new MockRMOperationHandler(); + handler.setAvailableToCancel(4); + handler.execute(ops); + assertEquals(2, handler.getAvailableToCancel()); + assertEquals(2, role0.getRequested()); + + // flex down one more + role0.setDesired(2); + ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 1); + for (AbstractRMOperation op : ops) { + assertTrue(op instanceof CancelSingleRequest); + } + handler.execute(ops); + assertEquals(1, handler.getAvailableToCancel()); + assertEquals(1, role0.getRequested()); + } + + @Test + public void testCancelNoActualContainers() throws Throwable { + RoleStatus role0 = getRole0Status(); + role0.setDesired(5); + List ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 5); + // now 5 outstanding requests. + assertEquals(5, role0.getRequested()); + role0.setDesired(0); + ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 5); + + } + + + @Test + public void testFlexDownOutstandingRequests() throws Throwable { + // engine only has two nodes, so > 2 will be outstanding + engine = new MockYarnEngine(1, 2); + List ops; + // role: desired = 2, requested = 1, actual=1 + RoleStatus role0 = getRole0Status(); + role0.setDesired(4); + createAndSubmitNodes(); + + assertEquals(2, role0.getRequested()); + assertEquals(2, role0.getRunning()); + // there are now two outstanding, two actual + // Release 3 and verify that the two + // cancellations were combined with a release + role0.setDesired(1); + assertEquals(-3, role0.getDelta()); + ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 3); + int numCancel = 0; + int numRelease = 0; + for (AbstractRMOperation op : ops) { + if (op instanceof CancelSingleRequest) { + numCancel++; + } + if (op instanceof ContainerReleaseOperation) { + numRelease++; + } + } + assertEquals(2, numCancel); + assertEquals(1, numRelease); + assertEquals(0, role0.getRequested()); + // TODO releasing? +// assertEquals(1, role0.getReleasing()); + } + + @Test + public void testCancelAllOutstandingRequests() throws Throwable { + + // role: desired = 2, requested = 1, actual=1 + RoleStatus role0 = getRole0Status(); + role0.setDesired(2); + List ops; + ops = appState.reviewRequestAndReleaseNodes(); + int count = 0; + for (AbstractRMOperation op : ops) { + if (op instanceof ContainerRequestOperation) { + count++; + } + } + assertEquals(2, count); + + // there are now two outstanding, two actual + // Release 3 and verify that the two + // cancellations were combined with a release + role0.setDesired(0); + ops = appState.reviewRequestAndReleaseNodes(); + assertEquals(2, ops.size()); + + for (AbstractRMOperation op : ops) { + assertTrue(op instanceof CancelSingleRequest); + } + } + + + @Test + public void testFlexUpOutstandingRequests() throws Throwable { + + List ops; + // role: desired = 2, requested = 1, actual=1 + RoleStatus role0 = getRole0Status(); + role0.setDesired(2); + appState.incRunningContainers(role0); + appState.incRequestedContainers(role0); + + // flex up 2 nodes, yet expect only one node to be requested, + // as the outstanding request is taken into account + role0.setDesired(4); + appState.incRequestedContainers(role0); + + assertEquals(1, role0.getRunning()); + assertEquals(2, role0.getRequested()); + assertEquals(3, role0.getActualAndRequested()); + assertEquals(1, role0.getDelta()); + ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 1); + assertTrue(ops.get(0) instanceof ContainerRequestOperation); + assertEquals(3, role0.getRequested()); + } + + @Test + public void testFlexUpNoSpace() throws Throwable { + // engine only has two nodes, so > 2 will be outstanding + engine = new MockYarnEngine(1, 2); + // role: desired = 2, requested = 1, actual=1 + RoleStatus role0 = getRole0Status(); + role0.setDesired(4); + createAndSubmitNodes(); + + assertEquals(2, role0.getRequested()); + assertEquals(2, role0.getRunning()); + role0.setDesired(8); + assertEquals(4, role0.getDelta()); + createAndSubmitNodes(); + assertEquals(6, role0.getRequested()); + } + + + @Test + public void testAllocateReleaseOp() throws Throwable { + getRole0Status().setDesired(1); + + List ops = appState.reviewRequestAndReleaseNodes(); + ContainerRequestOperation operation = (ContainerRequestOperation)ops.get(0); + AMRMClient.ContainerRequest request = operation.getRequest(); + Container cont = engine.allocateContainer(request); + List allocated = new ArrayList<>(); + allocated.add(cont); + List assignments = new ArrayList<>(); + List operations = new ArrayList<>(); + appState.onContainersAllocated(allocated, assignments, operations); + + assertListLength(ops, 1); + assertListLength(assignments, 1); + ContainerAssignment assigned = assignments.get(0); + Container target = assigned.container; + assertEquals(target.getId(), cont.getId()); + int roleId = assigned.role.getPriority(); + assertEquals(roleId, extractRole(request.getPriority())); + assertEquals(assigned.role.getName(), ROLE0); + RoleInstance ri = roleInstance(assigned); + //tell the app it arrived + appState.containerStartSubmitted(target, ri); + appState.innerOnNodeManagerContainerStarted(target.getId()); + assertEquals(1, getRole0Status().getRunning()); + + //now release it by changing the role status + getRole0Status().setDesired(0); + ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 1); + + assertTrue(ops.get(0) instanceof ContainerReleaseOperation); + ContainerReleaseOperation release = (ContainerReleaseOperation) ops.get(0); + assertEquals(release.getContainerId(), cont.getId()); + } + + @Test + public void testComplexAllocation() throws Throwable { + getRole0Status().setDesired(1); + getRole1Status().setDesired(3); + + List ops = appState.reviewRequestAndReleaseNodes(); + List allocations = engine.execute(ops); + List assignments = new ArrayList<>(); + List releases = new ArrayList<>(); + appState.onContainersAllocated(allocations, assignments, releases); + // we expect four release requests here for all the allocated containers + assertListLength(releases, 4); + for (AbstractRMOperation op : releases) { + assertTrue(op instanceof CancelSingleRequest); + } + assertListLength(assignments, 4); + for (ContainerAssignment assigned : assignments) { + Container target = assigned.container; + RoleInstance ri = roleInstance(assigned); + appState.containerStartSubmitted(target, ri); + } + //insert some async operation here + for (ContainerAssignment assigned : assignments) { + Container target = assigned.container; + appState.innerOnNodeManagerContainerStarted(target.getId()); + } + assertEquals(4, engine.containerCount()); + getRole1Status().setDesired(0); + ops = appState.reviewRequestAndReleaseNodes(); + assertListLength(ops, 3); + allocations = engine.execute(ops); + assertEquals(1, engine.containerCount()); + + appState.onContainersAllocated(allocations, assignments, releases); + assertTrue(assignments.isEmpty()); + assertTrue(releases.isEmpty()); + } + + @Test + public void testDoubleNodeManagerStartEvent() throws Throwable { + getRole0Status().setDesired(1); + + List ops = appState.reviewRequestAndReleaseNodes(); + List allocations = engine.execute(ops); + List assignments = new ArrayList<>(); + List releases = new ArrayList<>(); + appState.onContainersAllocated(allocations, assignments, releases); + assertListLength(assignments, 1); + ContainerAssignment assigned = assignments.get(0); + Container target = assigned.container; + RoleInstance ri = roleInstance(assigned); + appState.containerStartSubmitted(target, ri); + RoleInstance ri2 = appState.innerOnNodeManagerContainerStarted(target + .getId()); + assertEquals(ri2, ri); + //try a second time, expect an error + try { + appState.innerOnNodeManagerContainerStarted(target.getId()); + fail("Expected an exception"); + } catch (RuntimeException expected) { + // expected + } + //and non-faulter should not downgrade to a null + LOG.warn("Ignore any exception/stack trace that appears below"); + LOG.warn("==============================================================="); + RoleInstance ri3 = appState.onNodeManagerContainerStarted(target.getId()); + LOG.warn("==============================================================="); + LOG.warn("Ignore any exception/stack trace that appeared above"); + assertNull(ri3); + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org For additional commands, e-mail: common-commits-help@hadoop.apache.org