choria-colt 0.5.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5a3d94cfa899f41ced9fc0405ed5f4fb41924267d4b56f6d31ba28420c9b0e7
4
- data.tar.gz: df5bef6793badb31de0ee0185d0f8d3ca44288fedcf64b34084dbb60d1a6de30
3
+ metadata.gz: e86ae8b9274433cf146d571d2a7466a2c7c131e826ec5e4a8df9540d37f7994d
4
+ data.tar.gz: 9e4717f11b4f1f3f93c555818b9281e0118ac59a4bbf7b2573d6a85ed6e74fd9
5
5
  SHA512:
6
- metadata.gz: bba0206e6f84679cd6c7ed5fd1831dc8e2beba052b096b265ac272baadeaf20a69585d6165a37ca1f7db2ca48a6fe532eeb77171c75f656bb0aa9f2c50796ccb
7
- data.tar.gz: 02d6f79e80a664445793596ea9d96f106a14523ba9824893e7c1d43cc2c3484f63e27a1a20173d30e608e48f4d59872db9878385da2b197021d56374c502defa
6
+ metadata.gz: 3754982bc70a01d44fc4115c76693f67201d5a618d2f61b229d9b3408bea3ff2c2ac187ea5c0bd55fdbf8ca391c668f09086339ab366a85f93100c0ac1bd8973
7
+ data.tar.gz: c1a1dd4eaf6287d28d09baa7f45ff7ab297ffff5a8bd78d519d9a814dfcab74512d6f300fe6696789d1687e1a9f04aa021f90531a98143e263fae4d3a8d53688
data/.rubocop_todo.yml CHANGED
@@ -1,16 +1,11 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2022-04-28 09:34:37 UTC using RuboCop version 1.26.0.
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
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- # Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
11
- Metrics/AbcSize:
12
- Max: 22
13
-
14
9
  # Offense count: 1
15
10
  # Configuration parameters: IgnoredMethods.
16
11
  Metrics/CyclomaticComplexity:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.7.0](https://github.com/opus-codium/choria-colt/tree/v0.7.0) (2022-09-26)
4
+
5
+ [Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.6.0...v0.7.0)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Task: Fix IDs array processing [\#30](https://github.com/opus-codium/choria-colt/pull/30) ([neomilium](https://github.com/neomilium))
10
+
11
+ **Merged pull requests:**
12
+
13
+ - Use ActiveSuport::Cache instead our own Cache mecanism for tasks metadata [\#31](https://github.com/opus-codium/choria-colt/pull/31) ([neomilium](https://github.com/neomilium))
14
+ - Improve robustness when nodes are unhealthy [\#29](https://github.com/opus-codium/choria-colt/pull/29) ([neomilium](https://github.com/neomilium))
15
+
16
+ ## [v0.6.0](https://github.com/opus-codium/choria-colt/tree/v0.6.0) (2022-05-16)
17
+
18
+ [Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.5.1...v0.6.0)
19
+
20
+ **Merged pull requests:**
21
+
22
+ - Allow to run a task from any puppet environment [\#28](https://github.com/opus-codium/choria-colt/pull/28) ([neomilium](https://github.com/neomilium))
23
+ - CLI: Use symbol notation to target `:all` nodes [\#27](https://github.com/opus-codium/choria-colt/pull/27) ([neomilium](https://github.com/neomilium))
24
+ - CLI: Allow targeting on task status requests [\#26](https://github.com/opus-codium/choria-colt/pull/26) ([neomilium](https://github.com/neomilium))
25
+ - Fix `task status` command [\#25](https://github.com/opus-codium/choria-colt/pull/25) ([neomilium](https://github.com/neomilium))
26
+
27
+ ## [v0.5.1](https://github.com/opus-codium/choria-colt/tree/v0.5.1) (2022-05-02)
28
+
29
+ [Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.5.0...v0.5.1)
30
+
3
31
  ## [v0.5.0](https://github.com/opus-codium/choria-colt/tree/v0.5.0) (2022-05-02)
4
32
 
5
33
  [Full Changelog](https://github.com/opus-codium/choria-colt/compare/v0.4.0...v0.5.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
 
@@ -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,28 +28,22 @@ module Choria
19
28
 
20
29
  Parameters take the form parameter=value.
21
30
  DESC
22
- option :targets,
23
- aliases: ['--target', '-t'],
24
- desc: 'Identifies the targets of the command.'
25
- option :targets_with_classes,
26
- aliases: ['--targets-with-class', '-C'],
27
- desc: 'Select the targets which have the specified Puppet classes.'
28
- def run(*args) # rubocop:disable Metrics/AbcSize
29
- input = extract_task_parameters_from_args(args)
30
- targets = extract_targets_from_options
31
-
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)
32
37
  raise Thor::Error, 'Task name is required' if args.empty?
33
- raise Thor::Error, "Too many arguments: #{args}" unless args.count == 1
34
38
 
35
- raise Thor::Error, 'Flag --targets or --targets-with-class is required' if options['targets'].nil? && options['targets_with_classes'].nil?
39
+ input = extract_task_parameters_from_args(args)
40
+ raise Thor::Error, "Too many arguments: #{args}" unless args.count == 1
36
41
 
37
42
  task_name = args.shift
43
+ targets, targets_with_classes = extract_targets_and_filters_from_options
38
44
 
39
- logger.debug "Targets: #{targets}"
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
 
@@ -65,10 +68,9 @@ module Choria
65
68
  default: 'production'
66
69
  def show(*tasks_names)
67
70
  environment = options['environment']
68
- cache_directory = File.expand_path('.cache/colt/tasks')
69
- FileUtils.mkdir_p cache_directory
70
- cache = Cache.new(path: File.join(cache_directory, "#{environment}.yaml"))
71
71
 
72
+ require 'active_support/cache/file_store'
73
+ cache = ActiveSupport::Cache::FileStore.new File.expand_path('~/.cache/colt/tasks')
72
74
  tasks = colt.tasks(environment: environment, cache: cache)
73
75
 
74
76
  if tasks_names.empty?
@@ -84,8 +86,11 @@ module Choria
84
86
 
85
87
  A task ID is required to request Choria services and retrieve results.
86
88
  DESC
89
+ define_targets_and_filters_options
87
90
  def status(task_id)
88
- results = colt.wait_bolt_task task_id do |result|
91
+ targets, targets_with_classes = extract_targets_and_filters_from_options
92
+
93
+ results = colt.wait_bolt_task(task_id, targets: targets, targets_with_classes: targets_with_classes) do |result|
89
94
  $stdout.puts formatter.process_result(result)
90
95
  end
91
96
 
@@ -130,8 +135,17 @@ module Choria
130
135
  end.to_h
131
136
  end
132
137
 
138
+ def extract_targets_and_filters_from_options
139
+ raise Thor::Error, 'Flag --targets or --targets-with-class is required' if options['targets'].nil? && options['targets_with_classes'].nil?
140
+
141
+ targets = extract_targets_from_options
142
+ targets_with_classes = options['targets_with_classes']&.split(',')
143
+
144
+ [targets, targets_with_classes]
145
+ end
146
+
133
147
  def extract_targets_from_options
134
- return nil if options['targets'] == 'all'
148
+ return nil if options['targets'] == ':all'
135
149
 
136
150
  targets = options['targets']&.split(',')
137
151
  targets&.map do |t|
@@ -4,14 +4,25 @@ module Choria
4
4
  def self.structure(res) # rubocop:disable Metrics/AbcSize
5
5
  return res unless [0, 1].include? res[:statuscode]
6
6
 
7
+ # If data is empty, status message is an RPC error
8
+ if res[:data].empty?
9
+ res[:result] = {
10
+ _error: {
11
+ kind: 'choria/rpc',
12
+ msg: res[:statusmsg],
13
+ },
14
+ }
15
+ return res
16
+ end
17
+
7
18
  # data.stdout seems to always be JSON, so parse it once.
8
- res[:result] = JSON.parse res[:data][:stdout]
19
+ res[:result] = JSON.parse res[:data][:stdout] unless res.dig(:data, :stdout).nil?
9
20
  res[:data].delete :stdout
10
21
 
11
22
  # On one side, data.stderr is filled by the remote execution stderr.
12
23
  # On the other side, error description is in JSON (ie. '_error')
13
24
  # So merge data.stderr in '_error'.'details'
14
- unless res[:data][:stderr].empty?
25
+ unless res.dig(:data, :stderr).nil? || res[:data][:stderr].empty?
15
26
  raise NotImplementedError, 'What to do when res[:data][:stderr] contains something?' if res[:result]['_error'].empty?
16
27
 
17
28
  res[:result]['_error']['details'].merge!({ 'stderr' => res[:data][:stderr].split("\n") })
@@ -19,7 +30,7 @@ module Choria
19
30
  res[:data].delete :stderr
20
31
 
21
32
  # Convert '_output' (ie. stdout) lines into array
22
- res[:result]['_output'] = res[:result]['_output'].split("\n") unless res[:result]['_output'].nil?
33
+ res[:result]['_output'] = res[:result]['_output'].split("\n") unless res.dig(:result, '_output').nil?
23
34
 
24
35
  res
25
36
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Choria
4
4
  class Colt
5
- VERSION = '0.5.1'
5
+ VERSION = '0.7.0'
6
6
  end
7
7
  end
data/lib/choria/colt.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'choria/colt/cache'
4
3
  require 'choria/colt/version'
5
4
  require 'choria/orchestrator'
6
5
  require 'choria/orchestrator/task'
7
6
 
8
7
  require 'logger'
9
8
 
9
+ require 'active_support/cache/memory_store'
10
+
10
11
  module Choria
11
12
  class Colt
12
13
  class Error < StandardError; end
@@ -19,9 +20,9 @@ module Choria
19
20
  @orchestrator = Choria::Orchestrator.new logger: @logger
20
21
  end
21
22
 
22
- def run_bolt_task(task_name, input: {}, targets: nil, targets_with_classes: nil, &block)
23
+ def run_bolt_task(task_name, input: {}, targets: nil, targets_with_classes: nil, environment: 'production', &block)
23
24
  logger.debug "Instantiate task '#{task_name}' and validate input"
24
- task = Choria::Orchestrator::Task.new(name: task_name, input: input, orchestrator: orchestrator)
25
+ task = Choria::Orchestrator::Task.new(name: task_name, input: input, environment: environment, orchestrator: orchestrator)
25
26
 
26
27
  task.on_result(&block) if block_given?
27
28
 
@@ -33,11 +34,11 @@ module Choria
33
34
  raise
34
35
  end
35
36
 
36
- def wait_bolt_task(task_id, &block)
37
- task = Choria::Orchestrator::Task.new(id: task_id, orchestrator: orchestrator)
37
+ def wait_bolt_task(task_id, targets: nil, targets_with_classes: nil, &block)
38
+ orchestrator.discover(targets: targets, targets_with_classes: targets_with_classes)
38
39
 
40
+ task = Choria::Orchestrator::Task.new(id: task_id, orchestrator: orchestrator)
39
41
  task.on_result(&block) if block_given?
40
-
41
42
  task.wait
42
43
  task.results
43
44
  rescue Choria::Orchestrator::Error => e
@@ -45,32 +46,30 @@ module Choria
45
46
  raise
46
47
  end
47
48
 
48
- def tasks(environment:, cache: nil)
49
+ def tasks(environment:, cache: nil, force_cache_refresh: false)
50
+ cache ||= ActiveSupport::Cache::MemoryStore.new
51
+
49
52
  tasks_names = orchestrator.tasks_support.tasks(environment).map do |task|
50
53
  task['name']
51
54
  end
52
55
 
53
- return tasks_metadata(tasks_names, environment) if cache.nil?
54
-
55
- cached_tasks = cache.load
56
- return cached_tasks if cache.clean? && cached_tasks.keys.sort == tasks_names.sort
57
-
58
- updated_tasks = tasks_metadata(tasks_names, environment)
59
- cache.save updated_tasks
60
-
61
- updated_tasks
56
+ tasks_names.map do |task_name|
57
+ metadata = cache.fetch(task_name, force: force_cache_refresh) do
58
+ task_metadata(task_name, environment)
59
+ end
60
+ [task_name, metadata]
61
+ end.to_h
62
62
  end
63
63
 
64
64
  private
65
65
 
66
- def tasks_metadata(tasks, environment)
67
- logger.info "Fetching metadata for tasks (environment: '#{environment}')"
66
+ def task_metadata(name, environment)
67
+ logger.debug "Fetching metadata for task '#{name}' (environment: '#{environment}')"
68
68
 
69
- tasks.map do |task|
70
- logger.debug "Fetching metadata for task '#{task}' (environment: '#{environment}')"
71
- metadata = orchestrator.tasks_support.task_metadata(task, environment)
72
- [task, metadata]
73
- end.to_h
69
+ # FIXME: Remove this workaround when the Puppet bug is fixed
70
+ return { 'metadata' => { 'private' => true } } if ['application::utils'].include? name
71
+
72
+ orchestrator.tasks_support.task_metadata(name, environment)
74
73
  end
75
74
  end
76
75
  end
@@ -65,8 +65,8 @@ module Choria
65
65
  end
66
66
 
67
67
  def rpc_results=(results)
68
- pending_results, completed_results = results.partition { |res| res[:data][:exitcode] == -1 }
69
- @pending_targets ||= pending_results.map { |res| res[:sender] }
68
+ completed_results = results.reject { |res| res[:data][:exitcode] == -1 }
69
+ @pending_targets ||= results.map { |res| res[:sender] }
70
70
 
71
71
  new_results = completed_results.select { |res| @pending_targets.include? res[:sender] }
72
72
  new_results.each do |res|
@@ -103,6 +103,7 @@ module Choria
103
103
  end
104
104
  raise NoNodesLeftError, "No nodes left to continue after 'run_no_wait' action" if @pending_targets.empty?
105
105
 
106
+ task_ids.compact!
106
107
  task_ids.uniq!
107
108
  raise NotImplementedError, "Multiple task IDs: #{task_ids}" unless task_ids.count == 1
108
109
 
@@ -39,10 +39,16 @@ module Choria
39
39
  @tasks_support ||= MCollective::Util::Choria.new.tasks_support
40
40
  end
41
41
 
42
- def run(task, targets: nil, targets_with_classes: nil, verbose: false) # rubocop:disable Metrics/AbcSize
42
+ def run(task, targets: nil, targets_with_classes: nil, verbose: false)
43
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
44
49
 
45
- logger.debug "Running task: '#{task.name}' (targets: #{targets.nil? ? 'all' : targets})"
50
+ def discover(targets: nil, targets_with_classes: nil)
51
+ logger.debug "Targets: #{targets.nil? ? 'all' : targets})"
46
52
  targets&.each { |target| rpc_client.identity_filter target }
47
53
 
48
54
  unless targets_with_classes.nil?
@@ -51,9 +57,7 @@ module Choria
51
57
  end
52
58
 
53
59
  logger.info 'Discovering targets…'
54
- raise DiscoverError, 'No requests sent, no nodes discovered' if rpc_client.discover.size.zero?
55
-
56
- task.run
60
+ rpc_client.discover
57
61
  end
58
62
 
59
63
  def rpc_client
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.5.1
4
+ version: 0.7.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-05-02 00:00:00.000000000 Z
11
+ date: 2022-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -210,7 +210,6 @@ files:
210
210
  - choria-colt.gemspec
211
211
  - exe/colt
212
212
  - lib/choria/colt.rb
213
- - lib/choria/colt/cache.rb
214
213
  - lib/choria/colt/cli.rb
215
214
  - lib/choria/colt/cli/formatter.rb
216
215
  - lib/choria/colt/cli/thor.rb
@@ -1,33 +0,0 @@
1
- require 'yaml'
2
-
3
- module Choria
4
- class Colt
5
- class Cache
6
- def initialize(path:, force_refresh: false)
7
- @path = path
8
- @data = YAML.safe_load File.read(@path)
9
- @clean = true unless force_refresh
10
- rescue Errno::ENOENT
11
- @clean = false
12
- end
13
-
14
- def dirty?
15
- !clean?
16
- end
17
-
18
- def clean?
19
- @clean
20
- end
21
-
22
- def load
23
- @data
24
- end
25
-
26
- def save(data)
27
- @data = data
28
- File.write @path, data.to_yaml
29
- @clean = true
30
- end
31
- end
32
- end
33
- end