Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ body:
- K3S
- K6
- Kafka
- LDAP
- LocalStack
- MariaDB
- Milvus
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/enhancement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ body:
- K3S
- K6
- Kafka
- LDAP
- LocalStack
- MariaDB
- Milvus
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ body:
- K3S
- K6
- Kafka
- LDAP
- LocalStack
- MariaDB
- Milvus
Expand Down
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/ldap"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/localstack"
schedule:
Expand Down
4 changes: 4 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@
- changed-files:
- any-glob-to-any-file:
- modules/kafka/**/*
"modules/ldap":
- changed-files:
- any-glob-to-any-file:
- modules/ldap/**/*
"modules/localstack":
- changed-files:
- any-glob-to-any-file:
Expand Down
3 changes: 3 additions & 0 deletions .github/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ labels:
- name: modules/kafka
color: '#006b75'

- name: modules/ldap
color: '#006b75'

- name: modules/localstack
color: '#006b75'

Expand Down
30 changes: 30 additions & 0 deletions docs/modules/ldap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# LDAP

Testcontainers module for [LLDAP](https://hub.docker.com/r/lldap/lldap).

## LLdapContainer's usage examples

You can start a LLDAP container instance from any Java application by using:

<!--codeinclude-->
[LLDAP container](../../modules/ldap/src/test/java/org/testcontainers/ldap/LLdapContainerTest.java) inside_block:container
<!--/codeinclude-->

## Adding this module to your project dependencies

Add the following dependency to your `pom.xml`/`build.gradle` file:

=== "Gradle"
```groovy
testImplementation "org.testcontainers:ldap:{{latest_version}}"
```

=== "Maven"
```xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>ldap</artifactId>
<version>{{latest_version}}</version>
<scope>test</scope>
</dependency>
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ nav:
- modules/k3s.md
- modules/k6.md
- modules/kafka.md
- modules/ldap.md
- modules/localstack.md
- modules/milvus.md
- modules/minio.md
Expand Down
8 changes: 8 additions & 0 deletions modules/ldap/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
description = "Testcontainers :: LDAP"

dependencies {
api project(':testcontainers')

testImplementation 'org.assertj:assertj-core:3.26.3'
testImplementation 'com.unboundid:unboundid-ldapsdk:7.0.2'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.testcontainers.ldap;

import com.github.dockerjava.api.command.InspectContainerResponse;
import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

/**
* Testcontainers implementation for LLDAP.
* <p>
* Supported image: {@code lldap/lldap}
* <p>
* Exposed ports:
* <ul>
* <li>LDAP: 3890</li>
* <li>UI: 17170</li>
* </ul>
*/
@Slf4j
public class LLdapContainer extends GenericContainer<LLdapContainer> {
Copy link

@rfelgent rfelgent Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think of adding an abstract LdapContainer class as adapter (similar to org.testcontainers.containers.JdbcDatabaseContainer).

By doing so, we could (later) add support for other containers like 'bitnami/openldap' or 'docker-openldap' at a later time

Copy link
Member Author

@eddumelendez eddumelendez Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is different. I don't see much shared between ldap as you can see in the implementation. It only took exposed ports and specific wait strategy.

We still can add other implementations later with the proper setup.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,

I believe introducing an abstract class for the LDAP container could be beneficial once a second LDAP container is added to Testcontainers. For Spring Boot, this would allow the creation of a single ContainerConnectionDetailsFactory, similar to the JdbcContainerConnectionDetailsFactory.

@eddumelendez: Do you think it would be valuable to add another LDAP container for the bitnami/openldap image in the near future? If so, please let me know if you would welcome an OSS contribution on this.

Copy link
Member Author

@eddumelendez eddumelendez Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe introducing an abstract class for the LDAP container could be beneficial once a second LDAP container is added to Testcontainers. For Spring Boot, this would allow the creation of a single ContainerConnectionDetailsFactory, similar to the JdbcContainerConnectionDetailsFactory.

Yes, I understand that. I think this in general could be something to revisit in a major version.

Do you think it would be valuable to add another LDAP container for the bitnami/openldap image in the near future?

bitnami has really great images with easy configuration for some scenarios. I wish there is a bitnami module with support for their images. But, the suggestion was declined, see bitnami/containers#74973. And for us, it is getting a bit complex to handle many modules.

Copy link

@rfelgent rfelgent Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @eddumelendez ,

Yes, I understand that. I think this in general could be something to revisit in a major version.

I understand your point of delaying this abstraction.

Still, I think that such introduction "now" would have made the in the integration in "spring-boot" testcontainers easier and "future" proven.

By "future" proven, I mean there are a few ldap Information any ldap container should share:

  • "admin-user/password" (similar to a database user) so that the app can interact with the ldap server
  • base-dn, the majority - if not all - ldap server will provide base directory setup and not a blank one
  • another feature would be "ldif" importer


private static final String IMAGE_VERSION = "lldap/lldap";

private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(IMAGE_VERSION);

private static final int LDAP_PORT = 3890;

private static final int UI_PORT = 17170;

public LLdapContainer(String image) {
this(DockerImageName.parse(image));
}

public LLdapContainer(DockerImageName image) {
super(image);
image.assertCompatibleWith(DEFAULT_IMAGE_NAME);
addExposedPorts(LDAP_PORT, UI_PORT);

waitingFor(Wait.forHttp("/health").forPort(UI_PORT).forStatusCode(200));
}

@Override
protected void containerIsStarted(InspectContainerResponse containerInfo) {
log.info("LLDAP container is ready! UI available at http://{}:{}", getHost(), getMappedPort(UI_PORT));
}

public int getLdapPort() {
return getMappedPort(LDAP_PORT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.testcontainers.ldap;

import com.unboundid.ldap.sdk.BindResult;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class LLdapContainerTest {

@Test
public void test() throws LDAPException {
try ( // container {
LLdapContainer lldap = new LLdapContainer("lldap/lldap:v0.6.1-alpine")
// }
) {
lldap.start();
LDAPConnection connection = new LDAPConnection(lldap.getHost(), lldap.getLdapPort());
BindResult result = connection.bind("cn=admin,ou=people,dc=example,dc=com", "password");
assertThat(result).isNotNull();
}
}
}
16 changes: 16 additions & 0 deletions modules/ldap/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

<logger name="org.testcontainers" level="INFO"/>
</configuration>
Loading