diff --git a/examples/spring-boot-hot-reload/.project b/examples/spring-boot-hot-reload/.project new file mode 100644 index 0000000000..5fadea0a71 --- /dev/null +++ b/examples/spring-boot-hot-reload/.project @@ -0,0 +1,17 @@ + + + spring-boot-sample-devtools + + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + + diff --git a/examples/spring-boot-hot-reload/.settings/org.eclipse.m2e.core.prefs b/examples/spring-boot-hot-reload/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000000..f897a7f1cb --- /dev/null +++ b/examples/spring-boot-hot-reload/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/examples/spring-boot-hot-reload/README.md b/examples/spring-boot-hot-reload/README.md new file mode 100644 index 0000000000..22d994fd7c --- /dev/null +++ b/examples/spring-boot-hot-reload/README.md @@ -0,0 +1,45 @@ +# Spring Boot example project + +This example demonstrates Spring Boot running inside a Docker container (managed by a local Kubernetes cluster) with Spring Boot's dev tools enabled. We'll walk through live-restarting a Spring Boot service on compilation via Garden's hot reload functionality. + +## Prerequisites + +You'll need to have Java and [Maven](https://maven.apache.org/install.html) installed (follow the appropriate installation instructions for your platform). + +## Overview + +This project consists of a single module (in the `devtools` directory), which is a minimally modified version of the [Spring Boot devtools sample project found here](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-devtools). + +We've changed the parent pom from `spring-boot-samples` to `spring-boot-starter-parent`, and added a dependency on `spring-boot-starter-actuator` (to enable the health check endpoint for k8s readiness and liveness probes). We've also removed the artificial 5 second delay that was added by the `slowRestart` method in `MyController`. + +## Usage + +First, run `mvn compile` in the `devtools` directory. This will download the service's dependencies and compile it. Afterwards, you should see the `target` directory appear (under `devtools`). + +Subsequent calls to `mvn compile` will be much faster, since the dependencies will have been cached by Maven. + +You can now deploy the service with hot reloading enabled e.g. by running + +``` +garden deploy --hot=devtools +``` + +This should produce output that's something like this: + +``` +Deploy 🚀 + +✔ devtools → Building devtools:v-c6b9091207... → Done ( +took 0.4 sec) +✔ devtools → Deploying version v-c6b9091207... → Done ( +took 67 sec) + → Ingress: http://spring-boot-hot-reload.local.app.garden +🌻 Garden dashboard and API server running on http://localhost:50934 + +🕑 Waiting for code changes +``` +Open the ingress URL (http://spring-boot-hot-reload.local.app.garden above) in a web browser. + +Now, change the value of the `MESSAGE` constant in `devtools/src/main/java/sample/devtools/Message.java` to something else than `"Message"`, and run `mvn compile` (again in the `devtools` directory). + +This should trigger a hot reload. Refresh the the browser tab opened earlier, and you should see the change you made reflected in the header text. \ No newline at end of file diff --git a/examples/spring-boot-hot-reload/devtools/.gardenignore b/examples/spring-boot-hot-reload/devtools/.gardenignore new file mode 100644 index 0000000000..9603395f64 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/.gardenignore @@ -0,0 +1 @@ +src/main/java/**/* diff --git a/examples/spring-boot-hot-reload/devtools/.gitignore b/examples/spring-boot-hot-reload/devtools/.gitignore new file mode 100644 index 0000000000..eb5a316cbd --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/.gitignore @@ -0,0 +1 @@ +target diff --git a/examples/spring-boot-hot-reload/devtools/Dockerfile b/examples/spring-boot-hot-reload/devtools/Dockerfile new file mode 100644 index 0000000000..2b31d88a17 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/Dockerfile @@ -0,0 +1,16 @@ +FROM maven:3.6.0-jdk-11-slim +WORKDIR /app + +COPY pom.xml /app/pom.xml +RUN mvn dependency:resolve + +RUN mkdir -p /app/target +COPY src /app/src +# RUN mvn install + +ENV JVM_OPTS "-XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1" +RUN mvn install +# RUN mvn compile +CMD ["mvn", "spring-boot:run"] + +EXPOSE 8080 diff --git a/examples/spring-boot-hot-reload/devtools/garden.yml b/examples/spring-boot-hot-reload/devtools/garden.yml new file mode 100644 index 0000000000..55eae5c794 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/garden.yml @@ -0,0 +1,21 @@ +module: + description: Spring Boot devtools-sample module + type: container + name: devtools + hotReload: + sync: + - target: /app/target + source: target + services: + - name: devtools + ports: + - name: http + containerPort: 8080 + servicePort: 80 + healthCheck: + httpGet: + path: /actuator/health + port: http + ingresses: + - path: / + port: http diff --git a/examples/spring-boot-hot-reload/devtools/pom.xml b/examples/spring-boot-hot-reload/devtools/pom.xml new file mode 100755 index 0000000000..be536326d7 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.3.RELEASE + + spring-boot-sample-devtools + Spring Boot Developer Tools Sample + Spring Boot Developer Tools Sample + + ${basedir}/../.. + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-devtools + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/Message.java b/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/Message.java new file mode 100755 index 0000000000..24340365ed --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/Message.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2018 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 sample.devtools; + +public final class Message { + + /** + * Sample message. + */ + public static String MESSAGE = "Message"; + + private Message() { + } + +} diff --git a/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/MyController.java b/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/MyController.java new file mode 100755 index 0000000000..5f55913f17 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/MyController.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2016 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 sample.devtools; + +import java.util.Date; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpSession; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class MyController { + + @GetMapping("/") + public ModelAndView get(HttpSession session) { + Object sessionVar = session.getAttribute("var"); + if (sessionVar == null) { + sessionVar = new Date(); + session.setAttribute("var", sessionVar); + } + ModelMap model = new ModelMap("message", Message.MESSAGE) + .addAttribute("sessionVar", sessionVar); + return new ModelAndView("hello", model); + } + +} diff --git a/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/SampleDevToolsApplication.java b/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/SampleDevToolsApplication.java new file mode 100755 index 0000000000..e10f482e0d --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/main/java/sample/devtools/SampleDevToolsApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2016 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 sample.devtools; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleDevToolsApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleDevToolsApplication.class, args); + } + +} diff --git a/examples/spring-boot-hot-reload/devtools/src/main/resources/application.properties b/examples/spring-boot-hot-reload/devtools/src/main/resources/application.properties new file mode 100755 index 0000000000..c4d296d5b8 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/main/resources/application.properties @@ -0,0 +1,5 @@ +# Enable remote support, for local development you don't need this line +spring.devtools.remote.secret=secret +management.endpoint.health.enabled=true +management.endpoints.web.exposure.include=health +# management.endpoints.web.exposure.include=health,info \ No newline at end of file diff --git a/examples/spring-boot-hot-reload/devtools/src/main/resources/public/public.txt b/examples/spring-boot-hot-reload/devtools/src/main/resources/public/public.txt new file mode 100755 index 0000000000..e258b6c5c8 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/main/resources/public/public.txt @@ -0,0 +1 @@ +public file diff --git a/examples/spring-boot-hot-reload/devtools/src/main/resources/static/css/application.css b/examples/spring-boot-hot-reload/devtools/src/main/resources/static/css/application.css new file mode 100755 index 0000000000..1f83e00900 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/main/resources/static/css/application.css @@ -0,0 +1,9 @@ +h1 { + color: green; +} + +.content { + font-family: sans-serif; + border-top: 3px solid red; + padding-top: 30px; +} diff --git a/examples/spring-boot-hot-reload/devtools/src/main/resources/templates/hello.html b/examples/spring-boot-hot-reload/devtools/src/main/resources/templates/hello.html new file mode 100755 index 0000000000..bd6fb8a7f2 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/main/resources/templates/hello.html @@ -0,0 +1,23 @@ + + + + Hello + + +

Header

+
+

Session Var

+ Lorem ipsum dolor sit amet, + consectetur adipiscing elit. Cras ut fringilla augue, quis dictum + turpis. Sed tincidunt mi vel euismod viverra. Nulla facilisi. + Suspendisse mauris dolor, egestas ac leo at, porttitor ullamcorper + leo. Suspendisse consequat, justo ut rutrum interdum, nibh massa + semper dui, id sagittis tellus lectus at nibh. Etiam at scelerisque + nisi. Quisque vel eros tempor, fermentum sapien sed, gravida neque. + Fusce interdum sed dolor a semper. Morbi porta mauris a velit laoreet + viverra. Praesent et tellus vehicula, sagittis mi quis, faucibus urna. + Ut diam tortor, vehicula eget aliquam eget, elementum a odio. Fusce at + nisl sapien. Suspendisse potenti. +
+ + diff --git a/examples/spring-boot-hot-reload/devtools/src/test/java/sample/devtools/SampleDevToolsApplicationIntegrationTests.java b/examples/spring-boot-hot-reload/devtools/src/test/java/sample/devtools/SampleDevToolsApplicationIntegrationTests.java new file mode 100755 index 0000000000..88ce1286b8 --- /dev/null +++ b/examples/spring-boot-hot-reload/devtools/src/test/java/sample/devtools/SampleDevToolsApplicationIntegrationTests.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2018 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 sample.devtools; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link SampleDevToolsApplication}. + * + * @author Andy Wilkinson + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class SampleDevToolsApplicationIntegrationTests { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void testStaticResource() { + ResponseEntity entity = this.restTemplate + .getForEntity("/css/application.css", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("color: green;"); + } + + @Test + public void testPublicResource() { + ResponseEntity entity = this.restTemplate.getForEntity("/public.txt", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("public file"); + } + + @Test + public void testClassResource() { + ResponseEntity entity = this.restTemplate + .getForEntity("/application.properties", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + +} diff --git a/examples/spring-boot-hot-reload/garden.yml b/examples/spring-boot-hot-reload/garden.yml new file mode 100644 index 0000000000..36d4332afb --- /dev/null +++ b/examples/spring-boot-hot-reload/garden.yml @@ -0,0 +1,6 @@ +project: + name: spring-boot-hot-reload + environments: + - name: local + providers: + - name: local-kubernetes