Skip to content
This repository has been archived by the owner on Jan 4, 2021. It is now read-only.

(#347) Add a bolt Task data source #367

Merged
merged 1 commit into from
Nov 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions lib/mcollective/agent/bolt_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
module MCollective
module Agent
class Bolt_task < RPC::Agent
activate_when do
Util::Choria.new.tasks_support.tasks_compatible?
end

action "download" do
reply[:downloads] = 0

tasks = Util::Choria.new.tasks_support
tasks = support_factory

reply.fail!("Received empty or invalid task file specification", 3) unless request[:files]

Expand All @@ -23,7 +27,7 @@ class Bolt_task < RPC::Agent
end

action "run_and_wait" do
tasks = Util::Choria.new.tasks_support
tasks = support_factory

reply[:task_id] = request.uniqid

Expand Down Expand Up @@ -53,7 +57,7 @@ class Bolt_task < RPC::Agent
end

action "run_no_wait" do
tasks = Util::Choria.new.tasks_support
tasks = support_factory

reply[:task_id] = request.uniqid

Expand All @@ -72,7 +76,7 @@ class Bolt_task < RPC::Agent
end

action "task_status" do
tasks = Util::Choria.new.tasks_support
tasks = support_factory

begin
status = tasks.task_status(request[:task_id])
Expand All @@ -87,24 +91,29 @@ class Bolt_task < RPC::Agent
end
end

# Performs an additional authorization and audit line using the task name as action
def support_factory
Util::Choria.new.tasks_support
end

# Performs an additional authorization and audit using the task name as action
def before_processing_hook(msg, connection)
if respond_to?("authorization_hook")
original_action = request.action
task = request[:task]

begin
Log.debug(request)
if ["run_and_wait", "run_no_wait"].include?(original_action) && task
request.action = task
authorization_hook(request)
audit_request(request, connection)
original_action = request.action
task = request[:task]

begin
if ["run_and_wait", "run_no_wait"].include?(original_action) && task
request.action = task

begin
authorization_hook(request) if respond_to?("authorization_hook")
rescue
raise(RPCAborted, "You are not authorized to run Bolt Task %s" % task)
end
rescue
raise(RPCAborted, "You are not authorized to run Bolt Task %s" % task)
ensure
request.action = original_action

audit_request(request, connection)
end
ensure
request.action = original_action
end
end

Expand Down
90 changes: 90 additions & 0 deletions lib/mcollective/data/bolt_task_data.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
metadata :name => "bolt_task",
:description => "Information about past Bolt Task",
:author => "R.I.Pienaar <rip@devco.net>",
:license => "Apache-2.0",
:version => "0.0.1",
:url => "https://choria.io",
:timeout => 1

usage <<-EOU
This data plugin let you extract information about a previously
run Bolt Task for use in discovery and elsewhere.

To run a task on nodes where one previously failed:

mco tasks run myapp::update -S "bolt_task(ae561842dc7d5a9dae94f766dfb3d4c8).exitcode > 0"
EOU

dataquery :description => "Puppet Bolt Task state" do
input :query,
:prompt => "Task ID",
:description => "The Task ID to retrieve",
:type => :string,
:validation => '^[a-z,0-9]{32}$',
:maxlength => 32

output :known,
:description => "If this is a known task on this node",
:display_as => "Known Task",
:default => false

output :spool,
:description => "Where on disk the task status is stored",
:display_as => "Spool",
:default => ""

output :task,
:description => "The name of the task that was run",
:display_as => "Task",
:default => ""

output :caller,
:description => "The user who invoked the task",
:display_as => "Invoked by",
:default => ""

output :stdout,
:description => "The STDOUT output from the task",
:display_as => "STDOUT",
:default => ""

output :stderr,
:description => "The STDERR output from the task",
:display_as => "STDERR",
:default => ""

output :exitcode,
:description => "The exitcode from the task",
:display_as => "Exit Code",
:default => 127

output :runtime,
:description => "How long the task took to run",
:display_as => "Runtime",
:default => 0.0

output :start_time,
:description => "When the task was started, seconds since 1970 in UTC time",
:display_as => "Start Time",
:default => 0

output :wrapper_spawned,
:description => "Did the wrapper start successfully",
:display_as => "Wrapper Spawned",
:default => false

output :wrapper_error,
:description => "Error output from the wrapper command",
:display_as => "Wrapper Error",
:default => ""

output :wrapper_pid,
:description => "The PID of the wrapper that runs the task",
:display_as => "Wrapper PID",
:default => -1

output :completed,
:description => "Did the task complete running",
:display_as => "Completed",
:default => false
end
31 changes: 31 additions & 0 deletions lib/mcollective/data/bolt_task_data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module MCollective
module Data
class Bolt_task_data < Base
activate_when do
Util::Choria.new.tasks_support.tasks_compatible?
end

query do |taskid|
tasks = Util::Choria.new.tasks_support

begin
status = tasks.task_status(taskid)

result[:known] = true

if status["task"]
tasks.task_status(taskid).each do |item, value|
value = value.utc.to_i if value.is_a?(Time)

result[item.intern] = value
end

result[:start_time] = result[:start_time].to_i
end
rescue
Log.debug("Task %s was not found, returning default data" % taskid)
end
end
end
end
end
11 changes: 9 additions & 2 deletions lib/mcollective/util/tasks_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ def initialize(choria, cache_dir=nil)
@cache_dir = cache_dir || @choria.get_option("choria.tasks_cache")
end

# Determines if a machine is compatible with running bolt
#
# @note this should check for a compatible version of Puppet more
# @return [Boolean]
def tasks_compatible?
File.exist?(wrapper_path) && File.executable?(wrapper_path)
end

# Path to binaries like wrappers etc
def bin_path
if Util.windows?
Expand Down Expand Up @@ -453,8 +461,7 @@ def file_sha256(file_path)
# Determines the file size of a specific file
#
# @param file_path [String] a full path to the file to check
# @return [Integer] bytes
# @raise [StandardError] when the file does not exist
# @return [Integer] bytes, -1 when the file does not exist
def file_size(file_path)
File.stat(file_path).size
rescue
Expand Down
2 changes: 2 additions & 0 deletions module/data/plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mcollective_choria::server_files:
- agent/bolt_task.rb
- agent/choria_util.rb
- audit/choria.rb
- data/bolt_task_data.rb
- registration/choria.rb
mcollective_choria::client_files:
- application/choria.rb
Expand Down Expand Up @@ -59,6 +60,7 @@ mcollective_choria::common_files:
- agent/choria_util.ddl
- connector/nats.ddl
- connector/nats.rb
- data/bolt_task_data.ddl
- security/choria.rb
- util/federation_broker.rb
- util/federation_broker/base.rb
Expand Down
18 changes: 18 additions & 0 deletions spec/unit/mcollective/util/tasks_support_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ module Util
FileUtils.rm_rf("/tmp/tasks-cache-#{$$}")
end

describe "#tasks_compatible?" do
it "should report compatible only when the wrapper exist and is executable" do
ts.stubs(:wrapper_path).returns("/nonexisting/wrapper")

File.expects(:exist?).with("/nonexisting/wrapper").returns(false)
File.expects(:executable?).with("/nonexisting/wrapper").never
expect(ts.tasks_compatible?).to be(false)

File.expects(:exist?).with("/nonexisting/wrapper").returns(true)
File.expects(:executable?).with("/nonexisting/wrapper").returns(false)
expect(ts.tasks_compatible?).to be(false)

File.expects(:exist?).with("/nonexisting/wrapper").returns(true)
File.expects(:executable?).with("/nonexisting/wrapper").returns(true)
expect(ts.tasks_compatible?).to be(true)
end
end

describe "#task_runtime" do
it "should support succesfully completed tasks" do
ts.stubs(:request_spooldir).returns("spec/fixtures/tasks/completed_spool")
Expand Down