debian-mirror-gitlab/app/services/terraform/remote_state_handler.rb

101 lines
2.5 KiB
Ruby
Raw Normal View History

2020-04-22 19:07:51 +05:30
# frozen_string_literal: true
module Terraform
class RemoteStateHandler < BaseService
include Gitlab::OptimisticLocking
StateLockedError = Class.new(StandardError)
2020-07-28 23:09:34 +05:30
UnauthorizedError = Class.new(StandardError)
2020-04-22 19:07:51 +05:30
def find_with_lock
2020-07-28 23:09:34 +05:30
retrieve_with_lock(find_only: true) do |state|
yield state if block_given?
end
2020-04-22 19:07:51 +05:30
end
def handle_with_lock
2020-07-28 23:09:34 +05:30
raise UnauthorizedError unless can_modify_state?
2020-04-22 19:07:51 +05:30
retrieve_with_lock do |state|
raise StateLockedError unless lock_matches?(state)
yield state if block_given?
state.save! unless state.destroyed?
end
2020-11-05 12:06:23 +05:30
nil
2020-04-22 19:07:51 +05:30
end
def lock!
raise ArgumentError if params[:lock_id].blank?
2020-07-28 23:09:34 +05:30
raise UnauthorizedError unless can_modify_state?
2020-04-22 19:07:51 +05:30
retrieve_with_lock do |state|
raise StateLockedError if state.locked?
state.lock_xid = params[:lock_id]
state.locked_by_user = current_user
2020-05-24 23:13:21 +05:30
state.locked_at = Time.current
2020-04-22 19:07:51 +05:30
state.save!
end
end
def unlock!
2020-07-28 23:09:34 +05:30
raise UnauthorizedError unless can_modify_state?
2020-04-22 19:07:51 +05:30
retrieve_with_lock do |state|
# force-unlock does not pass ID, so we ignore it if it is missing
raise StateLockedError unless params[:lock_id].nil? || lock_matches?(state)
state.lock_xid = nil
state.locked_by_user = nil
state.locked_at = nil
state.save!
end
end
private
2020-07-28 23:09:34 +05:30
def retrieve_with_lock(find_only: false)
create_or_find!(find_only: find_only).tap { |state| retry_optimistic_lock(state) { |state| yield state } }
end
def create_or_find!(find_only:)
raise ArgumentError unless params[:name].present?
find_params = { project: project, name: params[:name] }
2021-03-11 19:13:27 +05:30
return find_state!(find_params) if find_only
state = Terraform::State.create_or_find_by(find_params)
# https://github.com/rails/rails/issues/36027
return state unless state.errors.of_kind? :name, :taken
find_state(find_params)
2020-04-22 19:07:51 +05:30
end
def lock_matches?(state)
return true if state.lock_xid.nil? && params[:lock_id].nil?
ActiveSupport::SecurityUtils
.secure_compare(state.lock_xid.to_s, params[:lock_id].to_s)
end
2020-07-28 23:09:34 +05:30
def can_modify_state?
current_user.can?(:admin_terraform_state, project)
end
2021-03-11 19:13:27 +05:30
def find_state(find_params)
Terraform::State.find_by(find_params) # rubocop: disable CodeReuse/ActiveRecord
end
def find_state!(find_params)
find_state(find_params) || raise(ActiveRecord::RecordNotFound.new("Couldn't find state"))
end
2020-04-22 19:07:51 +05:30
end
end