longleaf 0.1.0.pre.2 → 1.0.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.
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
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.9.19
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Longleaf.html" title="Longleaf (module)">Longleaf</a></span>
86
+
87
+
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Tue May 28 15:48:00 2019 by
104
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.19 (ruby-2.6.3).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
@@ -0,0 +1,139 @@
1
+ require 'longleaf/logging'
2
+
3
+ module Longleaf
4
+ # Selects and allows for iteration over files which match a provided set of selection criteria
5
+ class FileSelector
6
+ include Longleaf::Logging
7
+ SPECIFICITY_PATH = 'path'
8
+ SPECIFICITY_STORAGE_LOCATION = 'storage_location'
9
+
10
+ attr_reader :specificity
11
+
12
+ # May only provide either file_paths or storage_locations
13
+ def initialize(file_paths: nil, storage_locations: nil, app_config:)
14
+ if nil_or_empty?(file_paths) && nil_or_empty?(storage_locations)
15
+ raise ArgumentError.new("Must provide either file paths or storage locations")
16
+ end
17
+ if !nil_or_empty?(file_paths) && !nil_or_empty?(storage_locations)
18
+ raise ArgumentError.new("Cannot provide both file paths and storage locations")
19
+ end
20
+ @app_config = app_config
21
+ # The top level paths targeted by this selector
22
+ @target_paths = file_paths&.map do |path|
23
+ # Resolve relative paths against pwd
24
+ pathname = Pathname.new(path)
25
+ if !pathname.absolute?
26
+ path = File.join(Dir.pwd, path)
27
+ end
28
+ path = File.expand_path(path)
29
+
30
+ # adding trailing /'s to directories
31
+ if Dir.exist?(path) && !path.end_with?('/')
32
+ path + '/'
33
+ else
34
+ path
35
+ end
36
+ end
37
+ # The set of storage locations to select file paths from
38
+ @storage_locations = storage_locations
39
+ # Validate that the selected storage locations are known
40
+ if @storage_locations.nil?
41
+ @specificity = SPECIFICITY_PATH
42
+ else
43
+ @specificity = SPECIFICITY_STORAGE_LOCATION
44
+ locations = @app_config.location_manager.locations
45
+ @storage_locations.each do |loc_name|
46
+ unless locations.key?(loc_name)
47
+ raise StorageLocationUnavailableError.new("Cannot select unknown storage location #{loc_name}.")
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ # @return [Array] a list of top level paths from which files will be selected
54
+ def target_paths
55
+ # If starting from locations, initialize by expanding locations out to their actual paths
56
+ if @target_paths.nil? && !@storage_locations.nil?
57
+ @target_paths = Array.new
58
+ @storage_locations.each do |loc_name|
59
+ @target_paths << @app_config.location_manager.locations[loc_name].path
60
+ end
61
+ end
62
+
63
+ @target_paths
64
+ end
65
+
66
+ # Get the next file path for this selector.
67
+ # @return [String] an absolute path to the next file targeted by this selector,
68
+ # or nil if no more files selected
69
+ def next_path
70
+ if @paths.nil?
71
+ # Start the paths listing out from the targetted set of paths for this selector
72
+ # In reverse order since using a LIFO structure
73
+ @paths = target_paths.reverse
74
+ end
75
+
76
+ # No more paths to return
77
+ return nil if @paths&.empty?
78
+
79
+ # Get the most recently added path for depth first traversal of selected paths
80
+ path = @paths.pop
81
+ until path.nil? do
82
+ @app_config.location_manager.verify_path_in_location(path)
83
+
84
+ if File.exist?(path)
85
+ if File.directory?(path)
86
+ logger.debug("Expanding directory #{path}")
87
+ # For a directory, add all children to file_paths
88
+ Dir.entries(path).sort.reverse_each do |child|
89
+ @paths << File.join(path, child) unless child == '.' or child == '..'
90
+ end
91
+ else
92
+ logger.debug("Returning file #{path}")
93
+ return path
94
+ end
95
+ else
96
+ raise InvalidStoragePathError.new("File #{path} does not exist.")
97
+ end
98
+
99
+ # Returned path was not a suitable file, try the next path
100
+ path = @paths.pop
101
+ end
102
+ end
103
+
104
+ # Iterate through the file paths for this selector and execute the provided block with each.
105
+ # A block is required.
106
+ def each
107
+ file_path = next_path
108
+ until file_path.nil?
109
+ yield file_path
110
+
111
+ file_path = next_path
112
+ end
113
+ end
114
+
115
+ # return [Array] a list of all storage locations being targeted by this selector
116
+ def storage_locations
117
+ # Determine what storage_locations are represented by the given file paths
118
+ if @storage_locations.nil? && !@target_paths.nil?
119
+ loc_set = Set.new
120
+ @target_paths.each do |path|
121
+ loc = @app_config.location_manager.get_location_by_path(path)
122
+ loc_set.add(loc.name) unless loc.nil?
123
+ end
124
+ @storage_locations = loc_set.to_a
125
+ end
126
+
127
+ if @storage_locations.nil?
128
+ @storage_locations = Array.new
129
+ end
130
+
131
+ @storage_locations
132
+ end
133
+
134
+ private
135
+ def nil_or_empty?(value)
136
+ value.nil? || value.empty?
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,17 @@
1
+ module Longleaf
2
+ # Provides digests for files from a manifest
3
+ class ManifestDigestProvider
4
+ # @param hash which maps file paths to hashs of digests
5
+ def initialize(digests_mapping)
6
+ @digests_mapping = digests_mapping
7
+ end
8
+
9
+ # @param file_path [String] path of file
10
+ # @return hash containing all the manifested digests for the given path, or nil
11
+ def get_digests(file_path)
12
+ # return nil if key not found, in case the hash has default values
13
+ return nil unless @digests_mapping.key?(file_path)
14
+ @digests_mapping[file_path]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,67 @@
1
+ require 'longleaf/candidates/file_selector'
2
+ require 'longleaf/logging'
3
+
4
+ module Longleaf
5
+ # Selects and allows for iteration over files which are registered and match a provided
6
+ # set of selection criteria
7
+ class RegisteredFileSelector < FileSelector
8
+ include Longleaf::Logging
9
+
10
+ # Get the next file path for this selector.
11
+ # @raise [InvalidStoragePathError] if any of the selected files do not exist
12
+ # @raise [StorageLocationUnavailableError] if any of the selected paths are not
13
+ # in a registered storage location.
14
+ # @return [String] an absolute path to the next file targeted by this selector,
15
+ # or nil if no more files selected
16
+ def next_path
17
+ if @md_paths.nil?
18
+ # Compute the starting paths by looking up the metadata paths for the provided targets,
19
+ # in reverse order since @md_paths is a LIFO stack structure.
20
+ @md_paths = target_paths.reverse_each.map do |file_path|
21
+ storage_loc = @app_config.location_manager.verify_path_in_location(file_path)
22
+ storage_loc.get_metadata_path_for(file_path)
23
+ end
24
+ end
25
+
26
+ # No more paths to return
27
+ return nil if @md_paths&.empty?
28
+
29
+ # Get the most recently added path for depth first traversal of selected paths
30
+ md_path = @md_paths.pop
31
+ until md_path.nil? do
32
+ if File.exist?(md_path)
33
+ if File.directory?(md_path)
34
+ logger.debug("Expanding metadata directory #{md_path}")
35
+ # For a directory, add all children to file_paths
36
+ Dir.entries(md_path).sort.reverse_each do |child|
37
+ @md_paths << File.join(md_path, child) unless child == '.' or child == '..'
38
+ end
39
+ elsif md_path.end_with?(MetadataSerializer::metadata_suffix)
40
+ # Convert metadata path to file path before returning
41
+ return file_path_for_metadata(md_path)
42
+ else
43
+ logger.debug("Skipping non-metadata file in metadata directory #{md_path}")
44
+ end
45
+ else
46
+ file_path = file_path_for_metadata(md_path)
47
+ if File.exist?(file_path)
48
+ raise RegistrationError.new("File #{file_path} is not registered.")
49
+ else
50
+ raise InvalidStoragePathError.new("File #{file_path} does not exist.")
51
+ end
52
+ end
53
+
54
+ # Returned path was not a suitable file, try the next path
55
+ md_path = @md_paths.pop
56
+ end
57
+ end
58
+
59
+ private
60
+ def file_path_for_metadata(md_path)
61
+ storage_loc = @app_config.location_manager.get_location_by_metadata_path(md_path)
62
+ file_path = storage_loc.get_path_from_metadata_path(md_path)
63
+ logger.debug("Returning next file #{file_path} for metadata path #{md_path}")
64
+ return file_path
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,93 @@
1
+ require 'longleaf/services/service_manager'
2
+ require 'longleaf/events/event_names'
3
+ require 'longleaf/errors'
4
+ require 'longleaf/logging'
5
+ require 'time'
6
+
7
+ module Longleaf
8
+ # Iterator for getting file candidates which have services which need to be run.
9
+ # Implementation uses metadata files directly from the filesystem for determinations
10
+ # about service status.
11
+ class ServiceCandidateFilesystemIterator
12
+ include Longleaf::Logging
13
+
14
+ def initialize(file_selector, event, app_config, force = false)
15
+ @file_selector = file_selector
16
+ @event = event
17
+ @app_config = app_config
18
+ @force = force
19
+ end
20
+
21
+ # Get the file record for the next candidate which needs services run which match the
22
+ # provided file_selector
23
+ # @return [FileRecord] file record of the next candidate with services needing to be run,
24
+ # or nil if there are no more candidates.
25
+ def next_candidate
26
+ loop do
27
+ next_path = @file_selector.next_path
28
+ return nil if next_path.nil?
29
+
30
+ logger.debug("Evaluating candidate #{next_path}")
31
+ storage_loc = @app_config.location_manager.get_location_by_path(next_path)
32
+ file_rec = FileRecord.new(next_path, storage_loc)
33
+
34
+ # Skip over unregistered files
35
+ if !file_rec.metadata_present?
36
+ logger.debug("Ignoring unregistered file #{next_path}")
37
+ next
38
+ end
39
+
40
+ @app_config.md_manager.load(file_rec)
41
+
42
+ # Return the file record if it needs any services run
43
+ return file_rec if needs_run?(file_rec)
44
+ end
45
+ end
46
+
47
+ # Iterate through the candidates in this object and execute the provided block with each
48
+ # candidate. A block is required.
49
+ def each
50
+ file_rec = next_candidate
51
+ until file_rec.nil?
52
+ yield file_rec
53
+
54
+ file_rec = next_candidate
55
+ end
56
+ end
57
+
58
+ private
59
+ # Returns true if the file record contains any services which need to be run
60
+ def needs_run?(file_rec)
61
+ md_rec = file_rec.metadata_record
62
+ storage_loc = file_rec.storage_location
63
+ service_manager = @app_config.service_manager
64
+
65
+ # File is not a valid candidate for services if it is deregistered, unless performing cleanup
66
+ if @event != EventNames::CLEANUP && md_rec.deregistered?
67
+ logger.debug("Skipping deregistered file: #{file_rec.path}")
68
+ return false
69
+ end
70
+
71
+ expected_services = service_manager.list_services(
72
+ location: storage_loc.name,
73
+ event: @event)
74
+
75
+ # When in force mode, candidate needs a run as long as there are any services configured for it.
76
+ if @force && !expected_services.empty?
77
+ logger.debug("Forcing run needed for file: #{file_rec.path}")
78
+ return true
79
+ end
80
+
81
+ expected_services.each do |service_name|
82
+ if service_manager.service_needed?(service_name, md_rec)
83
+ logger.debug("Service #{service_name} needed for file: #{file_rec.path}")
84
+ return true
85
+ end
86
+ end
87
+
88
+ logger.debug("Run not needed for file: #{file_rec.path}")
89
+ # No services needed to be run for this file
90
+ false
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,84 @@
1
+ require 'longleaf/events/event_names'
2
+ require 'longleaf/errors'
3
+ require 'longleaf/logging'
4
+ require 'time'
5
+
6
+ module Longleaf
7
+ # Iterator for getting file candidates which have services which need to be run.
8
+ # Implementation uses an index of file metadata to determine if the file needs any
9
+ # services run.
10
+ class ServiceCandidateIndexIterator
11
+ include Longleaf::Logging
12
+
13
+ def initialize(file_selector, event, app_config, force = false)
14
+ @file_selector = file_selector
15
+ @event = event
16
+ @app_config = app_config
17
+ @force = force
18
+ @index_manager = @app_config.index_manager
19
+ @stale_datetime = Time.now.utc
20
+ @result_set = nil
21
+ end
22
+
23
+ # Get the file record for the next candidate which needs services run which match the
24
+ # provided file_selector
25
+ # @return [FileRecord] file record of the next candidate with services needing to be run,
26
+ # or nil if there are no more candidates.
27
+ def next_candidate
28
+ file_rec = nil
29
+ # loop until a candidate with metadata is retrieved
30
+ loop do
31
+ # Get the next page of results if the previous page has been processed
32
+ fetch_next_page if @result_set.nil? || @result_set.empty?
33
+
34
+ # Retrieve the next possible candidate path from the page
35
+ next_path = @result_set.shift
36
+ # given that the page was just retrieved, getting a nil path indicates that the retrieved page of
37
+ # candidates is empty and there are no more candidates to iterate at this time.
38
+ return nil if next_path.nil?
39
+
40
+ logger.debug("Retrieved candidate #{next_path}")
41
+ storage_loc = @app_config.location_manager.get_location_by_path(next_path)
42
+ file_rec = FileRecord.new(next_path, storage_loc)
43
+
44
+ # Keep seeking until a registered candidate is found, according to the file system.
45
+ if file_rec.metadata_present?
46
+ break
47
+ else
48
+ logger.warn("Encountered #{next_path} in index, but path is not registered. Clearing out of synch entry.")
49
+ @index_manager.remove(file_rec)
50
+ end
51
+ end
52
+
53
+ @app_config.md_manager.load(file_rec)
54
+
55
+ file_rec
56
+ end
57
+
58
+ # Iterate through the candidates in this object and execute the provided block with each
59
+ # candidate. A block is required.
60
+ def each
61
+ file_rec = next_candidate
62
+ until file_rec.nil?
63
+ yield file_rec
64
+
65
+ file_rec = next_candidate
66
+ end
67
+ end
68
+
69
+ private
70
+ def fetch_next_page
71
+ if @force
72
+ @result_set = @index_manager.registered_paths(@file_selector)
73
+ else
74
+ case @event
75
+ when EventNames::PRESERVE
76
+ @result_set = @index_manager.paths_with_stale_services(@file_selector, @stale_datetime)
77
+ when EventNames::CLEANUP
78
+ # TODO
79
+ end
80
+ end
81
+ logger.debug("Retrieve result set with #{@result_set&.length} entries")
82
+ end
83
+ end
84
+ end