6859: script does not catch expections during salvaging; instead they are propagated...
[arvados.git] / services / api / lib / salvage_collection.rb
1 module SalvageCollection
2   # Take two input parameters: a collection uuid and reason
3   # Get "src_collection" with the given uuid
4   # Create a new collection with:
5   #   src_collection.manifest_text as "invalid_manifest_text.txt"
6   #   Locators from src_collection.manifest_text as "salvaged_data"
7   # Update src_collection:
8   #   Set src_collection.manifest_text to: ""
9   #   Append to src_collection.name: " (reason; salvaged data at new_collection.uuid)"
10   #   Set portable_data_hash to "d41d8cd98f00b204e9800998ecf8427e+0"
11
12   require File.dirname(__FILE__) + '/../config/environment'
13   include ApplicationHelper
14   require 'tempfile'
15   require 'shellwords'
16
17   def self.salvage_collection_arv_put cmd
18     new_manifest = %x(#{cmd})
19     if $?.success?
20       new_manifest
21     else
22       raise "Error during arv-put."
23     end
24   end
25
26   LOCATOR_REGEXP = /^([[:xdigit:]]{32})(\+(.*))?\z/
27   def self.salvage_collection_locator_data manifest
28       # Get all the locators from the original manifest
29       locators = []
30       size = 0
31       manifest.each_line do |line|
32         line.split(' ').each do |word|
33           if match = LOCATOR_REGEXP.match(word)
34             if match.size > 3 and match[3]
35               size_str = match[3].split('+')[0]
36               if size_str.to_i.to_s == size_str
37                 word = match[1] + '+' + size_str     # get rid of any hints
38                 size += size_str.to_i
39               else
40                 word = match[1]
41               end
42             else
43               word = match[1]
44             end
45             locators << word
46           end
47         end
48       end
49       locators << 'd41d8cd98f00b204e9800998ecf8427e+0' if !locators.any?
50       return [locators, size]
51   end
52
53   def self.salvage_collection uuid, reason='salvaged - see #6277, #6859'
54     act_as_system_user do
55       if !ENV['ARVADOS_API_TOKEN'].present? or !ENV['ARVADOS_API_HOST'].present?
56         raise "ARVADOS environment variables missing. Please set your admin user credentials as ARVADOS environment variables."
57       end
58
59       if !uuid.present?
60         raise "Collection UUID is required."
61       end
62
63       src_collection = Collection.find_by_uuid uuid
64       if !src_collection
65         raise "No collection found for #{uuid}."
66       end
67
68       src_manifest = src_collection.manifest_text || ''
69
70       # create new collection using 'arv-put' with original manifest_text as the data
71       temp_file = Tempfile.new('temp')
72       temp_file.write(src_manifest)
73       temp_file.close
74
75       new_manifest = salvage_collection_arv_put "arv-put --as-stream --use-filename invalid_manifest_text.txt #{Shellwords::shellescape(temp_file.path)}"
76
77       temp_file.unlink
78
79       # Get the locator data in the format [[locators], size] from the original manifest
80       locator_data = salvage_collection_locator_data src_manifest
81
82       new_manifest += (". #{locator_data[0].join(' ')} 0:#{locator_data[1]}:salvaged_data\n")
83
84       new_collection = Collection.new
85       new_collection.name = "salvaged from #{src_collection.uuid}, #{src_collection.portable_data_hash}"
86       new_collection.manifest_text = new_manifest
87       new_collection.portable_data_hash = Digest::MD5.hexdigest(new_collection.manifest_text)
88
89       created = new_collection.save!
90       raise "New collection creation failed." if !created
91
92       $stderr.puts "Salvaged manifest and data for #{uuid} are in #{new_collection.uuid}."
93       puts "Created new collection #{new_collection.uuid}"
94
95       # update src_collection collection name, pdh, and manifest_text
96       src_collection.name = (src_collection.name || '') + ' (' + (reason || '') + '; salvaged data at ' + new_collection.uuid + ')'
97       src_collection.manifest_text = ''
98       src_collection.portable_data_hash = 'd41d8cd98f00b204e9800998ecf8427e+0'
99       src_collection.save!
100       $stderr.puts "Collection #{uuid} emptied and renamed to #{src_collection.name.inspect}."
101     end
102   end
103 end