From 0058a058a1329348d49bca4a88a471cf8b5254b3 Mon Sep 17 00:00:00 2001 From: David Backeus Date: Thu, 20 Jun 2024 11:30:09 +0200 Subject: [PATCH] WIP: AASM approach sketch --- Gemfile | 1 + Gemfile.lock | 3 ++ app/controllers/configs_controller.rb | 5 +-- app/controllers/servers_controller.rb | 2 +- app/models/server.rb | 59 +++++++++++++++++++++++++- app/models/server/hetzner_cloud.rb | 2 +- app/models/server/hetzner_dedicated.rb | 2 +- 7 files changed, 66 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 21e2e6d..b673bd1 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source "https://rubygems.org" ruby "3.2.0" +gem "aasm" gem "bootsnap", require: false gem "importmap-rails" gem "net-ssh" diff --git a/Gemfile.lock b/Gemfile.lock index 141a25b..09db31f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,8 @@ GEM remote: https://rubygems.org/ specs: + aasm (5.5.0) + concurrent-ruby (~> 1.0) actioncable (7.1.3.2) actionpack (= 7.1.3.2) activesupport (= 7.1.3.2) @@ -239,6 +241,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + aasm bootsnap cloudflare-rails debug diff --git a/app/controllers/configs_controller.rb b/app/controllers/configs_controller.rb index 0e9c2d8..fd80173 100644 --- a/app/controllers/configs_controller.rb +++ b/app/controllers/configs_controller.rb @@ -11,12 +11,11 @@ def show server = Server.find_by_ip!(ip) if server.machine_config - # Using 1.second.ago seems silly but is required to show the correct status in the UI at the moment - server.update!(last_request_for_configuration_at: 1.second.ago, last_configured_at: Time.now) + server.configure! headers["Content-Type"] = "text/yaml" render plain: server.machine_config.generate_config else - server.update!(last_configured_at: nil, last_request_for_configuration_at: Time.now) + server.request_configuration! render plain: "No configuration found for server with IP #{server.ip}", status: 420 end end diff --git a/app/controllers/servers_controller.rb b/app/controllers/servers_controller.rb index 5f17ce7..2da752b 100644 --- a/app/controllers/servers_controller.rb +++ b/app/controllers/servers_controller.rb @@ -42,7 +42,7 @@ def bootstrap def rescue server = Server.find(params[:id]) - server.rescue + server.rescue! redirect_to servers_path, notice: "Server #{server.name} is rebooting in rescue mode" end diff --git a/app/models/server.rb b/app/models/server.rb index 02d7917..30e6415 100644 --- a/app/models/server.rb +++ b/app/models/server.rb @@ -7,6 +7,61 @@ class Server < ApplicationRecord has_one :machine_config, dependent: :destroy has_one :config, through: :machine_config + include AASM + + aasm timestamps: true do + state :pending, initial: true + state :rescuing + state :failed_rescue + state :rescued + state :installing_talos + state :awaiting_configuration + state :configuring + state :configured + state :ready + state :inaccessible + + event :rescue, before: :before_rescue do + transitions to: :rescuing + end + + event :await_rescued do + transitions from: %i[rescuing failed_rescue], to: :rescued, guard: :bootstrappable? + transitions from: :rescuing, to: :failed_rescue, guard: :rescue_timeout? + end + + event :install_talos, before: :bootstrap! do + transitions from: :rescued, to: :installing_talos + end + + event :request_configuration do + before do + assign_attributes(last_configured_at: nil, last_request_for_configuration_at: Time.now) + end + + transitions to: :awaiting_configuration + end + + event :configure do + before do + # 1.second.ago seems silly but is required to show the correct status in the UI at the moment + assign_attributes(last_configured_at: Time.now, last_request_for_configuration_at: 1.second.ago) + end + + transitions to: :configuring + end + + event :await_configured do + transitions from: %i[configuring failed_configure], to: :configured, guard: :talos_accessible? + transitions from: :configuring, to: :failed_configure, guard: :configure_timeout? + end + + event :await_ready do + transitions from: %i[configured failed_ready], to: :ready, guard: :ready? + transitions from: :configured, to: :failed_ready, guard: :ready_timeout? + end + end + attr_accessor :sync # set to true to sync changed attributes to hetzner validates_uniqueness_of :name, allow_nil: true @@ -85,8 +140,8 @@ def bootstrap! update!(accessible: false) end - def rescue - raise "#rescue is not implemented for #{self.class.name}" + def before_rescue + raise "#before_rescue is not implemented for #{self.class.name}" end def reset diff --git a/app/models/server/hetzner_cloud.rb b/app/models/server/hetzner_cloud.rb index 8d8759b..c24939c 100644 --- a/app/models/server/hetzner_cloud.rb +++ b/app/models/server/hetzner_cloud.rb @@ -17,7 +17,7 @@ def bootstrappable? session&.shutdown! end - def rescue + def before_rescue ::HetznerCloud.active_rescue_system(id) # Hetzner appears to temporarily lock the server immediately after enabling rescue mode. diff --git a/app/models/server/hetzner_dedicated.rb b/app/models/server/hetzner_dedicated.rb index 28d0ead..a831d2e 100644 --- a/app/models/server/hetzner_dedicated.rb +++ b/app/models/server/hetzner_dedicated.rb @@ -17,7 +17,7 @@ def bootstrappable? session&.shutdown! end - def rescue + def before_rescue Hetzner.active_rescue_system(id) if Hetzner.reset_state(id).fetch("operating_status") == "shut off"