Commit a4b64339 authored by Nicola Bertazzo's avatar Nicola Bertazzo

Merge branch 'release/1.0'

parents 41270ba6 a4346c3b
Pipeline #30 passed with stage
in 3 minutes and 19 seconds
.git
.gitignore
.idea
src
docker-compose.yml
Jenkinsfile
pom.xml
*.md
tmp
host-m2-repo
.m2
.DS_Store
packer_cache
output-*
*.box
*.vmdk
*.ovf
*.ovf
*.zip
*gz
*.iso
*.rendered
.vagrant
.idea
*.iml
tmp
target
builds
host-m2-repo
.m2
# This file is a template, and might need editing before it works on your project.
# this file is a template, and might need editing before it works on your project.
---
# Build JAVA applications using Apache Maven (http://maven.apache.org)
# For docker image tags see https://hub.docker.com/_/maven/
......@@ -29,65 +29,33 @@ cache:
paths:
- /root/.m2/repository/
# This will only validate and compile stuff and run e.g. maven-enforcer-plugin.
# Because some enforcer rules might check dependency convergence and class duplications
# we use `test-compile` here instead of `validate`, so the correct classpath is picked up.
.validate: &validate
stage: build
script:
- 'mvn $MAVEN_CLI_OPTS test-compile'
# For merge requests do not `deploy` but only run `verify`.
# See https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
.verify: &verify
stage: test
script:
- 'mvn $MAVEN_CLI_OPTS verify site site:stage'
except:
- master
# Validate merge requests using JDK8
validate:jdk8:
<<: *validate
build:
script: "mvn clean compile"
image: maven:3.3.9-jdk-8
tags:
- docker
# Verify merge requests using JDK8
verify:jdk8:
<<: *verify
test:
script: "mvn test"
image: maven:3.3.9-jdk-8
tags:
- docker
# For `master` branch run `mvn deploy` automatically.
# Here you need to decide whether you want to use JDK7 or 8.
# To get this working you need to define a volume while configuring your gitlab-ci-multi-runner.
# Mount your `settings.xml` as `/root/.m2/settings.xml` which holds your secrets.
# See https://maven.apache.org/settings.html
deploy:jdk8:
# Use stage test here, so the pages job may later pickup the created site.
stage: test
script:
- 'mvn $MAVEN_CLI_OPTS deploy site site:stage'
only:
- master
# Archive up the built documentation site.
artifacts:
paths:
- target/staging
package:
script: "mvn package"
image: maven:3.3.9-jdk-8
tags:
- docker
int-test:
script: "mvn verify"
image: maven:3.3.9-jdk-8
tags:
- docker
pages:
image: busybox:latest
stage: deploy
docker-build:
script:
# Because Maven appends the artifactId automatically to the staging path if you did define a parent pom,
# you might need to use `mv target/staging/YOUR_ARTIFACT_ID public` instead.
- mv target/staging public
dependencies:
- deploy:jdk8
artifacts:
paths:
- public
only:
- master
- "mvn package"
image: maven:3.3.9-jdk-8
tags:
- docker
\ No newline at end of file
FROM openjdk:8-jre-alpine
MAINTAINER Damien DUPORTAL <dduportal@cloudbees.com>
COPY ./target/demoapp.jar /app/app.jar
COPY hello-world.yml /app/config.yml
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app/app.jar"]
CMD ["server","/app/config.yml"]
# DropWizard Demo Application
Welcome to the DropWizard Demo Application !
The goal of this repository is to show a tiny application example
to illustrate build pipelines and tools
## How to build it ?
### Requirements
To be built, this application needs:
* JDK 8 (OpenJDK is fine) as SDK
* **Maven 3.3** as build tool
### Get the code
Clone the repository wherever Maven + JDK is
available
* [Maven 3.3](https://maven.apache.org/) installation on "metal" with your JDK
* [A Maven + JDK Docker image](https://hub.docker.com/_/maven/)
* A [Virtual Machine with Maven and JDK](https://atlas.hashicorp.com/antapos/boxes/ubuntu-trusty64-jdk8-maven)
### Build it
From the root of the repository:
* Whole building process is as simple as
```bash
mvn clean install
```
* Just compiling classes:
```bash
mvn clean compile
```
* Running unit tests (implies compiling):
```bash
mvn test
```
* Running Integration tests (implies compiling but NO unit tests):
```bash
mvn verify
```
* Generating the standalone application:
```bash
mvn package
```
* "Installing" application in the local Maven repository:
```bash
mvn install
```
## How to run it ?
### Bare-metal
The [awesome DropWizard framework](http://www.dropwizard.io/1.0.0/docs/) makes the generated application a standalone one.
You can run it with just a Java JRE 8 installed:
```bash
java -jar ./target/demoapp.jar server ./hello-world.yml
```
The application will then be available on http://localhost:8080/ if everything went smoothly
### Docker run
They are a lot of reasons that would make you **NOT**
wanting to launch application in Bare Metal:
* Developer Syndrom (portability concerns): "It works on my machine" (**your** JDK and Maven installation)
* Security concerns: "What is this application doing ?"
* Ease of run: If any application is already running on
the `8080` port of your machine, you'll be required
to dig into the codebase + documentation to change
listening ports.
This is why you can run the application with your
own baked Docker image.
* A `Dockerfile` is provided at the root of the repository
* First step is to "build" the Docker image:
```bash
docker build -t dw-demo-app:latest ./
```
* Then run it in background,
letting Docker selecting a port:
```bash
CID=$(docker run -d -P dw-demo-app:latest)
```
* Fetch the allocated port by asking docker:
- Note that a simple ```docker ps``` could be sufficient, but not very machine-readable
```bash
docker port ${CID} 8080
```
Example: 0.0.0.0:**37567**
* Browse to the application homepage, using the fetched port: http://{DOCKER SERVICE IP}:**37567**
- {DOCKER SERVICE IP} is the IP address of your Docker service:
* `localhost` on Docker4Mac or Docker4Windows
* The result of ```boot2docker ip``` using Docker toolbox
* Stop and clean the application:
```bash
docker stop ${CID} && docker rm -v ${CID}
```
template: Hello, %s!
defaultName: Stranger
server:
rootPath: /api/
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dduportal.jenkins</groupId>
<artifactId>demoapp</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>Demo DropWizard Application</name>
<properties>
<dropwizard.version>1.0.0</dropwizard.version>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-assets</artifactId>
<version>${dropwizard.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.dduportal.jenkins.demoapp.HelloWorldApplication</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<excludedGroups>com.dduportal.jenkins.demoapp.test.IntegrationTest</excludedGroups>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<includes>
<include>**/*.java</include>
</includes>
<groups>com.dduportal.jenkins.demoapp.test.IntegrationTest</groups>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package com.dduportal.jenkins.demoapp;
import com.dduportal.jenkins.demoapp.health.TemplateHealthCheck;
import com.dduportal.jenkins.demoapp.resources.HelloWorldResource;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {
public static void main(String[] args) throws Exception {
new HelloWorldApplication().run(args);
}
@Override
public String getName() {
return "hello-world";
}
@Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
// nothing to do yet
bootstrap.addBundle(new AssetsBundle("/assets", "/","index.html"));
}
@Override
public void run(HelloWorldConfiguration configuration,
Environment environment) {
final HelloWorldResource resource = new HelloWorldResource(
configuration.getTemplate(),
configuration.getDefaultName()
);
final TemplateHealthCheck healthCheck =
new TemplateHealthCheck(configuration.getTemplate());
environment.healthChecks().register("template", healthCheck);
environment.jersey().register(resource);
environment.jersey().setUrlPattern("/api/*");
}
}
package com.dduportal.jenkins.demoapp;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.Configuration;
import org.hibernate.validator.constraints.NotEmpty;
public class HelloWorldConfiguration extends Configuration {
@NotEmpty
private String template;
@NotEmpty
private String defaultName = "Stranger";
@JsonProperty
public String getTemplate() {
return template;
}
@JsonProperty
public void setTemplate(String template) {
this.template = template;
}
@JsonProperty
public String getDefaultName() {
return defaultName;
}
@JsonProperty
public void setDefaultName(String name) {
this.defaultName = name;
}
}
package com.dduportal.jenkins.demoapp.api;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.Length;
public class Saying {
private long id;
@Length(max = 3)
private String content;
public Saying() {
// Jackson deserialization
}
public Saying(long id, String content) {
this.id = id;
this.content = content;
}
@JsonProperty
public long getId() {
return id;
}
@JsonProperty
public String getContent() {
return content;
}
}
package com.dduportal.jenkins.demoapp.health;
import com.codahale.metrics.health.HealthCheck;
public class TemplateHealthCheck extends HealthCheck {
private final String template;
public TemplateHealthCheck(String template) {
this.template = template;
}
@Override
protected Result check() throws Exception {
final String saying = String.format(template, "TEST");
if (!saying.contains("TEST")) {
return Result.unhealthy("template doesn't include a name");
}
return Result.healthy();
}
}
package com.dduportal.jenkins.demoapp.resources;
import com.dduportal.jenkins.demoapp.api.Saying;
import com.google.common.base.Optional;
import com.codahale.metrics.annotation.Timed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.concurrent.atomic.AtomicLong;
@Path("/hello-world")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {
private final String template;
private final String defaultName;
private final AtomicLong counter;
public HelloWorldResource(String template, String defaultName) {
this.template = template;
this.defaultName = defaultName;
this.counter = new AtomicLong();
}
@GET
@Timed
public Saying sayHello(@QueryParam("name") Optional<String> name) {
final String value = String.format(template, name.or(defaultName));
return new Saying(counter.incrementAndGet(), value);
}
}
This diff is collapsed.
function Hello($scope, $http) {
$http.get('/api/hello-world').
success(function(data) {
$scope.greeting = data;
});
}
<!doctype html>
<html ng-app>
<head>
<title>Hello Butler</title>
<script src="angular.min.js"></script>
<script src="hello.js"></script>
</head>
<body>
<div ng-controller="Hello">
<p>The ID is: {{greeting.id}}</p>
<p>The content is: {{greeting.content}}</p>
</div>
<img src="jenkins.png" alt="Jenkins" />
</body>
</html>
package com.dduportal.jenkins.demoapp.test;
import com.dduportal.jenkins.demoapp.api.Saying;
import com.dduportal.jenkins.demoapp.resources.HelloWorldResource;
import com.fasterxml.jackson.databind.ObjectReader;
import io.dropwizard.testing.junit.ResourceTestRule;
import org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThat;
public class HelloWorldEndpointTest {
@Rule
public final ResourceTestRule resources = ResourceTestRule.builder()
.addResource(new HelloWorldResource("Hello, %s!", "Stranger"))
.build();
@Test
public void helloWorldDropwizard() throws IOException {
// Hit the endpoint and get the raw json string
String resp = resources.client().target("/hello-world")
.queryParam("name", "dropwizard")
.request().get(String.class);
// The expected json that will be returned
String json = "{ \"id\": 1, \"content\": \"Hello, dropwizard!\" }";
// The object responsible for translating json to our class
ObjectReader reader = resources.getObjectMapper().reader(Saying.class);
// Deserialize our actual and expected responses
Saying actual = reader.readValue(resp);
Saying expected = reader.readValue(json);
assertThat(actual.getId())
.isEqualTo(expected.getId())
.isEqualTo(1);
assertThat(actual.getContent())
.isEqualTo(expected.getContent())
.isEqualTo("Hello, dropwizard!");
}
@Test
public void helloWorldAbsentName() {
// A more terse way to test just an endpoint
Saying actual = resources.client().target("/hello-world")
.request().get(Saying.class);
Saying expected = new Saying(1, "Hello, Stranger!");
assertThat(actual.getId()).isEqualTo(expected.getId());
assertThat(actual.getContent()).isEqualTo(expected.getContent());
}
}
package com.dduportal.jenkins.demoapp.test;
import com.dduportal.jenkins.demoapp.HelloWorldApplication;
import com.dduportal.jenkins.demoapp.HelloWorldConfiguration;
import com.dduportal.jenkins.demoapp.api.Saying;
import io.dropwizard.testing.ResourceHelpers;
import io.dropwizard.testing.junit.DropwizardAppRule;
import org.glassfish.jersey.client.JerseyClientBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import javax.ws.rs.client.Client;
import static org.assertj.core.api.Assertions.assertThat;
@Category(IntegrationTest.class)
public class HelloWorldIntegrationTest {
@Rule
public final DropwizardAppRule<HelloWorldConfiguration> RULE =
new DropwizardAppRule<HelloWorldConfiguration>(HelloWorldApplication.class,
ResourceHelpers.resourceFilePath("helloworld-integrationtest.yaml"));
@Test
public void runServerTest() throws Exception {
Client client = new JerseyClientBuilder().build();
// It is an integration test: this is really longer than just build + unit test time
Thread.sleep(30000);
Saying result = client.target(
String.format("http://localhost:%d/api/hello-world", RULE.getLocalPort())
).queryParam("name", "dropwizard").request().get(Saying.class);
assertThat(result.getContent()).isEqualTo("Hello, dropwizard!");
}
}
package com.dduportal.jenkins.demoapp.test;
import com.dduportal.jenkins.demoapp.api.Saying;
import com.dduportal.jenkins.demoapp.resources.HelloWorldResource;
import com.google.common.base.Optional;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class HelloWorldResourceTest {
private HelloWorldResource resource;
@Before
public void setup() {
// Before each test, we re-instantiate our resource so it will reset
// the counter. It is good practice when dealing with a class that
// contains mutable data to reset it so tests can be ran independently
// of each other.
resource = new HelloWorldResource("Hello, %s", "Stranger");
}
@Test
public void idStartsAtOne() {
Saying result = resource.sayHello(Optional.of("dropwizard"));
assertThat(result.getId()).isEqualTo(1);
}
@Test
public void idIncrementsByOne() {
Saying result = resource.sayHello(Optional.of("dropwizard"));
Saying result2 = resource.sayHello(Optional.of("dropwizard2"));
assertThat(result2.getId()).isEqualTo(result.getId() + 1);
}
@Test
public void absentNameReturnsDefaultName() {
Saying result = resource.sayHello(Optional.<String>absent());
assertThat(result.getContent()).contains("Stranger");
}
@Test
public void nameReturnsName() {
Saying result = resource.sayHello(Optional.of("dropwizard"));
assertThat(result.getContent()).contains("dropwizard");
}
}
package com.dduportal.jenkins.demoapp.test;
public interface IntegrationTest {
}
template: Hello, %s!
defaultName: IntegrationTest
server:
rootPath: /api/
applicationConnectors:
- type: http
port: 8082
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