Chef Server WebUI

WebUI的组织结构和Rails基础讲的类似。 唯一的区别就是:

Chef Server WebUI 没有直接操作数据库,所以config下面没有相关数据库配置。

因为都是通过erchef提供的 restful api来和数据库通信。

node示例

拿我们在第二章应用中创建的node2来示例。 打开chef server webui: http://local.chef.com (这是我本地/etc/hosts里设置的测试域名)

webui-demo-node

在登陆之后,我们点击nodes。默认的日志在「/var/log/chef-server/chef-server-webui/current」目录下面:

$ sudo tail -f /var/log/chef-server/chef-server-webui/current

你可以观察日志,当导航栏nodes被点击的时候:

Started GET "/nodes" for 127.0.0.1
...
...

日志中我们看到,是对/nodes发起了GET请求, 这个/nodes是啥呢? 我们之前Rails基础里讲过了, 这个是resources,我们去controller里面去找,根据Rails基础中提过的约定大于配置,就应该是在app/controllers/nodes_controller.rb中定义的。


require 'chef/node'

class NodesController < ApplicationController

  respond_to :html

  before_filter :require_login
  before_filter :require_admin, :only => [:destroy]

  def index
    node_hash = if session[:environment]
                  client_with_actor.get("environments/#{session[:environment]}/nodes")
                else
                  client_with_actor.get("nodes")
                end
    @node_list = node_hash.keys.sort
  rescue => e
    log_and_flash_exception(e, "Could not list nodes")
    @node_list = {}
  end

  # ...

end

我们只摘出了nodes controller的部分代码, index action,就是GET /nodes所响应的那个action方法。可以通过config/routes.rb文件得到印证:


ChefServerWebui::Application.routes.draw do
  resources :nodes, :id => /[^\/]+/
  # ...
end

可以回头参考我们讲的Rails基础,就知道当我们GET /nodes的时候,就是响应index action。这就是restful。

回到我们的index action中:

这个action,要获取注册到这个chef server的所有nodes信息。这里最关键的方法是这个:

client_with_actor.get

而client_with_actor这是一个方法,被定义在lib / chef_server_webui / api_client_helper.rb文件中:

require 'chef_server/rest_client'

module ChefServerWebui
  module ApiClientHelper
    #
    # We customize the behavior of Chef::REST in two important ways:
    #
    # 1. Set the 'x-ops-request-source' request header so all requests are
    #    authenticated as the webui user.
    # 2. Set the client_name of *some* requests to that of the logged-in user
    #    so these requests are effectively authorized as said user.
    #

    DEFAULT_REQUEST_HEADERS = {
      :headers => ChefServerWebui::Application.config.rest_client_custom_http_headers.merge({'x-ops-request-source' => 'web'})
    }.freeze

    # Returns an instance of ChefServer::RestClient with the 'actor' set to the
    # webui client.
    def client
      client_with_actor(ChefServerWebui::Application.config.chef_server_url,
                        ChefServerWebui::Application.config.rest_client_name,
                        ChefServerWebui::Application.config.rest_client_key)
    end

    # Returns an instance of ChefServer::RestClient with the 'actor' set to the
    # current logged in user. The current user is set in
    # Thread.current[:current_user_id] by the Rails appliction using an
    # around_filter
    def client_with_actor(url=Rails.configuration.chef_server_url,
                          actor=Thread.current[:current_user_id],
                          signing_key_filename=ChefServerWebui::Application.config.rest_client_key)
      ChefServer::RestClient.new(url, actor, signing_key_filename, DEFAULT_REQUEST_HEADERS)
    end
  end
end

这个文件代码很短,我就复制了全部在这里,我们可以看出来, 这个helper方法,是用于跟erchef核心api来交互的。client_with_actor方法最终返回的是一个ChefServer::RestClient对象。

我们再去看看这个ChefServer::RestClient类,在lib / chef_server / rest_client.rb里面被定义:


require 'chef/log'
require 'chef/rest'
require 'chef/run_list'
require 'forwardable'

module ChefServer
  class RestClient
    extend Forwardable

    attr_reader :rest_client

    def_delegator :@rest_client, :get_rest
    def_delegator :@rest_client, :post_rest
    def_delegator :@rest_client, :put_rest
    def_delegator :@rest_client, :delete_rest
    def_delegator :@rest_client, :fetch

    def initialize(chef_server_url,
                   client_name,
                   signing_key_filename,
                   options={})
      @rest_client = Chef::REST.new(chef_server_url,
                               client_name,
                               signing_key_filename,
                               options)
    end

    [:get, :post, :put, :delete].each do |method|
      define_method method do |*args|
        begin
          @rest_client.send("#{method}_rest".to_sym, *args)
        rescue => e
          Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
          raise e
        end
      end
    end
  end
end

可以看得出来:

client_with_actor.get方法,实际就是ChefServer::RestClient.get, 而ChefServer::RestClient.get,实际是调用了Chef::REST.get,也就是我们前面所讲的chef gem里的chef/rest.rb里定义的方法。

小结

client_with_actor和erchef api交互,来获取nodes的相关信息。

这里只是用nodes作为示例,实际上webui中其他导航栏的信息,道理都是一样的,大家可以自己思考。

而重点在于erchef的核心api。