18874: Merge branch 'main' into 18874-merge-wb2
[arvados.git] / services / api / app / controllers / database_controller.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class DatabaseController < ApplicationController
6   skip_before_action :find_object_by_uuid
7   skip_before_action :render_404_if_no_object
8   before_action :admin_required
9   around_action :silence_logs, only: [:reset]
10
11   def reset
12     raise ArvadosModel::PermissionDeniedError unless Rails.env == 'test'
13
14     # Sanity check: If someone has actually logged in here, this might
15     # not really be a throwaway database. Client test suites should
16     # use @example.com email addresses when creating user records, so
17     # we can tell they're not valuable.
18     user_uuids = User.
19       where('email is null or (email not like ? and email not like ?)', '%@example.com', '%.example.com').
20       collect(&:uuid)
21     fixture_uuids =
22       YAML::load_file(File.expand_path('../../../test/fixtures/users.yml',
23                                        __FILE__)).
24       values.collect { |u| u['uuid'] }
25     unexpected_uuids = user_uuids - fixture_uuids
26     if unexpected_uuids.any?
27       logger.error("Running in test environment, but non-fixture users exist: " +
28                    "#{unexpected_uuids}" + "\nMaybe test users without @example.com email addresses were created?")
29       raise ArvadosModel::PermissionDeniedError
30     end
31
32     require 'active_record/fixtures'
33
34     # What kinds of fixtures do we have?
35     fixturesets = Dir.glob(Rails.root.join('test', 'fixtures', '*.yml')).
36       collect { |yml| yml.match(/([^\/]*)\.yml$/)[1] }
37
38     # Don't reset keep_services: clients need to discover our
39     # integration-testing keepstores, not test fixtures.
40     fixturesets -= %w[keep_services]
41
42     table_names = '"' + ActiveRecord::Base.connection.tables.join('","') + '"'
43
44     attempts_left = 20
45     begin
46       ActiveRecord::Base.transaction do
47         # Avoid deadlock by locking all tables before doing anything
48         # drastic.
49         ActiveRecord::Base.connection.execute \
50         "LOCK TABLE #{table_names} IN ACCESS EXCLUSIVE MODE"
51
52         # Delete existing fixtures (and everything else) from fixture
53         # tables
54         fixturesets.each do |x|
55           x.classify.constantize.unscoped.delete_all
56         end
57
58         # create_fixtures() is a no-op for cached fixture sets, so
59         # uncache them all.
60         ActiveRecord::FixtureSet.reset_cache
61         ActiveRecord::FixtureSet.
62           create_fixtures(Rails.root.join('test', 'fixtures'), fixturesets)
63
64         # Reset cache and global state
65         Rails.cache.clear
66         ActiveRecord::Base.connection.clear_query_cache
67
68         # Reload database seeds
69         DatabaseSeeds.install
70       end
71     rescue ActiveRecord::StatementInvalid => e
72       if "#{e.inspect}" =~ /deadlock detected/i and (attempts_left -= 1) > 0
73         logger.info "Waiting for lock -- #{e.inspect}"
74         sleep 0.5
75         retry
76       end
77       raise
78     end
79
80     require 'update_permissions'
81
82     refresh_permissions
83     refresh_trashed
84
85     # Done.
86     send_json success: true
87   end
88
89   protected
90
91   def silence_logs
92     Rails.logger.info("(logging level temporarily raised to :error, see #{__FILE__})")
93     orig = ActiveRecord::Base.logger.level
94     ActiveRecord::Base.logger.level = :error
95     begin
96       yield
97     ensure
98       ActiveRecord::Base.logger.level = orig
99     end
100   end
101 end