| /* |
| * 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.monkey |
| |
| import groovy.transform.CompileStatic |
| import groovy.util.logging.Slf4j |
| import org.apache.hadoop.yarn.conf.YarnConfiguration |
| import org.apache.slider.api.InternalKeys |
| import org.apache.slider.server.appmaster.actions.ActionHalt |
| import org.apache.slider.server.appmaster.actions.ActionKillContainer |
| import org.apache.slider.server.appmaster.actions.QueueService |
| import org.apache.slider.server.appmaster.model.mock.BaseMockAppStateTest |
| import org.apache.slider.server.appmaster.model.mock.MockRMOperationHandler |
| import org.apache.slider.server.appmaster.monkey.ChaosKillAM |
| import org.apache.slider.server.appmaster.monkey.ChaosKillContainer |
| import org.apache.slider.server.appmaster.monkey.ChaosMonkeyService |
| import org.apache.slider.server.appmaster.monkey.ChaosTarget |
| import org.apache.slider.server.appmaster.operations.ContainerReleaseOperation |
| import org.apache.slider.server.appmaster.state.RoleInstance |
| import org.junit.Before |
| import org.junit.Test |
| |
| import java.util.concurrent.TimeUnit |
| |
| @CompileStatic |
| @Slf4j |
| class TestMockMonkey extends BaseMockAppStateTest { |
| |
| /** |
| * This queue service is NOT started; tests need to poll the queue |
| * rather than expect them to execute |
| */ |
| QueueService queues |
| ChaosMonkeyService monkey |
| |
| @Before |
| public void init() { |
| def configuration = new YarnConfiguration() |
| queues = new QueueService(); |
| queues.init(configuration) |
| monkey = new ChaosMonkeyService(metricRegistry, queues) |
| monkey.init(configuration) |
| } |
| |
| @Test |
| public void testMonkeyStart() throws Throwable { |
| monkey.start() |
| monkey.stop() |
| } |
| |
| @Test |
| public void testMonkeyPlay() throws Throwable { |
| ChaosCounter counter = new ChaosCounter() |
| monkey.addTarget("target", counter, InternalKeys.PROBABILITY_PERCENT_100) |
| assert 1 == monkey.targetCount; |
| monkey.play() |
| assert counter.count == 1 |
| } |
| |
| @Test |
| public void testMonkeySchedule() throws Throwable { |
| ChaosCounter counter = new ChaosCounter() |
| assert 0 == monkey.targetCount; |
| monkey.addTarget("target", counter, InternalKeys.PROBABILITY_PERCENT_100) |
| assert 1 == monkey.targetCount; |
| assert monkey.schedule(0, 1, TimeUnit.SECONDS) |
| assert 1 == queues.scheduledActions.size() |
| } |
| |
| @Test |
| public void testMonkeyDoesntAddProb0Actions() throws Throwable { |
| ChaosCounter counter = new ChaosCounter() |
| monkey.addTarget("target", counter, 0) |
| assert 0 == monkey.targetCount; |
| monkey.play() |
| assert counter.count == 0 |
| } |
| |
| |
| @Test |
| public void testMonkeyScheduleProb0Actions() throws Throwable { |
| ChaosCounter counter = new ChaosCounter() |
| monkey.addTarget("target", counter, 0) |
| assert !monkey.schedule(0, 1, TimeUnit.SECONDS) |
| assert 0 == queues.scheduledActions.size() |
| } |
| |
| |
| @Test |
| public void testMonkeyPlaySometimes() throws Throwable { |
| ChaosCounter counter = new ChaosCounter() |
| ChaosCounter counter2 = new ChaosCounter() |
| monkey.addTarget("target1", counter, InternalKeys.PROBABILITY_PERCENT_1 * 50) |
| monkey.addTarget("target2", counter2, InternalKeys.PROBABILITY_PERCENT_1 * 25) |
| |
| for (int i = 0; i < 100; i++) { |
| monkey.play() |
| } |
| log.info("Counter1 = ${counter.count} counter2 = ${counter2.count}") |
| /* |
| * Relying on probability here to give approximate answers |
| */ |
| assert counter.count > 25 |
| assert counter.count < 75 |
| assert counter2.count < counter.count |
| } |
| |
| @Test |
| public void testAMKiller() throws Throwable { |
| |
| def chaos = new ChaosKillAM(queues, -1) |
| chaos.chaosAction(); |
| assert queues.scheduledActions.size() == 1 |
| def action = queues.scheduledActions.take() |
| assert action instanceof ActionHalt |
| } |
| |
| |
| @Test |
| public void testContainerKillerEmptyApp() throws Throwable { |
| |
| |
| def chaos = new ChaosKillContainer(appState, |
| queues, |
| new MockRMOperationHandler()) |
| chaos.chaosAction(); |
| assert queues.scheduledActions.size() == 0 |
| } |
| |
| |
| @Test |
| public void testContainerKillerIgnoresAM() throws Throwable { |
| |
| addAppMastertoAppState() |
| assert 1 == appState.liveNodes.size() |
| |
| def chaos = new ChaosKillContainer(appState, |
| queues, |
| new MockRMOperationHandler()) |
| chaos.chaosAction(); |
| assert queues.scheduledActions.size() == 0 |
| } |
| |
| |
| |
| @Test |
| public void testContainerKiller() throws Throwable { |
| MockRMOperationHandler ops = new MockRMOperationHandler(); |
| role0Status.desired = 1 |
| List<RoleInstance> instances = createAndStartNodes() |
| assert instances.size() == 1 |
| def instance = instances[0] |
| |
| def chaos = new ChaosKillContainer(appState, queues, ops) |
| chaos.chaosAction(); |
| assert queues.scheduledActions.size() == 1 |
| def action = queues.scheduledActions.take() |
| ActionKillContainer killer = (ActionKillContainer) action |
| assert killer.containerId == instance.containerId; |
| killer.execute(null, queues, appState) |
| assert ops.releases == 1; |
| |
| ContainerReleaseOperation operation = (ContainerReleaseOperation) ops.operations[0] |
| assert operation.containerId == instance.containerId |
| } |
| |
| |
| |
| /** |
| * Chaos target that just implement a counter |
| */ |
| private static class ChaosCounter implements ChaosTarget { |
| int count; |
| |
| @Override |
| void chaosAction() { |
| count++; |
| } |
| |
| |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder( |
| "ChaosCounter{"); |
| sb.append("count=").append(count); |
| sb.append('}'); |
| return sb.toString(); |
| } |
| } |
| } |