Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.

Commit 3e3ad0d

Browse files
committed
Support multiple data volumes in single-node-asg module.
Tested in examples/single-node-asg-tester The change in all three modules is compatible with old code that defines just one data volume.
1 parent b1b7348 commit 3e3ad0d

File tree

13 files changed

+254
-109
lines changed

13 files changed

+254
-109
lines changed
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
provider "aws" {
2+
region = "ap-northeast-1"
3+
}
4+
5+
data "aws_availability_zones" "azs" {}
6+
7+
module "vpc" {
8+
source = "fpco/foundation/aws//modules/vpc-scenario-2"
9+
cidr = "192.168.0.0/16"
10+
public_subnet_cidrs = ["192.168.0.0/24", "192.168.1.0/24"]
11+
private_subnet_cidrs = ["192.168.100.0/24", "192.168.101.0/24"]
12+
azs = data.aws_availability_zones.azs.names
13+
name_prefix = "ebs-test"
14+
region = "ap-northeast-1"
15+
}
16+
17+
module "ubuntu" {
18+
source = "fpco/foundation/aws//modules/ami-ubuntu"
19+
}
20+
21+
resource "aws_security_group" "ssh" {
22+
vpc_id = module.vpc.vpc_id
23+
ingress {
24+
from_port = 22
25+
to_port = 22
26+
protocol = "tcp"
27+
cidr_blocks = ["0.0.0.0/0"]
28+
}
29+
egress {
30+
from_port = 0
31+
to_port = 0
32+
protocol = "-1"
33+
cidr_blocks = ["0.0.0.0/0"]
34+
}
35+
}
36+
37+
module "tester" {
38+
source = "../../modules/single-node-asg"
39+
name_prefix = "ebs"
40+
name_suffix = "test"
41+
key_name = "tokyo"
42+
ami = module.ubuntu.id
43+
instance_type = "t2.micro"
44+
subnet_id = module.vpc.public_subnet_ids[0]
45+
security_group_ids = [aws_security_group.ssh.id]
46+
region = "ap-northeast-1"
47+
compatible_with_single_volume = false
48+
data_volumes = [{ name = "a", device = "/dev/xvdm", size = 50 }, { name = "b", device = "/dev/xvdn" }]
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
if which wget; then
2+
INSTANCE_ID="$(wget -O- http://169.254.169.254/latest/meta-data/instance-id)"
3+
elif which curl; then
4+
INSTANCE_ID="$(curl http://169.254.169.254/latest/meta-data/instance-id)"
5+
fi
6+
7+
if [ "x$${INSTANCE_ID}" == "x" ]; then
8+
echo 'There is no wget or curl tool installed. Hence bootstrap cannot get instance ID.'
9+
exit 1
10+
fi

modules/init-snippet-attach-ebs-volume/main.tf

+17-50
Original file line numberDiff line numberDiff line change
@@ -9,70 +9,37 @@
99
*
1010
*/
1111

12-
# variables used by this snippet of init shellcode
13-
variable "device_path" {
14-
default = "/dev/xvdf"
15-
description = "path, to the device's path in /dev/"
16-
type = string
17-
}
18-
19-
variable "init_prefix" {
20-
default = ""
21-
description = "initial init (shellcode) to prefix this snippet with"
22-
type = string
23-
}
24-
25-
variable "init_suffix" {
26-
default = ""
27-
description = "init (shellcode) to append to the end of this snippet"
28-
type = string
29-
}
30-
31-
variable "log_level" {
32-
default = "info"
33-
description = "default log level verbosity for apps that support it"
34-
type = string
35-
}
36-
37-
variable "log_prefix" {
38-
default = "OPS: "
39-
description = "string to prefix log messages with"
40-
type = string
41-
}
42-
43-
variable "region" {
44-
description = "AWS region the volume is in"
45-
type = string
46-
}
47-
48-
variable "wait_interval" {
49-
default = "5"
50-
description = "time (in seconds) to wait when looping to find the device"
51-
type = number
52-
}
53-
54-
variable "volume_id" {
55-
description = "ID of the EBS volume to attach"
56-
type = string
12+
locals {
13+
device_paths = var.compatible_with_single_volume ? [var.device_path] : var.device_paths
14+
volume_ids = var.compatible_with_single_volume ? [var.volume_id] : var.volume_ids
5715
}
5816

5917
# render init script for a cluster using our generic template
6018
data "template_file" "init_snippet" {
19+
count = length(local.volume_ids)
20+
6121
template = file("${path.module}/snippet.tpl")
6222

6323
vars = {
64-
device_path = var.device_path
65-
init_prefix = var.init_prefix
66-
init_suffix = var.init_suffix
24+
device_path = local.device_paths[count.index]
6725
log_prefix = var.log_prefix
6826
log_level = var.log_level
6927
region = var.region
70-
volume_id = var.volume_id
28+
volume_id = local.volume_ids[count.index]
7129
wait_interval = var.wait_interval
7230
}
7331
}
7432

33+
data "template_file" "instance_id" {
34+
template = file("${path.module}/instance_id.tpl")
35+
}
36+
7537
output "init_snippet" {
76-
value = data.template_file.init_snippet.rendered
38+
value = <<EOF
39+
${var.init_prefix}
40+
${data.template_file.instance_id.rendered}
41+
${join("\n", data.template_file.init_snippet.*.rendered)}
42+
${var.init_suffix}
43+
EOF
7744
}
7845

Original file line numberDiff line numberDiff line change
@@ -1,34 +1,19 @@
1-
# start snippet - attach EBS volume
2-
${init_prefix}
31
export AWS_DEFAULT_REGION=${region}
42
VOLUME_ID=${volume_id}
5-
if which wget; then
6-
INSTANCE_ID="$(wget -O- http://169.254.169.254/latest/meta-data/instance-id)"
7-
elif which curl; then
8-
INSTANCE_ID="$(curl http://169.254.169.254/latest/meta-data/instance-id)"
9-
fi
3+
echo "${log_prefix} will attach $${VOLUME_ID} via the AWS API in ${region}"
4+
while ! aws ec2 attach-volume \
5+
--volume-id "$${VOLUME_ID}" \
6+
--instance-id "$${INSTANCE_ID}" \
7+
--device '${device_path}'; do
8+
echo "Attaching command failed to run. Retrying."
9+
sleep '${wait_interval}'
10+
done
11+
echo "${log_prefix} $${VOLUME_ID} attached."
12+
sleep '${wait_interval}' # Wait for device up
1013

11-
if [ "x$${INSTANCE_ID}" == "x" ]; then
12-
echo 'OS not functioning'
13-
else
14-
echo "${log_prefix} will attach $${VOLUME_ID} via the AWS API in ${region}"
15-
while ! aws ec2 attach-volume \
16-
--volume-id "$${VOLUME_ID}" \
17-
--instance-id "$${INSTANCE_ID}" \
18-
--device '${device_path}'; do
19-
echo "Attaching command failed to run. Retrying."
20-
sleep '${wait_interval}'
21-
done
22-
echo "${log_prefix} $${VOLUME_ID} attached."
23-
14+
if [ ! -e ${device_path} ]; then
2415
vol_id="$(echo "$${VOLUME_ID}" | tr -d '-')"
25-
while [ ! -e /dev/disk/by-id/*-Amazon_Elastic_Block_Store_$${vol_id} ]; do
26-
sleep '${wait_interval}'
27-
done
28-
2916
dev_id="$(ls /dev/disk/by-id/*-Amazon_Elastic_Block_Store_$${vol_id} | head -1)"
3017
dev_name="/dev/$(readlink "$${dev_id}" | tr / '\n' | tail -1)"
3118
[ "$${dev_name}" == "${device_path}" ] || ln -s "$${dev_name}" "${device_path}"
3219
fi
33-
34-
${init_suffix}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
variable "device_path" {
2+
default = "/dev/xvdf"
3+
description = "path, to the device's path in /dev/"
4+
type = string
5+
}
6+
7+
variable "device_paths" {
8+
type = list(string)
9+
description = "paths, to the device's path in /dev/"
10+
default = []
11+
}
12+
13+
variable "init_prefix" {
14+
default = ""
15+
description = "initial init (shellcode) to prefix this snippet with"
16+
type = string
17+
}
18+
19+
variable "init_suffix" {
20+
default = ""
21+
description = "init (shellcode) to append to the end of this snippet"
22+
type = string
23+
}
24+
25+
variable "log_level" {
26+
default = "info"
27+
description = "default log level verbosity for apps that support it"
28+
type = string
29+
}
30+
31+
variable "log_prefix" {
32+
default = "OPS: "
33+
description = "string to prefix log messages with"
34+
type = string
35+
}
36+
37+
variable "region" {
38+
description = "AWS region the volume is in"
39+
type = string
40+
}
41+
42+
variable "wait_interval" {
43+
default = "5"
44+
description = "time (in seconds) to wait when looping to find the device"
45+
type = number
46+
}
47+
48+
variable "volume_id" {
49+
description = "ID of the EBS volume to attach"
50+
type = string
51+
default = ""
52+
}
53+
54+
variable "volume_ids" {
55+
description = "IDs of the EBS volumes to attach"
56+
type = list(string)
57+
default = []
58+
}
59+
60+
variable "compatible_with_single_volume" {
61+
default = true
62+
description = "Using variables for single volumes or not."
63+
}

modules/persistent-ebs/data.tf

+2-4
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ data "aws_iam_policy_document" "attach_ebs_policy_doc" {
1414
"ec2:DetachVolume",
1515
]
1616

17-
resources = [
18-
"arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:volume/${aws_ebs_volume.main.id}",
19-
"arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:instance/*",
20-
]
17+
resources = concat(["arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:instance/*"],
18+
[for x in aws_ebs_volume.main.*.id : "arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:volume/${x}"])
2119
}
2220
}

modules/persistent-ebs/locals.tf

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
locals {
2+
volume_default = {
3+
type = "gp2"
4+
iops = 0
5+
size = 15
6+
encrypted = true
7+
kms_key_id = ""
8+
snapshot_id = ""
9+
}
10+
volumes_default_no_comp = [for x in var.volumes : merge(local.volume_default, x)]
11+
volumes_default = var.compatible_with_single_volume ? [{
12+
type = var.volume_type,
13+
iops = var.iops,
14+
size = var.size,
15+
encrypted = var.encrypted,
16+
kms_key_id = var.kms_key_id,
17+
snapshot_id = var.snapshot_id,
18+
name = "default"
19+
}] : local.volumes_default_no_comp
20+
}

modules/persistent-ebs/main.tf

+9-7
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,27 @@
99
*/
1010

1111
resource "aws_ebs_volume" "main" {
12+
count = length(local.volumes_default)
13+
1214
availability_zone = var.az
13-
size = var.size
14-
type = var.volume_type
15-
encrypted = var.encrypted
16-
kms_key_id = var.kms_key_id
17-
snapshot_id = var.snapshot_id
15+
size = local.volumes_default[count.index].size
16+
type = local.volumes_default[count.index].type
17+
encrypted = local.volumes_default[count.index].encrypted
18+
kms_key_id = local.volumes_default[count.index].kms_key_id
19+
snapshot_id = local.volumes_default[count.index].snapshot_id
1820

1921
# merge Name w/ extra_tags
2022
tags = merge(
2123
{
22-
"Name" = "${var.name_prefix}-${var.az}"
24+
"Name" = "${var.name_prefix}-${var.az}-${local.volumes_default[count.index].name}"
2325
},
2426
var.extra_tags,
2527
)
2628
}
2729

2830
# IAM policy that allows attaching this EBS volume to an EC2 instance
2931
resource "aws_iam_policy" "attach_ebs" {
30-
name = "${var.name_prefix}-attach-ebs-${aws_ebs_volume.main.id}"
32+
name = "${var.name_prefix}-attach-ebs"
3133
policy = data.aws_iam_policy_document.attach_ebs_policy_doc.json
3234
}
3335

modules/persistent-ebs/outputs.tf

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
output "volume_id" {
2-
value = aws_ebs_volume.main.id
1+
output "volume_ids" {
2+
value = aws_ebs_volume.main.*.id
33
description = "`id` exported from the `aws_ebs_volume`"
44
}
55

modules/persistent-ebs/variables.tf

+19
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,22 @@ variable "iam_instance_profile_role_name" {
6060
description = "The role to attach policy needed by this module."
6161
type = string
6262
}
63+
64+
variable "volumes" {
65+
type = list(map(any))
66+
### Note: what should be contained.
67+
# type = string,
68+
# iops = number,
69+
# size = number,
70+
# encrypted = bool,
71+
# kms_key_id = string,
72+
# snapshot_id = string,
73+
# name = string
74+
description = "Definition of volumes. `name` is required."
75+
default = []
76+
}
77+
78+
variable "compatible_with_single_volume" {
79+
default = true
80+
description = "Using variables for single volumes or not."
81+
}

0 commit comments

Comments
 (0)