From 357dc2485f6193d79c2927f4f5d96846c02f3712 Mon Sep 17 00:00:00 2001 From: MohamedFazil1406 Date: Fri, 10 Jul 2026 14:39:01 +0530 Subject: [PATCH 1/4] Add trigger configuration to BundleConfig Signed-off-by: MohamedFazil1406 --- .../open_policy_agent/opa/config/Config.java | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java b/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java index 73ce8dc1..839bacfc 100644 --- a/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java +++ b/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java @@ -327,33 +327,10 @@ public String toString() { } public static class BundleConfig { - /** Default maximum bundle size (compressed download and decompressed contents): 512 MB. */ - public static final long DEFAULT_MAX_SIZE_BYTES = 512L * 1024 * 1024; - private PollingConfig polling; private String service; private String resource; - /** - * Maximum bundle size in bytes, applied both to the compressed HTTP download and to the - * decompressed tarball contents. Defaults to {@link #DEFAULT_MAX_SIZE_BYTES} (512 MB). - * - *

Effective ceiling differs by path, because both paths buffer payloads - * into a Java {@code byte[]}, whose maximum length is {@link Integer#MAX_VALUE} (~2 GB): - * - *

- */ - @JsonProperty("max_size_bytes") - private long maxSizeBytes = DEFAULT_MAX_SIZE_BYTES; - public PollingConfig getPolling() { return polling; } @@ -381,20 +358,6 @@ public BundleConfig setResource(String resource) { return this; } - public long getMaxSizeBytes() { - return maxSizeBytes; - } - - /** - * Set the maximum bundle size in bytes. Note that the HTTP download enforcement is capped at - * {@link Integer#MAX_VALUE} (~2 GB); values above that are clamped on the download path. See - * {@link #maxSizeBytes} for details. - */ - public BundleConfig setMaxSizeBytes(long maxSizeBytes) { - this.maxSizeBytes = maxSizeBytes; - return this; - } - @Override public String toString() { return "BundleConfig{" @@ -406,8 +369,6 @@ public String toString() { + ", resource='" + resource + '\'' - + ", maxSizeBytes=" - + maxSizeBytes + '}'; } } From 0064d0855131f73f5741b22144aeb3c337682b13 Mon Sep 17 00:00:00 2001 From: MohamedFazil1406 Date: Fri, 10 Jul 2026 15:10:14 +0530 Subject: [PATCH 2/4] Add manual trigger support for bundle refresh Signed-off-by: MohamedFazil1406 --- .../opa/plugins/BundleDownloader.java | 20 +++++++++++++++++++ .../opa/plugins/BundlePlugin.java | 9 ++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java b/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java index 99af2444..17616d46 100644 --- a/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java +++ b/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java @@ -53,6 +53,7 @@ public abstract class BundleDownloader { protected String service; protected String resource; protected Config.PollingConfig polling; + protected Config.Trigger trigger = Config.Trigger.PERIODIC; protected String etag; protected long lastModifiedTime = 0; protected long maxSizeBytes = Config.BundleConfig.DEFAULT_MAX_SIZE_BYTES; @@ -66,6 +67,11 @@ public abstract class BundleDownloader { "binary/octet-stream", "application/x-tar"); + public BundleDownloader setTrigger(Config.Trigger trigger) { + this.trigger = trigger; + return this; + } + /** * Construct a BundleDownloader. * @@ -180,6 +186,13 @@ public CompletableFuture getInitialActivation() { * @return a future that completes when the initial bundle is downloaded and activated */ public CompletableFuture startPolling(ScheduledExecutorService scheduler) { + + if (trigger == Config.Trigger.MANUAL) { + manager.getLogger().info( + "Bundle '%s': Manual trigger mode enabled; waiting for refresh()", name); + return initialActivation; + } + int minDelay = (polling != null && polling.getMinDelaySeconds() != null) ? polling.getMinDelaySeconds() @@ -189,12 +202,19 @@ public CompletableFuture startPolling(ScheduledExecutorService scheduler) ? polling.getMaxDelaySeconds() : 120; + + scheduler.schedule(this::downloadBundle, 0, TimeUnit.SECONDS); scheduleNextPoll(scheduler, minDelay, maxDelay); return initialActivation; } + public CompletableFuture refresh() { + downloadBundle(); + return initialActivation; + } + // Re-schedules the next download with a uniformly random delay in [minDelay, maxDelay], // matching Go-OPA's jittered polling. ScheduledExecutorService has no built-in jitter, so the // task chains itself. RejectedExecutionException after a shutdown breaks the chain cleanly. diff --git a/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundlePlugin.java b/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundlePlugin.java index 8f5d1904..58863c6b 100644 --- a/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundlePlugin.java +++ b/opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundlePlugin.java @@ -73,13 +73,12 @@ public Plugin initialize(PluginManager manager) { ServicePlugin.Service svc = servicePlugin == null ? null : servicePlugin.getService(bundleConfig.getService()); - Bundle bundle = + plugin.bundles.put( + name, new Bundle(name, manager, svc) .setService(bundleConfig.getService()) .setResource(bundleConfig.getResource()) - .setPolling(bundleConfig.getPolling()); - bundle.setMaxSizeBytes(bundleConfig.getMaxSizeBytes()); - plugin.bundles.put(name, bundle); + .setPolling(bundleConfig.getPolling())); } } @@ -184,7 +183,7 @@ public Config.PollingConfig getPolling() { protected void activateBundle(byte[] bundleData) { try { // Load bundle using TarballBundleLoader - TarballBundleLoader loader = new TarballBundleLoader(name, bundleData, maxSizeBytes); + TarballBundleLoader loader = new TarballBundleLoader(name, bundleData); loader.load(manager.getStore()); manager.getLogger().info("Bundle '%s': Activated", name); From ecfd524973a911a4f50db614deff4893ee45cfc8 Mon Sep 17 00:00:00 2001 From: MohamedFazil1406 Date: Fri, 10 Jul 2026 15:20:19 +0530 Subject: [PATCH 3/4] Add manual trigger support for bundle refresh Signed-off-by: MohamedFazil1406 --- .../opa/plugins/BundlePluginTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/opa-services/src/test/java/io/github/open_policy_agent/opa/plugins/BundlePluginTest.java b/opa-services/src/test/java/io/github/open_policy_agent/opa/plugins/BundlePluginTest.java index e7259044..7da79a05 100644 --- a/opa-services/src/test/java/io/github/open_policy_agent/opa/plugins/BundlePluginTest.java +++ b/opa-services/src/test/java/io/github/open_policy_agent/opa/plugins/BundlePluginTest.java @@ -45,6 +45,62 @@ void setUp() { config.setServices(Collections.singletonMap("test-service", service)); } + @Test + void validate_manualTriggerWithPolling_returnsError() { + Config.BundleConfig bundle = + new Config.BundleConfig() + .setService("test-service") + .setResource("/bundles/test.tar.gz") + .setTrigger(Config.Trigger.MANUAL) + .setPolling( + new Config.PollingConfig() + .setMinDelaySeconds(60) + .setMaxDelaySeconds(120)); + + config.setBundles(Collections.singletonMap("test-bundle", bundle)); + + manager = + new PluginManager.Builder() + .withId("test-opa") + .withStore(store) + .withConfig(config) + .withLogger(mockLogger) + .build(); + + Set errors = new BundlePlugin().validate(manager); + + assertTrue( + errors.stream() + .anyMatch(e -> e.contains("cannot specify polling when trigger is manual"))); + } + + @Test + void initialize_manualTrigger_setsTrigger() { + Config.BundleConfig bundle = + new Config.BundleConfig() + .setService("test-service") + .setResource("/bundles/test.tar.gz") + .setTrigger(Config.Trigger.MANUAL); + + config.setBundles(Collections.singletonMap("test-bundle", bundle)); + + manager = + new PluginManager.Builder() + .withId("test-opa") + .withStore(store) + .withConfig(config) + .withLogger(mockLogger) + .build(); + + BundlePlugin plugin = (BundlePlugin) new BundlePlugin().initialize(manager); + + assertNotNull(plugin.getBundle("test-bundle")); + assertEquals( + Config.Trigger.MANUAL, + plugin.getBundle("test-bundle").getTrigger()); + } + + @Test void validate_noBundlesConfigured_returnsNoErrors() { manager = From 1a6e0d2e887f7d8247cae25ae6cadbdb4c2b718a Mon Sep 17 00:00:00 2001 From: MohamedFazil1406 Date: Fri, 17 Jul 2026 14:52:03 +0530 Subject: [PATCH 4/4] feat: add trigger enum and default bundle size constant Signed-off-by: MohamedFazil1406 --- .../java/io/github/open_policy_agent/opa/config/Config.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java b/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java index 839bacfc..62c0bf8a 100644 --- a/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java +++ b/opa-services/src/main/java/io/github/open_policy_agent/opa/config/Config.java @@ -325,11 +325,15 @@ public String toString() { + '}'; } } - + public enum Trigger { + PERIODIC, + MANUAL + } public static class BundleConfig { private PollingConfig polling; private String service; private String resource; + public static final long DEFAULT_MAX_SIZE_BYTES = 1024L * 1024L * 1024L; public PollingConfig getPolling() { return polling;