diff --git a/logstash-core/src/main/java/org/logstash/health/HealthObserver.java b/logstash-core/src/main/java/org/logstash/health/HealthObserver.java index 63a461a74e6..fdc09113354 100644 --- a/logstash-core/src/main/java/org/logstash/health/HealthObserver.java +++ b/logstash-core/src/main/java/org/logstash/health/HealthObserver.java @@ -25,6 +25,8 @@ public class HealthObserver { private static final Logger LOGGER = LogManager.getLogger(); + static final String FORCE_API_STATUS_PROPERTY = "logstash.forceApiStatus"; + private final MultiIndicator rootIndicator = new MultiIndicator(); private final MultiIndicator pipelinesIndicator = new MultiIndicator(); @@ -33,6 +35,15 @@ public HealthObserver() { } public final Status getStatus() { + // Short-term escape-hatch + final String forceApiStatus = System.getProperty(FORCE_API_STATUS_PROPERTY); + if (forceApiStatus != null) { + if (forceApiStatus.equals("green")) { + return Status.GREEN; + } else { + LOGGER.warn("Unsupported `logstash.forceApiStatus`: {} (health report status will be propagated)", forceApiStatus); + } + } return getReport().getStatus(); } diff --git a/logstash-core/src/test/java/org/logstash/health/HealthObserverTest.java b/logstash-core/src/test/java/org/logstash/health/HealthObserverTest.java new file mode 100644 index 00000000000..77313a17e94 --- /dev/null +++ b/logstash-core/src/test/java/org/logstash/health/HealthObserverTest.java @@ -0,0 +1,93 @@ +package org.logstash.health; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.Objects; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.logstash.health.HealthObserver.FORCE_API_STATUS_PROPERTY; + +public class HealthObserverTest { + + private final HealthObserver healthObserver = new HealthObserver(); + + @Test + public void testStatusWhenNotForcedPropagates() { + withUnsetSystemProperty(FORCE_API_STATUS_PROPERTY, () -> { + for (Status reportStatus : Status.values()) { + withIndicator(new TestIndicator(reportStatus), () -> { + assertThat(String.format("Status[%s] should propagate", reportStatus), healthObserver.getStatus(), Matchers.is(reportStatus)); + }); + } + }); + } + + @Test + public void testStatusWhenForcedGreenEmitsGreen() { + withSystemProperty(FORCE_API_STATUS_PROPERTY, "green", () -> { + for (Status reportStatus : Status.values()) { + withIndicator(new TestIndicator(reportStatus), () -> { + assertThat(String.format("Status[%s] should not propagate", reportStatus), healthObserver.getStatus(), Matchers.is(Status.GREEN)); + }); + } + }); + } + + @Test + public void testStatusWhenForcedNonsensePropagates() { + withSystemProperty(FORCE_API_STATUS_PROPERTY, "nonsense", () -> { + for (Status reportStatus : Status.values()) { + withIndicator(new TestIndicator(reportStatus), () -> { + assertThat(String.format("Status[%s] should propagate", reportStatus), healthObserver.getStatus(), Matchers.is(reportStatus)); + }); + } + }); + } + + void withUnsetSystemProperty(final String propertyName, final Runnable action) { + withSystemProperty(propertyName, null, action); + } + + void withSystemProperty(final String name, final String value, final Runnable action) { + synchronized (System.class) { + final String oldValue; + if (Objects.isNull(value)) { + oldValue = System.clearProperty(name); + } else { + oldValue = System.setProperty(name, value); + } + try { + action.run(); + } finally { + if (oldValue != null) { + System.setProperty(name, oldValue); + } else { + System.clearProperty(name); + } + } + } + } + + void withIndicator(Indicator indicator, Runnable runnable) { + this.healthObserver.getIndicator().attachIndicator("single", indicator); + try { + runnable.run(); + } finally { + this.healthObserver.getIndicator().detachIndicator("single", indicator); + } + } + + static class TestIndicator implements Indicator { + private final Status status; + + public TestIndicator(final Status status) { + this.status = status; + } + + @Override + public Report report(ReportContext reportContext) { + return () -> this.status; + } + } +} \ No newline at end of file