choria-colt 0.4.0 → 0.6.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/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +2 -7
- data/CHANGELOG.md +26 -0
- data/README.md +2 -2
- data/lib/choria/colt/cli/formatter.rb +69 -13
- data/lib/choria/colt/cli.rb +47 -22
- data/lib/choria/colt/data_structurer.rb +2 -0
- data/lib/choria/colt/version.rb +1 -1
- data/lib/choria/colt.rb +5 -5
- data/lib/choria/orchestrator/task/result_set.rb +28 -0
- data/lib/choria/orchestrator/task.rb +61 -48
- data/lib/choria/orchestrator.rb +28 -44
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e76e667917bf1958b48d45148712fa5e0d6f2c15280bbbda0d4899b0537e9f8
|
4
|
+
data.tar.gz: 17637d3bf6738bef6c28a282aabb1c4ac7680e4b2e8a6f0fe873a27b59079cc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bacccbdfd46669936f1271d05e4fdceae1864e07ae7f14b260f6de3ec2ed1e90d0c3270f57ea3faaa424b267eab59e1460abcdf4262feec421c6ea510bc811d5
|
7
|
+
data.tar.gz: ad8aaf32f6f71f349d04955cb795c0439c4ecd30103f290da82b3ce7037c6dce330b7e89123a416c120b61bc194c754debfb97412e6e200af8d53264aaba169c
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2022-
|
3
|
+
# on 2022-05-03 07:08:32 UTC using RuboCop version 1.26.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -9,9 +9,4 @@
|
|
9
9
|
# Offense count: 1
|
10
10
|
# Configuration parameters: IgnoredMethods.
|
11
11
|
Metrics/CyclomaticComplexity:
|
12
|
-
Max:
|
13
|
-
|
14
|
-
# Offense count: 1
|
15
|
-
# Configuration parameters: IgnoredMethods.
|
16
|
-
Metrics/PerceivedComplexity:
|
17
|
-
Max: 9
|
12
|
+
Max: 8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.6.0](https://github.com/opus-codium/choria-colt/tree/v0.6.0) (2022-05-16)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.5.1...v0.6.0)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- Allow to run a task from any puppet environment [\#28](https://github.com/opus-codium/choria-colt/pull/28) ([neomilium](https://github.com/neomilium))
|
10
|
+
- CLI: Use symbol notation to target `:all` nodes [\#27](https://github.com/opus-codium/choria-colt/pull/27) ([neomilium](https://github.com/neomilium))
|
11
|
+
- CLI: Allow targeting on task status requests [\#26](https://github.com/opus-codium/choria-colt/pull/26) ([neomilium](https://github.com/neomilium))
|
12
|
+
- Fix `task status` command [\#25](https://github.com/opus-codium/choria-colt/pull/25) ([neomilium](https://github.com/neomilium))
|
13
|
+
|
14
|
+
## [v0.5.1](https://github.com/opus-codium/choria-colt/tree/v0.5.1) (2022-05-02)
|
15
|
+
|
16
|
+
[Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.5.0...v0.5.1)
|
17
|
+
|
18
|
+
## [v0.5.0](https://github.com/opus-codium/choria-colt/tree/v0.5.0) (2022-05-02)
|
19
|
+
|
20
|
+
[Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.4.0...v0.5.0)
|
21
|
+
|
22
|
+
**Merged pull requests:**
|
23
|
+
|
24
|
+
- Process and display RPC errors like execution errors [\#24](https://github.com/opus-codium/choria-colt/pull/24) ([neomilium](https://github.com/neomilium))
|
25
|
+
- CLI: Be more green\(washing\) [\#23](https://github.com/opus-codium/choria-colt/pull/23) ([neomilium](https://github.com/neomilium))
|
26
|
+
- CLI: Support @file notation on --targets option [\#22](https://github.com/opus-codium/choria-colt/pull/22) ([neomilium](https://github.com/neomilium))
|
27
|
+
- Do not pass explicit options to rpcclient [\#21](https://github.com/opus-codium/choria-colt/pull/21) ([smortex](https://github.com/smortex))
|
28
|
+
|
3
29
|
## [v0.4.0](https://github.com/opus-codium/choria-colt/tree/v0.4.0) (2022-04-25)
|
4
30
|
|
5
31
|
[Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.3.0...v0.4.0)
|
data/README.md
CHANGED
@@ -51,8 +51,8 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
51
51
|
|
52
52
|
|
53
53
|
To release a new version:
|
54
|
-
1. update the version number in `version.rb`
|
55
|
-
1. generate CHANGELOG.md with `bundle exec rake changelog`
|
54
|
+
1. update the version number in `lib/choria/colt/version.rb`
|
55
|
+
1. generate `CHANGELOG.md` with `bundle exec rake changelog`
|
56
56
|
1. commit changes
|
57
57
|
1. run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
58
58
|
|
@@ -5,6 +5,33 @@ module Choria
|
|
5
5
|
class Colt
|
6
6
|
class CLI < Thor
|
7
7
|
class Formatter
|
8
|
+
module Result
|
9
|
+
def sender
|
10
|
+
self[:sender]
|
11
|
+
end
|
12
|
+
|
13
|
+
def exitcode
|
14
|
+
dig(:data, :exitcode)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ok?
|
18
|
+
exitcode&.zero?
|
19
|
+
end
|
20
|
+
|
21
|
+
def runtime
|
22
|
+
dig(:data, :runtime)
|
23
|
+
end
|
24
|
+
|
25
|
+
# CLI
|
26
|
+
def output
|
27
|
+
if dig(:result, :_output).nil?
|
28
|
+
JSON.pretty_generate(self[:result]).split("\n")
|
29
|
+
else
|
30
|
+
dig(:result, :_output)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
8
35
|
attr_reader :pastel
|
9
36
|
|
10
37
|
def initialize(colored:)
|
@@ -13,27 +40,36 @@ module Choria
|
|
13
40
|
end
|
14
41
|
|
15
42
|
def process_result(result)
|
16
|
-
|
43
|
+
result.extend Formatter::Result
|
17
44
|
|
18
|
-
|
45
|
+
case result[:statuscode]
|
46
|
+
when 0
|
47
|
+
# 0 OK
|
48
|
+
process_success(result)
|
49
|
+
when 1
|
50
|
+
# 1 OK, failed. All the data parsed ok, we have a action matching the request but the requested action could not be completed. RPCAborted
|
51
|
+
process_error(result) # unless result.ok?
|
52
|
+
else
|
53
|
+
# 2 Unknown action UnknownRPCAction
|
54
|
+
# 3 Missing data MissingRPCData
|
55
|
+
# 4 Invalid data InvalidRPCData
|
56
|
+
# 5 Other error
|
57
|
+
process_rpc_error(result)
|
58
|
+
end
|
19
59
|
end
|
20
60
|
|
21
61
|
def process_success(result)
|
22
|
-
|
23
|
-
|
24
|
-
]
|
25
|
-
|
26
|
-
output_lines += if result.dig(:result, :_output).nil?
|
27
|
-
JSON.pretty_generate(result[:result]).split("\n").map { |line| " #{line}" }
|
28
|
-
else
|
29
|
-
result.dig(:result, :_output).map { |line| " #{line}" }
|
30
|
-
end
|
62
|
+
host = format_host(result, "#{pastel.bright_green '√'} ")
|
63
|
+
headline = "#{pastel.on_green ' '} "
|
31
64
|
|
32
|
-
|
65
|
+
[
|
66
|
+
host,
|
67
|
+
result.output.map { |line| "#{headline}#{line}" },
|
68
|
+
].flatten.join("\n")
|
33
69
|
end
|
34
70
|
|
35
71
|
def process_error(result) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
36
|
-
host = "#{pastel.bright_red '⨯'}
|
72
|
+
host = format_host(result, "#{pastel.bright_red '⨯'} ")
|
37
73
|
output = result.dig(:result, '_output')
|
38
74
|
error_details = JSON.pretty_generate(result.dig(:result, :_error, :details)).split "\n"
|
39
75
|
error_description = [
|
@@ -61,6 +97,26 @@ module Choria
|
|
61
97
|
].flatten.map { |line| "#{headline}#{line}" },
|
62
98
|
].flatten.join("\n")
|
63
99
|
end
|
100
|
+
|
101
|
+
def process_rpc_error(result)
|
102
|
+
host = "#{pastel.bright_red '⨯'} #{pastel.host(result.sender)}"
|
103
|
+
headline = "#{pastel.on_red ' '} "
|
104
|
+
|
105
|
+
[
|
106
|
+
host,
|
107
|
+
"#{headline}#{pastel.bright_red "RPC error (#{result[:statuscode]})"}: #{pastel.bright_white result[:statusmsg]}",
|
108
|
+
].join("\n")
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def format_duration(result)
|
114
|
+
result.runtime.nil? ? '' : "duration: #{pastel.bright_white format('%.2fs', result.runtime)}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def format_host(result, headline)
|
118
|
+
"#{headline}#{pastel.host(result.sender).ljust(60, ' ')}#{format_duration(result)}"
|
119
|
+
end
|
64
120
|
end
|
65
121
|
end
|
66
122
|
end
|
data/lib/choria/colt/cli.rb
CHANGED
@@ -9,6 +9,15 @@ module Choria
|
|
9
9
|
class Colt
|
10
10
|
class CLI < Thor
|
11
11
|
class Tasks < Thor
|
12
|
+
def self.define_targets_and_filters_options
|
13
|
+
option :targets,
|
14
|
+
aliases: ['--target', '-t'],
|
15
|
+
desc: 'Identifies the targets of the command.'
|
16
|
+
option :targets_with_classes,
|
17
|
+
aliases: ['--targets-with-class', '-C'],
|
18
|
+
desc: 'Select the targets which have the specified Puppet classes.'
|
19
|
+
end
|
20
|
+
|
12
21
|
class_option :log_level,
|
13
22
|
desc: 'Set log level for CLI',
|
14
23
|
default: 'info'
|
@@ -19,34 +28,28 @@ module Choria
|
|
19
28
|
|
20
29
|
Parameters take the form parameter=value.
|
21
30
|
DESC
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def run(*args) # rubocop:disable Metrics/AbcSize
|
29
|
-
input = extract_task_parameters_from_args(args)
|
30
|
-
|
31
|
+
define_targets_and_filters_options
|
32
|
+
option :environment,
|
33
|
+
aliases: ['-E'],
|
34
|
+
desc: 'Puppet environment to grab tasks from',
|
35
|
+
default: 'production'
|
36
|
+
def run(*args)
|
31
37
|
raise Thor::Error, 'Task name is required' if args.empty?
|
32
|
-
raise Thor::Error, "Too many arguments: #{args}" unless args.count == 1
|
33
38
|
|
34
|
-
|
39
|
+
input = extract_task_parameters_from_args(args)
|
40
|
+
raise Thor::Error, "Too many arguments: #{args}" unless args.count == 1
|
35
41
|
|
36
42
|
task_name = args.shift
|
43
|
+
targets, targets_with_classes = extract_targets_and_filters_from_options
|
37
44
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
targets_with_classes = options['targets_with_classes']&.split(',')
|
42
|
-
|
43
|
-
results = colt.run_bolt_task task_name, input: input, targets: targets, targets_with_classes: targets_with_classes do |result|
|
45
|
+
environment = options['environment']
|
46
|
+
results = colt.run_bolt_task task_name, input: input, targets: targets, targets_with_classes: targets_with_classes, environment: environment do |result|
|
44
47
|
$stdout.puts formatter.process_result(result)
|
45
48
|
end
|
46
49
|
|
47
50
|
File.write 'last_run.json', JSON.pretty_generate(results)
|
48
|
-
rescue Choria::Orchestrator::Error
|
49
|
-
|
51
|
+
rescue Choria::Orchestrator::Error
|
52
|
+
# This error is already logged and displayed.
|
50
53
|
end
|
51
54
|
|
52
55
|
desc 'show [task name] [options]', 'Show available tasks and task documentation'
|
@@ -84,14 +87,17 @@ module Choria
|
|
84
87
|
|
85
88
|
A task ID is required to request Choria services and retrieve results.
|
86
89
|
DESC
|
90
|
+
define_targets_and_filters_options
|
87
91
|
def status(task_id)
|
88
|
-
|
92
|
+
targets, targets_with_classes = extract_targets_and_filters_from_options
|
93
|
+
|
94
|
+
results = colt.wait_bolt_task(task_id, targets: targets, targets_with_classes: targets_with_classes) do |result|
|
89
95
|
$stdout.puts formatter.process_result(result)
|
90
96
|
end
|
91
97
|
|
92
98
|
File.write 'last_run.json', JSON.pretty_generate(results)
|
93
|
-
rescue Choria::Orchestrator::Error
|
94
|
-
|
99
|
+
rescue Choria::Orchestrator::Error
|
100
|
+
# This error is already logged and displayed.
|
95
101
|
end
|
96
102
|
|
97
103
|
no_commands do # rubocop:disable Metrics/BlockLength
|
@@ -130,6 +136,25 @@ module Choria
|
|
130
136
|
end.to_h
|
131
137
|
end
|
132
138
|
|
139
|
+
def extract_targets_and_filters_from_options
|
140
|
+
raise Thor::Error, 'Flag --targets or --targets-with-class is required' if options['targets'].nil? && options['targets_with_classes'].nil?
|
141
|
+
|
142
|
+
targets = extract_targets_from_options
|
143
|
+
targets_with_classes = options['targets_with_classes']&.split(',')
|
144
|
+
|
145
|
+
[targets, targets_with_classes]
|
146
|
+
end
|
147
|
+
|
148
|
+
def extract_targets_from_options
|
149
|
+
return nil if options['targets'] == ':all'
|
150
|
+
|
151
|
+
targets = options['targets']&.split(',')
|
152
|
+
targets&.map do |t|
|
153
|
+
t = File.read(t.sub(/^@/, '')).lines.map(&:strip) if t.start_with? '@'
|
154
|
+
t
|
155
|
+
end&.flatten
|
156
|
+
end
|
157
|
+
|
133
158
|
def show_tasks_summary(tasks)
|
134
159
|
tasks.reject! { |_task, metadata| metadata['metadata']['private'] }
|
135
160
|
|
@@ -2,6 +2,8 @@ module Choria
|
|
2
2
|
class Colt
|
3
3
|
module DataStructurer
|
4
4
|
def self.structure(res) # rubocop:disable Metrics/AbcSize
|
5
|
+
return res unless [0, 1].include? res[:statuscode]
|
6
|
+
|
5
7
|
# data.stdout seems to always be JSON, so parse it once.
|
6
8
|
res[:result] = JSON.parse res[:data][:stdout]
|
7
9
|
res[:data].delete :stdout
|
data/lib/choria/colt/version.rb
CHANGED
data/lib/choria/colt.rb
CHANGED
@@ -19,9 +19,9 @@ module Choria
|
|
19
19
|
@orchestrator = Choria::Orchestrator.new logger: @logger
|
20
20
|
end
|
21
21
|
|
22
|
-
def run_bolt_task(task_name, input: {}, targets: nil, targets_with_classes: nil, &block)
|
22
|
+
def run_bolt_task(task_name, input: {}, targets: nil, targets_with_classes: nil, environment: 'production', &block)
|
23
23
|
logger.debug "Instantiate task '#{task_name}' and validate input"
|
24
|
-
task = Choria::Orchestrator::Task.new(name: task_name, input: input, orchestrator: orchestrator)
|
24
|
+
task = Choria::Orchestrator::Task.new(name: task_name, input: input, environment: environment, orchestrator: orchestrator)
|
25
25
|
|
26
26
|
task.on_result(&block) if block_given?
|
27
27
|
|
@@ -33,11 +33,11 @@ module Choria
|
|
33
33
|
raise
|
34
34
|
end
|
35
35
|
|
36
|
-
def wait_bolt_task(task_id, &block)
|
37
|
-
|
36
|
+
def wait_bolt_task(task_id, targets: nil, targets_with_classes: nil, &block)
|
37
|
+
orchestrator.discover(targets: targets, targets_with_classes: targets_with_classes)
|
38
38
|
|
39
|
+
task = Choria::Orchestrator::Task.new(id: task_id, orchestrator: orchestrator)
|
39
40
|
task.on_result(&block) if block_given?
|
40
|
-
|
41
41
|
task.wait
|
42
42
|
task.results
|
43
43
|
rescue Choria::Orchestrator::Error => e
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'choria/colt/data_structurer'
|
2
|
+
|
3
|
+
module Choria
|
4
|
+
class Orchestrator
|
5
|
+
class Task
|
6
|
+
class ResultSet
|
7
|
+
attr_reader :results
|
8
|
+
|
9
|
+
def initialize(on_result:)
|
10
|
+
@results = []
|
11
|
+
@on_result = on_result
|
12
|
+
end
|
13
|
+
|
14
|
+
def integrate_rpc_error(rpc_error)
|
15
|
+
result = rpc_error[:body]
|
16
|
+
result[:sender] = rpc_error[:senderid]
|
17
|
+
integrate_result(result)
|
18
|
+
end
|
19
|
+
|
20
|
+
def integrate_result(result)
|
21
|
+
structured_result = Choria::Colt::DataStructurer.structure(result).with_indifferent_access
|
22
|
+
@results << structured_result
|
23
|
+
@on_result&.call(structured_result)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'task/result_set'
|
2
2
|
|
3
3
|
require 'active_support'
|
4
4
|
require 'active_support/core_ext/hash/indifferent_access'
|
@@ -7,17 +7,15 @@ module Choria
|
|
7
7
|
class Orchestrator
|
8
8
|
class Task
|
9
9
|
class Error < Orchestrator::Error; end
|
10
|
+
class NoNodesLeftError < Error; end
|
10
11
|
|
11
|
-
attr_reader :id, :name, :input, :environment
|
12
|
-
attr_accessor :rpc_responses
|
12
|
+
attr_reader :id, :name, :input, :environment
|
13
13
|
|
14
14
|
def initialize(orchestrator:, id: nil, name: nil, input: {}, environment: 'production')
|
15
15
|
@id = id
|
16
16
|
@name = name
|
17
17
|
@environment = environment
|
18
18
|
@orchestrator = orchestrator
|
19
|
-
@results = []
|
20
|
-
|
21
19
|
return if @name.nil?
|
22
20
|
|
23
21
|
@input = default_input.merge input
|
@@ -33,71 +31,82 @@ module Choria
|
|
33
31
|
metadata['files'].to_json
|
34
32
|
end
|
35
33
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
rpc_responses_error.each do |res|
|
40
|
-
logger.error "Task request failed on '#{res[:senderid]}':\n#{pp res}"
|
41
|
-
end
|
34
|
+
def results
|
35
|
+
result_set.results
|
36
|
+
end
|
42
37
|
|
43
|
-
|
38
|
+
def run
|
39
|
+
raise Error, 'Unable to run a task by ID' if name.nil?
|
44
40
|
|
45
|
-
|
41
|
+
@pending_targets = rpc_client.discover
|
42
|
+
_download
|
43
|
+
_run_no_wait
|
44
|
+
end
|
46
45
|
|
47
|
-
|
48
|
-
|
46
|
+
def wait
|
47
|
+
raise Error, 'Task ID is required!' if @id.nil?
|
49
48
|
|
50
|
-
|
49
|
+
logger.info "Waiting task #{@id} results…"
|
50
|
+
@rpc_results = []
|
51
|
+
loop do
|
52
|
+
self.rpc_results = rpc_client.task_status(task_id: @id).map(&:results)
|
53
|
+
break if @pending_targets.empty?
|
54
|
+
end
|
51
55
|
end
|
52
56
|
|
53
57
|
def on_result(&block)
|
54
|
-
@on_result =
|
55
|
-
block.call(result)
|
56
|
-
}
|
58
|
+
@on_result = ->(result) { block.call(result) }
|
57
59
|
end
|
58
60
|
|
59
61
|
private
|
60
62
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
new_result_hosts.each do |host|
|
65
|
-
result = results.find { |res| res[:sender] == host }
|
66
|
-
|
67
|
-
next unless result[:data][:exitcode] != -1
|
68
|
-
|
69
|
-
logger.debug "New result for task ##{@id}: #{result}"
|
70
|
-
structured_result = Choria::Colt::DataStructurer.structure(result).with_indifferent_access
|
71
|
-
|
72
|
-
@on_result&.call(structured_result)
|
63
|
+
def result_set
|
64
|
+
@result_set ||= ResultSet.new(on_result: @on_result)
|
65
|
+
end
|
73
66
|
|
74
|
-
|
67
|
+
def rpc_results=(results)
|
68
|
+
completed_results = results.reject { |res| res[:data][:exitcode] == -1 }
|
69
|
+
@pending_targets ||= results.map { |res| res[:sender] }
|
70
|
+
|
71
|
+
new_results = completed_results.select { |res| @pending_targets.include? res[:sender] }
|
72
|
+
new_results.each do |res|
|
73
|
+
logger.debug "New result for task ##{@id}: #{res}"
|
74
|
+
result_set.integrate_result(res)
|
75
|
+
@pending_targets.delete res[:sender]
|
75
76
|
end
|
76
|
-
|
77
|
-
@rpc_results = results
|
78
77
|
end
|
79
78
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
@results = []
|
86
|
-
@rpc_results = []
|
79
|
+
def process_rpc_response(rpc_response)
|
80
|
+
rpc_response.extend Orchestrator::RpcResponse
|
81
|
+
logger.debug " RPC Response: '#{rpc_response}'"
|
82
|
+
return unless rpc_response.rpc_error?
|
87
83
|
|
88
|
-
|
89
|
-
|
84
|
+
@pending_targets.delete rpc_response.sender
|
85
|
+
result_set.integrate_rpc_error(rpc_response)
|
86
|
+
end
|
90
87
|
|
91
|
-
|
88
|
+
def _download
|
89
|
+
logger.info "Downloading task '#{name}' on #{rpc_client.discover.size} nodes…"
|
90
|
+
rpc_client.download(task: name, files: files, verbose: false) do |rpc_response|
|
91
|
+
process_rpc_response(rpc_response)
|
92
92
|
end
|
93
|
+
|
94
|
+
raise NoNodesLeftError, "No nodes left to continue after 'download' action" if @pending_targets.empty?
|
93
95
|
end
|
94
96
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
97
|
+
def _run_no_wait # rubocop:disable Metrics/AbcSize
|
98
|
+
logger.info "Starting task '#{name}' on #{rpc_client.discover.size} nodes…"
|
99
|
+
task_ids = []
|
100
|
+
rpc_client.run_no_wait(task: name, files: files, input: input.to_json, verbose: false) do |rpc_response|
|
101
|
+
process_rpc_response(rpc_response)
|
102
|
+
task_ids << rpc_response.task_id
|
98
103
|
end
|
104
|
+
raise NoNodesLeftError, "No nodes left to continue after 'run_no_wait' action" if @pending_targets.empty?
|
99
105
|
|
100
|
-
|
106
|
+
task_ids.uniq!
|
107
|
+
raise NotImplementedError, "Multiple task IDs: #{task_ids}" unless task_ids.count == 1
|
108
|
+
|
109
|
+
@id = task_ids.first
|
101
110
|
end
|
102
111
|
|
103
112
|
def _metadata
|
@@ -122,6 +131,10 @@ module Choria
|
|
122
131
|
def logger
|
123
132
|
@orchestrator.logger
|
124
133
|
end
|
134
|
+
|
135
|
+
def rpc_client
|
136
|
+
@orchestrator.rpc_client
|
137
|
+
end
|
125
138
|
end
|
126
139
|
end
|
127
140
|
end
|
data/lib/choria/orchestrator.rb
CHANGED
@@ -6,6 +6,24 @@ module Choria
|
|
6
6
|
class Error < StandardError; end
|
7
7
|
class DiscoverError < Error; end
|
8
8
|
|
9
|
+
module RpcResponse
|
10
|
+
def sender
|
11
|
+
self[:senderid]
|
12
|
+
end
|
13
|
+
|
14
|
+
def rpc_error?
|
15
|
+
!rpc_success?
|
16
|
+
end
|
17
|
+
|
18
|
+
def rpc_success?
|
19
|
+
[0, 1].include? self[:body][:statuscode]
|
20
|
+
end
|
21
|
+
|
22
|
+
def task_id
|
23
|
+
self[:body][:data][:task_id]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
9
27
|
include MCollective::RPC
|
10
28
|
|
11
29
|
attr_reader :logger
|
@@ -21,10 +39,16 @@ module Choria
|
|
21
39
|
@tasks_support ||= MCollective::Util::Choria.new.tasks_support
|
22
40
|
end
|
23
41
|
|
24
|
-
def run(task, targets: nil, targets_with_classes: nil, verbose: false)
|
42
|
+
def run(task, targets: nil, targets_with_classes: nil, verbose: false)
|
25
43
|
rpc_client.progress = verbose
|
44
|
+
discover(targets: targets, targets_with_classes: targets_with_classes)
|
45
|
+
raise DiscoverError, 'No requests sent, no nodes discovered' if rpc_client.discover.size.zero?
|
46
|
+
|
47
|
+
task.run
|
48
|
+
end
|
26
49
|
|
27
|
-
|
50
|
+
def discover(targets: nil, targets_with_classes: nil)
|
51
|
+
logger.debug "Targets: #{targets.nil? ? 'all' : targets})"
|
28
52
|
targets&.each { |target| rpc_client.identity_filter target }
|
29
53
|
|
30
54
|
unless targets_with_classes.nil?
|
@@ -33,51 +57,11 @@ module Choria
|
|
33
57
|
end
|
34
58
|
|
35
59
|
logger.info 'Discovering targets…'
|
36
|
-
|
37
|
-
|
38
|
-
logger.info "Downloading task '#{task.name}' on #{rpc_client.discover.size} nodes…"
|
39
|
-
rpc_client.download(task: task.name, files: task.files, verbose: verbose)
|
40
|
-
|
41
|
-
responses = []
|
42
|
-
logger.info "Starting task '#{task.name}' on #{rpc_client.discover.size} nodes…"
|
43
|
-
rpc_client.run_no_wait(task: task.name, files: task.files, input: task.input.to_json, verbose: verbose) do |response|
|
44
|
-
logger.debug " Response: '#{response}'"
|
45
|
-
responses << response
|
46
|
-
end
|
47
|
-
|
48
|
-
# TODO: Include stats in logs when logger will be available (see MCollective::RPC#printrpcstats)
|
49
|
-
|
50
|
-
task.rpc_responses = responses
|
51
|
-
end
|
52
|
-
|
53
|
-
def validate_rpc_result(result)
|
54
|
-
raise Error, "The RPC agent returned an error: #{result[:statusmsg]}" unless (result[:statuscode]).zero?
|
60
|
+
rpc_client.discover
|
55
61
|
end
|
56
62
|
|
57
63
|
def rpc_client
|
58
|
-
@rpc_client ||= rpcclient('bolt_tasks', options:
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def rpc_options
|
64
|
-
{
|
65
|
-
verbose: false,
|
66
|
-
disctimeout: nil,
|
67
|
-
timeout: 5,
|
68
|
-
config: '/etc/choria/client.conf',
|
69
|
-
collective: 'mcollective',
|
70
|
-
discovery_method: nil,
|
71
|
-
discovery_options: [],
|
72
|
-
filter: {
|
73
|
-
'fact' => [], 'cf_class' => [], 'agent' => [], 'identity' => [], 'compound' => []
|
74
|
-
},
|
75
|
-
progress_bar: false,
|
76
|
-
mcollective_limit_targets: false,
|
77
|
-
batch_size: nil,
|
78
|
-
batch_sleep_time: 1,
|
79
|
-
output_format: :json,
|
80
|
-
}
|
64
|
+
@rpc_client ||= rpcclient('bolt_tasks', options: {})
|
81
65
|
end
|
82
66
|
end
|
83
67
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: choria-colt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Romuald Conty
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -218,6 +218,7 @@ files:
|
|
218
218
|
- lib/choria/colt/version.rb
|
219
219
|
- lib/choria/orchestrator.rb
|
220
220
|
- lib/choria/orchestrator/task.rb
|
221
|
+
- lib/choria/orchestrator/task/result_set.rb
|
221
222
|
- sig/choria/colt.rbs
|
222
223
|
homepage: https://github.com/opus-codium/choria-colt
|
223
224
|
licenses: []
|