Skip to content

Commit daf9ed7

Browse files
robsymeclaude
andcommitted
Add aws.batch.forceGlacierTransfer config option
Add configuration option to enable --force-glacier-transfer flag for AWS CLI S3 download commands. This allows downloading restored Glacier/Deep Archive objects that retain their storage class metadata. When enabled via: aws.batch.forceGlacierTransfer = true The nxf_s3_download function will include --force-glacier-transfer in aws s3 cp commands, enabling downloads of restored Glacier objects without AWS CLI skipping them due to storage class checks. Fixes: #4747 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Rob Syme <rob.syme@gmail.com>
1 parent c95648b commit daf9ed7

File tree

5 files changed

+75
-4
lines changed

5 files changed

+75
-4
lines changed

plugins/nf-amazon/src/main/nextflow/cloud/aws/batch/AwsOptions.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,8 @@ class AwsOptions implements CloudTransferOptions {
164164
return awsConfig.batchConfig.terminateUnschedulableJobs
165165
}
166166

167+
Boolean getForceGlacierTransfer() {
168+
return awsConfig.batchConfig.forceGlacierTransfer
169+
}
170+
167171
}

plugins/nf-amazon/src/main/nextflow/cloud/aws/config/AwsBatchConfig.groovy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ class AwsBatchConfig implements CloudTransferOptions, ConfigScope {
125125
""")
126126
final List<String> volumes
127127

128+
@ConfigOption
129+
@Description("""
130+
When `true`, the `--force-glacier-transfer` flag is added to AWS CLI S3 download commands, allowing restored Glacier/Deep Archive objects to be downloaded (default: `false`).
131+
""")
132+
final boolean forceGlacierTransfer
133+
128134
/**
129135
* The path for the `s5cmd` tool as an alternative to `aws s3` CLI to upload/download files
130136
*/
@@ -151,6 +157,7 @@ class AwsBatchConfig implements CloudTransferOptions, ConfigScope {
151157
schedulingPriority = opts.schedulingPriority as Integer ?: 0
152158
executionRole = opts.executionRole
153159
terminateUnschedulableJobs = opts.terminateUnschedulableJobs as boolean
160+
forceGlacierTransfer = opts.forceGlacierTransfer as boolean
154161
if( retryMode == 'built-in' )
155162
retryMode = null // this force falling back on NF built-in retry mode instead of delegating to AWS CLI tool
156163
if( retryMode && retryMode !in AwsOptions.VALID_RETRY_MODES )

plugins/nf-amazon/src/main/nextflow/cloud/aws/util/S3BashLib.groovy

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class S3BashLib extends BashFunLib<S3BashLib> {
3838
private String s5cmdPath
3939
private String acl = ''
4040
private String requesterPays = ''
41+
private String forceGlacierTransfer = ''
4142

4243
S3BashLib withCliPath(String cliPath) {
4344
if( cliPath )
@@ -90,6 +91,11 @@ class S3BashLib extends BashFunLib<S3BashLib> {
9091
return this
9192
}
9293

94+
S3BashLib withForceGlacierTransfer(Boolean value) {
95+
this.forceGlacierTransfer = value ? '--force-glacier-transfer ' : ''
96+
return this
97+
}
98+
9399
protected String retryEnv() {
94100
if( !retryMode )
95101
return ''
@@ -126,9 +132,9 @@ class S3BashLib extends BashFunLib<S3BashLib> {
126132
local file_name=\$(basename \$1)
127133
local is_dir=\$($cli s3 ls \$source | grep -F "PRE \${file_name}/" -c)
128134
if [[ \$is_dir == 1 ]]; then
129-
$cli s3 cp --only-show-errors --recursive "\$source" "\$target"
135+
$cli s3 cp --only-show-errors --recursive ${forceGlacierTransfer}"\$source" "\$target"
130136
else
131-
$cli s3 cp --only-show-errors "\$source" "\$target"
137+
$cli s3 cp --only-show-errors ${forceGlacierTransfer}"\$source" "\$target"
132138
fi
133139
}
134140
""".stripIndent(true)
@@ -194,6 +200,7 @@ class S3BashLib extends BashFunLib<S3BashLib> {
194200
.withS5cmdPath( opts.s5cmdPath )
195201
.withAcl( opts.s3Acl )
196202
.withRequesterPays( opts.requesterPays )
203+
.withForceGlacierTransfer( opts.forceGlacierTransfer )
197204
}
198205

199206
static String script(AwsOptions opts) {

plugins/nf-amazon/src/test/nextflow/cloud/aws/config/AwsBatchConfigTest.groovy

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class AwsBatchConfigTest extends Specification {
4747
!batch.s5cmdPath
4848
batch.schedulingPriority == 0
4949
!batch.terminateUnschedulableJobs
50+
!batch.forceGlacierTransfer
5051
}
5152

5253
def 'should create config with options' () {
@@ -153,4 +154,18 @@ class AwsBatchConfigTest extends Specification {
153154
[terminateUnschedulableJobs: false] | false
154155
[terminateUnschedulableJobs: true] | true
155156
}
157+
158+
def 'should parse forceGlacierTransfer flag' () {
159+
given:
160+
def opts = new AwsBatchConfig(OPTS)
161+
162+
expect:
163+
opts.forceGlacierTransfer == FORCE_GLACIER
164+
165+
where:
166+
OPTS | FORCE_GLACIER
167+
[:] | false
168+
[forceGlacierTransfer: false] | false
169+
[forceGlacierTransfer: true] | true
170+
}
156171
}

plugins/nf-amazon/src/test/nextflow/cloud/aws/util/S3BashLibTest.groovy

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ class S3BashLibTest extends Specification {
802802
s5cmd cp --acl public-read --storage-class STANDARD "$name" "$s3path/$name"
803803
fi
804804
}
805-
805+
806806
nxf_s3_download() {
807807
local source=$1
808808
local target=$2
@@ -816,6 +816,44 @@ class S3BashLibTest extends Specification {
816816
}
817817
'''.stripIndent(true)
818818
}
819-
819+
820+
def 'should create script with force glacier transfer' () {
821+
given:
822+
Global.session = Mock(Session) {
823+
getConfig() >> [aws:[batch:[forceGlacierTransfer: true]]]
824+
}
825+
826+
expect:
827+
S3BashLib.script() == '''
828+
# aws cli retry config
829+
export AWS_RETRY_MODE=standard
830+
export AWS_MAX_ATTEMPTS=5
831+
# aws helper
832+
nxf_s3_upload() {
833+
local name=$1
834+
local s3path=$2
835+
if [[ "$name" == - ]]; then
836+
aws s3 cp --only-show-errors --storage-class STANDARD - "$s3path"
837+
elif [[ -d "$name" ]]; then
838+
aws s3 cp --only-show-errors --recursive --storage-class STANDARD "$name" "$s3path/$name"
839+
else
840+
aws s3 cp --only-show-errors --storage-class STANDARD "$name" "$s3path/$name"
841+
fi
842+
}
843+
844+
nxf_s3_download() {
845+
local source=$1
846+
local target=$2
847+
local file_name=$(basename $1)
848+
local is_dir=$(aws s3 ls $source | grep -F "PRE ${file_name}/" -c)
849+
if [[ $is_dir == 1 ]]; then
850+
aws s3 cp --only-show-errors --recursive --force-glacier-transfer "$source" "$target"
851+
else
852+
aws s3 cp --only-show-errors --force-glacier-transfer "$source" "$target"
853+
fi
854+
}
855+
'''.stripIndent(true)
856+
}
857+
820858

821859
}

0 commit comments

Comments
 (0)