2 * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.eos.akka.service;
10 import static org.awaitility.Awaitility.await;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertTrue;
14 import akka.actor.testkit.typed.javadsl.ActorTestKit;
15 import akka.actor.typed.javadsl.Adapter;
16 import akka.cluster.Member;
17 import akka.cluster.MemberStatus;
18 import akka.cluster.typed.Cluster;
19 import akka.testkit.javadsl.TestKit;
20 import com.google.common.util.concurrent.Futures;
21 import com.google.common.util.concurrent.ListenableFuture;
22 import java.time.Duration;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.concurrent.TimeUnit;
26 import org.awaitility.Awaitility;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.opendaylight.controller.eos.akka.AbstractNativeEosTest;
31 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
32 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
33 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
34 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
35 import org.opendaylight.mdsal.singleton.dom.impl.DOMClusterSingletonServiceProviderImpl;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 public class ClusterSingletonIntegrationTest extends AbstractNativeEosTest {
41 private static final Logger LOG = LoggerFactory.getLogger(ClusterSingletonIntegrationTest.class);
43 private AbstractNativeEosTest.MockNativeEntityOwnershipService node1;
44 private MockNativeEntityOwnershipService node2;
45 private MockNativeEntityOwnershipService node3;
47 private MockSingletonService singletonNode1;
48 private MockSingletonService singletonNode2;
49 private MockSingletonService singletonNode3;
53 public void setUp() throws Exception {
54 node1 = startupNativeService(2550, List.of("member-1"), THREE_NODE_SEED_NODES);
55 node2 = startupNativeService(2551, List.of("member-2"), THREE_NODE_SEED_NODES);
56 node3 = startupNativeService(2552, List.of("member-3"), THREE_NODE_SEED_NODES);
58 singletonNode1 = new MockSingletonService(node1);
59 singletonNode1.initializeProvider();
61 singletonNode2 = new MockSingletonService(node2);
62 singletonNode2.initializeProvider();
64 singletonNode3 = new MockSingletonService(node3);
65 singletonNode3.initializeProvider();
67 waitUntillNodeReady(node3);
71 public void tearDown() {
72 ActorTestKit.shutdown(Adapter.toTyped(node1.getActorSystem()), Duration.ofSeconds(20));
73 ActorTestKit.shutdown(Adapter.toTyped(node2.getActorSystem()), Duration.ofSeconds(20));
74 ActorTestKit.shutdown(Adapter.toTyped(node3.getActorSystem()), Duration.ofSeconds(20));
78 public void testSingletonOwnershipNotDropped() {
79 final MockClusterSingletonService service = new MockClusterSingletonService("member-1", "service-1");
80 singletonNode1.registerClusterSingletonService(service);
82 verifyServiceActive(service);
84 final MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
85 singletonNode2.registerClusterSingletonService(service2);
87 verifyServiceInactive(service2, 2);
91 public void testSingletonOwnershipHandoff() {
92 final MockClusterSingletonService service = new MockClusterSingletonService("member-1", "service-1");
93 final ClusterSingletonServiceRegistration registration =
94 singletonNode1.registerClusterSingletonService(service);
96 verifyServiceActive(service);
98 final MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
99 singletonNode2.registerClusterSingletonService(service2);
101 verifyServiceInactive(service2, 2);
103 registration.close();
104 verifyServiceInactive(service);
105 verifyServiceActive(service2);
109 public void testSingletonOwnershipHandoffOnNodeShutdown() throws Exception {
110 MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
111 ClusterSingletonServiceRegistration registration2 =
112 singletonNode2.registerClusterSingletonService(service2);
114 verifyServiceActive(service2);
116 final MockClusterSingletonService service3 = new MockClusterSingletonService("member-3", "service-1");
117 final ClusterSingletonServiceRegistration registration3 =
118 singletonNode3.registerClusterSingletonService(service3);
120 verifyServiceInactive(service3, 2);
122 LOG.debug("Shutting down node2");
123 TestKit.shutdownActorSystem(node2.getActorSystem());
124 verifyServiceActive(service3);
126 node2 = startupNativeService(2551, List.of("member-1"), THREE_NODE_SEED_NODES);
127 singletonNode2 = new MockSingletonService(node2);
128 singletonNode2.initializeProvider();
130 waitUntillNodeReady(node2);
131 service2 = new MockClusterSingletonService("member-2", "service-1");
132 singletonNode2.registerClusterSingletonService(service2);
134 verifyServiceActive(service3);
135 verifyServiceInactive(service2, 5);
138 private void waitUntillNodeReady(MockNativeEntityOwnershipService node) {
139 // need to wait until all nodes are ready
140 final Cluster cluster = Cluster.get(Adapter.toTyped(node.getActorSystem()));
141 Awaitility.await().atMost(Duration.ofSeconds(20)).until(() -> {
142 final List<Member> members = new ArrayList<>();
143 cluster.state().getMembers().forEach(members::add);
144 if (members.size() != 3) {
148 for (final Member member : members) {
149 if (!member.status().equals(MemberStatus.up())) {
158 private static void verifyServiceActive(MockClusterSingletonService service) {
159 await().untilAsserted(() -> assertTrue(service.isActivated()));
162 private static void verifyServiceActive(MockClusterSingletonService service, long delay) {
163 await().pollDelay(delay, TimeUnit.SECONDS).untilAsserted(() -> assertTrue(service.isActivated()));
166 private static void verifyServiceInactive(MockClusterSingletonService service) {
167 await().untilAsserted(() -> assertFalse(service.isActivated()));
170 private static void verifyServiceInactive(MockClusterSingletonService service, long delay) {
171 await().pollDelay(delay, TimeUnit.SECONDS).untilAsserted(() -> assertFalse(service.isActivated()));
174 private static class MockClusterSingletonService implements ClusterSingletonService {
176 private final String member;
177 private final ServiceGroupIdentifier identifier;
178 private boolean activated = false;
180 MockClusterSingletonService(String member, String identifier) {
181 this.member = member;
182 this.identifier = ServiceGroupIdentifier.create(identifier);
186 public void instantiateServiceInstance() {
187 LOG.debug("{} : Activating service: {}", member, identifier);
192 public ListenableFuture<? extends Object> closeServiceInstance() {
193 LOG.debug("{} : Closing service: {}", member, identifier);
195 return Futures.immediateFuture(null);
199 public ServiceGroupIdentifier getIdentifier() {
203 public boolean isActivated() {
208 private static class MockSingletonService extends DOMClusterSingletonServiceProviderImpl {
209 MockSingletonService(DOMEntityOwnershipService entityOwnershipService) {
210 super(entityOwnershipService);