Skip to content

Commit

Permalink
docs: added spring boot hot reload project
Browse files Browse the repository at this point in the history
  • Loading branch information
thsig committed Mar 8, 2019
1 parent a94de26 commit 258ff87
Show file tree
Hide file tree
Showing 19 changed files with 373 additions and 5 deletions.
2 changes: 1 addition & 1 deletion docs/faqs.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ We currently have a rough version of a Docker Swarm plug-in for Garden, but don'

The Garden orchestrator itself doesn't care where your services are built, tested and deployed. However, the current selection of plug-ins does support local development better than remote development. For Kubernetes development in particular, it is currently much easier to set up the `local-kubernetes` plugin, and feedback loops are generally faster than with the more generic `kubernetes` plugin (see [this example](https://github.com/garden-io/garden/tree/master/examples/remote-k8s) for how to configure remote clusters).

However, we are working to bridge that gap, since we strongly believe that remote building, testing and deployment is the way of the future. You can already use our [hot reloading](./hot-reload.md) feature with remote clusters, for example.
However, we are working to bridge that gap, since we strongly believe that remote building, testing and deployment is the way of the future. You can already use our [hot reloading](./using-garden/hot-reload.md) feature with remote clusters, for example.

### Garden vs. Skaffold?

Expand Down
17 changes: 17 additions & 0 deletions examples/spring-boot-hot-reload/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>spring-boot-sample-devtools</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
45 changes: 45 additions & 0 deletions examples/spring-boot-hot-reload/README.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions examples/spring-boot-hot-reload/devtools/.gardenignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/main/java/**/*
1 change: 1 addition & 0 deletions examples/spring-boot-hot-reload/devtools/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
16 changes: 16 additions & 0 deletions examples/spring-boot-hot-reload/devtools/Dockerfile
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions examples/spring-boot-hot-reload/devtools/garden.yml
Original file line number Diff line number Diff line change
@@ -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
49 changes: 49 additions & 0 deletions examples/spring-boot-hot-reload/devtools/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<artifactId>spring-boot-sample-devtools</artifactId>
<name>Spring Boot Developer Tools Sample</name>
<description>Spring Boot Developer Tools Sample</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency> <!-- Added for Garden example: For Kubernetes health checks -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -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() {
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public file
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
h1 {
color: green;
}

.content {
font-family: sans-serif;
border-top: 3px solid red;
padding-top: 30px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
</head>
<body>
<h1 th:text="${message}">Header</h1>
<div class="content">
<h2 th:text="${sessionVar}">Session Var</h2>
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.
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> 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<String> entity = this.restTemplate
.getForEntity("/application.properties", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}

}
6 changes: 6 additions & 0 deletions examples/spring-boot-hot-reload/garden.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project:
name: spring-boot-hot-reload
environments:
- name: local
providers:
- name: local-kubernetes
Loading

0 comments on commit 258ff87

Please sign in to comment.