longleaf 0.1.0.pre.2
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/longleaf +3 -0
- data/lib/longleaf/cli.rb +78 -0
- data/lib/longleaf/commands/abstract_command.rb +37 -0
- data/lib/longleaf/commands/register_command.rb +59 -0
- data/lib/longleaf/commands/validate_config_command.rb +29 -0
- data/lib/longleaf/errors.rb +15 -0
- data/lib/longleaf/events/register_event.rb +98 -0
- data/lib/longleaf/logging/redirecting_logger.rb +131 -0
- data/lib/longleaf/logging.rb +26 -0
- data/lib/longleaf/models/app_fields.rb +10 -0
- data/lib/longleaf/models/file_record.rb +25 -0
- data/lib/longleaf/models/md_fields.rb +18 -0
- data/lib/longleaf/models/metadata_record.rb +57 -0
- data/lib/longleaf/models/service_definition.rb +21 -0
- data/lib/longleaf/models/service_fields.rb +10 -0
- data/lib/longleaf/models/service_record.rb +27 -0
- data/lib/longleaf/models/storage_location.rb +37 -0
- data/lib/longleaf/services/application_config_deserializer.rb +46 -0
- data/lib/longleaf/services/application_config_manager.rb +24 -0
- data/lib/longleaf/services/application_config_validator.rb +18 -0
- data/lib/longleaf/services/configuration_validator.rb +8 -0
- data/lib/longleaf/services/metadata_deserializer.rb +68 -0
- data/lib/longleaf/services/metadata_serializer.rb +76 -0
- data/lib/longleaf/services/service_definition_manager.rb +36 -0
- data/lib/longleaf/services/service_definition_validator.rb +32 -0
- data/lib/longleaf/services/service_manager.rb +21 -0
- data/lib/longleaf/services/service_mapping_manager.rb +47 -0
- data/lib/longleaf/services/service_mapping_validator.rb +49 -0
- data/lib/longleaf/services/storage_location_manager.rb +37 -0
- data/lib/longleaf/services/storage_location_validator.rb +60 -0
- data/lib/longleaf/services/storage_path_validator.rb +16 -0
- data/lib/longleaf/specs/config_builder.rb +102 -0
- data/lib/longleaf/version.rb +3 -0
- data/lib/longleaf.rb +4 -0
- data/longleaf.gemspec +34 -0
- metadata +188 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'longleaf/models/storage_location'
|
3
|
+
require 'longleaf/models/app_fields'
|
4
|
+
require 'longleaf/errors'
|
5
|
+
require_relative 'configuration_validator'
|
6
|
+
require 'longleaf/services/storage_path_validator'
|
7
|
+
|
8
|
+
# Validates application configuration of storage locations
|
9
|
+
module Longleaf
|
10
|
+
class StorageLocationValidator < ConfigurationValidator
|
11
|
+
AF ||= Longleaf::AppFields
|
12
|
+
|
13
|
+
# Validates configuration to ensure that it is syntactically correct and does not violate
|
14
|
+
# schema and uniqueness requirements.
|
15
|
+
# @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]
|
20
|
+
assert("'#{AF::LOCATIONS}' must be a hash of locations", locations.class == Hash)
|
21
|
+
|
22
|
+
existing_paths = Array.new
|
23
|
+
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)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def self.assert_path_property_valid(name, path_prop, properties, existing_paths)
|
34
|
+
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?('/..'))
|
44
|
+
# Ensure paths have trailing slash to avoid matching on partial directory names
|
45
|
+
path += '/' unless path.end_with?('/')
|
46
|
+
# Verify that the (metadata_)path property's value is not inside of another storage location or vice versa
|
47
|
+
existing_paths.each do |existing|
|
48
|
+
if existing.start_with?(path) || path.start_with?(existing)
|
49
|
+
msg = "Location '#{name}' defines property #{path_prop} with value '#{path}'" \
|
50
|
+
" which overlaps with another configured path '#{existing}'." \
|
51
|
+
" Storage locations must not define #{AF::LOCATION_PATH} or #{AF::METADATA_PATH}" \
|
52
|
+
" properties which are contained by another location property"
|
53
|
+
raise ConfigurationError.new(msg)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
existing_paths << path
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'longleaf/errors'
|
2
|
+
|
3
|
+
# Validator for storage paths
|
4
|
+
module Longleaf
|
5
|
+
class StoragePathValidator
|
6
|
+
# Checks that the given path is a syntactically valid storage path
|
7
|
+
# @param path [String] file storage path to validate
|
8
|
+
# @raises [InvalidStoragePathError]
|
9
|
+
def self.validate(path)
|
10
|
+
raise InvalidStoragePathError.new("Path must not be empty") if path.to_s.strip.empty?
|
11
|
+
raise InvalidStoragePathError.new("Path must be absolute") unless Pathname.new(path).absolute?
|
12
|
+
raise InvalidStoragePathError.new("Path must not contain any relative modifiers (/..)") \
|
13
|
+
if path.include?('/..')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require_relative '../models/app_fields'
|
2
|
+
require_relative '../models/service_fields'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
# Test helper for constructing application configuration hashes
|
6
|
+
module Longleaf
|
7
|
+
class ConfigBuilder
|
8
|
+
AF ||= Longleaf::AppFields
|
9
|
+
SF ||= Longleaf::ServiceFields
|
10
|
+
|
11
|
+
attr_accessor :config
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@config = Hash.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add a root 'locations' field to the config
|
18
|
+
# @param locations [Hash] value for the locations fields. Default is {}
|
19
|
+
# @return this builder
|
20
|
+
def with_locations(locations = Hash.new)
|
21
|
+
@config[AF::LOCATIONS] = locations
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add a 'location' to the config
|
26
|
+
# @param name [String] name of the location
|
27
|
+
# @param path [String] value for the 'path' field
|
28
|
+
# @param md_path [String] value for the 'metadata_path' field
|
29
|
+
# @return this builder
|
30
|
+
def with_location(name:, path: '/file/path/', md_path: '/metadata/path/')
|
31
|
+
@config[AF::LOCATIONS] = Hash.new unless @config.key?(AF::LOCATIONS)
|
32
|
+
|
33
|
+
location = {}
|
34
|
+
@config[AF::LOCATIONS][name] = location
|
35
|
+
location[AF::LOCATION_PATH] = path unless path.nil?
|
36
|
+
location[AF::METADATA_PATH] = md_path unless md_path.nil?
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add a root 'services' field to the config
|
41
|
+
# @param services [Hash] value for the services field. Default is {}
|
42
|
+
# @return this builder
|
43
|
+
def with_services(services = Hash.new)
|
44
|
+
@config[AF::SERVICES] = services
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add a 'service' to the config
|
49
|
+
# @param name [String] name of the service
|
50
|
+
# @param work_script [String] value for the 'work_script' field
|
51
|
+
# @param frequency [String] value for the 'frequency' field
|
52
|
+
# @param delay [String] value for the 'delay' field
|
53
|
+
# @param properties [Hash] hash of additional properties to include in the service
|
54
|
+
# @return this builder
|
55
|
+
def with_service(name:, work_script: 'some_pres_service.rb', frequency: nil, delay: nil, properties: nil)
|
56
|
+
@config[AF::SERVICES] = Hash.new unless @config.key?(AF::SERVICES)
|
57
|
+
|
58
|
+
service = {}
|
59
|
+
service[SF::WORK_SCRIPT] = work_script
|
60
|
+
service[SF::FREQUENCY] = frequency unless frequency.nil?
|
61
|
+
service[SF::DELAY] = delay unless delay.nil?
|
62
|
+
service = service.merge(properties) unless properties.nil?
|
63
|
+
@config[AF::SERVICES][name] = service
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Adds a 'service_mappings' section to the config
|
68
|
+
# @param mappings [Object] the mappings config
|
69
|
+
# @return this builder
|
70
|
+
def with_mappings(mappings = Hash.new)
|
71
|
+
@config[AF::SERVICE_MAPPINGS] = mappings
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Add a mapping from one or more services to one or more location
|
76
|
+
# @param loc_names [Object] one or more location names. Can be a string or array.
|
77
|
+
# @param service_names [Object] one or more service names. Can be a string or array.
|
78
|
+
def map_services(loc_names, service_names)
|
79
|
+
@config[AF::SERVICE_MAPPINGS] = Array.new unless @config.key?(AF::SERVICE_MAPPINGS)
|
80
|
+
|
81
|
+
mapping = Hash.new
|
82
|
+
mapping[AF::LOCATIONS] = loc_names unless loc_names.nil?
|
83
|
+
mapping[AF::SERVICES] = service_names unless service_names.nil?
|
84
|
+
@config[AF::SERVICE_MAPPINGS].push(mapping)
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return the constructed configuration
|
89
|
+
def get
|
90
|
+
@config
|
91
|
+
end
|
92
|
+
|
93
|
+
# Writes the configuration from this builder into a temporary file
|
94
|
+
# @return the file path of the config file
|
95
|
+
def write_to_yaml_file
|
96
|
+
Tempfile.open('config') do |f|
|
97
|
+
f.write(@config.to_yaml)
|
98
|
+
return f.path
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/longleaf.rb
ADDED
data/longleaf.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'longleaf/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "longleaf"
|
8
|
+
spec.version = Longleaf::VERSION
|
9
|
+
spec.authors = ["bbpennel"]
|
10
|
+
spec.email = ["bbpennel@email.unc.edu"]
|
11
|
+
|
12
|
+
spec.summary = %q{Longleaf preservation services tool}
|
13
|
+
spec.description = %q{Provides a framework for performing preservation services over sets of files.}
|
14
|
+
spec.homepage = "https://github.com/UNC-Libraries/"
|
15
|
+
spec.license = "Apache-2.0"
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = "longleaf"
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency "thor", "~> 0.20.0"
|
27
|
+
spec.add_dependency "yard", "~> 0.9.16"
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
30
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
31
|
+
spec.add_development_dependency "rspec", "~> 3.6.0"
|
32
|
+
spec.add_development_dependency "factory_bot", "~> 4.0"
|
33
|
+
spec.add_development_dependency "aruba", "~> 0.14.0"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: longleaf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.pre.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- bbpennel
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.20.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.20.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: yard
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.16
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.9.16
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.16'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.16'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '12.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '12.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.6.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.6.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: factory_bot
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: aruba
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.14.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.14.0
|
111
|
+
description: Provides a framework for performing preservation services over sets of
|
112
|
+
files.
|
113
|
+
email:
|
114
|
+
- bbpennel@email.unc.edu
|
115
|
+
executables:
|
116
|
+
- longleaf
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".gitignore"
|
121
|
+
- ".rspec"
|
122
|
+
- ".travis.yml"
|
123
|
+
- Gemfile
|
124
|
+
- LICENSE.txt
|
125
|
+
- README.md
|
126
|
+
- Rakefile
|
127
|
+
- bin/console
|
128
|
+
- bin/setup
|
129
|
+
- exe/longleaf
|
130
|
+
- lib/longleaf.rb
|
131
|
+
- lib/longleaf/cli.rb
|
132
|
+
- lib/longleaf/commands/abstract_command.rb
|
133
|
+
- lib/longleaf/commands/register_command.rb
|
134
|
+
- lib/longleaf/commands/validate_config_command.rb
|
135
|
+
- lib/longleaf/errors.rb
|
136
|
+
- lib/longleaf/events/register_event.rb
|
137
|
+
- lib/longleaf/logging.rb
|
138
|
+
- lib/longleaf/logging/redirecting_logger.rb
|
139
|
+
- lib/longleaf/models/app_fields.rb
|
140
|
+
- lib/longleaf/models/file_record.rb
|
141
|
+
- lib/longleaf/models/md_fields.rb
|
142
|
+
- lib/longleaf/models/metadata_record.rb
|
143
|
+
- lib/longleaf/models/service_definition.rb
|
144
|
+
- lib/longleaf/models/service_fields.rb
|
145
|
+
- lib/longleaf/models/service_record.rb
|
146
|
+
- lib/longleaf/models/storage_location.rb
|
147
|
+
- lib/longleaf/services/application_config_deserializer.rb
|
148
|
+
- lib/longleaf/services/application_config_manager.rb
|
149
|
+
- lib/longleaf/services/application_config_validator.rb
|
150
|
+
- lib/longleaf/services/configuration_validator.rb
|
151
|
+
- lib/longleaf/services/metadata_deserializer.rb
|
152
|
+
- lib/longleaf/services/metadata_serializer.rb
|
153
|
+
- lib/longleaf/services/service_definition_manager.rb
|
154
|
+
- lib/longleaf/services/service_definition_validator.rb
|
155
|
+
- lib/longleaf/services/service_manager.rb
|
156
|
+
- lib/longleaf/services/service_mapping_manager.rb
|
157
|
+
- lib/longleaf/services/service_mapping_validator.rb
|
158
|
+
- lib/longleaf/services/storage_location_manager.rb
|
159
|
+
- lib/longleaf/services/storage_location_validator.rb
|
160
|
+
- lib/longleaf/services/storage_path_validator.rb
|
161
|
+
- lib/longleaf/specs/config_builder.rb
|
162
|
+
- lib/longleaf/version.rb
|
163
|
+
- longleaf.gemspec
|
164
|
+
homepage: https://github.com/UNC-Libraries/
|
165
|
+
licenses:
|
166
|
+
- Apache-2.0
|
167
|
+
metadata: {}
|
168
|
+
post_install_message:
|
169
|
+
rdoc_options: []
|
170
|
+
require_paths:
|
171
|
+
- lib
|
172
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
177
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">"
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 1.3.1
|
182
|
+
requirements: []
|
183
|
+
rubyforge_project:
|
184
|
+
rubygems_version: 2.7.6
|
185
|
+
signing_key:
|
186
|
+
specification_version: 4
|
187
|
+
summary: Longleaf preservation services tool
|
188
|
+
test_files: []
|