From 97e65a83454c51b24ab567555993bbb678885034 Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Fri, 22 Sep 2023 14:38:06 +0200 Subject: [PATCH] Attach a label to servers with their desired deletion date This then enables us to periodically scan for images with a unix timestamp in the label that's smaller than now() and delete them. Co-authored-by: Ewoud Kohl van Wijngaarden --- README.md | 14 ++++++++++++-- lib/beaker/hypervisor/hcloud.rb | 15 +++++++++++++++ spec/beaker/hypervisor/hcloud_spec.rb | 6 ++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1e99b49..37e674b 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,19 @@ are being created: # Cleanup -In cases where the beaker process is killed before finishing, it may leave resources in Hetzner cloud. These will need to be manually deleted. +Every created cloud instance gets a label `delete_vm_after: 1698792887`. By +default this is the UNIX timestamp during VM creation + an hour. -Look for servers in your project named exactly as the ones in your beaker host configuration and SSH keys with names beginning with `Beaker-`. +You can modify the default of an hour by setting the `BEAKER_HCLOUD_DELETE_VM_AFTER` +environment variable to any positive integer. It will be interpreted as seconds. + +In cases where the beaker process is killed before finishing, it may leave +resources in Hetzner cloud. These will need to be manually deleted. Look for +servers in your project named exactly as the ones in your beaker host +configuration and SSH keys with names beginning with `Beaker-`. + +You can also periodically scan for VMs where the `delete_vm_after` points to a +past timestamp and delete them. # Contributing diff --git a/lib/beaker/hypervisor/hcloud.rb b/lib/beaker/hypervisor/hcloud.rb index a05e35b..592224a 100644 --- a/lib/beaker/hypervisor/hcloud.rb +++ b/lib/beaker/hypervisor/hcloud.rb @@ -3,6 +3,7 @@ require 'hcloud' require 'ed25519' require 'bcrypt_pbkdf' +require 'time' require_relative '../../beaker-hcloud/ssh_data_patches' @@ -15,7 +16,9 @@ def initialize(hosts, options) # rubocop:disable Lint/MissingSuper @options = options @logger = options[:logger] || Beaker::Logger.new @hosts = hosts + @delete_vm_after = ENV.fetch('BEAKER_HCLOUD_DELETE_VM_AFTER', 60 * 60).to_i + raise 'BEAKER_HCLOUD_DELETE_VM_AFTER needs to be a positive integer' unless @delete_vm_after.positive? raise 'You need to pass a token as BEAKER_HCLOUD_TOKEN environment variable' unless ENV['BEAKER_HCLOUD_TOKEN'] @client = ::Hcloud::Client.new(token: ENV.fetch('BEAKER_HCLOUD_TOKEN')) @@ -53,6 +56,8 @@ def ssh_key_name @options[:aws_keyname_modifier], @options[:timestamp].strftime('%F_%H_%M_%S_%N'), ].join('-') + @logger.debug("ssh_key_name is: #{safe_hostname}") + safe_hostname end def create_ssh_key @@ -69,6 +74,15 @@ def create_ssh_key hcloud_ssh_key end + # we need to save the date as unix timestamp. Hetzner Cloud labels only support: + # "alphanumeric character ([a-z0-9A-Z]) with dashes (-), underscores (_), dots (.), and alphanumerics between." + # https://docs.hetzner.cloud/#labels + def vm_deletion_date + date = (Time.now.to_i + @delete_vm_after).to_s + @logger.debug("Server is valid until: #{date}") + date + end + def create_server(host) @logger.notify "provisioning #{host.name}" location = host[:location] || 'nbg1' @@ -79,6 +93,7 @@ def create_server(host) server_type: server_type, image: host[:image], ssh_keys: [ssh_key_name], + labels: { delete_vm_after: vm_deletion_date }, ) while action.status == 'running' sleep 5 diff --git a/spec/beaker/hypervisor/hcloud_spec.rb b/spec/beaker/hypervisor/hcloud_spec.rb index e2eb309..ac3b86a 100644 --- a/spec/beaker/hypervisor/hcloud_spec.rb +++ b/spec/beaker/hypervisor/hcloud_spec.rb @@ -45,7 +45,8 @@ 'dns_ptr' => 'server1.example.com', }, }, - destroy: true) + destroy: true, + labels: { vm_delete_after: '1695385549' }) end let(:server2) do double(:server2, @@ -56,7 +57,8 @@ 'dns_ptr' => 'server2.example.com', }, }, - destroy: true) + destroy: true, + labels: { vm_delete_after: '1695385549' }) end let(:action_double) do double(:action, status: 'success')