mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
17 hours ago d954c4c53f3d626f2a60c86eed653569dfd907fe
opendj-server-legacy/src/test/java/org/opends/server/replication/service/ReplicationBrokerTest.java
@@ -12,24 +12,31 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2014-2015 ForgeRock AS.
 * Portions Copyright 2026 3A Systems, LLC.
 */
package org.opends.server.replication.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.replication.common.DSInfo;
import org.opends.server.replication.common.RSInfo;
import org.opends.server.replication.common.ServerState;
import org.opends.server.replication.protocol.TopologyMsg;
import org.opends.server.replication.service.ReplicationBroker.RSEvaluations;
import org.opends.server.replication.service.ReplicationBroker.ReplicationServerInfo;
import org.opends.server.replication.service.ReplicationBroker.Topology;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
import static org.opends.server.replication.service.ReplicationBroker.computeBestReplicationServer;
import static org.opends.server.util.CollectionUtils.*;
import static org.testng.Assert.*;
@@ -254,4 +261,347 @@
    assertThat(currentRS.isLocallyConfigured()).isTrue();
    assertThat(anotherRS.isLocallyConfigured()).isFalse();
  }
  /**
   * A topology names a replication server before the configuration of the domain lists it: the next
   * topology, built once the configuration lists it, must stamp it as locally configured.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldStampRSAddedToTheConfigurationAfterThePreviousTopology(TopologyCtorToUse toUse)
  {
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "127.0.0.1:1389", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), newHashSet(CURRENT_RS.getServerURL()), EMPTY_MAP);
    assertThat(find(before.rsInfos, announcedRS).isLocallyConfigured()).isFalse();
    final RSInfo configuredRS = new RSInfo(ANOTHER_RS_ID, "localhost:1389", 0, (byte) 0, 0);
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), configuredRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(),
        newHashSet(CURRENT_RS.getServerURL(), "localhost:1389"), before.rsInfos);
    assertInvariants(after);
    final ReplicationServerInfo anotherRS = find(after.rsInfos, configuredRS);
    assertThat(anotherRS.isLocallyConfigured()).isTrue();
    assertThat(anotherRS.getServerURL()).isEqualTo("localhost:1389");
    assertThat(find(after.rsInfos, CURRENT_RS.toRSInfo()).isLocallyConfigured()).isTrue();
  }
  /**
   * A configured replication server announces in the topology messages a URL other than the
   * configured one (a port mapped by NAT or by Docker), and the broker hears of it first that way.
   * Once a phase-one handshake has reached it at the configured URL, it is locally configured, and
   * stays so when the next topology message names it with the announced URL again.
   */
  @Test
  @SuppressWarnings("unchecked")
  public void topologyShouldStampRSReachedAtAConfiguredURLItDoesNotAnnounce()
  {
    final Set<String> configured = newHashSet(CURRENT_RS.getServerURL(), "localhost:28989");
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:8989", 0, (byte) 0, 0);
    final Topology heard = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), configured, EMPTY_MAP);
    assertThat(find(heard.rsInfos, announcedRS).isLocallyConfigured()).isFalse();
    final RSInfo reachedRS = new RSInfo(ANOTHER_RS_ID, "localhost:28989", 0, (byte) 0, 0);
    final Topology connected = newTopology(TopologyCtorToUse.BUILD_WITH_DS_RS_LISTS,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), reachedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID, configured, heard.rsInfos);
    ReplicationServerInfo anotherRS = find(connected.rsInfos, reachedRS);
    assertThat(anotherRS.isLocallyConfigured()).isTrue();
    assertThat(anotherRS.getServerURL()).isEqualTo("localhost:28989");
    final Topology next = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID, configured, connected.rsInfos);
    anotherRS = find(next.rsInfos, announcedRS);
    assertThat(anotherRS.isLocallyConfigured()).isTrue();
    assertThat(anotherRS.getServerURL()).isEqualTo("localhost:28989");
  }
  /**
   * A replication server the previous topology stamped as locally configured stays so as long as the
   * configuration lists the URL it is known with, even when the topology names it with another URL (a port
   * mapped by NAT or by Docker) and its host name cannot be resolved at that moment: losing the flag
   * would make the broker leave the server it is connected to.
   * <p>
   * The case shows that the host is not resolved only where resolving it fails: a resolver which
   * answers for any name (the fake addresses of a proxy or VPN DNS) skips it.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldKeepStampedAConfiguredRSWithoutResolvingItsHost(TopologyCtorToUse toUse)
  {
    try
    {
      final InetAddress[] answer = InetAddress.getAllByName("rs.invalid");
      throw new SkipException("The resolver answers for rs.invalid with " + Arrays.toString(answer)
          + ": this case could not tell a resolved host from one that is not");
    }
    catch (UnknownHostException expected)
    {
      // The premise of this case: resolving the host fails
    }
    final String unresolvableURL = "rs.invalid:1389";
    final RSInfo connectedRS = new RSInfo(ANOTHER_RS_ID, unresolvableURL, 0, (byte) 0, 0);
    final Map<Integer, ReplicationServerInfo> previousRSs =
        newMap(new ReplicationServerInfo(connectedRS, newHashSet(CURRENT_DS.getDsId())));
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "rs.invalid:8989", 0, (byte) 0, 0);
    final Topology topo = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(announcedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID, newHashSet(unresolvableURL), previousRSs);
    assertInvariants(topo);
    final ReplicationServerInfo anotherRS = find(topo.rsInfos, announcedRS);
    assertThat(anotherRS.isLocallyConfigured()).isTrue();
    assertThat(anotherRS.getServerURL()).isEqualTo(unresolvableURL);
  }
  /**
   * A replication server is removed from the configuration of the domain: the next topology must no
   * longer stamp it as locally configured.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldNotStampRSRemovedFromTheConfigurationAfterThePreviousTopology(TopologyCtorToUse toUse)
  {
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:1389", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(),
        newHashSet(CURRENT_RS.getServerURL(), "localhost:1389"), EMPTY_MAP);
    assertThat(find(before.rsInfos, announcedRS).isLocallyConfigured()).isTrue();
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), newHashSet(CURRENT_RS.getServerURL()), before.rsInfos);
    assertInvariants(after);
    assertThat(find(after.rsInfos, announcedRS).isLocallyConfigured()).isFalse();
  }
  /**
   * A topology names a replication server before the configuration of the domain lists it, with the URL
   * the configuration then lists it with: the next topology must stamp it as locally configured.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldStampRSAddedToTheConfigurationWithTheURLItAnnounces(TopologyCtorToUse toUse)
  {
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:1389", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), newHashSet(CURRENT_RS.getServerURL()), EMPTY_MAP);
    assertThat(find(before.rsInfos, announcedRS).isLocallyConfigured()).isFalse();
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(),
        newHashSet(CURRENT_RS.getServerURL(), "localhost:1389"), before.rsInfos);
    assertInvariants(after);
    assertThat(find(after.rsInfos, announcedRS).isLocallyConfigured()).isTrue();
  }
  /**
   * The broker has reconnected to a replication server a topology named before the configuration of
   * the domain listed it, and the topology of the new session has the infos of the phase-one handshakes:
   * the periodic check of the best replication server must not reject it as a server which is not
   * locally configured.
   * <p>
   * The other server is not on this host, so the same-host filter keeps only this one, and the check
   * returns it before it reads which server the broker is connected to: the case pins the filter of the
   * locally configured servers only.
   */
  @Test
  @SuppressWarnings("unchecked")
  public void bestServerCheckShouldNotRejectTheRSAddedToTheConfiguration()
  {
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:1389", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), newHashSet(CURRENT_RS.getServerURL()), EMPTY_MAP);
    final Topology reconnected = newTopology(TopologyCtorToUse.BUILD_WITH_DS_RS_LISTS,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID,
        newHashSet(CURRENT_RS.getServerURL(), "localhost:1389"), before.rsInfos);
    final RSEvaluations evals = computeBestReplicationServer(false, ANOTHER_RS_ID, new ServerState(),
        reconnected.rsInfos, CURRENT_DS.getDsId(), (byte) 0, 0);
    assertThat(evals.getBestRS()).isNotNull();
    assertThat(evals.getBestRS().getServerId()).as(evals.toString()).isEqualTo(ANOTHER_RS_ID);
  }
  /**
   * The entry of the configuration which lists a replication server stamped as locally configured is
   * written another way, with an equivalent URL: the server must keep the flag, with the configured URL,
   * even when the restart of the session this asks for is refused (while a total update runs).
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldKeepStampedRSWhoseConfiguredURLIsWrittenAnotherWay(TopologyCtorToUse toUse)
  {
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:8989", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(),
        newHashSet(CURRENT_RS.getServerURL(), "localhost:8989"), EMPTY_MAP);
    assertThat(find(before.rsInfos, announcedRS).isLocallyConfigured()).isTrue();
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(),
        newHashSet(CURRENT_RS.getServerURL(), "127.0.0.1:8989"), before.rsInfos);
    assertInvariants(after);
    final ReplicationServerInfo anotherRS = find(after.rsInfos, announcedRS);
    assertThat(anotherRS.isLocallyConfigured()).isTrue();
    assertThat(anotherRS.getServerURL()).isEqualTo("127.0.0.1:8989");
  }
  /**
   * A known replication server which is not locally configured comes with a URL which is only an
   * equivalent of a configured one: the topology message must not resolve it, so the server gets the flag
   * at the next connection only.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldNotResolveTheURLAKnownRSComesWith(TopologyCtorToUse toUse)
  {
    final Set<String> configured = newHashSet(CURRENT_RS.getServerURL(), "localhost:28989");
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:8989", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), configured, EMPTY_MAP);
    assertThat(find(before.rsInfos, announcedRS).isLocallyConfigured()).isFalse();
    final RSInfo equivalentRS = new RSInfo(ANOTHER_RS_ID, "127.0.0.1:28989", 0, (byte) 0, 0);
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), equivalentRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), configured, before.rsInfos);
    assertInvariants(after);
    assertThat(find(after.rsInfos, equivalentRS).isLocallyConfigured()).isFalse();
  }
  /**
   * A known replication server which is not locally configured gets an equivalent of its URL in the
   * configuration of the domain, written another way: the next topology must not resolve it either, so
   * the server gets the flag at the next connection only.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldNotResolveTheURLAKnownRSIsKnownWith(TopologyCtorToUse toUse)
  {
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "127.0.0.1:28989", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), newHashSet(CURRENT_RS.getServerURL()), EMPTY_MAP);
    assertThat(find(before.rsInfos, announcedRS).isLocallyConfigured()).isFalse();
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(),
        newHashSet(CURRENT_RS.getServerURL(), "localhost:28989"), before.rsInfos);
    assertInvariants(after);
    assertThat(find(after.rsInfos, announcedRS).isLocallyConfigured()).isFalse();
  }
  /**
   * A known replication server comes with another URL in the next topology (a mapped port) while the
   * configuration of the domain now lists the URL it is known with: it is locally configured, without
   * resolving any host name.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldStampRSAddedToTheConfigurationWithTheURLItIsKnownWith(TopologyCtorToUse toUse)
  {
    final RSInfo knownRS = new RSInfo(ANOTHER_RS_ID, "127.0.0.2:28989", 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), knownRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), newHashSet(CURRENT_RS.getServerURL()), EMPTY_MAP);
    assertThat(find(before.rsInfos, knownRS).isLocallyConfigured()).isFalse();
    final RSInfo mappedRS = new RSInfo(ANOTHER_RS_ID, "127.0.0.3:28989", 0, (byte) 0, 0);
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), mappedRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(),
        newHashSet(CURRENT_RS.getServerURL(), "127.0.0.2:28989"), before.rsInfos);
    assertInvariants(after);
    final ReplicationServerInfo rs = find(after.rsInfos, mappedRS);
    assertThat(rs.isLocallyConfigured()).isTrue();
    assertThat(rs.getServerURL()).isEqualTo("127.0.0.2:28989");
  }
  /**
   * A known replication server with no URL (protocol version below 4) is not locally configured, and the
   * check must not ask the configured URLs for a null one: they are a sorted set, as in the configuration,
   * whose contains(null) throws.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldNotStampKnownRSWithoutURL(TopologyCtorToUse toUse)
  {
    // sorted, as the configuration is: contains(null) throws
    final Set<String> configured = new TreeSet<>(Arrays.asList(CURRENT_RS.getServerURL(), "localhost:8989"));
    final RSInfo noUrlRS = new RSInfo(ANOTHER_RS_ID, null, 0, (byte) 0, 0);
    final Topology before = newTopology(TopologyCtorToUse.BUILD_WITH_TOPOLOGY_MSG,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), noUrlRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), configured, EMPTY_MAP);
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), noUrlRS),
        CURRENT_DS.getDsId(), CURRENT_RS.getServerId(), configured, before.rsInfos);
    assertThat(find(after.rsInfos, noUrlRS).isLocallyConfigured()).isFalse();
  }
  /**
   * A locally configured replication server, known with the configured URL a phase-one handshake reached
   * it at, comes with another URL (a port mapped by NAT or by Docker), and the entry of the configuration
   * is written another way, as an equivalent of the URL it comes with: the server must keep the flag,
   * with the configured URL.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldKeepStampedRSWhoseConfiguredURLIsWrittenAsTheURLItComesWith(TopologyCtorToUse toUse)
  {
    final RSInfo reachedRS = new RSInfo(ANOTHER_RS_ID, "localhost:28989", 0, (byte) 0, 0);
    final Topology connected = newTopology(TopologyCtorToUse.BUILD_WITH_DS_RS_LISTS,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), reachedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID, newHashSet(CURRENT_RS.getServerURL(), "localhost:28989"), EMPTY_MAP);
    assertThat(find(connected.rsInfos, reachedRS).isLocallyConfigured()).isTrue();
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:8989", 0, (byte) 0, 0);
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID,
        newHashSet(CURRENT_RS.getServerURL(), "127.0.0.1:8989"), connected.rsInfos);
    assertInvariants(after);
    final ReplicationServerInfo rs = find(after.rsInfos, announcedRS);
    assertThat(rs.isLocallyConfigured()).isTrue();
    assertThat(rs.getServerURL()).isEqualTo("127.0.0.1:8989");
  }
  /**
   * A locally configured replication server, known with the configured URL a phase-one handshake reached
   * it at, comes with another URL, and the entry of the configuration is replaced with a URL equivalent to
   * neither: the server loses the flag, and keeps the URL it is known with.
   */
  @Test(dataProvider = "topologyCtorProvider")
  @SuppressWarnings("unchecked")
  public void topologyShouldNotStampRSWhoseConfiguredURLNamesNeitherOfItsURLs(TopologyCtorToUse toUse)
  {
    final RSInfo reachedRS = new RSInfo(ANOTHER_RS_ID, "localhost:28989", 0, (byte) 0, 0);
    final Topology connected = newTopology(TopologyCtorToUse.BUILD_WITH_DS_RS_LISTS,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), reachedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID, newHashSet(CURRENT_RS.getServerURL(), "localhost:28989"), EMPTY_MAP);
    assertThat(find(connected.rsInfos, reachedRS).isLocallyConfigured()).isTrue();
    final RSInfo announcedRS = new RSInfo(ANOTHER_RS_ID, "localhost:8989", 0, (byte) 0, 0);
    final Topology after = newTopology(toUse,
        newMap(OTHER_DS), newArrayList(CURRENT_RS.toRSInfo(), announcedRS),
        CURRENT_DS.getDsId(), ANOTHER_RS_ID,
        newHashSet(CURRENT_RS.getServerURL(), "127.0.0.1:18989"), connected.rsInfos);
    assertInvariants(after);
    final ReplicationServerInfo rs = find(after.rsInfos, announcedRS);
    assertThat(rs.isLocallyConfigured()).isFalse();
    assertThat(rs.getServerURL()).isEqualTo("localhost:28989");
  }
}