Skip to content

Conversation

bentsherman
Copy link
Member

Fix #5494 #5943

This PR fixes support for plugin secrets (i.e. AWS secrets) in the config by loading the config before resolving plugins. If the config attempts to use secrets, it will be reloaded with the complete set of secrets providers.

This is possible because plugin config must be static, unlike other config which can be dynamic and reference secrets. Therefore, we can simply load the config to extract the list of plugins (and other config settings like tower.enabled and process.executor which can trigger additional core plugins.

To minimize the impact of loading the config twice, we only reload the config if we detect that secrets were used by the config on the first load. This way, the double config load should only affect users who want to use secrets in the config.

In summary:

  1. Load config with "shim" secrets provider which only tracks whether secrets are used by config
  2. Load plugins
  3. Load secrets provider
  4. Reload config if secrets were used in config

Copy link

netlify bot commented Jul 3, 2025

Deploy Preview for nextflow-docs-staging canceled.

Name Link
🔨 Latest commit e732f34
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-docs-staging/deploys/68c6ea5df16b300008d877ee

@bentsherman bentsherman linked an issue Jul 3, 2025 that may be closed by this pull request
@bentsherman
Copy link
Member Author

Here is how I've been testing:

  1. Build with make pack
  2. Upload dist binary to s3
  3. Launch this pipeline in platform: https://github.com/bentsherman/nf-tests/tree/plugin-secrets
  4. Download dist binary from s3 and replace default nextflow in pre-run script

However, it's not loading the xpack-amzn plugin:

Jul-03 19:06:49.448 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins resolved requirement=[[email protected], [email protected], [email protected], [email protected]]

It's at least loading nf-amazon since the workDir is s3, but I can't remember how to trigger the xpack plugin

@bentsherman
Copy link
Member Author

I wonder if the issue is that I'm using the dist release instead of the standard release

@pditommaso pditommaso marked this pull request as draft July 6, 2025 13:34
Copy link
Member

@pditommaso pditommaso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks smart. Moving to draft until the problem with the plugins is addressed. Also unit test and integration test should be added (it may be convenience setup a test GH repo for this)

@bentsherman bentsherman marked this pull request as ready for review July 7, 2025 16:49
@bentsherman
Copy link
Member Author

It turns out the issue was that I was using the single-VM CE, which doesn't support AWS secrets. It works with AWS Batch.

Regarding testing, I don't think we'll need a git repo unless you want to do an e2e test with platform. I'll add a local e2e test that just tests the detection of config secrets -> config reloading.

@bentsherman
Copy link
Member Author

One minor issue with this double-loading approach is that you can't use secrets in a config include (#5312). On the first load, the secret will be null and the include will likely fail, which means the run will fail

However, you should be able to work around this using a dynamic include:

includeConfig secrets.GITHUB_TOKEN
    ? "https://raw.githubusercontent.com/markpanganiban/nf-test/master/ubuntu.config?token=${secrets.GITHUB_TOKEN}"
    : '/dev/null'

On the first load, it will access the secret, triggering a reload, but won't attempt to include the secured config. On the second load it will include the config.

I will add this to the docs on config secrets

@bentsherman bentsherman force-pushed the fix-plugin-secrets-config branch from e8f25b9 to 5d5ef8a Compare July 7, 2025 17:39
@bentsherman bentsherman requested a review from a team as a code owner July 7, 2025 17:39
@bentsherman bentsherman force-pushed the fix-plugin-secrets-config branch from 5d5ef8a to f03a097 Compare July 10, 2025 17:18
@bentsherman

This comment was marked as resolved.

@bentsherman bentsherman linked an issue Jul 10, 2025 that may be closed by this pull request
@bentsherman bentsherman requested a review from pditommaso July 14, 2025 14:12
@bentsherman bentsherman added this to the 25.10 milestone Jul 14, 2025
@pditommaso

This comment was marked as resolved.

@bentsherman

This comment was marked as resolved.

@bentsherman bentsherman force-pushed the fix-plugin-secrets-config branch from dd8a536 to 3cb54c2 Compare July 17, 2025 22:29
@bentsherman bentsherman force-pushed the fix-plugin-secrets-config branch from 3cb54c2 to 2012d05 Compare July 28, 2025 15:29
@bentsherman bentsherman force-pushed the fix-plugin-secrets-config branch from 2012d05 to 0ba2507 Compare August 7, 2025 17:59
Copy link
Collaborator

@christopher-hakkaart christopher-hakkaart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some very minor suggestions. Feel free to ignore if the meaning changes.

@pditommaso
Copy link
Member

@bentsherman can you have a look at the conflicts? I've tried but not sure how address them

Signed-off-by: Ben Sherman <[email protected]>
@bentsherman bentsherman force-pushed the fix-plugin-secrets-config branch from ef31401 to 59dfb0f Compare September 12, 2025 16:03
@pditommaso
Copy link
Member

@claude review

Copy link

claude bot commented Sep 14, 2025

Claude finished @pditommaso's task —— View job


Code Review Analysis

I'll review this PR that implements plugin secrets support in config by introducing a two-stage config loading approach.

Todo List:

  • Read and analyze core implementation files
  • Review the ConfigNullProvider "shim" approach
  • Analyze the two-stage config loading logic
  • Check test coverage and integration tests
  • Review documentation updates
  • Provide comprehensive review feedback

Review Summary

This is a well-designed solution for a complex problem. The two-stage config loading approach using a "shim" secrets provider is elegant and minimizes performance impact.

Strengths

  1. Smart Detection Mechanism: The ConfigNullProvider elegantly tracks secret usage without requiring config parsing twice in most cases.

  2. Minimal Performance Impact: Only reloads config when secrets are actually accessed, avoiding unnecessary double parsing.

  3. Good Test Coverage: Includes both unit tests and integration tests that verify the end-to-end behavior.

  4. Clear Documentation: Well-documented limitations and workarounds for edge cases like config includes.

  5. Clean Implementation: The logic in CmdRun.groovy is clear and follows a logical sequence.

🔍 Areas for Consideration

1. Error Handling (CmdRun.groovy:352-361)

The config reload happens unconditionally if secrets were used. Consider adding error handling:

if( secretsProvider.usedSecrets() ) {
    try {
        log.debug "Config file used secrets -- reloading config with secrets provider"
        builder = new ConfigBuilder()...
    } catch (Exception e) {
        throw new AbortOperationException("Failed to reload config with secrets provider: ${e.message}", e)
    }
}

2. ConfigNullProvider Implementation (ConfigNullProvider.groovy:34-37)

The implementation is minimal but correct. The empty string return value aligns with the documented behavior. Consider adding debug logging:

@Override
Secret getSecret(String name) {
    log.debug "Config requested secret: ${name} (returning empty string)"
    accessed = true
    return new SecretImpl(name, '')
}

3. Documentation Enhancement (docs/secrets.md:58-74)

The documentation is comprehensive but could benefit from a concrete example showing the actual log message users should expect:

DEBUG nextflow.cli.CmdRun - Config file used secrets -- reloading config with secrets provider

4. Test Coverage Verification

The integration test at tests/checks/config-secrets.nf/.checks:8 properly verifies the log message, which is good for regression testing.

🚨 Potential Issues

1. Thread Safety

The ConfigNullProvider.accessed field is not thread-safe. While config loading is likely single-threaded, consider using AtomicBoolean:

private final AtomicBoolean accessed = new AtomicBoolean(false)

@Override
Secret getSecret(String name) {
    accessed.set(true)
    return new SecretImpl(name, '')
}

boolean usedSecrets() {
    return accessed.get()
}

2. Plugin Loading Order

The sequence in CmdRun.groovy:344-361 assumes plugins are loaded synchronously. If plugin loading becomes asynchronous in the future, this could break.

📋 Minor Suggestions

  1. Consistent Naming: Consider renaming usedSecrets() to wasAccessed() for clarity.

  2. Config Validation: The second config build doesn't validate like the first (line 367-368 only applies to the first build).

  3. Memory Efficiency: The secretsProvider reference could be nulled after the check to help GC.

🎯 Overall Assessment

This is a solid implementation that solves a complex bootstrap problem elegantly. The approach is pragmatic and the performance considerations are well thought out. The test coverage is appropriate and the documentation clearly explains the limitations and workarounds.

Recommendation: ✅ Approve - The implementation is production-ready with only minor enhancement opportunities.


@pditommaso
Copy link
Member

Ok, thread safety is not relevant because this code is not expected to run in parallel manner. Added more docs and renamed ConfigNullProvider to EmptySecretProvider to avoid confusing it with NullProvider

@pditommaso pditommaso merged commit 9e4c975 into master Sep 14, 2025
11 checks passed
@pditommaso pditommaso deleted the fix-plugin-secrets-config branch September 14, 2025 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants