dtk-action-agent 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|