Skip to content

Commit

Permalink
Add tests + minor changes
Browse files Browse the repository at this point in the history
Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com>
  • Loading branch information
pditommaso committed Mar 14, 2024
1 parent c063de8 commit d4cab02
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 12 deletions.
5 changes: 5 additions & 0 deletions docs/script.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,11 @@ The following variables are implicitly defined in the script global execution sc
:::
: The directory where the main script is located.

`secrets`
: :::{versionadded} 24.02.0-edge
:::
: Dictionary like object holding workflow secrets. Read the {ref}`secrets-page` page for more information.

`workDir`
: The directory where tasks temporary files are created.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ abstract class BaseScript extends Script implements ExecutionContext {
binding.owner = this
session = binding.getSession()
processFactory = session.newProcessFactory(this)
final secretsProvider = SecretsLoader.isEnabled() ? SecretsLoader.instance.load() : null

binding.setVariable( 'baseDir', session.baseDir )
binding.setVariable( 'projectDir', session.baseDir )
Expand All @@ -88,18 +89,15 @@ abstract class BaseScript extends Script implements ExecutionContext {
binding.setVariable( 'nextflow', NextflowMeta.instance )
binding.setVariable( 'launchDir', Paths.get('./').toRealPath() )
binding.setVariable( 'moduleDir', meta.moduleDir )
binding.setVariable( 'secrets', makeSecretsContext() )
binding.setVariable( 'secrets', makeSecretsContext(secretsProvider) )
}

def makeSecretsContext() {
if( !SecretsLoader.isEnabled() )
return null
protected makeSecretsContext(SecretsProvider provider) {

return new Object() {
private SecretsProvider provider = SecretsLoader.instance.load()

@Override
def getProperty(String name) {
if( !provider )
throw new AbortOperationException("Unable to resolve secrets.$name - no secret provider is available")
provider.getSecret(name)?.value
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@

package nextflow.secret

import groovy.transform.Memoized

import groovy.util.logging.Slf4j
import nextflow.SysEnv
import nextflow.exception.AbortOperationException
import nextflow.plugin.Plugins

/**
* Implements dynamic secret providing loading strategy
*
Expand All @@ -32,12 +31,23 @@ import nextflow.plugin.Plugins
@Singleton
class SecretsLoader {

private SecretsProvider provider

static boolean isEnabled() {
SysEnv.get('NXF_ENABLE_SECRETS', 'true') == 'true'
}

@Memoized
SecretsProvider load() {
if( provider )
return provider
synchronized (this) {
if( provider )
return provider
return provider = load0()
}
}

private SecretsProvider load0() {
// discover all available secrets provider
final all = Plugins.getPriorityExtensions(SecretsProvider)
// find first activable in the current environment
Expand All @@ -49,4 +59,7 @@ class SecretsLoader {
throw new AbortOperationException("Unable to load secrets provider")
}

void reset() {
provider=null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ class CmdSecretTest extends Specification {
secretFile = new File("$tempDir/store.json")
SysEnv.push([NXF_SECRETS_FILE: secretFile.toString()])
//required to run all test due collisions with others
def memoized = SecretsLoader.instance.memoizedMethodClosure$load
memoized.@cache.clear()
SecretsLoader.instance.reset()
}

def cleanupSpec() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import java.nio.file.Paths

import nextflow.NextflowMeta
import nextflow.Session
import nextflow.SysEnv
import nextflow.extension.FilesEx
import nextflow.secret.Secret
import nextflow.secret.SecretsLoader
import nextflow.secret.SecretsProvider
import test.Dsl2Spec
import test.TestHelper
/**
Expand Down Expand Up @@ -141,4 +146,60 @@ class BaseScriptTest extends Dsl2Spec {
folder?.delete()
}

def 'should create secret context' () {
given:
def script = Spy(BaseScript)
def provider = Mock(SecretsProvider)
and:
def ctx = script.makeSecretsContext(provider)
when:
def result = ctx.'MY_SECRET'
then:
provider.getSecret('MY_SECRET') >> Mock(Secret) { getValue()>>'123' }
result == '123'
}

def 'should resolve secret in a script' () {
given:
SecretsLoader.instance.reset()
and:
def folder = Files.createTempDirectory('test')
def script = folder.resolve('main.nf')
def secrets = folder.resolve('store.json')
and:
secrets.text = '''
[
{
"name": "FOO",
"value": "ciao"
}
]
'''
and:
FilesEx.setPermissions(secrets, 'rw-------')
SysEnv.push(NXF_SECRETS_FILE:secrets.toAbsolutePath().toString())
and:
def session = Mock(Session)
def binding = new ScriptBinding([:])
def parser = new ScriptParser(session)

when:
script.text = '''
return secrets.FOO
'''

def result = parser
.setBinding(binding)
.runScript(script)
.getResult()

then:
result == 'ciao'

cleanup:
folder?.deleteDir()
and:
SysEnv.pop()
}

}

0 comments on commit d4cab02

Please sign in to comment.