diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 0a100676..9556c36c 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -15,7 +15,7 @@ runs: steps: - uses: actions/setup-python@v3 with: - python-version: 3.x + python-version: 3.11.x # Cache Python dependencies cache: pip diff --git a/terraform/github-runners/.terraform.lock.hcl b/terraform/github-runners/.terraform.lock.hcl new file mode 100644 index 00000000..737a7119 --- /dev/null +++ b/terraform/github-runners/.terraform.lock.hcl @@ -0,0 +1,46 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/ansible/ansible" { + version = "1.1.0" + constraints = "1.1.0" + hashes = [ + "h1:7xrnVIriMIjo18EPNksMCRtERYj2dDae9Y3zVZRR6iM=", + "zh:183fe9fabb56454f776b6b433bbeba5dc5e91fbab0afd53c4ccf1f7c3afe02a5", + "zh:1eda7c6051cd3d8e25865e3ec0d2923937d2c686db86e6e4c87d140a86a77a95", + "zh:26000eb225ce18d565b94db7dc51171b4b034fa9cc29cf7a356e3f17c365c76e", + "zh:333c6a6d613fdab7bd4deb179dd288f730f56168a5858867cf1b89ae89b39d10", + "zh:4698cc0ba2344e9897b77aba6338a98a554cdea8acef0ec19a5d0d5240eb0960", + "zh:89229350c6e4d77513a3f3fc3c42440dda8f9bf3cd0d76c99a2928742d4eef2c", + "zh:913e18aba90364d580a441d88e5b8268f644aabb6cf552394471e049b6f42f3f", + "zh:935ecc86328c5462d019eb9d6687065f5f81943a29173c61b0af835ca72b80d7", + "zh:9d2c2c198a41f09a943299c76d175522cc0c834af46ed3a5abc8db74c114bb76", + "zh:bd92445042d1217eb377c9101bfefdbd9cdf1c59a11c93a16d2385c253bc56d4", + "zh:d3e140cec9134007bf19c71c50e9ef0295ce30ea1015004802bcf21eb05dad4e", + "zh:d62617f738180368f86e762e26ebf22e538309d7850ef17d92da8b38b4520645", + "zh:d833fe94bdacbcc412d36deb768530a01ccf7654f7007530683208211ac479a9", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:f63ed6016d237e177f5a7d2a6a485ef7094a97b44944cbdf9428b30b0c7c76b8", + ] +} + +provider "registry.terraform.io/terraform-provider-openstack/openstack" { + version = "1.52.1" + hashes = [ + "h1:tzawotEtjBcVWnzA+wAqcbkxW7XnJCfXqod4SBts9vI=", + "zh:037f7ab5a0942daee00d23402e7ccab472380864e13013284910fa7841a6e37c", + "zh:52ac973e6c5cd584c5086494218e9b49d93217f5fbc34fc76fa8a9ddd635447a", + "zh:5acad7b8c7a493fd0b659271743e2853859a4b2669df26f21aecf1b2f60fa706", + "zh:5d9218a7f10849f2227fc11df19f78b3b11cccade6b674c314e804f0e98d4368", + "zh:91ea6bf80ff706e734300041cf22e946c049abf8dcf1bed899f93f20f7779121", + "zh:961d67ebf1116bd539b726ef483f7d67c95351efd09e55fbeb30cd2ca7946a12", + "zh:9d3d8ee11cda45804e9b759064fbc9f47d6f54203bd17654236f2f601424b460", + "zh:a0af7e5bad6114a7a0ac88cee63e2c14558572e293bebcf651ed8d8d9c20dfda", + "zh:a1fd5609f61a43c9c2a403e024042afc3a45fde39935a388009d05105e2d39d3", + "zh:bd84aae9f2ac6eb978837ea5994bb24be221e2e4d69a3e8842eef3fcf62594f0", + "zh:be690e77aa497ab8bb8ed59f7e03018e96805e2e13df334086a8c5ac4290db09", + "zh:c4ee17773e7295b0598e36148ac49b2c61caa6da3f7b02e439aa61ca6486da07", + "zh:c871d03abf9c916584dd8fc6b63ed85bbe41208eba684b2175ac741003bf9d25", + "zh:f1e5c4a5740ad75b9b37376db4ea0e3067b0c2b6871521bbc6a1625bef137abf", + ] +} diff --git a/terraform/github-runners/README.rst b/terraform/github-runners/README.rst new file mode 100644 index 00000000..1d532d5e --- /dev/null +++ b/terraform/github-runners/README.rst @@ -0,0 +1,148 @@ +======================== +Terraform GitHub runners +======================== + +This Terraform configuration deploys a GitHub Actions runner VMs on an +OpenStack cloud for the stackhpc-release-train repository. + +Usage +===== + +These instructions show how to use this Terraform configuration manually. They +assume you are running an Ubuntu host that will be used to run Terraform. The +machine should have network access to the VM that will be created by this +configuration. + +Install Terraform: + +.. code-block:: console + + wget -qO - terraform.gpg https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/terraform-archive-keyring.gpg + sudo echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/terraform-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/terraform.list + sudo apt update + sudo apt install terraform + +Clone and initialise the repo: + +.. code-block:: console + + git clone https://github.com/stackhpc/stackhpc-release-train + cd stackhpc-release-train + +Change to the terraform/github-runners directory: + +.. code-block:: console + + cd terraform/github-runners + +Initialise Terraform: + +.. code-block:: console + + terraform init + +Create an OpenStack clouds.yaml file with your credentials to access an +OpenStack cloud. Alternatively, download one from Horizon. The credentials +should be scoped to the stackhpc-release project. + +.. code-block:: console + + cat << EOF > clouds.yaml + --- + clouds: + sms-lab: + auth: + auth_url: https://api.sms-lab.cloud:5000 + username: + project_name: + domain_name: default + interface: public + EOF + +Export environment variables to use the correct cloud and provide a password: + +.. code-block:: console + + export OS_CLOUD=sms-lab + read -p OS_PASSWORD -s OS_PASSWORD + export OS_PASSWORD + +Verify that the Terraform variables in terraform.tfvars are correct. + +Generate a plan: + +.. code-block:: console + + terraform plan + +Apply the changes: + +.. code-block:: console + + terraform apply -auto-approve + +Create a virtualenv: + +.. code-block:: console + + python3 -m venv venv + +Activate the virtualenv: + +.. code-block:: console + + source venv/bin/activate + +Install Python dependencies: + +.. code-block:: console + + pip install -r ansible/requirements.txt + +Install Ansible Galaxy dependencies: + +.. code-block:: console + + ansible-galaxy collection install -r ansible/requirements.yml + ansible-galaxy role install -r ansible/requirements.yml + +Create a GitHub PAT token (classic) with repo:all scope. Export an environment +variable with the token. + +.. code-block:: console + + read -p PERSONAL_ACCESS_TOKEN -s PERSONAL_ACCESS_TOKEN + export PERSONAL_ACCESS_TOKEN + +Deploy runners: + +.. code-block:: console + + ansible-playbook ansible/site.yml -i ansible/inventory.yml + +To remove runners: + +.. code-block:: console + + ansible-playbook ansible/site.yml -i ansible/inventory.yml -e runner_state=absent + +Troubleshooting +=============== + +Install service fails +--------------------- + +If you see the following:: + + TASK [monolithprojects.github_actions_runner : Install service] ******************************************************************************************************************************************** + fatal: [10.205.0.50]: FAILED! => changed=true + cmd: ./svc.sh install ubuntu + msg: '[Errno 2] No such file or directory: b''./svc.sh''' + rc: 2 + stderr: '' + stderr_lines: + stdout: '' + stdout_lines: + +It might mean the runner is already registered, possibly from a previous VM. +Remove the runner using Ansible or the GitHub settings. diff --git a/terraform/github-runners/ansible.cfg b/terraform/github-runners/ansible.cfg new file mode 100644 index 00000000..b93b0446 --- /dev/null +++ b/terraform/github-runners/ansible.cfg @@ -0,0 +1,5 @@ +[defaults] +stdout_callback = community.general.yaml +host_key_checking = False +pipelining = True +deprecation_warnings=False diff --git a/terraform/github-runners/ansible/group_vars/all b/terraform/github-runners/ansible/group_vars/all new file mode 100644 index 00000000..ef5b3890 --- /dev/null +++ b/terraform/github-runners/ansible/group_vars/all @@ -0,0 +1,6 @@ +--- +runner_user: "{{ ansible_facts.user_id }}" +github_account: stackhpc +github_repo: stackhpc-release-train +runner_labels: + - stackhpc-release-train diff --git a/terraform/github-runners/ansible/inventory.yml b/terraform/github-runners/ansible/inventory.yml new file mode 100644 index 00000000..15988426 --- /dev/null +++ b/terraform/github-runners/ansible/inventory.yml @@ -0,0 +1 @@ +plugin: cloud.terraform.terraform_provider diff --git a/terraform/github-runners/ansible/requirements.txt b/terraform/github-runners/ansible/requirements.txt new file mode 100644 index 00000000..fe401526 --- /dev/null +++ b/terraform/github-runners/ansible/requirements.txt @@ -0,0 +1 @@ +ansible-core==2.15.* diff --git a/terraform/github-runners/ansible/requirements.yml b/terraform/github-runners/ansible/requirements.yml new file mode 100644 index 00000000..4b700266 --- /dev/null +++ b/terraform/github-runners/ansible/requirements.yml @@ -0,0 +1,6 @@ +--- +collections: + - name: cloud.terraform + - name: community.general +roles: + - name: monolithprojects.github_actions_runner diff --git a/terraform/github-runners/ansible/site.yml b/terraform/github-runners/ansible/site.yml new file mode 100644 index 00000000..be972dac --- /dev/null +++ b/terraform/github-runners/ansible/site.yml @@ -0,0 +1,22 @@ +--- +- name: Deploy GitHub runners + hosts: runners + gather_facts: no + tasks: + - name: Wait for connection + ansible.builtin.wait_for_connection: + + - name: Gather facts + ansible.builtin.setup: + + - import_role: + name: monolithprojects.github_actions_runner + become: true + + - name: Ensure runner service is running + ansible.builtin.service: + name: actions.runner.stackhpc-stackhpc-release-train.{{ ansible_facts.hostname }}.service + state: started + enabled: true + become: true + when: runner_state | default('started') == 'started' diff --git a/terraform/github-runners/outputs.tf b/terraform/github-runners/outputs.tf new file mode 100644 index 00000000..bd186f2f --- /dev/null +++ b/terraform/github-runners/outputs.tf @@ -0,0 +1,24 @@ +output "access_ip_v4" { + value = openstack_compute_instance_v2.runners.*.access_ip_v4 +} + +output "access_cidr" { + value = data.openstack_networking_subnet_v2.network.cidr +} + +output "access_gw" { + value = data.openstack_networking_subnet_v2.network.gateway_ip +} + +resource "ansible_host" "runners" { + for_each = { for host in openstack_compute_instance_v2.runners : host.name => host.access_ip_v4 } + name = each.value + groups = ["runners"] +} + +resource "ansible_group" "runners" { + name = "runners" + variables = { + ansible_user = var.ssh_username + } +} diff --git a/terraform/github-runners/provider.tf b/terraform/github-runners/provider.tf new file mode 100644 index 00000000..947e02f8 --- /dev/null +++ b/terraform/github-runners/provider.tf @@ -0,0 +1,18 @@ +#provider "openstack" { +# use environment variables +#} + +terraform { + required_version = ">= 0.14" + backend "local" { + } + required_providers { + ansible = { + source = "ansible/ansible" + version = "1.1.0" + } + openstack = { + source = "terraform-provider-openstack/openstack" + } + } +} diff --git a/terraform/github-runners/templates/userdata.cfg.tpl b/terraform/github-runners/templates/userdata.cfg.tpl new file mode 100644 index 00000000..dd04fec9 --- /dev/null +++ b/terraform/github-runners/templates/userdata.cfg.tpl @@ -0,0 +1,18 @@ +#cloud-config +# Configure SSH keys here, to avoid creating an ephemeral keypair. +# This means only the instance needs to be cleaned up if the destroy fails. +ssh_authorized_keys: + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXNGOmsnRcZdmjMA5f3fgq8l8+QMjBywJQzuvxlhslx mark@mark-xps15 + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICY4CwVM472QS5TYhEwAHge4vbkuBtpVUCC8cyIolYR5 michal@stackhpc.com + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHK1Kf1hRYskYfYtXnqnyzD6vFbpKVyUtcxcn2pYL2y+ piotr@stackhpc.com + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC/F0fT8MuXUBEDeCbi9XoaefpZuWf35NOHHECN8VyIq gkoper@Grzegorzs-MacBook-Air.local + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIATVu5COwRn9FirPl2GhQIY/RmZkNGM+CcXOhT7WujBf priteau@raider.home + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJg3W6wKjLrKObikQz84K3oZJjUDpp2c6k62ZE4x1fev matta@stackhpc.com + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID1c17mnzF71ElO+xbJKwxc2bgoR8Yb+DhcENrWfYt0d dawud + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDeIsZEbrsSuxBcr8qI4jiZxXavEv+Crh+QwDNcezF6xgrTkIzCodYuJ6CNLhbee1A6IBIB5csmJWZ6BE8BD1RqxPH1AnCVIoQD+b606HCWYQ8UAhD7E485fE3DxDffAgsSsaK5LBatdIBu3epOrJiKGIMuJmGSAv3Hp86BkQ2y9bQXMp1jhn+cQ1WoZm9AK77viEM4X2sVmWMipUu3RV1tlI/W9mBO6GCi2N/+kDtzpZzjQEevr/xgu4zir99z2s/xDIMN2IssQm3nD4pMTrj7lkpKehtdYkHOFSW4qvhwgRuyONtoWAJjt7krOURYI4W6Inxva4yv/tqTyCFEIV4jfFGuoN7N9uaA7HtaEtSWtxtGXPkITutE3u4vsvKhPHOwjhHIL9cgy/EquP2Jm/gN8UG+iogSsLqbfJVbLdpO3X1i6m3dIsJ5WYKhPGBXRfTXRJQ0H/tbDTDr0zt7xqQDZREExirtyJ6auMYOXOrE60wMkH2hEPMeretx+4oZ1KE= will@juno + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCpMZpCHFX5yp7+5mrm4rW0x7dIQSS+49i5nVe5HrXxWSCvz8RVtEstFBN8OuOnUgZnDfpxrcxhoiCtv5WWEJQOzeUNlcjzMjTW7MdLqpVciLQLad7gUl1kepv6P0iyKrb0ze5xA0nfKVKInQmU3jSklE+aghHUXGre/pwAaqKJGsMLXLfW4uqjdG2RSAL/Bpg9126Ly0u/r1zhaNefkpIVqAYGqS7aXis6FTic/WfK4PbImqMuGjqiHNbNLT2pyzoBTRt3WDE0cRI6ryEYKKtHPtQ15H/J7kj18vek4ca7wA7nK+0UtrtSWnE2U4l/tybJ8AQQOKJ2ocjAyvDOrfumSRd+KS/ljxrOFSmyV4Zap6WJrUgzicQKOpZ0L9f3JxwTc/NlXk/BTA+COaoM3NwjYu+eHuJudJSiA0EHTQ3LdLhZiyFTo+DX+J6sZzr0keGWd0gRXQ8A0vKi1tHqerR7Reh1NGobaNzScO/1nyDAZJLt9BNI1OFMJEuZD83CTSU= alex@stackhpc.com + +# Not including these until claimed. +# - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDNG48SwtW6B9Exs08fuSyAq+KTQ7xMJaypDXiku8vIuP2Q7le6GTwmlULX12djtTvTewRzV6AsqkCyGq+2+Yo8EaKZbU08pKLuzlTH++89vKYaq+FxbbpgrDlvnT1rA5Af4AGrNcUGj3f3ovdc4XC9HL2MvVcb3lKZelCLae7HNeEES4oMVLd+GytkQPruj4SDqLLedGkJRmDN7Z88zjjhWDrVNFeXvcuJOXwlRXvuw8gR4NQgoPpET91Do8uMj5YpvH19yc9U3XOZx9wj1+sh2XJwQF6/anOpNxoFmICuSJLFc/Ulrmk8ogq3GRKHX+dT6eTIZrkX0IkSNfDnaOi20e1YP5yN0d8NffVrZHRzUwQz5LJB8fXyQgd1JIDcMyIluCjz3ND90oOoGMIDABZmz5+8eqECEh7Du7b1/XFSpPoTRWJ4YrpmSbCuYBpLKe/vNJAqUQgxwpgYuFUEJiBfWrE3B4w7FAUcc1l4/A78OrYt1umnK3OM41OZobChavE= +# - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO4AyLmXAw4uceOvUte8H0tKEif7q63KIncL39I0QkpC +# - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOUaGDMtMzd93zbAhvZO0bDBqdna1QgHRQgDImKuPTXs diff --git a/terraform/github-runners/terraform.tfvars b/terraform/github-runners/terraform.tfvars new file mode 100644 index 00000000..fabcf401 --- /dev/null +++ b/terraform/github-runners/terraform.tfvars @@ -0,0 +1,6 @@ +vm_count = 2 +ssh_username = "ubuntu" +vm_image = "Ubuntu-20.04" +vm_flavor = "general.v1.tiny" +vm_network = "stackhpc-release" +vm_subnet = "stackhpc-release-subnet" diff --git a/terraform/github-runners/vm.tf b/terraform/github-runners/vm.tf new file mode 100644 index 00000000..6417aee3 --- /dev/null +++ b/terraform/github-runners/vm.tf @@ -0,0 +1,62 @@ +variable "vm_count" { + type = number +} + +variable "ssh_username" { + type = string +} + +variable "vm_name" { + type = string + default = "srt-runner" +} + +variable "vm_image" { + type = string +} + +variable "vm_flavor" { + type = string +} + +variable "vm_network" { + type = string +} + +variable "vm_subnet" { + type = string +} + +locals { + image_is_uuid = length(regexall("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", var.vm_image)) > 0 +} + +data "openstack_images_image_v2" "image" { + name = var.vm_image + most_recent = true + count = local.image_is_uuid ? 0 : 1 +} + +data "openstack_networking_subnet_v2" "network" { + name = var.vm_subnet +} + +resource "openstack_compute_instance_v2" "runners" { + count = var.vm_count + name = format("%s%s", var.vm_name, count.index) + flavor_name = var.vm_flavor + config_drive = true + user_data = file("templates/userdata.cfg.tpl") + network { + name = var.vm_network + } + + block_device { + uuid = local.image_is_uuid ? var.vm_image: data.openstack_images_image_v2.image[0].id + source_type = "image" + volume_size = 20 + boot_index = 0 + destination_type = "volume" + delete_on_termination = true + } +}