Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -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).
*
* <p><strong>Effective ceiling differs by path</strong>, because both paths buffer payloads
* into a Java {@code byte[]}, whose maximum length is {@link Integer#MAX_VALUE} (~2 GB):
*
* <ul>
* <li><strong>HTTP download:</strong> The entire response is buffered in a single {@code
* byte[]}, so the effective limit is capped at {@code Integer.MAX_VALUE} (~2 GB).
* Configured values above that are silently clamped to {@code Integer.MAX_VALUE} on the
* download path.
* <li><strong>Decompressed tarball contents:</strong> The cumulative budget is tracked as a
* {@code long} and may exceed 2 GB. Only each individual entry is bounded by the {@code
* byte[]} ceiling (~2 GB per entry); the sum across all entries can be larger.
* </ul>
*/
@JsonProperty("max_size_bytes")
private long maxSizeBytes = DEFAULT_MAX_SIZE_BYTES;

public PollingConfig getPolling() {
return polling;
}
Expand Down Expand Up @@ -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{"
Expand All @@ -406,8 +369,6 @@ public String toString() {
+ ", resource='"
+ resource
+ '\''
+ ", maxSizeBytes="
+ maxSizeBytes
+ '}';
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@
protected String service;
protected String resource;
protected Config.PollingConfig polling;
protected Config.Trigger trigger = Config.Trigger.PERIODIC;

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Checkstyle & PMD

cannot find symbol

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Checkstyle & PMD

cannot find symbol

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Build All Modules

cannot find symbol

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Build All Modules

cannot find symbol

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (17)

cannot find symbol

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (17)

cannot find symbol

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (21)

cannot find symbol

Check failure on line 56 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (21)

cannot find symbol
protected String etag;
protected long lastModifiedTime = 0;
protected long maxSizeBytes = Config.BundleConfig.DEFAULT_MAX_SIZE_BYTES;

Check failure on line 59 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Checkstyle & PMD

cannot find symbol

Check failure on line 59 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Build All Modules

cannot find symbol

Check failure on line 59 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (17)

cannot find symbol

Check failure on line 59 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (21)

cannot find symbol

private static final Set<String> ALLOWED_CONTENT_TYPES =
Set.of(
Expand All @@ -66,6 +67,11 @@
"binary/octet-stream",
"application/x-tar");

public BundleDownloader setTrigger(Config.Trigger trigger) {

Check failure on line 70 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Checkstyle & PMD

cannot find symbol

Check failure on line 70 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Build All Modules

cannot find symbol

Check failure on line 70 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (17)

cannot find symbol

Check failure on line 70 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (21)

cannot find symbol
this.trigger = trigger;
return this;
}

/**
* Construct a BundleDownloader.
*
Expand Down Expand Up @@ -180,6 +186,13 @@
* @return a future that completes when the initial bundle is downloaded and activated
*/
public CompletableFuture<Void> startPolling(ScheduledExecutorService scheduler) {

if (trigger == Config.Trigger.MANUAL) {

Check failure on line 190 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Checkstyle & PMD

cannot find symbol

Check failure on line 190 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / Build All Modules

cannot find symbol

Check failure on line 190 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (17)

cannot find symbol

Check failure on line 190 in opa-services/src/main/java/io/github/open_policy_agent/opa/plugins/BundleDownloader.java

View workflow job for this annotation

GitHub Actions / test-opa-services (21)

cannot find symbol
manager.getLogger().info(
"Bundle '%s': Manual trigger mode enabled; waiting for refresh()", name);
return initialActivation;
}

int minDelay =
(polling != null && polling.getMinDelaySeconds() != null)
? polling.getMinDelaySeconds()
Expand All @@ -189,12 +202,19 @@
? polling.getMaxDelaySeconds()
: 120;



scheduler.schedule(this::downloadBundle, 0, TimeUnit.SECONDS);
scheduleNextPoll(scheduler, minDelay, maxDelay);

return initialActivation;
}

public CompletableFuture<Void> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
}

Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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 =
Expand Down
Loading