diff --git a/.travis.yml b/.travis.yml index 63f8e2a7..a2e3df25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ install: - mvn --settings .settings.xml install -P docs -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true - ./docs/src/main/asciidoc/ghpages.sh script: -- ./consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul & +- ./consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -ui-dir ./src/test/resources/consul_ui & - '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || mvn --settings .settings.xml deploy -nsu -Dmaven.test.redirectTestOutputToFile=true' - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] || mvn --settings .settings.xml install -nsu -Dmaven.test.redirectTestOutputToFile=true' env: diff --git a/docs/src/main/asciidoc/spring-cloud-consul.adoc b/docs/src/main/asciidoc/spring-cloud-consul.adoc index c878f2c2..05cc08b1 100644 --- a/docs/src/main/asciidoc/spring-cloud-consul.adoc +++ b/docs/src/main/asciidoc/spring-cloud-consul.adoc @@ -161,3 +161,27 @@ spring: == Spring Cloud Bus with Consul TODO: document Spring Cloud Consul Bus + + +[[spring-cloud-consul-ui]] +== Proxy Consul UI through Spring Cloud Application + +To enable the Consul Web UI please read https://www.consul.io/intro/getting-started/ui.html[the documentation] under the "Self-hosted Dashboard" section. + +After you have enabled the Consul Web UI in the Agent, place the `@EnableConsulUi` annotation on a `@Configuration` class. `@EnableConsulUi` enables a zuul proxy configured to proxy to the Consul UI running on the Consul Agent. The UI will be available by default at `/consul/ui/`. To change the prefix the UI will be available under, set the `spring.cloud.consul.ui.path` property. + +.application.yml +---- +spring: + cloud: + consul: + ui: + path: /admin/** +---- + +This will make the Web UI available under `/admin/ui/`. + +By exposing the Consul Web UI via a Spring Boot application, you can secure access to it via the same Spring Security tools that you use to secure the rest of the application. + +[CAUTION] +The Consul Admin UI expects the Consul HTTP API to be available at `/v1`. If the `server.contextPath` is not `/` or your application has a route at `/v1`, then the Consul Web UI will fail to proxy. diff --git a/spring-cloud-consul-ui/pom.xml b/spring-cloud-consul-ui/pom.xml index 1a2a0114..b91e8b2c 100644 --- a/spring-cloud-consul-ui/pom.xml +++ b/spring-cloud-consul-ui/pom.xml @@ -40,6 +40,11 @@ spring-boot-starter-test test + + com.ecwid.consul + consul-api + test + diff --git a/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiConfiguration.java b/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiConfiguration.java index df0a6787..4b631e41 100644 --- a/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiConfiguration.java +++ b/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiConfiguration.java @@ -24,10 +24,12 @@ import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * @author Spencer Gibb */ +@Configuration @EnableZuulProxy public class ConsulUiConfiguration { @@ -44,15 +46,20 @@ public class ConsulUiConfiguration { public void init() { String url = String.format("http://%s:%s", consulProperties.getHost(), consulProperties.getPort()); - ZuulProperties.ZuulRoute route = new ZuulProperties.ZuulRoute("consulUi", + + ZuulProperties.ZuulRoute route = new ZuulProperties.ZuulRoute("consulApi", "/v1/**", null, url, false, false); + zuulProperties.getRoutes().put("consulApi", route); + + route = new ZuulProperties.ZuulRoute("consulUi", + "/consul/**", null, url, true, false); zuulProperties.getRoutes().put("consulUi", route); zuulHandlerMapping.registerHandlers(); } @Bean - public ConsulUiController consulUiController() { - return new ConsulUiController(); + public ConsulUiProperties consulUiProperties() { + return new ConsulUiProperties(); } } diff --git a/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiController.java b/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiProperties.java similarity index 64% rename from spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiController.java rename to spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiProperties.java index de559934..82cdbce7 100644 --- a/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiController.java +++ b/spring-cloud-consul-ui/src/main/java/org/springframework/cloud/ui/ConsulUiProperties.java @@ -16,19 +16,15 @@ package org.springframework.cloud.ui; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import lombok.Data; + +import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Spencer Gibb */ -@Controller -@RequestMapping("${spring.cloud.consul.ui.path:/ui}") -public class ConsulUiController { - - @RequestMapping(method = RequestMethod.GET) - public String index() { - return "forward:index.html"; - } +@ConfigurationProperties("spring.cloud.consul.ui") +@Data +public class ConsulUiProperties { + private String path = "/consul/**"; } diff --git a/spring-cloud-consul-ui/src/test/java/org/springframework/cloud/ui/EnableConsulUiTests.java b/spring-cloud-consul-ui/src/test/java/org/springframework/cloud/ui/EnableConsulUiTests.java new file mode 100644 index 00000000..14b663c1 --- /dev/null +++ b/spring-cloud-consul-ui/src/test/java/org/springframework/cloud/ui/EnableConsulUiTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.ui; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.TestRestTemplate; +import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.cloud.consul.ConsulAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Spencer Gibb + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = TestConfig.class) +@WebIntegrationTest(value = "spring.application.name=myTestService", randomPort = true) +public class EnableConsulUiTests { + + @Value("${local.server.port}") + private int port; + + @Test + public void consulWebUiWorks() { + ResponseEntity response = new TestRestTemplate().getForEntity("http://localhost:" + port + "/consul/ui/", String.class); + assertEquals("Wrong response code", HttpStatus.OK, response.getStatusCode()); + String body = response.getBody(); + assertNotNull("Null body", body); + assertTrue("Missing body text", body.toLowerCase().contains("consul") && body.toLowerCase().contains("services")); + } + +} + +@Configuration +@EnableAutoConfiguration +@Import({ ConsulAutoConfiguration.class }) +@EnableConsulUi +class TestConfig { + +} \ No newline at end of file diff --git a/src/main/bash/local_run_consul.sh b/src/main/bash/local_run_consul.sh index af5b5936..876422f3 100755 --- a/src/main/bash/local_run_consul.sh +++ b/src/main/bash/local_run_consul.sh @@ -1,2 +1,2 @@ #!/bin/bash -consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -ui-dir `dirname $0`/../../../spring-cloud-consul-ui/src/main/resources/public +consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -ui-dir `dirname $0`/../../../src/test/resources/consul_ui diff --git a/spring-cloud-consul-ui/src/main/resources/public/index.html b/src/test/resources/consul_ui/index.html similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/index.html rename to src/test/resources/consul_ui/index.html diff --git a/spring-cloud-consul-ui/src/main/resources/public/static/application.min.js b/src/test/resources/consul_ui/static/application.min.js similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/static/application.min.js rename to src/test/resources/consul_ui/static/application.min.js diff --git a/spring-cloud-consul-ui/src/main/resources/public/static/base.css b/src/test/resources/consul_ui/static/base.css similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/static/base.css rename to src/test/resources/consul_ui/static/base.css diff --git a/spring-cloud-consul-ui/src/main/resources/public/static/base.css.map b/src/test/resources/consul_ui/static/base.css.map similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/static/base.css.map rename to src/test/resources/consul_ui/static/base.css.map diff --git a/spring-cloud-consul-ui/src/main/resources/public/static/bootstrap.min.css b/src/test/resources/consul_ui/static/bootstrap.min.css similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/static/bootstrap.min.css rename to src/test/resources/consul_ui/static/bootstrap.min.css diff --git a/spring-cloud-consul-ui/src/main/resources/public/static/consul-logo.png b/src/test/resources/consul_ui/static/consul-logo.png similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/static/consul-logo.png rename to src/test/resources/consul_ui/static/consul-logo.png diff --git a/spring-cloud-consul-ui/src/main/resources/public/static/favicon.png b/src/test/resources/consul_ui/static/favicon.png similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/static/favicon.png rename to src/test/resources/consul_ui/static/favicon.png diff --git a/spring-cloud-consul-ui/src/main/resources/public/static/loading-cylon-purple.svg b/src/test/resources/consul_ui/static/loading-cylon-purple.svg similarity index 100% rename from spring-cloud-consul-ui/src/main/resources/public/static/loading-cylon-purple.svg rename to src/test/resources/consul_ui/static/loading-cylon-purple.svg