From 317192fb5d026725f0ea0f83a9a9f94efa2bd299 Mon Sep 17 00:00:00 2001 From: Pierre Date: Mon, 9 Aug 2021 09:44:14 +0200 Subject: [PATCH] SONAR-15239 implement a liveness API endpoint for K8 --- .../server/platform/ws/LivenessAction.java | 58 ++++++++++ .../platform/ws/LivenessActionSupport.java | 59 +++++++++++ .../server/platform/ws/LivenessChecker.java | 24 +++++ .../platform/ws/LivenessCheckerImpl.java | 66 ++++++++++++ .../platform/ws/SafeModeLivenessAction.java | 50 +++++++++ .../ws/SafeModeLivenessCheckerImpl.java | 36 +++++++ .../platform/ws/SafemodeSystemWsModule.java | 7 +- .../server/platform/ws/SystemWsModule.java | 5 + .../platform/ws/LivenessActionTest.java | 100 ++++++++++++++++++ .../platform/ws/LivenessCheckerImplTest.java | 92 ++++++++++++++++ .../ws/SafeModeLivenessActionTest.java | 84 +++++++++++++++ .../ws/SafeModeLivenessCheckerImplTest.java | 50 +++++++++ .../ws/SafemodeSystemWsModuleTest.java | 2 +- .../platform/ws/SystemWsModuleTest.java | 2 +- 14 files changed, 630 insertions(+), 5 deletions(-) create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessAction.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessActionSupport.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessChecker.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessCheckerImpl.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessAction.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImpl.java create mode 100644 server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessActionTest.java create mode 100644 server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessCheckerImplTest.java create mode 100644 server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessActionTest.java create mode 100644 server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImplTest.java diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessAction.java new file mode 100644 index 00000000000..2876e5c64f8 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessAction.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.user.SystemPasscode; +import org.sonar.server.user.UserSession; + +public class LivenessAction implements SystemWsAction { + private final LivenessActionSupport livenessActionSupport; + private final SystemPasscode systemPasscode; + private final UserSession userSession; + + public LivenessAction(LivenessActionSupport livenessActionSupport, SystemPasscode systemPasscode, UserSession userSession) { + this.livenessActionSupport = livenessActionSupport; + this.systemPasscode = systemPasscode; + this.userSession = userSession; + } + + @Override + public void define(WebService.NewController controller) { + livenessActionSupport.define(controller, this); + } + + @Override + public void handle(Request request, Response response) throws Exception { + if (!systemPasscode.isValid(request) && !isSystemAdmin()) { + throw new ForbiddenException("Insufficient privileges"); + } + + livenessActionSupport.checkliveness(response); + } + + private boolean isSystemAdmin() { + return userSession.isSystemAdministrator(); + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessActionSupport.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessActionSupport.java new file mode 100644 index 00000000000..9dc80850c71 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessActionSupport.java @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; + +public class LivenessActionSupport { + + private final LivenessChecker livenessChecker; + + public LivenessActionSupport(LivenessChecker livenessChecker) { + this.livenessChecker = livenessChecker; + } + + void define(WebService.NewController controller, SystemWsAction handler) { + controller.createAction("liveness") + .setDescription("Provide liveness of SonarQube, meant to be used for a liveness probe on Kubernetes" + + "

Require 'Administer System' permission or authentication with passcode

" + + "

When SonarQube is fully started, liveness check for database connectivity, Compute Engine status," + + " and, except for DataCenter Edition, if ElasticSearch is Green or Yellow

"+ + "

When SonarQube is on Safe Mode (for example when a database migration is running), liveness check only for database connectivity

"+ + "

" + + "

" + + "

") + .setSince("9.1") + .setInternal(true) + .setHandler(handler); + } + + void checkliveness(Response response) { + if (livenessChecker.liveness()) { + response.noContent(); + } else { + throw new IllegalStateException("Liveness check failed"); + } + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessChecker.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessChecker.java new file mode 100644 index 00000000000..013d8fc5788 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessChecker.java @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +public interface LivenessChecker { + boolean liveness(); +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessCheckerImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessCheckerImpl.java new file mode 100644 index 00000000000..0be2dd55e6a --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/LivenessCheckerImpl.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.sonar.server.health.CeStatusNodeCheck; +import org.sonar.server.health.DbConnectionNodeCheck; +import org.sonar.server.health.EsStatusNodeCheck; +import org.sonar.server.health.Health; +import org.sonar.server.health.WebServerStatusNodeCheck; +import org.sonar.server.platform.WebServer; + +public class LivenessCheckerImpl implements LivenessChecker { + + private final WebServer webServer; + private final DbConnectionNodeCheck dbConnectionNodeCheck; + private final CeStatusNodeCheck ceStatusNodeCheck; + private final EsStatusNodeCheck esStatusNodeCheck; + private final WebServerStatusNodeCheck webServerStatusNodeCheck; + + public LivenessCheckerImpl(WebServer webServer, DbConnectionNodeCheck dbConnectionNodeCheck, + WebServerStatusNodeCheck webServerStatusNodeCheck, CeStatusNodeCheck ceStatusNodeCheck, EsStatusNodeCheck esStatusNodeCheck) { + this.webServer = webServer; + this.dbConnectionNodeCheck = dbConnectionNodeCheck; + this.ceStatusNodeCheck = ceStatusNodeCheck; + this.esStatusNodeCheck = esStatusNodeCheck; + this.webServerStatusNodeCheck = webServerStatusNodeCheck; + } + + public boolean liveness() { + + if (!Health.Status.GREEN.equals(dbConnectionNodeCheck.check().getStatus())) { + return false; + } + + if (!Health.Status.GREEN.equals(webServerStatusNodeCheck.check().getStatus())) { + return false; + } + + if (!Health.Status.GREEN.equals(ceStatusNodeCheck.check().getStatus())) { + return false; + } + + if (webServer.isStandalone() && Health.Status.RED.equals(esStatusNodeCheck.check().getStatus())) { + return false; + } + + return true; + } +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessAction.java new file mode 100644 index 00000000000..38cb538b8ed --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessAction.java @@ -0,0 +1,50 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.user.SystemPasscode; + +public class SafeModeLivenessAction implements SystemWsAction { + private final LivenessActionSupport livenessActionSupport; + private final SystemPasscode systemPasscode; + + public SafeModeLivenessAction(LivenessActionSupport livenessActionSupport, SystemPasscode systemPasscode) { + this.livenessActionSupport = livenessActionSupport; + this.systemPasscode = systemPasscode; + } + + @Override + public void define(WebService.NewController controller) { + livenessActionSupport.define(controller, this); + } + + @Override + public void handle(Request request, Response response) throws Exception { + if (!systemPasscode.isValid(request)) { + throw new ForbiddenException("Insufficient privileges"); + } + + livenessActionSupport.checkliveness(response); + } +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImpl.java new file mode 100644 index 00000000000..519acf31fa2 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImpl.java @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.sonar.server.health.DbConnectionNodeCheck; +import org.sonar.server.health.Health; + +public class SafeModeLivenessCheckerImpl implements LivenessChecker { + + private final DbConnectionNodeCheck dbConnectionNodeCheck; + + public SafeModeLivenessCheckerImpl(DbConnectionNodeCheck dbConnectionNodeCheck) { + this.dbConnectionNodeCheck = dbConnectionNodeCheck; + } + + public boolean liveness() { + return Health.Status.GREEN.equals(dbConnectionNodeCheck.check().getStatus()); + } +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafemodeSystemWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafemodeSystemWsModule.java index f6b4c329da9..7a5df9d2f33 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafemodeSystemWsModule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafemodeSystemWsModule.java @@ -30,8 +30,9 @@ public class SafemodeSystemWsModule extends Module { DbMigrationStatusAction.class, HealthActionSupport.class, SafeModeHealthAction.class, - SystemWs.class - - ); + SafeModeLivenessCheckerImpl.class, + LivenessActionSupport.class, + SafeModeLivenessAction.class, + SystemWs.class); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SystemWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SystemWsModule.java index c8e16bfc366..4b907ca10aa 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SystemWsModule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SystemWsModule.java @@ -30,6 +30,11 @@ public class SystemWsModule extends Module { DbMigrationStatusAction.class, HealthActionSupport.class, HealthAction.class, + + LivenessCheckerImpl.class, + LivenessActionSupport.class, + LivenessAction.class, + InfoAction.class, LogsAction.class, MigrateDbAction.class, diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessActionTest.java new file mode 100644 index 00000000000..fbbc1b98b91 --- /dev/null +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessActionTest.java @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.user.SystemPasscode; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.WsActionTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LivenessActionTest { + + @Rule + public UserSessionRule userSessionRule = UserSessionRule.standalone(); + + private final SystemPasscode systemPasscode = mock(SystemPasscode.class); + private final LivenessChecker livenessChecker = mock(LivenessChecker.class); + private final LivenessActionSupport livenessActionSupport = new LivenessActionSupport(livenessChecker); + private final WsActionTester underTest = new WsActionTester(new LivenessAction(livenessActionSupport, systemPasscode, userSessionRule)); + + @Test + public void verify_definition() { + WebService.Action definition = underTest.getDef(); + + assertThat(definition.key()).isEqualTo("liveness"); + assertThat(definition.isPost()).isFalse(); + assertThat(definition.description()).isNotEmpty(); + assertThat(definition.since()).isEqualTo("9.1"); + assertThat(definition.isInternal()).isTrue(); + assertThat(definition.responseExample()).isNull(); + assertThat(definition.params()).isEmpty(); + } + + @Test + public void fail_when_system_passcode_is_invalid() { + when(systemPasscode.isValid(any())).thenReturn(false); + + TestRequest request = underTest.newRequest(); + assertThatThrownBy(request::execute) + .isInstanceOf(ForbiddenException.class) + .hasMessage("Insufficient privileges"); + } + + @Test + public void fail_when_user_is_not_system_admin() { + when(systemPasscode.isValid(any())).thenReturn(false); + userSessionRule.logIn(); + + TestRequest request = underTest.newRequest(); + assertThatThrownBy(request::execute) + .isInstanceOf(ForbiddenException.class) + .hasMessage("Insufficient privileges"); + } + + @Test + public void liveness_check_failed_expect_500() { + when(systemPasscode.isValid(any())).thenReturn(true); + when(livenessChecker.liveness()).thenReturn(false); + + TestRequest request = underTest.newRequest(); + assertThatThrownBy(request::execute) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Liveness check failed"); + } + + @Test + public void liveness_check_success_expect_204() { + when(systemPasscode.isValid(any())).thenReturn(true); + when(livenessChecker.liveness()).thenReturn(true); + + assertThat(underTest.newRequest().execute().getStatus()).isEqualTo(204); + } + +} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessCheckerImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessCheckerImplTest.java new file mode 100644 index 00000000000..75e73cb9638 --- /dev/null +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/LivenessCheckerImplTest.java @@ -0,0 +1,92 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.sonar.server.health.CeStatusNodeCheck; +import org.sonar.server.health.DbConnectionNodeCheck; +import org.sonar.server.health.EsStatusNodeCheck; +import org.sonar.server.health.Health; +import org.sonar.server.health.WebServerStatusNodeCheck; +import org.sonar.server.platform.WebServer; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LivenessCheckerImplTest { + + public static final Health RED = Health.newHealthCheckBuilder().setStatus(Health.Status.RED).build(); + + private final WebServer webServer = mock(WebServer.class); + private final DbConnectionNodeCheck dbConnectionNodeCheck = mock(DbConnectionNodeCheck.class); + private final WebServerStatusNodeCheck webServerStatusNodeCheck = mock(WebServerStatusNodeCheck.class); + private final CeStatusNodeCheck ceStatusNodeCheck = mock(CeStatusNodeCheck.class); + private final EsStatusNodeCheck esStatusNodeCheck = mock(EsStatusNodeCheck.class); + + LivenessCheckerImpl underTest = new LivenessCheckerImpl(webServer, dbConnectionNodeCheck, webServerStatusNodeCheck, ceStatusNodeCheck, esStatusNodeCheck); + + @Test + public void fail_when_db_connexion_check_fail() { + when(dbConnectionNodeCheck.check()).thenReturn(RED); + + Assertions.assertThat(underTest.liveness()).isFalse(); + } + + @Test + public void fail_when_web_check_fail() { + when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN); + when(webServerStatusNodeCheck.check()).thenReturn(RED); + + Assertions.assertThat(underTest.liveness()).isFalse(); + } + + @Test + public void fail_when_ce_check_fail() { + when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN); + when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN); + when(ceStatusNodeCheck.check()).thenReturn(RED); + + Assertions.assertThat(underTest.liveness()).isFalse(); + } + + @Test + public void dont_fail_when_not_standalone_but_es_check_fail() { + when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN); + when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN); + when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN); + when(webServer.isStandalone()).thenReturn(false); + when(esStatusNodeCheck.check()).thenReturn(RED); + + Assertions.assertThat(underTest.liveness()).isTrue(); + } + + @Test + public void fail_when_not_dce_and_es_check_fail() { + when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN); + when(webServerStatusNodeCheck.check()).thenReturn(Health.GREEN); + when(ceStatusNodeCheck.check()).thenReturn(Health.GREEN); + when(webServer.isStandalone()).thenReturn(true); + when(esStatusNodeCheck.check()).thenReturn(RED); + + Assertions.assertThat(underTest.liveness()).isFalse(); + } + +} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessActionTest.java new file mode 100644 index 00000000000..c634451de25 --- /dev/null +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessActionTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.junit.Test; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.user.SystemPasscode; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.WsActionTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SafeModeLivenessActionTest { + + private final SystemPasscode systemPasscode = mock(SystemPasscode.class); + private final LivenessChecker livenessChecker = mock(LivenessChecker.class); + private final LivenessActionSupport livenessActionSupport = new LivenessActionSupport(livenessChecker); + private final WsActionTester underTest = new WsActionTester(new SafeModeLivenessAction(livenessActionSupport, systemPasscode)); + + @Test + public void verify_definition() { + WebService.Action definition = underTest.getDef(); + + assertThat(definition.key()).isEqualTo("liveness"); + assertThat(definition.isPost()).isFalse(); + assertThat(definition.description()).isNotEmpty(); + assertThat(definition.since()).isEqualTo("9.1"); + assertThat(definition.isInternal()).isTrue(); + assertThat(definition.responseExample()).isNull(); + assertThat(definition.params()).isEmpty(); + } + + @Test + public void fail_when_system_passcode_is_invalid() { + when(systemPasscode.isValid(any())).thenReturn(false); + + TestRequest request = underTest.newRequest(); + assertThatThrownBy(request::execute) + .isInstanceOf(ForbiddenException.class) + .hasMessage("Insufficient privileges"); + } + + @Test + public void liveness_check_failed_expect_500() { + when(systemPasscode.isValid(any())).thenReturn(true); + when(livenessChecker.liveness()).thenReturn(false); + + TestRequest request = underTest.newRequest(); + assertThatThrownBy(request::execute) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Liveness check failed"); + } + + @Test + public void liveness_check_success_expect_204() { + when(systemPasscode.isValid(any())).thenReturn(true); + when(livenessChecker.liveness()).thenReturn(true); + + assertThat(underTest.newRequest().execute().getStatus()).isEqualTo(204); + } + +} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImplTest.java new file mode 100644 index 00000000000..06cc8c7171e --- /dev/null +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafeModeLivenessCheckerImplTest.java @@ -0,0 +1,50 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.sonar.server.health.DbConnectionNodeCheck; +import org.sonar.server.health.Health; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SafeModeLivenessCheckerImplTest { + + public static final Health RED = Health.newHealthCheckBuilder().setStatus(Health.Status.RED).build(); + private final DbConnectionNodeCheck dbConnectionNodeCheck = mock(DbConnectionNodeCheck.class); + private final SafeModeLivenessCheckerImpl underTest = new SafeModeLivenessCheckerImpl(dbConnectionNodeCheck); + + @Test + public void fail_when_db_connexion_check_fail() { + when(dbConnectionNodeCheck.check()).thenReturn(RED); + + Assertions.assertThat(underTest.liveness()).isFalse(); + } + + @Test + public void succeed_when_db_connexion_check_success() { + when(dbConnectionNodeCheck.check()).thenReturn(Health.GREEN); + + Assertions.assertThat(underTest.liveness()).isTrue(); + } + +} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafemodeSystemWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafemodeSystemWsModuleTest.java index fbecd406af4..c0f5884f7d4 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafemodeSystemWsModuleTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SafemodeSystemWsModuleTest.java @@ -30,7 +30,7 @@ public class SafemodeSystemWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new SafemodeSystemWsModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 6); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 9); } } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SystemWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SystemWsModuleTest.java index 6452b7cb752..4914f0c1782 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SystemWsModuleTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/platform/ws/SystemWsModuleTest.java @@ -30,7 +30,7 @@ public class SystemWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new SystemWsModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 12); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 15); } -- 2.39.5