-
Notifications
You must be signed in to change notification settings - Fork 77
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
Add initial support for packer #242
base: master
Are you sure you want to change the base?
Changes from all commits
1662f19
3f245eb
3ee6f80
0f74683
2af0d77
177fad6
0973037
95fd311
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# Utilities for creating the Ansible environment we use. | ||
|
||
import subprocess | ||
import pathlib | ||
import sys | ||
import shutil | ||
|
||
BASE_PATH = pathlib.Path(__file__).resolve().parent | ||
VENV_PATH = BASE_PATH / ".venv" | ||
|
||
# Ansible changes a lot between releases and deprecates a lot of stuff each of | ||
# them. Using a pinned ansible identical between all team members should | ||
# reduce the churn. | ||
def install_ansible(venv_path = VENV_PATH): | ||
requirements = BASE_PATH / "requirements.txt" | ||
venv_requirements = venv_path / "installed-requirements.txt" | ||
|
||
# Avoid installing ansible in the virtualenv multiple times | ||
if venv_requirements.exists() and \ | ||
venv_requirements.read_bytes() == requirements.read_bytes(): | ||
return | ||
|
||
print("creating a new virtual environment and install ansible in it...") | ||
shutil.rmtree(venv_path, ignore_errors=True) | ||
subprocess.run([sys.executable, "-m", "venv", str(venv_path)], check=True) | ||
subprocess.run([ | ||
str(venv_path / "bin" / "pip"), "install", "-r", str(requirements), | ||
], check=True) | ||
shutil.copy(str(requirements), str(venv_requirements)) | ||
|
||
def create_workspace(dir, env, playbook): | ||
env_dir = BASE_PATH / "envs" / env | ||
# Create a temporary directory merging together the chosen | ||
# environment, the chosen playbook and the shared files. | ||
(dir / "play").mkdir() | ||
(dir / "play" / "roles").symlink_to(BASE_PATH / "roles") | ||
(dir / "play" / "group_vars").symlink_to(BASE_PATH / "group_vars") | ||
(dir / "play" / "playbook.yml").symlink_to( | ||
BASE_PATH / "playbooks" / (playbook + ".yml") | ||
) | ||
(dir / "env").symlink_to(env_dir) | ||
(dir / "ansible.cfg").symlink_to(BASE_PATH / "ansible.cfg") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
--- | ||
|
||
sha: "{{ lookup('aws_ssm', '/docs-rs/builder/sha') }}" | ||
vars_repository_sha: "{{ sha | ternary(sha, 'HEAD') }}" | ||
vars_repository_sha: "{{ sha | ternary(sha, 'HEAD') }}" | ||
|
||
vars_extra_sudo_users: | ||
- rylev |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,4 @@ | |
|
||
- name: reboot | ||
reboot: | ||
when: packer_build_name is undefined |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ | |
- name: install apt packages | ||
apt: | ||
name: | ||
- aptitude # needed by ansible itself | ||
# - aptitude # needed by ansible itself | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like an unrelated change. Was this necessary to build the AMI? |
||
- ca-certificates | ||
- htop | ||
- iptables | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Packer | ||
|
||
This directory contains configuration for running packer to build AMIs. | ||
|
||
## Dependencies | ||
|
||
Running these packer scripts requires the following software: | ||
|
||
- python3 | ||
- [packer](https://developer.hashicorp.com/packer/downloads) | ||
- [aws-cli](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) | ||
|
||
## The `packer` wrapper | ||
|
||
This directory contains a python script named `packer` which wraps the system `packer`. The script creates an ansible virtual environment and moves the correct ansible configuration in place. | ||
|
||
### Running `packer` | ||
|
||
Before running the packer script, you will need to initialize packer: | ||
|
||
```bash | ||
packer init ./docs-rs-builder | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be run from within the |
||
``` | ||
|
||
You will also need to make sure that you are logged into the correct AWS account in the AWS cli. First, ensure you have the configuration needed to log into the appropriate AWS account in your "~/.aws/config" file (TODO: link to detailed instructions). | ||
|
||
For example, to log into the docs-rs staging account, run: | ||
|
||
```bash | ||
aws sso login --profile docs-rs-staging | ||
``` | ||
|
||
To run the wrapper pass the environment and playbook along with the profile name of the aws account you just logged into: | ||
|
||
```bash | ||
$ AWS_PROFILE=docs-rs-staging ./packer staging docs-rs-builder | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
packer { | ||
required_plugins { | ||
amazon = { | ||
version = ">= 1.1.1" | ||
source = "github.com/hashicorp/amazon" | ||
} | ||
} | ||
} | ||
|
||
data "amazon-parameterstore" "revision" { | ||
name = "/docs-rs/builder/sha" | ||
region = "us-east-1" | ||
} | ||
|
||
locals { | ||
revision = data.amazon-parameterstore.revision.value | ||
pretty_revision = substr(local.revision, 0, 8) | ||
timestamp = regex_replace(timestamp(), "[- TZ:]", "") | ||
} | ||
|
||
source "amazon-ebs" "ubuntu" { | ||
ami_name = "docs-rs-builder-${local.pretty_revision}-${local.timestamp}" | ||
instance_type = "t2.large" | ||
region = "us-east-1" | ||
source_ami_filter { | ||
filters = { | ||
name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*" | ||
root-device-type = "ebs" | ||
virtualization-type = "hvm" | ||
} | ||
most_recent = true | ||
owners = ["099720109477"] | ||
} | ||
ssh_username = "ubuntu" | ||
launch_block_device_mappings { | ||
device_name = "/dev/sda1" | ||
volume_size = 64 | ||
delete_on_termination = true | ||
} | ||
} | ||
|
||
build { | ||
sources = [ | ||
"source.amazon-ebs.ubuntu" | ||
] | ||
|
||
provisioner "ansible" { | ||
command = ".venv/bin/ansible-playbook" | ||
groups = ["docs-rs-builder"] | ||
inventory_directory = "./env" | ||
playbook_file = "./play/playbook.yml" | ||
# The default is the user running packer | ||
user = "ubuntu" | ||
extra_arguments = ["--extra-vars", "vars_repository_sha=${local.revision}"] | ||
# Work around for https://github.com/hashicorp/packer-plugin-ansible/issues/69 | ||
ansible_ssh_extra_args = ["-oHostKeyAlgorithms=+ssh-rsa -oPubkeyAcceptedKeyTypes=+ssh-rsa"] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import os | ||
import subprocess | ||
import pathlib | ||
import shutil | ||
import argparse | ||
import sys | ||
# This adds the ansible folder to the python package search path | ||
# so we can reuse functionality from there. | ||
sys.path.append("../ansible") | ||
rylev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import ansibleutils | ||
|
||
# Utility for removing everything in a directory except one thing | ||
def remove_all_except_one(directory, exception): | ||
for item in os.listdir(directory): | ||
if item != exception: | ||
item_path = os.path.join(directory, item) | ||
if os.path.islink(item_path) or os.path.isfile(item_path): | ||
# Delete the file or symlink | ||
os.remove(item_path) | ||
else: | ||
# Delete the directory | ||
shutil.rmtree(item_path) | ||
|
||
# Create the workspace directory leaving virtual environment | ||
# if it's already there | ||
def create_workspace_dir(): | ||
dir = pathlib.Path(__file__).resolve().parent | ||
workspace = os.path.join(dir, '.workspace') | ||
if os.path.exists(workspace): | ||
# Clean up workspace except for virtual environment | ||
remove_all_except_one(workspace, ".venv") | ||
else: | ||
# Make workspace | ||
os.mkdir(workspace) | ||
return pathlib.Path(workspace) | ||
|
||
# Create the workspace environment | ||
def create_workspace(env, playbook): | ||
workspace = create_workspace_dir() | ||
ansibleutils.install_ansible(workspace / ".venv") | ||
ansibleutils.create_workspace(workspace, env, playbook) | ||
|
||
# Link the template into the workspace | ||
template_path = pathlib.Path(playbook).resolve() | ||
if not os.path.exists(template_path): | ||
raise Exception(f"Last argument to packer call was the file '{template_path}' which does not exist") | ||
(workspace / "template").symlink_to(template_path) | ||
return workspace | ||
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("env") | ||
parser.add_argument("playbook") | ||
args = parser.parse_args() | ||
|
||
workspace = create_workspace(args.env, args.playbook) | ||
cmd = ["packer", "build", "template"] | ||
res = subprocess.run(cmd, cwd=str(workspace)) | ||
if res.returncode != 0: | ||
exit(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.