Class: Pione::RuleEngine::ActionHandler

Inherits:
BasicHandler show all
Defined in:
lib/pione/rule-engine/action-handler.rb

Overview

ActionHandler handles ActionRule.

Constant Summary

Constants included from Log::MessageLog

Log::MessageLog::MESSAGE_QUEUE

Instance Attribute Summary collapse

Attributes inherited from BasicHandler

#base_location, #caller_id, #digest, #domain_id, #domain_location, #dry_run, #env, #inputs, #outputs, #package_id, #param_set, #plain_env, #rule_condition, #rule_definition, #rule_name

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BasicHandler

#apply_touch_operation, #create_data_by_touch_operation, #find_outputs_from_space, #handle, #make_location, #make_output_location, #make_output_tuple, #make_rule_process_record, #make_task_process_record, #publish_outputs, #setup_env, #show_outputs, #update_time_by_touch_operation, #write_data_null

Methods included from Log::MessageLog

#debug_message, #debug_message_begin, #debug_message_end, debug_mode, debug_mode=, debug_mode?, message, quiet_mode, quiet_mode=, quiet_mode?, #show, #user_message, #user_message_begin, #user_message_end

Methods included from TupleSpace::TupleSpaceInterface

#process_log, #processing_error, #read!, #set_tuple_space, #take!, #take_all!, tuple_space_operation, #tuple_space_server, #with_process_log

Constructor Details

#initialize(*args) ⇒ ActionHandler

Returns a new instance of ActionHandler.



11
12
13
14
15
16
17
18
# File 'lib/pione/rule-engine/action-handler.rb', line 11

def initialize(*args)
  super(*args)
  @working_directory = Location[Global.working_directory_generator.mkdir]
  @env.variable_set(
    Lang::Variable.new("__WORKING_DIRECTORY__"),
    Lang::StringSequence.of(@working_directory.path.to_s)
  )
end

Instance Attribute Details

#working_directoryObject (readonly)

Returns the value of attribute working_directory.



9
10
11
# File 'lib/pione/rule-engine/action-handler.rb', line 9

def working_directory
  @working_directory
end

Class Method Details

.message_nameObject



5
6
7
# File 'lib/pione/rule-engine/action-handler.rb', line 5

def self.message_name
  "Action"
end

Instance Method Details

#call_shell_script(path) ⇒ Object

Call shell script of the path.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/pione/rule-engine/action-handler.rb', line 105

def call_shell_script(path)
  scriptname = File.basename(path)

  # stdout & stderr
  rule_condition = @rule_definition.rule_condition_context.eval(@env)
  output_conditions = rule_condition.outputs.map {|output| output.eval(@env)}
  stdout = output_conditions.find{|output| output.output_mode == :stdout}
  out = stdout ? stdout.pieces.first.pattern : ".stdout"
  err = ".stderr"

  # execute command
  `cd #{@working_directory.path}; PATH=#{(@working_directory + "bin").path}:$PATH ; ./#{scriptname} > #{out} 2> #{err}`

  # the case the script has errored
  unless $?.success?
    raise ActionError.new(digest, (@working_directory + err).read)
  end

  # delete .stdout file if it is empty
  if stdout.nil? and (@working_directory + out).size == 0
    (@working_directory + out).delete
  end

  # delete .stderr file if it is emtpy
  if (@working_directory + err).size == 0
    (@working_directory + err).delete
  end
end

#collect_outputsvoid

This method returns an undefined value.

Collect output data by names from working directory.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/pione/rule-engine/action-handler.rb', line 144

def collect_outputs
  outputs = []
  filenames = @working_directory.file_entries.map{|entry| entry.path.basename.to_s}
  @rule_condition.outputs.each_with_index do |condition, i|
    _condition = condition.eval(@env)
    case _condition.distribution
    when :all
      outputs[i] = filenames.select{|name| _condition.match?(name)}.map do |name|
        make_output_tuple_with_time(name)
      end
    when :each
      if name = filenames.find {|name| _condition.match?(name)}
        outputs[i] = [make_output_tuple_with_time(name)]
      end
    end

    # apply touch operation
    if tuple = apply_touch_operation(_condition, outputs[i])
      outputs[i] = tuple
    end

    # write data null if needed
    write_data_null(_condition, outputs[i], i)
  end

  return outputs
end

#executeObject

Execute the action.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/pione/rule-engine/action-handler.rb', line 21

def execute
  # prepare input files
  setup_working_directory
  # prepare shell script
  write_shell_script {|path| call_shell_script(path) }
  # collect outputs
  outputs = collect_outputs
  # write output data
  write_output_data(outputs)
  # write tuples
  write_output_tuples(outputs)
  # write environment info
  write_env_info
  # write other resources
  write_other_resources

  # clear working directory
  @working_directory.delete

  # return tuples
  return outputs
end

#make_output_tuple_with_time(name) ⇒ Object

Make output tuple by name.



135
136
137
138
139
# File 'lib/pione/rule-engine/action-handler.rb', line 135

def make_output_tuple_with_time(name)
  time = (@working_directory + name).mtime
  location = make_output_location(name)
  TupleSpace::DataTuple.new(name: name, domain: @domain_id, location: location, time: time)
end

#setup_variable_tableObject

Setup the variable table with working directory in addition.



45
46
47
48
49
50
# File 'lib/pione/rule-engine/action-handler.rb', line 45

def setup_variable_table
  super

  @variable_table.set(Variable.new("__BASE__"), PioneString.new(base_location.uri).to_seq)
  @variable_table.set(Variable.new("_"), PackageExprSequence.new([PackageExpr.new(@rule.package_name)]))
end

#setup_working_directoryObject

Synchronize input data into working directory.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/pione/rule-engine/action-handler.rb', line 53

def setup_working_directory
  @inputs.flatten.each do |input|
    # get file path in working directory
    wd_location = @working_directory + input.name
    # create a link to cache
    cache_location = System::FileCache.get(input.location)
    wd_location.path.make_symlink(cache_location.path)
    unless wd_location.exist?
      raise RuleExecutionError.new(self)
    end
  end

  # FIXME: should not copy bin files in the package each time.
  bin = @base_location + "package" + "bin"
  if bin.exist?
    bin.entries.each do |entry|
      dest = @working_directory + "bin" + entry.basename
      unless dest.exist?
        entry.copy(dest)
        dest.path.chmod(0700)
      end
    end
  end
end

#write_env_infoObject

Write action environment information file.



184
185
186
187
188
189
# File 'lib/pione/rule-engine/action-handler.rb', line 184

def write_env_info
  @env.variable_table.keys.map do |var|
    val = @env.variable_get(var)
    "%s: %s" % [var.name, val.textize]
  end.tap {|x| (@working_directory + ".pione-env").create(x.join("\n"))}
end

#write_other_resourcesObject

Move other intermediate files to the domain location.



192
193
194
195
196
197
198
199
200
201
# File 'lib/pione/rule-engine/action-handler.rb', line 192

def write_other_resources
  @working_directory.file_entries.each do |entry|
    location = make_location(entry.path.basename, @domain_id)
    begin
      entry.move(location)
    rescue => e
      Log::SystemLog.warn("cannot move %s to %s: %s" % [entry.path, location, e.message])
    end
  end
end

#write_output_data(outputs) ⇒ void

This method returns an undefined value.

Write output data with caching.



175
176
177
178
179
180
181
# File 'lib/pione/rule-engine/action-handler.rb', line 175

def write_output_data(outputs)
  outputs.flatten.compact.each do |output|
    src = @working_directory + output.name
    dest = output.location
    System::FileCache.put(src, dest)
  end
end

#write_output_tuples(outputs) ⇒ Object

Writes output tuples into the tuple space server.



204
205
206
# File 'lib/pione/rule-engine/action-handler.rb', line 204

def write_output_tuples(outputs)
  outputs.flatten.compact.each {|output| write(output)}
end

#write_shell_script(&b) ⇒ Object

Write the action into a shell script.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/pione/rule-engine/action-handler.rb', line 79

def write_shell_script(&b)
  file = @working_directory + "__pione-action__.sh"

  # write the action
  if @dry_run
    @rule.outputs.flatten.each do |output|
      file.create("touch %s" % output.eval(@env).name)
    end
  else
    # apply offside rule
    content = @rule_definition.action_context.eval(@env).content
    file.create(Util::EmbededExprExpander.expand(@env, content))
    # chmod 700
    if @working_directory.scheme == "local"
      FileUtils.chmod(0700, file.path)
    end
  end

  # message
  lines = @rule_definition.action_context.eval(@env).content.split("\n")
  user_message(["-"*60, lines, "-"*60].flatten, 0, "SH")

  return b.call(file.path)
end