|
@@ -0,0 +1,312 @@
|
|
|
+/**
|
|
|
+ * 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);
|
|
|
+ }
|
|
|
+}
|