longleaf 0.1.0.pre.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) 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 +139 -0
  108. data/lib/longleaf/candidates/manifest_digest_provider.rb +17 -0
  109. data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
  110. data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +93 -0
  111. data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
  112. data/lib/longleaf/candidates/service_candidate_locator.rb +23 -0
  113. data/lib/longleaf/candidates/single_digest_provider.rb +13 -0
  114. data/lib/longleaf/cli.rb +237 -46
  115. data/lib/longleaf/commands/deregister_command.rb +51 -0
  116. data/lib/longleaf/commands/preserve_command.rb +50 -0
  117. data/lib/longleaf/commands/register_command.rb +32 -43
  118. data/lib/longleaf/commands/reindex_command.rb +92 -0
  119. data/lib/longleaf/commands/validate_config_command.rb +33 -8
  120. data/lib/longleaf/commands/validate_metadata_command.rb +51 -0
  121. data/lib/longleaf/errors.rb +26 -7
  122. data/lib/longleaf/events/deregister_event.rb +53 -0
  123. data/lib/longleaf/events/event_names.rb +9 -0
  124. data/lib/longleaf/events/event_status_tracking.rb +59 -0
  125. data/lib/longleaf/events/preserve_event.rb +81 -0
  126. data/lib/longleaf/events/register_event.rb +52 -51
  127. data/lib/longleaf/helpers/case_insensitive_hash.rb +38 -0
  128. data/lib/longleaf/helpers/digest_helper.rb +56 -0
  129. data/lib/longleaf/helpers/s3_uri_helper.rb +86 -0
  130. data/lib/longleaf/helpers/selection_options_parser.rb +189 -0
  131. data/lib/longleaf/helpers/service_date_helper.rb +78 -0
  132. data/lib/longleaf/indexing/index_manager.rb +101 -0
  133. data/lib/longleaf/indexing/sequel_index_driver.rb +306 -0
  134. data/lib/longleaf/logging.rb +5 -4
  135. data/lib/longleaf/logging/redirecting_logger.rb +26 -25
  136. data/lib/longleaf/models/app_fields.rb +7 -2
  137. data/lib/longleaf/models/file_record.rb +17 -8
  138. data/lib/longleaf/models/filesystem_metadata_location.rb +56 -0
  139. data/lib/longleaf/models/filesystem_storage_location.rb +52 -0
  140. data/lib/longleaf/models/md_fields.rb +2 -1
  141. data/lib/longleaf/models/metadata_location.rb +47 -0
  142. data/lib/longleaf/models/metadata_record.rb +39 -15
  143. data/lib/longleaf/models/s3_storage_location.rb +133 -0
  144. data/lib/longleaf/models/service_definition.rb +7 -6
  145. data/lib/longleaf/models/service_fields.rb +7 -1
  146. data/lib/longleaf/models/service_record.rb +10 -6
  147. data/lib/longleaf/models/storage_location.rb +24 -19
  148. data/lib/longleaf/models/storage_types.rb +9 -0
  149. data/lib/longleaf/models/system_config_fields.rb +9 -0
  150. data/lib/longleaf/preservation_services/file_check_service.rb +58 -0
  151. data/lib/longleaf/preservation_services/fixity_check_service.rb +123 -0
  152. data/lib/longleaf/preservation_services/rsync_replication_service.rb +182 -0
  153. data/lib/longleaf/preservation_services/s3_replication_service.rb +143 -0
  154. data/lib/longleaf/services/application_config_deserializer.rb +81 -24
  155. data/lib/longleaf/services/application_config_manager.rb +20 -6
  156. data/lib/longleaf/services/application_config_validator.rb +19 -9
  157. data/lib/longleaf/services/configuration_validator.rb +67 -4
  158. data/lib/longleaf/services/filesystem_location_validator.rb +16 -0
  159. data/lib/longleaf/services/metadata_deserializer.rb +113 -42
  160. data/lib/longleaf/services/metadata_persistence_manager.rb +47 -0
  161. data/lib/longleaf/services/metadata_serializer.rb +138 -25
  162. data/lib/longleaf/services/metadata_validator.rb +76 -0
  163. data/lib/longleaf/services/s3_location_validator.rb +19 -0
  164. data/lib/longleaf/services/service_class_cache.rb +112 -0
  165. data/lib/longleaf/services/service_definition_manager.rb +10 -7
  166. data/lib/longleaf/services/service_definition_validator.rb +25 -18
  167. data/lib/longleaf/services/service_manager.rb +86 -11
  168. data/lib/longleaf/services/service_mapping_manager.rb +13 -12
  169. data/lib/longleaf/services/service_mapping_validator.rb +36 -26
  170. data/lib/longleaf/services/storage_location_manager.rb +76 -15
  171. data/lib/longleaf/services/storage_location_validator.rb +49 -35
  172. data/lib/longleaf/specs/config_builder.rb +47 -23
  173. data/lib/longleaf/specs/config_validator_helpers.rb +16 -0
  174. data/lib/longleaf/specs/custom_matchers.rb +9 -0
  175. data/lib/longleaf/specs/file_helpers.rb +61 -0
  176. data/lib/longleaf/specs/metadata_builder.rb +92 -0
  177. data/lib/longleaf/specs/system_config_builder.rb +27 -0
  178. data/lib/longleaf/version.rb +1 -1
  179. data/longleaf.gemspec +20 -7
  180. data/mkdocs.yml +21 -0
  181. metadata +306 -23
  182. data/.travis.yml +0 -4
  183. data/lib/longleaf/commands/abstract_command.rb +0 -37
  184. data/lib/longleaf/services/storage_path_validator.rb +0 -16
@@ -1,21 +1,22 @@
1
1
  require 'longleaf/logging/redirecting_logger'
2
2
 
3
3
  module Longleaf
4
+ # Module for access logging within longleaf
4
5
  module Logging
5
6
  # Get the main logger for longleaf
6
7
  def logger
7
8
  Logging.logger
8
9
  end
9
-
10
+
10
11
  # Get the main logger for longleaf
11
12
  def self.logger
12
13
  @logger ||= RedirectingLogger.new
13
14
  end
14
-
15
+
15
16
  def initialize_logger(failure_only, log_level, log_format, datetime_format)
16
17
  Logging.initialize_logger(failure_only, log_level, log_format, datetime_format)
17
18
  end
18
-
19
+
19
20
  def self.initialize_logger(failure_only, log_level, log_format, datetime_format)
20
21
  @logger = RedirectingLogger.new(failure_only: failure_only,
21
22
  log_level: log_level,
@@ -23,4 +24,4 @@ module Longleaf
23
24
  datetime_format: datetime_format)
24
25
  end
25
26
  end
26
- end
27
+ end
@@ -1,14 +1,14 @@
1
1
  require 'logger'
2
2
 
3
- # Logger which directs messages to stdout and/or stderr, depending on the nature of the message.
4
- # Status logging, which includes standard logger methods, goes to STDERR.
5
- # Operation success and failure messages go to STDOUT, and to STDERR at info level.
6
3
  module Longleaf
7
4
  module Logging
5
+ # Logger which directs messages to stdout and/or stderr, depending on the nature of the message.
6
+ # Status logging, which includes standard logger methods, goes to STDERR.
7
+ # Operation success and failure messages go to STDOUT, and to STDERR at info level.
8
8
  class RedirectingLogger
9
- # @param failure_only [Boolean] If set to true, only failure messages will be output to STDOUT
9
+ # @param [Boolean] failure_only If set to true, only failure messages will be output to STDOUT
10
10
  # @param log_level [String] logger level used for output to STDERR
11
- # @param log_format [Strfailure_onlying] format string for log entries to STDERR. There are 4 variables available
11
+ # @param log_format [String] format string for log entries to STDERR. There are 4 variables available
12
12
  # for inclusion in the output: severity, datetime, progname, msg. Variables must be wrapped in %{}.
13
13
  # @param datetime_format [String] datetime formatting string used for logger dates appearing in STDERR.
14
14
  def initialize(failure_only: false, log_level: 'WARN', log_format: nil, datetime_format: nil)
@@ -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,33 +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
- # @param eventOrMessage [String] name of the preservation event which succeeded,
70
+ #
71
+ # @param [String] eventOrMessage name of the preservation event which succeeded,
71
72
  # or the message to output if it is the only parameter. Required.
72
73
  # @param file_name [String] file name which is the subject of this message.
73
74
  # @param message [String] descriptive message to accompany this output
@@ -75,10 +76,10 @@ module Longleaf
75
76
  def success(eventOrMessage, file_name = nil, message = nil, service = nil)
76
77
  outcome('SUCCESS', eventOrMessage, file_name, message, service)
77
78
  end
78
-
79
+
79
80
  # Logs a failure message to STDOUT, as well as STDERR at info level.
80
81
  # If an error was provided, it is logged to STDERR at error level.
81
- # @param eventOrMessage [String] name of the preservation event which failed,
82
+ # @param eventOrMessage [String] name of the preservation event which failed,
82
83
  # or the message to output if it is the only parameter.
83
84
  # @param file_name [String] file name which is the subject of this message.
84
85
  # @param message [String] descriptive message to accompany this output
@@ -87,17 +88,18 @@ module Longleaf
87
88
  def failure(eventOrMessage, file_name = nil, message = nil, service = nil, error: nil)
88
89
  text = outcome_text('FAILURE', eventOrMessage, file_name, message, service, error)
89
90
  @stdout_log.warn(text)
90
-
91
+
91
92
  @stderr_log.info(text)
92
93
  @stderr_log.error("#{error.message}") unless error.nil?
94
+ @stderr_log.error("#{error.backtrace}") unless error.nil? || error.backtrace.nil?
93
95
  end
94
-
96
+
95
97
  # Logs an outcome message to STDOUT, as well as STDERR at info level.
96
98
  # If file_name and message are nil, eventOrMessage will be used as the message.
97
99
  #
98
100
  # @param outcome [String] The status of the outcome. Required.
99
- # @param eventOrMessage [String] name of the preservation event which was successful,
100
- # or the message to output if it is the only parameter. Required.
101
+ # @param eventOrMessage [String] name of the preservation event which was successful,
102
+ # or the message to output if it is the only parameter. Required.
101
103
  # @param file_name [String] file name which is the subject of this message.
102
104
  # @param message [String] descriptive message to accompany this output
103
105
  # @param service [String] name of the service which executed.
@@ -107,14 +109,13 @@ module Longleaf
107
109
  @stdout_log.info(text)
108
110
  @stderr_log.info(text)
109
111
  end
110
-
111
- # FAILURE verify[cdr_fixity_check] /path/to/file: Something terrible
112
+
112
113
  private
113
114
  def outcome_text(outcome, eventOrMessage, file_name = nil, message = nil, service = nil, error = nil)
114
115
  message_only = file_name.nil? && message.nil? && error.nil?
115
-
116
+
116
117
  text = "#{outcome}"
117
-
118
+
118
119
  if message_only
119
120
  text << ": #{eventOrMessage}"
120
121
  else
@@ -1,10 +1,15 @@
1
1
  module Longleaf
2
+ # Application configuration field names
2
3
  class AppFields
3
4
  LOCATIONS = 'locations'
4
5
  SERVICES = 'services'
5
6
  SERVICE_MAPPINGS = 'service_mappings'
6
-
7
+ SYSTEM = 'system'
8
+
7
9
  LOCATION_PATH = 'path'
8
- METADATA_PATH = 'metadata_path'
10
+ METADATA_CONFIG = 'metadata'
11
+ METADATA_DIGESTS = 'digests'
12
+
13
+ STORAGE_TYPE = 'type'
9
14
  end
10
15
  end
@@ -1,25 +1,34 @@
1
- # Record for an individual file and its associated information
2
1
  module Longleaf
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
- # @param storage_location [Longleaf::StorageLocation] storage location containing the file
11
- def initialize(file_path, storage_location)
9
+ # @param storage_location [StorageLocation] storage location containing the file
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
16
+ @metadata_record = metadata_record
17
17
  end
18
-
18
+
19
19
  # @return [String] path for the metadata file for this file
20
20
  def metadata_path
21
21
  @metadata_path = @storage_location.get_metadata_path_for(path) if @metadata_path.nil?
22
22
  @metadata_path
23
23
  end
24
+
25
+ def metadata_present?
26
+ File.exist?(metadata_path)
27
+ end
28
+
29
+ def ==(other_obj)
30
+ return false unless other_obj.is_a?(FileRecord)
31
+ path == other_obj.path
32
+ end
24
33
  end
25
- end
34
+ end
@@ -0,0 +1,56 @@
1
+ require 'longleaf/services/metadata_serializer'
2
+ require 'longleaf/models/metadata_location'
3
+ require 'longleaf/models/storage_types'
4
+
5
+ module Longleaf
6
+ # A filesystem based location in which metadata associated with registered files is stored.
7
+ class FilesystemMetadataLocation < MetadataLocation
8
+ AF ||= Longleaf::AppFields
9
+
10
+ def initialize(config)
11
+ super(config)
12
+ end
13
+
14
+ # @return the storage type for this location
15
+ def type
16
+ StorageTypes::FILESYSTEM_STORAGE_TYPE
17
+ end
18
+
19
+ # Get the absolute path for the metadata file for the given file path located in this storage location.
20
+ # @param file_path [String] path of the file relative its storage location
21
+ # @return absolute path to the metadata
22
+ # @raise [ArgumentError] if the file_path is not provided.
23
+ def metadata_path_for(file_path)
24
+ raise ArgumentError.new("A file_path parameter is required") if file_path.nil?
25
+ raise ArgumentError.new("File path must be relative") if Pathname.new(file_path).absolute?
26
+
27
+ md_path = File.join(@path, file_path)
28
+ # If the file_path is to a file, then add metadata suffix.
29
+ if md_path.end_with?('/')
30
+ md_path
31
+ else
32
+ md_path + MetadataSerializer::metadata_suffix
33
+ end
34
+ end
35
+
36
+ # Get the metadata path relative to this location
37
+ # @param md_path [String] metadata file path
38
+ # @return the metadata path relative to this location
39
+ # @raise [ArgumentError] if the metadata path is not contained by this location
40
+ def relativize(md_path)
41
+ return md_path if Pathname.new(md_path).relative?
42
+
43
+ raise ArgumentError.new("Metadata path must be contained by this location") if !md_path.start_with?(@path)
44
+
45
+ md_path.sub(@path, "")
46
+ end
47
+
48
+
49
+ # Checks that the path defined in this metadata location are available
50
+ # @raise [StorageLocationUnavailableError] if the metadata location is not available
51
+ def available?
52
+ raise StorageLocationUnavailableError.new("Metadata path does not exist or is not a directory: #{@path}")\
53
+ unless Dir.exist?(@path)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,52 @@
1
+ require 'longleaf/models/storage_location'
2
+ require 'longleaf/models/storage_types'
3
+
4
+ module Longleaf
5
+ # A storage location in a local filesystem
6
+ class FilesystemStorageLocation < StorageLocation
7
+ # @param name [String] the name of this storage location
8
+ # @param config [Hash] hash containing the configuration options for this location
9
+ # @param md_loc [MetadataLocation] metadata location associated with this storage location
10
+ def initialize(name, config, md_loc)
11
+ super(name, config, md_loc)
12
+ @path += File::SEPARATOR unless @path.end_with?(File::SEPARATOR)
13
+ end
14
+
15
+ # @return the storage type for this location
16
+ def type
17
+ StorageTypes::FILESYSTEM_STORAGE_TYPE
18
+ end
19
+
20
+ # Get that absolute path to the file associated with the provided metadata path
21
+ # @param md_path [String] metadata file path
22
+ # @raise [ArgumentError] if the md_path is not in this storage location
23
+ # @return [String] the path for the file associated with this metadata
24
+ def get_path_from_metadata_path(md_path)
25
+ raise ArgumentError.new("A file_path parameter is required") if md_path.nil? || md_path.empty?
26
+
27
+ rel_path = @metadata_location.relative_file_path_for(md_path)
28
+
29
+ File.join(@path, rel_path)
30
+ end
31
+
32
+ # Checks that the path and metadata path defined in this location are available
33
+ # @raise [StorageLocationUnavailableError] if the storage location is not available
34
+ def available?
35
+ raise StorageLocationUnavailableError.new("Path does not exist or is not a directory: #{@path}")\
36
+ unless Dir.exist?(@path)
37
+ @metadata_location.available?
38
+ end
39
+
40
+ # Get the file path relative to this location
41
+ # @param file_path [String] file path
42
+ # @return the file path relative to this location
43
+ # @raise [ArgumentError] if the file path is not contained by this location
44
+ def relativize(file_path)
45
+ return file_path if Pathname.new(file_path).relative?
46
+
47
+ raise ArgumentError.new("Metadata path must be contained by this location") if !file_path.start_with?(@path)
48
+
49
+ file_path.sub(@path, "")
50
+ end
51
+ end
52
+ end
@@ -1,8 +1,9 @@
1
1
  module Longleaf
2
+ # File metadata fields
2
3
  class MDFields
3
4
  DATA = 'data'
4
5
  SERVICES = 'services'
5
-
6
+
6
7
  REGISTERED_TIMESTAMP = 'registered'
7
8
  DEREGISTERED_TIMESTAMP = 'deregistered'
8
9
 
@@ -0,0 +1,47 @@
1
+ require 'longleaf/models/app_fields'
2
+
3
+ module Longleaf
4
+ # A location in which metadata associated with registered files is stored.
5
+ class MetadataLocation
6
+ AF ||= Longleaf::AppFields
7
+
8
+ attr_reader :path
9
+ attr_reader :digests
10
+
11
+ def initialize(config)
12
+ raise ArgumentError.new("Config parameter is required") unless config
13
+ @path = config[AF::LOCATION_PATH]
14
+ raise ArgumentError.new("Parameter path is required") unless @path
15
+ @path += '/' unless @path.end_with?('/')
16
+
17
+ digests = config[AF::METADATA_DIGESTS]
18
+ if digests.nil?
19
+ @digests = []
20
+ elsif digests.is_a?(String)
21
+ @digests = [digests.downcase]
22
+ else
23
+ @digests = digests.map(&:downcase)
24
+ end
25
+ DigestHelper::validate_algorithms(@digests)
26
+ end
27
+
28
+ # Transforms the given metadata path into a relative storage location path
29
+ # @param md_path [String] path of the metadata file or directory to compute file path for.
30
+ # @return
31
+ def relative_file_path_for(md_path)
32
+ rel_md_path = relativize(md_path)
33
+
34
+ if rel_md_path.end_with?(MetadataSerializer::metadata_suffix)
35
+ rel_md_path[0..-MetadataSerializer::metadata_suffix.length - 1]
36
+ else
37
+ rel_md_path
38
+ end
39
+ end
40
+
41
+ # @param [String] metadata path to check
42
+ # @return true if the metadata path is contained by the path for this location
43
+ def contains?(md_path)
44
+ md_path.start_with?(@path)
45
+ end
46
+ end
47
+ end
@@ -1,14 +1,16 @@
1
1
  require_relative 'md_fields'
2
2
  require_relative 'service_record'
3
+ require 'longleaf/helpers/case_insensitive_hash'
3
4
 
4
- # Metadata record for a single file
5
5
  module Longleaf
6
+ # Metadata record for a single file
6
7
  class MetadataRecord
7
- attr_reader :deregistered, :registered
8
+ attr_reader :registered
9
+ attr_accessor :deregistered
8
10
  attr_reader :checksums
9
11
  attr_reader :properties
10
12
  attr_accessor :file_size, :last_modified
11
-
13
+
12
14
  # @param properties [Hash] initial data properties for this record
13
15
  # @param services [Hash] initial service property tree
14
16
  # @param deregistered [String] deregistered timestamp
@@ -16,42 +18,64 @@ module Longleaf
16
18
  # @param checksums [Hash] hash of checksum values
17
19
  # @param file_size [Integer] size of file in bytes
18
20
  # @param last_modified [String] iso8601 representation of the last modified date of file
19
- def initialize(properties: Hash.new, services: Hash.new, deregistered: nil, registered: nil, checksums: Hash.new,
21
+ def initialize(properties: nil, services: nil, deregistered: nil, registered: nil, checksums: nil,
20
22
  file_size: nil, last_modified: nil)
21
- @properties = properties
23
+ @properties = properties || Hash.new
22
24
  @registered = registered
23
25
  @deregistered = deregistered
24
- @checksums = checksums
25
- @services = services
26
+ @checksums = CaseInsensitiveHash.new
27
+ @checksums.merge!(checksums) unless checksums.nil?
28
+ @services = services || Hash.new
26
29
  @file_size = file_size
27
30
  @last_modified = last_modified
28
31
  end
29
-
32
+
30
33
  # @return [Boolean] true if the record is deregistered
31
34
  def deregistered?
32
35
  !@deregistered.nil?
33
36
  end
34
-
37
+
35
38
  # Adds a service to this record
36
39
  #
37
40
  # @param name [String] identifier for the service being added
38
- # @param service_properties [ServiceRecord] properties for populating the new service
39
- def add_service(name, service = Longleaf::ServiceRecord.new)
41
+ # @param service [ServiceRecord] properties for populating the new service
42
+ # @return [ServiceRecord] the service added
43
+ def add_service(name, service = ServiceRecord.new)
40
44
  raise ArgumentError.new("Value must be a ServiceRecord object when adding a service") unless service.class == Longleaf::ServiceRecord
41
45
  raise IndexError.new("Service with name '#{name}' already exists") if @services.key?(name)
42
-
46
+
43
47
  @services[name] = service
44
48
  end
45
-
49
+
50
+ # Updates details of service record as if the service had been executed.
51
+ # @param service_name [String] name of the service run
52
+ # @return [ServiceRecord] the service record updated
53
+ def update_service_as_performed(service_name)
54
+ service_rec = service(service_name) || add_service(service_name)
55
+ service_rec.run_needed = false
56
+ service_rec.timestamp = ServiceDateHelper.formatted_timestamp
57
+ service_rec
58
+ end
59
+
60
+ # Updates details of service record as if the service had encountered a
61
+ # failure during execution.
62
+ # @param service_name [String] name of the service run
63
+ # @return [ServiceRecord] the service record updated
64
+ def update_service_as_failed(service_name)
65
+ service_rec = service(service_name) || add_service(service_name)
66
+ service_rec.failure_timestamp = ServiceDateHelper.formatted_timestamp
67
+ service_rec
68
+ end
69
+
46
70
  # @param name [String] name identifier of the service to retrieve
47
71
  # @return [ServiceRecord] the ServiceRecord for the service identified by name, or nil
48
72
  def service(name)
49
73
  @services[name]
50
74
  end
51
-
75
+
52
76
  # @return [Array<String>] a list of name identifiers for services registered to this record
53
77
  def list_services
54
78
  @services.keys
55
79
  end
56
80
  end
57
- end
81
+ end