Skip to content

Commit

Permalink
Support Azure Managed Identities in Fusion configuration logic
Browse files Browse the repository at this point in the history
This commit extends the `AzFusionEnv` class so that it now takes into
account a potential Azure Managed Identity in Nextflow's configuration
file. It also fixes a configuration error (nextflow-io#5081) by which Nextflow complained
of a missing SAS token despite having a Managed Identity properly configured.

Signed-off-by: Alberto Miranda <[email protected]>
  • Loading branch information
alberto-miranda committed Sep 3, 2024
1 parent 4667241 commit 94b164c
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,31 @@ class AzFusionEnv implements FusionEnv {

@Override
Map<String, String> getEnvironment(String scheme, FusionConfig config) {
if( scheme!='az' )
return Collections.<String,String>emptyMap()
if (scheme != 'az')
return Collections.<String, String> emptyMap()

final cfg = AzConfig.config.storage()
final cfg = AzConfig.config
final result = new LinkedHashMap(10)
if( !cfg.accountName )
throw new IllegalArgumentException("Missing Azure storage account name")
if( !cfg.sasToken && !cfg.accountKey )
throw new IllegalArgumentException("Missing Azure storage SAS token")

result.AZURE_STORAGE_ACCOUNT = cfg.accountName
result.AZURE_STORAGE_SAS_TOKEN = cfg.getOrCreateSasToken()

if (!cfg.storage().accountName)
throw new IllegalArgumentException("Missing Azure Storage account name")

if (cfg.storage().accountKey && cfg.storage().sasToken)
throw new IllegalArgumentException("Azure Storage Access key and SAS token detected. Only one is allowed")

// the account name is always required
result.AZURE_STORAGE_ACCOUNT = cfg.storage().accountName

// If a Managed Identity or Service Principal is configured, Fusion only needs to know the account name
if (cfg.managedIdentity().isConfigured() || cfg.activeDirectory().isConfigured()) {
return result
}

// If a SAS token is configured, instead, Fusion also requires the token value
if (cfg.storage().sasToken) {
result.AZURE_STORAGE_SAS_TOKEN = cfg.storage().getOrCreateSasToken()
}

return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2013-2024, Seqera Labs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package nextflow.cloud.azure.fusion

import nextflow.Global
import nextflow.Session
import nextflow.fusion.FusionConfig
import spock.lang.Specification

/**
*
* @author Alberto Miranda <[email protected]>
*/
class AzFusionEnvTest extends Specification {

def setup() {
Global.config = Collections.emptyMap()
}

def 'should return empty env'() {
given:
def provider = new AzFusionEnv()
when:
def env = provider.getEnvironment('aws', Mock(FusionConfig))
then:
env == Collections.emptyMap()
}

def 'should return env environment'() {
given:
Global.session = Mock(Session) {
getConfig() >> [azure: [storage: [accountName: 'x1']]]
}
and:

when:
def config = Mock(FusionConfig)
def env = new AzFusionEnv().getEnvironment('az', config)
then:
env == [AZURE_STORAGE_ACCOUNT: 'x1']

cleanup:
Global.session = null
}

def 'should return env environment with SAS token config'() {
given:
Global.session = Mock(Session) {
getConfig() >> [azure: [storage: [accountName: 'x1', sasToken: 'y1']]]
}
and:

when:
def config = Mock(FusionConfig)
def env = new AzFusionEnv().getEnvironment('az', config)
then:
env == [AZURE_STORAGE_ACCOUNT: 'x1', AZURE_STORAGE_SAS_TOKEN: 'y1']

cleanup:
Global.session = null
}

def 'should throw an exception when missing Azure Storage account name'() {
given:
Global.session = Mock(Session) {
getConfig() >> [azure: [storage: [sasToken: 'y1']]]
}
when:
def config = Mock(FusionConfig)
def env = new AzFusionEnv().getEnvironment('az', Mock(FusionConfig))
then:
thrown(IllegalArgumentException)
cleanup:
Global.session = null
}

def 'should throw an exception when both account key and SAS token are present'() {
given:
Global.session = Mock(Session) {
getConfig() >> [azure: [storage: [accountName: 'x1', accountKey: 'y1', sasToken: 'z1']]]
}
when:
def config = Mock(FusionConfig)
def env = new AzFusionEnv().getEnvironment('az', Mock(FusionConfig))
then:
thrown(IllegalArgumentException)
cleanup:
Global.session = null
}
}

0 comments on commit 94b164c

Please sign in to comment.