Standalone tool for initializing of hawkBit Database (#2369)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-04-24 16:27:28 +03:00
committed by GitHub
parent 6e24b6c33c
commit df398c9a74
8 changed files with 361 additions and 2 deletions

View File

@@ -1,3 +1,3 @@
# hawkBit JPA Flyway migration
JPA Flyway migrations scrypts
JPA Flyway migrations scripts

View File

@@ -0,0 +1,52 @@
# hawkBit JPA Initializer & Migrator
A standalone tool for validating and migrating the database to the current hawkBit schema. It is used to validate, initialize or migrate the database to the current hawkBit schema.
## Configuration
Could be configured with _hawkbit.db.\<key\>_ or <spring.database.\<key\>_ environment or system properties, with keys:
* **mode** - migrate or validate (default)
* **url** - database url
* **username** - database user - shall have the necessary permissions
* **password** - database user's password
* **sql-migration-suffixes** - flyway 'sqlMigrationSuffixes' if not the default ones (<upper case database (mariadb -> mysql)>.sql)
Where:
1. Environment properties takes precedence over the system properties
2. The _hawkbit.db.\<key\>_ properties take precedence over the _spring.database.\<key\>_ properties
There are two modes:
* **migrate** - migrate the database, only when started with parameter with key mode (environment or system property)
* **validate** - validate the database, default, only validates db and throws org.flywaydb.core.api.exception.FlywayValidateException if not in sync
**Note**: could also be configured using default flyway env properties
## Usage
The module builds executable jar with all dependencies - _hawkbit-repository-jpa-init-\<revision\>-exec.jar_. It could be configured with environment properties and run as an executable jar:
```shell
# sets the mode - default if validate
export hawkbit_db_mode=migrate
# sets the database url - default is local h2
export hawkbit_db_url=jdbc:mariadb://localhost:3306/hawkbit
# sets the database user - default is h2 default root - sa
export hawkbit_db_username=root
# sets the database user's password - default is empty
#export hawkbit_db_password=
# run executable jar
java -jar target/hawkbit-repository-jpa-init-0-SNAPSHOT-exec.jar
```
It could also be configured using system properties and run as a java main class:
```shell
java -classpath target/hawkbit-repository-jpa-init-0-SNAPSHOT-exec.jar \
-Dhawkbit.db.mode=migrate \
-Dhawkbit.db.url=jdbc:mariadb://localhost:3306/hawkbit \
-Dhawkbit.db.username=root \
-Dhawkbit.db.password= \
org.eclipse.hawkbit.repository.jpa.init.HawkbitFlywayDbInit
```
## Purpose and usecases
If you want to do db management separately from the running services - i.e. not in mgmt-server or monolith (for instance) servers but only occasionally and on real updates, or you like to validate a database according to a hawkbit db schema version you could:
1. set spring.flyway.enabled=false in the hawkbit services (or do not pack the hawkbit-repository-jpa-flyway module into the hawkbit services)
2. use the tool to do the db management (e.g. in some pipelines)

View File

@@ -0,0 +1,130 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<version>${revision}</version>
<artifactId>hawkbit-repository</artifactId>
</parent>
<artifactId>hawkbit-repository-jpa-init</artifactId>
<name>hawkBit :: Repository :: JPA DB Init / Upgrade App</name>
<dependencies>
<!-- Flyway to init and manage DB migrations -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!-- Database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
<!-- h2 flyway is supported by the flyway-core dependency -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
<!-- Database END -->
<!-- Logging -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-jpa-flyway</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>default-jar</id>
<!-- not existing phase - don't build jar -->
<phase>none</phase>
<configuration>
<finalName>_</finalName>
<classifier>_</classifier>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>
org.eclipse.hawkbit.repository.jpa.init.HawkbitFlywayDbInit
</mainClass>
<addClasspath>false</addClasspath>
</manifest>
</archive>
<descriptors>
<descriptor>src/assembly/exec.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,33 @@
<!--
Copyright (c) 2025 Contributors to the Eclipse Foundation
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
-->
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd">
<id>exec</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<containerDescriptorHandlers>
<containerDescriptorHandler>
<handlerName>metaInf-services</handlerName>
</containerDescriptorHandler>
</containerDescriptorHandlers>
<dependencySets>
<dependencySet>
<outputDirectory>/</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>true</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.init;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.flywaydb.core.Flyway;
/**
* hawkBit Flyway db init configuration. Could be configured with "hawkbit.db.*" or "spring.database.*" environment or system properties,
* with keys:
* <ul>
* <li>mode: <code>migrate</code> or <code>validate</code> (default)</li>
* <li>url: database url</li>
* <li>username: database user - shall have the necessary permissions</li>
* <li>password: database user's password</li>
* <li>sql-migration-suffixes: flyway 'sqlMigrationSuffixes' if not the default ones (&lt;upper case database (mariadb -> mysql)&gt;.sql) </li>
* </ul>
*
* Where:
* <ol>
* <li>Environment properties takes precedence over the system properties</li>
* <li>The "hawkbit.db.*" properties take precedence over the "spring.database.*" properties</li>
* </ol>
*
* There are two modes:
* <ul>
* <li>migrate: migrate the database, only when started with parameter with key <code>mode</code> (environment or system property)</li>
* <li>validate: validate the database, default, only validates db and throws {@link org.flywaydb.core.api.exception.FlywayValidateException} if not in sync</li>
* </ul>
* Note: could also be configured using default flyway env properties
*/
public class HawkbitFlywayDbInit {
public static final String MIGRATE = "migrate";
public static final String URL = prop("url", "jdbc:h2:mem:hawkbit;MODE=LEGACY;");
public static final String USER = prop("username", "sa");
public static final String PASSWORD = prop("password", "");
public static final String MODE = prop("mode", "validate");
public static void main(final String[] args) {
final Flyway flyway = Flyway.configure()
.dataSource(URL, USER, PASSWORD)
.cleanDisabled(true)
.table("schema_version")
.sqlMigrationSuffixes(prop("sql-migration-suffixes", suffix(URL)) + ".sql")
.validateOnMigrate(true)
.envVars()
.load();
if (MODE.equals(MIGRATE)) {
flyway.migrate();
} else {
flyway.validate();
}
}
private static String prop(final String key, final String defaultValue) {
String value = env(key);
if (value == null) {
value = sys(key);
}
if (value == null) {
value = defaultValue;
}
return value;
}
private static String env(final String key) {
final String value = env0("hawkbit.db." + key);
return value == null ? env0("spring.datasource." + key) : value;
}
private static String env0(final String property) {
String value = System.getenv(property);
if (value == null) {
value = System.getenv(property.toUpperCase());
}
if (value == null) {
value = System.getenv(property.replace('.', '_'));
}
if (value == null) {
value = System.getenv(property.replace('.', '_').toUpperCase());
}
return value;
}
private static String sys(final String key) {
return System.getProperty("hawkbit.db." + key, System.getProperty("spring.datasource." + key));
}
private static final Pattern PATTERN = Pattern.compile("jdbc:(?<database>\\w+):(\\\\)?.*", Pattern.CASE_INSENSITIVE);
private static String suffix(final String url) {
final Matcher matcher = PATTERN.matcher(url);
if (!matcher.matches()) {
throw new IllegalStateException("Invalid db url: " + url);
}
final String dbUpperCase = matcher.group("database").toUpperCase();
return "MARIADB".equals(dbUpperCase) ? "MYSQL" : dbUpperCase;
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright (c) 2025 Contributors to the Eclipse Foundation
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
-->
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<logger name="org.flywaydb.core.internal.command.DbValidate" level="DEBUG"/>
<logger name="org.flywaydb.core.internal.command.DbMigrate" level="DEBUG"/>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

View File

@@ -15,7 +15,7 @@ spring.jpa.database=H2
spring.jpa.show-sql=false
# need to use legacy mode for now until we can upgrade EclipseLink
# (see details: https://github.com/eclipse-ee4j/eclipselink/issues/1393)
spring.datasource.url=jdbc:h2:mem:testdb;MODE=LEGACY;
spring.datasource.url=jdbc:h2:mem:hawkbit;MODE=LEGACY;
# Logging
spring.jpa.properties.eclipselink.logging.level=off
# Cluster aware

View File

@@ -31,6 +31,8 @@
<module>hawkbit-repository-jpa</module>
<module>hawkbit-repository-jpa-flyway</module>
<module>hawkbit-repository-jpa-init</module>
<module>hawkbit-repository-test</module>
</modules>
</project>