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.singleton.api.ClusterSingletonService;
32 import org.opendaylight.mdsal.singleton.api.ServiceGroupIdentifier;
33 import org.opendaylight.mdsal.singleton.impl.EOSClusterSingletonServiceProvider;
34 import org.opendaylight.yangtools.concepts.Registration;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 public class ClusterSingletonIntegrationTest extends AbstractNativeEosTest {
40 private static final Logger LOG = LoggerFactory.getLogger(ClusterSingletonIntegrationTest.class);
42 private AbstractNativeEosTest.MockNativeEntityOwnershipService node1;
43 private MockNativeEntityOwnershipService node2;
44 private MockNativeEntityOwnershipService node3;
46 private EOSClusterSingletonServiceProvider singletonNode1;
47 private EOSClusterSingletonServiceProvider singletonNode2;
48 private EOSClusterSingletonServiceProvider singletonNode3;
52 public void setUp() throws Exception {
53 node1 = startupNativeService(2550, List.of("member-1"), THREE_NODE_SEED_NODES);
54 node2 = startupNativeService(2551, List.of("member-2"), THREE_NODE_SEED_NODES);
55 node3 = startupNativeService(2552, List.of("member-3"), THREE_NODE_SEED_NODES);
57 singletonNode1 = new EOSClusterSingletonServiceProvider(node1);
58 singletonNode2 = new EOSClusterSingletonServiceProvider(node2);
59 singletonNode3 = new EOSClusterSingletonServiceProvider(node3);
61 waitUntillNodeReady(node3);
65 public void tearDown() {
66 ActorTestKit.shutdown(Adapter.toTyped(node1.getActorSystem()), Duration.ofSeconds(20));
67 ActorTestKit.shutdown(Adapter.toTyped(node2.getActorSystem()), Duration.ofSeconds(20));
68 ActorTestKit.shutdown(Adapter.toTyped(node3.getActorSystem()), Duration.ofSeconds(20));
72 public void testSingletonOwnershipNotDropped() {
73 final MockClusterSingletonService service = new MockClusterSingletonService("member-1", "service-1");
74 singletonNode1.registerClusterSingletonService(service);
76 verifyServiceActive(service);
78 final MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
79 singletonNode2.registerClusterSingletonService(service2);
81 verifyServiceInactive(service2, 2);
85 public void testSingletonOwnershipHandoff() {
86 final MockClusterSingletonService service = new MockClusterSingletonService("member-1", "service-1");
87 final Registration registration = singletonNode1.registerClusterSingletonService(service);
89 verifyServiceActive(service);
91 final MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
92 singletonNode2.registerClusterSingletonService(service2);
94 verifyServiceInactive(service2, 2);
97 verifyServiceInactive(service);
98 verifyServiceActive(service2);
102 public void testSingletonOwnershipHandoffOnNodeShutdown() throws Exception {
103 MockClusterSingletonService service2 = new MockClusterSingletonService("member-2", "service-1");
104 Registration registration2 = singletonNode2.registerClusterSingletonService(service2);
106 verifyServiceActive(service2);
108 final MockClusterSingletonService service3 = new MockClusterSingletonService("member-3", "service-1");
109 final Registration registration3 = singletonNode3.registerClusterSingletonService(service3);
111 verifyServiceInactive(service3, 2);
113 LOG.debug("Shutting down node2");
114 TestKit.shutdownActorSystem(node2.getActorSystem());
115 verifyServiceActive(service3);
117 node2 = startupNativeService(2551, List.of("member-1"), THREE_NODE_SEED_NODES);
118 singletonNode2 = new EOSClusterSingletonServiceProvider(node2);
120 waitUntillNodeReady(node2);
121 service2 = new MockClusterSingletonService("member-2", "service-1");
122 singletonNode2.registerClusterSingletonService(service2);
124 verifyServiceActive(service3);
125 verifyServiceInactive(service2, 5);
128 private static void waitUntillNodeReady(final MockNativeEntityOwnershipService node) {
129 // need to wait until all nodes are ready
130 final Cluster cluster = Cluster.get(Adapter.toTyped(node.getActorSystem()));
131 Awaitility.await().atMost(Duration.ofSeconds(20)).until(() -> {
132 final List<Member> members = new ArrayList<>();
133 cluster.state().getMembers().forEach(members::add);
134 if (members.size() != 3) {
138 for (final Member member : members) {
139 if (!member.status().equals(MemberStatus.up())) {
148 private static void verifyServiceActive(final MockClusterSingletonService service) {
149 await().untilAsserted(() -> assertTrue(service.isActivated()));
152 private static void verifyServiceActive(final MockClusterSingletonService service, final long delay) {
153 await().pollDelay(delay, TimeUnit.SECONDS).untilAsserted(() -> assertTrue(service.isActivated()));
156 private static void verifyServiceInactive(final MockClusterSingletonService service) {
157 await().untilAsserted(() -> assertFalse(service.isActivated()));
160 private static void verifyServiceInactive(final MockClusterSingletonService service, final long delay) {
161 await().pollDelay(delay, TimeUnit.SECONDS).untilAsserted(() -> assertFalse(service.isActivated()));
164 private static class MockClusterSingletonService implements ClusterSingletonService {
166 private final String member;
167 private final ServiceGroupIdentifier identifier;
168 private boolean activated = false;
170 MockClusterSingletonService(final String member, final String identifier) {
171 this.member = member;
172 this.identifier = new ServiceGroupIdentifier(identifier);
176 public void instantiateServiceInstance() {
177 LOG.debug("{} : Activating service: {}", member, identifier);
182 public ListenableFuture<? extends Object> closeServiceInstance() {
183 LOG.debug("{} : Closing service: {}", member, identifier);
185 return Futures.immediateFuture(null);
189 public ServiceGroupIdentifier getIdentifier() {
193 public boolean isActivated() {