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
@@ -1,49 +1,59 @@
1
1
  require 'pathname'
2
- require_relative '../models/service_fields'
3
- require_relative '../models/app_fields'
4
- require_relative '../errors'
2
+ require 'longleaf/models/service_fields'
3
+ require 'longleaf/models/app_fields'
4
+ require 'longleaf/errors'
5
5
  require_relative 'configuration_validator'
6
6
 
7
- # Validates application configuration of service to location mappings
8
7
  module Longleaf
8
+ # Validates application configuration of service to location mappings
9
9
  class ServiceMappingValidator < ConfigurationValidator
10
10
  AF ||= Longleaf::AppFields
11
-
11
+
12
+ # @param config [Hash] hash containing the application configuration
13
+ def initialize(config)
14
+ super(config)
15
+ end
16
+
17
+ protected
12
18
  # Validates service mapping configuration to ensure that it is syntactically and referentially correct.
13
19
  # @param config [Hash] hash containing the application configuration
14
- def self.validate_config(config)
15
-
16
- assert("Configuration must be a hash, but a #{config.class} was provided", config.class == Hash)
17
- assert("Configuration must contain a root '#{AF::SERVICE_MAPPINGS}' key", config.key?(AF::SERVICE_MAPPINGS))
18
- mappings = config[AF::SERVICE_MAPPINGS]
20
+ def validate
21
+ assert("Configuration must be a hash, but a #{@config.class} was provided", @config.class == Hash)
22
+ assert("Configuration must contain a root '#{AF::SERVICE_MAPPINGS}' key", @config.key?(AF::SERVICE_MAPPINGS))
23
+ mappings = @config[AF::SERVICE_MAPPINGS]
19
24
  return if mappings.nil? || mappings.empty?
20
25
  assert("'#{AF::SERVICE_MAPPINGS}' must be an array of mappings", mappings.is_a?(Array))
21
-
22
- service_names = config[AF::SERVICES].keys
23
- location_names = config[AF::LOCATIONS].keys
24
-
25
- existing_paths = Array.new
26
+
27
+ service_names = @config[AF::SERVICES].keys
28
+ location_names = @config[AF::LOCATIONS].keys
29
+
26
30
  mappings.each do |mapping|
27
- assert("Mapping must be a hash, but received #{mapping.inspect} instead", mapping.is_a?(Hash))
28
-
29
- validate_mapping_field(AF::LOCATIONS, mapping, location_names)
30
- validate_mapping_field(AF::SERVICES, mapping, service_names)
31
+ register_on_failure do
32
+ assert("Mapping must be a hash, but received #{mapping.inspect} instead", mapping.is_a?(Hash))
33
+
34
+ register_on_failure { validate_mapping_field(AF::LOCATIONS, mapping, location_names) }
35
+ register_on_failure { validate_mapping_field(AF::SERVICES, mapping, service_names) }
36
+ end
31
37
  end
38
+
39
+ @result
32
40
  end
33
-
41
+
34
42
  private
35
- def self.validate_mapping_field(field, mapping, valid_values)
43
+ def validate_mapping_field(field, mapping, valid_values)
36
44
  assert("Mapping must contain a '#{field}' field", mapping.key?(field))
37
45
  field_values = mapping[field]
38
- assert("Mapping '#{field}' field must be either a string or an array, but received '#{field_values.inspect}' instead",
46
+ assert("Mapping '#{field}' field must be either a string or an array, but received '#{field_values.class}' instead",
39
47
  field_values.is_a?(Array) || field_values.is_a?(String))
40
48
  assert("Mapping must specify one or more value in the '#{field}' field", !field_values.empty?)
41
-
49
+
42
50
  check_values = field_values.is_a?(String) ? [field_values] : field_values
43
51
  check_values.each do |value|
44
- assert("Mapping '#{field}' specifies value '#{value}', but no #{field} with that name exist",
45
- valid_values.include?(value))
52
+ register_on_failure do
53
+ assert("Mapping specifies value '#{value}', but no #{field} with that name exist",
54
+ valid_values.include?(value))
55
+ end
46
56
  end
47
57
  end
48
58
  end
49
- end
59
+ end
@@ -1,37 +1,98 @@
1
- require_relative '../models/app_fields'
2
- require_relative '../models/storage_location'
1
+ require 'longleaf/models/app_fields'
2
+ require 'longleaf/models/storage_types'
3
+ require 'longleaf/models/filesystem_storage_location'
4
+ require 'longleaf/models/s3_storage_location'
5
+ require 'longleaf/models/filesystem_metadata_location'
6
+ require 'longleaf/errors'
3
7
 
4
- # Manager which loads and provides access to Longleaf::StorageLocation objects
5
8
  module Longleaf
9
+ # Manager which loads and provides access to {StorageLocation} objects
6
10
  class StorageLocationManager
7
11
  AF ||= Longleaf::AppFields
8
-
12
+ ST ||= Longleaf::StorageTypes
13
+
14
+ # Hash mapping storage location names to {StorageLocation} objects
9
15
  attr_reader :locations
10
-
16
+ # Mapping of storage types to storage location classes
17
+ @@storage_type_mappings = {
18
+ ST::FILESYSTEM_STORAGE_TYPE => Longleaf::FilesystemStorageLocation,
19
+ ST::S3_STORAGE_TYPE => Longleaf::S3StorageLocation
20
+ }
21
+ @@metadata_type_mappings = { ST::FILESYSTEM_STORAGE_TYPE => Longleaf::FilesystemMetadataLocation }
22
+
23
+ # @param config [Hash] has representation of the application configuration
11
24
  def initialize(config)
12
25
  raise ArgumentError.new("Configuration must be provided") if config&.empty?
13
26
 
14
27
  @locations = Hash.new
15
28
  config[AF::LOCATIONS].each do |name, properties|
16
- path = properties[AF::LOCATION_PATH]
17
- md_path = properties[AF::METADATA_PATH]
18
- location = Longleaf::StorageLocation.new(name: name, path: path, metadata_path: md_path)
19
-
20
- @locations[name] = location
29
+ md_loc = instantiate_metadata_location(properties)
30
+
31
+ @locations[name] = instantiate_storage_location(name, properties, md_loc)
21
32
  end
22
33
  @locations.freeze
23
34
  end
24
-
25
- # Get the StorageLocation object which should contain the given path
35
+
36
+ # Get the {StorageLocation} object which should contain the given path
26
37
  # @return [Longleaf::StorageLocation] location containing the given path
27
38
  # or nil if the path is not contained by a registered location.
28
39
  def get_location_by_path(path)
29
40
  raise ArgumentError.new("Path parameter is required") if path.nil? || path.empty?
30
41
  @locations.each do |name, location|
31
- return location if path.start_with?(location.path)
42
+ return location if location.contains?(path)
43
+ end
44
+
45
+ nil
46
+ end
47
+
48
+ # Get the {StorageLocation} object which should contain the given metadata path
49
+ # @return [Longleaf::StorageLocation] location containing the given metadata path
50
+ # or nil if the path is not contained by a registered location.
51
+ def get_location_by_metadata_path(md_path)
52
+ raise ArgumentError.new("Metadata path parameter is required") if md_path.nil? || md_path.empty?
53
+ @locations.each do |name, location|
54
+ return location if location.metadata_location.contains?(md_path)
32
55
  end
33
-
56
+
34
57
  nil
35
58
  end
59
+
60
+ # Raises a {StorageLocationUnavailableError} if the given path is not in a known storage location,
61
+ # or if it is not within the expected location if provided
62
+ # @param path [String] file path
63
+ # @param expected_loc [String] name of the storage location which path should be contained by
64
+ # @raise [StorageLocationUnavailableError] if the path is not in a known/expected storage location
65
+ # @return [StorageLocation] the storage location which contains path, if it was within one.
66
+ def verify_path_in_location(path, expected_loc = nil)
67
+ location = get_location_by_path(path)
68
+ if location.nil?
69
+ raise StorageLocationUnavailableError.new("Path #{path} is not from a known storage location.")
70
+ elsif !expected_loc.nil? && expected_loc != location.name
71
+ raise StorageLocationUnavailableError.new("Path #{path} is not contained by storage location #{expected_loc}.")
72
+ end
73
+ location
74
+ end
75
+
76
+ private
77
+ def instantiate_metadata_location(loc_properties)
78
+ m_config = loc_properties[AF::METADATA_CONFIG]
79
+ m_type = m_config[AF::STORAGE_TYPE]
80
+ m_type = ST::FILESYSTEM_STORAGE_TYPE if m_type.nil?
81
+
82
+ m_class = @@metadata_type_mappings[m_type]
83
+ raise ArgumentError.new("Unknown metadata location type #{m_type}") if m_class.nil?
84
+
85
+ m_class.new(m_config)
86
+ end
87
+
88
+ def instantiate_storage_location(name, properties, md_loc)
89
+ s_type = properties[AF::STORAGE_TYPE]
90
+ s_type = ST::FILESYSTEM_STORAGE_TYPE if s_type.nil?
91
+
92
+ s_class = @@storage_type_mappings[s_type]
93
+ raise ArgumentError.new("Unknown storage location type #{s_type}") if s_class.nil?
94
+
95
+ s_class.new(name, properties, md_loc)
96
+ end
36
97
  end
37
- end
98
+ end
@@ -1,60 +1,74 @@
1
1
  require 'pathname'
2
- require 'longleaf/models/storage_location'
3
2
  require 'longleaf/models/app_fields'
3
+ require 'longleaf/models/storage_types'
4
4
  require 'longleaf/errors'
5
5
  require_relative 'configuration_validator'
6
- require 'longleaf/services/storage_path_validator'
6
+ require 'longleaf/services/filesystem_location_validator'
7
+ require 'longleaf/services/s3_location_validator'
7
8
 
8
- # Validates application configuration of storage locations
9
9
  module Longleaf
10
+ # Validates application configuration of storage locations
10
11
  class StorageLocationValidator < ConfigurationValidator
11
12
  AF ||= Longleaf::AppFields
12
-
13
- # Validates configuration to ensure that it is syntactically correct and does not violate
14
- # schema and uniqueness requirements.
13
+ ST ||= Longleaf::StorageTypes
14
+
15
+ @@storage_type_mappings = {
16
+ ST::FILESYSTEM_STORAGE_TYPE => Longleaf::FilesystemLocationValidator,
17
+ ST::S3_STORAGE_TYPE => Longleaf::S3LocationValidator
18
+ }
19
+
15
20
  # @param config [Hash] hash containing the application configuration
16
- def self.validate_config(config)
17
- assert("Configuration must be a hash, but a #{config.class} was provided", config.class == Hash)
18
- assert("Configuration must contain a root '#{AF::LOCATIONS}' key", config.key?(AF::LOCATIONS))
19
- locations = config[AF::LOCATIONS]
21
+ def initialize(config)
22
+ super(config)
23
+ @existing_paths = Array.new
24
+ end
25
+
26
+ protected
27
+ # Validates configuration to ensure that it is syntactically correct and does not violate
28
+ # schema and uniqueness requirements.
29
+ def validate
30
+ assert("Configuration must be a hash, but a #{@config.class} was provided", @config.class == Hash)
31
+ assert("Configuration must contain a root '#{AF::LOCATIONS}' key", @config.key?(AF::LOCATIONS))
32
+ locations = @config[AF::LOCATIONS]
20
33
  assert("'#{AF::LOCATIONS}' must be a hash of locations", locations.class == Hash)
21
-
22
- existing_paths = Array.new
34
+
23
35
  locations.each do |name, properties|
24
- assert("Name of storage location must be a string, but was of type #{name.class}", name.instance_of?(String))
25
- assert("Storage location '#{name}' must be a hash, but a #{properties.class} was provided", properties.is_a?(Hash))
26
-
27
- assert_path_property_valid(name, AF::LOCATION_PATH, properties, existing_paths)
28
- assert_path_property_valid(name, AF::METADATA_PATH, properties, existing_paths)
36
+ register_on_failure do
37
+ assert("Name of storage location must be a string, but was of type #{name.class}", name.instance_of?(String))
38
+ assert("Storage location '#{name}' must be a hash, but a #{properties.class} was provided", properties.is_a?(Hash))
39
+
40
+ register_on_failure { assert_path_property_valid(name, AF::LOCATION_PATH, properties, 'location') }
41
+
42
+ assert("Metadata location must be present for location '#{name}'", properties.key?(AF::METADATA_CONFIG))
43
+ assert_path_property_valid(name, AF::LOCATION_PATH, properties[AF::METADATA_CONFIG], 'metadata')
44
+ end
29
45
  end
46
+
47
+ @result
30
48
  end
31
-
49
+
32
50
  private
33
- def self.assert_path_property_valid(name, path_prop, properties, existing_paths)
51
+ def assert_path_property_valid(name, path_prop, properties, section_name)
34
52
  path = properties[path_prop]
35
- begin
36
- StoragePathValidator::validate(path)
37
- rescue InvalidStoragePathError => err
38
- raise ConfigurationError.new(
39
- "Storage location '#{name}' specifies invalid '#{path_prop}' property: #{err.message}")
40
- end
41
- assert("Storage location '#{name}' must specify a '#{path_prop}' property", !path.nil? && !path.empty?)
42
- assert("Storage location '#{name}' must specify an absolute path for property '#{path_prop}'",
43
- Pathname.new(path).absolute? && !path.include?('/..'))
53
+
54
+ storage_type = properties[AF::STORAGE_TYPE] || ST::DEFAULT_STORAGE_TYPE
55
+ type_validator = @@storage_type_mappings[storage_type]
56
+ type_validator.validate(self, name, path_prop, section_name, path)
57
+
44
58
  # Ensure paths have trailing slash to avoid matching on partial directory names
45
59
  path += '/' unless path.end_with?('/')
46
60
  # Verify that the (metadata_)path property's value is not inside of another storage location or vice versa
47
- existing_paths.each do |existing|
61
+ @existing_paths.each do |existing|
48
62
  if existing.start_with?(path) || path.start_with?(existing)
49
- msg = "Location '#{name}' defines property #{path_prop} with value '#{path}'" \
63
+ msg = "Location '#{name}' defines property #{section_name} #{path_prop} with value '#{path}'" \
50
64
  " which overlaps with another configured path '#{existing}'." \
51
- " Storage locations must not define #{AF::LOCATION_PATH} or #{AF::METADATA_PATH}" \
65
+ " Storage locations must not define #{AF::LOCATION_PATH}" \
52
66
  " properties which are contained by another location property"
53
- raise ConfigurationError.new(msg)
67
+ fail(msg)
54
68
  end
55
69
  end
56
-
57
- existing_paths << path
70
+
71
+ @existing_paths << path
58
72
  end
59
73
  end
60
- end
74
+ end
@@ -1,19 +1,19 @@
1
- require_relative '../models/app_fields'
2
- require_relative '../models/service_fields'
1
+ require 'longleaf/models/app_fields'
2
+ require 'longleaf/models/service_fields'
3
3
  require 'yaml'
4
4
 
5
- # Test helper for constructing application configuration hashes
6
5
  module Longleaf
6
+ # Test helper for constructing application configuration hashes
7
7
  class ConfigBuilder
8
8
  AF ||= Longleaf::AppFields
9
9
  SF ||= Longleaf::ServiceFields
10
-
10
+
11
11
  attr_accessor :config
12
-
12
+
13
13
  def initialize
14
14
  @config = Hash.new
15
15
  end
16
-
16
+
17
17
  # Add a root 'locations' field to the config
18
18
  # @param locations [Hash] value for the locations fields. Default is {}
19
19
  # @return this builder
@@ -21,22 +21,30 @@ module Longleaf
21
21
  @config[AF::LOCATIONS] = locations
22
22
  self
23
23
  end
24
-
24
+
25
25
  # Add a 'location' to the config
26
26
  # @param name [String] name of the location
27
27
  # @param path [String] value for the 'path' field
28
28
  # @param md_path [String] value for the 'metadata_path' field
29
29
  # @return this builder
30
- def with_location(name:, path: '/file/path/', md_path: '/metadata/path/')
30
+ def with_location(name:, path: '/file/path/', s_type: nil, md_path: '/metadata/path/', md_type: nil, md_digests: nil)
31
31
  @config[AF::LOCATIONS] = Hash.new unless @config.key?(AF::LOCATIONS)
32
-
32
+
33
33
  location = {}
34
34
  @config[AF::LOCATIONS][name] = location
35
35
  location[AF::LOCATION_PATH] = path unless path.nil?
36
- location[AF::METADATA_PATH] = md_path unless md_path.nil?
36
+ location[AF::STORAGE_TYPE] = s_type unless s_type.nil?
37
+
38
+ if !md_path.nil?
39
+ md_loc = { AF::LOCATION_PATH => md_path }
40
+ location[AF::METADATA_CONFIG] = md_loc
41
+
42
+ md_loc[AF::METADATA_DIGESTS] = md_digests unless md_digests.nil?
43
+ md_loc[AF::STORAGE_TYPE] = md_type unless md_type.nil?
44
+ end
37
45
  self
38
46
  end
39
-
47
+
40
48
  # Add a root 'services' field to the config
41
49
  # @param services [Hash] value for the services field. Default is {}
42
50
  # @return this builder
@@ -44,26 +52,29 @@ module Longleaf
44
52
  @config[AF::SERVICES] = services
45
53
  self
46
54
  end
47
-
55
+
48
56
  # Add a 'service' to the config
49
57
  # @param name [String] name of the service
50
58
  # @param work_script [String] value for the 'work_script' field
59
+ # @param work_class [String] value for the 'work_class' field
51
60
  # @param frequency [String] value for the 'frequency' field
52
61
  # @param delay [String] value for the 'delay' field
53
62
  # @param properties [Hash] hash of additional properties to include in the service
54
63
  # @return this builder
55
- def with_service(name:, work_script: 'some_pres_service.rb', frequency: nil, delay: nil, properties: nil)
64
+ def with_service(name:, work_script: 'some_pres_service.rb', work_class: nil,
65
+ frequency: nil, delay: nil, properties: nil)
56
66
  @config[AF::SERVICES] = Hash.new unless @config.key?(AF::SERVICES)
57
-
67
+
58
68
  service = {}
59
69
  service[SF::WORK_SCRIPT] = work_script
70
+ service[SF::WORK_CLASS] = work_class
60
71
  service[SF::FREQUENCY] = frequency unless frequency.nil?
61
72
  service[SF::DELAY] = delay unless delay.nil?
62
73
  service = service.merge(properties) unless properties.nil?
63
74
  @config[AF::SERVICES][name] = service
64
75
  self
65
76
  end
66
-
77
+
67
78
  # Adds a 'service_mappings' section to the config
68
79
  # @param mappings [Object] the mappings config
69
80
  # @return this builder
@@ -71,32 +82,45 @@ module Longleaf
71
82
  @config[AF::SERVICE_MAPPINGS] = mappings
72
83
  self
73
84
  end
74
-
85
+
75
86
  # Add a mapping from one or more services to one or more location
76
87
  # @param loc_names [Object] one or more location names. Can be a string or array.
77
88
  # @param service_names [Object] one or more service names. Can be a string or array.
78
89
  def map_services(loc_names, service_names)
79
90
  @config[AF::SERVICE_MAPPINGS] = Array.new unless @config.key?(AF::SERVICE_MAPPINGS)
80
-
91
+
81
92
  mapping = Hash.new
82
93
  mapping[AF::LOCATIONS] = loc_names unless loc_names.nil?
83
94
  mapping[AF::SERVICES] = service_names unless service_names.nil?
84
95
  @config[AF::SERVICE_MAPPINGS].push(mapping)
85
96
  self
86
97
  end
87
-
98
+
99
+ # Add a system config section to the config
100
+ def with_system(sys_config)
101
+ @config[AF::SYSTEM] = sys_config
102
+ self
103
+ end
104
+
88
105
  # @return the constructed configuration
89
106
  def get
90
107
  @config
91
108
  end
92
-
109
+
93
110
  # Writes the configuration from this builder into a temporary file
94
111
  # @return the file path of the config file
95
- def write_to_yaml_file
96
- Tempfile.open('config') do |f|
112
+ def write_to_yaml_file(dest_path = nil)
113
+ if dest_path.nil?
114
+ file = Tempfile.new('config')
115
+ file.close
116
+ dest_path = file.path
117
+ # deleting temp file but reusing file name. This is to avoid premature garbage collection.
118
+ file.unlink
119
+ end
120
+ File.open(dest_path, 'w') do |f|
97
121
  f.write(@config.to_yaml)
98
- return f.path
99
122
  end
123
+ dest_path
100
124
  end
101
125
  end
102
- end
126
+ end