From e050132d623aceeecb050a49c08dd904875cf5a1 Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Tue, 14 Jan 2025 10:21:40 +0100 Subject: [PATCH] fixup: using toxiproxy Signed-off-by: Simon Schrottner --- providers/flagd/pom.xml | 6 ++ .../resolver/common/ConnectionEvent.java | 3 + .../providers/flagd/e2e/FlagdContainer.java | 14 ++-- .../providers/flagd/e2e/RunRpcTest.java | 7 +- .../contrib/providers/flagd/e2e/State.java | 4 +- .../providers/flagd/e2e/steps/EventSteps.java | 16 ++-- .../flagd/e2e/steps/ProviderSteps.java | 81 +++++++++++++------ .../test/resources/simplelogger.properties | 2 + 8 files changed, 90 insertions(+), 43 deletions(-) create mode 100644 providers/flagd/src/test/resources/simplelogger.properties diff --git a/providers/flagd/pom.xml b/providers/flagd/pom.xml index e8fa62c26..a713aaf6d 100644 --- a/providers/flagd/pom.xml +++ b/providers/flagd/pom.xml @@ -149,6 +149,12 @@ 1.20.4 test + + org.testcontainers + toxiproxy + 1.20.4 + test + diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java index 0e8ff4c6b..38dda199f 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java @@ -2,6 +2,7 @@ import dev.openfeature.sdk.ImmutableStructure; import dev.openfeature.sdk.Structure; +import lombok.Getter; import java.util.Collections; import java.util.List; @@ -16,6 +17,7 @@ public class ConnectionEvent { /** * The current state of the connection. */ + @Getter private final ConnectionState connected; /** @@ -121,4 +123,5 @@ public boolean isConnected() { public boolean isStale() { return this.connected == ConnectionState.STALE; } + } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java index 53bd91160..4da7830c6 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java @@ -1,18 +1,21 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import com.github.dockerjava.api.command.PauseContainerCmd; +import com.github.dockerjava.api.command.SyncDockerCmd; +import com.github.dockerjava.api.command.UnpauseContainerCmd; import dev.openfeature.contrib.providers.flagd.Config; import org.apache.logging.log4j.util.Strings; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import java.io.File; import java.nio.file.Files; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; public class FlagdContainer extends GenericContainer { private static final String version; @@ -43,13 +46,6 @@ public FlagdContainer(String feature) { this.addExposedPorts(8013, 8014, 8015, 8016); } - @Override - public void start() { - if (!"socket".equals(this.feature)) - this.addExposedPorts(8013, 8014, 8015, 8016); - super.start(); - waitUntilContainerStarted(); - } public int getPort(Config.Resolver resolver) { waitUntilContainerStarted(); diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java index 20dc320bc..53d4f6d37 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java @@ -9,6 +9,7 @@ import org.junit.platform.suite.api.IncludeEngines; import org.junit.platform.suite.api.IncludeTags; import org.junit.platform.suite.api.SelectDirectories; +import org.junit.platform.suite.api.SelectFile; import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; @@ -22,11 +23,13 @@ @Order(value = Integer.MAX_VALUE) @Suite @IncludeEngines("cucumber") -@SelectDirectories("test-harness/gherkin") +//@SelectDirectories("test-harness/gherkin") +@SelectFile("test-harness/gherkin/connection.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps") @ConfigurationParameter(key = OBJECT_FACTORY_PROPERTY_NAME, value = "io.cucumber.picocontainer.PicoFactory") -@IncludeTags("rpc") +@IncludeTags({"rpc","reconnect"}) +@ExcludeTags({"targetURI", "customCert", "unixsocket"}) @Testcontainers public class RunRpcTest { diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java index 99126a583..256174a53 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java @@ -10,12 +10,14 @@ import dev.openfeature.sdk.MutableContext; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import java.util.Optional; public class State { public ProviderType providerType; public Client client; - public ArrayList events = new ArrayList<>(); + public List events = new LinkedList<>(); public Optional lastEvent; public FlagSteps.Flag flag; public MutableContext context diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java index 67b24b155..0288fc370 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java @@ -7,25 +7,29 @@ import io.cucumber.java.en.When; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.parallel.Isolated; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.LinkedList; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.awaitility.Awaitility.await; @Isolated() public class EventSteps extends AbstractSteps { + private static final Logger LOG = LoggerFactory.getLogger(EventSteps.class); public EventSteps(State state) { super(state); - state.events = new ArrayList<>(); + state.events = new LinkedList<>(); } @Given("a {} event handler") public void a_stale_event_handler(String eventType) { state.client.on(mapEventType(eventType), eventDetails -> { - System.out.println(eventType); + LOG.info("event tracked for {} ", eventType); state.events.add(new Event(eventType, eventDetails)); }); } @@ -52,17 +56,15 @@ public void eventWasFired(String eventType) { @Then("the {} event handler should have been executed") public void eventHandlerShouldBeExecuted(String eventType) { - eventHandlerShouldBeExecutedWithin(eventType, 10000); + eventHandlerShouldBeExecutedWithin(eventType, 30000); } @Then("the {} event handler should have been executed within {int}ms") public void eventHandlerShouldBeExecutedWithin(String eventType, int ms) { + LOG.info("waiting for eventtype: {}", eventType); await() .atMost(ms, MILLISECONDS) - .until(() -> { - state.events.forEach((e) -> System.out.println(e.type)); - return state.events.stream().anyMatch(event -> event.type.equals(eventType)); - }); + .until(() -> state.events.stream().anyMatch(event -> event.type.equals(eventType))); state.lastEvent = state.events.stream().filter(event -> event.type.equals(eventType)).findFirst(); state.events.removeIf(event -> event.type.equals(eventType)); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java index 17ae53025..2b6aebb03 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java @@ -1,11 +1,15 @@ package dev.openfeature.contrib.providers.flagd.e2e.steps; +import dev.openfeature.contrib.providers.flagd.Config; import dev.openfeature.contrib.providers.flagd.FlagdProvider; import dev.openfeature.contrib.providers.flagd.e2e.FlagdContainer; import dev.openfeature.contrib.providers.flagd.e2e.State; -import dev.openfeature.sdk.Client; import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.OpenFeatureAPI; +import eu.rekawek.toxiproxy.Proxy; +import eu.rekawek.toxiproxy.ToxiproxyClient; +import eu.rekawek.toxiproxy.model.ToxicDirection; +import eu.rekawek.toxiproxy.model.toxic.Timeout; import io.cucumber.java.After; import io.cucumber.java.AfterAll; import io.cucumber.java.Before; @@ -17,6 +21,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.ToxiproxyContainer; import org.testcontainers.shaded.org.apache.commons.io.FileUtils; import java.io.File; @@ -36,6 +42,11 @@ public class ProviderSteps extends AbstractSteps { public static final int UNAVAILABLE_PORT = 9999; static Map containers = new HashMap<>(); + static Map> proxyports = new HashMap<>(); + public static Network network = Network.newNetwork(); + public static ToxiproxyContainer toxiproxy = new ToxiproxyContainer("ghcr.io/shopify/toxiproxy:2.5.0") + .withNetwork(network).withCreateContainerCmdModifier((cmd -> cmd.withName("toxiproxy"))); + public static ToxiproxyClient toxiproxyClient; static Path sharedTempDir; @@ -52,13 +63,32 @@ public ProviderSteps(State state) { super(state); } + static String generateProxyName(Config.Resolver resolver, ProviderType providerType) { + return providerType + "-" + resolver; + } + @BeforeAll public static void beforeAll() throws IOException { - containers.put(ProviderType.DEFAULT, new FlagdContainer()); - containers.put(ProviderType.SSL, new FlagdContainer("ssl")); + toxiproxy.start(); + toxiproxyClient = new ToxiproxyClient(toxiproxy.getHost(), toxiproxy.getControlPort()); + toxiproxyClient.createProxy( + generateProxyName(Config.Resolver.RPC, ProviderType.DEFAULT), + "0.0.0.0:8666", "default:8013"); + + toxiproxyClient.createProxy(generateProxyName(Config.Resolver.IN_PROCESS, ProviderType.DEFAULT), "0.0.0.0:8667", "default:8015"); + toxiproxyClient.createProxy(generateProxyName(Config.Resolver.RPC, ProviderType.SSL), "0.0.0.0:8668", "ssl:8013"); + toxiproxyClient.createProxy(generateProxyName(Config.Resolver.IN_PROCESS, ProviderType.SSL), "0.0.0.0:8669", "ssl:8015"); + + containers.put(ProviderType.DEFAULT, + new FlagdContainer().withNetwork(network).withNetworkAliases("default") + ); + containers.put(ProviderType.SSL, + new FlagdContainer("ssl").withNetwork(network).withNetworkAliases("ssl") + ); containers.put(ProviderType.SOCKET, new FlagdContainer("socket") .withFileSystemBind(sharedTempDir.toAbsolutePath().toString(), "/tmp", BindMode.READ_WRITE)); + } @AfterAll @@ -69,16 +99,19 @@ public static void afterAll() throws IOException { } @Before - public void before() { + public void before() throws IOException { + containers.values().stream().filter(containers -> !containers.isRunning()) .forEach(FlagdContainer::start); } + @After public void tearDown() { OpenFeatureAPI.getInstance().shutdown(); } + @Given("a {} flagd provider") public void setupProvider(String providerType) { state.builder @@ -106,14 +139,14 @@ public void setupProvider(String providerType) { this.state.providerType = ProviderType.SSL; state .builder - .port(getContainer().getPort(State.resolverType)) + .port(getContainer(state.providerType).getPort(State.resolverType)) .tls(true) .certPath(absolutePath); break; default: this.state.providerType = ProviderType.DEFAULT; - state.builder.port(getContainer().getPort(State.resolverType)); + state.builder.port(toxiproxy.getMappedPort(8666)); break; } FeatureProvider provider = new FlagdProvider(state.builder @@ -130,30 +163,30 @@ public void setupProvider(String providerType) { } @When("the connection is lost for {int}s") - public void the_connection_is_lost_for(int seconds) throws InterruptedException { - FlagdContainer container = getContainer(); - -/* TimerTask task = new TimerTask() { + public void the_connection_is_lost_for(int seconds) throws InterruptedException, IOException { + LOG.info("Timeout and wait for {} seconds", seconds); + Proxy proxy = toxiproxyClient.getProxy(generateProxyName(State.resolverType, state.providerType)); + Timeout restart = proxy + .toxics() + .timeout("restart", ToxicDirection.UPSTREAM, seconds); + + TimerTask task = new TimerTask() { public void run() { - container.start(); - int port = container.getPort(State.resolverType); + try { + proxy.toxics().get("restart").remove(); + } catch (IOException e) { + throw new RuntimeException(e); + } } }; - Timer timer = new Timer("Timer");*/ - - LOG.info("stopping container for {}", state.providerType); - container.stop(); + Timer timer = new Timer("Timer"); - //timer.schedule(task, seconds * 1000L); - Thread.sleep(seconds * 1000L); + timer.schedule(task, seconds * 1000L); - LOG.info("starting container for {}", state.providerType); - container.start(); } - private FlagdContainer getContainer() { - LOG.info("getting container for {}", state.providerType); - System.out.println("getting container for " + state.providerType); - return containers.getOrDefault(state.providerType, containers.get(ProviderType.DEFAULT)); + static FlagdContainer getContainer(ProviderType providerType) { + LOG.info("getting container for {}", providerType); + return containers.getOrDefault(providerType, containers.get(ProviderType.DEFAULT)); } } diff --git a/providers/flagd/src/test/resources/simplelogger.properties b/providers/flagd/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..55fc08759 --- /dev/null +++ b/providers/flagd/src/test/resources/simplelogger.properties @@ -0,0 +1,2 @@ +org.slf4j.simpleLogger.defaultLogLevel=debug +org.slf4j.simpleLogger.logFile=System.out