dtk-action-agent 0.0.6 → 0.0.7

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
  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