Commit 1d8d7ccc by Johannes Edmeier

Improve hover status info for down/offline apps

parent 14d8c01b
...@@ -4,10 +4,11 @@ ...@@ -4,10 +4,11 @@
<table class="table cable-condensed"> <table class="table cable-condensed">
<tr ng-repeat="detail in $ctrl.details | orderBy:'name'"> <tr ng-repeat="detail in $ctrl.details | orderBy:'name'">
<td style="text-transform: capitalize;" ng-bind="detail.name"></td> <td style="text-transform: capitalize;" ng-bind="detail.name"></td>
<td ng-bind="detail.status " class="status-{{detail.status}}"></td> <td ng-bind="detail.status" class="status-{{detail.status}}"></td>
</tr> </tr>
</table> </table>
<div style="max-width: 400px;" class="alert alert-error" ng-if="$ctrl.statusInfo.details.message"> <div style="max-width: 400px;" class="alert alert-error" ng-if="$ctrl.statusInfo.details.message || $ctrl.statusInfo.details.error">
<b style="word-break: break-all;" ng-if="$ctrl.statusInfo.details.status" ng-bind="$ctrl.statusInfo.details.status + ' ' + $ctrl.statusInfo.details.error"></b>
<b style="word-break: break-all;" ng-if="$ctrl.statusInfo.details.exception" ng-bind="$ctrl.statusInfo.details.exception + ':'"></b><br> <b style="word-break: break-all;" ng-if="$ctrl.statusInfo.details.exception" ng-bind="$ctrl.statusInfo.details.exception + ':'"></b><br>
<span ng-bind="$ctrl.statusInfo.details.message"></span> <span ng-bind="$ctrl.statusInfo.details.message"></span>
</div> </div>
......
...@@ -32,7 +32,7 @@ public class StatusInfo implements Serializable { ...@@ -32,7 +32,7 @@ public class StatusInfo implements Serializable {
private final String status; private final String status;
private final long timestamp; private final long timestamp;
private final Map<String, ? extends Serializable> details; private final Map<String, Serializable> details;
protected StatusInfo(String status, long timestamp, protected StatusInfo(String status, long timestamp,
Map<String, ? extends Serializable> details) { Map<String, ? extends Serializable> details) {
...@@ -87,7 +87,7 @@ public class StatusInfo implements Serializable { ...@@ -87,7 +87,7 @@ public class StatusInfo implements Serializable {
return timestamp; return timestamp;
} }
public Map<String, ? extends Serializable> getDetails() { public Map<String, Serializable> getDetails() {
return details; return details;
} }
......
...@@ -100,31 +100,39 @@ public class StatusUpdater implements ApplicationEventPublisherAware { ...@@ -100,31 +100,39 @@ public class StatusUpdater implements ApplicationEventPublisherAware {
protected StatusInfo queryStatus(Application application) { protected StatusInfo queryStatus(Application application) {
LOGGER.trace("Updating status for {}", application); LOGGER.trace("Updating status for {}", application);
try { try {
ResponseEntity<Map<String, Serializable>> response = applicationOps return convertStatusInfo(applicationOps.getHealth(application));
.getHealth(application);
if (response.hasBody() && response.getBody().get("status") instanceof String) {
return StatusInfo.valueOf((String) response.getBody().get("status"),
response.getBody());
} else if (response.getStatusCode().is2xxSuccessful()) {
return StatusInfo.ofUp();
} else {
return StatusInfo.ofDown();
}
} catch (Exception ex) { } catch (Exception ex) {
if ("OFFLINE".equals(application.getStatusInfo().getStatus())) { if ("OFFLINE".equals(application.getStatusInfo().getStatus())) {
LOGGER.debug("Couldn't retrieve status for {}", application, ex); LOGGER.debug("Couldn't retrieve status for {}", application, ex);
} else { } else {
LOGGER.info("Couldn't retrieve status for {}", application, ex); LOGGER.info("Couldn't retrieve status for {}", application, ex);
} }
return StatusInfo.ofOffline(toDetails(ex)); return convertStatusInfo(ex);
}
}
private StatusInfo convertStatusInfo(ResponseEntity<Map<String, Serializable>> response) {
if (response.hasBody() && response.getBody().get("status") instanceof String) {
return StatusInfo.valueOf((String) response.getBody().get("status"),
response.getBody());
}
if (response.getStatusCode().is2xxSuccessful()) {
return StatusInfo.ofUp();
}
Map<String, Serializable> details = new HashMap<>();
details.put("status", response.getStatusCodeValue());
details.put("error", response.getStatusCode().getReasonPhrase());
if (response.hasBody()) {
details.putAll(response.getBody());
} }
return StatusInfo.ofDown(details);
} }
protected Map<String, Serializable> toDetails(Exception ex) { private StatusInfo convertStatusInfo(Exception ex) {
Map<String, Serializable> details = new HashMap<>(); Map<String, Serializable> details = new HashMap<>();
details.put("message", ex.getMessage()); details.put("message", ex.getMessage());
details.put("exception", ex.getClass().getName()); details.put("exception", ex.getClass().getName());
return details; return StatusInfo.ofOffline(details);
} }
public void setStatusLifetime(long statusLifetime) { public void setStatusLifetime(long statusLifetime) {
......
...@@ -92,8 +92,7 @@ public class StatusUpdaterTest { ...@@ -92,8 +92,7 @@ public class StatusUpdaterTest {
} }
@Test @Test
public void test_update_noBody() { public void test_update_up_noBody() {
// HTTP 200 - UP
when(applicationOps.getHealth(any(Application.class))) when(applicationOps.getHealth(any(Application.class)))
.thenReturn(ResponseEntity.ok((Map<String, Serializable>) null)); .thenReturn(ResponseEntity.ok((Map<String, Serializable>) null));
...@@ -102,14 +101,36 @@ public class StatusUpdaterTest { ...@@ -102,14 +101,36 @@ public class StatusUpdaterTest {
assertThat(store.find("id").getStatusInfo().getStatus(), CoreMatchers.is("UP")); assertThat(store.find("id").getStatusInfo().getStatus(), CoreMatchers.is("UP"));
// HTTP != 200 - DOWN }
@Test
public void test_update_down() {
when(applicationOps.getHealth(any(Application.class)))
.thenReturn(
ResponseEntity.status(503).body(
Collections.<String, Serializable>singletonMap("foo", "bar")));
updater.updateStatus(
Application.create("foo").withId("id").withHealthUrl("health").build());
StatusInfo statusInfo = store.find("id").getStatusInfo();
assertThat(statusInfo.getStatus(), CoreMatchers.is("DOWN"));
assertThat(statusInfo.getDetails(), hasEntry("foo", (Serializable) "bar"));
}
@Test
public void test_update_down_noBody() {
when(applicationOps.getHealth(any(Application.class))) when(applicationOps.getHealth(any(Application.class)))
.thenReturn(ResponseEntity.status(503).body((Map<String, Serializable>) null)); .thenReturn(ResponseEntity.status(503).body((Map<String, Serializable>) null));
updater.updateStatus( updater.updateStatus(
Application.create("foo").withId("id").withHealthUrl("health").build()); Application.create("foo").withId("id").withHealthUrl("health").build());
assertThat(store.find("id").getStatusInfo().getStatus(), CoreMatchers.is("DOWN")); StatusInfo statusInfo = store.find("id").getStatusInfo();
assertThat(statusInfo.getStatus(), CoreMatchers.is("DOWN"));
assertThat(statusInfo.getDetails(), hasEntry("status", (Serializable) 503));
assertThat(statusInfo.getDetails(),
hasEntry("error", (Serializable) "Service Unavailable"));
} }
@Test @Test
...@@ -121,7 +142,11 @@ public class StatusUpdaterTest { ...@@ -121,7 +142,11 @@ public class StatusUpdaterTest {
.withStatusInfo(StatusInfo.ofUp()).build(); .withStatusInfo(StatusInfo.ofUp()).build();
updater.updateStatus(app); updater.updateStatus(app);
assertThat(store.find("id").getStatusInfo().getStatus(), CoreMatchers.is("OFFLINE")); StatusInfo statusInfo = store.find("id").getStatusInfo();
assertThat(statusInfo.getStatus(), CoreMatchers.is("OFFLINE"));
assertThat(statusInfo.getDetails(), hasEntry("message", (Serializable) "error"));
assertThat(statusInfo.getDetails(), hasEntry("exception",
(Serializable) "org.springframework.web.client.ResourceAccessException"));
} }
@Test @Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment