blob: 14aa1f522c3eeede8a213270d45d36c920bc2134 [file] [log] [blame]
/**
* 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.hadoop.yarn.service;
import org.apache.hadoop.conf.Configuration;
import org.junit.Test;
/**
* These tests verify that the {@link ServiceOperations} methods
* do a best-effort attempt to make the service state change operations
* idempotent. That is still best effort -there is no thread safety, and
* a failure during a state change does not prevent the operation
* being called again.
*/
public class TestServiceOperations extends ServiceAssert {
@Test
public void testWalkthrough() throws Throwable {
BreakableService svc = new BreakableService();
assertServiceStateCreated(svc);
Configuration conf = new Configuration();
conf.set("test.walkthrough","t");
ServiceOperations.init(svc, conf);
assertServiceStateInited(svc);
assertStateCount(svc, Service.STATE.INITED, 1);
//check the configuration made it all the way through.
assertServiceConfigurationContains(svc, "test.walkthrough");
ServiceOperations.start(svc);
assertServiceStateStarted(svc);
assertStateCount(svc, Service.STATE.STARTED, 1);
ServiceOperations.stop(svc);
assertServiceStateStopped(svc);
assertStateCount(svc, Service.STATE.STOPPED, 1);
}
/**
* Call init twice -expect a failure, and expect the count
* of initialization attempts to still be 1: the state
* check was made before the subclass method was called.
* @throws Throwable if need be
*/
@Test
public void testInitTwice() throws Throwable {
BreakableService svc = new BreakableService();
Configuration conf = new Configuration();
conf.set("test.init", "t");
ServiceOperations.init(svc, conf);
try {
ServiceOperations.init(svc, new Configuration());
fail("Expected a failure, got " + svc);
} catch (IllegalStateException e) {
//expected
}
assertStateCount(svc, Service.STATE.INITED, 1);
assertServiceConfigurationContains(svc, "test.init");
}
/**
* call start twice; expect failures and the start invoke count to
* be exactly 1.
* @throws Throwable if necessary
*/
@Test
public void testStartTwice() throws Throwable {
BreakableService svc = new BreakableService();
ServiceOperations.init(svc, new Configuration());
ServiceOperations.start(svc);
try {
ServiceOperations.start(svc);
fail("Expected a failure, got " + svc);
} catch (IllegalStateException e) {
//expected
}
assertStateCount(svc, Service.STATE.STARTED, 1);
}
/**
* Test that the deploy operation pushes a service into its started state
* @throws Throwable on any failure.
*/
@Test
public void testDeploy() throws Throwable {
BreakableService svc = new BreakableService();
assertServiceStateCreated(svc);
ServiceOperations.deploy(svc, new Configuration());
assertServiceStateStarted(svc);
assertStateCount(svc, Service.STATE.INITED, 1);
assertStateCount(svc, Service.STATE.STARTED, 1);
ServiceOperations.stop(svc);
assertServiceStateStopped(svc);
assertStateCount(svc, Service.STATE.STOPPED, 1);
}
/**
* Demonstrate that the deploy operation fails when invoked twice,
* but the service method call counts are unchanged after the second call.
* @throws Throwable on any failure.
*/
@Test
public void testDeployNotIdempotent() throws Throwable {
BreakableService svc = new BreakableService();
assertServiceStateCreated(svc);
ServiceOperations.deploy(svc, new Configuration());
try {
ServiceOperations.deploy(svc, new Configuration());
fail("Expected a failure, got " + svc);
} catch (IllegalStateException e) {
//expected
}
//verify state and values are unchanged
assertServiceStateStarted(svc);
assertStateCount(svc, Service.STATE.INITED, 1);
assertStateCount(svc, Service.STATE.STARTED, 1);
ServiceOperations.stop(svc);
}
/**
* Test that the deploy operation can fail part way through, in which
* case the service is in the state that it was in before the failing
* state method was called.
* @throws Throwable on any failure.
*/
@Test
public void testDeployNotAtomic() throws Throwable {
//this instance is set to fail in the start() call.
BreakableService svc = new BreakableService(false, true, false);
try {
ServiceOperations.deploy(svc, new Configuration());
fail("Expected a failure, got " + svc);
} catch (BreakableService.BrokenLifecycleEvent expected) {
//expected
}
//now in the inited state
assertServiceStateInited(svc);
assertStateCount(svc, Service.STATE.INITED, 1);
assertStateCount(svc, Service.STATE.STARTED, 1);
//try again -expect a failure as the service is now inited.
try {
ServiceOperations.deploy(svc, new Configuration());
fail("Expected a failure, got " + svc);
} catch (IllegalStateException e) {
//expected
}
}
/**
* verify that when a service is stopped more than once, no exception
* is thrown, and the counter is not incremented
* this is because the state change operations happen after the counter in
* the subclass is incremented, even though stop is meant to be a no-op
* @throws Throwable on a failure
*/
@Test
public void testStopTwice() throws Throwable {
BreakableService svc = new BreakableService();
ServiceOperations.deploy(svc, new Configuration());
ServiceOperations.stop(svc);
assertStateCount(svc, Service.STATE.STOPPED, 1);
assertServiceStateStopped(svc);
ServiceOperations.stop(svc);
assertStateCount(svc, Service.STATE.STOPPED, 1);
}
/**
* verify that when a service that is not started is stopped, it's counter
* is not incremented -the stop() method was not invoked.
* @throws Throwable on a failure
*/
@Test
public void testStopInit() throws Throwable {
BreakableService svc = new BreakableService();
ServiceOperations.stop(svc);
assertServiceStateCreated(svc);
assertStateCount(svc, Service.STATE.STOPPED, 0);
ServiceOperations.stop(svc);
assertStateCount(svc, Service.STATE.STOPPED, 0);
}
/**
* Show that if the service failed during an init
* operation, it stays in the created state, even after stopping it
* @throws Throwable
*/
@Test
public void testStopFailedInit() throws Throwable {
BreakableService svc = new BreakableService(true, false, false);
assertServiceStateCreated(svc);
try {
ServiceOperations.init(svc, new Configuration());
fail("Expected a failure, got " + svc);
} catch (BreakableService.BrokenLifecycleEvent e) {
//expected
}
//the service state wasn't passed
assertServiceStateCreated(svc);
//the init state got invoked once
assertStateCount(svc, Service.STATE.INITED, 1);
//now try to stop
ServiceOperations.stop(svc);
//even after the stop operation, we haven't entered the state
assertServiceStateCreated(svc);
}
/**
* Show that if the service failed during an init
* operation, it stays in the created state, even after stopping it
* @throws Throwable
*/
@Test
public void testStopFailedStart() throws Throwable {
BreakableService svc = new BreakableService(false, true, false);
ServiceOperations.init(svc, new Configuration());
assertServiceStateInited(svc);
try {
ServiceOperations.start(svc);
fail("Expected a failure, got " + svc);
} catch (BreakableService.BrokenLifecycleEvent e) {
//expected
}
//the service state wasn't passed
assertServiceStateInited(svc);
assertStateCount(svc, Service.STATE.INITED, 1);
//now try to stop
ServiceOperations.stop(svc);
//even after the stop operation, we haven't entered the state
assertServiceStateInited(svc);
}
/**
* verify that when a service is stopped more than once, no exception
* is thrown, and the counter is incremented
* this is because the state change operations happen after the counter in
* the subclass is incremented, even though stop is meant to be a no-op.
*
* The {@link ServiceOperations#stop(Service)} operation does not prevent
* this from happening
* @throws Throwable
*/
@Test
public void testFailingStop() throws Throwable {
BreakableService svc = new BreakableService(false, false, true);
ServiceOperations.deploy(svc, new Configuration());
try {
ServiceOperations.stop(svc);
fail("Expected a failure, got " + svc);
} catch (BreakableService.BrokenLifecycleEvent e) {
//expected
}
assertStateCount(svc, Service.STATE.STOPPED, 1);
//now try to stop, this time doing it quietly
Exception exception = ServiceOperations.stopQuietly(svc);
assertTrue("Wrong exception type : " + exception,
exception instanceof BreakableService.BrokenLifecycleEvent);
assertStateCount(svc, Service.STATE.STOPPED, 2);
}
/**
* verify that when a service that is not started is stopped, its counter
* of stop calls is still incremented-and the service remains in its
* original state..
* @throws Throwable on a failure
*/
@Test
public void testStopUnstarted() throws Throwable {
BreakableService svc = new BreakableService();
//invocation in NOTINITED state should be no-op
ServiceOperations.stop(svc);
assertServiceStateCreated(svc);
assertStateCount(svc, Service.STATE.STOPPED, 0);
//stop failed, now it can be initialised
ServiceOperations.init(svc, new Configuration());
//again, no-op
ServiceOperations.stop(svc);
assertServiceStateInited(svc);
assertStateCount(svc, Service.STATE.STOPPED, 0);
//once started, the service can be stopped reliably
ServiceOperations.start(svc);
ServiceOperations.stop(svc);
assertServiceStateStopped(svc);
assertStateCount(svc, Service.STATE.STOPPED, 1);
//now stop one more time
ServiceOperations.stop(svc);
assertStateCount(svc, Service.STATE.STOPPED, 1);
}
}