Skip to content

Commit b37075d

Browse files
authored
Merge pull request #6 from Geeky-Hacker/localstack
Add LocalStack
2 parents 0d26b5e + 7304166 commit b37075d

File tree

13 files changed

+286
-3
lines changed

13 files changed

+286
-3
lines changed

.github/workflows/build.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ jobs:
2525
distribution: 'zulu'
2626
java-version: '21'
2727
- name: Run tests
28+
env:
29+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
30+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
2831
run: mvn clean verify
2932
- name: Build with Maven
30-
run: mvn -B package --file pom.xml
33+
env:
34+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
35+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
36+
run: mvn -B package --file pom.xml

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ replay_pid*
3030
target/
3131
**/target/
3232
**/*.iml
33+
34+
# LocalStack volume
35+
spring-boot/volume

spring-boot/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
11
# Related articles
22

33
- [How to mock @Value field in Spring Boot](https://www.geekyhacker.com/how-to-mock-at-value-field-in-spring-boot/)
4+
5+
6+
## LocalStack
7+
8+
To run LocalStack in this project:
9+
10+
```bash
11+
$ docker-compose up
12+
```
13+
14+
To use AWS Secrets manager secret with the application, go to the `scripts` directory and run:
15+
16+
```bash
17+
$ ./create_secret.sh
18+
```
19+
20+
That creates a secret for `api.key` property of the application. Without that, the application defaults to `testKey` value and on test to `fakeApiKey` value.
21+
22+
After that you can access the LocalStack environment on `http://localhost:4566`. For example, to get list of secrets:
23+
24+
```bash
25+
$ aws --endpoint-url=http://localhost:4566 --region=eu-central-1 secretsmanager list-secrets
26+
```

spring-boot/docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: "3.8"
2+
3+
services:
4+
localstack:
5+
container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
6+
image: localstack/localstack
7+
ports:
8+
- "127.0.0.1:4566:4566" # LocalStack Gateway
9+
- "127.0.0.1:4510-4559:4510-4559" # external services port range
10+
environment:
11+
# LocalStack configuration: https://docs.localstack.cloud/references/configuration/
12+
- DEBUG=${DEBUG:-0}
13+
volumes:
14+
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
15+
- "/var/run/docker.sock:/var/run/docker.sock"

spring-boot/pom.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,39 @@
2727
<scope>runtime</scope>
2828
<optional>true</optional>
2929
</dependency>
30+
<dependency>
31+
<groupId>io.awspring.cloud</groupId>
32+
<artifactId>spring-cloud-aws-starter-secrets-manager</artifactId>
33+
</dependency>
3034
<dependency>
3135
<groupId>org.springframework.boot</groupId>
3236
<artifactId>spring-boot-starter-test</artifactId>
3337
<scope>test</scope>
3438
</dependency>
39+
<dependency>
40+
<groupId>org.testcontainers</groupId>
41+
<artifactId>junit-jupiter</artifactId>
42+
<version>1.20.1</version>
43+
<scope>test</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.testcontainers</groupId>
47+
<artifactId>localstack</artifactId>
48+
<version>1.20.1</version>
49+
<scope>test</scope>
50+
</dependency>
3551
</dependencies>
52+
<dependencyManagement>
53+
<dependencies>
54+
<dependency>
55+
<groupId>io.awspring.cloud</groupId>
56+
<artifactId>spring-cloud-aws-dependencies</artifactId>
57+
<version>3.1.1</version>
58+
<type>pom</type>
59+
<scope>import</scope>
60+
</dependency>
61+
</dependencies>
62+
</dependencyManagement>
3663
<build>
3764
<plugins>
3865
<plugin>

spring-boot/scripts/create_secret.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/sh
2+
3+
aws --endpoint-url=http://localhost:4566 --region=eu-central-1 \
4+
secretsmanager create-secret \
5+
--name weather/api/credentials \
6+
--description "Weather API related Credentials" \
7+
--secret-string file://secrets.json

spring-boot/scripts/secrets.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"api.key": "d3deaf2ba6ab43229be3e58ba2ada8d6"
3+
}

spring-boot/src/main/java/com/geekyhacker/springboot/integration/WeatherApi.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ public class WeatherApi {
1313
@Value("${api.endpoint}")
1414
private String apiEndpoint;
1515

16+
@Value("${api.key}")
17+
private String apiKey;
18+
1619
public double getCurrentTemperature() {
17-
logger.info("Getting the current weather temperature from {} endpoint", apiEndpoint);
20+
logger.info("Getting the current weather temperature from {} endpoint with key {}", apiEndpoint, apiKey);
1821
return -1;
1922
}
2023

2124
public String getApiEndpoint() {
2225
return apiEndpoint;
2326
}
27+
28+
public String getApiKey() {
29+
return apiKey;
30+
}
2431
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.geekyhacker.springboot.store;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.core.type.TypeReference;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import org.springframework.stereotype.Service;
7+
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
8+
import software.amazon.awssdk.services.secretsmanager.model.*;
9+
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
@Service
14+
public class SecretStore {
15+
16+
private final SecretsManagerClient secretsManagerClient;
17+
private final ObjectMapper objectMapper;
18+
19+
public SecretStore(SecretsManagerClient secretsManagerClient, ObjectMapper objectMapper) {
20+
this.secretsManagerClient = secretsManagerClient;
21+
this.objectMapper = objectMapper;
22+
}
23+
24+
public Map<String, String> getSecretAsMap(String secretName) throws JsonProcessingException {
25+
return objectMapper.readValue(getSecretValue(secretName), new TypeReference<>() {
26+
});
27+
}
28+
29+
public String getSecretValue(String secretName) {
30+
GetSecretValueResponse secretValueResponse = secretsManagerClient.getSecretValue(GetSecretValueRequest.builder().secretId(secretName).build());
31+
return secretValueResponse.secretString();
32+
}
33+
34+
public List<String> listSecrets() {
35+
return secretsManagerClient.listSecrets().secretList().stream().map(SecretListEntry::name).toList();
36+
}
37+
38+
public void createSecret(String secretName, String secretValue) {
39+
CreateSecretRequest createSecretRequest = CreateSecretRequest.builder()
40+
.name(secretName)
41+
.secretString(secretValue)
42+
.build();
43+
secretsManagerClient.createSecret(createSecretRequest);
44+
}
45+
46+
public void deleteSecret(String secretName, boolean forceDelete) {
47+
secretsManagerClient.deleteSecret(DeleteSecretRequest.builder().secretId(secretName).forceDeleteWithoutRecovery(forceDelete).build());
48+
}
49+
50+
public void deleteAllSecrets() {
51+
listSecrets().forEach(secretName -> deleteSecret(secretName, true));
52+
}
53+
54+
public void updateSecret(String secretName, String secretValue) {
55+
secretsManagerClient.updateSecret(UpdateSecretRequest.builder().secretId(secretName).secretString(secretValue).build());
56+
}
57+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
spring.application.name=geeky-hacker-spring-boot
2+
spring.cloud.aws.secretsmanager.region=eu-central-1
3+
spring.cloud.aws.secretsmanager.endpoint=http://localhost:4566
4+
spring.config.import=optional:aws-secretsmanager:weather/api/credentials
25
api.endpoint=https://eris.madadipouya.com/v1/weather/currentbyip
6+
api.key=testKey

0 commit comments

Comments
 (0)