Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ Now you're ready to go!
| `runner-home-dir` | Optional. Used only with the `start` mode. | Specifies a directory where pre-installed actions-runner software and scripts are located.<br><br> |
| `pre-runner-script` | Optional. Used only with the `start` mode. | Specifies bash commands to run before the runner starts. It's useful for installing dependencies with apt-get, yum, dnf, etc. For example:<pre> - name: Start EC2 runner<br> with:<br> mode: start<br> ...<br> pre-runner-script: \|<br> sudo yum update -y && \ <br> sudo yum install docker git libicu -y<br> sudo systemctl enable docker</pre> |
| `market-type` | Optional. Used only with the `start` mode. | This field accepts only the value `spot`. <br><br> If set to `spot`, the runner will be launched as a Spot instance. <br><br> If omitted, the runner will be launched as an on-demand instance. |
| `block-device-mappings` | Optional. Used only with the `start` mode. | JSON string specifying the block device mappings for the EC2 instance. For example: <br> <pre>[{"DeviceName": "/dev/sda1", "Ebs": {"VolumeSize": 100, "VolumeType": "gp3"}}]</pre> See <a href="https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html">AWS BlockDeviceMapping docs</a> for all options. |
| `block-device-mappings` | Optional. Used only with the `start` mode. | JSON string specifying the block device mappings for the EC2 instance. For example: <br> <pre>[{"DeviceName": "/dev/sda1", "Ebs": {"VolumeSize": 100, "VolumeType": "gp3"}}]</pre> See <a href="https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html">AWS BlockDeviceMapping docs</a> for all options. <br><br> **Note on EBS Volume Deletion**: All EBS volumes are automatically configured with `DeleteOnTermination: true` to prevent orphaned volumes and unexpected costs. The action will add this property to any volume that doesn't explicitly set it. If you explicitly set `DeleteOnTermination: false`, that setting will be respected. |
| `startup-quiet-period-seconds` | Optional | Default: 30 |
| `startup-retry-interval-seconds` | Optional | Default: 10 |
| `startup-timeout-minutes` | Optional | Default: 5 |
Expand Down Expand Up @@ -286,6 +286,8 @@ jobs:
[
{"DeviceName": "/dev/sda1", "Ebs": {"VolumeSize": 100, "VolumeType": "gp3"}}
]
# Note: To preserve a volume after instance termination, explicitly set DeleteOnTermination: false:
# {"DeviceName": "/dev/sdb", "Ebs": {"VolumeSize": 50, "DeleteOnTermination": false}}
do-the-job:
name: Do the job on the runner
needs: start-runner # required to start the main job when the runner is ready
Expand Down
35 changes: 34 additions & 1 deletion dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -145115,6 +145115,38 @@ function buildMarketOptions() {
};
}

/**
* Ensures all block device mappings have DeleteOnTermination set to true.
* Respects explicit user settings if DeleteOnTermination is already defined.
*
* @param {Array} blockDeviceMappings - Array of BlockDeviceMapping objects
* @returns {Array} Modified array with DeleteOnTermination set appropriately
*/
function ensureDeleteOnTermination(blockDeviceMappings) {
if (!Array.isArray(blockDeviceMappings) || blockDeviceMappings.length === 0) {
return blockDeviceMappings;
}

return blockDeviceMappings.map(mapping => {
// Create a shallow copy to avoid mutating the original
const modifiedMapping = { ...mapping };

// Only process if the mapping has an Ebs configuration
if (modifiedMapping.Ebs) {
// Create a shallow copy of the Ebs object
modifiedMapping.Ebs = { ...modifiedMapping.Ebs };

// Only set DeleteOnTermination if it's not already explicitly defined
// This respects user's explicit choice if they set it to false
if (modifiedMapping.Ebs.DeleteOnTermination === undefined) {
modifiedMapping.Ebs.DeleteOnTermination = true;
}
}

return modifiedMapping;
});
}

async function createEc2InstanceWithParams(imageId, subnetId, securityGroupId, label, githubRegistrationToken, region) {
// Region is always specified now, so we can directly use it
const ec2ClientOptions = { region };
Expand Down Expand Up @@ -145143,13 +145175,14 @@ async function createEc2InstanceWithParams(imageId, subnetId, securityGroupId, l
Ebs: {
...(config.input.ec2VolumeSize !== '' && { VolumeSize: config.input.ec2VolumeSize }),
...(config.input.ec2VolumeType !== '' && { VolumeType: config.input.ec2VolumeType }),
DeleteOnTermination: true,
},
},
];
}

if (config.input.blockDeviceMappings.length > 0) {
params.BlockDeviceMappings = config.input.blockDeviceMappings;
params.BlockDeviceMappings = ensureDeleteOnTermination(config.input.blockDeviceMappings);
}

const result = await ec2.send(new RunInstancesCommand(params));
Expand Down
35 changes: 34 additions & 1 deletion src/aws.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,38 @@ function buildMarketOptions() {
};
}

/**
* Ensures all block device mappings have DeleteOnTermination set to true.
* Respects explicit user settings if DeleteOnTermination is already defined.
*
* @param {Array} blockDeviceMappings - Array of BlockDeviceMapping objects
* @returns {Array} Modified array with DeleteOnTermination set appropriately
*/
function ensureDeleteOnTermination(blockDeviceMappings) {
if (!Array.isArray(blockDeviceMappings) || blockDeviceMappings.length === 0) {
return blockDeviceMappings;
}

return blockDeviceMappings.map(mapping => {
// Create a shallow copy to avoid mutating the original
const modifiedMapping = { ...mapping };

// Only process if the mapping has an Ebs configuration
if (modifiedMapping.Ebs) {
// Create a shallow copy of the Ebs object
modifiedMapping.Ebs = { ...modifiedMapping.Ebs };

// Only set DeleteOnTermination if it's not already explicitly defined
// This respects user's explicit choice if they set it to false
if (modifiedMapping.Ebs.DeleteOnTermination === undefined) {
modifiedMapping.Ebs.DeleteOnTermination = true;
}
}

return modifiedMapping;
});
}

async function createEc2InstanceWithParams(imageId, subnetId, securityGroupId, label, githubRegistrationToken, region) {
// Region is always specified now, so we can directly use it
const ec2ClientOptions = { region };
Expand Down Expand Up @@ -135,13 +167,14 @@ async function createEc2InstanceWithParams(imageId, subnetId, securityGroupId, l
Ebs: {
...(config.input.ec2VolumeSize !== '' && { VolumeSize: config.input.ec2VolumeSize }),
...(config.input.ec2VolumeType !== '' && { VolumeType: config.input.ec2VolumeType }),
DeleteOnTermination: true,
},
},
];
}

if (config.input.blockDeviceMappings.length > 0) {
params.BlockDeviceMappings = config.input.blockDeviceMappings;
params.BlockDeviceMappings = ensureDeleteOnTermination(config.input.blockDeviceMappings);
}

const result = await ec2.send(new RunInstancesCommand(params));
Expand Down