Skip to content

Commit 9f14b1a

Browse files
fix(#1205): add PRIMARY_DB to native image build (#1206)
Co-authored-by: Derek Roberts <derek.roberts@gmail.com>
1 parent 0e4e994 commit 9f14b1a

File tree

10 files changed

+270
-34
lines changed

10 files changed

+270
-34
lines changed

.github/workflows/.deploy.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ on:
4848
description: Primary database for backend
4949
default: "oracle"
5050
type: string
51-
51+
deployment_model:
52+
description: Deployment model (hybrid or postgres)
53+
default: "hybrid"
54+
type: string
5255
# FAM route: only used for PR deploys, not needed for test/prod since those use static routes
5356
fam_route_number:
5457
description: FAM route for frontend; only needed for PR deployments
@@ -64,7 +67,7 @@ permissions: {}
6467

6568
jobs:
6669
init:
67-
name: Deploy (init) - ${{ inputs.backend_primary_db }}
70+
name: Deploy (init) - ${{ inputs.deployment_model }}
6871
environment: ${{ inputs.environment }}
6972
outputs:
7073
allowed_origins: ${{ steps.allowed_origins.outputs.allowed_origins }}
@@ -115,7 +118,7 @@ jobs:
115118
triggers: ${{ inputs.triggers }}
116119

117120
deploy:
118-
name: Deploy (${{ matrix.name }} - ${{ inputs.backend_primary_db }})
121+
name: Deploy (${{ matrix.name }}) - ${{ inputs.deployment_model }}
119122
environment: ${{ inputs.environment }}
120123
needs: [init]
121124
runs-on: ubuntu-24.04
@@ -140,6 +143,7 @@ jobs:
140143
-p MIN_REPLICAS=${{ inputs.backend_min_replicas }}
141144
-p MAX_REPLICAS=${{ inputs.backend_max_replicas }}
142145
-p PRIMARY_DB=${{ inputs.backend_primary_db }}
146+
-p BACKEND_PACKAGE_IMAGE=${{ (inputs.deployment_model == 'hybrid' && 'backend') || 'backend-postgres' }}
143147
verification_path: /actuator/health
144148
- name: frontend
145149
file: frontend/openshift.deploy.yml

.github/workflows/pr-close.yml

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
steps:
1919
- uses: actions/checkout@v6
2020
- name: Remove FAM route allocations for PR
21-
uses: bcgov/action-oc-runner@v1.4.0
21+
uses: bcgov/action-oc-runner@57a28c38359c93e43edf609d35b9a3f50a070131 # v1.4.0
2222
env:
2323
PR_NUMBER: ${{ github.event.number }}
2424
with:
@@ -66,22 +66,14 @@ jobs:
6666
fi
6767
6868
cleanup:
69-
name: Cleanup and Images - ${{ matrix.name }}
70-
uses: bcgov/quickstart-openshift-helpers/.github/workflows/.pr-close.yml@6d695dd755fa9255ea4bde335890516beb6f95e4 # v1.0.1
71-
strategy:
72-
matrix:
73-
model: [hybrid, postgres]
74-
include:
75-
- model: hybrid
76-
name: "Hybrid (Oracle Primary)"
77-
- model: postgres
78-
name: "Postgres"
69+
name: Cleanup and Images
70+
uses: bcgov/quickstart-openshift-helpers/.github/workflows/.pr-close.yml@ca6579bdadf383a2513397081f963889b92823a4 # v1.1.0
7971
permissions:
8072
packages: write
8173
secrets:
8274
oc_namespace: ${{ vars.OC_NAMESPACE }}
8375
oc_token: ${{ secrets.OC_TOKEN }}
8476
with:
8577
cleanup: label
86-
packages: backend frontend
87-
target: ${{ github.event.number }}-${{ matrix.model }}
78+
packages: backend backend-postgres frontend
79+
cleanup_name: ${{ github.event.repository.name }}-${{ github.event.number }}-hybrid ${{ github.event.repository.name }}-${{ github.event.number }}-postgres

.github/workflows/pr-open.yml

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,34 @@ jobs:
2020
packages: write
2121
strategy:
2222
matrix:
23-
name: [backend, frontend]
23+
package: [backend, backend-postgres, frontend]
2424
include:
25-
- name: backend
25+
- package: backend
26+
build_context: backend
27+
extra_build_arg: PRIMARY_DB=oracle
2628
triggers: ('backend/')
27-
- name: frontend
29+
- package: backend-postgres
30+
build_context: backend
31+
extra_build_arg: PRIMARY_DB=postgres
32+
triggers: ('backend/')
33+
- package: frontend
34+
build_context: frontend
35+
extra_build_arg: ''
2836
triggers: ('frontend/')
2937
steps:
3038
- uses: actions/checkout@v6
3139
- uses: bcgov/action-builder-ghcr@2b24ac7f95e6a019064151498660437cca3202c5 # v4.2.1
3240
with:
33-
package: ${{ matrix.name }}
34-
tag_fallback: latest
41+
package: ${{ matrix.package }}
42+
build_context: ${{ matrix.build_context }}
43+
build_file: ${{ matrix.build_context }}/Dockerfile
3544
token: ${{ secrets.GITHUB_TOKEN }}
3645
triggers: ${{ matrix.triggers }}
46+
tags: ${{ github.event.number }}
47+
tag_fallback: latest
48+
build_args: |
49+
BUILDKIT_INLINE_CACHE=1
50+
${{ matrix.extra_build_arg }}
3751
3852
fam_routes:
3953
name: Allocate FAM Routes
@@ -64,10 +78,10 @@ jobs:
6478
include:
6579
- primary_db: oracle
6680
fam_route_number: ${{ needs.fam_routes.outputs.hybrid_route_number }}
67-
instance_name: hybrid
81+
deployment_model: hybrid
6882
- primary_db: postgres
6983
fam_route_number: ${{ needs.fam_routes.outputs.postgres_route_number }}
70-
instance_name: postgres
84+
deployment_model: postgres
7185
secrets: inherit
7286
uses: ./.github/workflows/.deploy.yml
7387
with:
@@ -76,7 +90,8 @@ jobs:
7690
backend_max_replicas: "1"
7791
backend_primary_db: "${{ matrix.primary_db }}"
7892
fam_route_number: "${{ matrix.fam_route_number }}"
79-
target: "${{ github.event.number }}-${{ matrix.instance_name }}"
93+
target: "${{ github.event.number }}-${{ matrix.deployment_model }}"
94+
deployment_model: ${{ matrix.deployment_model }}
8095
triggers: ''
8196

8297
cleanup_configmap:

backend/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
### Builder
22
FROM ghcr.io/graalvm/native-image:22.3.3 AS build
33

4+
ARG PRIMARY_DB=oracle
5+
ENV PRIMARY_DB=${PRIMARY_DB}
6+
47
# Copy
58
WORKDIR /app
69
COPY pom.xml mvnw ./

backend/openshift.deploy.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ parameters:
8484
- name: PRIMARY_DB
8585
description: The primary database to use (oracle or postgres)
8686
value: "oracle"
87+
- name: BACKEND_PACKAGE_IMAGE
88+
description: Backend package to pull from registry
89+
value: backend
8790
objects:
8891
- kind: PersistentVolumeClaim
8992
apiVersion: v1
@@ -143,7 +146,7 @@ objects:
143146
memory: ${MEMORY_REQUEST}
144147
containers:
145148
- name: ${NAME}-${ZONE}-${COMPONENT}
146-
image: ${REGISTRY}/${ORG}/${NAME}/${COMPONENT}:${TAG}
149+
image: ${REGISTRY}/${ORG}/${NAME}/${BACKEND_PACKAGE_IMAGE}:${TAG}
147150
imagePullPolicy: Always
148151
volumeMounts:
149152
- name: ${NAME}-${ZONE}-certs
Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,61 @@
11
package ca.bc.gov.restapi.results.common.configuration;
22

3+
import java.util.ArrayList;
4+
import java.util.List;
5+
36
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer;
7+
import org.springframework.context.ApplicationContext;
48
import org.springframework.context.annotation.Bean;
59
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.context.annotation.ImportRuntimeHints;
611
import org.springframework.core.env.Environment;
712

13+
import lombok.extern.slf4j.Slf4j;
14+
15+
/**
16+
* Configures Flyway migration locations based on the active primary database.
17+
*
18+
* <p>In GraalVM native image, @ConditionalOnProperty is evaluated at AOT build time (not
19+
* runtime), so the PRIMARY_DB value must be set as a Docker build arg so Spring Boot AOT bakes
20+
* the correct beans into the binary. This class reads PRIMARY_DB at runtime (via
21+
* Environment.getProperty) which is safe — it's a plain value lookup, not a conditional.
22+
*
23+
* <p>Flyway's ClassPathScanner fails in native image with "unsupported protocol: resource".
24+
* {@link FlywayRuntimeHints} pre-registers migration resources with Spring Boot's AOT processor,
25+
* and {@link NativeImageResourceProvider} replaces Flyway's scanner with Spring's
26+
* ApplicationContext.getResources() which uses the AOT-generated resource index.
27+
*/
28+
@Slf4j
829
@Configuration
30+
@ImportRuntimeHints(FlywayRuntimeHints.class)
931
public class FlywayConfiguration {
1032

1133
@Bean
12-
public FlywayConfigurationCustomizer flywayConfigurationCustomizer(Environment env) {
34+
public FlywayConfigurationCustomizer flywayConfigurationCustomizer(
35+
Environment env, ApplicationContext context) {
1336
return configuration -> {
14-
String primaryDatabase = env.getProperty("server.primary-db",
15-
env.getProperty("PRIMARY_DB", "oracle"));
16-
if (primaryDatabase.equals("oracle")) {
17-
configuration.locations("classpath:db/migration");
18-
} else if (primaryDatabase.equals("postgres")) {
19-
configuration.locations("classpath:db/migration", "classpath:db/migration-dev");
20-
} else {
37+
38+
String primaryDb = env.getProperty("PRIMARY_DB",
39+
env.getProperty("server.primary-db", "oracle"));
40+
log.info("Configuring Flyway for primary database: {}", primaryDb);
41+
42+
List<String> locations = new ArrayList<>();
43+
locations.add("classpath:db/migration");
44+
45+
if ("postgres".equals(primaryDb)) {
46+
locations.add("classpath:db/migration-dev");
47+
} else if (!"oracle".equals(primaryDb)) {
2148
throw new IllegalStateException("Unsupported value for primary database configuration " +
2249
"(property 'server.primary-db' or env var 'PRIMARY_DB'): '" +
23-
primaryDatabase + "'. Expected one of: oracle, postgres."
50+
primaryDb + "'. Expected one of: oracle, postgres."
2451
);
2552
}
53+
54+
configuration
55+
.schemas("silva")
56+
.defaultSchema("silva")
57+
.locations(locations.toArray(String[]::new))
58+
.resourceProvider(new NativeImageResourceProvider(context, locations));
2659
};
2760
}
2861
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package ca.bc.gov.restapi.results.common.configuration;
2+
3+
import org.springframework.aot.hint.RuntimeHints;
4+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
5+
import org.springframework.lang.NonNull;
6+
import org.springframework.lang.Nullable;
7+
8+
/**
9+
* Registers Flyway SQL migration resources with Spring Boot's native image AOT processor.
10+
*
11+
* <p>In GraalVM native image, Flyway's {@code ClassPathScanner} fails with
12+
* "unsupported protocol: resource" because it uses {@code ClassLoader.getResources(directory)}
13+
* for directory enumeration, which is not supported in native image. Spring Boot 3.x AOT
14+
* processes these hints at build time, creating a resource index that makes
15+
* {@code ApplicationContext.getResources(pattern)} work correctly at runtime.
16+
*
17+
* <p>This is used together with {@link NativeImageResourceProvider} which replaces Flyway's
18+
* scanner with Spring's resource loading.
19+
*/
20+
public class FlywayRuntimeHints implements RuntimeHintsRegistrar {
21+
22+
@Override
23+
public void registerHints(@NonNull RuntimeHints hints, @Nullable ClassLoader classLoader) {
24+
hints.resources().registerPattern("db/migration/*.sql");
25+
hints.resources().registerPattern("db/migration-dev/*.sql");
26+
}
27+
}

0 commit comments

Comments
 (0)