diff --git a/hawkbit-repository/hawkbit-repository-jpa-flyway/README.md b/hawkbit-repository/hawkbit-repository-jpa-flyway/README.md index 6b64d158d..c1db913b4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-flyway/README.md +++ b/hawkbit-repository/hawkbit-repository-jpa-flyway/README.md @@ -1,3 +1,3 @@ # hawkBit JPA Flyway migration -JPA Flyway migrations scrypts \ No newline at end of file +JPA Flyway migrations scripts \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa-init/README.md b/hawkbit-repository/hawkbit-repository-jpa-init/README.md new file mode 100644 index 000000000..ff8345077 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa-init/README.md @@ -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.\_ or _ 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 ( mysql)>.sql) + +Where: +1. Environment properties takes precedence over the system properties +2. The _hawkbit.db.\_ properties take precedence over the _spring.database.\_ 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-\-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) \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa-init/pom.xml b/hawkbit-repository/hawkbit-repository-jpa-init/pom.xml new file mode 100644 index 000000000..8c2776f5d --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa-init/pom.xml @@ -0,0 +1,130 @@ + + + 4.0.0 + + org.eclipse.hawkbit + ${revision} + hawkbit-repository + + + hawkbit-repository-jpa-init + hawkBit :: Repository :: JPA DB Init / Upgrade App + + + + + org.flywaydb + flyway-core + + + + + com.h2database + h2 + + + org.postgresql + postgresql + + + org.mariadb.jdbc + mariadb-java-client + + + + + org.flywaydb + flyway-database-postgresql + + + org.flywaydb + flyway-mysql + + + + + + ch.qos.logback + logback-classic + ${logback.version} + + + ch.qos.logback + logback-core + ${logback.version} + + + + org.eclipse.hawkbit + hawkbit-repository-jpa-flyway + ${project.version} + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-autoconfigure + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + default-jar + + none + + _ + _ + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + + org.eclipse.hawkbit.repository.jpa.init.HawkbitFlywayDbInit + + false + + + + src/assembly/exec.xml + + false + + + + + + + \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa-init/src/assembly/exec.xml b/hawkbit-repository/hawkbit-repository-jpa-init/src/assembly/exec.xml new file mode 100644 index 000000000..df672630a --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa-init/src/assembly/exec.xml @@ -0,0 +1,33 @@ + + + exec + + jar + + false + + + metaInf-services + + + + + / + true + true + runtime + + + \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa-init/src/main/java/org/eclipse/hawkbit/repository/jpa/init/HawkbitFlywayDbInit.java b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/java/org/eclipse/hawkbit/repository/jpa/init/HawkbitFlywayDbInit.java new file mode 100644 index 000000000..985999505 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/java/org/eclipse/hawkbit/repository/jpa/init/HawkbitFlywayDbInit.java @@ -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: + *
    + *
  • 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. + *
  3. The "hawkbit.db.*" properties take precedence over the "spring.database.*" properties
  4. + *
+ * + * 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 {@link org.flywaydb.core.api.exception.FlywayValidateException} if not in sync
  • + *
+ * 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:(?\\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; + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa-init/src/main/resources/logback.xml b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/resources/logback.xml new file mode 100644 index 000000000..87af64287 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa-init/src/main/resources/logback.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/hawkbit-jpa-defaults.properties b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/hawkbit-jpa-defaults.properties index b00c9d560..e5d66e965 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/hawkbit-jpa-defaults.properties +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/hawkbit-jpa-defaults.properties @@ -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 diff --git a/hawkbit-repository/pom.xml b/hawkbit-repository/pom.xml index c8ad17eeb..e10d83525 100644 --- a/hawkbit-repository/pom.xml +++ b/hawkbit-repository/pom.xml @@ -31,6 +31,8 @@ hawkbit-repository-jpa hawkbit-repository-jpa-flyway + hawkbit-repository-jpa-init + hawkbit-repository-test \ No newline at end of file