From dcf1e5adfc42fea1196fe706643e5047c60ddf18 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Thu, 23 Jan 2025 21:56:16 -0600 Subject: [PATCH 1/9] r/blackbox-exporter: Rework to run as container Instead of downloading the `blackbox_exporter` binary from GitHub and copying it to the managed node, the _blackbox-exporter_ role now installs _podman_ and configures a systemd container unit (Quadlet) to run it in a container. This simplifies the deployment considerably, and will make updating easier (just run the playbook with `-e blackbox_exporter_pull_image=true`). --- roles/blackbox-exporter/defaults/main.yml | 3 + roles/blackbox-exporter/handlers/main.yml | 8 +-- roles/blackbox-exporter/tasks/deploy.yml | 38 ++++++++++-- roles/blackbox-exporter/tasks/install.yml | 59 ++----------------- .../templates/blackbox-exporter.container.j2 | 22 +++++++ roles/blackbox-exporter/vars/install.yml | 14 ----- 6 files changed, 65 insertions(+), 79 deletions(-) create mode 100644 roles/blackbox-exporter/templates/blackbox-exporter.container.j2 delete mode 100644 roles/blackbox-exporter/vars/install.yml diff --git a/roles/blackbox-exporter/defaults/main.yml b/roles/blackbox-exporter/defaults/main.yml index 85824cb..d750715 100644 --- a/roles/blackbox-exporter/defaults/main.yml +++ b/roles/blackbox-exporter/defaults/main.yml @@ -1,3 +1,6 @@ blackbox_modules: {} blackbox_config: modules: '{{ blackbox_modules }}' +blackbox_container_image: '{{ blackbox_container_image_name }}:{{ blackbox_container_image_tag }}' +blackbox_container_image_name: quay.io/prometheus/blackbox-exporter +blackbox_container_image_tag: latest diff --git a/roles/blackbox-exporter/handlers/main.yml b/roles/blackbox-exporter/handlers/main.yml index f4b3b76..2857a34 100644 --- a/roles/blackbox-exporter/handlers/main.yml +++ b/roles/blackbox-exporter/handlers/main.yml @@ -2,12 +2,12 @@ systemd: daemon_reload: true -- name: restart blackbox_exporter +- name: restart blackbox-exporter service: - name: blackbox_exporter + name: blackbox-exporter state: restarted -- name: reload blackbox_exporter +- name: reload blackbox-exporter service: - name: blackbox_exporter + name: blackbox-exporter state: reloaded diff --git a/roles/blackbox-exporter/tasks/deploy.yml b/roles/blackbox-exporter/tasks/deploy.yml index c758ebb..529ab6c 100644 --- a/roles/blackbox-exporter/tasks/deploy.yml +++ b/roles/blackbox-exporter/tasks/deploy.yml @@ -1,3 +1,26 @@ +- name: ensure blackbox container is present + podman_image: + name: '{{ blackbox_container_image_name }}' + tag: '{{ blackbox_container_image_tag }}' + state: present + pull: '{{ blackbox_exporter_pull_image|d(false)|bool }}' + notify: + - reload systemd + - restart blackbox-exporter + tags: + - container + +- name: ensure blackbox-exporter system container is configured + template: + src: blackbox-exporter.container.j2 + dest: /etc/containers/systemd/blackbox-exporter.container + mode: u=rw,go=r + owner: root + group: root + notify: + - reload systemd + - restart blackbox-exporter + - name: ensure /etc/prometheus directory exists file: path: /etc/prometheus @@ -6,7 +29,7 @@ group: root state: directory -- name: ensure blackbox_exporter is configured +- name: ensure blackbox-exporter is configured copy: dest: /etc/prometheus/blackbox.yml content: | @@ -15,19 +38,22 @@ owner: root group: root notify: - - reload blackbox_exporter + - reload blackbox-exporter -- name: ensure blackbox_exporter starts at boot +- name: flush handlers + meta: flush_handlers + +- name: ensure blackbox-exporter starts at boot service: - name: blackbox_exporter + name: blackbox-exporter enabled: true tags: - service - name: flush_handlers meta: flush_handlers -- name: ensure blackbox_exporter is running +- name: ensure blackbox-exporter is running service: - name: blackbox_exporter + name: blackbox-exporter state: started tags: - service diff --git a/roles/blackbox-exporter/tasks/install.yml b/roles/blackbox-exporter/tasks/install.yml index a731f91..e1350c1 100644 --- a/roles/blackbox-exporter/tasks/install.yml +++ b/roles/blackbox-exporter/tasks/install.yml @@ -1,55 +1,4 @@ -- name: load installation variables - include_vars: install.yml - tags: - - always - -- name: load architecture variables - include_vars: '{{ item }}' - with_first_found: - - '{{ ansible_architecture }}.yml' - - arch-defaults.yml - tags: - - always - -- name: ensure blackbox_exporter release archive is available - delegate_to: localhost - become: false - get_url: - url: '{{ blackbox_xptr_tar_url }}' - checksum: 'sha256:{{ blackbox_xptr_cksm_url }}' - dest: '{{ playbook_dir }}/tmp/{{ blackbox_xptr_tar_name }}' - tags: - - download - -- name: ensure blackbox_exporter archive is unpacked locally - delegate_to: localhost - become: false - unarchive: - src: '{{ playbook_dir }}/tmp/{{ blackbox_xptr_tar_name }}' - dest: '{{ playbook_dir }}/tmp/' - remote_src: true - creates: '{{ blackbox_xptr_extract_dir }}/blackbox_exporter' - tags: - - unarchive - -- name: ensure blackbox_exporter is installed - copy: - src: '{{ blackbox_xptr_extract_dir }}/blackbox_exporter' - dest: /usr/local/sbin/blackbox_exporter - mode: u=rwx,go=rx - diff: false - notify: - - restart blackbox_exporter - -- name: ensure blackbox_exporter systemd unit is installed - file: - src: blackbox_exporter.service - dest: /etc/systemd/system/blackbox_exporter.services - mode: u=rw,go=r - notify: - - reload systemd - - restart blackbox_exporter - tags: - - service - - systemd - +- name: ensure podman is installed + package: + name: podman + state: present diff --git a/roles/blackbox-exporter/templates/blackbox-exporter.container.j2 b/roles/blackbox-exporter/templates/blackbox-exporter.container.j2 new file mode 100644 index 0000000..1b02db5 --- /dev/null +++ b/roles/blackbox-exporter/templates/blackbox-exporter.container.j2 @@ -0,0 +1,22 @@ +[Unit] +Description=Blackbox exporter +Documentation=https://github.com/prometheus/blackbox_exporter/blob/master/README.md +After=network-online.target +Wants=network-online.target + +[Container] +Image={{ blackbox_container_image }} +Pull=never +Exec=--config.file=/etc/prometheus/blackbox.yml +Mount=type=bind,source=/etc/prometheus,target=/etc/prometheus,readonly=true +ReadOnly=yes +ReadOnlyTmpfs=yes +NoNewPrivileges=yes +User=215 +Group=215 +PublishPort=9115:9115 + +[Service] +Restart=always +RestartSec=1s +ExecReload=/usr/bin/podman kill --cidfile=%t/%N.cid -s HUP diff --git a/roles/blackbox-exporter/vars/install.yml b/roles/blackbox-exporter/vars/install.yml deleted file mode 100644 index 757a853..0000000 --- a/roles/blackbox-exporter/vars/install.yml +++ /dev/null @@ -1,14 +0,0 @@ -blackbox_xptr_version: 0.22.0 - -blackbox_xptr_base_url: - https://github.com/prometheus/blackbox_exporter/releases/download -blackbox_xptr_archive: - blackbox_exporter-{{ blackbox_xptr_version }}.linux-{{ blackbox_xptr_arch }} -blackbox_xptr_tar_name: >- - {{ blackbox_xptr_archive }}.tar.gz -blackbox_xptr_tar_url: >- - {{ blackbox_xptr_base_url }}/v{{ blackbox_xptr_version }}/{{ blackbox_xptr_tar_name }} -blackbox_xptr_cksm_url: >- - {{ blackbox_xptr_base_url }}/v{{ blackbox_xptr_version }}/sha256sums.txt -blackbox_xptr_extract_dir: >- - {{ playbook_dir }}/tmp/{{ blackbox_xptr_archive }} From 3e8ac36f88481af3fdd35bf63fb024b04cd651a7 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 26 Jan 2025 06:55:11 -0600 Subject: [PATCH 2/9] r/vmagent: Rework as container deployment Like the _blackbox-exporter_ role, the _vmagent_ role now deploys `vmagent` as a container. This simplifies the process considerably, eliminating the download/transfer step. While refactoring this role, I also changed how the trusted CA certificates are handled. Rather than copy files, the role now expects a `vmagent_ca_certs` variable. This variable is a mapping of certificate name (file name without extension) to PEM contents. This allows certificates to be defined using normal host/group variables. --- roles/vmagent/defaults/main.yml | 5 +++ roles/vmagent/files/ca-certs/kube-root-ca.crt | 1 - roles/vmagent/files/vmagent.service | 13 ------ roles/vmagent/handlers/main.yml | 5 ++- roles/vmagent/tasks/deploy.yml | 41 +++++++++++++------ roles/vmagent/tasks/install.yml | 15 ++----- roles/vmagent/templates/vmagent.container.j2 | 26 ++++++++++++ 7 files changed, 66 insertions(+), 40 deletions(-) delete mode 120000 roles/vmagent/files/ca-certs/kube-root-ca.crt delete mode 100644 roles/vmagent/files/vmagent.service create mode 100644 roles/vmagent/templates/vmagent.container.j2 diff --git a/roles/vmagent/defaults/main.yml b/roles/vmagent/defaults/main.yml index f31a20b..630d01e 100644 --- a/roles/vmagent/defaults/main.yml +++ b/roles/vmagent/defaults/main.yml @@ -1,3 +1,8 @@ +vmagent_container_image_name: docker.io/victoriametrics/vmagent +vmagent_container_image_tag: latest + +vmagent_ca_certs: {} + vmagent_remotewrite_url: http://[::1]:8428/api/v1/write vmagent_scrape_interval: 1m diff --git a/roles/vmagent/files/ca-certs/kube-root-ca.crt b/roles/vmagent/files/ca-certs/kube-root-ca.crt deleted file mode 120000 index 70b6c8f..0000000 --- a/roles/vmagent/files/ca-certs/kube-root-ca.crt +++ /dev/null @@ -1 +0,0 @@ -../../../../kube-root-ca.crt \ No newline at end of file diff --git a/roles/vmagent/files/vmagent.service b/roles/vmagent/files/vmagent.service deleted file mode 100644 index 39013b2..0000000 --- a/roles/vmagent/files/vmagent.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Victoria Metrics vmagent - -[Service] -Type=exec -Environment=httpListenAddr=[::1]:8429 -Environment=loggerDisableTimestamps=true -EnvironmentFile=-/etc/sysconfig/vmagent -ExecStart=/usr/bin/vmagent -enableTCP6 -envflag.enable -User=victoriametrics - -[Install] -WantedBy=multi-user.target diff --git a/roles/vmagent/handlers/main.yml b/roles/vmagent/handlers/main.yml index bbbe825..330dd08 100644 --- a/roles/vmagent/handlers/main.yml +++ b/roles/vmagent/handlers/main.yml @@ -8,6 +8,7 @@ state: restarted - name: reload vmagent - uri: - url: http://[::1]:8429/-/reload + service: + name: vmagent + state: reloaded changed_when: true diff --git a/roles/vmagent/tasks/deploy.yml b/roles/vmagent/tasks/deploy.yml index f9c8aa9..9698c7f 100644 --- a/roles/vmagent/tasks/deploy.yml +++ b/roles/vmagent/tasks/deploy.yml @@ -1,13 +1,27 @@ -- name: ensure victoria metrics user exists - user: - name: victoriametrics - system: true - home: /var/lib/victoria-metrics - createhome: false - shell: /sbin/nologin +- name: ensure vmagent container image is present + podman_image: + name: '{{ vmagent_container_image_name }}' + tag: '{{ vmagent_container_image_tag }}' state: present + pull: '{{ vmagent_pull_image|d(false)|bool }}' + notify: + - restart vmagent tags: - - user + - container-image + +- name: ensure vmagent system container is configured + template: + src: vmagent.container.j2 + dest: /etc/containers/systemd/vmagent.container + owner: root + group: root + mode: u=rw,go=r + notify: + - reload systemd + - restart vmagent + tags: + - container + - systemd - name: ensure /etc/sysconfig directory exists file: @@ -48,20 +62,21 @@ - name: ensure additional ca certificates are installed copy: - src: '{{ item }}' - dest: /etc/victoria-metrics/{{ item | basename }} + content: '{{ item.1 }}' + dest: /etc/victoria-metrics/{{ item.0 }}.crt mode: u=rw,go=r owner: root group: root - with_fileglob: - - ca-certs/{{ inventory_hostname }}/*.crt - - ca-certs/*.crt + loop: '{{ vmagent_ca_certs|dictsort }}' notify: - reload vmagent tags: - cert - scrape-config +- name: flush handlers + meta: flush_handlers + - name: ensure vmagent starts at boot service: name: vmagent diff --git a/roles/vmagent/tasks/install.yml b/roles/vmagent/tasks/install.yml index d97e530..e1350c1 100644 --- a/roles/vmagent/tasks/install.yml +++ b/roles/vmagent/tasks/install.yml @@ -1,11 +1,4 @@ -- name: ensure vmagent systemd unit is installed - copy: - src: vmagent.service - dest: /etc/systemd/system/vmagent.service - mode: '0644' - notify: - - reload systemd - - restart vmagent - tags: - - service - - systemd +- name: ensure podman is installed + package: + name: podman + state: present diff --git a/roles/vmagent/templates/vmagent.container.j2 b/roles/vmagent/templates/vmagent.container.j2 new file mode 100644 index 0000000..89c0943 --- /dev/null +++ b/roles/vmagent/templates/vmagent.container.j2 @@ -0,0 +1,26 @@ +[Unit] +Description=Victoria Metrics vmagent +After=network.target +Wants=network.target + +[Container] +Image={{ vmagent_container_image_name }}:{{ vmagent_container_image_tag }} +Pull=never +Exec=-enableTCP6 -envflag.enable +Environment=loggerDisableTimestamps=true +EnvironmentFile=/etc/sysconfig/vmagent +Mount=type=bind,source=/etc/victoria-metrics,target=/etc/victoria-metrics,readonly=true +Mount=type=bind,source=/var/lib/victoria-metrics,target=/var/lib/victoria-metrics,chown=true,relabel=shared +WorkingDir=/var/lib/victoria-metrics/vmagent +ReadOnly=yes +ReadOnlyTmpfs=yes +NoNewPrivileges=yes +User=212 +Group=212 +Network=host + +[Service] +Restart=always +RestartSec=1s +ExecReload=/usr/bin/podman kill --cidfile=%t/%N.cid -s HUP +StateDirectory=victoria-metrics victoria-metrics/vmagent From ad0bd7d4a556708a72d28e77cfc03ccbfd79eaae Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 26 Jan 2025 07:00:02 -0600 Subject: [PATCH 3/9] remote-blackbox: Add group The _remote-blackbox_ group defines a system that runs _blackbox-exporter_ and _vmagent_ in a remote (cloud) location. This system will monitor our public web sites. This will give a better idea of their availability from the perspective of a user on the Internet, which can be by factors that are necessarily visible from within the network. --- group_vars/remote-blackbox.yml | 51 ++++++++++++++++++++++++++++++++++ hosts | 6 ++++ 2 files changed, 57 insertions(+) create mode 100644 group_vars/remote-blackbox.yml diff --git a/group_vars/remote-blackbox.yml b/group_vars/remote-blackbox.yml new file mode 100644 index 0000000..3f6f617 --- /dev/null +++ b/group_vars/remote-blackbox.yml @@ -0,0 +1,51 @@ +blackbox_modules: + http: + http: + headers: + Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 + Accept-Charset: utf-8 + Accept-Language: en-US + method: GET + prober: http + timeout: 5s + +vmagent_remotewrite_url: https://metrics.pyrocufflink.blue/insert/1/prometheus/api/v1/write + +vmagent_scrape_configs: +- job_name: cloud-vmagent + static_configs: + - targets: + - localhost:8429 + +- job_name: blackbox-metrics + static_configs: + - targets: + - localhost:9115 + +- job_name: websites + scrape_interval: 4m + metrics_path: /probe + params: + module: + - http + static_configs: + - targets: + - https://dustin.hatch.name/ + - https://darkchestofwonders.us/ + - http://nratonpass.com/ + - http://pyrocufflink.net/ + - http://ebonfire.com/ + - http://chmod777.sh/ + - https://nextcloud.pyrocufflink.net/index.php + - https://bitwarden.pyrocufflink.net/ + - https://git.pyrocufflink.net/ + - https://tabitha.biz/ + - https://dustinandtabitha.com/ + - https://hatchlearningcenter.org/ + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - source_labels: [__param_target] + target_label: instance + - target_label: __address__ + replacement: localhost:9115 diff --git a/hosts b/hosts index 262ff92..bf61b4a 100644 --- a/hosts +++ b/hosts @@ -8,6 +8,9 @@ file0.pyrocufflink.blue [bitwarden_rs] bw0.pyrocufflink.blue +[blackbox-exporter:children] +remote-blackbox + [btop] chromie.pyrocufflink.blue @@ -211,6 +214,9 @@ smtp1.pyrocufflink.blue vmhost0.pyrocufflink.blue vmhost1.pyrocufflink.blue +[vmagent:children] +remote-blackbox + [wheelhost] file0.pyrocufflink.blue From 304cacb95bcbb60b2abc6af3979a45f216470c75 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 26 Jan 2025 08:12:20 -0600 Subject: [PATCH 4/9] dch-proxy: Proxy Victoria Metrics Need to expose Victoria Metrics to the Internet so the `vmagent` process on the VPS can push the metrics it has scraped from its Blackbox exporter. Authelia needs to allow access to the `/insert/` paths, of course. --- group_vars/dch-proxy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/group_vars/dch-proxy.yml b/group_vars/dch-proxy.yml index 62b1d3a..f958121 100644 --- a/group_vars/dch-proxy.yml +++ b/group_vars/dch-proxy.yml @@ -60,6 +60,8 @@ dch_proxy_sites: match: invoiceninja.pyrocufflink.net - backend: kubernetes match: dynk8s-provisioner.pyrocufflink.net +- backend: kubernetes + match: metrics.pyrocufflink.blue dch_proxy_backends: bitwarden: From f868cea05cd9640f2e4e6a2798748e47c1a795db Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 26 Jan 2025 07:00:34 -0600 Subject: [PATCH 5/9] pulumi: Manage HostVDS instances HostVDS provides public access to their OpenStack API, which we can use to manage cloud instances. This particular instance will be used to run the remote blackbox exporter/vmagent to monitor website availability. --- Pulumi.prod.yaml | 10 ++ Pulumi.yaml | 8 ++ pulumi/.gitignore | 3 + pulumi/.pulumi/meta.yaml | 1 + pulumi/.pulumi/meta.yaml.attrs | 1 + pulumi/dch/vps.py | 33 +++++ pulumi/main.py | 1 + pyproject.toml | 39 ++++++ uv.lock | 238 +++++++++++++++++++++++++++++++++ 9 files changed, 334 insertions(+) create mode 100644 Pulumi.prod.yaml create mode 100644 Pulumi.yaml create mode 100644 pulumi/.gitignore create mode 100644 pulumi/.pulumi/meta.yaml create mode 100644 pulumi/.pulumi/meta.yaml.attrs create mode 100644 pulumi/dch/vps.py create mode 100644 pulumi/main.py create mode 100644 pyproject.toml create mode 100644 uv.lock diff --git a/Pulumi.prod.yaml b/Pulumi.prod.yaml new file mode 100644 index 0000000..2b63e6a --- /dev/null +++ b/Pulumi.prod.yaml @@ -0,0 +1,10 @@ +encryptionsalt: v1:es1H1lzWTlU=:v1:8fNjYuxDn2RXRmjS:VXwcnewu1R69V1kbBWLCUAzHCa5w9g== +config: + openstack:authUrl: https://os-api.hostvds.com/identity + openstack:tenantName: hostvds-b3897be3-8920-4fb0-955d-340a162da6dd + openstack:userDomainName: Default + openstack:projectDomainId: default + openstack:userName: hostvds-b3897be3-8920-4fb0-955d-340a162da6dd + openstack:password: + secure: v1:qPzbLlSFUUeMiXBR:M4UE/NM5fvlYd08dPeT4Ov3LK2Cjle97Ge845fTXh0B7AWzOPWYakt7ssA== + openstack:region: us-east2 diff --git a/Pulumi.yaml b/Pulumi.yaml new file mode 100644 index 0000000..3042cdc --- /dev/null +++ b/Pulumi.yaml @@ -0,0 +1,8 @@ +name: dch +main: pulumi/main.py +runtime: + name: python + options: + toolchain: uv + typechecker: pyright + virtualenv: .venv diff --git a/pulumi/.gitignore b/pulumi/.gitignore new file mode 100644 index 0000000..14c71f0 --- /dev/null +++ b/pulumi/.gitignore @@ -0,0 +1,3 @@ +*.egg-info/ +__pycache__/ +*.py[co] diff --git a/pulumi/.pulumi/meta.yaml b/pulumi/.pulumi/meta.yaml new file mode 100644 index 0000000..b825518 --- /dev/null +++ b/pulumi/.pulumi/meta.yaml @@ -0,0 +1 @@ +version: 1 diff --git a/pulumi/.pulumi/meta.yaml.attrs b/pulumi/.pulumi/meta.yaml.attrs new file mode 100644 index 0000000..4466031 --- /dev/null +++ b/pulumi/.pulumi/meta.yaml.attrs @@ -0,0 +1 @@ +{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":null,"md5":"EaRWdV65+nlqCnYlI4a4Wg=="} diff --git a/pulumi/dch/vps.py b/pulumi/dch/vps.py new file mode 100644 index 0000000..03c8aa5 --- /dev/null +++ b/pulumi/dch/vps.py @@ -0,0 +1,33 @@ +import pulumi_openstack as os + +key_pair = os.compute.Keypair( + 'default', + name='dustin--rosalina', + public_key='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJsL5fSylmiJmBtW0DH/viAAmtU2E/2M17GPvysiyRs+', +) + +flavor = os.compute.get_flavor( + vcpus=1, + ram=1024, +) + +image = os.images.get_image( + name='AlmaLinux-9-amd64', +) + +instance = os.compute.Instance( + 'vps', + flavor_id=flavor.id, + image_id=image.id, + key_pair=key_pair.id, + networks=[ + {'name': 'Internet-01'}, + ], + metadata={ + 'os_distro': 'alma', # Sets the icon in the HostVDS Control Panel + 'groups': 'remote-blackbox', + }, + security_groups=[ + 'allow_all', + ], +) diff --git a/pulumi/main.py b/pulumi/main.py new file mode 100644 index 0000000..022945c --- /dev/null +++ b/pulumi/main.py @@ -0,0 +1 @@ +import dch.vps diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4e71ec2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,39 @@ +[project] +name = "dch-configpolicy" +authors = [ + {name = "Dustin C. Hatch", email = "dustin@hatch.name"}, +] +description = "Dustin's configuration policy" +requires-python = ">=3.12" +license = {text = "BSD-3-Clause"} +classifiers = [ + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3", +] +dependencies = [ + "pulumi>=3.146.0", + "pulumi-openstack>=5.0.2", +] +dynamic = ["version"] + +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["pulumi"] + +[tool.setuptools_scm] + +[tool.pyright] +venvPath = '.' +venv = '.venv' + +[tool.black] +line-length = 79 +skip-string-normalization = true + +[dependency-groups] +dev = [ + "pyright>=1.1.391", +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..ee54352 --- /dev/null +++ b/uv.lock @@ -0,0 +1,238 @@ +version = 1 +requires-python = ">=3.12" + +[[package]] +name = "arpeggio" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/c4/516bb54456f85ad1947702ea4cef543a59de66d31a9887dbc3d9df36e3e1/Arpeggio-2.0.2.tar.gz", hash = "sha256:c790b2b06e226d2dd468e4fbfb5b7f506cec66416031fde1441cf1de2a0ba700", size = 766643 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/4f/d28bf30a19d4649b40b501d531b44e73afada99044df100380fd9567e92f/Arpeggio-2.0.2-py2.py3-none-any.whl", hash = "sha256:f7c8ae4f4056a89e020c24c7202ac8df3e2bc84e416746f20b0da35bb1de0250", size = 55287 }, +] + +[[package]] +name = "attrs" +version = "24.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 }, +] + +[[package]] +name = "dch-configpolicy" +version = "0.1.dev1011+g81663a6.d20250119" +source = { editable = "." } +dependencies = [ + { name = "pulumi" }, + { name = "pulumi-openstack" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pyright" }, +] + +[package.metadata] +requires-dist = [ + { name = "pulumi", specifier = ">=3.146.0" }, + { name = "pulumi-openstack", specifier = ">=5.0.2" }, +] + +[package.metadata.requires-dev] +dev = [{ name = "pyright", specifier = ">=1.1.391" }] + +[[package]] +name = "debugpy" +version = "1.8.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/25/c74e337134edf55c4dfc9af579eccb45af2393c40960e2795a94351e8140/debugpy-1.8.12.tar.gz", hash = "sha256:646530b04f45c830ceae8e491ca1c9320a2d2f0efea3141487c82130aba70dce", size = 1641122 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/e6/0f876ecfe5831ebe4762b19214364753c8bc2b357d28c5d739a1e88325c7/debugpy-1.8.12-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:7e94b643b19e8feb5215fa508aee531387494bf668b2eca27fa769ea11d9f498", size = 2500846 }, + { url = "https://files.pythonhosted.org/packages/19/64/33f41653a701f3cd2cbff8b41ebaad59885b3428b5afd0d93d16012ecf17/debugpy-1.8.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:086b32e233e89a2740c1615c2f775c34ae951508b28b308681dbbb87bba97d06", size = 4222181 }, + { url = "https://files.pythonhosted.org/packages/32/a6/02646cfe50bfacc9b71321c47dc19a46e35f4e0aceea227b6d205e900e34/debugpy-1.8.12-cp312-cp312-win32.whl", hash = "sha256:2ae5df899732a6051b49ea2632a9ea67f929604fd2b036613a9f12bc3163b92d", size = 5227017 }, + { url = "https://files.pythonhosted.org/packages/da/a6/10056431b5c47103474312cf4a2ec1001f73e0b63b1216706d5fef2531eb/debugpy-1.8.12-cp312-cp312-win_amd64.whl", hash = "sha256:39dfbb6fa09f12fae32639e3286112fc35ae976114f1f3d37375f3130a820969", size = 5267555 }, + { url = "https://files.pythonhosted.org/packages/cf/4d/7c3896619a8791effd5d8c31f0834471fc8f8fb3047ec4f5fc69dd1393dd/debugpy-1.8.12-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:696d8ae4dff4cbd06bf6b10d671e088b66669f110c7c4e18a44c43cf75ce966f", size = 2485246 }, + { url = "https://files.pythonhosted.org/packages/99/46/bc6dcfd7eb8cc969a5716d858e32485eb40c72c6a8dc88d1e3a4d5e95813/debugpy-1.8.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:898fba72b81a654e74412a67c7e0a81e89723cfe2a3ea6fcd3feaa3395138ca9", size = 4218616 }, + { url = "https://files.pythonhosted.org/packages/03/dd/d7fcdf0381a9b8094da1f6a1c9f19fed493a4f8576a2682349b3a8b20ec7/debugpy-1.8.12-cp313-cp313-win32.whl", hash = "sha256:22a11c493c70413a01ed03f01c3c3a2fc4478fc6ee186e340487b2edcd6f4180", size = 5226540 }, + { url = "https://files.pythonhosted.org/packages/25/bd/ecb98f5b5fc7ea0bfbb3c355bc1dd57c198a28780beadd1e19915bf7b4d9/debugpy-1.8.12-cp313-cp313-win_amd64.whl", hash = "sha256:fdb3c6d342825ea10b90e43d7f20f01535a72b3a1997850c0c3cefa5c27a4a2c", size = 5267134 }, + { url = "https://files.pythonhosted.org/packages/38/c4/5120ad36405c3008f451f94b8f92ef1805b1e516f6ff870f331ccb3c4cc0/debugpy-1.8.12-py2.py3-none-any.whl", hash = "sha256:274b6a2040349b5c9864e475284bce5bb062e63dce368a394b8cc865ae3b00c6", size = 5229490 }, +] + +[[package]] +name = "dill" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 }, +] + +[[package]] +name = "grpcio" +version = "1.66.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/d1/49a96df4eb1d805cf546247df40636515416d2d5c66665e5129c8b4162a8/grpcio-1.66.2.tar.gz", hash = "sha256:563588c587b75c34b928bc428548e5b00ea38c46972181a4d8b75ba7e3f24231", size = 12489713 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/5c/c4da36b7a77dbb15c4bc72228dff7161874752b2c6bddf7bb046d9da1b90/grpcio-1.66.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:802d84fd3d50614170649853d121baaaa305de7b65b3e01759247e768d691ddf", size = 5002933 }, + { url = "https://files.pythonhosted.org/packages/a0/d5/b631445dff250a5301f51ff56c5fc917c7f955cd02fa55379f158a89abeb/grpcio-1.66.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80fd702ba7e432994df208f27514280b4b5c6843e12a48759c9255679ad38db8", size = 10793953 }, + { url = "https://files.pythonhosted.org/packages/c8/1c/2179ac112152e92c02990f98183edf645df14aa3c38b39f1a3a60358b6c6/grpcio-1.66.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:12fda97ffae55e6526825daf25ad0fa37483685952b5d0f910d6405c87e3adb6", size = 5499791 }, + { url = "https://files.pythonhosted.org/packages/0b/53/8d7ab865fbd983309c8242930f00b28a01047f70c2b2e4c79a5c92a46a08/grpcio-1.66.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:950da58d7d80abd0ea68757769c9db0a95b31163e53e5bb60438d263f4bed7b7", size = 6109606 }, + { url = "https://files.pythonhosted.org/packages/86/e9/3dfb5a3ff540636d46b8b723345e923e8c553d9b3f6a8d1b09b0d915eb46/grpcio-1.66.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e636ce23273683b00410f1971d209bf3689238cf5538d960adc3cdfe80dd0dbd", size = 5762866 }, + { url = "https://files.pythonhosted.org/packages/f1/cb/c07493ad5dd73d51e4e15b0d483ff212dfec136ee1e4f3b49d115bdc7a13/grpcio-1.66.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a917d26e0fe980b0ac7bfcc1a3c4ad6a9a4612c911d33efb55ed7833c749b0ee", size = 6446819 }, + { url = "https://files.pythonhosted.org/packages/ff/5f/142e19db367a34ea0ee8a8451e43215d0a1a5dbffcfdcae8801f22903301/grpcio-1.66.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49f0ca7ae850f59f828a723a9064cadbed90f1ece179d375966546499b8a2c9c", size = 6040273 }, + { url = "https://files.pythonhosted.org/packages/5c/3b/12fcd752c55002e4b0e0a7bd5faec101bc0a4e3890be3f95a43353142481/grpcio-1.66.2-cp312-cp312-win32.whl", hash = "sha256:31fd163105464797a72d901a06472860845ac157389e10f12631025b3e4d0453", size = 3537988 }, + { url = "https://files.pythonhosted.org/packages/f1/70/76bfea3faa862bfceccba255792e780691ff25b8227180759c9d38769379/grpcio-1.66.2-cp312-cp312-win_amd64.whl", hash = "sha256:ff1f7882e56c40b0d33c4922c15dfa30612f05fb785074a012f7cda74d1c3679", size = 4275553 }, + { url = "https://files.pythonhosted.org/packages/72/31/8708a8dfb3f1ac89926c27c5dd17412764157a2959dbc5a606eaf8ac71f6/grpcio-1.66.2-cp313-cp313-linux_armv7l.whl", hash = "sha256:3b00efc473b20d8bf83e0e1ae661b98951ca56111feb9b9611df8efc4fe5d55d", size = 5004245 }, + { url = "https://files.pythonhosted.org/packages/8b/37/0b57c3769efb3cc9ec97fcaa9f7243046660e7ed58c0faebc4ef315df92c/grpcio-1.66.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1caa38fb22a8578ab8393da99d4b8641e3a80abc8fd52646f1ecc92bcb8dee34", size = 10756749 }, + { url = "https://files.pythonhosted.org/packages/bf/5a/425e995724a19a1b110340ed653bc7c5de8019d9fc84b3798a0f79c3eb31/grpcio-1.66.2-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c408f5ef75cfffa113cacd8b0c0e3611cbfd47701ca3cdc090594109b9fcbaed", size = 5499666 }, + { url = "https://files.pythonhosted.org/packages/2e/e4/86a5c5ec40a6b683671a1d044ebca433812d99da8fcfc2889e9c43cecbd4/grpcio-1.66.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c806852deaedee9ce8280fe98955c9103f62912a5b2d5ee7e3eaa284a6d8d8e7", size = 6109578 }, + { url = "https://files.pythonhosted.org/packages/2f/86/a86742f3deaa22385c3bff984c5947fc62d47d3fab26c508730037d027e5/grpcio-1.66.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f145cc21836c332c67baa6fc81099d1d27e266401565bf481948010d6ea32d46", size = 5763274 }, + { url = "https://files.pythonhosted.org/packages/c3/61/b9a2a4345dea0a354c4ed8ac7aacbdd0ff986acbc8f92680213cf3d2faa3/grpcio-1.66.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:73e3b425c1e155730273f73e419de3074aa5c5e936771ee0e4af0814631fb30a", size = 6450416 }, + { url = "https://files.pythonhosted.org/packages/50/b9/ad303ce75d8cd71d855a661519aa160ce42f27498f589f1ae6d9f8c5e8ac/grpcio-1.66.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c509a4f78114cbc5f0740eb3d7a74985fd2eff022971bc9bc31f8bc93e66a3b", size = 6040045 }, + { url = "https://files.pythonhosted.org/packages/ac/b3/8db1873e3240ef1672ba87b89e949ece367089e29e4d221377bfdd288bd3/grpcio-1.66.2-cp313-cp313-win32.whl", hash = "sha256:20657d6b8cfed7db5e11b62ff7dfe2e12064ea78e93f1434d61888834bc86d75", size = 3537126 }, + { url = "https://files.pythonhosted.org/packages/a2/df/133216989fe7e17caeafd7ff5b17cc82c4e722025d0b8d5d2290c11fe2e6/grpcio-1.66.2-cp313-cp313-win_amd64.whl", hash = "sha256:fb70487c95786e345af5e854ffec8cb8cc781bcc5df7930c4fbb7feaa72e1cdf", size = 4278018 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "parver" +version = "0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arpeggio" }, + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/e5/1c774688a90f0b76e872e30f6f1ba3f5e14056cd0d96a684047d4a986226/parver-0.5.tar.gz", hash = "sha256:b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777", size = 26908 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/4c/f98024021bef4d44dce3613feebd702c7ad8883f777ff8488384c59e9774/parver-0.5-py3-none-any.whl", hash = "sha256:2281b187276c8e8e3c15634f62287b2fb6fe0efe3010f739a6bd1e45fa2bf2b2", size = 15172 }, +] + +[[package]] +name = "pip" +version = "24.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/b422acd212ad7eedddaf7981eee6e5de085154ff726459cf2da7c5a184c1/pip-24.3.1.tar.gz", hash = "sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99", size = 1931073 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/7d/500c9ad20238fcfcb4cb9243eede163594d7020ce87bd9610c9e02771876/pip-24.3.1-py3-none-any.whl", hash = "sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed", size = 1822182 }, +] + +[[package]] +name = "protobuf" +version = "4.25.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/dd/48d5fdb68ec74d70fabcc252e434492e56f70944d9f17b6a15e3746d2295/protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584", size = 380315 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/35/1b3c5a5e6107859c4ca902f4fbb762e48599b78129a05d20684fef4a4d04/protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8", size = 392457 }, + { url = "https://files.pythonhosted.org/packages/a7/ad/bf3f358e90b7e70bf7fb520702cb15307ef268262292d3bdb16ad8ebc815/protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea", size = 413449 }, + { url = "https://files.pythonhosted.org/packages/51/49/d110f0a43beb365758a252203c43eaaad169fe7749da918869a8c991f726/protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173", size = 394248 }, + { url = "https://files.pythonhosted.org/packages/c6/ab/0f384ca0bc6054b1a7b6009000ab75d28a5506e4459378b81280ae7fd358/protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d", size = 293717 }, + { url = "https://files.pythonhosted.org/packages/05/a6/094a2640be576d760baa34c902dcb8199d89bce9ed7dd7a6af74dcbbd62d/protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331", size = 294635 }, + { url = "https://files.pythonhosted.org/packages/33/90/f198a61df8381fb43ae0fe81b3d2718e8dcc51ae8502c7657ab9381fbc4f/protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41", size = 156467 }, +] + +[[package]] +name = "pulumi" +version = "3.146.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "debugpy" }, + { name = "dill" }, + { name = "grpcio" }, + { name = "pip" }, + { name = "protobuf" }, + { name = "pyyaml" }, + { name = "semver" }, + { name = "six" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/d5/8bc81fbfcac45a1bb4bc0be1d12005c7d970b7fed612cd2b5e015bf8447b/pulumi-3.146.0-py3-none-any.whl", hash = "sha256:d41d1fe147e733cd9d9dea9869a96a069e413b666545d2b1538526d6eec0e2ac", size = 297309 }, +] + +[[package]] +name = "pulumi-openstack" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parver" }, + { name = "pulumi" }, + { name = "semver" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/4e/969787c418ef1048ccfc29903d1d65226831ad6e14050a262beaca8ce1d5/pulumi_openstack-5.0.2.tar.gz", hash = "sha256:ffabd4cc755aeee435f0914d15ead5b0cf5d2a2fb223e1f96269b575519950ea", size = 344986 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/71/c783f5f81f9348854a69239e4f9e597a03254c1bbff2e434862c873d985e/pulumi_openstack-5.0.2-py3-none-any.whl", hash = "sha256:ad0bbb4c976e5b605013c5c1f9e0657d0fdc4fa3a4f45bab11dff410821ea268", size = 540442 }, +] + +[[package]] +name = "pyright" +version = "1.1.392.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/df/3c6f6b08fba7ccf49b114dfc4bb33e25c299883fd763f93fad47ef8bc58d/pyright-1.1.392.post0.tar.gz", hash = "sha256:3b7f88de74a28dcfa90c7d90c782b6569a48c2be5f9d4add38472bdaac247ebd", size = 3789911 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/b1/a18de17f40e4f61ca58856b9ef9b0febf74ff88978c3f7776f910071f567/pyright-1.1.392.post0-py3-none-any.whl", hash = "sha256:252f84458a46fa2f0fd4e2f91fc74f50b9ca52c757062e93f6c250c0d8329eb2", size = 5595487 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "semver" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/a9/b61190916030ee9af83de342e101f192bbb436c59be20a4cb0cdb7256ece/semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f", size = 45816 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/70/b84f9944a03964a88031ef6ac219b6c91e8ba2f373362329d8770ef36f02/semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4", size = 12901 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] From 319cc80a9fdcc846b5b53e75b18254da37949d83 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 26 Jan 2025 07:03:26 -0600 Subject: [PATCH 6/9] inventory: Configure for HostVDS openstack Using the Ansible OpenStack inventory plugin, we can automatically fetch information about running instances in HostVDS. We're deriving group membership from the `groups` metadata tag. The OpenStack API password must be specified in a `secure.yaml` file. We're omitting this from the repository because there's no apparent way to encrypt it. The inventory plugin tends to prefer IPv6 addresses over IPv4 when populating `ansible_host`, even if the control machine does not have IPv6 connectivity. Thus, we have to compose the relevant variables ourselves with a Jinja2 expression. --- .gitignore | 2 ++ clouds.yaml | 9 +++++++++ hostvds.openstack.yml | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 clouds.yaml create mode 100644 hostvds.openstack.yml diff --git a/.gitignore b/.gitignore index 2f1273a..59040e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ +/.inventory-cache /.vault-secret.gpg .fact-cache +/secure.yaml /victoria-metrics-*.tar.gz /victoria-metrics-*/ /tmp/ diff --git a/clouds.yaml b/clouds.yaml new file mode 100644 index 0000000..a6038e2 --- /dev/null +++ b/clouds.yaml @@ -0,0 +1,9 @@ +clouds: + hostvds: + region_name: us-east2 + auth: + username: hostvds-b3897be3-8920-4fb0-955d-340a162da6dd + project_name: hostvds-b3897be3-8920-4fb0-955d-340a162da6dd + auth_url: https://os-api.hostvds.com/identity + user_domain_name: Default + project_domain_id: default diff --git a/hostvds.openstack.yml b/hostvds.openstack.yml new file mode 100644 index 0000000..f349180 --- /dev/null +++ b/hostvds.openstack.yml @@ -0,0 +1,18 @@ +plugin: openstack.cloud.openstack +only_clouds: +- hostvds +cache: true +cache_plugin: jsonfile +cache_connection: .inventory-cache +compose: + ansible_user: '"root"' + ansible_host: &ipv4 + (openstack.addresses[openstack.addresses|first] + | selectattr('version', 'eq', 4) + | first + ).addr + ansible_ssh_host: *ipv4 +keyed_groups: +- key: openstack.metadata.groups|split(',') + prefix: '' + separator: '' From 33f315334e4bd3b387737c8f223dfeff81812d29 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 26 Jan 2025 07:33:16 -0600 Subject: [PATCH 7/9] users: Configure sudo on some machines `doas` is not available on Alma Linux, so we still have to use `sudo` on the VPS. --- group_vars/pyrocufflink/main.yml | 9 --------- group_vars/sudo.yml | 9 +++++++++ hosts | 7 +++++++ users.yml | 8 +++++++- 4 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 group_vars/sudo.yml diff --git a/group_vars/pyrocufflink/main.yml b/group_vars/pyrocufflink/main.yml index 49db7d7..0ace68b 100644 --- a/group_vars/pyrocufflink/main.yml +++ b/group_vars/pyrocufflink/main.yml @@ -16,13 +16,4 @@ root_authorized_keys: | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBw1T18jnBfR5reKAACOs/LMcs+jbclj6Eh8z56kJE7+ dustin@luma {% endif %} -sudo_use_pam_ssh_agent: true -sudo_authorized_ssh_keys: | - sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIF4yQAS0bAQ9Ymxgxv828MsX0z4ff/Fs//0PQOtPexRJAAAABHNzaDo= dustin@rosalina.pyrocufflink.blue - sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAINal4+Gn/KuyP6YTsQuW4cphfDcjrS428osVIqnqMfagAAAABHNzaDo= dustin@luma.pyrocufflink.blue - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDD3Ebb7dyEyCylgEjmhFxvGqbPkT+0KSpI+xEGXLFnn jenkins -# Default flags include -n, which makes Ansible complain about a "missing -# become password," even though it would never actually prompt for one. -ansible_become_flags: -H - fileserver_sftp_only_match: 'Group !server?admins,*' diff --git a/group_vars/sudo.yml b/group_vars/sudo.yml new file mode 100644 index 0000000..672410b --- /dev/null +++ b/group_vars/sudo.yml @@ -0,0 +1,9 @@ +ansible_become_method: sudo +sudo_use_pam_ssh_agent: true +sudo_authorized_ssh_keys: | + sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIF4yQAS0bAQ9Ymxgxv828MsX0z4ff/Fs//0PQOtPexRJAAAABHNzaDo= dustin@rosalina.pyrocufflink.blue + sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAINal4+Gn/KuyP6YTsQuW4cphfDcjrS428osVIqnqMfagAAAABHNzaDo= dustin@luma.pyrocufflink.blue + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDD3Ebb7dyEyCylgEjmhFxvGqbPkT+0KSpI+xEGXLFnn jenkins +# Default flags include -n, which makes Ansible complain about a "missing +# become password," even though it would never actually prompt for one. +ansible_become_flags: -H diff --git a/hosts b/hosts index bf61b4a..ab46368 100644 --- a/hosts +++ b/hosts @@ -206,6 +206,10 @@ smtp1.pyrocufflink.blue [squid] +[sudo:children] +pyrocufflink +vps + [synapse] [unifi] @@ -217,6 +221,9 @@ vmhost1.pyrocufflink.blue [vmagent:children] remote-blackbox +[vps:children] +hostvds + [wheelhost] file0.pyrocufflink.blue diff --git a/users.yml b/users.yml index eef74fe..7c96f15 100644 --- a/users.yml +++ b/users.yml @@ -1,8 +1,14 @@ -- hosts: all +- hosts: sudo + roles: + - role: sudo + tags: + - sudo +- hosts: '!sudo' roles: - role: doas tags: - doas +- hosts: all tasks: - name: ensure users exist user: From c00d6f49de6b567c82cd74fbb8778d195a333b06 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 26 Jan 2025 07:55:56 -0600 Subject: [PATCH 8/9] hosts: Add OVH VPS It turns out, $0.99/mo might be _too_ cheap for a cloud server. Running the Blackbox Exporter+vmagent on the HostVDS instance worked for a few days, but then it started having frequent timeouts when probing the websites. I tried redeploying the instance, switching to a larger instance, and moving it to different networks. Unfortunately, none of this seemed to help. Switching over to a VPS running in OVH cloud. OVH VPS servers are managed statically, as opposed to via API, so we can't use Pulumi to create them. This one was created for me when I signed up for an OVH acount. --- hosts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hosts b/hosts index ab46368..ed63f74 100644 --- a/hosts +++ b/hosts @@ -181,6 +181,9 @@ web0.pyrocufflink.blue [radius:children] samba-dc +[remote-blackbox] +vps-04485add.vps.ovh.us + [repohost] file0.pyrocufflink.blue @@ -221,6 +224,9 @@ vmhost1.pyrocufflink.blue [vmagent:children] remote-blackbox +[vps] +vps-04485add.vps.ovh.us + [vps:children] hostvds From a58dbb74c53c5048c496852492938d95884e408f Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Tue, 28 Jan 2025 17:36:35 -0600 Subject: [PATCH 9/9] r/vmhost: Clean up qemu packages At some point, the _qemu-kvm_ package became a meta-package that installs _everything_ QEMU-related. All drivers, backends, frontends, etc. get pulled in, which results in a huge amount of wasted space. Recently, the VM hosts started getting alerts about their `/` filesystem getting too full, which is how I discovered this. We can dramatically reduce the disk space footprint by installing only the "core" package and the drivers we need for our servers. After making and applying this change, which marks the listed packages as "leaf" installs, I then manually uninstalled the _qemu-kvm_ package. This uninstalled everything else that is not specifically listed. --- roles/vmhost/vars/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/roles/vmhost/vars/main.yml b/roles/vmhost/vars/main.yml index f8cb985..0346f28 100644 --- a/roles/vmhost/vars/main.yml +++ b/roles/vmhost/vars/main.yml @@ -4,4 +4,7 @@ vmhost_required_packages: - libvirt-daemon-driver-storage-core - python3-libvirt - python3-lxml -- qemu-kvm +- qemu-audio-spice +- qemu-device-display-qxl +- qemu-device-display-virtio-vga +- qemu-system-x86-core