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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +94 -0
- data/.editorconfig +13 -0
- data/.gitignore +4 -1
- data/.rubocop.yml +44 -0
- data/.rubocop_todo.yml +834 -0
- data/.yardopts +1 -0
- data/Gemfile +16 -1
- data/README.md +98 -12
- data/Rakefile +6 -0
- data/bin/setup +16 -1
- data/docs/aboutlongleaf.md +28 -0
- data/docs/extra.css +32 -0
- data/docs/img/change-file.png +0 -0
- data/docs/img/ll-example-preserved.png +0 -0
- data/docs/index.md +19 -0
- data/docs/install.md +66 -0
- data/docs/ll-example/config-example-relative.yml +33 -0
- data/docs/ll-example/files-dir/LLexample-PDF.pdf +0 -0
- data/docs/ll-example/files-dir/LLexample-TOCHANGE.txt +15 -0
- data/docs/ll-example/files-dir/LLexample-tokeep.txt +10 -0
- data/docs/ll-example/metadata-dir/.gitkeep +0 -0
- data/docs/ll-example/replica-files/.gitkeep +0 -0
- data/docs/ll-example/replica-metadata/.gitkeep +0 -0
- data/docs/quickstart.md +270 -0
- data/docs/rdocs/Longleaf.html +135 -0
- data/docs/rdocs/Longleaf/AppFields.html +178 -0
- data/docs/rdocs/Longleaf/ApplicationConfigDeserializer.html +631 -0
- data/docs/rdocs/Longleaf/ApplicationConfigManager.html +610 -0
- data/docs/rdocs/Longleaf/ApplicationConfigValidator.html +238 -0
- data/docs/rdocs/Longleaf/CLI.html +909 -0
- data/docs/rdocs/Longleaf/ChecksumMismatchError.html +151 -0
- data/docs/rdocs/Longleaf/ConfigBuilder.html +1339 -0
- data/docs/rdocs/Longleaf/ConfigurationError.html +143 -0
- data/docs/rdocs/Longleaf/ConfigurationValidator.html +227 -0
- data/docs/rdocs/Longleaf/DeregisterCommand.html +420 -0
- data/docs/rdocs/Longleaf/DeregisterEvent.html +453 -0
- data/docs/rdocs/Longleaf/DeregistrationError.html +151 -0
- data/docs/rdocs/Longleaf/DigestHelper.html +419 -0
- data/docs/rdocs/Longleaf/EventError.html +147 -0
- data/docs/rdocs/Longleaf/EventNames.html +163 -0
- data/docs/rdocs/Longleaf/EventStatusTracking.html +656 -0
- data/docs/rdocs/Longleaf/FileCheckService.html +540 -0
- data/docs/rdocs/Longleaf/FileHelpers.html +520 -0
- data/docs/rdocs/Longleaf/FileRecord.html +716 -0
- data/docs/rdocs/Longleaf/FileSelector.html +901 -0
- data/docs/rdocs/Longleaf/FixityCheckService.html +691 -0
- data/docs/rdocs/Longleaf/IndexManager.html +1155 -0
- data/docs/rdocs/Longleaf/InvalidDigestAlgorithmError.html +143 -0
- data/docs/rdocs/Longleaf/InvalidStoragePathError.html +143 -0
- data/docs/rdocs/Longleaf/Logging.html +405 -0
- data/docs/rdocs/Longleaf/Logging/RedirectingLogger.html +1213 -0
- data/docs/rdocs/Longleaf/LongleafError.html +139 -0
- data/docs/rdocs/Longleaf/MDFields.html +193 -0
- data/docs/rdocs/Longleaf/MetadataBuilder.html +787 -0
- data/docs/rdocs/Longleaf/MetadataDeserializer.html +537 -0
- data/docs/rdocs/Longleaf/MetadataError.html +143 -0
- data/docs/rdocs/Longleaf/MetadataPersistenceManager.html +539 -0
- data/docs/rdocs/Longleaf/MetadataRecord.html +1411 -0
- data/docs/rdocs/Longleaf/MetadataSerializer.html +786 -0
- data/docs/rdocs/Longleaf/PreservationServiceError.html +147 -0
- data/docs/rdocs/Longleaf/PreserveCommand.html +410 -0
- data/docs/rdocs/Longleaf/PreserveEvent.html +491 -0
- data/docs/rdocs/Longleaf/RegisterCommand.html +428 -0
- data/docs/rdocs/Longleaf/RegisterEvent.html +628 -0
- data/docs/rdocs/Longleaf/RegisteredFileSelector.html +446 -0
- data/docs/rdocs/Longleaf/RegistrationError.html +151 -0
- data/docs/rdocs/Longleaf/ReindexCommand.html +576 -0
- data/docs/rdocs/Longleaf/RsyncReplicationService.html +1180 -0
- data/docs/rdocs/Longleaf/SequelIndexDriver.html +1978 -0
- data/docs/rdocs/Longleaf/ServiceCandidateFilesystemIterator.html +572 -0
- data/docs/rdocs/Longleaf/ServiceCandidateIndexIterator.html +532 -0
- data/docs/rdocs/Longleaf/ServiceCandidateLocator.html +333 -0
- data/docs/rdocs/Longleaf/ServiceClassCache.html +725 -0
- data/docs/rdocs/Longleaf/ServiceDateHelper.html +425 -0
- data/docs/rdocs/Longleaf/ServiceDefinition.html +683 -0
- data/docs/rdocs/Longleaf/ServiceDefinitionManager.html +371 -0
- data/docs/rdocs/Longleaf/ServiceDefinitionValidator.html +269 -0
- data/docs/rdocs/Longleaf/ServiceFields.html +173 -0
- data/docs/rdocs/Longleaf/ServiceManager.html +1229 -0
- data/docs/rdocs/Longleaf/ServiceMappingManager.html +410 -0
- data/docs/rdocs/Longleaf/ServiceMappingValidator.html +347 -0
- data/docs/rdocs/Longleaf/ServiceRecord.html +821 -0
- data/docs/rdocs/Longleaf/StorageLocation.html +985 -0
- data/docs/rdocs/Longleaf/StorageLocationManager.html +729 -0
- data/docs/rdocs/Longleaf/StorageLocationUnavailableError.html +143 -0
- data/docs/rdocs/Longleaf/StorageLocationValidator.html +373 -0
- data/docs/rdocs/Longleaf/StoragePathValidator.html +253 -0
- data/docs/rdocs/Longleaf/SystemConfigBuilder.html +441 -0
- data/docs/rdocs/Longleaf/SystemConfigFields.html +163 -0
- data/docs/rdocs/Longleaf/ValidateConfigCommand.html +451 -0
- data/docs/rdocs/Longleaf/ValidateMetadataCommand.html +408 -0
- data/docs/rdocs/_index.html +660 -0
- data/docs/rdocs/class_list.html +51 -0
- data/docs/rdocs/css/common.css +1 -0
- data/docs/rdocs/css/full_list.css +58 -0
- data/docs/rdocs/css/style.css +496 -0
- data/docs/rdocs/file.README.html +165 -0
- data/docs/rdocs/file_list.html +56 -0
- data/docs/rdocs/frames.html +17 -0
- data/docs/rdocs/index.html +165 -0
- data/docs/rdocs/js/app.js +303 -0
- data/docs/rdocs/js/full_list.js +216 -0
- data/docs/rdocs/js/jquery.js +4 -0
- data/docs/rdocs/method_list.html +2051 -0
- data/docs/rdocs/top-level-namespace.html +110 -0
- data/lib/longleaf/candidates/file_selector.rb +150 -0
- data/lib/longleaf/candidates/manifest_digest_provider.rb +17 -0
- data/lib/longleaf/candidates/physical_path_provider.rb +17 -0
- data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
- data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +93 -0
- data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
- data/lib/longleaf/candidates/service_candidate_locator.rb +23 -0
- data/lib/longleaf/candidates/single_digest_provider.rb +13 -0
- data/lib/longleaf/cli.rb +252 -46
- data/lib/longleaf/commands/deregister_command.rb +51 -0
- data/lib/longleaf/commands/preserve_command.rb +50 -0
- data/lib/longleaf/commands/register_command.rb +34 -43
- data/lib/longleaf/commands/reindex_command.rb +92 -0
- data/lib/longleaf/commands/validate_config_command.rb +33 -8
- data/lib/longleaf/commands/validate_metadata_command.rb +51 -0
- data/lib/longleaf/errors.rb +26 -7
- data/lib/longleaf/events/deregister_event.rb +53 -0
- data/lib/longleaf/events/event_names.rb +9 -0
- data/lib/longleaf/events/event_status_tracking.rb +59 -0
- data/lib/longleaf/events/preserve_event.rb +82 -0
- data/lib/longleaf/events/register_event.rb +59 -51
- data/lib/longleaf/helpers/case_insensitive_hash.rb +38 -0
- data/lib/longleaf/helpers/digest_helper.rb +56 -0
- data/lib/longleaf/helpers/s3_uri_helper.rb +86 -0
- data/lib/longleaf/helpers/selection_options_parser.rb +215 -0
- data/lib/longleaf/helpers/service_date_helper.rb +78 -0
- data/lib/longleaf/indexing/index_manager.rb +101 -0
- data/lib/longleaf/indexing/sequel_index_driver.rb +306 -0
- data/lib/longleaf/logging.rb +5 -4
- data/lib/longleaf/logging/redirecting_logger.rb +30 -25
- data/lib/longleaf/models/app_fields.rb +7 -2
- data/lib/longleaf/models/file_record.rb +31 -8
- data/lib/longleaf/models/filesystem_metadata_location.rb +56 -0
- data/lib/longleaf/models/filesystem_storage_location.rb +52 -0
- data/lib/longleaf/models/md_fields.rb +3 -1
- data/lib/longleaf/models/metadata_location.rb +47 -0
- data/lib/longleaf/models/metadata_record.rb +43 -16
- data/lib/longleaf/models/s3_storage_location.rb +138 -0
- data/lib/longleaf/models/service_definition.rb +7 -6
- data/lib/longleaf/models/service_fields.rb +7 -1
- data/lib/longleaf/models/service_record.rb +10 -6
- data/lib/longleaf/models/storage_location.rb +24 -19
- data/lib/longleaf/models/storage_types.rb +9 -0
- data/lib/longleaf/models/system_config_fields.rb +9 -0
- data/lib/longleaf/preservation_services/file_check_service.rb +59 -0
- data/lib/longleaf/preservation_services/fixity_check_service.rb +124 -0
- data/lib/longleaf/preservation_services/rsync_replication_service.rb +198 -0
- data/lib/longleaf/preservation_services/s3_replication_service.rb +131 -0
- data/lib/longleaf/services/application_config_deserializer.rb +81 -24
- data/lib/longleaf/services/application_config_manager.rb +20 -6
- data/lib/longleaf/services/application_config_validator.rb +19 -9
- data/lib/longleaf/services/configuration_validator.rb +67 -4
- data/lib/longleaf/services/filesystem_location_validator.rb +16 -0
- data/lib/longleaf/services/metadata_deserializer.rb +115 -42
- data/lib/longleaf/services/metadata_persistence_manager.rb +47 -0
- data/lib/longleaf/services/metadata_serializer.rb +156 -23
- data/lib/longleaf/services/metadata_validator.rb +76 -0
- data/lib/longleaf/services/s3_location_validator.rb +19 -0
- data/lib/longleaf/services/service_class_cache.rb +112 -0
- data/lib/longleaf/services/service_definition_manager.rb +10 -7
- data/lib/longleaf/services/service_definition_validator.rb +25 -18
- data/lib/longleaf/services/service_manager.rb +86 -11
- data/lib/longleaf/services/service_mapping_manager.rb +13 -12
- data/lib/longleaf/services/service_mapping_validator.rb +36 -26
- data/lib/longleaf/services/storage_location_manager.rb +76 -15
- data/lib/longleaf/services/storage_location_validator.rb +49 -35
- data/lib/longleaf/specs/config_builder.rb +47 -23
- data/lib/longleaf/specs/config_validator_helpers.rb +16 -0
- data/lib/longleaf/specs/custom_matchers.rb +9 -0
- data/lib/longleaf/specs/file_helpers.rb +61 -0
- data/lib/longleaf/specs/metadata_builder.rb +98 -0
- data/lib/longleaf/specs/system_config_builder.rb +27 -0
- data/lib/longleaf/version.rb +1 -1
- data/longleaf.gemspec +20 -7
- data/mkdocs.yml +21 -0
- metadata +308 -24
- data/.travis.yml +0 -4
- data/lib/longleaf/commands/abstract_command.rb +0 -37
- 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/
|
|
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
|
-
|
|
14
|
-
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
51
|
+
def assert_path_property_valid(name, path_prop, properties, section_name)
|
|
34
52
|
path = properties[path_prop]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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}
|
|
65
|
+
" Storage locations must not define #{AF::LOCATION_PATH}" \
|
|
52
66
|
" properties which are contained by another location property"
|
|
53
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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::
|
|
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',
|
|
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
|
-
|
|
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
|