From 0f323e8196c9af4c6a0977810a96bda8097325ec Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Mon, 5 Feb 2024 10:48:06 -0500 Subject: [PATCH 01/14] Update PostgreSQL version. --- variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.tf b/variables.tf index f0a0089..ff15a45 100644 --- a/variables.tf +++ b/variables.tf @@ -172,7 +172,7 @@ variable "hosted_zone_name" { } variable "postgres_version" { - default = "14.10" + default = "14.12" } #variable "sms_notification" { From 7cc546cddd5129f3e01e1ca07c40556911f19612 Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Fri, 8 Nov 2024 12:56:46 -0500 Subject: [PATCH 02/14] compose-init.sh: Generate random SECRET_KEY_BASE. I don't think the application is using this for anything yet, but this is supposed to be secret. --- scripts/compose-init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index f5ae7c7..78ef672 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -48,7 +48,7 @@ AVALON_REPO=${avalon_repo} DATABASE_URL=postgres://${db_avalon_username}:${db_avalon_password}@${db_avalon_address}/avalon ELASTICACHE_HOST=${redis_host_name} -SECRET_KEY_BASE=112f7d33c8864e0ef22910b45014a1d7925693ef549850974631021864e2e67b16f44aa54a98008d62f6874360284d00bb29dc08c166197d043406b42190188a +SECRET_KEY_BASE=$(tr -dc 0-9A-Za-z &- | head -c 64) AVALON_BRANCH=main AWS_REGION=${aws_region} RAILS_LOG_TO_STDOUT=true From d00263b09fefc8f4618a51ba9e83dd1b56f72a12 Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Fri, 8 Nov 2024 14:10:05 -0500 Subject: [PATCH 03/14] Add for passing extra vars to the container envs. --- compose.tf | 1 + scripts/compose-init.sh | 3 +++ variables.tf | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/compose.tf b/compose.tf index fc5def8..d997368 100644 --- a/compose.tf +++ b/compose.tf @@ -228,6 +228,7 @@ resource "aws_instance" "compose" { bib_retriever_attribute = "${var.bib_retriever_attribute}" bib_retriever_class = "${var.bib_retriever_class}" bib_retriever_class_require = "${var.bib_retriever_class_require}" + extra_docker_environment_variables = var.extra_docker_environment_variables })) vpc_security_group_ids = [ diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index 78ef672..eb8aa75 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -81,5 +81,8 @@ SETTINGS__ACTIVE_STORAGE__SERVICE=amazon SETTINGS__ACTIVE_STORAGE__BUCKET=${supplemental_files_bucket_id} CDN_HOST=https://${avalon_fqdn} +%{ for key, value in extra_docker_environment_variables ~} +${key}=${value} +%{ endfor ~} EOF sudo chown -R ec2-user /home/ec2-user/avalon-docker-aws_min diff --git a/variables.tf b/variables.tf index ff15a45..a6f010f 100644 --- a/variables.tf +++ b/variables.tf @@ -143,6 +143,12 @@ variable "environment" { type = string } +variable "extra_docker_environment_variables" { + description = "These are passed in to the compose-init.sh script" + type = map(string) + default = {} +} + variable "fcrepo_binary_bucket_username" { type = string default = "" From bcdee76abf9d06dc0e9afc1254975f1d8c3ecb95 Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Wed, 13 Nov 2024 12:28:44 -0500 Subject: [PATCH 04/14] compose-init.sh: Remove unnecessary use of Sudo. This already runs as root. See . --- scripts/compose-init.sh | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index eb8aa75..329f1d0 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -2,36 +2,34 @@ # Add SSH public key if var was set if [[ -n "${ec2_public_key}" ]]; then - # But first ensure existance and correct permissions - sudo -Hu ec2-user bash <<- EOF - umask 0077 - mkdir -p /home/ec2-user/.ssh - touch /home/ec2-user/.ssh/authorized_keys - EOF - echo "${ec2_public_key}" >> /home/ec2-user/.ssh/authorized_keys + install -d -m 0755 -o ec2-user -g ec2-user ~ec2-user/.ssh + touch ~ec2-user/.ssh/authorized_keys + chown ec2-user: ~ec2-user/.ssh/authorized_keys + chmod 0644 ~ec2-user/.ssh/authorized_keys + printf %s\\n "${ec2_public_key}" >>~ec2-user/.ssh/authorized_keys fi # Create filesystem only if there isn't one -if [[ ! `sudo file -s /dev/xvdh` == *"Linux"* ]]; then - sudo mkfs -t ext4 /dev/xvdh +if [[ ! `file -s /dev/xvdh` == *"Linux"* ]]; then + mkfs -t ext4 /dev/xvdh fi -sudo mkdir /srv/solr_data -sudo mount /dev/xvdh /srv/solr_data -sudo chown -R 8983:8983 /srv/solr_data -sudo echo /dev/xvdh /srv/solr_data ext4 defaults,nofail 0 2 >> /etc/fstab +mkdir /srv/solr_data +mount /dev/xvdh /srv/solr_data +chown -R 8983:8983 /srv/solr_data +echo /dev/xvdh /srv/solr_data ext4 defaults,nofail 0 2 >> /etc/fstab # Setup -echo '${solr_backups_efs_id}:/ /srv/solr_backups nfs nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0' | sudo tee -a /etc/fstab -sudo mkdir -p /srv/solr_backups && sudo mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev ${solr_backups_efs_dns_name}:/ /srv/solr_backups -sudo chown 8983:8983 /srv/solr_backups -sudo yum install -y docker && sudo usermod -a -G docker ec2-user && sudo systemctl enable --now docker -sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose -sudo chmod +x /usr/local/bin/docker-compose +echo '${solr_backups_efs_id}:/ /srv/solr_backups nfs nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0' | tee -a /etc/fstab +mkdir -p /srv/solr_backups && mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev ${solr_backups_efs_dns_name}:/ /srv/solr_backups +chown 8983:8983 /srv/solr_backups +yum install -y docker && usermod -a -G docker ec2-user && systemctl enable --now docker +curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose -sudo wget https://github.com/avalonmediasystem/avalon-docker/archive/aws_min.zip -O /home/ec2-user/aws_min.zip && cd /home/ec2-user && unzip aws_min.zip +wget https://github.com/avalonmediasystem/avalon-docker/archive/aws_min.zip -O /home/ec2-user/aws_min.zip && cd /home/ec2-user && unzip aws_min.zip # Create .env file -sudo cat << EOF > /home/ec2-user/avalon-docker-aws_min/.env +cat << EOF > /home/ec2-user/avalon-docker-aws_min/.env FEDORA_OPTIONS=-Dfcrepo.postgresql.host=${db_fcrepo_address} -Dfcrepo.postgresql.username=${db_fcrepo_username} -Dfcrepo.postgresql.password=${db_fcrepo_password} -Dfcrepo.postgresql.port=${db_fcrepo_port} -Daws.accessKeyId=${fcrepo_binary_bucket_access_key} -Daws.secretKey=${fcrepo_binary_bucket_secret_key} -Daws.bucket=${fcrepo_binary_bucket_id} FEDORA_LOGGROUP=${compose_log_group_name}/fedora.log FEDORA_MODESHAPE_CONFIG=classpath:/config/jdbc-postgresql-s3/repository${fcrepo_db_ssl ? "-ssl" : ""}.json @@ -85,4 +83,4 @@ CDN_HOST=https://${avalon_fqdn} ${key}=${value} %{ endfor ~} EOF -sudo chown -R ec2-user /home/ec2-user/avalon-docker-aws_min +chown -R ec2-user /home/ec2-user/avalon-docker-aws_min From 8952fac4e7314bc1e88554149bbcba55473a1350 Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Wed, 13 Nov 2024 14:53:02 -0500 Subject: [PATCH 05/14] Allow adding users to the EC2 instance. --- compose.tf | 1 + scripts/compose-init.sh | 18 ++++++++++++++++++ terraform.tfvars.example | 19 ++++++++++++++++++- variables.tf | 11 ++++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/compose.tf b/compose.tf index d997368..eb80bbb 100644 --- a/compose.tf +++ b/compose.tf @@ -190,6 +190,7 @@ resource "aws_instance" "compose" { user_data = base64encode(templatefile("scripts/compose-init.sh", { ec2_public_key = "${var.ec2_public_key}" + ec2_users = var.ec2_users solr_backups_efs_id = "${aws_efs_file_system.solr_backups.id}" solr_backups_efs_dns_name = "${aws_efs_file_system.solr_backups.dns_name}" db_fcrepo_address = "${module.db_fcrepo.db_instance_address}" diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index 329f1d0..39edf59 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -9,6 +9,24 @@ if [[ -n "${ec2_public_key}" ]]; then printf %s\\n "${ec2_public_key}" >>~ec2-user/.ssh/authorized_keys fi +groupadd --system docker + +# Allow all users in the wheel group to run all commands without a password. +sed -i 's/^# \(%wheel\s\+ALL=(ALL)\s\+NOPASSWD: ALL$\)/\1/' /etc/sudoers + +%{ for username, user_config in ec2_users ~} +useradd --comment "${user_config.gecos}" --groups adm,wheel,docker "${username}" +install -d -o "${username}" -g "${username}" ~${username}/.ssh +install -m 0644 -o "${username}" -g "${username}" \ + /dev/null ~${username}/.ssh/authorized_keys +%{ for ssh_key in user_config.ssh_keys ~} +printf %s\\n "${ssh_key}" >>~${username}/.ssh/authorized_keys +%{ endfor ~} +%{ for setup_command in user_config.setup_commands ~} +${setup_command} +%{ endfor } +%{ endfor ~} + # Create filesystem only if there isn't one if [[ ! `file -s /dev/xvdh` == *"Linux"* ]]; then mkfs -t ext4 /dev/xvdh diff --git a/terraform.tfvars.example b/terraform.tfvars.example index e9bb843..2d58be9 100644 --- a/terraform.tfvars.example +++ b/terraform.tfvars.example @@ -1,9 +1,26 @@ environment = "dev" #zone_prefix = "" hosted_zone_name = "mydomain.org" -# At least one of ec2_keyname or ec2_public_key must be set +# At least one of 'ec2_keyname' or 'ec2_public_key', or 'ec2_users' must be set +# for you to have access to your EC2 instance. #ec2_keyname = "my-ec2-key" #ec2_public_key = "" +#ec2_users = [ +# user = { +# ssh_keys = [ +# "ssh-ed25519 ..." +# ] +# } +# another = { +# gecos = "Another User" +# ssh_keys = [ +# "ssh-rsa ..." +# ] +# setup_commands = [ +# "echo 'set editing-mode vi' | install -m 0644 -o another -g another /dev/stdin ~another/.inputrc" +# ] +# } +#] stack_name = "mystack" ssh_cidr_blocks = [] # If the user below is empty, Terraform will attempt to diff --git a/variables.tf b/variables.tf index a6f010f..2bec63d 100644 --- a/variables.tf +++ b/variables.tf @@ -117,7 +117,7 @@ variable "db_fcrepo_username" { variable "ec2_keyname" { type = string - default = "" + default = null description = "The name of an AWS EC2 key pair to use for authenticating" } @@ -127,6 +127,15 @@ variable "ec2_public_key" { description = "A SSH public key string to use for authenticating" } +variable "ec2_users" { + type = map(object({ + gecos = optional(string, "") + ssh_keys = optional(list(string), []) + setup_commands = optional(list(string), []) + })) + default = {} +} + variable "email_comments" { type = string } From eb1bf60108a6ca6f9caf6bf1832c8e03e58ef273 Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Fri, 15 Nov 2024 11:28:23 -0500 Subject: [PATCH 06/14] Use correct device for Solr data and fail safely. This was using a nonexistent device for the Solr data. An alias to the correct device is declared as the 'device_name' attribute on 'aws_ebs_volume.solr_data', so this uses that instead. This also makes the mountpoint unwriteable if it can't be mounted, so Solr will fail rather than writing its data to the EC2 instance's root filesystem. --- compose.tf | 7 ++++++- scripts/compose-init.sh | 22 ++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/compose.tf b/compose.tf index eb80bbb..17c7e8e 100644 --- a/compose.tf +++ b/compose.tf @@ -36,6 +36,10 @@ data "aws_iam_policy_document" "compose" { } } +locals { + solr_data_device_name = "/dev/sdh" +} + resource "aws_iam_policy" "this_bucket_policy" { name = "${local.namespace}-compose-bucket-access" policy = data.aws_iam_policy_document.this_bucket_access.json @@ -191,6 +195,7 @@ resource "aws_instance" "compose" { user_data = base64encode(templatefile("scripts/compose-init.sh", { ec2_public_key = "${var.ec2_public_key}" ec2_users = var.ec2_users + solr_data_device_name = local.solr_data_device_name solr_backups_efs_id = "${aws_efs_file_system.solr_backups.id}" solr_backups_efs_dns_name = "${aws_efs_file_system.solr_backups.dns_name}" db_fcrepo_address = "${module.db_fcrepo.db_instance_address}" @@ -290,7 +295,7 @@ resource "aws_cloudwatch_log_group" "compose_log_group" { } resource "aws_volume_attachment" "compose_solr" { - device_name = "/dev/sdh" + device_name = local.solr_data_device_name volume_id = aws_ebs_volume.solr_data.id instance_id = aws_instance.compose.id } diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index 39edf59..b7669ea 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -1,5 +1,7 @@ #!/bin/bash +declare -r SOLR_DATA_DEVICE=${solr_data_device_name} + # Add SSH public key if var was set if [[ -n "${ec2_public_key}" ]]; then install -d -m 0755 -o ec2-user -g ec2-user ~ec2-user/.ssh @@ -27,15 +29,19 @@ ${setup_command} %{ endfor } %{ endfor ~} -# Create filesystem only if there isn't one -if [[ ! `file -s /dev/xvdh` == *"Linux"* ]]; then - mkfs -t ext4 /dev/xvdh -fi +# +# Configure Solr data mount. +# + +# Only format the Solr disk if it's blank. +blkid --probe "$SOLR_DATA_DEVICE" -o export | grep -qE "^PTTYPE=|^TYPE=" || + mkfs -t ext4 "$SOLR_DATA_DEVICE" -mkdir /srv/solr_data -mount /dev/xvdh /srv/solr_data -chown -R 8983:8983 /srv/solr_data -echo /dev/xvdh /srv/solr_data ext4 defaults,nofail 0 2 >> /etc/fstab +install -d -m 0 /srv/solr_data +echo "$SOLR_DATA_DEVICE /srv/solr_data ext4 defaults 0 2" >>/etc/fstab +# If the mountpoint couldn't be mounted, leave it mode 0 so Solr will fail +# safely. +mount /srv/solr_data && chown -R 8983:8983 /srv/solr_data # Setup echo '${solr_backups_efs_id}:/ /srv/solr_backups nfs nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0' | tee -a /etc/fstab From 60ca721edb76b5bc790712640f3fb5fccd5a359a Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Fri, 15 Nov 2024 12:11:31 -0500 Subject: [PATCH 07/14] compose: Rebuild the EC2 VM as needed, etc. The VM should be rebuilt when its configuration changes or when there's a newer VM image. Also, since the "user data" script isn't idempotent, the VM should be rebuilt rather than restarted when the script changes. Lowering the TTL on the VM's A record, which is only used for administrative access, will make it available faster without imposing a burden on DNS. --- compose.tf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compose.tf b/compose.tf index 17c7e8e..dfda6cf 100644 --- a/compose.tf +++ b/compose.tf @@ -243,16 +243,14 @@ resource "aws_instance" "compose" { aws_security_group.public_ip.id, ] - lifecycle { - ignore_changes = [ami, user_data] - } + user_data_replace_on_change = true } resource "aws_route53_record" "ec2_hostname" { zone_id = module.dns.public_zone_id name = local.ec2_hostname type = "A" - ttl = 300 + ttl = 30 records = [aws_instance.compose.public_ip] } From 29537f716d02b064ff4dc9c37fd32264ebf78ea1 Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Fri, 15 Nov 2024 12:30:00 -0500 Subject: [PATCH 08/14] compose: Remove unnecessary string interpolation. --- compose.tf | 78 +++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/compose.tf b/compose.tf index dfda6cf..023d1dd 100644 --- a/compose.tf +++ b/compose.tf @@ -193,47 +193,47 @@ resource "aws_instance" "compose" { } user_data = base64encode(templatefile("scripts/compose-init.sh", { - ec2_public_key = "${var.ec2_public_key}" + ec2_public_key = var.ec2_public_key ec2_users = var.ec2_users solr_data_device_name = local.solr_data_device_name - solr_backups_efs_id = "${aws_efs_file_system.solr_backups.id}" - solr_backups_efs_dns_name = "${aws_efs_file_system.solr_backups.dns_name}" - db_fcrepo_address = "${module.db_fcrepo.db_instance_address}" - db_fcrepo_username = "${module.db_fcrepo.db_instance_username}" - db_fcrepo_password = "${module.db_fcrepo.db_instance_password}" - db_fcrepo_port = "${module.db_fcrepo.db_instance_port}" - db_avalon_address = "${module.db_avalon.db_instance_address}" - db_avalon_username = "${module.db_avalon.db_instance_username}" - db_avalon_password = "${module.db_avalon.db_instance_password}" - fcrepo_binary_bucket_access_key = "${length(var.fcrepo_binary_bucket_username) > 0 ? var.fcrepo_binary_bucket_access_key : values(aws_iam_access_key.fcrepo_bin_created_access)[0].id }" - fcrepo_binary_bucket_secret_key = "${length(var.fcrepo_binary_bucket_username) > 0 ? var.fcrepo_binary_bucket_secret_key : values(aws_iam_access_key.fcrepo_bin_created_access)[0].secret }" - fcrepo_binary_bucket_id = "${aws_s3_bucket.fcrepo_binary_bucket.id}" - compose_log_group_name = "${aws_cloudwatch_log_group.compose_log_group.name}" - fcrepo_db_ssl = "${var.fcrepo_db_ssl}" - derivatives_bucket_id = "${aws_s3_bucket.this_derivatives.id}" - masterfiles_bucket_id = "${aws_s3_bucket.this_masterfiles.id}" - preservation_bucket_id = "${aws_s3_bucket.this_preservation.id}" - supplemental_files_bucket_id = "${aws_s3_bucket.this_supplemental_files.id}" - avalon_ecr_repository_url = "${aws_ecr_repository.avalon.repository_url}" - avalon_repo = "${var.avalon_repo}" - redis_host_name = "${aws_route53_record.redis.name}" - aws_region = "${var.aws_region}" - avalon_fqdn = "${length(var.alt_hostname) > 0 ? values(var.alt_hostname)[0].hostname : aws_route53_record.alb.fqdn}" - streaming_fqdn = "${aws_route53_record.alb_streaming.fqdn}" - elastictranscoder_pipeline_id = "${aws_elastictranscoder_pipeline.this_pipeline.id}" - email_comments = "${var.email_comments}" - email_notification = "${var.email_notification}" - email_support = "${var.email_support}" - avalon_admin = "${var.avalon_admin}" - bib_retriever_protocol = "${var.bib_retriever_protocol}" - bib_retriever_url = "${var.bib_retriever_url}" - bib_retriever_query = "${var.bib_retriever_query}" - bib_retriever_host = "${var.bib_retriever_host}" - bib_retriever_port = "${var.bib_retriever_port}" - bib_retriever_database = "${var.bib_retriever_database}" - bib_retriever_attribute = "${var.bib_retriever_attribute}" - bib_retriever_class = "${var.bib_retriever_class}" - bib_retriever_class_require = "${var.bib_retriever_class_require}" + solr_backups_efs_id = aws_efs_file_system.solr_backups.id + solr_backups_efs_dns_name = aws_efs_file_system.solr_backups.dns_name + db_fcrepo_address = module.db_fcrepo.db_instance_address + db_fcrepo_username = module.db_fcrepo.db_instance_username + db_fcrepo_password = module.db_fcrepo.db_instance_password + db_fcrepo_port = module.db_fcrepo.db_instance_port + db_avalon_address = module.db_avalon.db_instance_address + db_avalon_username = module.db_avalon.db_instance_username + db_avalon_password = module.db_avalon.db_instance_password + fcrepo_binary_bucket_access_key = length(var.fcrepo_binary_bucket_username) > 0 ? var.fcrepo_binary_bucket_access_key : values(aws_iam_access_key.fcrepo_bin_created_access)[0].id + fcrepo_binary_bucket_secret_key = length(var.fcrepo_binary_bucket_username) > 0 ? var.fcrepo_binary_bucket_secret_key : values(aws_iam_access_key.fcrepo_bin_created_access)[0].secret + fcrepo_binary_bucket_id = aws_s3_bucket.fcrepo_binary_bucket.id + compose_log_group_name = aws_cloudwatch_log_group.compose_log_group.name + fcrepo_db_ssl = var.fcrepo_db_ssl + derivatives_bucket_id = aws_s3_bucket.this_derivatives.id + masterfiles_bucket_id = aws_s3_bucket.this_masterfiles.id + preservation_bucket_id = aws_s3_bucket.this_preservation.id + supplemental_files_bucket_id = aws_s3_bucket.this_supplemental_files.id + avalon_ecr_repository_url = aws_ecr_repository.avalon.repository_url + avalon_repo = var.avalon_repo + redis_host_name = aws_route53_record.redis.name + aws_region = var.aws_region + avalon_fqdn = length(var.alt_hostname) > 0 ? values(var.alt_hostname)[0].hostname : aws_route53_record.alb.fqdn + streaming_fqdn = aws_route53_record.alb_streaming.fqdn + elastictranscoder_pipeline_id = aws_elastictranscoder_pipeline.this_pipeline.id + email_comments = var.email_comments + email_notification = var.email_notification + email_support = var.email_support + avalon_admin = var.avalon_admin + bib_retriever_protocol = var.bib_retriever_protocol + bib_retriever_url = var.bib_retriever_url + bib_retriever_query = var.bib_retriever_query + bib_retriever_host = var.bib_retriever_host + bib_retriever_port = var.bib_retriever_port + bib_retriever_database = var.bib_retriever_database + bib_retriever_attribute = var.bib_retriever_attribute + bib_retriever_class = var.bib_retriever_class + bib_retriever_class_require = var.bib_retriever_class_require extra_docker_environment_variables = var.extra_docker_environment_variables })) From b3687c861019a33ae8ff235ccaf3cb802bfc678c Mon Sep 17 00:00:00 2001 From: Renee Margaret McConahy Date: Mon, 18 Nov 2024 11:36:39 -0500 Subject: [PATCH 09/14] compose-init.sh: Refactor. --- scripts/compose-init.sh | 69 ++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index b7669ea..3f2c6e3 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -1,7 +1,16 @@ #!/bin/bash +# +# Check the output from this script on the EC2 VM with: +# +# journalctl -u cloud-final +# declare -r SOLR_DATA_DEVICE=${solr_data_device_name} +# +# Add and configure users. +# + # Add SSH public key if var was set if [[ -n "${ec2_public_key}" ]]; then install -d -m 0755 -o ec2-user -g ec2-user ~ec2-user/.ssh @@ -16,6 +25,9 @@ groupadd --system docker # Allow all users in the wheel group to run all commands without a password. sed -i 's/^# \(%wheel\s\+ALL=(ALL)\s\+NOPASSWD: ALL$\)/\1/' /etc/sudoers +# The EC2 user's home directory can safely be made world-readable +chmod 0755 ~ec2-user + %{ for username, user_config in ec2_users ~} useradd --comment "${user_config.gecos}" --groups adm,wheel,docker "${username}" install -d -o "${username}" -g "${username}" ~${username}/.ssh @@ -30,7 +42,7 @@ ${setup_command} %{ endfor ~} # -# Configure Solr data mount. +# Configure filesystems. # # Only format the Solr disk if it's blank. @@ -41,19 +53,47 @@ install -d -m 0 /srv/solr_data echo "$SOLR_DATA_DEVICE /srv/solr_data ext4 defaults 0 2" >>/etc/fstab # If the mountpoint couldn't be mounted, leave it mode 0 so Solr will fail # safely. -mount /srv/solr_data && chown -R 8983:8983 /srv/solr_data - -# Setup -echo '${solr_backups_efs_id}:/ /srv/solr_backups nfs nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev 0 0' | tee -a /etc/fstab -mkdir -p /srv/solr_backups && mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,_netdev ${solr_backups_efs_dns_name}:/ /srv/solr_backups -chown 8983:8983 /srv/solr_backups -yum install -y docker && usermod -a -G docker ec2-user && systemctl enable --now docker -curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose -chmod +x /usr/local/bin/docker-compose - -wget https://github.com/avalonmediasystem/avalon-docker/archive/aws_min.zip -O /home/ec2-user/aws_min.zip && cd /home/ec2-user && unzip aws_min.zip -# Create .env file -cat << EOF > /home/ec2-user/avalon-docker-aws_min/.env +if mount /srv/solr_data; then + chown -R 8983:8983 /srv/solr_data +else + echo "Error: Could not mount solr_data EBS volume." >&2 +fi + +install -d -m 0 /srv/solr_backups +echo '${solr_backups_efs_id}:/ /srv/solr_backups nfs _netdev 0 0' >>/etc/fstab +if mount /srv/solr_backups; then + chown -R 8983:8983 /srv/solr_backups +else + echo "Error: Could not mount solr_backups EFS volume." >&2 +fi + +# +# Install Avalon dependencies. +# + +yum install -y docker +systemctl enable --now docker +usermod -a -G docker ec2-user + +tmp=$(mktemp -d) || exit 1 +curl -L "$(printf %s "https://github.com/docker/compose/releases/latest/" \ + "download/docker-compose-$(uname -s)-$(uname -m)" )" \ + -o "$tmp/docker-compose" && + install -t /usr/local/bin "$tmp/docker-compose" +rm -rf -- "$tmp" +unset tmp + +curl -L https://github.com/avalonmediasystem/avalon-docker/archive/aws_min.zip | + install -m 0644 -o ec2-user -g ec2-user /dev/stdin ~ec2-user/aws_min.zip && + setpriv --reuid ec2-user --regid ec2-user --clear-groups -- \ + unzip -d ~ec2-user ~ec2-user/aws_min.zip + +# +# Set up Avalon. +# + +install -m 0600 -o ec2-user -g ec2-user \ + /dev/stdin ~ec2-user/avalon-docker-aws_min/.env < Date: Wed, 18 Dec 2024 14:56:20 -0500 Subject: [PATCH 10/14] Allow configuring which branch/commit of avalon-docker to use --- build.tf | 2 +- compose.tf | 3 +++ scripts/compose-init.sh | 10 ++++++---- variables.tf | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/build.tf b/build.tf index d0d1775..d7ab721 100644 --- a/build.tf +++ b/build.tf @@ -165,7 +165,7 @@ phases: - echo Pushing the Docker images... - docker push $AVALON_DOCKER_REPO:$AVALON_COMMIT - docker push $AVALON_DOCKER_REPO:latest - - aws ssm send-command --document-name "AWS-RunShellScript" --document-version "1" --targets '[{"Key":"InstanceIds","Values":["${aws_instance.compose.id}"]}]' --parameters '{"commands":["$(aws ecr get-login --region ${local.region} --no-include-email) && docker-compose pull && docker-compose up -d"],"workingDirectory":["/home/ec2-user/avalon-docker-aws_min"],"executionTimeout":["360"]}' --timeout-seconds 600 --max-concurrency "50" --max-errors "0" --cloud-watch-output-config '{"CloudWatchLogGroupName":"avalon-${var.environment}/ssm","CloudWatchOutputEnabled":true}' --region us-east-1 + - aws ssm send-command --document-name "AWS-RunShellScript" --document-version "1" --targets '[{"Key":"InstanceIds","Values":["${aws_instance.compose.id}"]}]' --parameters '{"commands":["$(aws ecr get-login --region ${local.region} --no-include-email) && docker-compose pull && docker-compose up -d"],"workingDirectory":["/home/ec2-user/avalon-docker"],"executionTimeout":["360"]}' --timeout-seconds 600 --max-concurrency "50" --max-errors "0" --cloud-watch-output-config '{"CloudWatchLogGroupName":"avalon-${var.environment}/ssm","CloudWatchOutputEnabled":true}' --region us-east-1 BUILDSPEC } diff --git a/compose.tf b/compose.tf index 023d1dd..72954ea 100644 --- a/compose.tf +++ b/compose.tf @@ -216,6 +216,9 @@ resource "aws_instance" "compose" { supplemental_files_bucket_id = aws_s3_bucket.this_supplemental_files.id avalon_ecr_repository_url = aws_ecr_repository.avalon.repository_url avalon_repo = var.avalon_repo + avalon_docker_code_repo = var.avalon_docker_code_repo + avalon_docker_code_branch = var.avalon_docker_code_branch + avalon_docker_code_commit = var.avalon_docker_code_commit redis_host_name = aws_route53_record.redis.name aws_region = var.aws_region avalon_fqdn = length(var.alt_hostname) > 0 ? values(var.alt_hostname)[0].hostname : aws_route53_record.alb.fqdn diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index 3f2c6e3..68fa9ea 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -83,17 +83,19 @@ curl -L "$(printf %s "https://github.com/docker/compose/releases/latest/" \ rm -rf -- "$tmp" unset tmp -curl -L https://github.com/avalonmediasystem/avalon-docker/archive/aws_min.zip | - install -m 0644 -o ec2-user -g ec2-user /dev/stdin ~ec2-user/aws_min.zip && +declare -r AVALON_DOCKER_CHECKOUT_NAME=%{ if avalon_docker_code_branch != "" }${avalon_docker_code_branch}%{ else }${avalon_docker_code_commit}%{ endif } +curl -L ${avalon_docker_code_repo}/archive/$AVALON_DOCKER_CHECKOUT_NAME.zip > avalon-docker.zip | + install -m 0644 -o ec2-user -g ec2-user /dev/stdin ~ec2-user/avalon-docker.zip && setpriv --reuid ec2-user --regid ec2-user --clear-groups -- \ - unzip -d ~ec2-user ~ec2-user/aws_min.zip + unzip -d ~ec2-user ~ec2-user/avalon-docker.zip +mv ~ec2-user/avalon-docker-$AVALON_DOCKER_CHECKOUT_NAME ~ec2-user/avalon-docker # # Set up Avalon. # install -m 0600 -o ec2-user -g ec2-user \ - /dev/stdin ~ec2-user/avalon-docker-aws_min/.env < Date: Wed, 18 Dec 2024 15:12:31 -0500 Subject: [PATCH 11/14] Follow pattern elsewhere and use install to condense commands --- scripts/compose-init.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index 68fa9ea..b5e46a8 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -14,9 +14,7 @@ declare -r SOLR_DATA_DEVICE=${solr_data_device_name} # Add SSH public key if var was set if [[ -n "${ec2_public_key}" ]]; then install -d -m 0755 -o ec2-user -g ec2-user ~ec2-user/.ssh - touch ~ec2-user/.ssh/authorized_keys - chown ec2-user: ~ec2-user/.ssh/authorized_keys - chmod 0644 ~ec2-user/.ssh/authorized_keys + install -m 0644 -o ec2-user -g ec2-user /dev/null ~ec2-user/.ssh/authorized_keys printf %s\\n "${ec2_public_key}" >>~ec2-user/.ssh/authorized_keys fi From 7b8dc84c4f930fb2e9c65377229c38a35d184d4a Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 20 Dec 2024 12:50:52 -0500 Subject: [PATCH 12/14] Fix setting of AVALON_COMMIT env var and bump codebuild image to 7.0 tag --- build.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.tf b/build.tf index d7ab721..1632e62 100644 --- a/build.tf +++ b/build.tf @@ -106,7 +106,7 @@ resource "aws_codebuild_project" "docker" { environment { compute_type = "BUILD_GENERAL1_MEDIUM" - image = "aws/codebuild/standard:6.0" + image = "aws/codebuild/standard:7.0" type = "LINUX_CONTAINER" image_pull_credentials_type = "CODEBUILD" privileged_mode = true @@ -151,7 +151,7 @@ phases: - echo Logging in to Amazon ECR... - aws --version - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AVALON_DOCKER_REPO - - (test -z "$AVALON_COMMIT" && AVALON_COMMIT=`git ls-remote $AVALON_REPO refs/heads/$AVALON_BRANCH | cut -f 1`) || true + - test -z "$AVALON_COMMIT" && AVALON_COMMIT=`git ls-remote $AVALON_REPO refs/heads/$AVALON_BRANCH | cut -f 1` || true - AVALON_DOCKER_CACHE_TAG=$AVALON_COMMIT - docker pull $AVALON_DOCKER_REPO:$AVALON_DOCKER_CACHE_TAG || docker pull $AVALON_DOCKER_REPO:latest || true build: From 0da856be9fa6858eb141fcc8453b84e20aefa58d Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 20 Dec 2024 14:51:45 -0500 Subject: [PATCH 13/14] Fix issue with downloading avalon-docker zip file --- scripts/compose-init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index b5e46a8..13c50ca 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -82,7 +82,7 @@ rm -rf -- "$tmp" unset tmp declare -r AVALON_DOCKER_CHECKOUT_NAME=%{ if avalon_docker_code_branch != "" }${avalon_docker_code_branch}%{ else }${avalon_docker_code_commit}%{ endif } -curl -L ${avalon_docker_code_repo}/archive/$AVALON_DOCKER_CHECKOUT_NAME.zip > avalon-docker.zip | +curl -L ${avalon_docker_code_repo}/archive/$AVALON_DOCKER_CHECKOUT_NAME.zip | install -m 0644 -o ec2-user -g ec2-user /dev/stdin ~ec2-user/avalon-docker.zip && setpriv --reuid ec2-user --regid ec2-user --clear-groups -- \ unzip -d ~ec2-user ~ec2-user/avalon-docker.zip From 45fd20687eec9a0feb7edd9c1ab3a85b7217a723 Mon Sep 17 00:00:00 2001 From: Chris Colvard Date: Fri, 20 Dec 2024 17:36:40 -0500 Subject: [PATCH 14/14] Favor commit over branch since branch has a default value and commit does not --- scripts/compose-init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/compose-init.sh b/scripts/compose-init.sh index 13c50ca..0fab42d 100644 --- a/scripts/compose-init.sh +++ b/scripts/compose-init.sh @@ -81,7 +81,7 @@ curl -L "$(printf %s "https://github.com/docker/compose/releases/latest/" \ rm -rf -- "$tmp" unset tmp -declare -r AVALON_DOCKER_CHECKOUT_NAME=%{ if avalon_docker_code_branch != "" }${avalon_docker_code_branch}%{ else }${avalon_docker_code_commit}%{ endif } +declare -r AVALON_DOCKER_CHECKOUT_NAME=%{ if avalon_docker_code_commit != "" }${avalon_docker_code_commit}%{ else }${avalon_docker_code_branch}%{ endif } curl -L ${avalon_docker_code_repo}/archive/$AVALON_DOCKER_CHECKOUT_NAME.zip | install -m 0644 -o ec2-user -g ec2-user /dev/stdin ~ec2-user/avalon-docker.zip && setpriv --reuid ec2-user --regid ec2-user --clear-groups -- \