Provide an embedded Docker container for local development #1
45
Dockerfile
Normal file
45
Dockerfile
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
FROM eclipse-temurin:23 AS build
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
COPY pom.xml mvnw ./
|
||||||
|
COPY .mvn .mvn
|
||||||
|
|
||||||
|
RUN ./mvnw dependency:copy-dependencies \
|
||||||
|
--activate-profiles=!persistent \
|
||||||
|
--define includeScope=compile \
|
||||||
|
--define outputDirectory=lib
|
||||||
|
|
||||||
|
RUN ./mvnw dependency:build-classpath \
|
||||||
|
--activate-profiles=!persistent \
|
||||||
|
--define includeScope=compile \
|
||||||
|
--define mdep.outputFile=classpath \
|
||||||
|
--define mdep.prefix=lib
|
||||||
|
|
||||||
|
COPY src src
|
||||||
|
|
||||||
|
RUN ./mvnw compile
|
||||||
|
|
||||||
|
# Create as small a runtime as possible but Spring/Tomcat needs a lot of modules
|
||||||
|
RUN jlink \
|
||||||
|
--output jre \
|
||||||
|
--add-modules java.sql,java.desktop,java.management,java.naming,java.security.jgss,java.instrument
|
||||||
|
|
||||||
|
FROM debian:stable-slim AS runtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=build /build/jre jre
|
||||||
|
COPY --from=build /build/lib lib
|
||||||
|
COPY --from=build /build/classpath classpath
|
||||||
|
COPY --from=build /build/target/classes classes
|
||||||
|
|
||||||
|
# Adds the output of Maven compilation to output
|
||||||
|
RUN echo ":classes" >> classpath
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD [ "./jre/bin/java" \
|
||||||
|
, "-cp", "@classpath" \
|
||||||
|
, "se.su.dsv.oauth2.AuthorizationServer" \
|
||||||
|
, "--spring.profiles.active=dev,embedded" \
|
||||||
|
]
|
||||||
48
pom.xml
48
pom.xml
@ -23,10 +23,6 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
|
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
|
||||||
@ -52,21 +48,6 @@
|
|||||||
<version>${jte.version}</version>
|
<version>${jte.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.flywaydb</groupId>
|
|
||||||
<artifactId>flyway-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.flywaydb</groupId>
|
|
||||||
<artifactId>flyway-mysql</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mariadb.jdbc</groupId>
|
|
||||||
<artifactId>mariadb-java-client</artifactId>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Development tools -->
|
<!-- Development tools -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -146,7 +127,6 @@
|
|||||||
<sourceDirectory>${project.basedir}/src/main/resources/templates</sourceDirectory>
|
<sourceDirectory>${project.basedir}/src/main/resources/templates</sourceDirectory>
|
||||||
<targetDirectory>${project.build.directory}/jte-classes</targetDirectory>
|
<targetDirectory>${project.build.directory}/jte-classes</targetDirectory>
|
||||||
<contentType>Html</contentType>
|
<contentType>Html</contentType>
|
||||||
<binaryStaticContent>true</binaryStaticContent>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<extension>
|
<extension>
|
||||||
<className>gg.jte.models.generator.ModelExtension</className>
|
<className>gg.jte.models.generator.ModelExtension</className>
|
||||||
@ -172,4 +152,32 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>persistent</id>
|
||||||
|
<activation>
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-mysql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mariadb.jdbc</groupId>
|
||||||
|
<artifactId>mariadb-java-client</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
91
src/main/java/se/su/dsv/oauth2/EmbeddedConfiguration.java
Normal file
91
src/main/java/se/su/dsv/oauth2/EmbeddedConfiguration.java
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package se.su.dsv.oauth2;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import se.su.dsv.oauth2.admin.repository.ClientRepository;
|
||||||
|
import se.su.dsv.oauth2.admin.repository.ClientRow;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Profile("embedded")
|
||||||
|
public class EmbeddedConfiguration {
|
||||||
|
@Bean
|
||||||
|
public ClientRepository clientRepository() {
|
||||||
|
ArrayList<ClientRow> clients = new ArrayList<>();
|
||||||
|
ClientRow clientRow = getClientFromEnvironment();
|
||||||
|
if (clientRow != null) {
|
||||||
|
clients.add(clientRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InMemoryClientrepository(clients);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClientRow getClientFromEnvironment() {
|
||||||
|
String clientId = System.getenv("CLIENT_ID");
|
||||||
|
String clientSecret = System.getenv("CLIENT_SECRET");
|
||||||
|
String redirectUri = System.getenv("CLIENT_REDIRECT_URI");
|
||||||
|
String scopeString = System.getenv("CLIENT_SCOPES");
|
||||||
|
|
||||||
|
return new ClientRow(clientId, clientId, clientId, "dev@localhost",
|
||||||
|
redirectUri, scopeString, clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InMemoryClientrepository implements ClientRepository {
|
||||||
|
private List<ClientRow> clientRows;
|
||||||
|
private Map<String, List<String>> clientOwners = new HashMap<>();
|
||||||
|
|
||||||
|
public InMemoryClientrepository(final List<ClientRow> clients) {
|
||||||
|
this.clientRows = new ArrayList<>(clients);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addNewClient(final ClientRow clientRow) {
|
||||||
|
clientRows.add(clientRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ClientRow> getClients(final Principal owner) {
|
||||||
|
return List.copyOf(clientRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addClientOwner(final String principalName, final String id) {
|
||||||
|
clientOwners.putIfAbsent(id, new ArrayList<>());
|
||||||
|
clientOwners.get(id).add(principalName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeOwner(final String id, final String owner) {
|
||||||
|
clientOwners.putIfAbsent(id, new ArrayList<>());
|
||||||
|
clientOwners.get(id).remove(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOwners(final String id) {
|
||||||
|
return clientOwners.getOrDefault(id, List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ClientRow> getClientRowById(final String id) {
|
||||||
|
return clientRows.stream()
|
||||||
|
.filter(clientRow -> Objects.equals(clientRow.id(), id))
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ClientRow> getClientRowByClientId(final String clientId) {
|
||||||
|
return clientRows.stream()
|
||||||
|
.filter(clientRow -> Objects.equals(clientRow.clientId(), clientId))
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,9 +2,11 @@ package se.su.dsv.oauth2;
|
|||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.jdbc.core.simple.JdbcClient;
|
import org.springframework.jdbc.core.simple.JdbcClient;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@Profile("!embedded")
|
||||||
public class PersistentConfiguration {
|
public class PersistentConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
public JDBCClientRepository jdbcClientRepository(JdbcClient jdbcClient) {
|
public JDBCClientRepository jdbcClientRepository(JdbcClient jdbcClient) {
|
||||||
|
|||||||
5
src/main/resources/application-embedded.yml
Normal file
5
src/main/resources/application-embedded.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
gg:
|
||||||
|
jte:
|
||||||
|
templateLocation: src/main/resources/templates
|
||||||
|
developmentMode: false
|
||||||
|
usePrecompiledTemplates: true
|
||||||
52
src/test/java/se/su/dsv/oauth2/EmbeddedContainerTest.java
Normal file
52
src/test/java/se/su/dsv/oauth2/EmbeddedContainerTest.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package se.su.dsv.oauth2;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.images.builder.ImageFromDockerfile;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
public class EmbeddedContainerTest {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
static GenericContainer<?> container = new GenericContainer<>(
|
||||||
|
new ImageFromDockerfile()
|
||||||
|
.withFileFromPath(".", Paths.get(".")))
|
||||||
|
.withExposedPorts(8080)
|
||||||
|
.withEnv("CLIENT_ID", "client-id")
|
||||||
|
.withEnv("CLIENT_SECRET", "client-secret")
|
||||||
|
.withEnv("CLIENT_REDIRECT_URI", "http://localhost:8080")
|
||||||
|
.withEnv("CLIENT_SCOPES", "openid profile email");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void working_container() {
|
||||||
|
String baseUri = UriComponentsBuilder.newInstance()
|
||||||
|
.scheme("http")
|
||||||
|
.host(container.getHost())
|
||||||
|
.port(container.getMappedPort(8080))
|
||||||
|
.toUriString();
|
||||||
|
|
||||||
|
RestClient restClient = RestClient.create(baseUri);
|
||||||
|
|
||||||
|
ResponseEntity<String> response = restClient
|
||||||
|
.get()
|
||||||
|
.retrieve()
|
||||||
|
.onStatus(ignored -> false) // treat all responses as successful and let asserts fail
|
||||||
|
.toEntity(String.class);
|
||||||
|
|
||||||
|
assertThat(response.getStatusCode())
|
||||||
|
.isEqualTo(HttpStatus.OK);
|
||||||
|
|
||||||
|
assertThat(response.getBody())
|
||||||
|
.contains("DSV");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user