longleaf 0.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +94 -0
  3. data/.editorconfig +13 -0
  4. data/.gitignore +4 -1
  5. data/.rubocop.yml +44 -0
  6. data/.rubocop_todo.yml +834 -0
  7. data/.yardopts +1 -0
  8. data/Gemfile +16 -1
  9. data/README.md +98 -12
  10. data/Rakefile +6 -0
  11. data/bin/setup +16 -1
  12. data/docs/aboutlongleaf.md +28 -0
  13. data/docs/extra.css +32 -0
  14. data/docs/img/change-file.png +0 -0
  15. data/docs/img/ll-example-preserved.png +0 -0
  16. data/docs/index.md +19 -0
  17. data/docs/install.md +66 -0
  18. data/docs/ll-example/config-example-relative.yml +33 -0
  19. data/docs/ll-example/files-dir/LLexample-PDF.pdf +0 -0
  20. data/docs/ll-example/files-dir/LLexample-TOCHANGE.txt +15 -0
  21. data/docs/ll-example/files-dir/LLexample-tokeep.txt +10 -0
  22. data/docs/ll-example/metadata-dir/.gitkeep +0 -0
  23. data/docs/ll-example/replica-files/.gitkeep +0 -0
  24. data/docs/ll-example/replica-metadata/.gitkeep +0 -0
  25. data/docs/quickstart.md +270 -0
  26. data/docs/rdocs/Longleaf.html +135 -0
  27. data/docs/rdocs/Longleaf/AppFields.html +178 -0
  28. data/docs/rdocs/Longleaf/ApplicationConfigDeserializer.html +631 -0
  29. data/docs/rdocs/Longleaf/ApplicationConfigManager.html +610 -0
  30. data/docs/rdocs/Longleaf/ApplicationConfigValidator.html +238 -0
  31. data/docs/rdocs/Longleaf/CLI.html +909 -0
  32. data/docs/rdocs/Longleaf/ChecksumMismatchError.html +151 -0
  33. data/docs/rdocs/Longleaf/ConfigBuilder.html +1339 -0
  34. data/docs/rdocs/Longleaf/ConfigurationError.html +143 -0
  35. data/docs/rdocs/Longleaf/ConfigurationValidator.html +227 -0
  36. data/docs/rdocs/Longleaf/DeregisterCommand.html +420 -0
  37. data/docs/rdocs/Longleaf/DeregisterEvent.html +453 -0
  38. data/docs/rdocs/Longleaf/DeregistrationError.html +151 -0
  39. data/docs/rdocs/Longleaf/DigestHelper.html +419 -0
  40. data/docs/rdocs/Longleaf/EventError.html +147 -0
  41. data/docs/rdocs/Longleaf/EventNames.html +163 -0
  42. data/docs/rdocs/Longleaf/EventStatusTracking.html +656 -0
  43. data/docs/rdocs/Longleaf/FileCheckService.html +540 -0
  44. data/docs/rdocs/Longleaf/FileHelpers.html +520 -0
  45. data/docs/rdocs/Longleaf/FileRecord.html +716 -0
  46. data/docs/rdocs/Longleaf/FileSelector.html +901 -0
  47. data/docs/rdocs/Longleaf/FixityCheckService.html +691 -0
  48. data/docs/rdocs/Longleaf/IndexManager.html +1155 -0
  49. data/docs/rdocs/Longleaf/InvalidDigestAlgorithmError.html +143 -0
  50. data/docs/rdocs/Longleaf/InvalidStoragePathError.html +143 -0
  51. data/docs/rdocs/Longleaf/Logging.html +405 -0
  52. data/docs/rdocs/Longleaf/Logging/RedirectingLogger.html +1213 -0
  53. data/docs/rdocs/Longleaf/LongleafError.html +139 -0
  54. data/docs/rdocs/Longleaf/MDFields.html +193 -0
  55. data/docs/rdocs/Longleaf/MetadataBuilder.html +787 -0
  56. data/docs/rdocs/Longleaf/MetadataDeserializer.html +537 -0
  57. data/docs/rdocs/Longleaf/MetadataError.html +143 -0
  58. data/docs/rdocs/Longleaf/MetadataPersistenceManager.html +539 -0
  59. data/docs/rdocs/Longleaf/MetadataRecord.html +1411 -0
  60. data/docs/rdocs/Longleaf/MetadataSerializer.html +786 -0
  61. data/docs/rdocs/Longleaf/PreservationServiceError.html +147 -0
  62. data/docs/rdocs/Longleaf/PreserveCommand.html +410 -0
  63. data/docs/rdocs/Longleaf/PreserveEvent.html +491 -0
  64. data/docs/rdocs/Longleaf/RegisterCommand.html +428 -0
  65. data/docs/rdocs/Longleaf/RegisterEvent.html +628 -0
  66. data/docs/rdocs/Longleaf/RegisteredFileSelector.html +446 -0
  67. data/docs/rdocs/Longleaf/RegistrationError.html +151 -0
  68. data/docs/rdocs/Longleaf/ReindexCommand.html +576 -0
  69. data/docs/rdocs/Longleaf/RsyncReplicationService.html +1180 -0
  70. data/docs/rdocs/Longleaf/SequelIndexDriver.html +1978 -0
  71. data/docs/rdocs/Longleaf/ServiceCandidateFilesystemIterator.html +572 -0
  72. data/docs/rdocs/Longleaf/ServiceCandidateIndexIterator.html +532 -0
  73. data/docs/rdocs/Longleaf/ServiceCandidateLocator.html +333 -0
  74. data/docs/rdocs/Longleaf/ServiceClassCache.html +725 -0
  75. data/docs/rdocs/Longleaf/ServiceDateHelper.html +425 -0
  76. data/docs/rdocs/Longleaf/ServiceDefinition.html +683 -0
  77. data/docs/rdocs/Longleaf/ServiceDefinitionManager.html +371 -0
  78. data/docs/rdocs/Longleaf/ServiceDefinitionValidator.html +269 -0
  79. data/docs/rdocs/Longleaf/ServiceFields.html +173 -0
  80. data/docs/rdocs/Longleaf/ServiceManager.html +1229 -0
  81. data/docs/rdocs/Longleaf/ServiceMappingManager.html +410 -0
  82. data/docs/rdocs/Longleaf/ServiceMappingValidator.html +347 -0
  83. data/docs/rdocs/Longleaf/ServiceRecord.html +821 -0
  84. data/docs/rdocs/Longleaf/StorageLocation.html +985 -0
  85. data/docs/rdocs/Longleaf/StorageLocationManager.html +729 -0
  86. data/docs/rdocs/Longleaf/StorageLocationUnavailableError.html +143 -0
  87. data/docs/rdocs/Longleaf/StorageLocationValidator.html +373 -0
  88. data/docs/rdocs/Longleaf/StoragePathValidator.html +253 -0
  89. data/docs/rdocs/Longleaf/SystemConfigBuilder.html +441 -0
  90. data/docs/rdocs/Longleaf/SystemConfigFields.html +163 -0
  91. data/docs/rdocs/Longleaf/ValidateConfigCommand.html +451 -0
  92. data/docs/rdocs/Longleaf/ValidateMetadataCommand.html +408 -0
  93. data/docs/rdocs/_index.html +660 -0
  94. data/docs/rdocs/class_list.html +51 -0
  95. data/docs/rdocs/css/common.css +1 -0
  96. data/docs/rdocs/css/full_list.css +58 -0
  97. data/docs/rdocs/css/style.css +496 -0
  98. data/docs/rdocs/file.README.html +165 -0
  99. data/docs/rdocs/file_list.html +56 -0
  100. data/docs/rdocs/frames.html +17 -0
  101. data/docs/rdocs/index.html +165 -0
  102. data/docs/rdocs/js/app.js +303 -0
  103. data/docs/rdocs/js/full_list.js +216 -0
  104. data/docs/rdocs/js/jquery.js +4 -0
  105. data/docs/rdocs/method_list.html +2051 -0
  106. data/docs/rdocs/top-level-namespace.html +110 -0
  107. data/lib/longleaf/candidates/file_selector.rb +150 -0
  108. data/lib/longleaf/candidates/manifest_digest_provider.rb +17 -0
  109. data/lib/longleaf/candidates/physical_path_provider.rb +17 -0
  110. data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
  111. data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +93 -0
  112. data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
  113. data/lib/longleaf/candidates/service_candidate_locator.rb +23 -0
  114. data/lib/longleaf/candidates/single_digest_provider.rb +13 -0
  115. data/lib/longleaf/cli.rb +252 -46
  116. data/lib/longleaf/commands/deregister_command.rb +51 -0
  117. data/lib/longleaf/commands/preserve_command.rb +50 -0
  118. data/lib/longleaf/commands/register_command.rb +34 -43
  119. data/lib/longleaf/commands/reindex_command.rb +92 -0
  120. data/lib/longleaf/commands/validate_config_command.rb +33 -8
  121. data/lib/longleaf/commands/validate_metadata_command.rb +51 -0
  122. data/lib/longleaf/errors.rb +26 -7
  123. data/lib/longleaf/events/deregister_event.rb +53 -0
  124. data/lib/longleaf/events/event_names.rb +9 -0
  125. data/lib/longleaf/events/event_status_tracking.rb +59 -0
  126. data/lib/longleaf/events/preserve_event.rb +82 -0
  127. data/lib/longleaf/events/register_event.rb +59 -51
  128. data/lib/longleaf/helpers/case_insensitive_hash.rb +38 -0
  129. data/lib/longleaf/helpers/digest_helper.rb +56 -0
  130. data/lib/longleaf/helpers/s3_uri_helper.rb +86 -0
  131. data/lib/longleaf/helpers/selection_options_parser.rb +215 -0
  132. data/lib/longleaf/helpers/service_date_helper.rb +78 -0
  133. data/lib/longleaf/indexing/index_manager.rb +101 -0
  134. data/lib/longleaf/indexing/sequel_index_driver.rb +306 -0
  135. data/lib/longleaf/logging.rb +5 -4
  136. data/lib/longleaf/logging/redirecting_logger.rb +30 -25
  137. data/lib/longleaf/models/app_fields.rb +7 -2
  138. data/lib/longleaf/models/file_record.rb +31 -8
  139. data/lib/longleaf/models/filesystem_metadata_location.rb +56 -0
  140. data/lib/longleaf/models/filesystem_storage_location.rb +52 -0
  141. data/lib/longleaf/models/md_fields.rb +3 -1
  142. data/lib/longleaf/models/metadata_location.rb +47 -0
  143. data/lib/longleaf/models/metadata_record.rb +43 -16
  144. data/lib/longleaf/models/s3_storage_location.rb +138 -0
  145. data/lib/longleaf/models/service_definition.rb +7 -6
  146. data/lib/longleaf/models/service_fields.rb +7 -1
  147. data/lib/longleaf/models/service_record.rb +10 -6
  148. data/lib/longleaf/models/storage_location.rb +24 -19
  149. data/lib/longleaf/models/storage_types.rb +9 -0
  150. data/lib/longleaf/models/system_config_fields.rb +9 -0
  151. data/lib/longleaf/preservation_services/file_check_service.rb +59 -0
  152. data/lib/longleaf/preservation_services/fixity_check_service.rb +124 -0
  153. data/lib/longleaf/preservation_services/rsync_replication_service.rb +198 -0
  154. data/lib/longleaf/preservation_services/s3_replication_service.rb +131 -0
  155. data/lib/longleaf/services/application_config_deserializer.rb +81 -24
  156. data/lib/longleaf/services/application_config_manager.rb +20 -6
  157. data/lib/longleaf/services/application_config_validator.rb +19 -9
  158. data/lib/longleaf/services/configuration_validator.rb +67 -4
  159. data/lib/longleaf/services/filesystem_location_validator.rb +16 -0
  160. data/lib/longleaf/services/metadata_deserializer.rb +115 -42
  161. data/lib/longleaf/services/metadata_persistence_manager.rb +47 -0
  162. data/lib/longleaf/services/metadata_serializer.rb +156 -23
  163. data/lib/longleaf/services/metadata_validator.rb +76 -0
  164. data/lib/longleaf/services/s3_location_validator.rb +19 -0
  165. data/lib/longleaf/services/service_class_cache.rb +112 -0
  166. data/lib/longleaf/services/service_definition_manager.rb +10 -7
  167. data/lib/longleaf/services/service_definition_validator.rb +25 -18
  168. data/lib/longleaf/services/service_manager.rb +86 -11
  169. data/lib/longleaf/services/service_mapping_manager.rb +13 -12
  170. data/lib/longleaf/services/service_mapping_validator.rb +36 -26
  171. data/lib/longleaf/services/storage_location_manager.rb +76 -15
  172. data/lib/longleaf/services/storage_location_validator.rb +49 -35
  173. data/lib/longleaf/specs/config_builder.rb +47 -23
  174. data/lib/longleaf/specs/config_validator_helpers.rb +16 -0
  175. data/lib/longleaf/specs/custom_matchers.rb +9 -0
  176. data/lib/longleaf/specs/file_helpers.rb +61 -0
  177. data/lib/longleaf/specs/metadata_builder.rb +98 -0
  178. data/lib/longleaf/specs/system_config_builder.rb +27 -0
  179. data/lib/longleaf/version.rb +1 -1
  180. data/longleaf.gemspec +20 -7
  181. data/mkdocs.yml +21 -0
  182. metadata +308 -24
  183. data/.travis.yml +0 -4
  184. data/lib/longleaf/commands/abstract_command.rb +0 -37
  185. data/lib/longleaf/services/storage_path_validator.rb +0 -16
@@ -0,0 +1,53 @@
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(3)
39
+
40
+ # persist the metadata
41
+ @app_manager.md_manager.persist(@file_rec)
42
+
43
+ record_success(EventNames::DEREGISTER, @file_rec.path)
44
+ rescue DeregistrationError => err
45
+ record_failure(EventNames::DEREGISTER, @file_rec.path, err.message)
46
+ rescue InvalidStoragePathError => err
47
+ record_failure(EventNames::DEREGISTER, @file_rec.path, err.message)
48
+ end
49
+
50
+ return_status
51
+ end
52
+ end
53
+ 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,82 @@
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
+ phys_path = @file_rec.physical_path
31
+
32
+ logger.info("Performing preserve event on #{f_path}")
33
+
34
+ needs_persist = false
35
+ begin
36
+ if !File.exist?(phys_path)
37
+ # Need to persist metadata to avoid repeating processing of this file too soon.
38
+ needs_persist = true
39
+ record_failure(EventNames::PRESERVE, f_path, "File is registered but missing.")
40
+ return return_status
41
+ end
42
+
43
+ # get the list of services applicable to this location and event
44
+ service_manager.list_services(location: storage_loc.name, event: EventNames::PRESERVE).each do |service_name|
45
+ # Skip over this service if it does not need to be run, unless force flag active
46
+ unless @force || service_manager.service_needed?(service_name, md_rec)
47
+ logger.debug("Service #{service_name} not needed for file '#{@file_rec.path}', skipping")
48
+ next
49
+ end
50
+
51
+ begin
52
+ logger.info("Performing preserve service #{service_name} for #{@file_rec.path}")
53
+ needs_persist = true
54
+ # execute the service
55
+ service_manager.perform_service(service_name, @file_rec, EventNames::PRESERVE)
56
+
57
+ # record the outcome
58
+ @file_rec.metadata_record.update_service_as_performed(service_name)
59
+ record_success(EventNames::PRESERVE, f_path, nil, service_name)
60
+ rescue PreservationServiceError => e
61
+ @file_rec.metadata_record.update_service_as_failed(service_name)
62
+ record_failure(EventNames::PRESERVE, f_path, e.message, service_name)
63
+ rescue StorageLocationUnavailableError => e
64
+ raise e
65
+ rescue StandardError => e
66
+ @file_rec.metadata_record.update_service_as_failed(service_name)
67
+ record_failure(EventNames::PRESERVE, f_path, nil, service_name, error: e)
68
+ return return_status
69
+ end
70
+ end
71
+ ensure
72
+ # persist the metadata out to file if any services were executed
73
+ if needs_persist
74
+ # persist the metadata
75
+ @app_manager.md_manager.persist(@file_rec)
76
+ end
77
+ end
78
+
79
+ return_status
80
+ end
81
+ end
82
+ end
@@ -1,92 +1,100 @@
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'
11
-
12
+ include Longleaf::EventStatusTracking
13
+
12
14
  # @param file_rec [FileRecord] file record
13
15
  # @param app_manager [ApplicationConfigManager] the application configuration
14
16
  # @param force [boolean] if true, then already registered files will be re-registered
15
- def initialize(file_rec:, app_manager:, force: false, checksums: nil)
17
+ # @param digest_provider [#get_digests] object which provides digests for files being registered
18
+ def initialize(file_rec:, app_manager:, force: false, digest_provider: nil)
16
19
  raise ArgumentError.new('Must provide a file_rec parameter') if file_rec.nil?
17
20
  raise ArgumentError.new('Parameter file_rec must be a FileRecord') \
18
21
  unless file_rec.is_a?(FileRecord)
19
22
  raise ArgumentError.new('Must provide an ApplicationConfigManager') if app_manager.nil?
20
23
  raise ArgumentError.new('Parameter app_manager must be an ApplicationConfigManager') \
21
24
  unless app_manager.is_a?(ApplicationConfigManager)
22
-
25
+
23
26
  @app_manager = app_manager
24
27
  @file_rec = file_rec
25
28
  @force = force
26
- @checksums = checksums
29
+ @digest_provider = digest_provider
27
30
  end
28
-
31
+
29
32
  # Perform a registration event on the given file
30
- # @raises RegistrationError if a file cannot be registered
33
+ # @raise RegistrationError if a file cannot be registered
31
34
  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
37
-
38
- # create metadata record
39
- md_rec = MetadataRecord.new(registered: Time.now.utc.iso8601)
40
- @file_rec.metadata_record = md_rec
41
-
42
- # retain significant details from former record
43
- if metadata_exists
44
- retain_existing_properties
35
+ begin
36
+ # Only need to re-register file if the force flag is provided
37
+ if @file_rec.metadata_present? && !@force
38
+ raise RegistrationError.new("Unable to register '#{@file_rec.path}', it is already registered.")
39
+ end
40
+
41
+ # create metadata record
42
+ md_rec = MetadataRecord.new(registered: Time.now.utc.iso8601(3))
43
+ @file_rec.metadata_record = md_rec
44
+
45
+ # retain significant details from former record
46
+ if @file_rec.metadata_present?
47
+ retain_existing_properties
48
+ end
49
+
50
+ populate_file_properties
51
+
52
+ if !@digest_provider.nil?
53
+ checksums = @digest_provider.get_digests(@file_rec.path)
54
+ md_rec.checksums.merge!(checksums) unless checksums.nil?
55
+ end
56
+
57
+ # persist the metadata
58
+ @app_manager.md_manager.persist(@file_rec)
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)
45
65
  end
46
-
47
- populate_file_properties
48
-
49
- md_rec.checksums.merge!(@checksums) unless @checksums.nil?
50
-
51
- populate_services
52
-
53
- # persist the metadata out to file
54
- MetadataSerializer::write(metadata: md_rec, file_path: @file_rec.metadata_path)
66
+
67
+ return_status
55
68
  end
56
-
69
+
57
70
  private
58
71
  def populate_file_properties
59
72
  md_rec = @file_rec.metadata_record
60
-
73
+ physical_path = @file_rec.physical_path
74
+
61
75
  # Set file properties
62
- md_rec.last_modified = File.mtime(@file_rec.path).utc.iso8601
63
- md_rec.file_size = File.size(@file_rec.path)
64
- end
65
-
66
- def populate_services
67
- md_rec = @file_rec.metadata_record
68
-
69
- service_manager = @app_manager.service_manager
70
- definitions = service_manager.list_service_definitions(location: @file_rec.storage_location.name)
71
-
72
- # Add service section
73
- definitions.each do |serv_def|
74
- serv_name = serv_def.name
75
- md_rec.add_service(serv_name)
76
+ md_rec.last_modified = File.mtime(physical_path).utc.iso8601(3)
77
+ md_rec.file_size = File.size(physical_path)
78
+
79
+ if physical_path != @file_rec.path
80
+ md_rec.physical_path = physical_path
81
+ else
82
+ md_rec.physical_path = nil
76
83
  end
77
84
  end
78
-
85
+
79
86
  # Copy a subset of properties from an existing metadata record to the new record
80
87
  def retain_existing_properties
81
88
  md_rec = @file_rec.metadata_record
82
-
83
- old_md = MetadataDeserializer.deserialize(file_path: @file_rec.metadata_path)
89
+
90
+ old_md = MetadataDeserializer.deserialize(file_path: @file_rec.metadata_path,
91
+ digest_algs: @file_rec.storage_location.metadata_location.digests)
84
92
  # Copy custom properties
85
93
  old_md.properties.each { |name, value| md_rec.properties[name] = value }
86
94
  # Copy stale-replicas flag per service
87
95
  old_md.list_services.each do |serv_name|
88
96
  serv_rec = old_md.service(serv_name)
89
-
97
+
90
98
  stale_replicas = serv_rec.stale_replicas
91
99
  if stale_replicas
92
100
  new_service = md_rec.service(serv_name)
@@ -95,4 +103,4 @@ module Longleaf
95
103
  end
96
104
  end
97
105
  end
98
- end
106
+ end
@@ -0,0 +1,38 @@
1
+ module Longleaf
2
+ # Hash subclass which provides case insensitive keys, where keys are always downcased.
3
+ class CaseInsensitiveHash < Hash
4
+ def [](key)
5
+ super _insensitive(key)
6
+ end
7
+
8
+ def []=(key, value)
9
+ super _insensitive(key), value
10
+ end
11
+
12
+ def delete(key)
13
+ super _insensitive(key)
14
+ end
15
+
16
+ def has_key?(key)
17
+ super _insensitive(key)
18
+ end
19
+
20
+ def merge(other_hash)
21
+ super other_hash.map {|k, v| [_insensitive(k), v] }.to_h
22
+ end
23
+
24
+ def merge!(other_hash)
25
+ super other_hash.map {|k, v| [_insensitive(k), v] }.to_h
26
+ end
27
+
28
+ # Cause this hash to serialize as a regular hash to avoid deserialization failures
29
+ def encode_with coder
30
+ coder.represent_map nil, self
31
+ end
32
+
33
+ protected
34
+ def _insensitive(key)
35
+ key.respond_to?(:downcase) ? key.downcase : key
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,56 @@
1
+ require 'longleaf/errors'
2
+ require 'digest'
3
+
4
+ module Longleaf
5
+ # Helper methods for generating digests
6
+ class DigestHelper
7
+ KNOWN_DIGESTS ||= ['md5', 'sha1', 'sha2', 'sha256', 'sha384', 'sha512', 'rmd160']
8
+
9
+ # @param algs Either a string containing one or an array containing zero or more digest
10
+ # algorithm names.
11
+ # @raise [InvalidDigestAlgorithmError] thrown if any of the digest algorithms listed are not
12
+ # known to the system.
13
+ def self.validate_algorithms(algs)
14
+ return if algs.nil?
15
+ if algs.is_a?(String)
16
+ unless self.is_known_algorithm?(algs)
17
+ raise InvalidDigestAlgorithmError.new("Unknown digest algorithm #{algs}")
18
+ end
19
+ else
20
+ unknown = algs.select { |alg| !KNOWN_DIGESTS.include?(alg) }
21
+ unless unknown.empty?
22
+ raise InvalidDigestAlgorithmError.new("Unknown digest algorithm(s): #{unknown}")
23
+ end
24
+ end
25
+ end
26
+
27
+ # @param alg [String] identifier of digest algorithm
28
+ # @return [Boolean] true if the digest is a valid known algorithm
29
+ def self.is_known_algorithm?(alg)
30
+ KNOWN_DIGESTS.include?(alg)
31
+ end
32
+
33
+ # Get a Digest class for the specified algorithm
34
+ # @param alg [String] name of the digest algorithm
35
+ # @return [Digest] A digest class for the requested algorithm
36
+ # @raise [InvalidDigestAlgorithmError] if an unknown digest algorithm is requested
37
+ def self.start_digest(alg)
38
+ case alg
39
+ when 'md5'
40
+ return Digest::MD5.new
41
+ when 'sha1'
42
+ return Digest::SHA1.new
43
+ when 'sha2', 'sha256'
44
+ return Digest::SHA2.new
45
+ when 'sha384'
46
+ return Digest::SHA2.new(384)
47
+ when 'sha512'
48
+ return Digest::SHA2.new(512)
49
+ when 'rmd160'
50
+ return Digest::RMD160.new
51
+ else
52
+ raise InvalidDigestAlgorithmError.new("Cannot produce digest for unknown algorithm '#{alg}'.")
53
+ end
54
+ end
55
+ end
56
+ end