diff --git a/CHANGELOG.md b/CHANGELOG.md index d557f12..d186c1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.3.0](https://github.com/seqeralabs/nf-aggregate/releases/tag/0.3.0)] - 2024-07-01 + +### Credits + +Special thanks to the following for their contributions to the release: + +- [Adam Talbot](https://github.com/adamrtalbot) +- [Esha Joshi](https://github.com/ejseqera) +- [Rob Syme](https://github.com/robsyme) + +Thank you to everyone else that has contributed by reporting bugs, enhancements or in any other way, shape or form. + +### Enhancements & fixes + +[PR #49](https://github.com/seqeralabs/nf-aggregate/pull/49) - Add custom java truststore support and improved exception handling + ## [[0.2.0](https://github.com/seqeralabs/nf-aggregate/releases/tag/0.2.0)] - 2024-05-29 ### Credits diff --git a/README.md b/README.md index db33305..16fce74 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ nextflow run seqeralabs/nf-aggregate \ -profile docker ``` +If you are using a Seqera Platform Enterprise instance that is secured with a private CA SSL certificate not recognized by default Java certificate authorities, you can specify a custom `cacerts` store path through the `--java_truststore_path` parameter and optionally, a password with the `--java_truststore_password`. This certificate will be used to achieve connectivity with your Seqera Platform instance through API and CLI. + ## Output The results from the pipeline will be published in the path specified by the `--outdir` and will consist of the following contents: diff --git a/main.nf b/main.nf index 0950f8e..0d800d3 100644 --- a/main.nf +++ b/main.nf @@ -46,7 +46,9 @@ workflow { ch_multiqc_logo, params.seqera_api_endpoint, params.skip_run_gantt, - params.skip_multiqc + params.skip_multiqc, + params.java_truststore_path, + params.java_truststore_password ) } diff --git a/modules/local/plot_run_gantt/tests/main.nf.test b/modules/local/plot_run_gantt/tests/main.nf.test index 712ed7c..ced8d9c 100644 --- a/modules/local/plot_run_gantt/tests/main.nf.test +++ b/modules/local/plot_run_gantt/tests/main.nf.test @@ -17,6 +17,8 @@ nextflow_process { """ input[0] = ['id': '4Bi5xBK6E2Nbhj', 'workspace': 'community/showcase'] input[1] = "https://api.tower.nf" + input[2] = "" + input[3] = "" """ } } diff --git a/modules/local/seqera_runs_dump/functions.nf b/modules/local/seqera_runs_dump/functions.nf index 10043a7..038cb7c 100644 --- a/modules/local/seqera_runs_dump/functions.nf +++ b/modules/local/seqera_runs_dump/functions.nf @@ -1,6 +1,14 @@ - @Grab('com.github.groovy-wslite:groovy-wslite:1.1.2;transitive=false') import wslite.rest.RESTClient +import groovy.json.JsonSlurper + +// Set system properties for custom Java trustStore +def setTrustStore(trustStorePath, trustStorePassword) { + System.setProperty("javax.net.ssl.trustStore", trustStorePath) + if (trustStorePassword) { + System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword) + } +} Long getWorkspaceId(orgName, workspaceName, client, authHeader) { def orgResponse = client.get(path: '/orgs', headers: authHeader) @@ -14,15 +22,22 @@ Long getWorkspaceId(orgName, workspaceName, client, authHeader) { if (workspaceReponse.statusCode == 200) { def workspaceMap = workspaceReponse.json?.workspaces.collectEntries { ws -> [ws.name, ws.id]} return workspaceMap?.get(workspaceName) + } else { + log.error "Failed to fetch workspaces for orgId: ${orgId}, statusCode: ${workspaceResponse.statusCode}" } } return null } -Map getRunMetadata(meta, log, api_endpoint) { +Map getRunMetadata(meta, log, api_endpoint, trustStorePath, trustStorePassword) { def runId = meta.id def (orgName, workspaceName) = meta.workspace.tokenize("/") + if (trustStorePath) { + log.info "Setting custom truststore: ${trustStorePath}" + setTrustStore(trustStorePath, trustStorePassword) + } + def client = new RESTClient(api_endpoint) def token = System.getenv("TOWER_ACCESS_TOKEN") def authHeader = ["Authorization": "Bearer ${token}"] @@ -39,11 +54,18 @@ Map getRunMetadata(meta, log, api_endpoint) { return metaMap ?: [:] } } - } catch(Exception ex) { + } catch (wslite.rest.RESTClientException ex) { log.warn """ Could not get workflow details for workflow ${runId} in workspace ${meta.workspace}: - ↳ Status code ${ex.response.statusCode} returned from request to ${ex.request.url} (authentication headers excluded) + ↳ Status code ${ex.response?.statusCode} returned from request to ${ex.request?.url} (authentication headers excluded) + """.stripIndent() + log.error "Exception: ${ex.message}", ex + } catch (Exception ex) { + log.warn """ + An error occurred while getting workflow details for workflow ${runId} in workspace ${meta.workspace}: + ↳ ${ex.message} """.stripIndent() + log.error "Exception: ${ex.message}", ex } return [:] } diff --git a/modules/local/seqera_runs_dump/main.nf b/modules/local/seqera_runs_dump/main.nf index 619a13c..43d324f 100644 --- a/modules/local/seqera_runs_dump/main.nf +++ b/modules/local/seqera_runs_dump/main.nf @@ -8,6 +8,8 @@ process SEQERA_RUNS_DUMP { input: val meta val api_endpoint + val java_truststore_path + val java_truststore_password output: tuple val(metaOut), path("${prefix}"), emit: run_dump @@ -17,11 +19,15 @@ process SEQERA_RUNS_DUMP { def args = task.ext.args ?: '' def args2 = task.ext.args2 ?: '' prefix = task.ext.prefix ?: "${meta.id}" - metaOut = meta + getRunMetadata(meta, log, api_endpoint) + metaOut = meta + getRunMetadata(meta, log, api_endpoint, java_truststore_path, java_truststore_password) fusion = metaOut.fusion ? '--add-fusion-logs' : '' + javaTrustStore = java_truststore_path ? "-Djavax.net.ssl.trustStore=${java_truststore_path}" : '' + javaTrustStorePassword = java_truststore_password ? "-Djavax.net.ssl.trustStorePassword=${java_truststore_password}" : '' """ tw \\ $args \\ + $javaTrustStore \\ + $javaTrustStorePassword \\ --url=${api_endpoint} \\ --access-token=$TOWER_ACCESS_TOKEN \\ runs \\ diff --git a/modules/local/seqera_runs_dump/nextflow.config b/modules/local/seqera_runs_dump/nextflow.config index ce78f9b..dbf621d 100644 --- a/modules/local/seqera_runs_dump/nextflow.config +++ b/modules/local/seqera_runs_dump/nextflow.config @@ -2,6 +2,7 @@ process { withName: 'SEQERA_RUNS_DUMP' { ext.args = { params.seqera_cli_extra_args ? params.seqera_cli_extra_args.split("\\s(?=--)") : '' } ext.args2 = { params.skip_run_gantt ? '' : '--add-task-logs' } + containerOptions = { params.java_truststore_path ? "--volume ${params.java_truststore_path}:${params.java_truststore_path}" : '' } publishDir = [ path: { "${params.outdir}/${metaOut?.projectName?.replace("/", "_") ?: ""}/runs_dump" }, mode: params.publish_dir_mode, diff --git a/modules/local/seqera_runs_dump/tests/main.nf.test b/modules/local/seqera_runs_dump/tests/main.nf.test index 9d90498..41758fb 100644 --- a/modules/local/seqera_runs_dump/tests/main.nf.test +++ b/modules/local/seqera_runs_dump/tests/main.nf.test @@ -15,6 +15,8 @@ nextflow_process { """ input[0] = ['id': '4Bi5xBK6E2Nbhj', 'workspace': 'community/showcase'] input[1] = "https://api.tower.nf" + input[2] = "" + input[3] = "" """ } } diff --git a/nextflow.config b/nextflow.config index f7ca780..5178fbb 100644 --- a/nextflow.config +++ b/nextflow.config @@ -18,6 +18,9 @@ params { // Seqera CLI options seqera_api_endpoint = "https://api.cloud.seqera.io" seqera_cli_extra_args = null + java_truststore_path = null + java_truststore_password = null + // MultiQC options multiqc_config = null multiqc_title = null @@ -219,6 +222,6 @@ manifest { mainScript = 'main.nf' nextflowVersion = '!>=23.10.0' defaultBranch = 'main' - version = '0.2.0' + version = '0.3.0' doi = '' } diff --git a/nextflow_schema.json b/nextflow_schema.json index c017879..f8fd597 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -48,6 +48,16 @@ "description": "Extra arguments to pass to the Seqera Platform CLI command in addition to defaults defined by the pipeline.", "fa_icon": "fas fa-plus" }, + "java_truststore_path": { + "type": "string", + "description": "Path to custom cacerts Java truststore used by Seqera Platform.", + "fa_icon": "fas fa-key" + }, + "java_truststore_password": { + "type": "string", + "description": "Password for custom cacerts Java truststore used by Seqera Platform.", + "fa_icon": "fas fa-key" + }, "skip_run_gantt": { "type": "boolean", "description": "Skip GANTT chart creation for each run.", diff --git a/workflows/nf_aggregate/main.nf b/workflows/nf_aggregate/main.nf index ecbd16f..c30f3c3 100644 --- a/workflows/nf_aggregate/main.nf +++ b/workflows/nf_aggregate/main.nf @@ -13,13 +13,15 @@ include { paramsSummaryMap } from 'plugin/nf-validation' workflow NF_AGGREGATE { take: - ids // channel: run ids read in from --input - multiqc_custom_config // channel: user specified custom config file used by MultiQC - multiqc_logo // channel: logo rendered in MultiQC report - seqera_api_endpoint // val: Seqera Platform API endpoint URL - skip_run_gantt // val: Skip GANTT chart creation for each run - skip_multiqc // val: Skip MultiQC - + ids // channel: run ids read in from --input + multiqc_custom_config // channel: user specified custom config file used by MultiQC + multiqc_logo // channel: logo rendered in MultiQC report + seqera_api_endpoint // val: Seqera Platform API endpoint URL + skip_run_gantt // val: Skip GANTT chart creation for each run + skip_multiqc // val: Skip MultiQC + java_truststore_path // val: Path to java truststore if using private certs + java_truststore_password // val: Password for java truststore if using private certs + main: ch_versions = Channel.empty() @@ -30,7 +32,9 @@ workflow NF_AGGREGATE { // SEQERA_RUNS_DUMP ( ids, - seqera_api_endpoint + seqera_api_endpoint, + java_truststore_path ?: '', + java_truststore_password ?: '' ) ch_versions = ch_versions.mix(SEQERA_RUNS_DUMP.out.versions.first()) @@ -40,9 +44,9 @@ workflow NF_AGGREGATE { SEQERA_RUNS_DUMP .out .run_dump - .filter { + .filter { meta, run_dir -> - meta.fusion && !params.skip_run_gantt + meta.fusion && !params.skip_run_gantt } .set { ch_runs_for_gantt } diff --git a/workflows/nf_aggregate/tests/main.nf.test b/workflows/nf_aggregate/tests/main.nf.test index 1243975..b4008ae 100644 --- a/workflows/nf_aggregate/tests/main.nf.test +++ b/workflows/nf_aggregate/tests/main.nf.test @@ -22,6 +22,8 @@ nextflow_workflow { input[3] = 'https://api.tower.nf' input[4] = false input[5] = false + input[6] = "" + input[7] = "" """ } }