longleaf 0.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +94 -0
- data/.editorconfig +13 -0
- data/.gitignore +4 -1
- data/.rubocop.yml +44 -0
- data/.rubocop_todo.yml +834 -0
- data/.yardopts +1 -0
- data/Gemfile +16 -1
- data/README.md +98 -12
- data/Rakefile +6 -0
- data/bin/setup +16 -1
- data/docs/aboutlongleaf.md +28 -0
- data/docs/extra.css +32 -0
- data/docs/img/change-file.png +0 -0
- data/docs/img/ll-example-preserved.png +0 -0
- data/docs/index.md +19 -0
- data/docs/install.md +66 -0
- data/docs/ll-example/config-example-relative.yml +33 -0
- data/docs/ll-example/files-dir/LLexample-PDF.pdf +0 -0
- data/docs/ll-example/files-dir/LLexample-TOCHANGE.txt +15 -0
- data/docs/ll-example/files-dir/LLexample-tokeep.txt +10 -0
- data/docs/ll-example/metadata-dir/.gitkeep +0 -0
- data/docs/ll-example/replica-files/.gitkeep +0 -0
- data/docs/ll-example/replica-metadata/.gitkeep +0 -0
- data/docs/quickstart.md +270 -0
- data/docs/rdocs/Longleaf.html +135 -0
- data/docs/rdocs/Longleaf/AppFields.html +178 -0
- data/docs/rdocs/Longleaf/ApplicationConfigDeserializer.html +631 -0
- data/docs/rdocs/Longleaf/ApplicationConfigManager.html +610 -0
- data/docs/rdocs/Longleaf/ApplicationConfigValidator.html +238 -0
- data/docs/rdocs/Longleaf/CLI.html +909 -0
- data/docs/rdocs/Longleaf/ChecksumMismatchError.html +151 -0
- data/docs/rdocs/Longleaf/ConfigBuilder.html +1339 -0
- data/docs/rdocs/Longleaf/ConfigurationError.html +143 -0
- data/docs/rdocs/Longleaf/ConfigurationValidator.html +227 -0
- data/docs/rdocs/Longleaf/DeregisterCommand.html +420 -0
- data/docs/rdocs/Longleaf/DeregisterEvent.html +453 -0
- data/docs/rdocs/Longleaf/DeregistrationError.html +151 -0
- data/docs/rdocs/Longleaf/DigestHelper.html +419 -0
- data/docs/rdocs/Longleaf/EventError.html +147 -0
- data/docs/rdocs/Longleaf/EventNames.html +163 -0
- data/docs/rdocs/Longleaf/EventStatusTracking.html +656 -0
- data/docs/rdocs/Longleaf/FileCheckService.html +540 -0
- data/docs/rdocs/Longleaf/FileHelpers.html +520 -0
- data/docs/rdocs/Longleaf/FileRecord.html +716 -0
- data/docs/rdocs/Longleaf/FileSelector.html +901 -0
- data/docs/rdocs/Longleaf/FixityCheckService.html +691 -0
- data/docs/rdocs/Longleaf/IndexManager.html +1155 -0
- data/docs/rdocs/Longleaf/InvalidDigestAlgorithmError.html +143 -0
- data/docs/rdocs/Longleaf/InvalidStoragePathError.html +143 -0
- data/docs/rdocs/Longleaf/Logging.html +405 -0
- data/docs/rdocs/Longleaf/Logging/RedirectingLogger.html +1213 -0
- data/docs/rdocs/Longleaf/LongleafError.html +139 -0
- data/docs/rdocs/Longleaf/MDFields.html +193 -0
- data/docs/rdocs/Longleaf/MetadataBuilder.html +787 -0
- data/docs/rdocs/Longleaf/MetadataDeserializer.html +537 -0
- data/docs/rdocs/Longleaf/MetadataError.html +143 -0
- data/docs/rdocs/Longleaf/MetadataPersistenceManager.html +539 -0
- data/docs/rdocs/Longleaf/MetadataRecord.html +1411 -0
- data/docs/rdocs/Longleaf/MetadataSerializer.html +786 -0
- data/docs/rdocs/Longleaf/PreservationServiceError.html +147 -0
- data/docs/rdocs/Longleaf/PreserveCommand.html +410 -0
- data/docs/rdocs/Longleaf/PreserveEvent.html +491 -0
- data/docs/rdocs/Longleaf/RegisterCommand.html +428 -0
- data/docs/rdocs/Longleaf/RegisterEvent.html +628 -0
- data/docs/rdocs/Longleaf/RegisteredFileSelector.html +446 -0
- data/docs/rdocs/Longleaf/RegistrationError.html +151 -0
- data/docs/rdocs/Longleaf/ReindexCommand.html +576 -0
- data/docs/rdocs/Longleaf/RsyncReplicationService.html +1180 -0
- data/docs/rdocs/Longleaf/SequelIndexDriver.html +1978 -0
- data/docs/rdocs/Longleaf/ServiceCandidateFilesystemIterator.html +572 -0
- data/docs/rdocs/Longleaf/ServiceCandidateIndexIterator.html +532 -0
- data/docs/rdocs/Longleaf/ServiceCandidateLocator.html +333 -0
- data/docs/rdocs/Longleaf/ServiceClassCache.html +725 -0
- data/docs/rdocs/Longleaf/ServiceDateHelper.html +425 -0
- data/docs/rdocs/Longleaf/ServiceDefinition.html +683 -0
- data/docs/rdocs/Longleaf/ServiceDefinitionManager.html +371 -0
- data/docs/rdocs/Longleaf/ServiceDefinitionValidator.html +269 -0
- data/docs/rdocs/Longleaf/ServiceFields.html +173 -0
- data/docs/rdocs/Longleaf/ServiceManager.html +1229 -0
- data/docs/rdocs/Longleaf/ServiceMappingManager.html +410 -0
- data/docs/rdocs/Longleaf/ServiceMappingValidator.html +347 -0
- data/docs/rdocs/Longleaf/ServiceRecord.html +821 -0
- data/docs/rdocs/Longleaf/StorageLocation.html +985 -0
- data/docs/rdocs/Longleaf/StorageLocationManager.html +729 -0
- data/docs/rdocs/Longleaf/StorageLocationUnavailableError.html +143 -0
- data/docs/rdocs/Longleaf/StorageLocationValidator.html +373 -0
- data/docs/rdocs/Longleaf/StoragePathValidator.html +253 -0
- data/docs/rdocs/Longleaf/SystemConfigBuilder.html +441 -0
- data/docs/rdocs/Longleaf/SystemConfigFields.html +163 -0
- data/docs/rdocs/Longleaf/ValidateConfigCommand.html +451 -0
- data/docs/rdocs/Longleaf/ValidateMetadataCommand.html +408 -0
- data/docs/rdocs/_index.html +660 -0
- data/docs/rdocs/class_list.html +51 -0
- data/docs/rdocs/css/common.css +1 -0
- data/docs/rdocs/css/full_list.css +58 -0
- data/docs/rdocs/css/style.css +496 -0
- data/docs/rdocs/file.README.html +165 -0
- data/docs/rdocs/file_list.html +56 -0
- data/docs/rdocs/frames.html +17 -0
- data/docs/rdocs/index.html +165 -0
- data/docs/rdocs/js/app.js +303 -0
- data/docs/rdocs/js/full_list.js +216 -0
- data/docs/rdocs/js/jquery.js +4 -0
- data/docs/rdocs/method_list.html +2051 -0
- data/docs/rdocs/top-level-namespace.html +110 -0
- data/lib/longleaf/candidates/file_selector.rb +150 -0
- data/lib/longleaf/candidates/manifest_digest_provider.rb +17 -0
- data/lib/longleaf/candidates/physical_path_provider.rb +17 -0
- data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
- data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +93 -0
- data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
- data/lib/longleaf/candidates/service_candidate_locator.rb +23 -0
- data/lib/longleaf/candidates/single_digest_provider.rb +13 -0
- data/lib/longleaf/cli.rb +252 -46
- data/lib/longleaf/commands/deregister_command.rb +51 -0
- data/lib/longleaf/commands/preserve_command.rb +50 -0
- data/lib/longleaf/commands/register_command.rb +34 -43
- data/lib/longleaf/commands/reindex_command.rb +92 -0
- data/lib/longleaf/commands/validate_config_command.rb +33 -8
- data/lib/longleaf/commands/validate_metadata_command.rb +51 -0
- data/lib/longleaf/errors.rb +26 -7
- data/lib/longleaf/events/deregister_event.rb +53 -0
- data/lib/longleaf/events/event_names.rb +9 -0
- data/lib/longleaf/events/event_status_tracking.rb +59 -0
- data/lib/longleaf/events/preserve_event.rb +82 -0
- data/lib/longleaf/events/register_event.rb +59 -51
- data/lib/longleaf/helpers/case_insensitive_hash.rb +38 -0
- data/lib/longleaf/helpers/digest_helper.rb +56 -0
- data/lib/longleaf/helpers/s3_uri_helper.rb +86 -0
- data/lib/longleaf/helpers/selection_options_parser.rb +215 -0
- data/lib/longleaf/helpers/service_date_helper.rb +78 -0
- data/lib/longleaf/indexing/index_manager.rb +101 -0
- data/lib/longleaf/indexing/sequel_index_driver.rb +306 -0
- data/lib/longleaf/logging.rb +5 -4
- data/lib/longleaf/logging/redirecting_logger.rb +30 -25
- data/lib/longleaf/models/app_fields.rb +7 -2
- data/lib/longleaf/models/file_record.rb +31 -8
- data/lib/longleaf/models/filesystem_metadata_location.rb +56 -0
- data/lib/longleaf/models/filesystem_storage_location.rb +52 -0
- data/lib/longleaf/models/md_fields.rb +3 -1
- data/lib/longleaf/models/metadata_location.rb +47 -0
- data/lib/longleaf/models/metadata_record.rb +43 -16
- data/lib/longleaf/models/s3_storage_location.rb +138 -0
- data/lib/longleaf/models/service_definition.rb +7 -6
- data/lib/longleaf/models/service_fields.rb +7 -1
- data/lib/longleaf/models/service_record.rb +10 -6
- data/lib/longleaf/models/storage_location.rb +24 -19
- data/lib/longleaf/models/storage_types.rb +9 -0
- data/lib/longleaf/models/system_config_fields.rb +9 -0
- data/lib/longleaf/preservation_services/file_check_service.rb +59 -0
- data/lib/longleaf/preservation_services/fixity_check_service.rb +124 -0
- data/lib/longleaf/preservation_services/rsync_replication_service.rb +198 -0
- data/lib/longleaf/preservation_services/s3_replication_service.rb +131 -0
- data/lib/longleaf/services/application_config_deserializer.rb +81 -24
- data/lib/longleaf/services/application_config_manager.rb +20 -6
- data/lib/longleaf/services/application_config_validator.rb +19 -9
- data/lib/longleaf/services/configuration_validator.rb +67 -4
- data/lib/longleaf/services/filesystem_location_validator.rb +16 -0
- data/lib/longleaf/services/metadata_deserializer.rb +115 -42
- data/lib/longleaf/services/metadata_persistence_manager.rb +47 -0
- data/lib/longleaf/services/metadata_serializer.rb +156 -23
- data/lib/longleaf/services/metadata_validator.rb +76 -0
- data/lib/longleaf/services/s3_location_validator.rb +19 -0
- data/lib/longleaf/services/service_class_cache.rb +112 -0
- data/lib/longleaf/services/service_definition_manager.rb +10 -7
- data/lib/longleaf/services/service_definition_validator.rb +25 -18
- data/lib/longleaf/services/service_manager.rb +86 -11
- data/lib/longleaf/services/service_mapping_manager.rb +13 -12
- data/lib/longleaf/services/service_mapping_validator.rb +36 -26
- data/lib/longleaf/services/storage_location_manager.rb +76 -15
- data/lib/longleaf/services/storage_location_validator.rb +49 -35
- data/lib/longleaf/specs/config_builder.rb +47 -23
- data/lib/longleaf/specs/config_validator_helpers.rb +16 -0
- data/lib/longleaf/specs/custom_matchers.rb +9 -0
- data/lib/longleaf/specs/file_helpers.rb +61 -0
- data/lib/longleaf/specs/metadata_builder.rb +98 -0
- data/lib/longleaf/specs/system_config_builder.rb +27 -0
- data/lib/longleaf/version.rb +1 -1
- data/longleaf.gemspec +20 -7
- data/mkdocs.yml +21 -0
- metadata +308 -24
- data/.travis.yml +0 -4
- data/lib/longleaf/commands/abstract_command.rb +0 -37
- 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,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
|
-
|
|
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
|
-
|
|
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
|
-
@
|
|
29
|
+
@digest_provider = digest_provider
|
|
27
30
|
end
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
# Perform a registration event on the given file
|
|
30
|
-
# @
|
|
33
|
+
# @raise RegistrationError if a file cannot be registered
|
|
31
34
|
def perform
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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(
|
|
63
|
-
md_rec.file_size = File.size(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|