Skip to content

Commit

Permalink
refactor init script / user data resolution into method and add test
Browse files Browse the repository at this point in the history
  • Loading branch information
ssadedin committed Dec 22, 2024
1 parent e98e665 commit 56f3fe0
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 8 deletions.
47 changes: 39 additions & 8 deletions src/main/groovy/bpipe/executor/AWSEC2CommandExecutor.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,6 @@ class AWSEC2CommandExecutor extends CloudExecutor {

RunInstancesRequest runInstancesRequest = new RunInstancesRequest();
runInstancesRequest.withImageId(image)
// .withInstanceType(InstanceType.T1Micro)
.withInstanceType(instanceType)
.withMinCount(1)
.withMaxCount(1)
Expand All @@ -366,14 +365,11 @@ class AWSEC2CommandExecutor extends CloudExecutor {

if(config.containsKey('instanceProfile'))
runInstancesRequest.setIamInstanceProfile(new IamInstanceProfileSpecification().withName((String)config.instanceProfile))

if(config.containsKey('initScript')) {
final String script = config['initScript']
final String userData = script.bytes.encodeBase64()

def userData = resolveUserData(config)
if(userData)
runInstancesRequest = runInstancesRequest.withUserData(userData)
log.info "Configured init script for $command.name as userData"
}


RunInstancesResult result

List<String> retryErrorCodes = [ "InsufficientInstanceCapacity", "VcpuLimitExceeded", "RequestLimitExceeded" ]
Expand All @@ -393,6 +389,41 @@ class AWSEC2CommandExecutor extends CloudExecutor {
log.info "Instance $instanceId started with host name $hostname"
}

/**
* Check the given config for elements that would require user data to be set. These include
* an explicit init script, or alternatively, a walltime which is implemented through a shutdown
* command that is set as an init script
*
* @param config
* @return base 64 encoded user data
*/
@CompileStatic
String resolveUserData(Map config) {
List<String> initScriptParts = []
if(config.containsKey('walltime')) {
// Walltime expressed in hh:mm:ss form
// But we need it in minutes
List<Integer> timeParts = ((String)config.walltime).tokenize(':')*.toInteger()
int minutes = timeParts.size()==3 ? (timeParts[0] * 60 + timeParts[1]) : timeParts[0]
log.info "Calculated $minutes shutdown minutes from walltime $config.walltime"
initScriptParts.add("shutdown -h +$minutes".toString())
}

if(config.containsKey('initScript')) {
initScriptParts.add(config['initScript'])
}

if(initScriptParts) {
final String script = '#!/bin/bash\n' + initScriptParts.join('\n') + '\n'
final String userData = script.bytes.encodeBase64()
log.info "Resolved init script for $command.name as userData:\n\n$script"
return userData
}
else {
return null
}
}

@CompileStatic
void connectInstance(Map config) {

Expand Down
34 changes: 34 additions & 0 deletions test-src/bpipe/executor/AWSEC2CommandExecutorTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class AWSEC2CommandExecutorTest {
user: 'centos',
region: 'ap-southeast-2'
]


@BeforeClass
static void before() {
Expand Down Expand Up @@ -79,4 +80,37 @@ class AWSEC2CommandExecutorTest {
println "Terminating instance $awse.instanceId"
// awse.ec2.terminateInstances(new TerminateInstancesRequest([awse.instanceId]))
}


@Test
public void 'user data calculation'() {

def awse = new AWSEC2CommandExecutor(command: new Command(name: 'test'))

String userData = awse.resolveUserData(walltime:'12:00:00')
assert userData : "should have user data when walltime is set"

String result = new String(userData.decodeBase64(), 'utf-8')
assert result.contains("shutdown -h +720")

userData = awse.resolveUserData(walltime:'12:00')
assert userData : "should have user data when walltime is set"
result = new String(userData.decodeBase64(), 'utf-8')
assert result.contains("shutdown -h +12")

userData = awse.resolveUserData(walltime:'12:00', initScript: 'echo "hello world"')
assert userData : "should have user data when walltime or initScript is set"
result = new String(userData.decodeBase64(), 'utf-8')
assert result.contains("shutdown -h +12")

userData = awse.resolveUserData(initScript: 'echo "hello world"')
assert userData : "should have user data when walltime or initScript is set"
result = new String(userData.decodeBase64(), 'utf-8')
assert !result.contains("shutdown")
assert result.contains("hello world")

userData = awse.resolveUserData([:])
assert !userData : "should not have user data when neither walltime or initScript is set"

}
}

0 comments on commit 56f3fe0

Please sign in to comment.