Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add spring boot integration #83

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 17 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,24 @@
<version>2.0.16</version>
<scope>test</scope>
</dependency>

<!-- Spring integration (optional!) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.16</version>
<groupId>org.springframework.boot</groupId>
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like liquibase does not follow the convention, but it would be great to have spring-boot-liquibase-opensearch-starter / liquibase-opensearch-starter as a separate module

<artifactId>spring-boot-starter</artifactId>
<version>3.3.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>3.3.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.3.5</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.reactor.ssl.TlsDetails;
Expand Down Expand Up @@ -144,7 +145,7 @@ private void connect() {
try {
sslcontext = SSLContextBuilder
.create()
.loadTrustMaterial(null, (chains, authType) -> true)
.loadTrustMaterial(null, new TrustAllStrategy())
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is fine for tests but not production, the `TrustAllStrategy is unsafe

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is unrelated to this PR (sorry, i just snuck it in here, but at least as an atomic commit) - this commit doesn't change the behaviour at all, it's just making it more obvious what's happening here. there's #29 to improve this situation (but no clear idea on how to solve it yet).

.build();
} catch (final NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package liquibase.ext.opensearch.integration.spring;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@EnableConfigurationProperties(SpringLiquibaseOpenSearchProperties.class)
@ConditionalOnProperty("opensearch.uris")
public class LiquibaseOpenSearchAutoConfiguration {
Copy link
Collaborator

@reta reta Nov 22, 2024

Choose a reason for hiding this comment

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

I think we need to account for OpenSearch related beans (clients, etc) to be present (or not) in the context, I am happy to help you there (if it makes sense)

Copy link
Collaborator Author

@rursprung rursprung Nov 25, 2024

Choose a reason for hiding this comment

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

liquibase-opensearch already pulls in the opensearch client as a dependency, so the beans will always be here; thus i don't think that we need the conditional?
any help is welcome, of course! 👍 the faster we get this working the better for everyone

@Bean
public SpringLiquibaseOpenSearch getLiquibaseOpenSearch() {
return new SpringLiquibaseOpenSearch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package liquibase.ext.opensearch.integration.spring;

import liquibase.Scope;
import liquibase.UpdateSummaryOutputEnum;
import liquibase.command.CommandScope;
import liquibase.command.core.UpdateCommandStep;
import liquibase.command.core.helpers.DbUrlConnectionArgumentsCommandStep;
import liquibase.command.core.helpers.ShowSummaryArgument;
import liquibase.database.DatabaseFactory;
import liquibase.ext.opensearch.database.OpenSearchConnection;
import liquibase.ext.opensearch.database.OpenSearchLiquibaseDatabase;
import liquibase.integration.spring.SpringResourceAccessor;
import liquibase.ui.UIServiceEnum;
import lombok.Getter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;

import static liquibase.ext.opensearch.database.OpenSearchLiquibaseDatabase.OPENSEARCH_PREFIX;
import static liquibase.ext.opensearch.database.OpenSearchLiquibaseDatabase.OPENSEARCH_URI_SEPARATOR;

public class SpringLiquibaseOpenSearch implements InitializingBean {

@Autowired
private ResourceLoader resourceLoader;

@Autowired
private SpringLiquibaseOpenSearchProperties properties;

@Getter
protected UIServiceEnum uiService = UIServiceEnum.LOGGER;

@Override
public void afterPropertiesSet() throws Exception {
// liquibase requires the prefix to identify this as an OpenSearch database.
final var url = OPENSEARCH_PREFIX + String.join(OPENSEARCH_URI_SEPARATOR, properties.uris());

Scope.child(Scope.Attr.ui.name(), this.uiService.getUiServiceClass().getDeclaredConstructor().newInstance(),
() -> {
final var database = (OpenSearchLiquibaseDatabase) DatabaseFactory.getInstance().openDatabase(url, properties.username(), properties.password(), null, new SpringResourceAccessor(this.resourceLoader));
final var connection = (OpenSearchConnection) database.getConnection();
new CommandScope(UpdateCommandStep.COMMAND_NAME)
.addArgumentValue(ShowSummaryArgument.SHOW_SUMMARY_OUTPUT, UpdateSummaryOutputEnum.LOG)
.addArgumentValue(DbUrlConnectionArgumentsCommandStep.DATABASE_ARG, database)
.addArgumentValue(UpdateCommandStep.CHANGELOG_FILE_ARG, properties.liquibase().changelogFile())
.addArgumentValue(UpdateCommandStep.CONTEXTS_ARG, properties.liquibase().contexts())
.addArgumentValue(UpdateCommandStep.LABEL_FILTER_ARG, properties.liquibase().labelFilterArgs())
.execute();
connection.close();
database.close();
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package liquibase.ext.opensearch.integration.spring;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;

import java.util.List;

/**
* @param uris URL of the OpenSearch instances to use. If multiple URLs are listed they must all belong to the same cluster.
* @param username Username for authentication with OpenSearch.
* @param password Password for authentication with OpenSearch.
* @param liquibase Liquibase-specific properties.
*/
@ConfigurationProperties("opensearch")
public record SpringLiquibaseOpenSearchProperties(
@DefaultValue("http://localhost:9200")
List<String> uris,
String username,
String password,

@DefaultValue()
SpringLiquibaseProperties liquibase
) {
/**
* @param changelogFile Path to the liquibase changelog file (must be on the classpath).
* @param contexts Context filter to be used.
* @param labelFilterArgs Label filter to be used.
*/
public record SpringLiquibaseProperties(
@DefaultValue("db.changelog/db.changelog-master.yaml")
String changelogFile,

String contexts,

String labelFilterArgs
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
liquibase.ext.opensearch.integration.spring.LiquibaseOpenSearchAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public abstract class AbstractOpenSearchLiquibaseIT {
protected OpenSearchLiquibaseDatabase database;
protected OpenSearchConnection connection;

protected static final String OPENSEARCH_DOCKER_IMAGE_NAME = "opensearchproject/opensearch:2.18.0";
public static final String OPENSEARCH_DOCKER_IMAGE_NAME = "opensearchproject/opensearch:2.18.0";

@Container
protected OpensearchContainer<?> container = new OpensearchContainer<>(DockerImageName.parse(OPENSEARCH_DOCKER_IMAGE_NAME));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package liquibase.ext.opensearch.integration.spring;

import org.junit.jupiter.api.Test;
import org.opensearch.testcontainers.OpensearchContainer;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

import static liquibase.ext.opensearch.AbstractOpenSearchLiquibaseIT.OPENSEARCH_DOCKER_IMAGE_NAME;

@Testcontainers
@SpringBootTest
class SpringLiquibaseOpenSearchIT {

@Container
protected static OpensearchContainer<?> container = new OpensearchContainer<>(DockerImageName.parse(OPENSEARCH_DOCKER_IMAGE_NAME));

@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("opensearch.uris", container::getHttpHostAddress);
registry.add("opensearch.username", container::getUsername);
registry.add("opensearch.password", container::getPassword);
}

/**
* On context load liquibase is automatically being triggered (hard to test as we'd have to construct our own OpenSearch client).
*/
@Test
void contextLoads() {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package liquibase.ext.opensearch.integration.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringTestApplication {

public static void main(String[] args) {
SpringApplication.run(SpringTestApplication.class, args);
}

}
3 changes: 3 additions & 0 deletions src/test/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
spring.application.name=liquibase-opensearch-test

opensearch.liquibase.changelog-file=liquibase/ext/changelog.httprequest.yaml
Loading