Jacky Wu bio photo

Jacky Wu

Software Architect in China

Email Twitter Facebook LinkedIn Github Weibo

Puppet Agent的启动

author: 燃烧 jacky.wucheng@gmail.com 本代码分析基于 puppet-2.7.19, 编写于2014-03-14 如下的讲解中可能尽量讲述较关键的地方,其他地方可能会省略。

sbin/puppetd脚本是agent启动入口,其中调用Puppet::Application[:agent].run 启动agent daemon。 其调用的是lib/puppet/application/agent.rb中的class Puppet::Application::Agent里的run方法。 class Puppet::Application::Agent的run方法是从Puppet::Application中继承的。

class Puppet::Application 中

```
# This is the main application entry point
def run
  exit_on_fail("initialize")                                   { hook('preinit')       { preinit } }
  exit_on_fail("parse options")                                { hook('parse_options') { parse_options } }
  exit_on_fail("parse configuration file")                     { Puppet.settings.parse } if should_parse_config?
  exit_on_fail("prepare for execution")                        { hook('setup')         { setup } }
  exit_on_fail("configure routes from #{Puppet[:route_file]}") { configure_indirector_routes }
  exit_on_fail("run")                                          { hook('run_command')   { run_command } }
end
```

显然代码了做了

  • 预初始化。
  • 解析配置文件。
  • 配置indirector,其用途可参考 indirection
  • 执行run_command,此run_command方法是在class Puppet::Application::Agent中定义的,其覆盖了父类Puppet::Application中的run_command方法。
  • run_command方法中调用main方法执行了@daemon.start来启动puppetd的客户端。

Puppet Agent Run Rest-API的实现

通过Puppet官方文档Rest_API,可知我们可以通过给Puppet Agent发送http请求来触发Agent进行更新,那么通过此run接口可以实现多少需求呢?是否可以实现noop更新noop参数的解释的呢?我们分析一下接口的实现。

lib/puppet/network/handler/runner.rb中定义了

```
class Puppet::Network::Handler
  class MissingMasterError < RuntimeError; end # Cannot find the master client
  # A simple server for triggering a new run on a Puppet client.
  class Runner < Handler
    desc "An interface for triggering client configuration runs."

    @interface = XMLRPC::Service::Interface.new("puppetrunner") { |iface|
      iface.add_method("string run(string, string)")
    }

    side :client

    # Run the client configuration right now, optionally specifying
    # tags and whether to ignore schedules
    def run(tags = nil, ignoreschedules = false, fg = true, client = nil, clientip = nil)
      options = {}
      options[:tags] = tags if tags
      options[:ignoreschedules] = ignoreschedules if ignoreschedules
      options[:background] = !fg

      runner = Puppet::Run.new(options)

      runner.run

      runner.status
    end
  end
end
```

从中可见,支持的参数只有tags,ignoreschedules,background。所以,此run接口目前的功能是非常若弱的,如果要实现我们的功能,我们需要考察下是否可以定制。 其中Puppet::Run定义在lib/puppet/run.rb中,

```
class Puppet::Run
...

  def initialize(options = {})
    if options.include?(:background)
      @background = options[:background]
      options.delete(:background)
    end

    valid_options = [:tags, :ignoreschedules]
    options.each do |key, value|
      raise ArgumentError, "Run does not accept #{key}" unless valid_options.include?(key)
    end

    @options = options
  end


  def run
    if agent.running?
      @status = "running"
      return self
    end

    log_run

    if background?
      Thread.new { agent.run(options) }
    else
      agent.run(options)
    end

    @status = "success"

    self
  end
...
end
```

其中进行了参数检查,只支持[:tags, :ignoreschedules]这几个参数。这就比较悲剧了,如果要实现更多的参数,没有合适的插件模式,而是需要改原生代码。从puppet-3.1中,依然没有看到改进。

```
from puppet-3.1

  def initialize(options = {})
    if options.include?(:background)
    ¦ @background = options[:background]
    ¦ options.delete(:background)
    end

    valid_options = [:tags, :ignoreschedules, :pluginsync]
    options.each do |key, value|
    ¦ raise ArgumentError, "Run does not accept #{key}" unless valid_options.include?(key)
    end

    @options = options
  end
```