Skip to content
Closed
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
3 changes: 3 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Usage of Spring Cloud in your production code is optional, but __you will need i

|3.2.X, 3.3.X, 3.4.X, 3.5.X
|3.1.X

|4.0.X
|4.0.X
|===

[[how-to-use]]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.playtika.testcontainer.aerospike.EmbeddedAerospikeBootstrapConfiguration,\
com.playtika.testcontainers.aerospike.enterprise.SetupEnterpriseAerospikeBootstrapConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
import org.springframework.test.context.DynamicPropertyRegistrar;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.ToxiproxyContainer;
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.toxiproxy.ToxiproxyContainer;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

public class AerospikeScansTest extends BaseAerospikeTest {

Expand All @@ -15,7 +15,7 @@ void shouldDetectScans() {

queryWithoutFilter();

assertThrows(AssertionError.class, () -> aerospikeTestOperations.assertNoScans());
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> aerospikeTestOperations.assertNoScans());
int scansCount = aerospikeTestOperations.getScans().size();

assertThat(scansCount - initialScansCount).isEqualTo(1);
Expand All @@ -27,4 +27,4 @@ private void queryWithoutFilter() {
statement.setNamespace(namespace);
client.query(policy, statement);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.aerospike.client.Key;
import com.aerospike.client.policy.WritePolicy;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

Expand All @@ -16,6 +15,7 @@
import static java.time.Duration.ofDays;
import static java.time.Duration.ofHours;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

public class AerospikeTimeTravelTest extends BaseAerospikeTest {

Expand Down Expand Up @@ -91,7 +91,7 @@ public void shouldSetFutureTime() {
@Test
public void shouldNotSetFutureTime() {
OffsetDateTime minusDay = DateTimeUtils.now().minusDays(1);
Assertions.assertThrows(IllegalArgumentException.class, () -> aerospikeTestOperations.timeTravelTo(minusDay));
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> aerospikeTestOperations.timeTravelTo(minusDay));
}

@Test
Expand All @@ -115,4 +115,4 @@ private void putBin(Key key, int expiration) {
client.put(writePolicy, key, bin);
}

}
}
9 changes: 8 additions & 1 deletion embedded-artifactory/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,20 @@

* `embedded.artifactory.enabled` `(true|false, default is true)`
* `embedded.artifactory.reuseContainer` `(true|false, default is false)`
* `embedded.artifactory.dockerImage` `(default is 'releases-docker.jfrog.io/jfrog/artifactory-oss:7.77.12')`
* `embedded.artifactory.dockerImage` `(default is 'releases-docker.jfrog.io/jfrog/artifactory-oss:7.84.14')`
** Release notes on https://www.jfrog.com/confluence/display/JFROG/Artifactory+Release+Notes[jfrog.com]
* `embedded.artifactory.networkAlias` `(default is 'artifactory')`
* `embedded.artifactory.username` `(default is 'admin')`
* `embedded.artifactory.password` `(default is 'password')`
* `embedded.artifactory.databaseName` `(default is 'artifactory')` - Database name for PostgreSQL connection
* `embedded.artifactory.databaseUser` `(default is 'artifactory')` - Database user for PostgreSQL connection
* `embedded.artifactory.databasePassword` `(default is 'password')` - Database password for PostgreSQL connection
* `embedded.artifactory.securityMasterKey` `(default is a fixed 64 hex chars value)` - Master key for Artifactory 7.x bootstrap (env: `JF_SHARED_SECURITY_MASTER_KEY`)
* `embedded.artifactory.securityJoinKey` `(default is a fixed 64 hex chars value)` - Join key for JFrog platform bootstrap (env: `JF_SHARED_SECURITY_JOIN_KEY`)
* `embedded.toxiproxy.proxies.artifactory.enabled` Enables both creation of the container with ToxiProxy TCP proxy and a proxy to the `embedded-artifactory` container.

NOTE: Artifactory OSS 7.84+ requires PostgreSQL as the database backend. The `embedded-postgresql` module is automatically started as a dependency. Ensure that PostgreSQL database properties (`embedded.postgresql.user`, `embedded.postgresql.password`, `embedded.postgresql.database`) match the Artifactory database properties for proper connection.

==== Produces

* `embedded.artifactory.host`
Expand Down
7 changes: 6 additions & 1 deletion embedded-artifactory/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
<groupId>com.playtika.testcontainers</groupId>
<artifactId>embedded-toxiproxy</artifactId>
</dependency>
<dependency>
<groupId>com.playtika.testcontainers</groupId>
<artifactId>embedded-postgresql</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>io.rest-assured</groupId>
Expand All @@ -47,4 +52,4 @@
</exclusions>
</dependency>
</dependencies>
</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,29 @@ public class ArtifactoryProperties extends CommonContainerProperties {
String password = "password";
int restApiPort = 8081;
int generalPort = 8082;
String databaseName = "artifactory";
String databaseUser = "artifactory";
String databasePassword = "password";
/**
* Artifactory 7.x requires a master key for encryption (normally stored in system.yaml / master.key).
* We provide it via env vars for ephemeral test containers.
* Expected format: 32 bytes hex (64 chars).
*/
String securityMasterKey = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
/**
* Join key is required for JFrog platform services bootstrapping in container setups.
* Expected format: 32 bytes hex (64 chars).
*/
String securityJoinKey = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789";

public ArtifactoryProperties() {
setWaitTimeoutInSeconds(120);
setWaitTimeoutInSeconds(300);
}

@Override
public String getDefaultDockerImage() {
// Please don`t remove this comment.
// renovate: datasource=docker
return "releases-docker.jfrog.io/jfrog/artifactory-oss:7.77.12";
return "releases-docker.jfrog.io/jfrog/artifactory-oss:7.84.14";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.playtika.testcontainer.common.spring.DockerPresenceBootstrapConfiguration;
import com.playtika.testcontainer.common.utils.ContainerUtils;
import com.playtika.testcontainer.postgresql.EmbeddedPostgreSQLBootstrapConfiguration;
import com.playtika.testcontainer.toxiproxy.ToxiproxyClientProxy;
import com.playtika.testcontainer.toxiproxy.ToxiproxyHelper;
import com.playtika.testcontainer.toxiproxy.condition.ConditionalOnToxiProxyEnabled;
Expand All @@ -18,32 +19,38 @@
import org.springframework.test.context.DynamicPropertyRegistrar;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.ToxiproxyContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.images.builder.Transferable;
import org.testcontainers.postgresql.PostgreSQLContainer;
import org.testcontainers.toxiproxy.ToxiproxyContainer;

import java.util.Optional;
import java.nio.charset.StandardCharsets;

import static com.playtika.testcontainer.artifactory.ArtifactoryProperties.ARTIFACTORY_BEAN_NAME;
import static com.playtika.testcontainer.common.utils.ContainerUtils.configureCommonsAndStart;
import static java.time.Duration.ofSeconds;
import static org.testcontainers.postgresql.PostgreSQLContainer.POSTGRESQL_PORT;

@Slf4j
@Configuration
@ConditionalOnExpression("${embedded.containers.enabled:true}")
@AutoConfigureAfter(DockerPresenceBootstrapConfiguration.class)
@AutoConfigureAfter({DockerPresenceBootstrapConfiguration.class, EmbeddedPostgreSQLBootstrapConfiguration.class})
@ConditionalOnProperty(name = "embedded.artifactory.enabled", matchIfMissing = true)
@EnableConfigurationProperties(ArtifactoryProperties.class)
public class EmbeddedArtifactoryBootstrapConfiguration {

private static final String ARTIFACTORY_NETWORK_ALIAS = "artifactory.testcontainer.docker";
private static final String POSTGRESQL_NETWORK_ALIAS = "postgresql.testcontainer.docker";

@Bean
@ConditionalOnMissingBean(name = "artifactoryWaitStrategy")
public WaitStrategy artifactoryWaitStrategy(ArtifactoryProperties properties) {
return new HttpWaitStrategy()
.forPath("/")
.forPort(properties.getGeneralPort())
.forStatusCode(200);
.forStatusCode(200)
.withStartupTimeout(ofSeconds(properties.getWaitTimeoutInSeconds()));
}

@Bean
Expand All @@ -69,14 +76,54 @@ public DynamicPropertyRegistrar artifactoryToxiProxyDynamicPropertyRegistrar(@Qu
@Bean(name = ARTIFACTORY_BEAN_NAME, destroyMethod = "stop")
public GenericContainer<?> artifactory(ArtifactoryProperties properties,
WaitStrategy artifactoryWaitStrategy,
Optional<Network> network) {
Network network,
@Qualifier("embeddedPostgreSql") PostgreSQLContainer postgresql) {
String databaseUrl = String.format("jdbc:postgresql://%s:%d/%s",
POSTGRESQL_NETWORK_ALIAS,
POSTGRESQL_PORT,
properties.getDatabaseName());

String systemYaml = ""
+ "shared:\n"
+ " database:\n"
+ " type: postgresql\n"
+ " driver: org.postgresql.Driver\n"
+ " url: " + databaseUrl + "\n"
+ " username: " + properties.getDatabaseUser() + "\n"
+ " password: " + properties.getDatabasePassword() + "\n"
+ " security:\n"
+ " masterKey: " + properties.getSecurityMasterKey() + "\n"
+ " joinKey: " + properties.getSecurityJoinKey() + "\n";
GenericContainer<?> container =
new GenericContainer<>(ContainerUtils.getDockerImageName(properties))
.withExposedPorts(properties.getRestApiPort(), properties.getGeneralPort())
.withNetwork(Network.SHARED)
.withNetwork(network)
.withNetworkAliases(properties.getNetworkAlias(), ARTIFACTORY_NETWORK_ALIAS)
.withCopyToContainer(
Transferable.of(systemYaml.getBytes(StandardCharsets.UTF_8), 0666),
"/opt/jfrog/artifactory/var/etc/system.yaml")
.withExtraHost("localhost", "127.0.0.1")
.withEnv("JF_ROUTER_ENTRYPOINTS_INTERNALHOST", "::1")
.withEnv("JF_SHARED_DATABASE_TYPE", "postgresql")
.withEnv("JF_SHARED_DATABASE_URL", databaseUrl)
.withEnv("JF_SHARED_DATABASE_USERNAME", properties.getDatabaseUser())
.withEnv("JF_SHARED_DATABASE_PASSWORD", properties.getDatabasePassword())
.withEnv("JF_SHARED_SECURITY_MASTER_KEY", properties.getSecurityMasterKey())
.withEnv("JF_SHARED_SECURITY_JOIN_KEY", properties.getSecurityJoinKey())
// Some internal services resolve config key as `shared.security.masterKey` / `shared.security.joinKey`
// (camelCase). Provide both variants.
.withEnv("JF_SHARED_SECURITY_MASTERKEY", properties.getSecurityMasterKey())
.withEnv("JF_SHARED_SECURITY_JOINKEY", properties.getSecurityJoinKey())
// Some Artifactory 7.x components still require the key files to exist.
// Provide them explicitly for ephemeral test containers.
.withCopyToContainer(
Transferable.of((properties.getSecurityMasterKey() + "\n").getBytes(StandardCharsets.UTF_8), 0666),
"/opt/jfrog/artifactory/var/etc/security/master.key")
.withCopyToContainer(
Transferable.of((properties.getSecurityJoinKey() + "\n").getBytes(StandardCharsets.UTF_8), 0666),
"/opt/jfrog/artifactory/var/etc/security/join.key")
.dependsOn(postgresql)
.waitingFor(artifactoryWaitStrategy);
network.ifPresent(container::withNetwork);
configureCommonsAndStart(container, properties, log);
Integer mappedPort = container.getMappedPort(properties.generalPort);
String host = container.getHost();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.playtika.testcontainer.artifactory;

import com.playtika.testcontainer.common.spring.DockerPresenceBootstrapConfiguration;
import com.playtika.testcontainer.postgresql.EmbeddedPostgreSQLBootstrapConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.testcontainers.containers.Network;

@Slf4j
@Configuration
@ConditionalOnExpression("${embedded.containers.enabled:true}")
@ConditionalOnProperty(name = "embedded.artifactory.enabled", matchIfMissing = true)
@AutoConfigureAfter(DockerPresenceBootstrapConfiguration.class)
@AutoConfigureBefore(EmbeddedPostgreSQLBootstrapConfiguration.class)
public class EmbeddedArtifactoryNetworkBootstrapConfiguration {

@Bean(destroyMethod = "close")
@ConditionalOnMissingBean(Network.class)
public Network network() {
Network network = Network.newNetwork();
log.info("Created docker Network with id={}", network.getId());
return network;
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.playtika.testcontainer.artifactory.EmbeddedArtifactoryNetworkBootstrapConfiguration,\
com.playtika.testcontainer.artifactory.EmbeddedArtifactoryBootstrapConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
import org.springframework.context.annotation.Configuration;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = BaseEmbeddedArtifactoryTest.TestConfiguration.class
classes = BaseEmbeddedArtifactoryTest.TestConfiguration.class,
properties = {
"embedded.postgresql.user=artifactory",
"embedded.postgresql.password=password",
"embedded.postgresql.database=artifactory"
}
)
class BaseEmbeddedArtifactoryTest {
@Value("${embedded.artifactory.host}")
Expand All @@ -26,4 +31,4 @@ static class TestConfiguration {

}

}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
package com.playtika.testcontainer.artifactory;

import org.junit.jupiter.api.Test;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import static io.restassured.RestAssured.given;
import java.net.URI;

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

class EmbeddedArtifactoryBootstrapConfigurationTest extends BaseEmbeddedArtifactoryTest {

private final RestTemplate restTemplate = new RestTemplate();

@Test
void shouldStartupArtifactory() {
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("http")
.host(artifactoryHost)
.port(artifactoryPort)
.path("/")
.build();

given()
.get(uriComponents.toUriString())
.then()
.assertThat()
.statusCode(200);
URI artifactoryUri = URI.create(String.format("http://%s:%d/", artifactoryHost, artifactoryPort));
ResponseEntity<String> response = restTemplate.getForEntity(artifactoryUri, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}

}
}
5 changes: 5 additions & 0 deletions embedded-artifactory/src/test/resources/bootstrap-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
embedded:
postgresql:
database: artifactory
password: password
user: artifactory
Loading
Loading