longleaf 0.2.0.pre.1 → 0.3.0
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 +84 -0
- data/.gitignore +4 -2
- data/.rubocop.yml +42 -2
- data/.rubocop_todo.yml +390 -311
- data/.yardopts +1 -0
- data/Gemfile +16 -1
- data/README.md +67 -13
- 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 +47 -15
- data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
- data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +29 -35
- data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
- data/lib/longleaf/candidates/service_candidate_locator.rb +9 -4
- data/lib/longleaf/cli.rb +162 -80
- data/lib/longleaf/commands/deregister_command.rb +12 -11
- data/lib/longleaf/commands/preserve_command.rb +13 -8
- data/lib/longleaf/commands/register_command.rb +9 -6
- data/lib/longleaf/commands/reindex_command.rb +92 -0
- data/lib/longleaf/commands/validate_config_command.rb +27 -6
- data/lib/longleaf/commands/validate_metadata_command.rb +11 -9
- data/lib/longleaf/errors.rb +12 -12
- data/lib/longleaf/events/deregister_event.rb +13 -15
- data/lib/longleaf/events/event_status_tracking.rb +7 -7
- data/lib/longleaf/events/preserve_event.rb +24 -14
- data/lib/longleaf/events/register_event.rb +21 -35
- data/lib/longleaf/helpers/digest_helper.rb +4 -4
- data/lib/longleaf/helpers/service_date_helper.rb +5 -6
- data/lib/longleaf/indexing/index_manager.rb +101 -0
- data/lib/longleaf/indexing/sequel_index_driver.rb +324 -0
- data/lib/longleaf/logging.rb +4 -4
- data/lib/longleaf/logging/redirecting_logger.rb +20 -20
- data/lib/longleaf/models/app_fields.rb +2 -1
- data/lib/longleaf/models/file_record.rb +10 -6
- data/lib/longleaf/models/md_fields.rb +1 -1
- data/lib/longleaf/models/metadata_record.rb +22 -12
- data/lib/longleaf/models/service_definition.rb +3 -3
- data/lib/longleaf/models/service_fields.rb +1 -1
- data/lib/longleaf/models/service_record.rb +6 -5
- data/lib/longleaf/models/storage_location.rb +26 -7
- data/lib/longleaf/models/system_config_fields.rb +9 -0
- data/lib/longleaf/preservation_services/file_check_service.rb +58 -0
- data/lib/longleaf/preservation_services/fixity_check_service.rb +16 -14
- data/lib/longleaf/preservation_services/rsync_replication_service.rb +32 -31
- data/lib/longleaf/services/application_config_deserializer.rb +55 -18
- data/lib/longleaf/services/application_config_manager.rb +16 -4
- data/lib/longleaf/services/application_config_validator.rb +1 -2
- data/lib/longleaf/services/configuration_validator.rb +6 -4
- data/lib/longleaf/services/metadata_deserializer.rb +40 -38
- data/lib/longleaf/services/metadata_persistence_manager.rb +46 -0
- data/lib/longleaf/services/metadata_serializer.rb +23 -22
- data/lib/longleaf/services/service_class_cache.rb +15 -15
- data/lib/longleaf/services/service_definition_manager.rb +5 -6
- data/lib/longleaf/services/service_definition_validator.rb +5 -6
- data/lib/longleaf/services/service_manager.rb +37 -17
- data/lib/longleaf/services/service_mapping_manager.rb +9 -9
- data/lib/longleaf/services/service_mapping_validator.rb +9 -10
- data/lib/longleaf/services/storage_location_manager.rb +22 -8
- data/lib/longleaf/services/storage_location_validator.rb +11 -8
- data/lib/longleaf/services/storage_path_validator.rb +1 -1
- data/lib/longleaf/specs/config_builder.rb +30 -17
- data/lib/longleaf/specs/custom_matchers.rb +1 -1
- data/lib/longleaf/specs/file_helpers.rb +15 -14
- data/lib/longleaf/specs/metadata_builder.rb +91 -0
- data/lib/longleaf/specs/system_config_builder.rb +27 -0
- data/lib/longleaf/version.rb +1 -1
- data/longleaf.gemspec +17 -7
- data/mkdocs.yml +20 -0
- metadata +233 -22
data/lib/longleaf/logging.rb
CHANGED
|
@@ -7,16 +7,16 @@ module Longleaf
|
|
|
7
7
|
def logger
|
|
8
8
|
Logging.logger
|
|
9
9
|
end
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
# Get the main logger for longleaf
|
|
12
12
|
def self.logger
|
|
13
13
|
@logger ||= RedirectingLogger.new
|
|
14
14
|
end
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
def initialize_logger(failure_only, log_level, log_format, datetime_format)
|
|
17
17
|
Logging.initialize_logger(failure_only, log_level, log_format, datetime_format)
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
def self.initialize_logger(failure_only, log_level, log_format, datetime_format)
|
|
21
21
|
@logger = RedirectingLogger.new(failure_only: failure_only,
|
|
22
22
|
log_level: log_level,
|
|
@@ -24,4 +24,4 @@ module Longleaf
|
|
|
24
24
|
datetime_format: datetime_format)
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
|
-
end
|
|
27
|
+
end
|
|
@@ -25,12 +25,12 @@ module Longleaf
|
|
|
25
25
|
@stderr_log.formatter = proc do |severity, datetime, progname, msg|
|
|
26
26
|
# Make sure the format ends with a newline
|
|
27
27
|
@log_format = @log_format + "\n" unless @log_format.end_with?("\n")
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
formatted_date = @stderr_log.datetime_format.nil? ? datetime : datetime.strftime(datetime_format)
|
|
30
30
|
@log_format % { :severity => severity, :datetime => formatted_date, :progname => progname, :msg => msg }
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
@stdout_log = Logger.new($stdout)
|
|
35
35
|
@stdout_log.formatter = proc do |severity, datetime, progname, msg|
|
|
36
36
|
"#{msg}\n"
|
|
@@ -41,34 +41,34 @@ module Longleaf
|
|
|
41
41
|
@stdout_log.level = 'info'
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
def debug(progname = nil, &block)
|
|
46
46
|
@stderr_log.debug(progname, &block)
|
|
47
47
|
end
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
def info(progname = nil, &block)
|
|
50
50
|
@stderr_log.info(progname, &block)
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
def warn(progname = nil, &block)
|
|
54
54
|
@stderr_log.warn(progname, &block)
|
|
55
55
|
end
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
def error(progname = nil, &block)
|
|
58
58
|
@stderr_log.error(progname, &block)
|
|
59
59
|
end
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
def fatal(progname = nil, &block)
|
|
62
62
|
@stderr_log.fatal(progname, &block)
|
|
63
63
|
end
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
def unknown(progname = nil, &block)
|
|
66
66
|
@stderr_log.unknown(progname, &block)
|
|
67
67
|
end
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
# Logs a success message to STDOUT, as well as STDERR at info level.
|
|
70
|
-
#
|
|
71
|
-
# @param [String] eventOrMessage name of the preservation event which succeeded,
|
|
70
|
+
#
|
|
71
|
+
# @param [String] eventOrMessage name of the preservation event which succeeded,
|
|
72
72
|
# or the message to output if it is the only parameter. Required.
|
|
73
73
|
# @param file_name [String] file name which is the subject of this message.
|
|
74
74
|
# @param message [String] descriptive message to accompany this output
|
|
@@ -76,10 +76,10 @@ module Longleaf
|
|
|
76
76
|
def success(eventOrMessage, file_name = nil, message = nil, service = nil)
|
|
77
77
|
outcome('SUCCESS', eventOrMessage, file_name, message, service)
|
|
78
78
|
end
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
# Logs a failure message to STDOUT, as well as STDERR at info level.
|
|
81
81
|
# If an error was provided, it is logged to STDERR at error level.
|
|
82
|
-
# @param eventOrMessage [String] name of the preservation event which failed,
|
|
82
|
+
# @param eventOrMessage [String] name of the preservation event which failed,
|
|
83
83
|
# or the message to output if it is the only parameter.
|
|
84
84
|
# @param file_name [String] file name which is the subject of this message.
|
|
85
85
|
# @param message [String] descriptive message to accompany this output
|
|
@@ -88,17 +88,17 @@ module Longleaf
|
|
|
88
88
|
def failure(eventOrMessage, file_name = nil, message = nil, service = nil, error: nil)
|
|
89
89
|
text = outcome_text('FAILURE', eventOrMessage, file_name, message, service, error)
|
|
90
90
|
@stdout_log.warn(text)
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
@stderr_log.info(text)
|
|
93
93
|
@stderr_log.error("#{error.message}") unless error.nil?
|
|
94
|
-
@stderr_log.error("#{error.backtrace
|
|
94
|
+
@stderr_log.error("#{error.backtrace}") unless error.nil? || error.backtrace.nil?
|
|
95
95
|
end
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
# Logs an outcome message to STDOUT, as well as STDERR at info level.
|
|
98
98
|
# If file_name and message are nil, eventOrMessage will be used as the message.
|
|
99
99
|
#
|
|
100
100
|
# @param outcome [String] The status of the outcome. Required.
|
|
101
|
-
# @param eventOrMessage [String] name of the preservation event which was successful,
|
|
101
|
+
# @param eventOrMessage [String] name of the preservation event which was successful,
|
|
102
102
|
# or the message to output if it is the only parameter. Required.
|
|
103
103
|
# @param file_name [String] file name which is the subject of this message.
|
|
104
104
|
# @param message [String] descriptive message to accompany this output
|
|
@@ -109,13 +109,13 @@ module Longleaf
|
|
|
109
109
|
@stdout_log.info(text)
|
|
110
110
|
@stderr_log.info(text)
|
|
111
111
|
end
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
private
|
|
114
114
|
def outcome_text(outcome, eventOrMessage, file_name = nil, message = nil, service = nil, error = nil)
|
|
115
115
|
message_only = file_name.nil? && message.nil? && error.nil?
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
text = "#{outcome}"
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
if message_only
|
|
120
120
|
text << ": #{eventOrMessage}"
|
|
121
121
|
else
|
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
module Longleaf
|
|
2
2
|
# Record for an individual file and its associated information
|
|
3
3
|
class FileRecord
|
|
4
|
-
|
|
5
4
|
attr_accessor :metadata_record
|
|
6
5
|
attr_reader :storage_location
|
|
7
6
|
attr_reader :path
|
|
8
|
-
|
|
7
|
+
|
|
9
8
|
# @param file_path [String] path to the file
|
|
10
9
|
# @param storage_location [StorageLocation] storage location containing the file
|
|
11
10
|
def initialize(file_path, storage_location, metadata_record = nil)
|
|
12
11
|
raise ArgumentError.new("FileRecord requires a path") if file_path.nil?
|
|
13
12
|
raise ArgumentError.new("FileRecord requires a storage_location") if storage_location.nil?
|
|
14
|
-
|
|
13
|
+
|
|
15
14
|
@path = file_path
|
|
16
15
|
@storage_location = storage_location
|
|
17
16
|
@metadata_record = metadata_record
|
|
18
17
|
end
|
|
19
|
-
|
|
18
|
+
|
|
20
19
|
# @return [String] path for the metadata file for this file
|
|
21
20
|
def metadata_path
|
|
22
21
|
@metadata_path = @storage_location.get_metadata_path_for(path) if @metadata_path.nil?
|
|
23
22
|
@metadata_path
|
|
24
23
|
end
|
|
25
|
-
|
|
24
|
+
|
|
26
25
|
def metadata_present?
|
|
27
26
|
File.exist?(metadata_path)
|
|
28
27
|
end
|
|
28
|
+
|
|
29
|
+
def ==(other_obj)
|
|
30
|
+
return false unless other_obj.is_a?(FileRecord)
|
|
31
|
+
path == other_obj.path
|
|
32
|
+
end
|
|
29
33
|
end
|
|
30
|
-
end
|
|
34
|
+
end
|
|
@@ -9,7 +9,7 @@ module Longleaf
|
|
|
9
9
|
attr_reader :checksums
|
|
10
10
|
attr_reader :properties
|
|
11
11
|
attr_accessor :file_size, :last_modified
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
# @param properties [Hash] initial data properties for this record
|
|
14
14
|
# @param services [Hash] initial service property tree
|
|
15
15
|
# @param deregistered [String] deregistered timestamp
|
|
@@ -17,22 +17,22 @@ module Longleaf
|
|
|
17
17
|
# @param checksums [Hash] hash of checksum values
|
|
18
18
|
# @param file_size [Integer] size of file in bytes
|
|
19
19
|
# @param last_modified [String] iso8601 representation of the last modified date of file
|
|
20
|
-
def initialize(properties:
|
|
20
|
+
def initialize(properties: nil, services: nil, deregistered: nil, registered: nil, checksums: nil,
|
|
21
21
|
file_size: nil, last_modified: nil)
|
|
22
|
-
@properties = properties
|
|
22
|
+
@properties = properties || Hash.new
|
|
23
23
|
@registered = registered
|
|
24
24
|
@deregistered = deregistered
|
|
25
|
-
@checksums = checksums
|
|
26
|
-
@services = services
|
|
25
|
+
@checksums = checksums || Hash.new
|
|
26
|
+
@services = services || Hash.new
|
|
27
27
|
@file_size = file_size
|
|
28
28
|
@last_modified = last_modified
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# @return [Boolean] true if the record is deregistered
|
|
32
32
|
def deregistered?
|
|
33
33
|
!@deregistered.nil?
|
|
34
34
|
end
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
# Adds a service to this record
|
|
37
37
|
#
|
|
38
38
|
# @param name [String] identifier for the service being added
|
|
@@ -41,10 +41,10 @@ module Longleaf
|
|
|
41
41
|
def add_service(name, service = ServiceRecord.new)
|
|
42
42
|
raise ArgumentError.new("Value must be a ServiceRecord object when adding a service") unless service.class == Longleaf::ServiceRecord
|
|
43
43
|
raise IndexError.new("Service with name '#{name}' already exists") if @services.key?(name)
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
@services[name] = service
|
|
46
46
|
end
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
# Updates details of service record as if the service had been executed.
|
|
49
49
|
# @param service_name [String] name of the service run
|
|
50
50
|
# @return [ServiceRecord] the service record updated
|
|
@@ -54,16 +54,26 @@ module Longleaf
|
|
|
54
54
|
service_rec.timestamp = ServiceDateHelper.formatted_timestamp
|
|
55
55
|
service_rec
|
|
56
56
|
end
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
# Updates details of service record as if the service had encountered a
|
|
59
|
+
# failure during execution.
|
|
60
|
+
# @param service_name [String] name of the service run
|
|
61
|
+
# @return [ServiceRecord] the service record updated
|
|
62
|
+
def update_service_as_failed(service_name)
|
|
63
|
+
service_rec = service(service_name) || add_service(service_name)
|
|
64
|
+
service_rec.failure_timestamp = ServiceDateHelper.formatted_timestamp
|
|
65
|
+
service_rec
|
|
66
|
+
end
|
|
67
|
+
|
|
58
68
|
# @param name [String] name identifier of the service to retrieve
|
|
59
69
|
# @return [ServiceRecord] the ServiceRecord for the service identified by name, or nil
|
|
60
70
|
def service(name)
|
|
61
71
|
@services[name]
|
|
62
72
|
end
|
|
63
|
-
|
|
73
|
+
|
|
64
74
|
# @return [Array<String>] a list of name identifiers for services registered to this record
|
|
65
75
|
def list_services
|
|
66
76
|
@services.keys
|
|
67
77
|
end
|
|
68
78
|
end
|
|
69
|
-
end
|
|
79
|
+
end
|
|
@@ -7,10 +7,10 @@ module Longleaf
|
|
|
7
7
|
attr_reader :work_script, :work_class
|
|
8
8
|
attr_reader :frequency, :delay
|
|
9
9
|
attr_reader :properties
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
def initialize(name:, work_script:, work_class: nil, frequency: nil, delay: nil, properties: Hash.new)
|
|
12
12
|
raise ArgumentError.new("Parameters name and work_script are required") unless name && work_script
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
@properties = properties
|
|
15
15
|
@name = name
|
|
16
16
|
@work_script = work_script
|
|
@@ -19,4 +19,4 @@ module Longleaf
|
|
|
19
19
|
@delay = delay
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
|
-
end
|
|
22
|
+
end
|
|
@@ -3,28 +3,29 @@ module Longleaf
|
|
|
3
3
|
class ServiceRecord
|
|
4
4
|
attr_reader :properties
|
|
5
5
|
attr_accessor :stale_replicas, :timestamp, :run_needed
|
|
6
|
-
|
|
6
|
+
attr_accessor :failure_timestamp
|
|
7
|
+
|
|
7
8
|
# @param properties [Hash] initial properties for this service record
|
|
8
9
|
# @param stale_replicas [Boolean] whether there are any stale replicas from this service
|
|
9
10
|
# @param timestamp [String] timestamp when this service last ran or was initialized
|
|
10
11
|
# @param run_needed [Boolean] flag indicating that this service should be run at the next available opportunity
|
|
11
12
|
def initialize(properties: Hash.new, stale_replicas: false, timestamp: nil, run_needed: false)
|
|
12
13
|
raise ArgumentError.new("Service properties must be a hash") if properties.class != Hash
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
@properties = properties
|
|
15
16
|
@timestamp = timestamp
|
|
16
17
|
@stale_replicas = stale_replicas
|
|
17
18
|
@run_needed = run_needed
|
|
18
19
|
end
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
# @return the value of a service property identified by key
|
|
21
22
|
def [](key)
|
|
22
23
|
@properties[key]
|
|
23
24
|
end
|
|
24
|
-
|
|
25
|
+
|
|
25
26
|
# set the value of a service property identified by key
|
|
26
27
|
def []=(key, value)
|
|
27
28
|
@properties[key] = value
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
|
-
end
|
|
31
|
+
end
|
|
@@ -7,20 +7,20 @@ module Longleaf
|
|
|
7
7
|
attr_reader :path
|
|
8
8
|
attr_reader :metadata_path
|
|
9
9
|
attr_reader :metadata_digests
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
# @param name [String] the name of this storage location
|
|
12
12
|
# @param path [String] absolute path where the storage location is located
|
|
13
13
|
# @param metadata_path [String] absolute path where the metadata for files in this location will be stored.
|
|
14
14
|
# @param metadata_digests list of digest algorithms to use for metadata file digests in this location.
|
|
15
15
|
def initialize(name:, path:, metadata_path:, metadata_digests: [])
|
|
16
16
|
raise ArgumentError.new("Parameters name, path and metadata_path are required") unless name && path && metadata_path
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
@path = path
|
|
19
19
|
@path += '/' unless @path.end_with?('/')
|
|
20
20
|
@name = name
|
|
21
21
|
@metadata_path = metadata_path
|
|
22
22
|
@metadata_path += '/' unless @metadata_path.end_with?('/')
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
if metadata_digests.nil?
|
|
25
25
|
@metadata_digests = []
|
|
26
26
|
elsif metadata_digests.is_a?(String)
|
|
@@ -30,7 +30,7 @@ module Longleaf
|
|
|
30
30
|
end
|
|
31
31
|
DigestHelper::validate_algorithms(@metadata_digests)
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
# Get the path for the metadata file for the given file path located in this storage location.
|
|
35
35
|
# @param file_path [String] path of the file
|
|
36
36
|
# @raise [ArgumentError] if the file_path is not provided or is not in this storage location.
|
|
@@ -39,9 +39,28 @@ module Longleaf
|
|
|
39
39
|
raise ArgumentError.new("Provided file path is not contained by storage location #{@name}: #{file_path}") \
|
|
40
40
|
unless file_path.start_with?(@path)
|
|
41
41
|
|
|
42
|
-
file_path.sub(/^#{@path}/, metadata_path)
|
|
42
|
+
md_path = file_path.sub(/^#{@path}/, @metadata_path)
|
|
43
|
+
# If the file_path is to a file, then add metadata suffix.
|
|
44
|
+
if md_path.end_with?('/')
|
|
45
|
+
md_path
|
|
46
|
+
else
|
|
47
|
+
md_path + MetadataSerializer::metadata_suffix
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Get the metadata path for the provided file path located in this storage location.
|
|
52
|
+
# @param md_path [String] metadata file path
|
|
53
|
+
# @raise [ArgumentError] if the md_path is not in this storage location
|
|
54
|
+
# @return [String] the path for the file associated with this metadata
|
|
55
|
+
def get_path_from_metadata_path(md_path)
|
|
56
|
+
raise ArgumentError.new("A file_path parameter is required") if md_path.nil? || md_path.empty?
|
|
57
|
+
raise ArgumentError.new("Provided metadata path is not contained by storage location #{@name}: #{md_path}") \
|
|
58
|
+
unless md_path&.start_with?(@metadata_path)
|
|
59
|
+
|
|
60
|
+
file_path = md_path.sub(/^#{@metadata_path}/, @path)
|
|
61
|
+
file_path.sub(/#{MetadataSerializer::metadata_suffix}$/, '')
|
|
43
62
|
end
|
|
44
|
-
|
|
63
|
+
|
|
45
64
|
# Checks that the path and metadata path defined in this location are available
|
|
46
65
|
# @raise [StorageLocationUnavailableError] if the storage location is not available
|
|
47
66
|
def available?
|
|
@@ -51,4 +70,4 @@ module Longleaf
|
|
|
51
70
|
unless Dir.exist?(@metadata_path)
|
|
52
71
|
end
|
|
53
72
|
end
|
|
54
|
-
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'longleaf/events/event_names'
|
|
2
|
+
require 'longleaf/logging'
|
|
3
|
+
|
|
4
|
+
module Longleaf
|
|
5
|
+
# Preservation service which validates a file using current filesystem information compared against the
|
|
6
|
+
# last registered details for that file. Checks using file name, size and last modified timestamp.
|
|
7
|
+
class FileCheckService
|
|
8
|
+
include Longleaf::Logging
|
|
9
|
+
|
|
10
|
+
# Initialize a FileCheckService from the given service definition
|
|
11
|
+
#
|
|
12
|
+
# @param service_def [ServiceDefinition] the configuration for this service
|
|
13
|
+
# @param app_manager [ApplicationConfigManager] manager for configured storage locations
|
|
14
|
+
def initialize(service_def, app_manager)
|
|
15
|
+
@service_def = service_def
|
|
16
|
+
@app_manager = app_manager
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Perform file information check.
|
|
20
|
+
#
|
|
21
|
+
# @param file_rec [FileRecord] record representing the file to perform the service on.
|
|
22
|
+
# @param event [String] name of the event this service is being invoked by.
|
|
23
|
+
# @raise [PreservationServiceError] if the file system information does not match the stored details
|
|
24
|
+
def perform(file_rec, event)
|
|
25
|
+
file_path = file_rec.path
|
|
26
|
+
md_rec = file_rec.metadata_record
|
|
27
|
+
|
|
28
|
+
logger.debug("Performing file information check of #{file_path}")
|
|
29
|
+
|
|
30
|
+
if !File.exist?(file_path)
|
|
31
|
+
raise PreservationServiceError.new("File does not exist: #{file_path}")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
file_size = File.size(file_rec.path)
|
|
35
|
+
if file_size != md_rec.file_size
|
|
36
|
+
raise PreservationServiceError.new("File size for #{file_path} does not match the expected value: registered = #{md_rec.file_size} bytes, actual = #{file_size} bytes")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
last_modified = File.mtime(file_rec.path).utc.iso8601(3)
|
|
40
|
+
if last_modified != md_rec.last_modified
|
|
41
|
+
raise PreservationServiceError.new("Last modified timestamp for #{file_path} does not match the expected value: registered = #{md_rec.last_modified}, actual = #{last_modified}")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Determine if this service is applicable for the provided event, given the configured service definition
|
|
46
|
+
#
|
|
47
|
+
# @param event [String] name of the event
|
|
48
|
+
# @return [Boolean] returns true if this service is applicable for the provided event
|
|
49
|
+
def is_applicable?(event)
|
|
50
|
+
case event
|
|
51
|
+
when EventNames::PRESERVE
|
|
52
|
+
true
|
|
53
|
+
else
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|