choria-colt 0.5.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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