From 431e04e5425c9917fe4fa4623c74a6d0b73c8403 Mon Sep 17 00:00:00 2001 From: Keita Yamaguchi Date: Sun, 1 Dec 2013 11:56:29 +0900 Subject: [PATCH 1/5] Fix stability of front server and child command connections. --- lib/pione/command/basic-command.rb | 21 +++++-- lib/pione/command/spawner.rb | 5 +- lib/pione/front/basic-front.rb | 90 +++++++++++++++++++++++------- lib/pione/front/front-exception.rb | 8 ++- 4 files changed, 96 insertions(+), 28 deletions(-) diff --git a/lib/pione/command/basic-command.rb b/lib/pione/command/basic-command.rb index f6d98ee..9c3305d 100644 --- a/lib/pione/command/basic-command.rb +++ b/lib/pione/command/basic-command.rb @@ -172,6 +172,14 @@ def initialize(argv) Global.command = self end + # Return current phase name. + # + # @return [Symbol] + # :init, :setup, :execution, or :termination + def current_phase + @__phase_name__ + end + # Run 4 phase lifecycle of the command. This fires actions in each phase. def run @running_thread = Thread.current @@ -355,7 +363,7 @@ module CommonCommandAction define_action(:terminate_child_process) do |cmd| if Global.front # send signal TERM to the child process - Global.front.child.each do |pid, uri| + Global.front.child_pids.each do |pid| Util.ignore_exception {Process.kill(:TERM, pid)} end @@ -368,14 +376,19 @@ module CommonCommandAction end define_action(:setup_parent_process_connection) do |cmd| - cmd.option[:parent_front].add_child(Process.pid, Global.front.uri) - ParentFrontWatchDog.new(self) # start to watch parent process + begin + cmd.option[:parent_front].register_child(Process.pid, Global.front.uri) + ParentFrontWatchDog.new(self) # start to watch parent process + rescue Front::ChildRegistrationError + # terminate if the registration failed + Global.command.terminate + end end define_action(:terminate_parent_process_connection) do |cmd| # maybe parent process is dead in this timing Util.ignore_exception do - cmd.option[:parent_front].remove_child(Process.pid) + cmd.option[:parent_front].unregister_child(Process.pid) end end end diff --git a/lib/pione/command/spawner.rb b/lib/pione/command/spawner.rb index 3389895..d4c7816 100644 --- a/lib/pione/command/spawner.rb +++ b/lib/pione/command/spawner.rb @@ -18,9 +18,6 @@ def spawn # create a new process and watch it pid = Process.spawn(@name, *@args) - # register PID to front server for termination - Global.front.child[pid] = nil if Global.front - # keep to watch child process thread = Process.detach(pid) @@ -63,7 +60,7 @@ def when_terminated(&b) # URI to children table of my front, so we get it from my front and create # the reference. def find_child_front(pid) - if child_front_uri = Global.front.child[pid] + if child_front_uri = Global.front.child_front_uri(pid) return DRbObject.new_with_uri(child_front_uri).tap do |front| timeout(1) {front.ping} # test connection end diff --git a/lib/pione/front/basic-front.rb b/lib/pione/front/basic-front.rb index 0fd543f..91058f6 100644 --- a/lib/pione/front/basic-front.rb +++ b/lib/pione/front/basic-front.rb @@ -1,19 +1,19 @@ module Pione module Front # This is base class for all PIONE front classes. PIONE fronts exist in each - # command and behave as remote interface. + # command and behave as remote control interface. class BasicFront < PioneObject include DRbUndumped attr_reader :uri # front server's URI string - attr_reader :attrs - attr_reader :child # child process table # Creates a front server as druby's service. def initialize(port) @uri = start_service(port, {}) # port is number or range - @attrs = {} + @attr = {} @child = {} + @child_lock = Mutex.new + @child_watchers = ThreadGroup.new end # Return PID of the process. @@ -22,26 +22,78 @@ def pid end def [](name) - @attrs[name] + @attr[name] end def []=(name, val) - @attrs[name] = val + @attr[name] = val end - # Add child process. - def add_child(pid, front_uri) - @child[pid] = front_uri + # Register the process as a child of this process. It is unregistered when + # the child is terminated. + # + # @param pid [String] + # child process's PID + # @param front_uri [String] + # child process's front URI + # @return [void] + def register_child(pid, front_uri) + unless Global.command.current_phase == :termination + # register child's PID + @child_lock.synchronize {@child[pid] = front_uri} + + # unregister automatically when the child is terminated + thread = Thread.new do + Util.ignore_exception(Errno::ECHILD) do + Process.waitpid(pid) + unregister_child(pid) + end + end + thread[:pid] = pid + @child_watchers.add(thread) + + return nil + else + raise ChildRegistrationError.new + end + end + + # Unregister the child process. + # + # @param pid [String] + # child's PID to be removed + def unregister_child(pid) + # unregister the pid + @child_lock.synchronize {@child.delete(pid)} + + # kill the watcher thread + @child_watchers.list.each do |thread| + if thread[:pid] == pid + thread.kill + break + end + end + end + + # Return list of child process's PIDs. + # + # @return [Array] + # list of child command's PIDs. + def child_pids + @child.keys end - # Delete child process. - def remove_child(pid) - @child.delete(pid) + # Return a front URI of the child PID. + # + # @return [String] + # URI of the child PID + def child_front_uri(pid) + @child[pid] end # Terminate the front server. This method assumes to be not called from - # other process. Note that front servers have no responsibility of killing - # child processes. + # other processes. Note that front servers have no responsibility of + # killing child processes. def terminate DRb.stop_service end @@ -61,24 +113,24 @@ def start_service(port, config) if port.kind_of?(Range) enum = port.each begin - DRb.start_service(build_front_server_uri(enum.next), self, config) + DRb.start_service(build_uri(enum.next), self, config) rescue StopIteration => e raise rescue retry end else - DRb.start_service(build_front_server_uri(port), self, config) + DRb.start_service(build_uri(port), self, config) end return DRb.uri rescue => e - raise FrontError.new(self, e) + raise FrontServerError.new(self, e) end # Build front server URI. Note that the address is configured by - # +Global.my_ip_address+. - def build_front_server_uri(port) + # `Global.my_ip_address`. + def build_uri(port) "druby://%s:%s" % [Global.my_ip_address, port] end end diff --git a/lib/pione/front/front-exception.rb b/lib/pione/front/front-exception.rb index e99b1e8..56c18f4 100644 --- a/lib/pione/front/front-exception.rb +++ b/lib/pione/front/front-exception.rb @@ -1,7 +1,10 @@ module Pione module Front + # `Front::Error` is a base exception class. + class Error < StandardError; end + # FrontError is raised when front server cannnot start. - class FrontError < StandardError + class FrontServerError < Error def initialize(front, exception) @front = front @exception = exception @@ -12,5 +15,8 @@ def message end end + # `ChildRegistrationError` is raised when child process failed to register to + # parent front. + class ChildRegistrationError < Error; end end end From fb7534dd4e893c0dc5cc1c99ed10670b0b8f05ff Mon Sep 17 00:00:00 2001 From: Keita Yamaguchi Date: Mon, 2 Dec 2013 13:28:14 +0900 Subject: [PATCH 2/5] Add `pione config` subcommand. --- lib/pione/command.rb | 1 + lib/pione/command/basic-command.rb | 7 +- lib/pione/command/option.rb | 34 +++---- lib/pione/command/pione-client.rb | 41 +++----- lib/pione/command/pione-config.rb | 130 ++++++++++++++++++++++++ lib/pione/command/pione-package.rb | 29 ++++-- lib/pione/global.rb | 1 + lib/pione/global/config.rb | 70 +++++++++++-- lib/pione/global/item.rb | 25 ++++- lib/pione/global/path-variable.rb | 2 +- lib/pione/test-helper/command-helper.rb | 2 +- test/command/spec_pione-client.rb | 8 -- test/command/spec_pione-config.rb | 109 ++++++++++++++++++++ test/command/spec_pione-package.rb | 8 ++ test/global/spec_config.rb | 50 +++++++++ test/global/spec_item.rb | 2 +- 16 files changed, 442 insertions(+), 77 deletions(-) create mode 100644 lib/pione/command/pione-config.rb create mode 100644 test/command/spec_pione-config.rb create mode 100644 test/global/spec_config.rb diff --git a/lib/pione/command.rb b/lib/pione/command.rb index bced1c1..39887da 100644 --- a/lib/pione/command.rb +++ b/lib/pione/command.rb @@ -26,3 +26,4 @@ module Command; end require 'pione/command/pione-update-package-info' # pione update-package-info require 'pione/command/pione-action' # pione action require 'pione/command/pione-action-list' # pione action:list +require 'pione/command/pione-config' diff --git a/lib/pione/command/basic-command.rb b/lib/pione/command/basic-command.rb index f6d98ee..78b25f2 100644 --- a/lib/pione/command/basic-command.rb +++ b/lib/pione/command/basic-command.rb @@ -157,6 +157,7 @@ def register_action(phase_actions, action, option={}) attr_reader :option attr_reader :running_thread + attr_accessor :action_type forward! :class, :option_definition, :command_name, :command_name_block forward! :class, :command_banner, :command_front, :command_front_block @@ -167,6 +168,7 @@ def initialize(argv) @__exit_status__ = true @__phase_name__ = nil @__action_name__ = nil + @action_type = nil # process has just one command object Global.command = self @@ -195,7 +197,7 @@ def enter_phase(phase_name) @__phase_name__ = phase_name actions.each do |(targets, action_name, action_option)| # check current mode is target or not - if not(targets.empty?) and not(targets.include?(option[:action_mode])) + if not(targets.empty?) and not(targets.include?(@action_type)) next end @@ -260,6 +262,7 @@ def exit def abort(msg_or_exception, pos=caller(1).first) # hide the message because some option errors are meaningless invisible = msg_or_exception.is_a?(HideableOptionError) + p msg_or_exception # setup abortion message msg = msg_or_exception.is_a?(Exception) ? msg_or_exception.message : msg_or_exception @@ -301,7 +304,7 @@ def init_signal_trap # Initialize command options. def init_option - @option = option_definition.parse(@argv, command_name, command_banner) + @option = option_definition.parse(@argv, self) rescue OptionParser::ParseError, OptionError => e abort(e) end diff --git a/lib/pione/command/option.rb b/lib/pione/command/option.rb index 5ddc9c1..9ac0796 100644 --- a/lib/pione/command/option.rb +++ b/lib/pione/command/option.rb @@ -57,17 +57,17 @@ def item(name) end # Parse the command options. - def parse(argv, command_name, command_banner) + def parse(argv, cmd) data = Hash.new # parse options OptionParser.new do |opt| # set banner - opt.banner = "Usage: %s [options]" % command_name - opt.banner << "\n\n" + command_banner + "\n" if command_banner + opt.banner = "Usage: %s [options]" % cmd.command_name + opt.banner << "\n\n" + cmd.command_banner + "\n" if cmd.command_banner # set version - opt.program_name = command_name + opt.program_name = cmd.command_name opt.version = Pione::VERSION # default values @@ -75,7 +75,7 @@ def parse(argv, command_name, command_banner) data.merge!(@default) # setup option parser - @items.sort{|a,b| a.long <=> b.long}.each {|item| setup_item(command_name, opt, data, item)} + @items.sort{|a,b| a.long <=> b.long}.each {|item| setup_item(cmd, opt, data, item)} end.send(@parser_mode, argv) # check option's validness @@ -96,28 +96,28 @@ def validate(&b) private # Setup the option item. - def setup_item(command_name, opt, data, item) + def setup_item(cmd, opt, data, item) defs = [item.short, item.long, item.desc].compact [ :setup_item_action, :setup_item_values, :setup_item_value, :setup_item_static_value - ].find {|method_name| send(method_name, command_name, opt, data, item, defs)} + ].find {|method_name| send(method_name, cmd, opt, data, item, defs)} end - def setup_item_action(command_name, opt, data, item, defs) + def setup_item_action(cmd, opt, data, item, defs) if item.action - opt.on(*defs, Proc.new{|*args| self.instance_exec(command_name, data, *args, &item.action)}) + opt.on(*defs, Proc.new{|*args| self.instance_exec(cmd, data, *args, &item.action)}) end end - def setup_item_values(command_name, opt, data, item, defs) + def setup_item_values(cmd, opt, data, item, defs) if item.values.kind_of?(Proc) opt.on(*defs, Proc.new{|*args| data[item.name] << self.instance_exec(*args, &item.values)}) end end - def setup_item_value(command_name, opt, data, item, defs) + def setup_item_value(cmd, opt, data, item, defs) case item.value when Proc opt.on(*defs, Proc.new{|*args| data[item.name] = self.instance_exec(*args, &item.value)}) @@ -126,7 +126,7 @@ def setup_item_value(command_name, opt, data, item, defs) end end - def setup_item_static_value(command_name, opt, data, item, defs) + def setup_item_static_value(cmd, opt, data, item, defs) if item.value opt.on(*defs, Proc.new{ data[item.name] = item.value}) end @@ -169,7 +169,7 @@ module CommonOption define(:debug) do |item| item.long = '--debug[=TYPE]' item.desc = "turn on debug mode about the type(system / rule_engine / ignored_exception / presence_notifier / communication)" - item.action = proc {|command_nane, _, type| + item.action = proc {|cmd, _, type| Global.system_logger.level = :debug case type when "system", nil @@ -191,13 +191,13 @@ module CommonOption define(:features) do |item| item.long = '--features=FEATURES' item.desc = 'set features' - item.action = proc {|command_name, option, features| + item.action = proc {|cmd, option, features| begin # store features Global.features = features rescue Parslet::ParseFailed => e raise OptionError.new( - "invalid feature expression \"%s\" is given for %s" % [features, command_name] + "invalid feature expression \"%s\" is given for %s" % [features, cmd.command_name] ) end } @@ -213,13 +213,13 @@ module CommonOption item.long = '--parent-front=URI' item.desc = 'set parent front URI' item.requisite = true - item.action = proc do |command_name, option, uri| + item.action = proc do |cmd, option, uri| begin option[:parent_front] = DRbObject.new_with_uri(uri) timeout(1) {option[:parent_front].ping} rescue Exception => e raise HideableOptionError.new( - "%s couldn't connect to parent front \"%s\": %s" % [command_name, uri, e.message] + "%s couldn't connect to parent front \"%s\": %s" % [cmd.command_name, uri, e.message] ) end end diff --git a/lib/pione/command/pione-client.rb b/lib/pione/command/pione-client.rb index 7a0ea43..c374f02 100644 --- a/lib/pione/command/pione-client.rb +++ b/lib/pione/command/pione-client.rb @@ -23,8 +23,6 @@ class PioneClient < BasicCommand use_option :task_worker use_option :features - option_default(:action_mode, :process_job) - define_option(:input_location) do |item| item.short = '-i LOCATION' item.long = '--input=LOCATION' @@ -43,14 +41,14 @@ class PioneClient < BasicCommand item.long = '--output=LOCATION' item.desc = 'set output directory' item.default = Location["local:./output/"] - item.action = proc do |command_name, option, uri| + item.action = proc do |cmd, option, uri| begin option[:output_location] = Location[uri] if URI.parse(uri).scheme == "myftp" option[:myftp] = URI.parse(uri).normalize end rescue ArgumentError - raise OptionError.new("output location '%s' is bad in %s" % [uri, command_name]) + raise OptionError.new("output location '%s' is bad in %s" % [uri, cmd.command_name]) end end end @@ -73,11 +71,11 @@ class PioneClient < BasicCommand item.long = '--params="{Var:1,...}"' item.desc = "set user parameters" item.default = Lang::ParameterSetSequence.new - item.action = proc do |command_name, option, str| + item.action = proc do |cmd, option, str| begin option[:params] = option[:params].merge(Util.parse_param_set(str)) rescue Parslet::ParseFailed => e - raise OptionError.new("invalid parameters \"%s\" in %s" % [str, command_name]) + raise OptionError.new("invalid parameters \"%s\" in %s" % [str, cmd.command_name]) end end end @@ -108,12 +106,6 @@ class PioneClient < BasicCommand item.value = proc {|uri| uri} end - define_option(:list_params) do |item| - item.long = '--list-params' - item.desc = 'show user parameter list in the document' - item.action = proc {|_, option| option[:action_mode] = :list_params} - end - define_option(:rehearse) do |item| item.long = '--rehearse [SCENARIO]' item.desc = 'rehearse the scenario' @@ -295,23 +287,14 @@ def start_relay_connection # command lifecycle: execution phase # - # mode "list params" - execute :list_params => :list_params - - # mode "process_job" - execute :process_job => :job_terminator - execute :process_job => :messenger - execute :process_job => :logger - execute :process_job => :input_generator - execute :process_job => :tuple_space_provider - execute :process_job => :task_worker - execute :process_job => :process_manager - execute :process_job => :check_rehearsal_result - - # Print list of user parameters. - def execute_list_params - Util::PackageParametersList.print(@env, @env.current_package_id) - end + execute :job_terminator + execute :messenger + execute :logger + execute :input_generator + execute :tuple_space_provider + execute :task_worker + execute :process_manager + execute :check_rehearsal_result def execute_job_terminator @job_terminator = Agent::JobTerminator.start(@tuple_space) do |status| diff --git a/lib/pione/command/pione-config.rb b/lib/pione/command/pione-config.rb new file mode 100644 index 0000000..1b8f915 --- /dev/null +++ b/lib/pione/command/pione-config.rb @@ -0,0 +1,130 @@ +module Pione + module Command + # PioneConfig is a command for configurating PIONE global variables. + class PioneConfig < BasicCommand + # + # basic informations + # + + command_name "pione-config" + command_banner "config PIONE global variables" + PioneCommand.add_subcommand("config", self) + + # + # options + # + + use_option :debug + + define_option(:get) do |item| + item.long = "--get NAME" + item.desc = "get the item value" + item.action = Proc.new {|cmd, option, name| + cmd.action_type = :get + option[:name] = name + } + end + + define_option(:list) do |item| + item.long = "--list" + item.desc = "list all" + item.action = Proc.new {|cmd, option, _| + cmd.action_type = :list + } + end + + define_option(:set) do |item| + item.long = "--set NAME VALUE" + item.desc = "set the item" + item.action = Proc.new {|cmd, option, name| + cmd.action_type = :set + option[:name] = name + } + end + + define_option(:unset) do |item| + item.long = "--unset NAME VALUE" + item.desc = "set the item" + item.action = Proc.new {|cmd, option, name| + cmd.action_type = :unset + option[:name] = name + } + end + + define_option(:file) do |item| + item.short = "-f" + item.long = "--file PATH" + item.desc = "config file path" + item.default = Global.config_path + item.value = lambda {|filepath| Pathname.new(filepath)} + end + + # + # command lifecycle: execution phase + # + + execute :get => :get_item + execute :list => :list_all + execute :set => :set_item + execute :unset => :unset_item + + def execute_get_item + name = option[:name] + + if name + config = Global::Config.new(option[:file]) + puts config.get(name) + else + abort("`--get` option requires an item name") + end + rescue Global::UnconfigurableVariableError + abort("'%s' is not a configurable item name." % name) + end + + def execute_list_all + table = Hash.new + + Global.item.each do |key, item| + if item.configurable? + table[key] = item.init + end + end + + Global::Config.new(option[:file]).each do |name, value| + table[name] = value + end + + table.keys.sort.each do |name| + puts "%s: %s" % [name, table[name]] + end + end + + def execute_set_item + name = option[:name] + value = @argv.first + + if name + config = Global::Config.new(option[:file]) + config.set(name, value) + config.save(option[:file]) + else + abort("`--set` option requires an item name") + end + rescue Global::UnconfigurableVariableError + abort("'%s' is not a configurable item name." % name) + end + + def execute_unset_item + name = option[:name] + + if name + global = Global::Config.new(option[:file]) + global.unset(name) + global.save(option[:file]) + else + abort("`--unset` option requires an item name") + end + end + end + end +end diff --git a/lib/pione/command/pione-package.rb b/lib/pione/command/pione-package.rb index c376dea..363573f 100644 --- a/lib/pione/command/pione-package.rb +++ b/lib/pione/command/pione-package.rb @@ -21,27 +21,33 @@ class PionePackage < BasicCommand define_option(:add) do |item| item.long = '--add' item.desc = 'add the package to package database' - item.action = lambda do |_, option, location| - option[:action_mode] = :add + item.action = lambda do |cmd, option, location| + cmd.action_type = :add end end define_option(:build) do |item| item.long = '--build' item.desc = 'build PIONE archive file(*.ppg)' - item.action = lambda do |_, option, location| - option[:action_mode] = :build + item.action = lambda do |cmd, option, location| + cmd.action_type = :build end end define_option(:write_info) do |item| item.long = '--write-info' item.desc = 'write package and scenario info files' - item.action = lambda do |_, option, location| - option[:action_mode] = :write_info + item.action = lambda do |cmd, option, location| + cmd.action_type = :write_info end end + define_option(:list_params) do |item| + item.long = '--list-params' + item.desc = 'show user parameter list in the document' + item.action = proc {|cmd, option| cmd.action_type = :list_params} + end + define_option(:output) do |item| item.short = "-o" item.long = "--output=LOCATION" @@ -123,6 +129,17 @@ def execute_write_info abort(e.message) end + execute :list_params => :list_params + + # Print list of user parameters. + def execute_list_params + # read package + package_handler = Package::PackageReader.read(Location[@target]) + env = package_handler.eval(Lang::Environment.new) + + Util::PackageParametersList.print(env, env.current_package_id) + end + # # helper methods # diff --git a/lib/pione/global.rb b/lib/pione/global.rb index e229d9f..b5770c4 100644 --- a/lib/pione/global.rb +++ b/lib/pione/global.rb @@ -5,6 +5,7 @@ module Pione module Global; end end +require 'pione/global/global-exception' require 'pione/global/item' require 'pione/global/config' require 'pione/global/system-variable' diff --git a/lib/pione/global/config.rb b/lib/pione/global/config.rb index 701b878..1dc6880 100644 --- a/lib/pione/global/config.rb +++ b/lib/pione/global/config.rb @@ -7,7 +7,7 @@ def self.load(path) new(path).tap {|x| x.apply} end - forward :@table, "[]" + forward! :@table, "[]", "each" # config file path attr_reader :path @@ -15,21 +15,77 @@ def self.load(path) # Create a new configuration. def initialize(path) @path = Pathname.new(path) - @table = @path.exist? ? YAML.load(@path.read) : {} + @table = Hash.new + (@path.exist? ? JSON.load(@path.read) : {}).each do |key, val| + @table[key.to_sym] = val + end raise InvalidConfigFile.new(@path) unless @table.kind_of?(Hash) end # Apply config date to global settings. def apply @table.each do |key, val| - key = key.to_sym - if Global.item[key] and Global.item[key].configurable? - Global.set(key, val) - else - raise UnconfigurableVariableError.new(key) + name = key.to_sym + if_configurable_item(name) do + Global.set(name, val) end end end + + # Set the global item. + # + # @param name [String] + # item name + # @param value [Object] + # item value + # @return [void] + def set(name, value) + raise ArgumentError.new if value.nil? + + name = name.to_sym + if_configurable_item(name) do + @table[name] = ValueConverter.convert(Global.item[name].type, value) + end + end + + # Get the global item's value. + # + # @param name [String] + # item name + # @return [Object] + # item value + def get(name) + name = name.to_sym + if_configurable_item(name) do + @table[name] + end + end + + def unset(name) + name = name.to_sym + @table.delete(name) + end + + # Save the configuration items to the file. + # + # @param path [Pathname] + # file path + # @return [void] + def save(path=@path) + path.open("w+") do |file| + file.write(JSON.pretty_generate(@table)) + end + end + + private + + def if_configurable_item(name, &b) + if Global.item[name] and Global.item[name].configurable? + b.call + else + raise UnconfigurableVariableError.new(name) + end + end end end end diff --git a/lib/pione/global/item.rb b/lib/pione/global/item.rb index a930136..d8aa758 100644 --- a/lib/pione/global/item.rb +++ b/lib/pione/global/item.rb @@ -15,7 +15,7 @@ class << self # Define an internal item. The item cannot be configured by user. def define_internal_item(name, initial_value=nil, &definition) Item.new(name, false).tap do |item| - definition.call(item) + definition.call(item) if block_given? item.register end end @@ -23,7 +23,7 @@ def define_internal_item(name, initial_value=nil, &definition) # Define an external item. The item can be configured by user. def define_external_item(name, initial_value=nil, &definition) Item.new(name, true).tap do |item| - definition.call(item) + definition.call(item) if block_given? item.register end end @@ -31,7 +31,7 @@ def define_external_item(name, initial_value=nil, &definition) # Define a computed item. The item cannote be configured by user. def define_computed_item(name, dependencies, &definition) Item.new(name, false, :dependencies => dependencies).tap do |item| - definition.call(item) + definition.call(item) if block_given? item.register end end @@ -101,7 +101,7 @@ def initialize(name, configurable, option={}) @dependencies = option[:dependencies] || [] @desc = nil @init = nil - @type = :string + @type = nil @updater = Proc.new {|val| val} @orig = nil @record = false @@ -161,7 +161,22 @@ def define_updater(&b) # Update the item with the value. def update(val) @orig = val - @value = @updater.call(val) + @value = @updater.call(ValueConverter.convert(@type, val)) + end + end + + module ValueConverter + def self.convert(type, val) + case type + when :string + val = val.to_s unless val.kind_of?(String) + when :integer + val = val.to_i unless val.kind_of?(Integer) + when :boolean + val = val == "true" ? true : false unless val == true or val == false + end + + return val end end end diff --git a/lib/pione/global/path-variable.rb b/lib/pione/global/path-variable.rb index 026ae4e..2bfb512 100644 --- a/lib/pione/global/path-variable.rb +++ b/lib/pione/global/path-variable.rb @@ -12,7 +12,7 @@ module Global # This is a configuration file path. define_computed_item(:config_path, [:dot_pione_dir]) do |item| item.desc = "configuration file of PIONE" - item.define_updater {Global.dot_pione_dir + "config.yml"} + item.define_updater {Global.dot_pione_dir + "config.json"} end # This is a current working directory. The directory is defined by the diff --git a/lib/pione/test-helper/command-helper.rb b/lib/pione/test-helper/command-helper.rb index e04c43e..9261ed4 100644 --- a/lib/pione/test-helper/command-helper.rb +++ b/lib/pione/test-helper/command-helper.rb @@ -80,7 +80,7 @@ def execute(*options, &b) $stdout = STDOUT $stderr = STDERR - if options.include?(:report) + if options.include?(:report) or ENV["PIONE_TEST_REPORT"] == "true" res.report end diff --git a/test/command/spec_pione-client.rb b/test/command/spec_pione-client.rb index 425989e..a5c8f77 100644 --- a/test/command/spec_pione-client.rb +++ b/test/command/spec_pione-client.rb @@ -56,14 +56,6 @@ end end - it "should show parameters list of package" do - args = ["example/HelloWorld/HelloWorld.pione", "--list-params"] - res = TestHelper::Command.succeed do - Pione::Command::PioneClient.run(args) - end - res.stdout.string.size.should > 0 - end - describe "example/Fib" do TestHelper::PioneClientRunner.test(self) do |runner| runner.title = "should get a result with no parameters (default fib(3))" diff --git a/test/command/spec_pione-config.rb b/test/command/spec_pione-config.rb new file mode 100644 index 0000000..8738097 --- /dev/null +++ b/test/command/spec_pione-config.rb @@ -0,0 +1,109 @@ +require "pione/test-helper" + +describe Pione::Command::PioneConfig do + before do + Global.define_external_item(:test1) do |item| + item.type = :string + end + + Global.define_external_item(:test2) do |item| + item.type = :integer + end + + Global.define_external_item(:test3) do |item| + item.type = :boolean + end + + @path = Temppath.create + end + + after do + Global.item[:test1].unregister + Global.item[:test2].unregister + Global.item[:test3].unregister + end + + it "should get item value" do + config = Global::Config.new(@path) + config.set(:test1, "a") + config.set(:test2, 1) + config.set(:test3, true) + config.save + + res1 = TestHelper::Command.succeed do + Command::PioneConfig.run(["--get", "test1", "-f", @path.to_s]) + end + res1.stdout.string.chomp.should == "a" + + res2 = TestHelper::Command.succeed do + Command::PioneConfig.run(["--get", "test2", "-f", @path.to_s]) + end + res2.stdout.string.chomp.should == "1" + + res3 = TestHelper::Command.succeed do + Command::PioneConfig.run(["--get", "test3", "-f", @path.to_s]) + end + res3.stdout.string.chomp.should == "true" + end + + it "should list all" do + config = Global::Config.new(@path) + config.set(:test1, "a") + config.set(:test2, 1) + config.set(:test3, true) + config.save + + res = TestHelper::Command.succeed do + Command::PioneConfig.run(["--list", "-f", @path.to_s]) + end + + list = res.stdout.string.split("\n") + list.should.include("test1: a") + list.should.include("test2: 1") + list.should.include("test3: true") + end + + it "should set items" do + TestHelper::Command.succeed do + Command::PioneConfig.run(["--set", "test1", "a", "-f", @path.to_s]) + end + + TestHelper::Command.succeed do + Command::PioneConfig.run(["--set", "test2", "1", "-f", @path.to_s]) + end + + TestHelper::Command.succeed do + Command::PioneConfig.run(["--set", "test3", "true", "-f", @path.to_s]) + end + + config = Global::Config.new(@path) + config[:test1].should == "a" + config[:test2].should == 1 + config[:test3].should.true + end + + it "should unset items" do + config = Global::Config.new(@path) + config.set(:test1, "a") + config.set(:test2, 1) + config.set(:test3, true) + config.save + + TestHelper::Command.succeed do + Command::PioneConfig.run(["--unset", "test1", "-f", @path.to_s]) + end + + TestHelper::Command.succeed do + Command::PioneConfig.run(["--unset", "test2", "-f", @path.to_s]) + end + + TestHelper::Command.succeed do + Command::PioneConfig.run(["--unset", "test3", "-f", @path.to_s]) + end + + config = Global::Config.new(@path) + config[:test1].should.nil + config[:test2].should.nil + config[:test3].should.nil + end +end diff --git a/test/command/spec_pione-package.rb b/test/command/spec_pione-package.rb index 5c58de9..9f0c7ed 100644 --- a/test/command/spec_pione-package.rb +++ b/test/command/spec_pione-package.rb @@ -109,6 +109,14 @@ end (location + "pione-package.json").should.exist end + + it "should show parameters list of the package" do + args = ["example/HelloWorld/HelloWorld.pione", "--list-params"] + res = TestHelper::Command.succeed do + Pione::Command::PionePackage.run(args) + end + res.stdout.string.size.should > 0 + end end end diff --git a/test/global/spec_config.rb b/test/global/spec_config.rb new file mode 100644 index 0000000..2f8063a --- /dev/null +++ b/test/global/spec_config.rb @@ -0,0 +1,50 @@ +require "pione/test-helper" + +describe Pione::Global::Config do + before do + Global.define_external_item(:test) + + @path = Temppath.create + @config = Global::Config.new(@path) + end + + after do + Global.item[:test].unregister + end + + it "should set and get a item" do + @config.set("test", "a") + @config.get("test").should == "a" + end + + it "should unset a item" do + @config.set("test", "a") + @config.unset("test") + @config.get("test").should.nil + end + + it "should save config file" do + Global.define_external_item(:test1) + Global.define_external_item(:test2) + Global.define_external_item(:test3) + + # set + @config.set("test1", "a") + @config.set("test2", 1) + @config.set("test3", true) + + # save + @config.save + + # delete + Global.item[:test1].unregister + Global.item[:test2].unregister + Global.item[:test3].unregister + + # test + table = JSON.load(@path.read) + table["test1"].should == "a" + table["test2"].should == 1 + table["test3"].should.true + end +end diff --git a/test/global/spec_item.rb b/test/global/spec_item.rb index a65d550..911da01 100644 --- a/test/global/spec_item.rb +++ b/test/global/spec_item.rb @@ -1,6 +1,6 @@ require "pione/test-helper" -describe do +describe Pione::Global do it "should define an external item" do item = Global.define_external_item(:test_external_item) do |item| item.desc = "test external item" From b2b47e3e9d690bcdf9abf01b71ef320102a47923 Mon Sep 17 00:00:00 2001 From: Keita Yamaguchi Date: Mon, 2 Dec 2013 15:14:00 +0900 Subject: [PATCH 3/5] Make ignore spawn errors when the command is terminating. --- lib/pione/command/pione-client.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/pione/command/pione-client.rb b/lib/pione/command/pione-client.rb index 7a0ea43..1c00684 100644 --- a/lib/pione/command/pione-client.rb +++ b/lib/pione/command/pione-client.rb @@ -352,7 +352,11 @@ def execute_tuple_space_provider end @tuple_space_provider = spawner.child_front rescue SpawnError => e - abort(e.message) + if termination? + Log::Debug.system(e.message) + else + abort(e.message) + end end end @spawner_threads.add(thread) @@ -372,7 +376,11 @@ def execute_task_worker begin Command::PioneTaskWorker.spawn(Global.features, @tuple_space.uuid) rescue SpawnError => e - abort(e.message) + if termination? + Log::Debug.system(e.message) + else + abort(e.message) + end end end end From d7048beed0f12b3a242e67a7c3f2346ec7b8819d Mon Sep 17 00:00:00 2001 From: Keita Yamaguchi Date: Mon, 2 Dec 2013 16:17:46 +0900 Subject: [PATCH 4/5] Change option name from `--my-ip-address` to `--communication-address`. --- lib/pione/command/option.rb | 8 ++++---- lib/pione/command/pione-broker.rb | 2 +- lib/pione/command/pione-client.rb | 2 +- lib/pione/command/pione-relay.rb | 2 +- lib/pione/command/pione-task-worker.rb | 2 +- lib/pione/command/pione-tuple-space-provider.rb | 4 ++-- lib/pione/command/pione-tuple-space-receiver.rb | 4 ++-- lib/pione/front/basic-front.rb | 4 ++-- lib/pione/global/network-variable.rb | 4 ++-- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/pione/command/option.rb b/lib/pione/command/option.rb index 9ac0796..d28316e 100644 --- a/lib/pione/command/option.rb +++ b/lib/pione/command/option.rb @@ -203,10 +203,10 @@ module CommonOption } end - define(:my_ip_address) do |item| - item.long = "--my-ip-address=ADDRESS" - item.desc = "set my IP address" - item.action = proc {|_, _, address| Global.my_ip_address = address} + define(:communication_address) do |item| + item.long = "--communication-address=ADDRESS" + item.desc = "set IP address for interprocess communication" + item.action = proc {|_, _, address| Global.communication_address = address} end define(:parent_front) do |item| diff --git a/lib/pione/command/pione-broker.rb b/lib/pione/command/pione-broker.rb index e47cc24..4509d12 100644 --- a/lib/pione/command/pione-broker.rb +++ b/lib/pione/command/pione-broker.rb @@ -22,7 +22,7 @@ class PioneBroker < BasicCommand use_option :daemon use_option :debug use_option :features - use_option :my_ip_address + use_option :communication_address use_option :task_worker validate_option do |option| diff --git a/lib/pione/command/pione-client.rb b/lib/pione/command/pione-client.rb index 483150c..62f4b38 100644 --- a/lib/pione/command/pione-client.rb +++ b/lib/pione/command/pione-client.rb @@ -18,7 +18,7 @@ class PioneClient < BasicCommand use_option :debug use_option :color - use_option :my_ip_address + use_option :communication_address use_option :presence_notification_address use_option :task_worker use_option :features diff --git a/lib/pione/command/pione-relay.rb b/lib/pione/command/pione-relay.rb index c96832a..ce6a8b2 100644 --- a/lib/pione/command/pione-relay.rb +++ b/lib/pione/command/pione-relay.rb @@ -16,7 +16,7 @@ class PioneRelay < BasicCommand use_option :color use_option :debug - use_option :my_ip_address + use_option :communication_address define_option(:realm) do |item| item.long = "--realm name" diff --git a/lib/pione/command/pione-task-worker.rb b/lib/pione/command/pione-task-worker.rb index 2c72751..b6dd4b6 100644 --- a/lib/pione/command/pione-task-worker.rb +++ b/lib/pione/command/pione-task-worker.rb @@ -23,7 +23,7 @@ class PioneTaskWorker < BasicCommand use_option :color use_option :debug - use_option :my_ip_address + use_option :communication_address use_option :parent_front use_option :features diff --git a/lib/pione/command/pione-tuple-space-provider.rb b/lib/pione/command/pione-tuple-space-provider.rb index 974e1b1..2960d84 100644 --- a/lib/pione/command/pione-tuple-space-provider.rb +++ b/lib/pione/command/pione-tuple-space-provider.rb @@ -26,7 +26,7 @@ class PioneTupleSpaceProvider < BasicCommand use_option :color use_option :debug - use_option :my_ip_address + use_option :communication_address use_option :parent_front use_option :presence_notification_address @@ -47,7 +47,7 @@ def self.spawn # requisite options spawner.option("--parent-front", Global.front.uri) - spawner.option("--my-ip-address", Global.my_ip_address) + spawner.option("--communication-address", Global.communication_address) Global.presence_notification_addresses.each do |address| spawner.option("--presence-notification-address", address.to_s) end diff --git a/lib/pione/command/pione-tuple-space-receiver.rb b/lib/pione/command/pione-tuple-space-receiver.rb index 8311158..11e58cd 100644 --- a/lib/pione/command/pione-tuple-space-receiver.rb +++ b/lib/pione/command/pione-tuple-space-receiver.rb @@ -25,7 +25,7 @@ class PioneTupleSpaceReceiver < BasicCommand use_option :color use_option :debug - use_option :my_ip_address + use_option :communication_address use_option :parent_front define_option(:presence_port) do |item| @@ -53,7 +53,7 @@ def self.spawn # requisite options spawner.option("--parent-front", Global.front.uri) - spawner.option("--my-ip-address", Global.my_ip_address) + spawner.option("--communication-address", Global.communication_address) spawner.option("--presence-port", Global.presence_port.to_s) # optionals diff --git a/lib/pione/front/basic-front.rb b/lib/pione/front/basic-front.rb index 91058f6..57e9aea 100644 --- a/lib/pione/front/basic-front.rb +++ b/lib/pione/front/basic-front.rb @@ -129,9 +129,9 @@ def start_service(port, config) end # Build front server URI. Note that the address is configured by - # `Global.my_ip_address`. + # `Global.communication_address`. def build_uri(port) - "druby://%s:%s" % [Global.my_ip_address, port] + "druby://%s:%s" % [Global.communication_address, port] end end end diff --git a/lib/pione/global/network-variable.rb b/lib/pione/global/network-variable.rb index c43a292..2743bbf 100644 --- a/lib/pione/global/network-variable.rb +++ b/lib/pione/global/network-variable.rb @@ -2,8 +2,8 @@ module Pione module Global # This is IP address of this system. Note that you should select one IP # address if system has multiple addresses. - define_external_item(:my_ip_address) do |item| - item.desc = "IP address of this system" + define_external_item(:communication_address) do |item| + item.desc = "IP address for interprocess communication" item.init = Util::IPAddress.myself end end From bc4be697ebc155a1eaeb33da05ea7d83cf795fdf Mon Sep 17 00:00:00 2001 From: Keita Yamaguchi Date: Tue, 3 Dec 2013 16:51:24 +0900 Subject: [PATCH 5/5] Remove an unnecessary line. --- lib/pione/command/basic-command.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pione/command/basic-command.rb b/lib/pione/command/basic-command.rb index d8773e9..e6448b9 100644 --- a/lib/pione/command/basic-command.rb +++ b/lib/pione/command/basic-command.rb @@ -270,7 +270,6 @@ def exit def abort(msg_or_exception, pos=caller(1).first) # hide the message because some option errors are meaningless invisible = msg_or_exception.is_a?(HideableOptionError) - p msg_or_exception # setup abortion message msg = msg_or_exception.is_a?(Exception) ? msg_or_exception.message : msg_or_exception