longleaf 0.1.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 41b149cc264dcc00b79558d1acc06fd42c7810e8b06fe62627baf359fb23a784
|
4
|
+
data.tar.gz: f4634c72c87d9858faace5e8c480d96b85365a37e989addfac8d2a044b957d08
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 07aa3758499e04ff45f1fcbf1bd9e51af22b0d64ef4314ba784dc1eb0fae78bffeb6faf285e0fbe107761cf6c0c89dd024b814eb6a130821718a53d985eebe47
|
7
|
+
data.tar.gz: 4546b08c4e3119f783bd7134b0a49ee7cb0f022509829122b136f872fad95b38c8c31a4db1eed021d51adb639e4cf63c2972cfeeeeaeda8cef194b026fd9051b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2018 UNC Libraries
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Longleaf
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/longleaf`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'longleaf'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install longleaf
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
#### Validate configuration files
|
26
|
+
Application configuration files can be validated prior to usage with the following command:
|
27
|
+
`longleaf validate_config <config.yml>`
|
28
|
+
|
29
|
+
## Development
|
30
|
+
|
31
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
32
|
+
|
33
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/longleaf.
|
38
|
+
|
39
|
+
|
40
|
+
## License
|
41
|
+
|
42
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
43
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "longleaf"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/longleaf
ADDED
data/lib/longleaf/cli.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'yaml'
|
3
|
+
require 'longleaf/logging'
|
4
|
+
require 'longleaf/errors'
|
5
|
+
require 'longleaf/commands/validate_config_command'
|
6
|
+
require 'longleaf/commands/register_command'
|
7
|
+
|
8
|
+
module Longleaf
|
9
|
+
class CLI < Thor
|
10
|
+
include Longleaf::Logging
|
11
|
+
|
12
|
+
class_option(:config, :aliases => "-c",
|
13
|
+
:default => ENV['LONGLEAF_CFG'],
|
14
|
+
:desc => 'Absolute path to the application configuration used for this command. By default, the value of the environment variable LONGLEAF_CFG is used.')
|
15
|
+
# Logging/output options
|
16
|
+
class_option(:failure_only,
|
17
|
+
:type => :boolean,
|
18
|
+
:default => false,
|
19
|
+
:desc => 'Only display failure messages to STDOUT.')
|
20
|
+
class_option(:log_level,
|
21
|
+
:default => 'WARN',
|
22
|
+
:desc => 'Level of logging to send to STDERR, following standard ruby Logger levels. This includes: DEBUG, INFO, WARN, ERROR, FATAL, UNKNOWN. Default is WARN.')
|
23
|
+
class_option(:log_format,
|
24
|
+
:desc => 'Format to use for log information sent to STDERR. Can contain the following parameters, which must be wrapped in %{}: severity, datetime, progname, msg. Default is "%{severity} [%{datetime}]: %{msg}"')
|
25
|
+
class_option(:log_datetime,
|
26
|
+
:desc => 'Format to use for timestamps used in logging to STDERR, following strftime syntax.')
|
27
|
+
|
28
|
+
desc "register", "Register files with Longleaf"
|
29
|
+
method_option(:file, :aliases => "-f",
|
30
|
+
:required => true,
|
31
|
+
:desc => 'File or files to register. Paths must be absolute. If multiple files are provided, they must be comma separated.')
|
32
|
+
method_option(:force,
|
33
|
+
:type => :boolean,
|
34
|
+
:default => false,
|
35
|
+
:desc => 'Force the registration of already registered files.')
|
36
|
+
method_option(:checksums,
|
37
|
+
:desc => %q{Checksums for the submitted file. Each checksum must be prefaced with an algorithm prefix. Multiple checksums must be comma separated. If multiple files were submitted, they will be provided with the same checksums. For example:
|
38
|
+
'--checksums "md5:d8e8fca2dc0f896fd7cb4cb0031ba249,sha1:4e1243bd22c66e76c2ba9eddc1f91394e57f9f83"'})
|
39
|
+
# Register event command
|
40
|
+
def register
|
41
|
+
setup_logger(options)
|
42
|
+
|
43
|
+
config_path = options[:config]
|
44
|
+
file_paths = options[:file]&.split(/\s*,\s*/)
|
45
|
+
if options[:checksums]
|
46
|
+
checksums = options[:checksums]
|
47
|
+
# validate checksum list format, must a comma delimited list of prefix:checksums
|
48
|
+
if /^[^:,]+:[^:,]+(,[^:,]+:[^:,]+)*$/.match(checksums)
|
49
|
+
# convert checksum list into hash with prefix as key
|
50
|
+
checksums = Hash[*checksums.split(/\s*[:,]\s*/)]
|
51
|
+
else
|
52
|
+
logger.failure("Invalid checksums parameter format, see `longleaf help <command>` for more information")
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
command = Longleaf::RegisterCommand.new(config_path)
|
58
|
+
exit command.execute(file_paths: file_paths, force: options[:force], checksums: checksums)
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "validate_config [CONFIG_PATH]", "Validate an application configuration file"
|
62
|
+
# Application configuration validation command
|
63
|
+
def validate_config(config_path)
|
64
|
+
setup_logger(options)
|
65
|
+
|
66
|
+
exit Longleaf::ValidateConfigCommand.new(config_path).execute
|
67
|
+
end
|
68
|
+
|
69
|
+
no_commands do
|
70
|
+
def setup_logger(options)
|
71
|
+
initialize_logger(options[:failure_only],
|
72
|
+
options[:log_level],
|
73
|
+
options[:log_format],
|
74
|
+
options[:log_datetime])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'longleaf/logging'
|
2
|
+
|
3
|
+
# Parent class for longleaf commands
|
4
|
+
module Longleaf
|
5
|
+
class AbstractCommand
|
6
|
+
include Longleaf::Logging
|
7
|
+
|
8
|
+
# Record a successful operation to the output and the overall status of this command.
|
9
|
+
# @param args [Array] arguments to pass to logger
|
10
|
+
def record_success(*args)
|
11
|
+
logger.success(*args)
|
12
|
+
if @return_status.nil? || @return_status == 0
|
13
|
+
@return_status = 0
|
14
|
+
else
|
15
|
+
@return_status = 2
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Record a failed operation to the output and the overall status of this command.
|
20
|
+
# @param args [Array] arguments to pass to logger
|
21
|
+
def record_failure(*args)
|
22
|
+
logger.failure(*args)
|
23
|
+
if @return_status.nil? || @return_status == 1
|
24
|
+
@return_status = 1
|
25
|
+
else
|
26
|
+
@return_status = 2
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Integer] the return status for this command, where 0 indicates success,
|
31
|
+
# 1 indicates failure, and 2 indicates partial failure
|
32
|
+
def return_status
|
33
|
+
@return_status = 0 if @return_status.nil?
|
34
|
+
@return_status
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'longleaf/services/application_config_deserializer'
|
2
|
+
require 'longleaf/events/register_event'
|
3
|
+
require 'longleaf/models/file_record'
|
4
|
+
require 'longleaf/commands/abstract_command'
|
5
|
+
|
6
|
+
# Command for registering files with longleaf
|
7
|
+
module Longleaf
|
8
|
+
class RegisterCommand < AbstractCommand
|
9
|
+
|
10
|
+
def initialize(config_path)
|
11
|
+
@config_path = config_path
|
12
|
+
end
|
13
|
+
|
14
|
+
# Execute the register command on the given parameters
|
15
|
+
def execute(file_paths: nil, force: false, checksums: nil)
|
16
|
+
if file_paths.nil? || file_paths.empty?
|
17
|
+
record_failure("Must provide one or more file paths to register")
|
18
|
+
return return_status
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
# Retrieve the application configuration
|
23
|
+
app_manager = Longleaf::ApplicationConfigDeserializer.deserialize(@config_path)
|
24
|
+
|
25
|
+
# Perform register events on each of the file paths provided
|
26
|
+
file_paths.each do |f_path|
|
27
|
+
begin
|
28
|
+
storage_location = app_manager.location_manager.get_location_by_path(f_path)
|
29
|
+
if storage_location.nil?
|
30
|
+
raise InvalidStoragePathError.new(
|
31
|
+
"Unable to register '#{f_path}', it does not belong to any registered storage locations.")
|
32
|
+
end
|
33
|
+
|
34
|
+
raise InvalidStoragePathError.new("Unable to register '#{f_path}', file does not exist or is unreachable.") \
|
35
|
+
unless File.file?(f_path)
|
36
|
+
|
37
|
+
file_rec = FileRecord.new(f_path, storage_location)
|
38
|
+
|
39
|
+
register_event = RegisterEvent.new(file_rec: file_rec, force: force, app_manager: app_manager,
|
40
|
+
checksums: checksums)
|
41
|
+
register_event.perform
|
42
|
+
|
43
|
+
record_success(RegisterEvent::EVENT_NAME, f_path)
|
44
|
+
rescue RegistrationError => err
|
45
|
+
record_failure(RegisterEvent::EVENT_NAME, f_path, err.message)
|
46
|
+
rescue InvalidStoragePathError => err
|
47
|
+
record_failure(RegisterEvent::EVENT_NAME, f_path, err.message)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
rescue ConfigurationError => err
|
51
|
+
record_failure("Failed to load application configuration due to the following issue:\n#{err.message}")
|
52
|
+
rescue => err
|
53
|
+
record_failure(RegisterEvent::EVENT_NAME, error: err)
|
54
|
+
end
|
55
|
+
|
56
|
+
return_status
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'longleaf/services/application_config_deserializer'
|
2
|
+
require 'longleaf/commands/abstract_command'
|
3
|
+
|
4
|
+
module Longleaf
|
5
|
+
class ValidateConfigCommand < AbstractCommand
|
6
|
+
def initialize(config_path)
|
7
|
+
@config_path = config_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
begin
|
12
|
+
app_config_manager = Longleaf::ApplicationConfigDeserializer.deserialize(@config_path)
|
13
|
+
|
14
|
+
location_manager = app_config_manager.location_manager
|
15
|
+
location_manager.locations.each do |name, location|
|
16
|
+
location.available?
|
17
|
+
end
|
18
|
+
|
19
|
+
record_success("Application configuration passed validation: #{@config_path}")
|
20
|
+
rescue Longleaf::ConfigurationError, Longleaf::StorageLocationUnavailableError => err
|
21
|
+
record_failure("Application configuration invalid due to the following issue:\n#{err.message}")
|
22
|
+
rescue => err
|
23
|
+
record_failure("Failed to validate application configuration", error: err)
|
24
|
+
end
|
25
|
+
|
26
|
+
return_status
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Longleaf
|
2
|
+
class LongleafError < StandardError; end
|
3
|
+
|
4
|
+
class ConfigurationError < LongleafError; end
|
5
|
+
|
6
|
+
class InvalidStoragePathError < LongleafError; end
|
7
|
+
|
8
|
+
class MetadataError < LongleafError; end
|
9
|
+
|
10
|
+
class StorageLocationUnavailableError < LongleafError; end
|
11
|
+
|
12
|
+
class EventError < LongleafError; end
|
13
|
+
|
14
|
+
class RegistrationError < EventError; end
|
15
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'longleaf/errors'
|
2
|
+
require 'longleaf/models/metadata_record'
|
3
|
+
require 'longleaf/services/metadata_deserializer'
|
4
|
+
require 'longleaf/services/metadata_serializer'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
# Event to register a file with longleaf
|
8
|
+
module Longleaf
|
9
|
+
class RegisterEvent
|
10
|
+
EVENT_NAME = 'register'
|
11
|
+
|
12
|
+
# @param file_rec [FileRecord] file record
|
13
|
+
# @param app_manager [ApplicationConfigManager] the application configuration
|
14
|
+
# @param force [boolean] if true, then already registered files will be re-registered
|
15
|
+
def initialize(file_rec:, app_manager:, force: false, checksums: nil)
|
16
|
+
raise ArgumentError.new('Must provide a file_rec parameter') if file_rec.nil?
|
17
|
+
raise ArgumentError.new('Parameter file_rec must be a FileRecord') \
|
18
|
+
unless file_rec.is_a?(FileRecord)
|
19
|
+
raise ArgumentError.new('Must provide an ApplicationConfigManager') if app_manager.nil?
|
20
|
+
raise ArgumentError.new('Parameter app_manager must be an ApplicationConfigManager') \
|
21
|
+
unless app_manager.is_a?(ApplicationConfigManager)
|
22
|
+
|
23
|
+
@app_manager = app_manager
|
24
|
+
@file_rec = file_rec
|
25
|
+
@force = force
|
26
|
+
@checksums = checksums
|
27
|
+
end
|
28
|
+
|
29
|
+
# Perform a registration event on the given file
|
30
|
+
# @raises RegistrationError if a file cannot be registered
|
31
|
+
def perform
|
32
|
+
metadata_exists = File.file?(@file_rec.metadata_path)
|
33
|
+
# If the file's metadata exists, only need to register it if the force flag is provided
|
34
|
+
if metadata_exists && !@force
|
35
|
+
raise RegistrationError.new("Unable to register '#{@file_rec.path}', it is already registered.")
|
36
|
+
end
|
37
|
+
|
38
|
+
# create metadata record
|
39
|
+
md_rec = MetadataRecord.new(registered: Time.now.utc.iso8601)
|
40
|
+
@file_rec.metadata_record = md_rec
|
41
|
+
|
42
|
+
# retain significant details from former record
|
43
|
+
if metadata_exists
|
44
|
+
retain_existing_properties
|
45
|
+
end
|
46
|
+
|
47
|
+
populate_file_properties
|
48
|
+
|
49
|
+
md_rec.checksums.merge!(@checksums) unless @checksums.nil?
|
50
|
+
|
51
|
+
populate_services
|
52
|
+
|
53
|
+
# persist the metadata out to file
|
54
|
+
MetadataSerializer::write(metadata: md_rec, file_path: @file_rec.metadata_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def populate_file_properties
|
59
|
+
md_rec = @file_rec.metadata_record
|
60
|
+
|
61
|
+
# Set file properties
|
62
|
+
md_rec.last_modified = File.mtime(@file_rec.path).utc.iso8601
|
63
|
+
md_rec.file_size = File.size(@file_rec.path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def populate_services
|
67
|
+
md_rec = @file_rec.metadata_record
|
68
|
+
|
69
|
+
service_manager = @app_manager.service_manager
|
70
|
+
definitions = service_manager.list_service_definitions(location: @file_rec.storage_location.name)
|
71
|
+
|
72
|
+
# Add service section
|
73
|
+
definitions.each do |serv_def|
|
74
|
+
serv_name = serv_def.name
|
75
|
+
md_rec.add_service(serv_name)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Copy a subset of properties from an existing metadata record to the new record
|
80
|
+
def retain_existing_properties
|
81
|
+
md_rec = @file_rec.metadata_record
|
82
|
+
|
83
|
+
old_md = MetadataDeserializer.deserialize(file_path: @file_rec.metadata_path)
|
84
|
+
# Copy custom properties
|
85
|
+
old_md.properties.each { |name, value| md_rec.properties[name] = value }
|
86
|
+
# Copy stale-replicas flag per service
|
87
|
+
old_md.list_services.each do |serv_name|
|
88
|
+
serv_rec = old_md.service(serv_name)
|
89
|
+
|
90
|
+
stale_replicas = serv_rec.stale_replicas
|
91
|
+
if stale_replicas
|
92
|
+
new_service = md_rec.service(serv_name)
|
93
|
+
new_service.stale_replicas = stale_replicas unless new_service.nil?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
# Logger which directs messages to stdout and/or stderr, depending on the nature of the message.
|
4
|
+
# Status logging, which includes standard logger methods, goes to STDERR.
|
5
|
+
# Operation success and failure messages go to STDOUT, and to STDERR at info level.
|
6
|
+
module Longleaf
|
7
|
+
module Logging
|
8
|
+
class RedirectingLogger
|
9
|
+
# @param failure_only [Boolean] If set to true, only failure messages will be output to STDOUT
|
10
|
+
# @param log_level [String] logger level used for output to STDERR
|
11
|
+
# @param log_format [Strfailure_onlying] format string for log entries to STDERR. There are 4 variables available
|
12
|
+
# for inclusion in the output: severity, datetime, progname, msg. Variables must be wrapped in %{}.
|
13
|
+
# @param datetime_format [String] datetime formatting string used for logger dates appearing in STDERR.
|
14
|
+
def initialize(failure_only: false, log_level: 'WARN', log_format: nil, datetime_format: nil)
|
15
|
+
@stderr_log = Logger.new($stderr)
|
16
|
+
@stderr_log.level = log_level
|
17
|
+
@stderr_log.datetime_format = datetime_format
|
18
|
+
@log_format = log_format
|
19
|
+
if @log_format.nil?
|
20
|
+
@stderr_log.formatter = proc do |severity, datetime, progname, msg|
|
21
|
+
formatted_date = @stderr_log.datetime_format.nil? ? datetime : datetime.strftime(datetime_format)
|
22
|
+
"#{severity} [#{formatted_date}]: #{msg}\n"
|
23
|
+
end
|
24
|
+
elsif @log_format.is_a?(String)
|
25
|
+
@stderr_log.formatter = proc do |severity, datetime, progname, msg|
|
26
|
+
# Make sure the format ends with a newline
|
27
|
+
@log_format = @log_format + "\n" unless @log_format.end_with?("\n")
|
28
|
+
|
29
|
+
formatted_date = @stderr_log.datetime_format.nil? ? datetime : datetime.strftime(datetime_format)
|
30
|
+
@log_format % { :severity => severity, :datetime => formatted_date, :progname => progname, :msg => msg }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@stdout_log = Logger.new($stdout)
|
35
|
+
@stdout_log.formatter = proc do |severity, datetime, progname, msg|
|
36
|
+
"#{msg}\n"
|
37
|
+
end
|
38
|
+
if failure_only
|
39
|
+
@stdout_log.level = 'warn'
|
40
|
+
else
|
41
|
+
@stdout_log.level = 'info'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def debug(progname = nil, &block)
|
46
|
+
@stderr_log.debug(progname, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def info(progname = nil, &block)
|
50
|
+
@stderr_log.info(progname, &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def warn(progname = nil, &block)
|
54
|
+
@stderr_log.warn(progname, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def error(progname = nil, &block)
|
58
|
+
@stderr_log.error(progname, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def fatal(progname = nil, &block)
|
62
|
+
@stderr_log.fatal(progname, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def unknown(progname = nil, &block)
|
66
|
+
@stderr_log.unknown(progname, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Logs a success message to STDOUT, as well as STDERR at info level.
|
70
|
+
# @param eventOrMessage [String] name of the preservation event which succeeded,
|
71
|
+
# or the message to output if it is the only parameter. Required.
|
72
|
+
# @param file_name [String] file name which is the subject of this message.
|
73
|
+
# @param message [String] descriptive message to accompany this output
|
74
|
+
# @param service [String] name of the service which executed.
|
75
|
+
def success(eventOrMessage, file_name = nil, message = nil, service = nil)
|
76
|
+
outcome('SUCCESS', eventOrMessage, file_name, message, service)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Logs a failure message to STDOUT, as well as STDERR at info level.
|
80
|
+
# If an error was provided, it is logged to STDERR at error level.
|
81
|
+
# @param eventOrMessage [String] name of the preservation event which failed,
|
82
|
+
# or the message to output if it is the only parameter.
|
83
|
+
# @param file_name [String] file name which is the subject of this message.
|
84
|
+
# @param message [String] descriptive message to accompany this output
|
85
|
+
# @param service [String] name of the service which executed.
|
86
|
+
# @param error [Error] error which occurred
|
87
|
+
def failure(eventOrMessage, file_name = nil, message = nil, service = nil, error: nil)
|
88
|
+
text = outcome_text('FAILURE', eventOrMessage, file_name, message, service, error)
|
89
|
+
@stdout_log.warn(text)
|
90
|
+
|
91
|
+
@stderr_log.info(text)
|
92
|
+
@stderr_log.error("#{error.message}") unless error.nil?
|
93
|
+
end
|
94
|
+
|
95
|
+
# Logs an outcome message to STDOUT, as well as STDERR at info level.
|
96
|
+
# If file_name and message are nil, eventOrMessage will be used as the message.
|
97
|
+
#
|
98
|
+
# @param outcome [String] The status of the outcome. Required.
|
99
|
+
# @param eventOrMessage [String] name of the preservation event which was successful,
|
100
|
+
# or the message to output if it is the only parameter. Required.
|
101
|
+
# @param file_name [String] file name which is the subject of this message.
|
102
|
+
# @param message [String] descriptive message to accompany this output
|
103
|
+
# @param service [String] name of the service which executed.
|
104
|
+
# @param error [Error] error which occurred
|
105
|
+
def outcome(outcome, eventOrMessage, file_name = nil, message = nil, service = nil, error = nil)
|
106
|
+
text = outcome_text(outcome, eventOrMessage, file_name, message, service, error)
|
107
|
+
@stdout_log.info(text)
|
108
|
+
@stderr_log.info(text)
|
109
|
+
end
|
110
|
+
|
111
|
+
# FAILURE verify[cdr_fixity_check] /path/to/file: Something terrible
|
112
|
+
private
|
113
|
+
def outcome_text(outcome, eventOrMessage, file_name = nil, message = nil, service = nil, error = nil)
|
114
|
+
message_only = file_name.nil? && message.nil? && error.nil?
|
115
|
+
|
116
|
+
text = "#{outcome}"
|
117
|
+
|
118
|
+
if message_only
|
119
|
+
text << ": #{eventOrMessage}"
|
120
|
+
else
|
121
|
+
text << " #{eventOrMessage}"
|
122
|
+
text << "[#{service}]" unless service.nil?
|
123
|
+
text << " #{file_name}" unless file_name.nil?
|
124
|
+
msg = message || error&.message
|
125
|
+
text << ": #{msg}" unless msg.nil?
|
126
|
+
end
|
127
|
+
text
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'longleaf/logging/redirecting_logger'
|
2
|
+
|
3
|
+
module Longleaf
|
4
|
+
module Logging
|
5
|
+
# Get the main logger for longleaf
|
6
|
+
def logger
|
7
|
+
Logging.logger
|
8
|
+
end
|
9
|
+
|
10
|
+
# Get the main logger for longleaf
|
11
|
+
def self.logger
|
12
|
+
@logger ||= RedirectingLogger.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize_logger(failure_only, log_level, log_format, datetime_format)
|
16
|
+
Logging.initialize_logger(failure_only, log_level, log_format, datetime_format)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.initialize_logger(failure_only, log_level, log_format, datetime_format)
|
20
|
+
@logger = RedirectingLogger.new(failure_only: failure_only,
|
21
|
+
log_level: log_level,
|
22
|
+
log_format: log_format,
|
23
|
+
datetime_format: datetime_format)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Record for an individual file and its associated information
|
2
|
+
module Longleaf
|
3
|
+
class FileRecord
|
4
|
+
|
5
|
+
attr_accessor :metadata_record
|
6
|
+
attr_reader :storage_location
|
7
|
+
attr_reader :path
|
8
|
+
|
9
|
+
# @param file_path [String] path to the file
|
10
|
+
# @param storage_location [Longleaf::StorageLocation] storage location containing the file
|
11
|
+
def initialize(file_path, storage_location)
|
12
|
+
raise ArgumentError.new("FileRecord requires a path") if file_path.nil?
|
13
|
+
raise ArgumentError.new("FileRecord requires a storage_location") if storage_location.nil?
|
14
|
+
|
15
|
+
@path = file_path
|
16
|
+
@storage_location = storage_location
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] path for the metadata file for this file
|
20
|
+
def metadata_path
|
21
|
+
@metadata_path = @storage_location.get_metadata_path_for(path) if @metadata_path.nil?
|
22
|
+
@metadata_path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Longleaf
|
2
|
+
class MDFields
|
3
|
+
DATA = 'data'
|
4
|
+
SERVICES = 'services'
|
5
|
+
|
6
|
+
REGISTERED_TIMESTAMP = 'registered'
|
7
|
+
DEREGISTERED_TIMESTAMP = 'deregistered'
|
8
|
+
|
9
|
+
LAST_MODIFIED = 'last-modified'
|
10
|
+
FILE_SIZE = 'size'
|
11
|
+
|
12
|
+
CHECKSUMS = 'checksums'
|
13
|
+
|
14
|
+
STALE_REPLICAS = 'stale-replicas'
|
15
|
+
SERVICE_TIMESTAMP = 'timestamp'
|
16
|
+
RUN_NEEDED = 'run-needed'
|
17
|
+
end
|
18
|
+
end
|