Skip to content

Commit 84111a0

Browse files
committed
Add Nacos module
1 parent cc7721c commit 84111a0

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

docs/modules/nacos.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Hashicorp Nacos Module
2+
3+
Testcontainers module for [Nacos](https://github.com/alibaba/nacos). Nacos an easy-to-use dynamic service discovery, configuration and service management platform for building AI cloud native applications. More information on Nacos [here](https://nacos.io/docs/latest/overview/).
4+
5+
## Usage example
6+
7+
<!--codeinclude-->
8+
[Running Nacos in your Junit tests](../../modules/nacos/src/test/java/org/testcontainers/nacos/NacosContainerTest.java)
9+
<!--/codeinclude-->
10+
11+
## Why Nacos in Junit tests?
12+
13+
With the increasing popularity of Nacos and config externalization, applications are now needing to source properties from Nacos.
14+
This can prove challenging in the development phase without a running Nacos instance readily on hand. This library
15+
aims to solve your apps integration testing with Nacos. You can also use it to
16+
test how your application behaves with Nacos by writing different test scenarios in Junit.
17+
18+
## Adding this module to your project dependencies
19+
20+
Add the following dependency to your `pom.xml`/`build.gradle` file:
21+
22+
=== "Gradle"
23+
```groovy
24+
testImplementation "org.testcontainers:nacos:{{latest_version}}"
25+
```
26+
27+
=== "Maven"
28+
```xml
29+
<dependency>
30+
<groupId>org.testcontainers</groupId>
31+
<artifactId>nacos</artifactId>
32+
<version>{{latest_version}}</version>
33+
<scope>test</scope>
34+
</dependency>
35+
```

modules/nacos/build.gradle

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
description = "Testcontainers :: Nacos"
2+
3+
dependencies {
4+
api project(':testcontainers')
5+
6+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.11.0'
7+
8+
testImplementation 'org.junit.jupiter:junit-jupiter:5.13.4'
9+
testImplementation 'com.alibaba.nacos:nacos-client:3.0.3'
10+
testImplementation 'io.rest-assured:rest-assured:5.5.6'
11+
testImplementation 'org.assertj:assertj-core:3.27.4'
12+
}
13+
14+
test {
15+
useJUnitPlatform()
16+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.testcontainers.nacos;
2+
3+
import org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.containers.wait.strategy.Wait;
5+
import org.testcontainers.utility.DockerImageName;
6+
7+
/**
8+
* Testcontainers implementation for Nacos.
9+
* <p>
10+
* Supported images: {@code nacos/nacos-server}, {@code nacos}
11+
* <p>
12+
* Exposed ports:
13+
* <ul>
14+
* <li>HTTP: 8848</li>
15+
* <li>HTTP: 8080</li>
16+
* <li>gRPC: 9848</li>
17+
* </ul>
18+
*
19+
*/
20+
public class NacosContainer extends GenericContainer<NacosContainer> {
21+
22+
private static final DockerImageName DEFAULT_OLD_IMAGE_NAME = DockerImageName.parse("nacos");
23+
24+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("nacos/nacos-server");
25+
26+
private static final int NACOS_HTTP_ADMIN_PORT = 8848;
27+
28+
private static final int NACOS_HTTP_CONSOLE_PORT = 8080;
29+
30+
private static final int NACOS_GRPC_PORT = 9848;
31+
32+
public NacosContainer(String dockerImageName) {
33+
this(DockerImageName.parse(dockerImageName), 38848, 38080, 39848);
34+
}
35+
36+
public NacosContainer(final DockerImageName dockerImageName, int adminPort, int consolePort, int grpcPort) {
37+
super(dockerImageName);
38+
dockerImageName.assertCompatibleWith(DEFAULT_OLD_IMAGE_NAME, DEFAULT_IMAGE_NAME);
39+
40+
// Wait until the Nacos server is ready to accept requests.
41+
// Visit the login page to verify if nacos is running.
42+
setWaitStrategy(Wait.forHttp("/#/login").forPort(NACOS_HTTP_CONSOLE_PORT).forStatusCode(200));
43+
44+
// According to Nacos' design, the gRPC client port adds 1000 to the main port, which means that if the main port is 8849, the gRPC port defaults to 9849
45+
addFixedExposedPort(adminPort, NACOS_HTTP_ADMIN_PORT);
46+
addFixedExposedPort(consolePort, NACOS_HTTP_CONSOLE_PORT);
47+
addFixedExposedPort(grpcPort, NACOS_GRPC_PORT);
48+
49+
// Configure Nacos for single machine startup.
50+
withEnv("MODE", "standalone");
51+
// Nacos is used to generate keys for JWT tokens, using strings longer than 32 characters and then encoded with Base64.
52+
withEnv("NACOS_AUTH_TOKEN", "SecretKey012345678901234567890123456789012345678901234567890123456789");
53+
// The key for the identity identifier of the Inner API between Nacos servers is required.
54+
withEnv("NACOS_AUTH_IDENTITY_KEY", "serverIdentity");
55+
// The value of the identity identifier for the Inner API between Nacos servers is required.
56+
withEnv("NACOS_AUTH_IDENTITY_VALUE", "security");
57+
}
58+
59+
public String getServerAddr() {
60+
return String.format("%s:%s", this.getHost(), this.getMappedPort(NACOS_HTTP_ADMIN_PORT));
61+
}
62+
63+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.testcontainers.nacos;
2+
3+
import com.alibaba.nacos.api.NacosFactory;
4+
import com.alibaba.nacos.api.PropertyKeyConst;
5+
import com.alibaba.nacos.api.config.ConfigService;
6+
import com.alibaba.nacos.api.exception.NacosException;
7+
import org.junit.jupiter.api.AfterAll;
8+
import org.junit.jupiter.api.BeforeAll;
9+
import org.junit.jupiter.api.BeforeEach;
10+
import org.junit.jupiter.api.Test;
11+
12+
import java.util.Properties;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
class NacosContainerTest {
17+
18+
private ConfigService configService;
19+
20+
private static NacosContainer nacos = new NacosContainer("nacos/nacos-server:v3.0.3");
21+
22+
@BeforeAll
23+
static void setup() {
24+
nacos.start();
25+
}
26+
27+
@AfterAll
28+
static void teardown() {
29+
nacos.stop();
30+
}
31+
32+
@BeforeEach
33+
void init() throws NacosException {
34+
Properties properties = new Properties();
35+
properties.put(PropertyKeyConst.SERVER_ADDR, nacos.getServerAddr());
36+
properties.put(PropertyKeyConst.USERNAME, "nacos");
37+
properties.put(PropertyKeyConst.PASSWORD, "nacos");
38+
configService = NacosFactory.createConfigService(properties);
39+
}
40+
41+
42+
@Test
43+
void writeAndRemoveValue() throws NacosException, InterruptedException {
44+
assertThat(configService.publishConfig("test.yaml", "DEFAULT", "name: 123")).isTrue();
45+
Thread.sleep(1500);
46+
assertThat(configService.getConfig("test.yaml", "DEFAULT", 5000)).isEqualTo("name: 123");
47+
assertThat(configService.removeConfig("test.yaml", "DEFAULT")).isTrue();
48+
Thread.sleep(1500);
49+
assertThat(configService.getConfig("test.yaml", "DEFAULT", 5000)).isEqualTo(null);
50+
}
51+
52+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<configuration>
2+
3+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
4+
<!-- encoders are assigned the type
5+
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
6+
<encoder>
7+
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
8+
</encoder>
9+
</appender>
10+
11+
<root level="INFO">
12+
<appender-ref ref="STDOUT"/>
13+
</root>
14+
15+
<logger name="org.testcontainers" level="INFO"/>
16+
</configuration>

0 commit comments

Comments
 (0)