longleaf 0.1.0 → 0.2.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +13 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +4 -0
  5. data/.rubocop_todo.yml +755 -0
  6. data/README.md +29 -7
  7. data/lib/longleaf/candidates/file_selector.rb +107 -0
  8. data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +99 -0
  9. data/lib/longleaf/candidates/service_candidate_locator.rb +18 -0
  10. data/lib/longleaf/cli.rb +102 -6
  11. data/lib/longleaf/commands/deregister_command.rb +50 -0
  12. data/lib/longleaf/commands/preserve_command.rb +45 -0
  13. data/lib/longleaf/commands/register_command.rb +24 -38
  14. data/lib/longleaf/commands/validate_config_command.rb +6 -2
  15. data/lib/longleaf/commands/validate_metadata_command.rb +49 -0
  16. data/lib/longleaf/errors.rb +19 -0
  17. data/lib/longleaf/events/deregister_event.rb +55 -0
  18. data/lib/longleaf/events/event_names.rb +9 -0
  19. data/lib/longleaf/events/event_status_tracking.rb +59 -0
  20. data/lib/longleaf/events/preserve_event.rb +71 -0
  21. data/lib/longleaf/events/register_event.rb +37 -26
  22. data/lib/longleaf/helpers/digest_helper.rb +50 -0
  23. data/lib/longleaf/helpers/service_date_helper.rb +51 -0
  24. data/lib/longleaf/logging.rb +1 -0
  25. data/lib/longleaf/logging/redirecting_logger.rb +9 -8
  26. data/lib/longleaf/models/app_fields.rb +2 -0
  27. data/lib/longleaf/models/file_record.rb +8 -3
  28. data/lib/longleaf/models/md_fields.rb +1 -0
  29. data/lib/longleaf/models/metadata_record.rb +16 -4
  30. data/lib/longleaf/models/service_definition.rb +4 -3
  31. data/lib/longleaf/models/service_fields.rb +2 -0
  32. data/lib/longleaf/models/service_record.rb +4 -1
  33. data/lib/longleaf/models/storage_location.rb +18 -1
  34. data/lib/longleaf/preservation_services/fixity_check_service.rb +121 -0
  35. data/lib/longleaf/preservation_services/rsync_replication_service.rb +183 -0
  36. data/lib/longleaf/services/application_config_deserializer.rb +4 -6
  37. data/lib/longleaf/services/application_config_manager.rb +4 -2
  38. data/lib/longleaf/services/application_config_validator.rb +1 -1
  39. data/lib/longleaf/services/configuration_validator.rb +1 -0
  40. data/lib/longleaf/services/metadata_deserializer.rb +47 -10
  41. data/lib/longleaf/services/metadata_serializer.rb +42 -6
  42. data/lib/longleaf/services/service_class_cache.rb +112 -0
  43. data/lib/longleaf/services/service_definition_manager.rb +5 -1
  44. data/lib/longleaf/services/service_definition_validator.rb +4 -4
  45. data/lib/longleaf/services/service_manager.rb +72 -9
  46. data/lib/longleaf/services/service_mapping_manager.rb +4 -3
  47. data/lib/longleaf/services/service_mapping_validator.rb +4 -4
  48. data/lib/longleaf/services/storage_location_manager.rb +26 -5
  49. data/lib/longleaf/services/storage_location_validator.rb +1 -1
  50. data/lib/longleaf/services/storage_path_validator.rb +2 -2
  51. data/lib/longleaf/specs/config_builder.rb +9 -5
  52. data/lib/longleaf/specs/custom_matchers.rb +9 -0
  53. data/lib/longleaf/specs/file_helpers.rb +60 -0
  54. data/lib/longleaf/version.rb +1 -1
  55. data/longleaf.gemspec +1 -0
  56. metadata +39 -7
  57. data/lib/longleaf/commands/abstract_command.rb +0 -37
@@ -0,0 +1,45 @@
1
+ require 'longleaf/errors'
2
+ require 'longleaf/events/event_status_tracking'
3
+ require 'longleaf/events/preserve_event'
4
+ require 'longleaf/services/application_config_deserializer'
5
+ require 'longleaf/candidates/file_selector'
6
+ require 'longleaf/candidates/service_candidate_locator'
7
+ require 'longleaf/events/event_names'
8
+ require 'longleaf/logging'
9
+
10
+ module Longleaf
11
+ # Command for preserving files
12
+ class PreserveCommand
13
+ include Longleaf::Logging
14
+ include Longleaf::EventStatusTracking
15
+
16
+ def initialize(app_manager)
17
+ @app_manager = app_manager
18
+ end
19
+
20
+ # Execute the preserve command on the given parameters
21
+ # @param file_selector [FileSelector] selector for files to preserve
22
+ # @param force [Boolean] force flag
23
+ # @return [Integer] status code
24
+ def execute(file_selector:, force: false)
25
+ begin
26
+ # Perform preserve events on each of the file paths provided
27
+ candidate_locator = ServiceCandidateLocator.new(@app_manager)
28
+ candidate_it = candidate_locator.candidate_iterator(file_selector, EventNames::PRESERVE, force)
29
+ candidate_it.each do |file_rec|
30
+ f_path = file_rec.path
31
+
32
+ logger.debug("Selected candidate #{file_rec.path} for a preserve event")
33
+ preserve_event = PreserveEvent.new(file_rec: file_rec, force: force, app_manager: @app_manager)
34
+ track_status(preserve_event.perform)
35
+ end
36
+ rescue LongleafError => e
37
+ record_failure(EventNames::PRESERVE, nil, e.message)
38
+ rescue => err
39
+ record_failure(EventNames::PRESERVE, error: err)
40
+ end
41
+
42
+ return_status
43
+ end
44
+ end
45
+ end
@@ -1,56 +1,42 @@
1
1
  require 'longleaf/services/application_config_deserializer'
2
2
  require 'longleaf/events/register_event'
3
3
  require 'longleaf/models/file_record'
4
- require 'longleaf/commands/abstract_command'
4
+ require 'longleaf/events/event_names'
5
+ require 'longleaf/events/event_status_tracking'
5
6
 
6
- # Command for registering files with longleaf
7
7
  module Longleaf
8
- class RegisterCommand < AbstractCommand
8
+ # Command for registering files with longleaf
9
+ class RegisterCommand
10
+ include Longleaf::EventStatusTracking
9
11
 
10
- def initialize(config_path)
11
- @config_path = config_path
12
+ def initialize(app_manager)
13
+ @app_manager = app_manager
12
14
  end
13
15
 
14
16
  # Execute the register command on the given parameters
15
- def execute(file_paths: nil, force: false, checksums: nil)
16
- if file_paths.nil? || file_paths.empty?
17
- record_failure("Must provide one or more file paths to register")
18
- return return_status
19
- end
20
-
17
+ # @param file_selector [FileSelector] selector for files to register
18
+ # @param force [Boolean] force flag
19
+ # @param checksums [Array] array of checksums
20
+ # @return [Integer] status code
21
+ def execute(file_selector:, force: false, checksums: nil)
21
22
  begin
22
- # Retrieve the application configuration
23
- app_manager = Longleaf::ApplicationConfigDeserializer.deserialize(@config_path)
24
-
25
23
  # Perform register events on each of the file paths provided
26
- file_paths.each do |f_path|
27
- begin
28
- storage_location = app_manager.location_manager.get_location_by_path(f_path)
29
- if storage_location.nil?
30
- raise InvalidStoragePathError.new(
31
- "Unable to register '#{f_path}', it does not belong to any registered storage locations.")
32
- end
24
+ loop do
25
+ f_path = file_selector.next_path
26
+ break if f_path.nil?
33
27
 
34
- raise InvalidStoragePathError.new("Unable to register '#{f_path}', file does not exist or is unreachable.") \
35
- unless File.file?(f_path)
28
+ storage_location = @app_manager.location_manager.get_location_by_path(f_path)
29
+
30
+ file_rec = FileRecord.new(f_path, storage_location)
36
31
 
37
- file_rec = FileRecord.new(f_path, storage_location)
38
-
39
- register_event = RegisterEvent.new(file_rec: file_rec, force: force, app_manager: app_manager,
40
- checksums: checksums)
41
- register_event.perform
42
-
43
- record_success(RegisterEvent::EVENT_NAME, f_path)
44
- rescue RegistrationError => err
45
- record_failure(RegisterEvent::EVENT_NAME, f_path, err.message)
46
- rescue InvalidStoragePathError => err
47
- record_failure(RegisterEvent::EVENT_NAME, f_path, err.message)
48
- end
32
+ register_event = RegisterEvent.new(file_rec: file_rec, force: force, app_manager: @app_manager,
33
+ checksums: checksums)
34
+ track_status(register_event.perform)
49
35
  end
50
- rescue ConfigurationError => err
51
- record_failure("Failed to load application configuration due to the following issue:\n#{err.message}")
36
+ rescue InvalidStoragePathError, StorageLocationUnavailableError => err
37
+ record_failure(EventNames::REGISTER, nil, err.message)
52
38
  rescue => err
53
- record_failure(RegisterEvent::EVENT_NAME, error: err)
39
+ record_failure(EventNames::REGISTER, error: err)
54
40
  end
55
41
 
56
42
  return_status
@@ -1,12 +1,16 @@
1
1
  require 'longleaf/services/application_config_deserializer'
2
- require 'longleaf/commands/abstract_command'
2
+ require 'longleaf/events/event_status_tracking'
3
3
 
4
4
  module Longleaf
5
- class ValidateConfigCommand < AbstractCommand
5
+ # Command for validating an application configuration file
6
+ class ValidateConfigCommand
7
+ include Longleaf::EventStatusTracking
8
+
6
9
  def initialize(config_path)
7
10
  @config_path = config_path
8
11
  end
9
12
 
13
+ # Execute the validate command on the specified configuration yml file
10
14
  def execute
11
15
  begin
12
16
  app_config_manager = Longleaf::ApplicationConfigDeserializer.deserialize(@config_path)
@@ -0,0 +1,49 @@
1
+ require 'longleaf/services/application_config_deserializer'
2
+ require 'longleaf/models/file_record'
3
+ require 'longleaf/events/event_status_tracking'
4
+ require 'longleaf/errors'
5
+
6
+ module Longleaf
7
+ # Command for validating file metadata longleaf
8
+ class ValidateMetadataCommand
9
+ include Longleaf::EventStatusTracking
10
+
11
+ def initialize(app_manager)
12
+ @app_manager = app_manager
13
+ end
14
+
15
+ # Execute the validation command
16
+ # @param file_selector [FileSelector] selector for files to register
17
+ # @return [Integer] status code
18
+ def execute(file_selector:)
19
+ begin
20
+ # Perform metadata validation on each of the file paths provided
21
+ loop do
22
+ f_path = file_selector.next_path
23
+ break if f_path.nil?
24
+
25
+ storage_location = @app_manager.location_manager.get_location_by_path(f_path)
26
+
27
+ begin
28
+ file_rec = FileRecord.new(f_path, storage_location)
29
+ unless file_rec.metadata_present?
30
+ raise MetadataError.new("Cannot validate metadata for #{f_path}, file is not registered.")
31
+ end
32
+
33
+ MetadataDeserializer::deserialize(file_path: file_rec.metadata_path,
34
+ digest_algs: storage_location.metadata_digests)
35
+ record_success("Metadata for file passed validation: #{f_path}")
36
+ rescue LongleafError => err
37
+ record_failure(err.message)
38
+ end
39
+ end
40
+ rescue InvalidStoragePathError, StorageLocationUnavailableError => err
41
+ record_failure(err.message)
42
+ rescue => err
43
+ record_failure("Encountered error while validating metadata files", error: err)
44
+ end
45
+
46
+ return_status
47
+ end
48
+ end
49
+ end
@@ -1,15 +1,34 @@
1
1
  module Longleaf
2
+ # General Longleaf error
2
3
  class LongleafError < StandardError; end
3
4
 
5
+ # Invalid application configuration error
4
6
  class ConfigurationError < LongleafError; end
5
7
 
8
+ # Invalid storage path error
6
9
  class InvalidStoragePathError < LongleafError; end
7
10
 
11
+ # Metadata does not meet requirements error
8
12
  class MetadataError < LongleafError; end
9
13
 
14
+ # Unavailable storage location error
10
15
  class StorageLocationUnavailableError < LongleafError; end
11
16
 
17
+ # Error related to executing a preservation event
12
18
  class EventError < LongleafError; end
13
19
 
20
+ # Error while attempting to perform a registration event
14
21
  class RegistrationError < EventError; end
22
+
23
+ # Error while attempting to perform a deregistration event
24
+ class DeregistrationError < EventError; end
25
+
26
+ # Error while performing a preservation service
27
+ class PreservationServiceError < LongleafError; end
28
+
29
+ # Fixity check failure error
30
+ class ChecksumMismatchError < PreservationServiceError; end
31
+
32
+ # Error indicating an unknown or invalid digest algorithm was specified
33
+ class InvalidDigestAlgorithmError < LongleafError; end
15
34
  end
@@ -0,0 +1,55 @@
1
+ require 'longleaf/errors'
2
+ require 'longleaf/events/event_names'
3
+ require 'longleaf/events/event_status_tracking'
4
+ require 'longleaf/services/metadata_serializer'
5
+
6
+ module Longleaf
7
+ # Event to deregister a file from longleaf
8
+ class DeregisterEvent
9
+ include Longleaf::EventStatusTracking
10
+
11
+ # @param file_rec [FileRecord] file record
12
+ # @param app_manager [ApplicationConfigManager] the application configuration
13
+ # @param force [boolean] if true, then already deregistered files will be deregistered again
14
+ def initialize(file_rec:, app_manager:, force: false)
15
+ raise ArgumentError.new('Must provide a file_rec parameter') if file_rec.nil?
16
+ raise ArgumentError.new('Parameter file_rec must be a FileRecord') \
17
+ unless file_rec.is_a?(FileRecord)
18
+ raise ArgumentError.new('Must provide an ApplicationConfigManager') if app_manager.nil?
19
+ raise ArgumentError.new('Parameter app_manager must be an ApplicationConfigManager') \
20
+ unless app_manager.is_a?(ApplicationConfigManager)
21
+
22
+ @app_manager = app_manager
23
+ @file_rec = file_rec
24
+ @force = force
25
+ end
26
+
27
+ # Perform a deregistration event on the given file record
28
+ # @raise DeregistrationError if a file cannot be deregistered
29
+ def perform
30
+ begin
31
+ md_rec = @file_rec.metadata_record
32
+
33
+ # Only need to deregister a deregistered file if the force flag is provided
34
+ if md_rec.deregistered? && !@force
35
+ raise DeregistrationError.new("Unable to deregister '#{@file_rec.path}', it is already deregistered.")
36
+ end
37
+
38
+ md_rec.deregistered = Time.now.utc.iso8601
39
+
40
+ # persist the metadata out to file
41
+ MetadataSerializer::write(metadata: md_rec,
42
+ file_path: @file_rec.metadata_path,
43
+ digest_algs: @file_rec.storage_location.metadata_digests)
44
+
45
+ record_success(EventNames::DEREGISTER, @file_rec.path)
46
+ rescue DeregistrationError => err
47
+ record_failure(EventNames::DEREGISTER, @file_rec.path, err.message)
48
+ rescue InvalidStoragePathError => err
49
+ record_failure(EventNames::DEREGISTER, @file_rec.path, err.message)
50
+ end
51
+
52
+ return_status
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,9 @@
1
+ module Longleaf
2
+ # Constants for preservation event names
3
+ class EventNames
4
+ REGISTER = 'register'
5
+ PRESERVE = 'preserve'
6
+ CLEANUP = 'cleanup'
7
+ DEREGISTER = 'deregister'
8
+ end
9
+ end
@@ -0,0 +1,59 @@
1
+ require 'longleaf/logging'
2
+
3
+ module Longleaf
4
+ # Helper methods for tracking and recording the overall outcome of a preservation event.
5
+ module EventStatusTracking
6
+ include Longleaf::Logging
7
+
8
+ # Record a successful operation to the output and the overall status of this event.
9
+ # @param args [Array] arguments to pass to logger
10
+ def record_success(*args)
11
+ logger.success(*args)
12
+ track_success
13
+ end
14
+
15
+ # Update the status of this action with a success outcome.
16
+ def track_success
17
+ if @return_status.nil? || @return_status == 0
18
+ @return_status = 0
19
+ else
20
+ @return_status = 2
21
+ end
22
+ end
23
+
24
+ # Record a failed operation to the output and the overall status of this event.
25
+ # @param args [Array] arguments to pass to logger
26
+ def record_failure(*args)
27
+ logger.failure(*args)
28
+ track_failure
29
+ end
30
+
31
+ # Update the status of this action with a failure outcome.
32
+ def track_failure
33
+ if @return_status.nil? || @return_status == 1
34
+ @return_status = 1
35
+ else
36
+ @return_status = 2
37
+ end
38
+ end
39
+
40
+ # Update the status of this action with the provided outcome status number.
41
+ # @param status [Integer] outcome status
42
+ def track_status(status)
43
+ if status == 2
44
+ @return_status = 2
45
+ elsif status == 0
46
+ track_success
47
+ elsif status == 1
48
+ track_failure
49
+ end
50
+ end
51
+
52
+ # @return [Integer] the return status for this event, where 0 indicates success,
53
+ # 1 indicates failure, and 2 indicates partial failure
54
+ def return_status
55
+ @return_status = 0 if @return_status.nil?
56
+ @return_status
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,71 @@
1
+ require 'longleaf/services/service_manager'
2
+ require 'longleaf/events/event_names'
3
+ require 'longleaf/events/event_status_tracking'
4
+ require 'longleaf/logging'
5
+
6
+ module Longleaf
7
+ # Verify event for a single file
8
+ class PreserveEvent
9
+ include Longleaf::Logging
10
+ include Longleaf::EventStatusTracking
11
+
12
+ # @param file_rec [FileRecord] file record
13
+ # @param app_manager [ApplicationConfigManager] the application configuration
14
+ # @param force [boolean] if true, then services run regardless of whether they are flagged as needed
15
+ def initialize(file_rec:, app_manager:, force: false)
16
+ raise ArgumentError.new('Must provide a file_rec parameter') if file_rec.nil?
17
+ raise ArgumentError.new('Must provide an ApplicationConfigManager') if app_manager.nil?
18
+
19
+ @app_manager = app_manager
20
+ @file_rec = file_rec
21
+ @force = force
22
+ end
23
+
24
+ # Perform a preserve event on the given file, updating its metadata record if any services were executed.
25
+ def perform
26
+ storage_loc = @file_rec.storage_location
27
+ service_manager = @app_manager.service_manager
28
+ md_rec = @file_rec.metadata_record
29
+ f_path = @file_rec.path
30
+
31
+ logger.info("Performing preserve event on #{@file_rec.path}")
32
+
33
+ service_performed = false
34
+ begin
35
+ # get the list of services applicable to this location and event
36
+ service_manager.list_services(location: storage_loc.name, event: EventNames::PRESERVE).each do |service_name|
37
+ # Skip over this service if it does not need to be run, unless force flag active
38
+ unless @force || service_manager.service_needed?(service_name, md_rec)
39
+ logger.debug("Service #{service_name} not needed for file '#{@file_rec.path}', skipping")
40
+ next
41
+ end
42
+
43
+ begin
44
+ logger.info("Performing preserve service #{service_name} for #{@file_rec.path}")
45
+ # execute the service
46
+ service_manager.perform_service(service_name, @file_rec, EventNames::PRESERVE)
47
+
48
+ # record the outcome
49
+ @file_rec.metadata_record.update_service_as_performed(service_name)
50
+ service_performed = true
51
+ record_success(EventNames::PRESERVE, f_path, nil, service_name)
52
+ rescue PreservationServiceError => e
53
+ record_failure(EventNames::PRESERVE, f_path, e.message, service_name)
54
+ rescue StandardError => e
55
+ record_failure(EventNames::PRESERVE, f_path, nil, service_name, error: e)
56
+ return return_status
57
+ end
58
+ end
59
+ ensure
60
+ # persist the metadata out to file if any services were executed
61
+ if service_performed
62
+ MetadataSerializer::write(metadata: @file_rec.metadata_record,
63
+ file_path: @file_rec.metadata_path,
64
+ digest_algs: storage_loc.metadata_digests)
65
+ end
66
+ end
67
+
68
+ return_status
69
+ end
70
+ end
71
+ end
@@ -1,13 +1,15 @@
1
1
  require 'longleaf/errors'
2
+ require 'longleaf/events/event_names'
3
+ require 'longleaf/events/event_status_tracking'
2
4
  require 'longleaf/models/metadata_record'
3
5
  require 'longleaf/services/metadata_deserializer'
4
6
  require 'longleaf/services/metadata_serializer'
5
7
  require 'time'
6
8
 
7
- # Event to register a file with longleaf
8
9
  module Longleaf
10
+ # Event to register a file with longleaf
9
11
  class RegisterEvent
10
- EVENT_NAME = 'register'
12
+ include Longleaf::EventStatusTracking
11
13
 
12
14
  # @param file_rec [FileRecord] file record
13
15
  # @param app_manager [ApplicationConfigManager] the application configuration
@@ -27,31 +29,42 @@ module Longleaf
27
29
  end
28
30
 
29
31
  # Perform a registration event on the given file
30
- # @raises RegistrationError if a file cannot be registered
32
+ # @raise RegistrationError if a file cannot be registered
31
33
  def perform
32
- metadata_exists = File.file?(@file_rec.metadata_path)
33
- # If the file's metadata exists, only need to register it if the force flag is provided
34
- if metadata_exists && !@force
35
- raise RegistrationError.new("Unable to register '#{@file_rec.path}', it is already registered.")
36
- end
34
+ begin
35
+ # Only need to re-register file if the force flag is provided
36
+ if @file_rec.metadata_present? && !@force
37
+ raise RegistrationError.new("Unable to register '#{@file_rec.path}', it is already registered.")
38
+ end
37
39
 
38
- # create metadata record
39
- md_rec = MetadataRecord.new(registered: Time.now.utc.iso8601)
40
- @file_rec.metadata_record = md_rec
40
+ # create metadata record
41
+ md_rec = MetadataRecord.new(registered: Time.now.utc.iso8601)
42
+ @file_rec.metadata_record = md_rec
41
43
 
42
- # retain significant details from former record
43
- if metadata_exists
44
- retain_existing_properties
45
- end
44
+ # retain significant details from former record
45
+ if @file_rec.metadata_present?
46
+ retain_existing_properties
47
+ end
46
48
 
47
- populate_file_properties
49
+ populate_file_properties
48
50
 
49
- md_rec.checksums.merge!(@checksums) unless @checksums.nil?
51
+ md_rec.checksums.merge!(@checksums) unless @checksums.nil?
50
52
 
51
- populate_services
53
+ populate_services
52
54
 
53
- # persist the metadata out to file
54
- MetadataSerializer::write(metadata: md_rec, file_path: @file_rec.metadata_path)
55
+ # persist the metadata out to file
56
+ MetadataSerializer::write(metadata: md_rec,
57
+ file_path: @file_rec.metadata_path,
58
+ digest_algs: @file_rec.storage_location.metadata_digests)
59
+
60
+ record_success(EventNames::REGISTER, @file_rec.path)
61
+ rescue RegistrationError => err
62
+ record_failure(EventNames::REGISTER, @file_rec.path, err.message)
63
+ rescue InvalidStoragePathError => err
64
+ record_failure(EventNames::REGISTER, @file_rec.path, err.message)
65
+ end
66
+
67
+ return_status
55
68
  end
56
69
 
57
70
  private
@@ -67,20 +80,18 @@ module Longleaf
67
80
  md_rec = @file_rec.metadata_record
68
81
 
69
82
  service_manager = @app_manager.service_manager
70
- definitions = service_manager.list_service_definitions(location: @file_rec.storage_location.name)
83
+ service_names = service_manager.list_services(location: @file_rec.storage_location.name)
71
84
 
72
85
  # Add service section
73
- definitions.each do |serv_def|
74
- serv_name = serv_def.name
75
- md_rec.add_service(serv_name)
76
- end
86
+ service_names.each { |serv_name| md_rec.add_service(serv_name) }
77
87
  end
78
88
 
79
89
  # Copy a subset of properties from an existing metadata record to the new record
80
90
  def retain_existing_properties
81
91
  md_rec = @file_rec.metadata_record
82
92
 
83
- old_md = MetadataDeserializer.deserialize(file_path: @file_rec.metadata_path)
93
+ old_md = MetadataDeserializer.deserialize(file_path: @file_rec.metadata_path,
94
+ digest_algs: @file_rec.storage_location.metadata_digests)
84
95
  # Copy custom properties
85
96
  old_md.properties.each { |name, value| md_rec.properties[name] = value }
86
97
  # Copy stale-replicas flag per service