Skip to content

Commit

Permalink
separate health, management and service url
Browse files Browse the repository at this point in the history
  • Loading branch information
Johannes Stelzer authored and Johannes Stelzer committed Apr 26, 2015
1 parent 0c2cf04 commit 35e27a7
Show file tree
Hide file tree
Showing 25 changed files with 446 additions and 171 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = function ($scope, $location, $interval, $q, Application) {
var getHealth = function (app) {
return app.getHealth()
.success(function (response) {
app.status = response.status;
app.status = response.status || 'UP';
})
.error(function (response, httpStatus) {
if (httpStatus === 503) {
Expand Down
12 changes: 10 additions & 2 deletions spring-boot-admin-server-ui/app/views/apps.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="navbar">
<div class="navbar" style="margin-bottom: 0px;">
<div class="navbar-inner">
<span class="brand">{{ application.name }}</span>
<ul class="nav pull-right">
Expand All @@ -8,7 +8,15 @@
<li class="navbar-link" ui-sref-active="active" ><a ui-sref="apps.threads({id: application.id})">Threads</a></li>
<li class="navbar-link" ui-sref-active="active" ><a ui-sref="apps.trace({id: application.id})">Trace</a></li>
</ul>
<small class="navbar-text">{{ application.url }}</small>
</div>
</div>
<div class="navbar">
<div class="navbar-inner">
<ul class="nav" style="width: 100%;">
<li style="width: 33%; text-align: center;"><a href="{{ application.serviceUrl }}" title="Service URL"><i class="icon-home" ></i> {{ application.serviceUrl }}</a></li>
<li style="width: 33%; text-align: center;"><a href="{{ application.healthUrl }}" title="Health URL"><i class="icon-heart" ></i> {{ application.healthUrl }}</a></li>
<li style="width: 33%; text-align: center;"><a href="{{ application.managementUrl }}" title="Management URL"><i class="icon-wrench"></i> {{ application.managementUrl }}</a></li>
</ul>
</div>
</div>
<div ui-view></div>
6 changes: 3 additions & 3 deletions spring-boot-admin-server-ui/app/views/apps/details.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<div class="row">
<div class="span6">
<table class="table">
<thead><tr><th colspan="2">Application <small class="pull-right"><a href="{{ application.url }}/info">raw JSON</a></small></th></tr></thead>
<thead><tr><th colspan="2">Application <small class="pull-right"><a href="{{ application.managementUrl }}/info">raw JSON</a></small></th></tr></thead>
<tbody>
<tr ng-repeat="(key, value) in info" >
<td>{{ key }}</td><td>{{ value }}</td>
Expand All @@ -17,7 +17,7 @@
<div class="span6">
<table class="table">
<thead>
<tr><th colspan="2">Health Checks <small class="pull-right"><a href="{{ application.url }}/health">raw JSON</a></small></th></tr>
<tr><th colspan="2">Health Checks <small class="pull-right"><a href="{{ application.healthUrl }}">raw JSON</a></small></th></tr>
</thead>
<tbody>
<tr><td ng-init="name= 'Application'" ng-include="'health.html'"></td></tr>
Expand Down Expand Up @@ -63,7 +63,7 @@

<div class="span6">
<table class="table">
<thead><tr><th colspan="2">JVM <small class="pull-right"><a href="{{ application.url }}/metrics">raw JSON</a></small></th></tr></thead>
<thead><tr><th colspan="2">JVM <small class="pull-right"><a href="{{ application.managementUrl }}/metrics">raw JSON</a></small></th></tr></thead>
<tbody>
<tr ng-if="metrics['systemload.average'] != null && metrics['systemload.average'] >= 0.0">
<td>Systemload</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<col style="width:auto">
<thead>
<tr>
<th>Classpath <small class="pull-right"><a href="{{ application.url }}/env">raw JSON</a></small></th>
<th>Classpath <small class="pull-right"><a href="{{ application.managementUrl }}/env">raw JSON</a></small></th>
</tr>
</thead>
<tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<thead>
<tr>
<th>Property</th>
<th>Value <small class="pull-right"><a href="{{ application.url }}/env">raw JSON</a></small></th>
<th>Value <small class="pull-right"><a href="{{ application.managementUrl }}/env">raw JSON</a></small></th>
</tr>
</thead>
<tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<thead>
<tr>
<th>Property</th>
<th>Value <small class="pull-right"><a href="{{ application.url }}/env">raw JSON</a></small></th>
<th>Value <small class="pull-right"><a href="{{ application.managementUrl }}/env">raw JSON</a></small></th>
</tr>
</thead>
<tbody>
Expand Down
4 changes: 2 additions & 2 deletions spring-boot-admin-server-ui/app/views/overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ <h2 >Spring-Boot applications<br>
</thead>
<tbody>
<tr ng-repeat="application in applications|orderBy:order.column:order.descending|orderBy:'status':false track by application.id">
<td>{{ application.name }}<br/><span class="muted">{{ application.url }}</span></td>
<td>{{ application.name }}<br/><span class="muted">{{ application.serviceUrl || application.managementUrl || application.healthUrl }}</span></td>
<td>{{ application.version }}</td>
<td><span ng-repeat="(name, value) in application.info track by name">{{name}}: {{value}}<br></span></td>
<td><span class="status-{{application.status}}">{{ application.status }}</span>
<span ng-show="application.refreshing" class="refresh"></span></td>
<td style="text-align: right;">
<div class="btn-group" ng-hide="application.status == null || application.status == 'OFFLINE'">
<div class="btn-group" ng-hide="application.managementUrl == null || application.status == null || application.status == 'OFFLINE'">
<a ng-disabled="!application.providesLogfile" target="_self" class="btn btn-success" ng-href="{{application.providesLogfile ? application.url + '/logfile' :''}}"><i class="icon-file icon-white"></i>Log</a>
<a ui-sref="apps.details.metrics({id: application.id})" class="btn btn-success">Details</a>
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public class ApplicationDiscoveryListener implements ApplicationListener<Applica

private String managementContextPath = "";

private String serviceContextPath = "";

private String healthEndpoint = "health";


public ApplicationDiscoveryListener(DiscoveryClient discoveryClient, ApplicationRegistry registry) {
this.discoveryClient = discoveryClient;
Expand Down Expand Up @@ -77,13 +81,28 @@ public void discover() {
}

private Application convert(ServiceInstance instance) {
String url = instance.getUri()
.resolve(managementContextPath.startsWith("/") ? managementContextPath : "/" + managementContextPath)
String managementUrl = instance.getUri()
.resolve(managementContextPath)
.toString();
return new Application(url, instance.getServiceId());
String serviceUrl = instance.getUri()
.resolve(serviceContextPath)
.toString();
String healthUrl = managementUrl + "/" + healthEndpoint;

return new Application(healthUrl, managementUrl, serviceUrl, instance.getServiceId());
}

public void setManagementContextPath(String managementContextPath) {
this.managementContextPath = managementContextPath;
this.managementContextPath = managementContextPath.startsWith("/") ? managementContextPath
: "/" + managementContextPath;
}

public void setServiceContextPath(String serviceContextPath) {
this.serviceContextPath = serviceContextPath.startsWith("/") ? serviceContextPath
: "/" + serviceContextPath;
}

public void setHealthEndpoint(String healthEndpoint) {
this.healthEndpoint = healthEndpoint;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import de.codecentric.boot.admin.event.ClientApplicationRegisteredEvent;
import de.codecentric.boot.admin.event.ClientApplicationUnregisteredEvent;
Expand Down Expand Up @@ -53,21 +55,30 @@ public ApplicationRegistry(ApplicationStore store, ApplicationIdGenerator genera
* @return the registered application.
*/
public Application register(Application app) {
Validate.notNull(app, "Application must not be null");
Validate.notNull(app.getUrl(), "URL must not be null");
Validate.isTrue(checkUrl(app.getUrl()), "URL is not valid");
Assert.notNull(app, "Application must not be null");
Assert.hasText(app.getName(), "Name must not be null");
Assert.hasText(app.getHealthUrl(), "Health-URL must not be null");
Assert.isTrue(checkUrl(app.getHealthUrl()), "Health-URL is not valid");
Assert.isTrue(
StringUtils.isEmpty(app.getManagementUrl())
|| checkUrl(app.getManagementUrl()), "URL is not valid");
Assert.isTrue(
StringUtils.isEmpty(app.getServiceUrl()) || checkUrl(app.getServiceUrl()),
"URL is not valid");

String applicationId = generator.generateId(app);
Validate.notNull(applicationId, "ID must not be null");

Application newApp = new Application(app.getUrl(), app.getName(), applicationId);
Application newApp = new Application(app.getHealthUrl(), app.getManagementUrl(),
app.getServiceUrl(), app.getName(), applicationId);
Application oldApp = store.save(newApp);

if (oldApp == null) {
LOGGER.info("New Application {} registered ", newApp);
context.publishEvent(new ClientApplicationRegisteredEvent(this, newApp));
} else {
if ((app.getUrl().equals(oldApp.getUrl()) && app.getName().equals(oldApp.getName()))) {
if ((newApp.getId().equals(oldApp.getId()) && app.getName().equals(
oldApp.getName()))) {
LOGGER.debug("Application {} refreshed", newApp);
} else {
LOGGER.warn("Application {} replaced by Application {}", newApp, oldApp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public class HashingApplicationUrlIdGenerator implements ApplicationIdGenerator
public String generateId(Application a) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] bytes = digest.digest(a.getUrl().getBytes(StandardCharsets.UTF_8));
byte[] bytes = digest.digest(a.getHealthUrl()
.getBytes(StandardCharsets.UTF_8));
return new String(encodeHex(bytes, 0, 8));
}
catch (NoSuchAlgorithmException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.util.StringUtils;

import de.codecentric.boot.admin.model.Application;
import de.codecentric.boot.admin.registry.ApplicationRegistry;
Expand All @@ -46,12 +47,22 @@ protected LinkedHashMap<String, ZuulRoute> locateRoutes() {

if (registry != null) {
for (Application application : registry.getApplications()) {
String key = prefix + "/" + application.getId() + "/*/**";
locateRoutes.put(key, new ZuulRoute(key, application.getUrl()));
addRoute(locateRoutes, prefix + "/" + application.getId() + "/health/**",
application.getHealthUrl());

if (!StringUtils.isEmpty(application.getManagementUrl())) {
addRoute(locateRoutes, prefix + "/" + application.getId() + "/*/**",
application.getManagementUrl());
}
}
}

return locateRoutes;
}

private void addRoute(LinkedHashMap<String, ZuulRoute> locateRoutes, String path,
String url) {
locateRoutes.put(path, new ZuulRoute(path, url));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ public static class TestAdminApplication {
public void setup() throws InterruptedException {
System.setProperty("hazelcast.wait.seconds.before.join", "0");
instance1 = (EmbeddedWebApplicationContext) SpringApplication.run(TestAdminApplication.class, new String[] {
"--server.port=0", "--spring.jmx.enabled=false", "--spring.boot.admin.hazelcast.enabled=true" });
"--server.port=0", "--spring.jmx.enabled=false", "--spring.boot.admin.hazelcast.enabled=true" });
instance2 = (EmbeddedWebApplicationContext) SpringApplication.run(TestAdminApplication.class, new String[] {
"--server.port=0", "--spring.jmx.enabled=false", "--spring.boot.admin.hazelcast.enabled=true" });
"--server.port=0", "--spring.jmx.enabled=false", "--spring.boot.admin.hazelcast.enabled=true" });
}

@After
Expand All @@ -76,9 +76,12 @@ public void shutdown() {

@Test
public void test() {
Application app = new Application("http://127.0.0.1", "Hazelcast Test");
Application app2 = new Application("http://127.0.0.1:2", "Hazelcast Test");
Application app3 = new Application("http://127.0.0.1:3", "Do not find");
Application app = new Application("http://127.0.0.1/health", "", "",
"Hazelcast Test");
Application app2 = new Application("http://127.0.0.1:2/health", "", "",
"Hazelcast Test");
Application app3 = new Application("http://127.0.0.1:3/health", "", "",
"Do not find");

// publish app on instance1
ResponseEntity<Application> postResponse = registerApp(app, instance1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestAdminApplication.class)
@WebIntegrationTest({ "server.port=0" })
@WebIntegrationTest({ "server.port=0", "spring.cloud.config.enabled=false" })
public class AdminApplicationTest {

@Value("${local.server.port}")
Expand All @@ -53,24 +53,30 @@ public class AdminApplicationTest {
@Test
public void testGetApplications() {
@SuppressWarnings("rawtypes")
ResponseEntity<List> entity = new TestRestTemplate().getForEntity("http://localhost:" + port
+ "/api/applications", List.class);
ResponseEntity<List> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + port + "/api/applications", List.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}

@Test
public void testReverseProxy() {
String apiBaseUrl = "http://localhost:" + port + "/api/applications";

ResponseEntity<Application> entity = new TestRestTemplate().postForEntity(apiBaseUrl, new Application(
"http://localhost:" + port, "TestApp"), Application.class);
ResponseEntity<Application> entity = new TestRestTemplate().postForEntity(
apiBaseUrl, new Application("http://localhost:" + port + "/health",
"http://localhost:" + port, "", "TestApp"), Application.class);

@SuppressWarnings("rawtypes")
ResponseEntity<Map> health = new TestRestTemplate().getForEntity(apiBaseUrl + "/" + entity.getBody().getId()
+ "/info", Map.class);
ResponseEntity<Map> info = new TestRestTemplate().getForEntity(apiBaseUrl + "/"
+ entity.getBody().getId() + "/info", Map.class);
assertEquals(HttpStatus.OK, info.getStatusCode());

@SuppressWarnings("rawtypes")
ResponseEntity<Map> health = new TestRestTemplate().getForEntity(apiBaseUrl + "/"
+ entity.getBody().getId() + "/health", Map.class);
assertEquals(HttpStatus.OK, health.getStatusCode());
}

}

@Configuration
@EnableAutoConfiguration
Expand Down
Loading

0 comments on commit 35e27a7

Please sign in to comment.