dtk-action-agent 0.0.6 → 0.0.7

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
  SHA1:
3
- metadata.gz: 59abb9f8d4c53e5d82e17b93033b27601585ff70
4
- data.tar.gz: b3885713293250f5beb51480f31669bfc723054f
3
+ metadata.gz: 91b2a5186f6e4ccaba5687ce9ffac7b2baa1c603
4
+ data.tar.gz: 7b2176431344e9e2078fe2372616cff51b705627
5
5
  SHA512:
6
- metadata.gz: ee0795deb8734cac59bfad5fb537602f6947c7c1e9e6dd6f674798950941752fd18f09e5c53e857b8facffcf9ee9c8c9b57c6c003bcc075dfe3ab2234cf4abaf
7
- data.tar.gz: 8391a8ebaaf3a2efbc5c14a5b273e7e18977555ad5d036040ae1cf098756a1927111fe68f4527cdc39ca5ab7156346a2d903b8105df5a21cc65373b2b1a3f53a
6
+ metadata.gz: a0e490c27b693136932285d1e1632270725b4a2381eef22f0e00c59cdcf281f5fcf704faad40a3ca7d021f2fdf37cdd935845c63fce90f6306fdab5049e9fedd
7
+ data.tar.gz: ac498ebb4fd65f5f289e61a198d8e2dcd2de13f2b7f74a1d01db6f25ba5d7c710ed9f32195b5df30363e1d6f4bacde956b70c3f9aef3d13cd4985be3f265675c
@@ -18,8 +18,8 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ["lib"]
19
19
  gem.version = "#{DTK::ActionAgent::VERSION}.#{ARGV[3]}".chomp(".")
20
20
 
21
- gem.add_dependency 'posix-spawn','0.3.8'
22
21
  gem.add_dependency 'awesome_print', '1.1.0'
23
22
  gem.add_dependency 'json', '1.8.2'
24
23
  gem.add_dependency 'git', '1.2.9.1'
24
+ gem.add_dependency 'after_do', '0.3.1'
25
25
  end
@@ -1,5 +1,4 @@
1
1
  require 'ap'
2
- require 'posix-spawn'
3
2
 
4
3
  module DTK
5
4
  module Agent
@@ -12,6 +11,11 @@ module DTK
12
11
  @process_pool = []
13
12
  @execution_list = @received_message['execution_list']||[]
14
13
 
14
+ @top_task_id = @received_message['top_task_id']
15
+ @task_id = @received_message['task_id']
16
+ @module_name = @received_message['module_name']
17
+ @action_name = @received_message['action_name']
18
+
15
19
  # no need to run other commands if there is no execution list
16
20
  if @execution_list.empty?
17
21
  Log.error "Execution list is not provided or empty, DTK Action Agent has nothing to run"
@@ -27,8 +31,14 @@ module DTK
27
31
  # start commander runnes
28
32
  @commander.run()
29
33
 
34
+ results = @commander.results()
35
+
36
+ if @top_task_id && @task_id
37
+ Log.log_results(@execution_list, results, @module_name, @action_name, @top_task_id, @task_id)
38
+ end
39
+
30
40
  # return results
31
- { :results => @commander.results(), :errors => Log.execution_errors }
41
+ { :results => results, :errors => Log.execution_errors }
32
42
  end
33
43
 
34
44
  end
@@ -1,3 +1,6 @@
1
+ require 'open3'
2
+ require 'timeout'
3
+
1
4
  module DTK
2
5
  module Agent
3
6
 
@@ -6,7 +9,7 @@ module DTK
6
9
 
7
10
  class Command
8
11
 
9
- attr_accessor :command_type, :command, :if_success, :if_fail, :process, :child_task
12
+ attr_accessor :command_type, :command, :process, :backtrace
10
13
 
11
14
  ##
12
15
  # command - string to be run on system, e.g. ifconfig
@@ -14,7 +17,6 @@ module DTK
14
17
  # if - callback to be run if exit status is = 0
15
18
  # unless - callback to be run if exit status is != 0
16
19
  # stdout_redirect - redirect all output to stdout
17
- # child_task - if it is spawned by another main task
18
20
  #
19
21
 
20
22
  STDOUT_REDIRECT = ' 2>&1'
@@ -23,15 +25,15 @@ module DTK
23
25
  @command_type = value_hash['type']
24
26
  @command = value_hash['command']
25
27
  @stdout_redirect = !!value_hash['stdout_redirect']
26
- @if_success = value_hash['if']
27
- @if_fail = value_hash['unless']
28
- @spawned = false
29
- @child_task = value_hash['child_task'] || false
28
+
29
+ @if = value_hash['if']
30
+ @unless = value_hash['unless']
31
+
30
32
  @timeout = (value_hash['timeout'] || 0).to_i
31
33
 
32
34
  @env_vars = value_hash['env_vars']
33
35
 
34
- if @if_success && @if_fail
36
+ if @if && @unless
35
37
  Log.warn "Unexpected case, both if/unless conditions have been set for command #{@command}(#{@command_type})"
36
38
  end
37
39
  end
@@ -40,15 +42,20 @@ module DTK
40
42
  # Creates Posix Spawn of given process
41
43
  #
42
44
  def start_task
43
-
44
45
  begin
45
46
  Commander.set_environment_variables(@env_vars)
46
- @process = POSIX::Spawn::Child.new(formulate_command, :timeout => @timeout)
47
- Log.debug("Command started: '#{self.to_s}'")
48
- rescue POSIX::Spawn::TimeoutExceeded => e
47
+
48
+ Timeout.timeout(@timeout) do
49
+ Log.debug("Command started: '#{self.to_s}'")
50
+ @out, @err, @process_status = Open3.capture3(formulate_command)
51
+ end
52
+
53
+ rescue Timeout::Error
49
54
  @error_message = "Timeout (#{@timeout} sec) for this action has been exceeded"
50
55
  rescue Exception => e
51
56
  @error_message = e.message
57
+ @backtrace = e.backtrace
58
+ Log.error(@error_message, @backtrace)
52
59
  ensure
53
60
  Commander.clear_environment_variables(@env_vars)
54
61
  end
@@ -58,50 +65,61 @@ module DTK
58
65
  # Checks if there is callaback present, callback beeing if/unless command
59
66
  #
60
67
  def callback_pending?
61
- return false if @spawned
62
- command_to_run = (self.exitstatus.to_i == 0) ? @if_success : @if_fail
63
- !!command_to_run
68
+ @if || @unless
64
69
  end
65
70
 
66
71
  def is_positioning?
67
72
  'file'.eql?(@command_type)
68
73
  end
69
74
 
70
-
71
75
  ##
72
- # Creates Command object for callback, first check 'if' than 'unless'. There should be no both set so priority is given
73
- # to 'if' callback in case there are two
76
+ # Returns true/false based on condition data and result of process
74
77
  #
75
- def spawn_callback_task
76
- callback_command = (self.exitstatus.to_i == 0) ? @if_success : @if_fail
77
- new_command = Command.new('type' => @command_type, 'command' => callback_command, 'child_task' => true)
78
- @spawned = true
79
- new_command
78
+ def run_condition_task
79
+ condition_command = @if
80
+ condition_command ||= @unless
81
+
82
+ # this is needed since Timeout block will not allow initialization of new variables
83
+ condition_process_status = nil
84
+
85
+ begin
86
+ Timeout.timeout(@timeout) do
87
+ _out, _err, condition_process_status = Open3.capture3(condition_command)
88
+ end
89
+ rescue Exception => e
90
+ # do not log error in cases it was expected. Meaning that 'unless' condition was set.
91
+ Log.warn("Condition command '#{condition_command}' ran into an exception, message: #{e.message}") unless @unless
92
+ # return true if unless condition was used
93
+ return @unless ? true : false
94
+ end
95
+
96
+ return condition_process_status.exitstatus > 0 ? false : true if @if
97
+ return condition_process_status.exitstatus > 0 ? true : false if @unless
80
98
  end
81
99
 
82
100
  def exited?
83
101
  return true if @error_message
84
- self.process.status.exited?
102
+ @process_status.exited?
103
+ end
104
+
105
+ def started?
106
+ return true if @error_message
107
+ !!@process_status
85
108
  end
86
109
 
87
110
  def exitstatus
88
111
  return 1 if @error_message
89
- self.process.status.exitstatus
112
+ @process_status.exitstatus
90
113
  end
91
114
 
92
115
  def out
93
116
  return '' if @error_message
94
- self.process.out
117
+ @out.encode('UTF-8', :invalid => :replace, :undef => :replace, :replace => '')
95
118
  end
96
119
 
97
120
  def err
98
121
  return @error_message if @error_message
99
- self.process.err
100
- end
101
-
102
- def started?
103
- return true if @error_message
104
- !!self.process
122
+ @err.encode!('UTF-8', :invalid => :replace, :undef => :replace, :replace => '')
105
123
  end
106
124
 
107
125
  def to_s
@@ -114,7 +132,7 @@ module DTK
114
132
  # Based on stdout-redirect flag
115
133
  #
116
134
  def formulate_command
117
- @stdout_redirect ? "#{@command} #{STDOUT_REDIRECT}" : @command
135
+ @command
118
136
  end
119
137
 
120
138
  end
@@ -28,8 +28,13 @@ module DTK
28
28
 
29
29
  def sequential_run
30
30
  @command_tasks.each do |command_task|
31
- command_task.start_task
32
31
 
32
+ if command_task.callback_pending? && !command_task.run_condition_task
33
+ Log.info("Skipping command task #{command_task}, conditions have not been met")
34
+ next
35
+ end
36
+
37
+ command_task.start_task
33
38
  loop do
34
39
  if command_task.exited?
35
40
  Log.debug("Command '#{command_task}' finished, with status #{command_task.exitstatus}")
@@ -37,9 +42,6 @@ module DTK
37
42
  # exit if there is an error
38
43
  return nil if (command_task.exitstatus.to_i > 0)
39
44
 
40
- # if there is a callback start it
41
- spawn_callback_task(command_task) if command_task.callback_pending?
42
-
43
45
  break
44
46
  end
45
47
 
@@ -51,6 +53,10 @@ module DTK
51
53
 
52
54
  def parallel_run
53
55
  @command_tasks.each do |command_task|
56
+ if command_task.callback_pending? && !command_task.run_condition_task
57
+ Log.info("Skipping command task #{command_task}, conditions have not been met")
58
+ next
59
+ end
54
60
  command_task.start_task
55
61
  end
56
62
 
@@ -64,13 +70,6 @@ module DTK
64
70
  # is task finished
65
71
  if command_task.exited?
66
72
  Log.debug("Command '#{command_task}' finished, with status #{command_task.exitstatus}")
67
-
68
- # if there is a callback start it
69
- if command_task.callback_pending?
70
- spawn_callback_task(command_task, true)
71
- # new task added we need to check again
72
- all_finished = false
73
- end
74
73
  else
75
74
  # we are not ready yet, some tasks need to finish
76
75
  all_finished = false
@@ -81,13 +80,6 @@ module DTK
81
80
  end
82
81
  end
83
82
 
84
- def spawn_callback_task(command_task, start_task = false)
85
- new_command_task = command_task.spawn_callback_task
86
- new_command_task.start_task if start_task
87
- @command_tasks << new_command_task
88
- Log.debug("Command '#{new_command_task}' spawned as callback")
89
- end
90
-
91
83
  def results
92
84
  res = @command_tasks.collect do |command_task|
93
85
  next unless command_task.started?
@@ -96,7 +88,7 @@ module DTK
96
88
  :stdout => command_task.out,
97
89
  :stderr => command_task.err,
98
90
  :description => command_task.to_s,
99
- :child_task => command_task.child_task
91
+ :backtrace => command_task.backtrace
100
92
  }
101
93
  end
102
94
 
@@ -1,5 +1,5 @@
1
1
  module DTK
2
2
  module ActionAgent
3
- VERSION = "0.0.6"
3
+ VERSION = "0.0.7"
4
4
  end
5
5
  end
@@ -1,20 +1,29 @@
1
1
  require 'logger'
2
2
  require 'singleton'
3
+ require 'after_do'
4
+ require 'fileutils'
5
+ require 'json'
3
6
 
4
7
  module DTK
5
8
  module Agent
6
9
  class Log
7
10
  include Singleton
8
11
 
9
- attr_accessor :logger, :error_msgs
12
+ attr_accessor :logger, :error_msgs, :all_msgs
13
+
14
+ self.singleton_class.extend AfterDo
10
15
 
11
16
  LOG_TO_CONSOLE = false
12
17
  LOG_TO_FILE = '/var/log/action-agent.log'
18
+ LOGS_DIR = '/var/log/dtk/action-agent'
13
19
  # LOG_TO_FILE = '/Users/haris/test.log'
14
20
 
15
21
  def initialize
16
22
  # @logger = Logger.new(File.new(LOG_TO_FILE,'w'))
23
+ @all_msgs =[]
17
24
  @error_msgs =[]
25
+
26
+ FileUtils.mkdir_p LOGS_DIR unless File.directory?(LOGS_DIR)
18
27
  end
19
28
 
20
29
  def self.execution_errors()
@@ -31,16 +40,35 @@ module DTK
31
40
  ap "info: #{msg}" if LOG_TO_CONSOLE
32
41
  end
33
42
 
34
- def self.warn(msg)
43
+ def self.warn(msg, backtrace = nil)
35
44
  # self.instance.logger.warn(msg)
36
45
  ap "warn: #{msg}" if LOG_TO_CONSOLE
37
- self.instance.error_msgs << msg
46
+ self.instance.error_msgs << { :message => msg, :backtrace => backtrace }
38
47
  end
39
48
 
40
- def self.error(msg)
49
+ def self.error(msg, backtrace = nil)
41
50
  # self.instance.logger.error(msg)
42
51
  ap "error: #{msg}" if LOG_TO_CONSOLE
43
- self.instance.error_msgs << msg
52
+ self.instance.error_msgs << { :message => msg, :backtrace => backtrace }
53
+ end
54
+
55
+ def self.log_results(params_in, results, component_name, action_name, top_task_id, task_id)
56
+ component_dir = File.join(LOGS_DIR, "#{component_name}_#{top_task_id}")
57
+ FileUtils.mkdir_p(component_dir) unless File.directory?(component_dir)
58
+
59
+ filename = File.join(component_dir, "#{task_id}_#{action_name}.log")
60
+ File.open(filename, 'w') do |file|
61
+ file.puts('Input data: ')
62
+ file.puts JSON.pretty_generate(params_in)
63
+ file.puts self.instance.all_msgs.join("\n")
64
+ file.puts('Results: ')
65
+ file.puts JSON.pretty_generate(results)
66
+ end
67
+ end
68
+
69
+
70
+ self.singleton_class.after :debug, :info, :warn, :error do |msg, _backtrace|
71
+ self.instance.all_msgs << msg
44
72
  end
45
73
 
46
74
  end
@@ -1,33 +1,48 @@
1
1
  require 'thread'
2
2
  require 'git'
3
3
  require 'fileutils'
4
+ require File.expand_path('../utils/permission', __FILE__)
5
+
4
6
 
5
7
  module DTK
6
8
  module Agent
7
9
  class Position
8
10
 
9
- attr_accessor :position_file_info, :exitstatus, :started, :out, :err, :child_task
11
+ attr_accessor :position_file_info, :exitstatus, :started, :out, :err, :backtrace, :owner, :mode
10
12
 
11
13
  def initialize(command_hash)
12
14
  source_info, target_info = command_hash['source'], command_hash['target']
13
15
 
16
+ @exited = false
17
+ @started = false
18
+ @exitstatus = 0
19
+
14
20
  @type = source_info['type'].to_sym
15
21
  @git_url = source_info['url']
16
22
  @branch = source_info['ref'] || 'master'
17
23
  @content = source_info['content']
18
24
 
25
+ @owner = command_hash['owner']
26
+
27
+ if command_hash['mode']
28
+ if DTK::Utils::Permission.check(command_hash['mode'])
29
+ @mode = command_hash['mode'].to_s.oct
30
+ else
31
+ trigger_error("Permissions '#{command_hash['mode']}' are not valid, aborting operation")
32
+ end
33
+ end
34
+
19
35
  @env_vars = command_hash['env_vars']
20
36
 
21
37
  @target_path = target_info['path']
22
-
23
- @exited = false
24
- @started = false
25
- @exitstatus = 0
26
- @child_task = false
27
38
  end
28
39
 
29
40
  def start_task()
30
41
  @started = true
42
+
43
+ # for cases when there was an error
44
+ return if @exited
45
+
31
46
  prepare_path()
32
47
 
33
48
  Commander.set_environment_variables(@env_vars)
@@ -41,7 +56,7 @@ module DTK
41
56
  end
42
57
  rescue Exception => e
43
58
  cleanup_path()
44
- raise e
59
+ trigger_error(e.message, 1, e.backtrace)
45
60
  ensure
46
61
  Commander.clear_environment_variables(@env_vars)
47
62
  end
@@ -59,10 +74,6 @@ module DTK
59
74
  @started
60
75
  end
61
76
 
62
- def spawn_callback_task
63
- raise "Callback task is not supported for positioner"
64
- end
65
-
66
77
  def callback_pending?
67
78
  # not supported at the moment
68
79
  false
@@ -74,6 +85,14 @@ module DTK
74
85
 
75
86
  private
76
87
 
88
+ def trigger_error(error_message, err_status = 1, error_backtrace = nil)
89
+ @err = error_message
90
+ Log.error(error_message, error_backtrace)
91
+ @exitstatus = err_status
92
+ @started = true
93
+ @exited = true
94
+ end
95
+
77
96
  def position_git()
78
97
  unless File.directory?(@target_path)
79
98
  begin
@@ -81,9 +100,7 @@ module DTK
81
100
  Log.info("Positioner successfully cloned git repository '#{@git_url}@#{@branch}' to location '#{@target_path}'")
82
101
  rescue Exception => e
83
102
  cleanup_path()
84
- @exitstatus = 1
85
- Log.error("Positioner unable to clone #{@git_url}")
86
- Log.error(e.message)
103
+ trigger_error("Positioner unable to clone provided url #{@git_url}. Reasone: #{e.message}", 1, e.backtrace)
87
104
  end
88
105
  else
89
106
  Log.warn("Positioner detected folder '#{@target_path}' skipping git clone")
@@ -92,10 +109,29 @@ module DTK
92
109
  @exited = true
93
110
  end
94
111
 
95
- def position_in_payload(position_info)
112
+ def position_in_payload()
96
113
  # write to file
97
- File.open(@target_path, 'w') { |file| file.write(@content) }
114
+ file = File.open(@target_path, 'w')
115
+ file.write(@content)
116
+
117
+ if @owner
118
+ begin
119
+ FileUtils.chown(@owner, nil, file.path)
120
+ rescue Exception => e
121
+ Log.warn("Not able to set owner '#{@owner}', reason: " + e.message)
122
+ end
123
+ end
124
+
125
+ if @mode
126
+ begin
127
+ FileUtils.chmod(@mode, file.path)
128
+ rescue Exception => e
129
+ Log.warn("Not able to set chmod permissions '#{@mode}', reason: " + e.message)
130
+ end
131
+ end
132
+
98
133
  Log.info("Positioner successfully created 'IN_PAYLOAD' file '#{@target_path}'")
134
+ file.close
99
135
  @exited = true
100
136
  end
101
137
 
@@ -0,0 +1,13 @@
1
+ module DTK
2
+ module Utils
3
+ class Permission
4
+
5
+ PERMISSION_CHECKER = /^[01]?[0-7]{3}$/
6
+
7
+ def self.check(permission_value)
8
+ !permission_value.to_s.match(PERMISSION_CHECKER).nil?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dtk-action-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
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-05-20 00:00:00.000000000 Z
11
+ date: 2015-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: posix-spawn
14
+ name: awesome_print
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.3.8
19
+ version: 1.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.3.8
26
+ version: 1.1.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: awesome_print
28
+ name: json
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 1.1.0
33
+ version: 1.8.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 1.1.0
40
+ version: 1.8.2
41
41
  - !ruby/object:Gem::Dependency
42
- name: json
42
+ name: git
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 1.8.2
47
+ version: 1.2.9.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 1.8.2
54
+ version: 1.2.9.1
55
55
  - !ruby/object:Gem::Dependency
56
- name: git
56
+ name: after_do
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 1.2.9.1
61
+ version: 0.3.1
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: 1.2.9.1
68
+ version: 0.3.1
69
69
  description: The DTK Action Agent is designed to run commands on remote machine.
70
70
  email:
71
71
  - rich@reactor8.com
@@ -83,6 +83,7 @@ files:
83
83
  - lib/dtk-action-agent/version.rb
84
84
  - lib/logger.rb
85
85
  - lib/position.rb
86
+ - lib/utils/permission.rb
86
87
  homepage: ''
87
88
  licenses:
88
89
  - GPL-3.0