Skip to content

Commit 5b61afe

Browse files
authored
Add Google Batch LogsPolicy PATH option for logging to GCS (#6431)
--------- Signed-off-by: David Glazer <[email protected]> Signed-off-by: Emma Rogge <[email protected]> Signed-off-by: Ben Sherman <[email protected]>
1 parent 3f60878 commit 5b61afe

File tree

6 files changed

+53
-13
lines changed

6 files changed

+53
-13
lines changed

docs/reference/config.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,12 @@ The following settings are available:
922922
:::
923923
: List of custom mount options for `gcsfuse` (default: `['-o rw', '-implicit-dirs']`).
924924

925+
`google.batch.logsPath`
926+
: :::{versionadded} 25.11.0-edge
927+
:::
928+
: The Google Cloud Storage path where job logs should be stored, e.g. `gs://my-logs-bucket/logs`.
929+
: When specified, Google Batch will write job logs to this location instead of [Cloud Logging](https://cloud.google.com/logging/docs). The bucket must be accessible and writable by the service account.
930+
925931
`google.batch.maxSpotAttempts`
926932
: :::{versionadded} 23.11.0-edge
927933
:::

plugins/nf-google/src/main/nextflow/cloud/google/batch/GoogleBatchScriptLauncher.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,17 @@ class GoogleBatchScriptLauncher extends BashWrapperBuilder implements GoogleBatc
186186

187187
GoogleBatchScriptLauncher withConfig(GoogleOpts config) {
188188
this.config = config
189+
addLogsBucket(config.batch.logsPath())
189190
return this
190191
}
191192

193+
protected void addLogsBucket(Path path) {
194+
if( path instanceof CloudStoragePath )
195+
buckets.add(path.bucket())
196+
else if( path != null )
197+
throw new IllegalArgumentException("Unexpected value for Google Batch logs path: ${path.toUriString()}")
198+
}
199+
192200
GoogleBatchScriptLauncher withIsArray(boolean value) {
193201
this.isArray = value
194202
return this

plugins/nf-google/src/main/nextflow/cloud/google/batch/GoogleBatchTaskHandler.groovy

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import com.google.cloud.batch.v1.ServiceAccount
3232
import com.google.cloud.batch.v1.TaskGroup
3333
import com.google.cloud.batch.v1.TaskSpec
3434
import com.google.cloud.batch.v1.Volume
35+
import com.google.cloud.storage.contrib.nio.CloudStoragePath
3536
import com.google.protobuf.Duration
3637
import groovy.transform.CompileStatic
3738
import groovy.transform.PackageScope
@@ -447,14 +448,26 @@ class GoogleBatchTaskHandler extends TaskHandler implements FusionAwareTask {
447448
return Job.newBuilder()
448449
.addTaskGroups(taskGroup)
449450
.setAllocationPolicy(allocationPolicy)
450-
.setLogsPolicy(
451-
LogsPolicy.newBuilder()
452-
.setDestination(LogsPolicy.Destination.CLOUD_LOGGING)
453-
)
451+
.setLogsPolicy(createLogsPolicy())
454452
.putAllLabels(task.config.getResourceLabels())
455453
.build()
456454
}
457455

456+
protected LogsPolicy createLogsPolicy() {
457+
final logsPath = executor.batchConfig.logsPath()
458+
if( logsPath instanceof CloudStoragePath ) {
459+
return LogsPolicy.newBuilder()
460+
.setDestination(LogsPolicy.Destination.PATH)
461+
.setLogsPath(GoogleBatchScriptLauncher.containerMountPath(logsPath))
462+
.build()
463+
}
464+
else {
465+
return LogsPolicy.newBuilder()
466+
.setDestination(LogsPolicy.Destination.CLOUD_LOGGING)
467+
.build()
468+
}
469+
}
470+
458471
/**
459472
* @return Retrieve the submitted task state
460473
*/

plugins/nf-google/src/main/nextflow/cloud/google/batch/client/BatchConfig.groovy

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@
1616

1717
package nextflow.cloud.google.batch.client
1818

19-
import com.google.auth.oauth2.GoogleCredentials
19+
import java.nio.file.Path
20+
2021
import groovy.transform.CompileStatic
2122
import groovy.util.logging.Slf4j
22-
import nextflow.Session
23-
import nextflow.cloud.google.GoogleOpts
2423
import nextflow.config.spec.ConfigOption
2524
import nextflow.config.spec.ConfigScope
2625
import nextflow.script.dsl.Description
@@ -85,6 +84,12 @@ class BatchConfig implements ConfigScope {
8584
""")
8685
final boolean installGpuDrivers
8786

87+
@ConfigOption
88+
@Description("""
89+
The Google Cloud Storage path where job logs should be stored, e.g. `gs://my-logs-bucket/logs`.
90+
""")
91+
final String logsPath
92+
8893
@ConfigOption
8994
@Description("""
9095
Max number of execution attempts of a job interrupted by a Compute Engine Spot reclaim event (default: `0`).
@@ -142,6 +147,7 @@ class BatchConfig implements ConfigScope {
142147
cpuPlatform = opts.cpuPlatform
143148
gcsfuseOptions = opts.gcsfuseOptions as List<String> ?: DEFAULT_GCSFUSE_OPTS
144149
installGpuDrivers = opts.installGpuDrivers as boolean
150+
logsPath = opts.logsPath
145151
maxSpotAttempts = opts.maxSpotAttempts != null ? opts.maxSpotAttempts as int : DEFAULT_MAX_SPOT_ATTEMPTS
146152
network = opts.network
147153
networkTags = opts.networkTags as List<String> ?: Collections.emptyList()
@@ -155,4 +161,8 @@ class BatchConfig implements ConfigScope {
155161

156162
BatchRetryConfig getRetryConfig() { retry }
157163

164+
Path logsPath() {
165+
return logsPath as Path
166+
}
167+
158168
}

plugins/nf-google/src/test/nextflow/cloud/google/batch/GoogleBatchTaskHandlerTest.groovy

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class GoogleBatchTaskHandlerTest extends Specification {
142142
def MACHINE_TYPE = 'vm-type-2'
143143
def MEM = MemoryUnit.of('8 GB')
144144
def TIMEOUT = Duration.of('1 hour')
145+
def LOGS_PATH = CloudStorageFileSystem.forBucket('my-logs-bucket').getPath('/logs')
145146
and:
146147
def exec = Mock(GoogleBatchExecutor) {
147148
getBatchConfig() >> Mock(BatchConfig) {
@@ -157,6 +158,7 @@ class GoogleBatchTaskHandlerTest extends Specification {
157158
getServiceAccountEmail() >> '[email protected]'
158159
getSubnetwork() >> 'subnet-1'
159160
usePrivateAddress >> true
161+
logsPath() >> LOGS_PATH
160162
}
161163
}
162164
and:
@@ -239,7 +241,8 @@ class GoogleBatchTaskHandlerTest extends Specification {
239241
networkInterface.getSubnetwork() == 'subnet-1'
240242
networkInterface.getNoExternalIpAddress() == true
241243
and:
242-
req.getLogsPolicy().getDestination().toString() == 'CLOUD_LOGGING'
244+
req.getLogsPolicy().getDestination().toString() == 'PATH'
245+
req.getLogsPolicy().getLogsPath() == '/mnt/disks/my-logs-bucket/logs'
243246
and:
244247
req.getLabelsMap() == [foo: 'bar']
245248

plugins/nf-google/src/test/nextflow/cloud/google/batch/client/BatchConfigTest.groovy

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,14 @@
1717

1818
package nextflow.cloud.google.batch.client
1919

20-
import nextflow.Session
2120
import nextflow.util.MemoryUnit
22-
import spock.lang.Requires
2321
import spock.lang.Specification
2422
/**
2523
*
2624
* @author Paolo Di Tommaso <[email protected]>
2725
*/
2826
class BatchConfigTest extends Specification {
2927

30-
@Requires({System.getenv('GOOGLE_APPLICATION_CREDENTIALS')})
3128
def 'should create batch config' () {
3229
when:
3330
def config = new BatchConfig([:])
@@ -40,9 +37,9 @@ class BatchConfigTest extends Specification {
4037
and:
4138
!config.bootDiskImage
4239
!config.bootDiskSize
40+
!config.logsPath
4341
}
4442

45-
@Requires({System.getenv('GOOGLE_APPLICATION_CREDENTIALS')})
4643
def 'should create batch config with custom settings' () {
4744
given:
4845
def opts = [
@@ -51,7 +48,8 @@ class BatchConfigTest extends Specification {
5148
autoRetryExitCodes: [50001, 50003, 50005],
5249
retryPolicy: [maxAttempts: 10],
5350
bootDiskImage: 'batch-foo',
54-
bootDiskSize: '100GB'
51+
bootDiskSize: '100GB',
52+
logsPath: 'gs://my-logs-bucket/logs'
5553
]
5654

5755
when:
@@ -65,6 +63,8 @@ class BatchConfigTest extends Specification {
6563
and:
6664
config.bootDiskImage == 'batch-foo'
6765
config.bootDiskSize == MemoryUnit.of('100GB')
66+
and:
67+
config.logsPath == 'gs://my-logs-bucket/logs'
6868
}
6969

7070
}

0 commit comments

Comments
 (0)