dtk-action-agent 0.0.1 → 0.0.2
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/bin/dtk-action-agent +5 -3
- data/dtk-action-agent.gemspec +1 -1
- data/lib/arbiter.rb +19 -31
- data/lib/command.rb +90 -0
- data/lib/commander.rb +76 -0
- data/lib/dtk-action-agent/version.rb +1 -1
- data/lib/logger.rb +48 -0
- data/lib/positioner.rb +65 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d0db3719b17b9856404b7b9e81a1e9f91bc1c04
|
4
|
+
data.tar.gz: 3bcd4713fb782f37a6894f38b99a9cba3b4e7a59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ccabb36b8cdd75c8a28b043255750f165ef03a9257e3f9e6d8ca72aaba4ec45f8b2ee8e2dca10fb1f42abe13e65e638d55c04b6eb119b9f0168c974a88fe0c6
|
7
|
+
data.tar.gz: 628007e0af112995cf08fd4e4bf395d38a213d3af5e44ddf57aa584b6e5c59f3b4f8715cce3795df42bec5a5c64eb8ac5c3cd065591ce7cf009059999ec9b899
|
data/bin/dtk-action-agent
CHANGED
@@ -1,19 +1,21 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
3
|
require 'json'
|
4
|
+
require File.expand_path('../../lib/logger', __FILE__)
|
4
5
|
require File.expand_path('../../lib/arbiter', __FILE__)
|
6
|
+
require File.expand_path('../../lib/positioner', __FILE__)
|
7
|
+
require File.expand_path('../../lib/commander', __FILE__)
|
5
8
|
|
6
9
|
unless ARGV[0]
|
7
10
|
error_msg = {
|
8
|
-
:
|
11
|
+
:errors => ['You need to provide JSON with argument to method call']
|
9
12
|
}
|
10
13
|
print error_msg.to_json
|
11
14
|
else
|
12
15
|
content = JSON.parse(ARGV[0])
|
13
16
|
arbiter = DTK::Agent::Arbiter.new(content)
|
14
|
-
arbiter.run()
|
17
|
+
results = arbiter.run()
|
15
18
|
|
16
|
-
results = arbiter.results()
|
17
19
|
# we print results in json so that they can be serialized in another
|
18
20
|
print results.to_json
|
19
21
|
end
|
data/dtk-action-agent.gemspec
CHANGED
data/lib/arbiter.rb
CHANGED
@@ -10,43 +10,31 @@ module DTK
|
|
10
10
|
def initialize(consumer_hash)
|
11
11
|
@received_message = consumer_hash
|
12
12
|
@process_pool = []
|
13
|
-
|
13
|
+
@execution_list = @received_message['execution_list']||[]
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# no need to run other commands if there is no execution list
|
16
|
+
if @execution_list.empty?
|
17
|
+
Log.error "Execution list is not provided or empty, DTK Action Agent has nothing to run"
|
18
|
+
return
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
next
|
25
|
-
end
|
26
|
-
end
|
27
|
-
break
|
28
|
-
end
|
21
|
+
# sets enviorment variables
|
22
|
+
Commander.set_environment_variables(@received_message['env_vars'])
|
23
|
+
@positioner = Positioner.new(@received_message['positioning'])
|
24
|
+
@commander = Commander.new(@received_message['execution_list'])
|
29
25
|
end
|
30
26
|
|
31
|
-
def
|
32
|
-
|
33
|
-
{
|
34
|
-
:status => process.status.exitstatus,
|
35
|
-
:stdout => process.out,
|
36
|
-
:stderr => process.err
|
37
|
-
}
|
38
|
-
end
|
39
|
-
end
|
27
|
+
def run
|
28
|
+
return { :results => [], :errors => Log.execution_errrors } if @execution_list.empty?
|
40
29
|
|
41
|
-
|
42
|
-
@
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
30
|
+
# start positioning files
|
31
|
+
@positioner.run()
|
32
|
+
|
33
|
+
# start commander runnes
|
34
|
+
@commander.run()
|
35
|
+
|
36
|
+
# return results
|
37
|
+
{ :results => @commander.results(), :errors => Log.execution_errrors }
|
50
38
|
end
|
51
39
|
|
52
40
|
end
|
data/lib/command.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module DTK
|
2
|
+
module Agent
|
3
|
+
|
4
|
+
##
|
5
|
+
# This is container for command as received from Node Agent
|
6
|
+
|
7
|
+
class Command
|
8
|
+
|
9
|
+
attr_accessor :command_type, :command, :if_success, :if_fail, :process, :child_task
|
10
|
+
|
11
|
+
##
|
12
|
+
# command - string to be run on system, e.g. ifconfig
|
13
|
+
# type - type of command e.g. syscall, ruby
|
14
|
+
# if - callback to be run if exit status is = 0
|
15
|
+
# unless - callback to be run if exit status is != 0
|
16
|
+
# child_task - if it is spawned by another main task
|
17
|
+
#
|
18
|
+
def initialize(value_hash)
|
19
|
+
@command_type = value_hash['type']
|
20
|
+
@command = value_hash['command']
|
21
|
+
@if_success = value_hash['if']
|
22
|
+
@if_fail = value_hash['unless']
|
23
|
+
@spawned = false
|
24
|
+
@child_task = value_hash['child_task'] || false
|
25
|
+
|
26
|
+
if @if_success && @if_fail
|
27
|
+
Log.warn "Unexpected case, both if/unless conditions have been set for command #{@command}(#{@command_type})"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Creates Posix Spawn of given process
|
33
|
+
#
|
34
|
+
def start_task
|
35
|
+
begin
|
36
|
+
@process = POSIX::Spawn::Child.new(@command)
|
37
|
+
Log.debug("Command started: '#{self.to_s}'")
|
38
|
+
rescue Exception => e
|
39
|
+
@error_message = e.message
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Checks if there is callaback present, callback beeing if/unless command
|
45
|
+
#
|
46
|
+
def callback_pending?
|
47
|
+
return false if @spawned
|
48
|
+
command_to_run = (self.exitstatus.to_i == 0) ? @if_success : @if_fail
|
49
|
+
!!command_to_run
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
##
|
54
|
+
# Creates Command object for callback, first check 'if' than 'unless'. There should be no both set so priority is given
|
55
|
+
# to 'if' callback in case there are two
|
56
|
+
#
|
57
|
+
def spawn_callback_task
|
58
|
+
callback_command = (self.exitstatus.to_i == 0) ? @if_success : @if_fail
|
59
|
+
new_command = Command.new('type' => @command_type, 'command' => callback_command, 'child_task' => true)
|
60
|
+
@spawned = true
|
61
|
+
new_command
|
62
|
+
end
|
63
|
+
|
64
|
+
def exited?
|
65
|
+
return true if @error_message
|
66
|
+
self.process.status.exited?
|
67
|
+
end
|
68
|
+
|
69
|
+
def exitstatus
|
70
|
+
return 1 if @error_message
|
71
|
+
self.process.status.exitstatus
|
72
|
+
end
|
73
|
+
|
74
|
+
def out
|
75
|
+
return '' if @error_message
|
76
|
+
self.process.out
|
77
|
+
end
|
78
|
+
|
79
|
+
def err
|
80
|
+
return @error_message if @error_message
|
81
|
+
self.process.err
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_s
|
85
|
+
"#{@command} (#{command_type})"
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/commander.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.expand_path('../command', __FILE__)
|
2
|
+
|
3
|
+
|
4
|
+
module DTK
|
5
|
+
module Agent
|
6
|
+
class Commander
|
7
|
+
|
8
|
+
def initialize(execution_list)
|
9
|
+
@command_tasks = execution_list.collect { |command| Command.new(command) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
@command_tasks.each do |command_task|
|
14
|
+
command_task.start_task
|
15
|
+
end
|
16
|
+
|
17
|
+
loop do
|
18
|
+
all_finished = true
|
19
|
+
sleep(1)
|
20
|
+
|
21
|
+
# we check status of all tasks
|
22
|
+
# (Usually is not good practice to change array/map you are iterating but this seems as cleanest solutions)
|
23
|
+
@command_tasks.each do |command_task|
|
24
|
+
|
25
|
+
# is task finished
|
26
|
+
if command_task.exited?
|
27
|
+
Log.debug("Command '#{command_task}' finished, with status #{command_task.exitstatus}")
|
28
|
+
|
29
|
+
# if there is a callback start it
|
30
|
+
if command_task.callback_pending?
|
31
|
+
new_command_task = command_task.spawn_callback_task
|
32
|
+
new_command_task.start_task
|
33
|
+
@command_tasks << new_command_task
|
34
|
+
Log.debug("Command '#{new_command_task}' spawned as callback")
|
35
|
+
# new task added we need to check again
|
36
|
+
all_finished = false
|
37
|
+
end
|
38
|
+
else
|
39
|
+
# we are not ready yet, some tasks need to finish
|
40
|
+
all_finished = false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
break if all_finished
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def results
|
49
|
+
@command_tasks.collect do |command_task|
|
50
|
+
process = command_task.process
|
51
|
+
{
|
52
|
+
:status => command_task.exitstatus,
|
53
|
+
:stdout => command_task.out,
|
54
|
+
:stderr => command_task.err,
|
55
|
+
:description => command_task.to_s,
|
56
|
+
:child_task => command_task.child_task
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
##
|
64
|
+
# Sets environmental variables
|
65
|
+
def self.set_environment_variables(env_vars_hash)
|
66
|
+
return unless env_vars_hash
|
67
|
+
env_vars_hash.each do |k, v|
|
68
|
+
ENV[k] = v.to_s.strip
|
69
|
+
Log.debug("Environment variable set (#{k}: #{v})")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/logger.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module DTK
|
5
|
+
module Agent
|
6
|
+
class Log
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
attr_accessor :logger, :error_msgs
|
10
|
+
|
11
|
+
LOG_TO_CONSOLE = false
|
12
|
+
LOG_TO_FILE = '/var/log/action-agent.log'
|
13
|
+
# LOG_TO_FILE = '/Users/haris/test.log'
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
# @logger = Logger.new(File.new(LOG_TO_FILE,'w'))
|
17
|
+
@error_msgs =[]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.execution_errrors()
|
21
|
+
self.instance.error_msgs
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.debug(msg)
|
25
|
+
# self.instance.logger.debug(msg)
|
26
|
+
ap "debug: #{msg}" if LOG_TO_CONSOLE
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.info(msg)
|
30
|
+
# self.instance.logger.info(msg)
|
31
|
+
ap "info: #{msg}" if LOG_TO_CONSOLE
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.warn(msg)
|
35
|
+
# self.instance.logger.warn(msg)
|
36
|
+
ap "warn: #{msg}" if LOG_TO_CONSOLE
|
37
|
+
self.instance.error_msgs << msg
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.error(msg)
|
41
|
+
# self.instance.logger.error(msg)
|
42
|
+
ap "error: #{msg}" if LOG_TO_CONSOLE
|
43
|
+
self.instance.error_msgs << msg
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/positioner.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'git'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module DTK
|
6
|
+
module Agent
|
7
|
+
class Positioner
|
8
|
+
|
9
|
+
attr_accessor :position_file_info
|
10
|
+
|
11
|
+
def initialize(*position_file_info)
|
12
|
+
@position_file_info = position_file_info.flatten.compact
|
13
|
+
end
|
14
|
+
|
15
|
+
def run()
|
16
|
+
@position_file_info.each do |pfi|
|
17
|
+
case pfi['source']['type'].to_sym
|
18
|
+
when :git
|
19
|
+
position_git(pfi)
|
20
|
+
when :in_payload
|
21
|
+
position_in_payload(pfi)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def position_git(position_info)
|
29
|
+
folder_path = prepare_path(position_info)
|
30
|
+
git_url = position_info['source']['url']
|
31
|
+
git_branch = position_info['source']['ref']
|
32
|
+
|
33
|
+
unless File.directory?(folder_path)
|
34
|
+
begin
|
35
|
+
g_repo = Git.clone("#{git_url}", '', :path => folder_path, :branch => git_branch)
|
36
|
+
Log.info("Positioner successfully cloned git repository '#{git_url}@#{git_branch}' to location '#{folder_path}'")
|
37
|
+
rescue Exception => e
|
38
|
+
Log.error("Positioner unable to clone #{git_url}")
|
39
|
+
Log.error(e.message)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
Log.warn("Positioner detected folder '#{folder_path}' skipping git clone")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def position_in_payload(position_info)
|
47
|
+
file_path = prepare_path(position_info)
|
48
|
+
file_content = position_info['source']['content']
|
49
|
+
# write to file
|
50
|
+
File.open(file_path, 'w') { |file| file.write(file_content) }
|
51
|
+
Log.info("Positioner successfully created 'IN_PAYLOAD' file '#{file_path}'")
|
52
|
+
end
|
53
|
+
|
54
|
+
def prepare_path(position_info)
|
55
|
+
path = position_info['target']['path']
|
56
|
+
|
57
|
+
# create necessery dir structure
|
58
|
+
FileUtils.mkdir_p(File.dirname(path))
|
59
|
+
|
60
|
+
path
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dtk-action-agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rich PELAVIN
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: posix-spawn
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.8.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: git
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.2.9.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.2.9.1
|
55
69
|
description: The DTK Action Agent is designed to run commands on remote machine.
|
56
70
|
email:
|
57
71
|
- rich@reactor8.com
|
@@ -64,7 +78,11 @@ files:
|
|
64
78
|
- bin/dtk-action-agent
|
65
79
|
- dtk-action-agent.gemspec
|
66
80
|
- lib/arbiter.rb
|
81
|
+
- lib/command.rb
|
82
|
+
- lib/commander.rb
|
67
83
|
- lib/dtk-action-agent/version.rb
|
84
|
+
- lib/logger.rb
|
85
|
+
- lib/positioner.rb
|
68
86
|
homepage: ''
|
69
87
|
licenses:
|
70
88
|
- GPL-3.0
|