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.
- 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 +139 -0
- data/lib/longleaf/candidates/manifest_digest_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 +237 -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 +32 -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 +81 -0
- data/lib/longleaf/events/register_event.rb +52 -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 +189 -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 +26 -25
- data/lib/longleaf/models/app_fields.rb +7 -2
- data/lib/longleaf/models/file_record.rb +17 -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 +2 -1
- data/lib/longleaf/models/metadata_location.rb +47 -0
- data/lib/longleaf/models/metadata_record.rb +39 -15
- data/lib/longleaf/models/s3_storage_location.rb +133 -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 +58 -0
- data/lib/longleaf/preservation_services/fixity_check_service.rb +123 -0
- data/lib/longleaf/preservation_services/rsync_replication_service.rb +182 -0
- data/lib/longleaf/preservation_services/s3_replication_service.rb +143 -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 +113 -42
- data/lib/longleaf/services/metadata_persistence_manager.rb +47 -0
- data/lib/longleaf/services/metadata_serializer.rb +138 -25
- 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 +92 -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 +306 -23
- 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,49 +1,59 @@
|
|
|
1
1
|
require 'pathname'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
15
|
-
|
|
16
|
-
assert("Configuration must
|
|
17
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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/
|
|
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
|