longleaf 0.1.0 → 0.2.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +13 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +4 -0
- data/.rubocop_todo.yml +755 -0
- data/README.md +29 -7
- data/lib/longleaf/candidates/file_selector.rb +107 -0
- data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +99 -0
- data/lib/longleaf/candidates/service_candidate_locator.rb +18 -0
- data/lib/longleaf/cli.rb +102 -6
- data/lib/longleaf/commands/deregister_command.rb +50 -0
- data/lib/longleaf/commands/preserve_command.rb +45 -0
- data/lib/longleaf/commands/register_command.rb +24 -38
- data/lib/longleaf/commands/validate_config_command.rb +6 -2
- data/lib/longleaf/commands/validate_metadata_command.rb +49 -0
- data/lib/longleaf/errors.rb +19 -0
- data/lib/longleaf/events/deregister_event.rb +55 -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 +71 -0
- data/lib/longleaf/events/register_event.rb +37 -26
- data/lib/longleaf/helpers/digest_helper.rb +50 -0
- data/lib/longleaf/helpers/service_date_helper.rb +51 -0
- data/lib/longleaf/logging.rb +1 -0
- data/lib/longleaf/logging/redirecting_logger.rb +9 -8
- data/lib/longleaf/models/app_fields.rb +2 -0
- data/lib/longleaf/models/file_record.rb +8 -3
- data/lib/longleaf/models/md_fields.rb +1 -0
- data/lib/longleaf/models/metadata_record.rb +16 -4
- data/lib/longleaf/models/service_definition.rb +4 -3
- data/lib/longleaf/models/service_fields.rb +2 -0
- data/lib/longleaf/models/service_record.rb +4 -1
- data/lib/longleaf/models/storage_location.rb +18 -1
- data/lib/longleaf/preservation_services/fixity_check_service.rb +121 -0
- data/lib/longleaf/preservation_services/rsync_replication_service.rb +183 -0
- data/lib/longleaf/services/application_config_deserializer.rb +4 -6
- data/lib/longleaf/services/application_config_manager.rb +4 -2
- data/lib/longleaf/services/application_config_validator.rb +1 -1
- data/lib/longleaf/services/configuration_validator.rb +1 -0
- data/lib/longleaf/services/metadata_deserializer.rb +47 -10
- data/lib/longleaf/services/metadata_serializer.rb +42 -6
- data/lib/longleaf/services/service_class_cache.rb +112 -0
- data/lib/longleaf/services/service_definition_manager.rb +5 -1
- data/lib/longleaf/services/service_definition_validator.rb +4 -4
- data/lib/longleaf/services/service_manager.rb +72 -9
- data/lib/longleaf/services/service_mapping_manager.rb +4 -3
- data/lib/longleaf/services/service_mapping_validator.rb +4 -4
- data/lib/longleaf/services/storage_location_manager.rb +26 -5
- data/lib/longleaf/services/storage_location_validator.rb +1 -1
- data/lib/longleaf/services/storage_path_validator.rb +2 -2
- data/lib/longleaf/specs/config_builder.rb +9 -5
- data/lib/longleaf/specs/custom_matchers.rb +9 -0
- data/lib/longleaf/specs/file_helpers.rb +60 -0
- data/lib/longleaf/version.rb +1 -1
- data/longleaf.gemspec +1 -0
- metadata +39 -7
- data/lib/longleaf/commands/abstract_command.rb +0 -37
data/README.md
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
# Longleaf
|
2
|
+
Longleaf is a command-line tool which allows users to configure a set of storage locations and define custom sets of preservation services to run on their contents. These services are executed in response to applicable preservation events issued by clients. Its primary goal is to provide tools to create a simple and customizable preservation environment. Longleaf:
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
* Offers a predictable command-line interface and integrates with standard command-line tools.
|
5
|
+
* Offers configurable and customizable criteria based preservation workflows.
|
6
|
+
* Provides a base set of tools and a framework for building extensions.
|
7
|
+
* Provides activity logging and notifications.
|
8
|
+
* Performs preservation services only when required.
|
6
9
|
|
7
10
|
## Installation
|
8
11
|
|
@@ -22,22 +25,41 @@ Or install it yourself as:
|
|
22
25
|
|
23
26
|
## Usage
|
24
27
|
|
28
|
+
#### Register a file
|
29
|
+
In order to register a new file with Longleaf, use the register command:
|
30
|
+
`longleaf register -c <config.yml> -f <path to file>`
|
31
|
+
|
32
|
+
In the case that a file's content is replaced, the file can be re-registered by providing the `--force` flag.
|
33
|
+
|
25
34
|
#### Validate configuration files
|
26
35
|
Application configuration files can be validated prior to usage with the following command:
|
27
|
-
`longleaf validate_config <config.yml>`
|
36
|
+
`longleaf validate_config -c <config.yml>`
|
37
|
+
|
38
|
+
#### Output and logging
|
39
|
+
|
40
|
+
The primary output from Longleaf is directed to STDOUT, and contains both success and failure messages. If you would like to only return failure messages, you may provide the `--failure_only` flag.
|
41
|
+
|
42
|
+
Additional logging is sent to STDERR. To control the level of logging, you may provide the `--log-level` parameter, which expects the standard [Ruby Logger levels](https://ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html). The default log level is 'WARN'.
|
43
|
+
|
44
|
+
Messages sent to STDOUT are duplicated to STDERR at 'INFO' level, so they are excluded by default. In order to store an ongoing log of activity and errors, you would perform the following:
|
45
|
+
`longleaf <command> --log-level 'INFO' 2> /logs/longleaf.log`
|
28
46
|
|
29
47
|
## Development
|
30
48
|
|
31
49
|
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
50
|
|
33
|
-
To
|
51
|
+
To run Longleaf with local changes without needing to do a local install, you may run `bundle exec exe/longleaf <command>`.
|
52
|
+
|
53
|
+
To install this gem onto your local machine, run `bundle exec rake install`. This will allow you to run `longleaf <command>` and places the gem into `pkg/`. Note: Only files committed to git will be included in the installed gem.
|
54
|
+
|
55
|
+
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
56
|
|
35
57
|
## Contributing
|
36
58
|
|
37
|
-
Bug reports and pull requests are welcome on GitHub at https://
|
59
|
+
Bug reports and pull requests are welcome on GitHub at https://gitlab.lib.unc.edu/cdr/longleaf.
|
38
60
|
|
39
61
|
|
40
62
|
## License
|
41
63
|
|
42
|
-
The gem is available as open source under the terms of the [
|
64
|
+
The gem is available as open source under the terms of the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
43
65
|
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'longleaf/logging'
|
2
|
+
|
3
|
+
module Longleaf
|
4
|
+
# Selects and allows for iteration over files which match a provided set of selection criteria
|
5
|
+
class FileSelector
|
6
|
+
include Longleaf::Logging
|
7
|
+
|
8
|
+
# May only provide either file_paths or storage_locations
|
9
|
+
def initialize(file_paths: nil, storage_locations: nil, app_config:)
|
10
|
+
if nil_or_empty?(file_paths) && nil_or_empty?(storage_locations)
|
11
|
+
raise ArgumentError.new("Must provide either file paths or storage locations")
|
12
|
+
end
|
13
|
+
if !nil_or_empty?(file_paths) && !nil_or_empty?(storage_locations)
|
14
|
+
raise ArgumentError.new("Cannot provide both file paths and storage locations")
|
15
|
+
end
|
16
|
+
@app_config = app_config
|
17
|
+
# The top level paths targeted by this selector
|
18
|
+
@target_paths = file_paths
|
19
|
+
# The set of storage locations to select file paths from
|
20
|
+
@storage_locations = storage_locations
|
21
|
+
# Validate that the selected storage locations are known
|
22
|
+
unless @storage_locations.nil?
|
23
|
+
locations = @app_config.location_manager.locations
|
24
|
+
@storage_locations.each do |loc_name|
|
25
|
+
unless locations.key?(loc_name)
|
26
|
+
raise StorageLocationUnavailableError.new("Cannot select unknown storage location #{loc_name}.")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array] a list of top level paths from which files will be selected
|
33
|
+
def target_paths
|
34
|
+
# If starting from locations, initialize by expanding locations out to their actual paths
|
35
|
+
if @target_paths.nil? && !@storage_locations.nil?
|
36
|
+
@target_paths = Array.new
|
37
|
+
@storage_locations.each do |loc_name|
|
38
|
+
@target_paths << @app_config.location_manager.locations[loc_name].path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@target_paths
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get the next file path for this selector.
|
46
|
+
# @return [String] an absolute path to the next file targeted by this selector,
|
47
|
+
# or nil if no more files selected
|
48
|
+
def next_path
|
49
|
+
if @paths.nil?
|
50
|
+
# Start the paths listing out from the targetted set of paths for this selector
|
51
|
+
# In reverse order since using a LIFO structure
|
52
|
+
@paths = target_paths.reverse
|
53
|
+
end
|
54
|
+
|
55
|
+
# No more paths to return
|
56
|
+
return nil if @paths&.empty?
|
57
|
+
|
58
|
+
# Get the most recently added path for depth first traversal of selected paths
|
59
|
+
path = @paths.pop
|
60
|
+
until path.nil? do
|
61
|
+
@app_config.location_manager.verify_path_in_location(path)
|
62
|
+
|
63
|
+
if File.exist?(path)
|
64
|
+
if File.directory?(path)
|
65
|
+
logger.debug("Expanding directory #{path}")
|
66
|
+
# For a directory, add all children to file_paths
|
67
|
+
Dir.entries(path).sort.reverse.each do |child|
|
68
|
+
@paths << File.join(path, child) unless child == '.' or child == '..'
|
69
|
+
end
|
70
|
+
else
|
71
|
+
logger.debug("Returning file #{path}")
|
72
|
+
return path
|
73
|
+
end
|
74
|
+
else
|
75
|
+
raise InvalidStoragePathError.new("File #{path} does not exist.")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returned path was not a suitable file, try the next path
|
79
|
+
path = @paths.pop
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# return [Array] a list of all storage locations being targeted by this selector
|
84
|
+
def storage_locations
|
85
|
+
# Determine what storage_locations are represented by the given file paths
|
86
|
+
if @storage_locations.nil? && !@target_paths.nil?
|
87
|
+
loc_set = Set.new
|
88
|
+
@target_paths.each do |path|
|
89
|
+
loc = @app_config.location_manager.get_location_by_path(path)
|
90
|
+
loc_set.add(loc.name) unless loc.nil?
|
91
|
+
end
|
92
|
+
@storage_locations = loc_set.to_a
|
93
|
+
end
|
94
|
+
|
95
|
+
if @storage_locations.nil?
|
96
|
+
@storage_locations = Array.new
|
97
|
+
end
|
98
|
+
|
99
|
+
@storage_locations
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def nil_or_empty?(value)
|
104
|
+
value.nil? || value.empty?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'longleaf/services/service_manager'
|
2
|
+
require 'longleaf/services/metadata_deserializer'
|
3
|
+
require 'longleaf/events/event_names'
|
4
|
+
require 'longleaf/errors'
|
5
|
+
require 'longleaf/logging'
|
6
|
+
require 'time'
|
7
|
+
|
8
|
+
module Longleaf
|
9
|
+
# Iterator for getting file candidates which have services which need to be run.
|
10
|
+
# Implementation uses metadata files directly from the filesystem for determinations
|
11
|
+
# about service status.
|
12
|
+
class ServiceCandidateFilesystemIterator
|
13
|
+
include Longleaf::Logging
|
14
|
+
|
15
|
+
def initialize(file_selector, event, app_config, force = false)
|
16
|
+
@file_selector = file_selector
|
17
|
+
@event = event
|
18
|
+
@app_config = app_config
|
19
|
+
@force = force
|
20
|
+
end
|
21
|
+
|
22
|
+
# Get the file record for the next candidate which needs services run which match the
|
23
|
+
# provided file_selector
|
24
|
+
# @return [FileRecord] file record of the next candidate with services needing to be run,
|
25
|
+
# or nil if there are no more candidates.
|
26
|
+
def next_candidate
|
27
|
+
loop do
|
28
|
+
begin
|
29
|
+
next_path = @file_selector.next_path
|
30
|
+
return nil if next_path.nil?
|
31
|
+
|
32
|
+
logger.debug("Evaluating candidate #{next_path}")
|
33
|
+
storage_loc = @app_config.location_manager.get_location_by_path(next_path)
|
34
|
+
file_rec = FileRecord.new(next_path, storage_loc)
|
35
|
+
|
36
|
+
# Skip over unregistered files
|
37
|
+
if !file_rec.metadata_present?
|
38
|
+
logger.debug("Ignoring unregistered file #{next_path}")
|
39
|
+
next
|
40
|
+
end
|
41
|
+
|
42
|
+
file_rec.metadata_record = MetadataDeserializer.deserialize(file_path: file_rec.metadata_path,
|
43
|
+
digest_algs: storage_loc.metadata_digests)
|
44
|
+
|
45
|
+
# Return the file record if it needs any services run
|
46
|
+
return file_rec if needs_run?(file_rec)
|
47
|
+
rescue InvalidStoragePathError => e
|
48
|
+
logger.warn("Skipping candidate file: #{e.message}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Iterate through the candidates in this object and execute the provided block with each
|
54
|
+
# candidate. A block is required.
|
55
|
+
def each
|
56
|
+
file_rec = next_candidate
|
57
|
+
until file_rec.nil?
|
58
|
+
yield file_rec
|
59
|
+
|
60
|
+
file_rec = next_candidate
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
# Returns true if the file record contains any services which need to be run
|
66
|
+
def needs_run?(file_rec)
|
67
|
+
md_rec = file_rec.metadata_record
|
68
|
+
storage_loc = file_rec.storage_location
|
69
|
+
service_manager = @app_config.service_manager
|
70
|
+
|
71
|
+
# File is not a valid candidate for services if it is deregistered, unless performing cleanup
|
72
|
+
if @event != EventNames::CLEANUP && md_rec.deregistered?
|
73
|
+
logger.debug("Skipping deregistered file: #{file_rec.path}")
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
expected_services = service_manager.list_services(
|
78
|
+
location: storage_loc.name,
|
79
|
+
event: @event)
|
80
|
+
|
81
|
+
# When in force mode, candidate needs a run as long as there are any services configured for it.
|
82
|
+
if @force && expected_services.length > 0
|
83
|
+
logger.debug("Forcing run needed for file: #{file_rec.path}")
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
|
87
|
+
expected_services.each do |service_name|
|
88
|
+
if service_manager.service_needed?(service_name, md_rec)
|
89
|
+
logger.debug("Service #{service_name} needed for file: #{file_rec.path}")
|
90
|
+
return true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
logger.debug("Run not needed for file: #{file_rec.path}")
|
95
|
+
# No services needed to be run for this file
|
96
|
+
false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'longleaf/candidates/service_candidate_filesystem_iterator'
|
2
|
+
|
3
|
+
module Longleaf
|
4
|
+
# Service which locates files that have services which need to be performed on them.
|
5
|
+
class ServiceCandidateLocator
|
6
|
+
def initialize(app_config)
|
7
|
+
@app_config = app_config
|
8
|
+
end
|
9
|
+
|
10
|
+
# Get a iterator of the candidates matching the given FileSelector which need services run.
|
11
|
+
# @param file_selector [FileSelector] selector identifying the files to pull candidates from.
|
12
|
+
# @return an iterator
|
13
|
+
def candidate_iterator(file_selector, event, force = false)
|
14
|
+
# Get filesystem based implementation
|
15
|
+
ServiceCandidateFilesystemIterator.new(file_selector, event, @app_config, force)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/longleaf/cli.rb
CHANGED
@@ -2,16 +2,24 @@ require 'thor'
|
|
2
2
|
require 'yaml'
|
3
3
|
require 'longleaf/logging'
|
4
4
|
require 'longleaf/errors'
|
5
|
+
require 'longleaf/commands/deregister_command'
|
5
6
|
require 'longleaf/commands/validate_config_command'
|
7
|
+
require 'longleaf/commands/validate_metadata_command'
|
6
8
|
require 'longleaf/commands/register_command'
|
9
|
+
require 'longleaf/commands/preserve_command'
|
10
|
+
require 'longleaf/candidates/file_selector'
|
7
11
|
|
8
12
|
module Longleaf
|
13
|
+
# Main commandline interface setup for Longleaf using Thor.
|
9
14
|
class CLI < Thor
|
10
15
|
include Longleaf::Logging
|
11
16
|
|
12
17
|
class_option(:config, :aliases => "-c",
|
13
18
|
:default => ENV['LONGLEAF_CFG'],
|
19
|
+
:required => true,
|
14
20
|
:desc => 'Absolute path to the application configuration used for this command. By default, the value of the environment variable LONGLEAF_CFG is used.')
|
21
|
+
class_option(:load_path, :aliases => "-I",
|
22
|
+
:desc => 'Specify comma seperated directories to add to the $LOAD_PATH, which can be used to specify additional paths from which to load preservation services.')
|
15
23
|
# Logging/output options
|
16
24
|
class_option(:failure_only,
|
17
25
|
:type => :boolean,
|
@@ -41,7 +49,8 @@ module Longleaf
|
|
41
49
|
setup_logger(options)
|
42
50
|
|
43
51
|
config_path = options[:config]
|
44
|
-
|
52
|
+
app_config_manager = load_application_config(options[:config])
|
53
|
+
file_selector = create_file_selector(options[:file], nil, app_config_manager)
|
45
54
|
if options[:checksums]
|
46
55
|
checksums = options[:checksums]
|
47
56
|
# validate checksum list format, must a comma delimited list of prefix:checksums
|
@@ -54,16 +63,75 @@ module Longleaf
|
|
54
63
|
end
|
55
64
|
end
|
56
65
|
|
57
|
-
command =
|
58
|
-
exit command.execute(
|
66
|
+
command = RegisterCommand.new(app_config_manager)
|
67
|
+
exit command.execute(file_selector: file_selector, force: options[:force], checksums: checksums)
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "deregister", "Deregister files with Longleaf"
|
71
|
+
method_option(:file, :aliases => "-f",
|
72
|
+
:required => true,
|
73
|
+
:desc => 'File or files to deregister. Paths must be absolute. If multiple files are provided, they must be comma separated.')
|
74
|
+
method_option(:force,
|
75
|
+
:type => :boolean,
|
76
|
+
:default => false,
|
77
|
+
:desc => 'Force the deregistration of already deregistered files.')
|
78
|
+
# Deregister event command
|
79
|
+
def deregister
|
80
|
+
setup_logger(options)
|
81
|
+
|
82
|
+
config_path = options[:config]
|
83
|
+
app_config_manager = load_application_config(options[:config])
|
84
|
+
file_selector = create_file_selector(options[:file], nil, app_config_manager)
|
85
|
+
|
86
|
+
command = DeregisterCommand.new(app_config_manager)
|
87
|
+
exit command.execute(file_selector: file_selector, force: options[:force])
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "preserve", "Perform preservation services on files with Longleaf"
|
91
|
+
method_option(:file, :aliases => "-f",
|
92
|
+
:required => false,
|
93
|
+
:desc => 'File or files to preserve. Paths must be absolute. If multiple files are provided, they must be comma separated.')
|
94
|
+
method_option(:location, :aliases => "-s",
|
95
|
+
:required => false,
|
96
|
+
:desc => 'Name or comma separated names of storage locations to preserve.')
|
97
|
+
method_option(:force,
|
98
|
+
:type => :boolean,
|
99
|
+
:default => false,
|
100
|
+
:desc => 'Force the execution of preservation services, disregarding scheduling information.')
|
101
|
+
def preserve
|
102
|
+
setup_logger(options)
|
103
|
+
|
104
|
+
extend_load_path(options[:load_path])
|
105
|
+
app_config_manager = load_application_config(options[:config])
|
106
|
+
file_selector = create_file_selector(options[:file], options[:location], app_config_manager)
|
107
|
+
|
108
|
+
command = PreserveCommand.new(app_config_manager)
|
109
|
+
exit command.execute(file_selector: file_selector, force: options[:force])
|
59
110
|
end
|
60
111
|
|
61
|
-
desc "validate_config
|
112
|
+
desc "validate_config", "Validate an application configuration file, provided using --config."
|
62
113
|
# Application configuration validation command
|
63
|
-
def validate_config
|
114
|
+
def validate_config
|
115
|
+
setup_logger(options)
|
116
|
+
|
117
|
+
exit Longleaf::ValidateConfigCommand.new(options[:config]).execute
|
118
|
+
end
|
119
|
+
|
120
|
+
desc "validate_metadata", "Validate metadata files."
|
121
|
+
method_option(:file, :aliases => "-f",
|
122
|
+
:required => false,
|
123
|
+
:desc => 'File or files of which to validate the metadata. Paths must be absolute. If multiple files are provided, they must be comma separated.')
|
124
|
+
method_option(:location, :aliases => "-s",
|
125
|
+
:required => false,
|
126
|
+
:desc => 'Name or comma separated names of storage locations to validate.')
|
127
|
+
# File metadata validation command
|
128
|
+
def validate_metadata
|
64
129
|
setup_logger(options)
|
65
130
|
|
66
|
-
|
131
|
+
app_config_manager = load_application_config(options[:config])
|
132
|
+
file_selector = create_file_selector(options[:file], options[:location], app_config_manager)
|
133
|
+
|
134
|
+
exit Longleaf::ValidateMetadataCommand.new(app_config_manager).execute(file_selector: file_selector)
|
67
135
|
end
|
68
136
|
|
69
137
|
no_commands do
|
@@ -73,6 +141,34 @@ module Longleaf
|
|
73
141
|
options[:log_format],
|
74
142
|
options[:log_datetime])
|
75
143
|
end
|
144
|
+
|
145
|
+
def load_application_config(config_path)
|
146
|
+
begin
|
147
|
+
app_manager = ApplicationConfigDeserializer.deserialize(config_path)
|
148
|
+
rescue ConfigurationError => err
|
149
|
+
logger.failure("Failed to load application configuration due to the following issue:\n#{err.message}")
|
150
|
+
exit 1
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def create_file_selector(file_paths, storage_locations, app_config_manager)
|
155
|
+
file_paths = file_paths&.split(/\s*,\s*/)
|
156
|
+
storage_locations = storage_locations&.split(/\s*,\s*/)
|
157
|
+
|
158
|
+
begin
|
159
|
+
FileSelector.new(file_paths: file_paths, storage_locations: storage_locations, app_config: app_config_manager)
|
160
|
+
rescue ArgumentError => e
|
161
|
+
logger.failure(e.message)
|
162
|
+
exit 1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def extend_load_path(load_paths)
|
167
|
+
load_paths = load_paths&.split(/\s*,\s*/)
|
168
|
+
if !load_paths.nil?
|
169
|
+
load_paths.each { |path| $LOAD_PATH.unshift(path) }
|
170
|
+
end
|
171
|
+
end
|
76
172
|
end
|
77
173
|
end
|
78
174
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'longleaf/services/application_config_deserializer'
|
2
|
+
require 'longleaf/events/deregister_event'
|
3
|
+
require 'longleaf/models/file_record'
|
4
|
+
require 'longleaf/events/event_names'
|
5
|
+
require 'longleaf/events/event_status_tracking'
|
6
|
+
require 'longleaf/services/metadata_deserializer'
|
7
|
+
|
8
|
+
module Longleaf
|
9
|
+
# Command for deregistering files with longleaf
|
10
|
+
class DeregisterCommand
|
11
|
+
include Longleaf::EventStatusTracking
|
12
|
+
|
13
|
+
def initialize(app_manager)
|
14
|
+
@app_manager = app_manager
|
15
|
+
end
|
16
|
+
|
17
|
+
# Execute the deregister command on the given parameters
|
18
|
+
# @param file_selector [FileSelector] selector for files to deregister
|
19
|
+
# @param force [Boolean] force flag
|
20
|
+
# @return [Integer] status code
|
21
|
+
def execute(file_selector:, force: false)
|
22
|
+
begin
|
23
|
+
# Perform deregister events on each of the file paths provided
|
24
|
+
loop do
|
25
|
+
f_path = file_selector.next_path
|
26
|
+
break if f_path.nil?
|
27
|
+
|
28
|
+
storage_location = @app_manager.location_manager.get_location_by_path(f_path)
|
29
|
+
|
30
|
+
file_rec = FileRecord.new(f_path, storage_location)
|
31
|
+
unless file_rec.metadata_present?
|
32
|
+
raise DeregistrationError.new("Cannot deregister #{f_path}, file is not registered.")
|
33
|
+
end
|
34
|
+
|
35
|
+
file_rec.metadata_record = MetadataDeserializer.deserialize(file_path: file_rec.metadata_path,
|
36
|
+
digest_algs: storage_location.metadata_digests)
|
37
|
+
|
38
|
+
event = DeregisterEvent.new(file_rec: file_rec, force: force, app_manager: @app_manager)
|
39
|
+
track_status(event.perform)
|
40
|
+
end
|
41
|
+
rescue DeregistrationError, InvalidStoragePathError, StorageLocationUnavailableError => err
|
42
|
+
record_failure(EventNames::DEREGISTER, nil, err.message)
|
43
|
+
rescue => err
|
44
|
+
record_failure(EventNames::DEREGISTER, error: err)
|
45
|
+
end
|
46
|
+
|
47
|
+
return_status
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|