longleaf 0.1.0 → 1.1.1

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 (185) 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 +150 -0
  108. data/lib/longleaf/candidates/manifest_digest_provider.rb +17 -0
  109. data/lib/longleaf/candidates/physical_path_provider.rb +17 -0
  110. data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
  111. data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +93 -0
  112. data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
  113. data/lib/longleaf/candidates/service_candidate_locator.rb +23 -0
  114. data/lib/longleaf/candidates/single_digest_provider.rb +13 -0
  115. data/lib/longleaf/cli.rb +252 -46
  116. data/lib/longleaf/commands/deregister_command.rb +51 -0
  117. data/lib/longleaf/commands/preserve_command.rb +50 -0
  118. data/lib/longleaf/commands/register_command.rb +34 -43
  119. data/lib/longleaf/commands/reindex_command.rb +92 -0
  120. data/lib/longleaf/commands/validate_config_command.rb +33 -8
  121. data/lib/longleaf/commands/validate_metadata_command.rb +51 -0
  122. data/lib/longleaf/errors.rb +26 -7
  123. data/lib/longleaf/events/deregister_event.rb +53 -0
  124. data/lib/longleaf/events/event_names.rb +9 -0
  125. data/lib/longleaf/events/event_status_tracking.rb +59 -0
  126. data/lib/longleaf/events/preserve_event.rb +82 -0
  127. data/lib/longleaf/events/register_event.rb +59 -51
  128. data/lib/longleaf/helpers/case_insensitive_hash.rb +38 -0
  129. data/lib/longleaf/helpers/digest_helper.rb +56 -0
  130. data/lib/longleaf/helpers/s3_uri_helper.rb +86 -0
  131. data/lib/longleaf/helpers/selection_options_parser.rb +215 -0
  132. data/lib/longleaf/helpers/service_date_helper.rb +78 -0
  133. data/lib/longleaf/indexing/index_manager.rb +101 -0
  134. data/lib/longleaf/indexing/sequel_index_driver.rb +306 -0
  135. data/lib/longleaf/logging.rb +5 -4
  136. data/lib/longleaf/logging/redirecting_logger.rb +30 -25
  137. data/lib/longleaf/models/app_fields.rb +7 -2
  138. data/lib/longleaf/models/file_record.rb +31 -8
  139. data/lib/longleaf/models/filesystem_metadata_location.rb +56 -0
  140. data/lib/longleaf/models/filesystem_storage_location.rb +52 -0
  141. data/lib/longleaf/models/md_fields.rb +3 -1
  142. data/lib/longleaf/models/metadata_location.rb +47 -0
  143. data/lib/longleaf/models/metadata_record.rb +43 -16
  144. data/lib/longleaf/models/s3_storage_location.rb +138 -0
  145. data/lib/longleaf/models/service_definition.rb +7 -6
  146. data/lib/longleaf/models/service_fields.rb +7 -1
  147. data/lib/longleaf/models/service_record.rb +10 -6
  148. data/lib/longleaf/models/storage_location.rb +24 -19
  149. data/lib/longleaf/models/storage_types.rb +9 -0
  150. data/lib/longleaf/models/system_config_fields.rb +9 -0
  151. data/lib/longleaf/preservation_services/file_check_service.rb +59 -0
  152. data/lib/longleaf/preservation_services/fixity_check_service.rb +124 -0
  153. data/lib/longleaf/preservation_services/rsync_replication_service.rb +198 -0
  154. data/lib/longleaf/preservation_services/s3_replication_service.rb +131 -0
  155. data/lib/longleaf/services/application_config_deserializer.rb +81 -24
  156. data/lib/longleaf/services/application_config_manager.rb +20 -6
  157. data/lib/longleaf/services/application_config_validator.rb +19 -9
  158. data/lib/longleaf/services/configuration_validator.rb +67 -4
  159. data/lib/longleaf/services/filesystem_location_validator.rb +16 -0
  160. data/lib/longleaf/services/metadata_deserializer.rb +115 -42
  161. data/lib/longleaf/services/metadata_persistence_manager.rb +47 -0
  162. data/lib/longleaf/services/metadata_serializer.rb +156 -23
  163. data/lib/longleaf/services/metadata_validator.rb +76 -0
  164. data/lib/longleaf/services/s3_location_validator.rb +19 -0
  165. data/lib/longleaf/services/service_class_cache.rb +112 -0
  166. data/lib/longleaf/services/service_definition_manager.rb +10 -7
  167. data/lib/longleaf/services/service_definition_validator.rb +25 -18
  168. data/lib/longleaf/services/service_manager.rb +86 -11
  169. data/lib/longleaf/services/service_mapping_manager.rb +13 -12
  170. data/lib/longleaf/services/service_mapping_validator.rb +36 -26
  171. data/lib/longleaf/services/storage_location_manager.rb +76 -15
  172. data/lib/longleaf/services/storage_location_validator.rb +49 -35
  173. data/lib/longleaf/specs/config_builder.rb +47 -23
  174. data/lib/longleaf/specs/config_validator_helpers.rb +16 -0
  175. data/lib/longleaf/specs/custom_matchers.rb +9 -0
  176. data/lib/longleaf/specs/file_helpers.rb +61 -0
  177. data/lib/longleaf/specs/metadata_builder.rb +98 -0
  178. data/lib/longleaf/specs/system_config_builder.rb +27 -0
  179. data/lib/longleaf/version.rb +1 -1
  180. data/longleaf.gemspec +20 -7
  181. data/mkdocs.yml +21 -0
  182. metadata +308 -24
  183. data/.travis.yml +0 -4
  184. data/lib/longleaf/commands/abstract_command.rb +0 -37
  185. data/lib/longleaf/services/storage_path_validator.rb +0 -16
@@ -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
@@ -0,0 +1,16 @@
1
+ module Longleaf
2
+ module ConfigValidatorHelpers
3
+ def fails_validation_with_error(validator, *error_messages)
4
+ result = validator.validate_config
5
+ expect(result.valid?).to be false
6
+ error_messages.each do |error_message|
7
+ expect(result.errors).to include(error_message)
8
+ end
9
+ end
10
+
11
+ def passes_validation(validator)
12
+ result = validator.validate_config
13
+ expect(result.valid?).to eq(true), "expected validation to pass, but received errors:\n#{result.errors&.join("\n")}"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Longleaf
2
+ # Match if the parameter is a {FileRecord} with the expected path
3
+ RSpec::Matchers.define :be_file_record_for do |expected|
4
+ match do |actual|
5
+ return false if actual.nil? || !actual.is_a?(Longleaf::FileRecord)
6
+ actual.path == expected
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ require 'tempfile'
2
+ require 'tmpdir'
3
+
4
+ module Longleaf
5
+ # Test helper methods for creating test files
6
+ module FileHelpers
7
+ def make_test_dir(parent: nil, name: 'dir')
8
+ FileHelpers.make_test_dir(parent: parent, name: name)
9
+ end
10
+
11
+ def self.make_test_dir(parent: nil, name: 'dir')
12
+ if parent.nil?
13
+ Dir.mktmpdir(name)
14
+ else
15
+ path = File.join(parent, name)
16
+ Dir.mkdir(path)
17
+ path
18
+ end
19
+ end
20
+
21
+ def create_test_file(dir: nil, name: nil, content: 'content')
22
+ FileHelpers.create_test_file(dir: dir, name: name, content: content)
23
+ end
24
+
25
+ def self.create_test_file(dir: nil, name: nil, content: 'content')
26
+ if dir.nil? || name.nil?
27
+ name = 'test_file' if name.nil?
28
+ file = Tempfile.create(name, dir)
29
+ file << content
30
+ file.close
31
+ return file.path
32
+ else
33
+ path = File.join(dir, name)
34
+ File.open(path, 'w') { |f| f.write(content) }
35
+ path
36
+ end
37
+ end
38
+
39
+ def create_work_class(lib_dir, class_name, file_name, module_name = nil, is_applicable: true, init_body: "", perform: "")
40
+ FileHelpers.create_work_class(lib_dir, class_name, file_name, module_name, is_applicable, init_body, perform)
41
+ end
42
+
43
+ def self.create_work_class(lib_dir, class_name, file_name, module_name, is_applicable, init_body, perform)
44
+ class_contents = %Q(
45
+ class #{class_name}
46
+ def initialize(service_def, app_manager)
47
+ #{init_body}
48
+ end
49
+ def perform(file_rec, event)
50
+ #{perform}
51
+ end
52
+ def is_applicable?(event)
53
+ #{is_applicable}
54
+ end
55
+ end
56
+ )
57
+ class_contents = "module #{module_name}\n#{class_contents}\nend" unless module_name.nil?
58
+ create_test_file(dir: lib_dir, name: file_name, content: class_contents)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,98 @@
1
+ require 'longleaf/models/md_fields'
2
+ require 'longleaf/helpers/service_date_helper'
3
+ require 'yaml'
4
+
5
+ module Longleaf
6
+ # Test helper for constructing file metadata records
7
+ class MetadataBuilder
8
+ MF ||= Longleaf::MDFields
9
+
10
+ def initialize(file_path: nil, registered: ServiceDateHelper::formatted_timestamp)
11
+ @data = Hash.new
12
+ @services = Hash.new
13
+
14
+ unless file_path.nil?
15
+ @last_modified = File.mtime(file_path).utc.iso8601(3)
16
+ @file_size = File.size(file_path)
17
+ end
18
+
19
+ @registered = registered
20
+ end
21
+
22
+ def deregistered(timestamp = ServiceDateHelper::formatted_timestamp)
23
+ @deregistered = timestamp
24
+ self
25
+ end
26
+
27
+ def with_checksum(alg, value)
28
+ @checksums = Hash.new unless @data.key?(MF::CHECKSUMS)
29
+ @checksums[alg] = value
30
+ self
31
+ end
32
+
33
+ def with_physical_path(phys_path)
34
+ @physical_path = phys_path
35
+ self
36
+ end
37
+
38
+ def with_service(name, timestamp: ServiceDateHelper::formatted_timestamp, run_needed: false, properties: nil,
39
+ failure_timestamp: nil)
40
+ timestamp = format_timestamp(timestamp)
41
+ failure_timestamp = format_timestamp(failure_timestamp) unless failure_timestamp.nil?
42
+
43
+ @services[name] = ServiceRecord.new(
44
+ properties: properties.nil? ? Hash.new : nil,
45
+ timestamp: timestamp,
46
+ run_needed: run_needed)
47
+ @services[name].failure_timestamp = failure_timestamp
48
+ self
49
+ end
50
+
51
+ def with_properties(properties)
52
+ @properties = properties
53
+ self
54
+ end
55
+
56
+ # @return the constructed metadata record
57
+ def get_metadata_record
58
+ MetadataRecord.new(properties: @properties,
59
+ services: @services,
60
+ deregistered: @deregistered,
61
+ registered: @registered,
62
+ checksums: @checksums,
63
+ file_size: @file_size,
64
+ last_modified: @last_modified,
65
+ physical_path: @physical_path)
66
+ end
67
+
68
+ # Add the generated metadata record to the given file record
69
+ def register_to(file_rec)
70
+ file_rec.metadata_record = get_metadata_record
71
+ self
72
+ end
73
+
74
+ # Writes the metadata record from this builder into a temporary file, or if a file
75
+ # record is provided, then to the expected metadata path for the record, and assigns
76
+ # the result as the metadata record for the file record.
77
+ # @return the file path of the config file
78
+ def write_to_yaml_file(file_rec: nil)
79
+ md_path = nil
80
+ if file_rec.nil?
81
+ md_path = TempFile.new(['metadata', 'yml']).path
82
+ else
83
+ md_path = file_rec.metadata_path
84
+ end
85
+
86
+ md_rec = get_metadata_record
87
+ MetadataSerializer::write(metadata: md_rec, file_path: md_path)
88
+ file_rec.metadata_record = md_rec
89
+
90
+ md_path
91
+ end
92
+
93
+ private
94
+ def format_timestamp(timestamp)
95
+ timestamp.kind_of?(Time) ? ServiceDateHelper::formatted_timestamp(timestamp) : timestamp
96
+ end
97
+ end
98
+ end