longleaf 0.2.0.pre.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +84 -0
  3. data/.gitignore +4 -2
  4. data/.rubocop.yml +42 -2
  5. data/.rubocop_todo.yml +390 -311
  6. data/.yardopts +1 -0
  7. data/Gemfile +16 -1
  8. data/README.md +67 -13
  9. data/Rakefile +6 -0
  10. data/bin/setup +16 -1
  11. data/docs/aboutlongleaf.md +28 -0
  12. data/docs/extra.css +32 -0
  13. data/docs/img/change-file.png +0 -0
  14. data/docs/img/ll-example-preserved.png +0 -0
  15. data/docs/index.md +19 -0
  16. data/docs/install.md +66 -0
  17. data/docs/ll-example/config-example-relative.yml +33 -0
  18. data/docs/ll-example/files-dir/LLexample-PDF.pdf +0 -0
  19. data/docs/ll-example/files-dir/LLexample-TOCHANGE.txt +15 -0
  20. data/docs/ll-example/files-dir/LLexample-tokeep.txt +10 -0
  21. data/docs/ll-example/metadata-dir/.gitkeep +0 -0
  22. data/docs/ll-example/replica-files/.gitkeep +0 -0
  23. data/docs/ll-example/replica-metadata/.gitkeep +0 -0
  24. data/docs/quickstart.md +270 -0
  25. data/docs/rdocs/Longleaf.html +135 -0
  26. data/docs/rdocs/Longleaf/AppFields.html +178 -0
  27. data/docs/rdocs/Longleaf/ApplicationConfigDeserializer.html +631 -0
  28. data/docs/rdocs/Longleaf/ApplicationConfigManager.html +610 -0
  29. data/docs/rdocs/Longleaf/ApplicationConfigValidator.html +238 -0
  30. data/docs/rdocs/Longleaf/CLI.html +909 -0
  31. data/docs/rdocs/Longleaf/ChecksumMismatchError.html +151 -0
  32. data/docs/rdocs/Longleaf/ConfigBuilder.html +1339 -0
  33. data/docs/rdocs/Longleaf/ConfigurationError.html +143 -0
  34. data/docs/rdocs/Longleaf/ConfigurationValidator.html +227 -0
  35. data/docs/rdocs/Longleaf/DeregisterCommand.html +420 -0
  36. data/docs/rdocs/Longleaf/DeregisterEvent.html +453 -0
  37. data/docs/rdocs/Longleaf/DeregistrationError.html +151 -0
  38. data/docs/rdocs/Longleaf/DigestHelper.html +419 -0
  39. data/docs/rdocs/Longleaf/EventError.html +147 -0
  40. data/docs/rdocs/Longleaf/EventNames.html +163 -0
  41. data/docs/rdocs/Longleaf/EventStatusTracking.html +656 -0
  42. data/docs/rdocs/Longleaf/FileCheckService.html +540 -0
  43. data/docs/rdocs/Longleaf/FileHelpers.html +520 -0
  44. data/docs/rdocs/Longleaf/FileRecord.html +716 -0
  45. data/docs/rdocs/Longleaf/FileSelector.html +901 -0
  46. data/docs/rdocs/Longleaf/FixityCheckService.html +691 -0
  47. data/docs/rdocs/Longleaf/IndexManager.html +1155 -0
  48. data/docs/rdocs/Longleaf/InvalidDigestAlgorithmError.html +143 -0
  49. data/docs/rdocs/Longleaf/InvalidStoragePathError.html +143 -0
  50. data/docs/rdocs/Longleaf/Logging.html +405 -0
  51. data/docs/rdocs/Longleaf/Logging/RedirectingLogger.html +1213 -0
  52. data/docs/rdocs/Longleaf/LongleafError.html +139 -0
  53. data/docs/rdocs/Longleaf/MDFields.html +193 -0
  54. data/docs/rdocs/Longleaf/MetadataBuilder.html +787 -0
  55. data/docs/rdocs/Longleaf/MetadataDeserializer.html +537 -0
  56. data/docs/rdocs/Longleaf/MetadataError.html +143 -0
  57. data/docs/rdocs/Longleaf/MetadataPersistenceManager.html +539 -0
  58. data/docs/rdocs/Longleaf/MetadataRecord.html +1411 -0
  59. data/docs/rdocs/Longleaf/MetadataSerializer.html +786 -0
  60. data/docs/rdocs/Longleaf/PreservationServiceError.html +147 -0
  61. data/docs/rdocs/Longleaf/PreserveCommand.html +410 -0
  62. data/docs/rdocs/Longleaf/PreserveEvent.html +491 -0
  63. data/docs/rdocs/Longleaf/RegisterCommand.html +428 -0
  64. data/docs/rdocs/Longleaf/RegisterEvent.html +628 -0
  65. data/docs/rdocs/Longleaf/RegisteredFileSelector.html +446 -0
  66. data/docs/rdocs/Longleaf/RegistrationError.html +151 -0
  67. data/docs/rdocs/Longleaf/ReindexCommand.html +576 -0
  68. data/docs/rdocs/Longleaf/RsyncReplicationService.html +1180 -0
  69. data/docs/rdocs/Longleaf/SequelIndexDriver.html +1978 -0
  70. data/docs/rdocs/Longleaf/ServiceCandidateFilesystemIterator.html +572 -0
  71. data/docs/rdocs/Longleaf/ServiceCandidateIndexIterator.html +532 -0
  72. data/docs/rdocs/Longleaf/ServiceCandidateLocator.html +333 -0
  73. data/docs/rdocs/Longleaf/ServiceClassCache.html +725 -0
  74. data/docs/rdocs/Longleaf/ServiceDateHelper.html +425 -0
  75. data/docs/rdocs/Longleaf/ServiceDefinition.html +683 -0
  76. data/docs/rdocs/Longleaf/ServiceDefinitionManager.html +371 -0
  77. data/docs/rdocs/Longleaf/ServiceDefinitionValidator.html +269 -0
  78. data/docs/rdocs/Longleaf/ServiceFields.html +173 -0
  79. data/docs/rdocs/Longleaf/ServiceManager.html +1229 -0
  80. data/docs/rdocs/Longleaf/ServiceMappingManager.html +410 -0
  81. data/docs/rdocs/Longleaf/ServiceMappingValidator.html +347 -0
  82. data/docs/rdocs/Longleaf/ServiceRecord.html +821 -0
  83. data/docs/rdocs/Longleaf/StorageLocation.html +985 -0
  84. data/docs/rdocs/Longleaf/StorageLocationManager.html +729 -0
  85. data/docs/rdocs/Longleaf/StorageLocationUnavailableError.html +143 -0
  86. data/docs/rdocs/Longleaf/StorageLocationValidator.html +373 -0
  87. data/docs/rdocs/Longleaf/StoragePathValidator.html +253 -0
  88. data/docs/rdocs/Longleaf/SystemConfigBuilder.html +441 -0
  89. data/docs/rdocs/Longleaf/SystemConfigFields.html +163 -0
  90. data/docs/rdocs/Longleaf/ValidateConfigCommand.html +451 -0
  91. data/docs/rdocs/Longleaf/ValidateMetadataCommand.html +408 -0
  92. data/docs/rdocs/_index.html +660 -0
  93. data/docs/rdocs/class_list.html +51 -0
  94. data/docs/rdocs/css/common.css +1 -0
  95. data/docs/rdocs/css/full_list.css +58 -0
  96. data/docs/rdocs/css/style.css +496 -0
  97. data/docs/rdocs/file.README.html +165 -0
  98. data/docs/rdocs/file_list.html +56 -0
  99. data/docs/rdocs/frames.html +17 -0
  100. data/docs/rdocs/index.html +165 -0
  101. data/docs/rdocs/js/app.js +303 -0
  102. data/docs/rdocs/js/full_list.js +216 -0
  103. data/docs/rdocs/js/jquery.js +4 -0
  104. data/docs/rdocs/method_list.html +2051 -0
  105. data/docs/rdocs/top-level-namespace.html +110 -0
  106. data/lib/longleaf/candidates/file_selector.rb +47 -15
  107. data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
  108. data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +29 -35
  109. data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
  110. data/lib/longleaf/candidates/service_candidate_locator.rb +9 -4
  111. data/lib/longleaf/cli.rb +162 -80
  112. data/lib/longleaf/commands/deregister_command.rb +12 -11
  113. data/lib/longleaf/commands/preserve_command.rb +13 -8
  114. data/lib/longleaf/commands/register_command.rb +9 -6
  115. data/lib/longleaf/commands/reindex_command.rb +92 -0
  116. data/lib/longleaf/commands/validate_config_command.rb +27 -6
  117. data/lib/longleaf/commands/validate_metadata_command.rb +11 -9
  118. data/lib/longleaf/errors.rb +12 -12
  119. data/lib/longleaf/events/deregister_event.rb +13 -15
  120. data/lib/longleaf/events/event_status_tracking.rb +7 -7
  121. data/lib/longleaf/events/preserve_event.rb +24 -14
  122. data/lib/longleaf/events/register_event.rb +21 -35
  123. data/lib/longleaf/helpers/digest_helper.rb +4 -4
  124. data/lib/longleaf/helpers/service_date_helper.rb +5 -6
  125. data/lib/longleaf/indexing/index_manager.rb +101 -0
  126. data/lib/longleaf/indexing/sequel_index_driver.rb +324 -0
  127. data/lib/longleaf/logging.rb +4 -4
  128. data/lib/longleaf/logging/redirecting_logger.rb +20 -20
  129. data/lib/longleaf/models/app_fields.rb +2 -1
  130. data/lib/longleaf/models/file_record.rb +10 -6
  131. data/lib/longleaf/models/md_fields.rb +1 -1
  132. data/lib/longleaf/models/metadata_record.rb +22 -12
  133. data/lib/longleaf/models/service_definition.rb +3 -3
  134. data/lib/longleaf/models/service_fields.rb +1 -1
  135. data/lib/longleaf/models/service_record.rb +6 -5
  136. data/lib/longleaf/models/storage_location.rb +26 -7
  137. data/lib/longleaf/models/system_config_fields.rb +9 -0
  138. data/lib/longleaf/preservation_services/file_check_service.rb +58 -0
  139. data/lib/longleaf/preservation_services/fixity_check_service.rb +16 -14
  140. data/lib/longleaf/preservation_services/rsync_replication_service.rb +32 -31
  141. data/lib/longleaf/services/application_config_deserializer.rb +55 -18
  142. data/lib/longleaf/services/application_config_manager.rb +16 -4
  143. data/lib/longleaf/services/application_config_validator.rb +1 -2
  144. data/lib/longleaf/services/configuration_validator.rb +6 -4
  145. data/lib/longleaf/services/metadata_deserializer.rb +40 -38
  146. data/lib/longleaf/services/metadata_persistence_manager.rb +46 -0
  147. data/lib/longleaf/services/metadata_serializer.rb +23 -22
  148. data/lib/longleaf/services/service_class_cache.rb +15 -15
  149. data/lib/longleaf/services/service_definition_manager.rb +5 -6
  150. data/lib/longleaf/services/service_definition_validator.rb +5 -6
  151. data/lib/longleaf/services/service_manager.rb +37 -17
  152. data/lib/longleaf/services/service_mapping_manager.rb +9 -9
  153. data/lib/longleaf/services/service_mapping_validator.rb +9 -10
  154. data/lib/longleaf/services/storage_location_manager.rb +22 -8
  155. data/lib/longleaf/services/storage_location_validator.rb +11 -8
  156. data/lib/longleaf/services/storage_path_validator.rb +1 -1
  157. data/lib/longleaf/specs/config_builder.rb +30 -17
  158. data/lib/longleaf/specs/custom_matchers.rb +1 -1
  159. data/lib/longleaf/specs/file_helpers.rb +15 -14
  160. data/lib/longleaf/specs/metadata_builder.rb +91 -0
  161. data/lib/longleaf/specs/system_config_builder.rb +27 -0
  162. data/lib/longleaf/version.rb +1 -1
  163. data/longleaf.gemspec +17 -7
  164. data/mkdocs.yml +20 -0
  165. metadata +233 -22
@@ -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.to_s}") unless error.nil? || error.backtrace.nil?
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
@@ -4,7 +4,8 @@ module Longleaf
4
4
  LOCATIONS = 'locations'
5
5
  SERVICES = 'services'
6
6
  SERVICE_MAPPINGS = 'service_mappings'
7
-
7
+ SYSTEM = 'system'
8
+
8
9
  LOCATION_PATH = 'path'
9
10
  METADATA_PATH = 'metadata_path'
10
11
  METADATA_DIGESTS = 'metadata_digests'
@@ -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
@@ -3,7 +3,7 @@ module Longleaf
3
3
  class MDFields
4
4
  DATA = 'data'
5
5
  SERVICES = 'services'
6
-
6
+
7
7
  REGISTERED_TIMESTAMP = 'registered'
8
8
  DEREGISTERED_TIMESTAMP = 'deregistered'
9
9
 
@@ -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: Hash.new, services: Hash.new, deregistered: nil, registered: nil, checksums: Hash.new,
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
@@ -5,7 +5,7 @@ module Longleaf
5
5
  WORK_CLASS = 'work_class'
6
6
  FREQUENCY = 'frequency'
7
7
  DELAY = 'delay'
8
-
8
+
9
9
  REPLICATE_TO = 'to'
10
10
  DIGEST_ALGORITHMS = 'algorithms'
11
11
  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) + MetadataSerializer::metadata_suffix
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,9 @@
1
+ module Longleaf
2
+ # System configuration field names
3
+ class SystemConfigFields
4
+ MD_INDEX = 'index'
5
+ MD_INDEX_ADAPTER = 'adapter'
6
+ MD_INDEX_CONNECTION = 'connection'
7
+ MD_INDEX_PAGE_SIZE = 'page_size'
8
+ end
9
+ 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