18004: Fixes a couple of race condition bugs related to caching remote users.
[arvados.git] / services / api / lib / tasks / symbols.rake
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'current_api_client'
6
7 # This is needed instead of just including CurrentApiClient so that its
8 # methods don't get imported as Object's class methods; this is a problem because
9 # the methods would be imported only on test environment. See #15716 for more info.
10 class CurrentApiClientHelper
11   extend CurrentApiClient
12 end
13
14 def has_symbols? x
15   if x.is_a? Hash
16     x.each do |k,v|
17       return true if has_symbols?(k) or has_symbols?(v)
18     end
19   elsif x.is_a? Array
20     x.each do |k|
21       return true if has_symbols?(k)
22     end
23   elsif x.is_a? Symbol
24     return true
25   elsif x.is_a? String
26     return true if x.start_with?(':') && !x.start_with?('::')
27   end
28   false
29 end
30
31 def check_for_serialized_symbols rec
32   jsonb_cols = rec.class.columns.select{|c| c.type == :jsonb}.collect{|j| j.name}
33   (jsonb_cols + rec.class.serialized_attributes.keys).uniq.each do |colname|
34     if has_symbols? rec.attributes[colname]
35       st = recursive_stringify rec.attributes[colname]
36       puts "Found value potentially containing Ruby symbols in #{colname} attribute of #{rec.uuid}, current value is\n#{rec.attributes[colname].to_s[0..1024]}\nrake symbols:stringify will update it to:\n#{st.to_s[0..1024]}\n\n"
37     end
38   end
39 end
40
41 def recursive_stringify x
42   if x.is_a? Hash
43     Hash[x.collect do |k,v|
44            [recursive_stringify(k), recursive_stringify(v)]
45          end]
46   elsif x.is_a? Array
47     x.collect do |k|
48       recursive_stringify k
49     end
50   elsif x.is_a? Symbol
51     x.to_s
52   elsif x.is_a? String and x.start_with?(':') and !x.start_with?('::')
53     x[1..-1]
54   else
55     x
56   end
57 end
58
59 def stringify_serialized_symbols rec
60   # ensure_serialized_attribute_type should prevent symbols from
61   # getting into the database in the first place. If someone managed
62   # to get them into the database (perhaps using an older version)
63   # we'll convert symbols to strings when loading from the
64   # database. (Otherwise, loading and saving an object with existing
65   # symbols in a serialized field will crash.)
66   jsonb_cols = rec.class.columns.select{|c| c.type == :jsonb}.collect{|j| j.name}
67   (jsonb_cols + rec.class.serialized_attributes.keys).uniq.each do |colname|
68     if has_symbols? rec.attributes[colname]
69       begin
70         st = recursive_stringify rec.attributes[colname]
71         puts "Updating #{colname} attribute of #{rec.uuid} from\n#{rec.attributes[colname].to_s[0..1024]}\nto\n#{st.to_s[0..1024]}\n\n"
72         rec.write_attribute(colname, st)
73         rec.save!
74       rescue => e
75         puts "Failed to update #{rec.uuid}: #{e}"
76       end
77     end
78   end
79 end
80
81 namespace :symbols do
82   desc 'Warn about serialized values starting with ":" that may be symbols'
83   task check: :environment do
84     [ApiClientAuthorization, ApiClient,
85      AuthorizedKey, Collection,
86      Container, ContainerRequest, Group,
87      Human, Job, JobTask, KeepDisk, KeepService, Link,
88      Node, PipelineInstance, PipelineTemplate,
89      Repository, Specimen, Trait, User, VirtualMachine,
90      Workflow].each do |klass|
91       CurrentApiClientHelper.act_as_system_user do
92         klass.all.each do |c|
93           check_for_serialized_symbols c
94         end
95       end
96     end
97   end
98
99   task stringify: :environment do
100     [ApiClientAuthorization, ApiClient,
101      AuthorizedKey, Collection,
102      Container, ContainerRequest, Group,
103      Human, Job, JobTask, KeepDisk, KeepService, Link,
104      Node, PipelineInstance, PipelineTemplate,
105      Repository, Specimen, Trait, User, VirtualMachine,
106      Workflow].each do |klass|
107       CurrentApiClientHelper.act_as_system_user do
108         klass.all.each do |c|
109           stringify_serialized_symbols c
110         end
111       end
112     end
113   end
114 end