Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ansible for the docs-rs builder #231

Merged
merged 15 commits into from
Feb 13, 2023
4 changes: 4 additions & 0 deletions ansible/envs/staging/group_vars/docs-rs-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---

sha: "{{ lookup('aws_ssm', '/docs-rs/builder/sha') }}"
vars_repository_sha: "{{ sha | ternary(sha, 'HEAD') }}"
14 changes: 14 additions & 0 deletions ansible/playbooks/docs-rs-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---

- hosts: docs-rs-builder
become: yes
become_user: root
jyn514 marked this conversation as resolved.
Show resolved Hide resolved

roles:

- role: common
papertrail_url: "{{ vars_papertrail_url }}"
collect_metrics_from: "{{ global_collect_metrics_from }}"
sudo_users: "{{ global_sudo_users }}"

- role: docs-rs-builder
2 changes: 1 addition & 1 deletion ansible/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ansible==2.9.26
boto3==1.13.16
boto3==1.14.0
2 changes: 1 addition & 1 deletion ansible/roles/common/tasks/metrics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
state: absent

when: |
node_exporter_capture.rc != 0 or not node_exporter_capture.stderr.startswith(
node_exporter_capture.rc != 0 or not node_exporter_capture.stdout.startswith(
rylev marked this conversation as resolved.
Show resolved Hide resolved
"node_exporter, version " + node_exporter_version
)

Expand Down
21 changes: 21 additions & 0 deletions ansible/roles/docs-rs-builder/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---

vars_mountpoint_file_path: /builder.fs
vars_mountpoint_size: 53687091200
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you determine this number? I'm not sure 50 GB is enough; how often is this builder going to be torn down and recreated? Cargo has several caches that grow without bound, we ended up bumping this all the way to 200 GB in prod because we kept running out of space on the 100 GB disk.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was completely unscientific. I took what the playground is using (which was my basis for these tasks) and added some zeros until I didn't run out of memory. I think we'll need to think more about this...

vars_mountpoint_path: /mnt/builder

vars_user: builder
vars_group: "{{ vars_user }}"
vars_home_path: "/home/{{ vars_user }}"

vars_repository_url: https://github.com/rust-lang/docs.rs
vars_repository_sha: HEAD

vars_checkout_path: "{{ vars_home_path }}/builder"
vars_working_dir: "{{ vars_checkout_path }}"
vars_executable_path: "{{ vars_working_dir }}/target/release/cratesfyi"
rylev marked this conversation as resolved.
Show resolved Hide resolved

vars_toolchain: nightly

vars_database_url: "{{ lookup('aws_ssm', '/docs-rs/database-url') }}"
vars_s3_bucket: docs-rs-storage-519825364412
7 changes: 7 additions & 0 deletions ansible/roles/docs-rs-builder/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---

- name: restart-docker
systemd:
name: docker
state: restarted
daemon_reload: true
152 changes: 152 additions & 0 deletions ansible/roles/docs-rs-builder/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---

- name: Create user
user:
name: "{{ vars_user }}"
groups: docker
append: true

# --------------------

# Install Docker

- name: Add Docker APT repository GPG key
apt_key:
state: present
keyserver: "https://download.docker.com/linux/{{ ansible_distribution|lower }}/gpg"
id: 7EA0A9C3F273FCD8

- name: Add Docker APT repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution|lower }} {{ ansible_distribution_release }} stable"
state: present
update_cache: true

- name: Install Docker
apt:
name: docker-ce
state: present
notify: restart-docker

- name: Make sure /etc/docker dir exists
file:
path: /etc/docker
state: directory

- name: Configure Docker daemon
template:
src: daemon.json
dest: /etc/docker/daemon.json
mode: 0600
notify: restart-docker

# --------------------

# Set up a partition with limited space to avoid temporary
# input/output files consuming all of the space on the primary
# partition.

- name: Check for mountpoint file
stat:
path: "{{ vars_mountpoint_file_path }}"
register: mountpoint_stat

- name: Create mountpoint file
block:
- name: Allocate file
command: "fallocate -l {{ vars_mountpoint_size }} {{ vars_mountpoint_file_path }}"

- name: Locate mountpoint loopback device
command: "losetup -f --show {{ vars_mountpoint_file_path }}"
register: loopback

- name: Partition mountpoint
command: "mkfs -t ext3 -m 1 -v {{ loopback.stdout }}"
when: mountpoint_stat.stat.size is not defined or mountpoint_stat.stat.size != vars_mountpoint_size

- name: Create mountpoint
file:
path: "{{ vars_mountpoint_path }}"
state: directory

- name: Mount mountpoint
mount:
src: "{{ vars_mountpoint_file_path }}"
name: "{{ vars_mountpoint_path }}"
fstype: ext3
state: mounted

- name: Change kubeconfig file permission
file:
path: "{{ vars_mountpoint_path }}"
owner: "{{ vars_user }}"
group: docker

# --------------------

- name: Checkout repository
become: true
become_user: "{{ vars_user }}"
git:
repo: "{{ vars_repository_url }}"
dest: "{{ vars_checkout_path }}"
version: "{{ vars_repository_sha }}"

- name: Install awscli
apt:
name: awscli
state: present

- name: Allow user to start and stop service
template:
src: sudoers
dest: "/etc/sudoers.d/{{ vars_user }}"
mode: 0440

# ---------

- name: install apt packages
apt:
name:
- build-essential
- cmake
- libssl-dev
- pkg-config
state: present

- name: check if cargo is installed
shell: |
test -f ~/.cargo/env && . ~/.cargo/env && command -v cargo
register: cargo_exists
ignore_errors: yes

- name: Download Installer
when: cargo_exists is failed
get_url:
url: https://sh.rustup.rs
dest: /tmp/sh.rustup.rs
mode: '0755'
force: 'yes'

- name: install rust/cargo
when: cargo_exists is failed
shell: /tmp/sh.rustup.rs -y

- name: Build builder
shell: |
. ~/.cargo/env
cargo build --release
args:
chdir: "{{ vars_checkout_path }}"

- name: Configure service
template:
src: builder.service
dest: /etc/systemd/system/builder.service
mode: 0644

- name: Start and enable service
systemd:
name: builder
state: started
enabled: true
27 changes: 27 additions & 0 deletions ansible/roles/docs-rs-builder/templates/builder.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
# {{ ansible_managed }}
#

[Unit]
Description=The docs.rs Builder

[Service]
Environment=TMPDIR={{ vars_mountpoint_path }}
Environment=DOCSRS_PREFIX={{ vars_mountpoint_path }}
Environment=DOCSRS_RUSTWIDE_WORKSPACE={{ vars_mountpoint_path }}
Environment=DOCSRS_LOG=debug
Environment=DOCSRS_DATABASE_URL={{ vars_database_url }}
Environment=DOCSRS_DOCKER_IMAGE=ghcr.io/rust-lang/crates-build-env/linux
Environment=DOCSRS_STORAGE_BACKEND=s3
Environment=S3_REGION=us-east-1
Environment=DOCSRS_S3_BUCKET={{ vars_s3_bucket }}
Environment=RUST_BACKTRACE=1
Environment=DOCSRS_TOOLCHAIN={{ vars_toolchain }}

LimitNOFILE=20000 # Consider removing now that we upload zip files
Restart=on-failure
WorkingDirectory={{ vars_working_dir }}
ExecStart={{ vars_executable_path }} start-build-server

[Install]
WantedBy=multi-user.target
3 changes: 3 additions & 0 deletions ansible/roles/docs-rs-builder/templates/daemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"storage-driver": "overlay2"
}
7 changes: 7 additions & 0 deletions ansible/roles/docs-rs-builder/templates/sudoers
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# {{ ansible_managed }}
#

builder ALL= NOPASSWD: /bin/systemctl stop builder.service
builder ALL= NOPASSWD: /bin/systemctl start builder.service
rylev marked this conversation as resolved.
Show resolved Hide resolved
builder ALL= NOPASSWD: /bin/systemctl restart builder.service
44 changes: 44 additions & 0 deletions terragrunt/modules/docs-rs/builder.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// The instance profile the builder will assume when communicating with
// other AWS services.

resource "aws_iam_instance_profile" "builder" {
name = "builder"
role = aws_iam_role.builder.name
}

resource "aws_iam_role" "builder" {
name = "builder"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = ["sts:AssumeRole"]
}
]
})
}

resource "aws_iam_role_policy" "builder_s3" {
role = aws_iam_role.builder.name
name = "builder_s3"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
// Access to s3
{
Effect = "Allow"
Action = "s3:*"

Resource = [
aws_s3_bucket.storage.arn,
"${aws_s3_bucket.storage.arn}/*"
Comment on lines +29 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to make this append-only, so it can't delete or read existing files?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can set the Action above to only non-delete write operations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried moving to only allowing the following actions:

PutObject, PutObjectTagging, PutObjectAcl, CreateBucket

This failed in a strange way. All artifacts were created and successfully uploaded to the s3. The logs only showed one error which simply stated that the library in question could not be documented (even though it clearly was successfully being done so). There were no other errors in the logs as far as I could see to indicate some additional error. Any idea what might be happening here?

]
}
]
})
}