# Chef源碼組織
我們這里講的Chef源碼,是指opscode發布在github的那個名為chef的項目 [chef in github: https://github.com/opscode/chef](https://github.com/opscode/chef)
chef本身就是一個gem,它的源碼組織結構,首先是一個gem的組織結構:
### 組織結構

基本上Chef的組織結構是一個標準的gem包。
一個gem包的主要元素:
-
**lib/**
lib下面,是這個gem的核心代碼
-
**.gemspec文件**
這里是這個gem的規格文件,里面包含了gem的作者信息、lib的目錄位置、bin目錄位置、運行時依賴包以及開發時依賴包等
-
**bin/**
如果你的gem有終端命令,那么就放到這個目錄下面吧
-
**tasks/**
放置Rack任務的目錄。
-
**spec/**
Rspec測試目錄
-
**Gemfile**
bunlder的包管理文件。
回到我們的Chef源碼目錄里面:
### chef.gemspec
這是chef這個gem的規格文件,
~~~
Gem::Specification.new do |s|
s.name = 'chef'
s.version = Chef::VERSION
s.platform = Gem::Platform::RUBY
#...
s.required_ruby_version = ">= 1.9.3"
s.add_dependency "mixlib-config", "~> 2.0"
s.add_dependency "mixlib-cli", "~> 1.4"
s.add_dependency "mixlib-log", "~> 1.3"
s.add_dependency "mixlib-authentication", "~> 1.3"
s.add_dependency "mixlib-shellout", ">= 2.0.0.rc.0", "< 3.0"
s.add_dependency "ohai", ">= 7.6.0.rc.0"
#...
s.bindir = "bin"
#...
s.require_path = 'lib'
#...
end
~~~
一個gemspec文件,必須是按照這種格式來寫。實際上是初始化了一個Gem::Specification類的對象。你也可以理解為,chef這個gem,就是一個gem對象。這個文件指定了chef這個gem的各種屬性。
### bin
我們再來看下bin目錄。 bin里面放置的是chef這個gem所包含的命令:

我們看到chef常用的幾個命令了:chef-client、knife、chef-shell、chef-solo等。我們打開chef-client文件看看:
~~~
require 'rubygems'
$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
require 'chef'
require 'chef/application/client'
Chef::Application::Client.new.run
~~~
再打開knife文件看看:
~~~
require 'rubygems'
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require 'chef/application/knife'
Chef::Application::Knife.new.run
~~~
~~~
require 'rubygems'
$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
require 'chef/application/apply'
Chef::Application::Apply.new.run
~~~
我們依次打開這個幾個bin文件看了下,除了chef-shell和shef文件之外,結構基本都差不多, 都和上面的代碼類似,都是加載了rubygems, 在加載路徑里面添加了lib目錄, 然后又把lib/chef/applications/下面的相關文件加載,最后用了幾乎統一的方法:
~~~
Chef::Application::Xxx.new.run
~~~
我們學過Ruby的基礎知識,可以看得出來,Chef::Application::Xxx肯定是被定義在 chef/application/目錄下面的Xxx類。
而chef-shell和shef文件,我們打開后,驚異的發現,這倆文件中代碼幾乎一模一樣:
chef-shell:
~~~
begin
require "rubygems"
rescue LoadError
end
require "irb"
require "irb/completion"
require 'irb/ext/save-history'
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require "chef/shell"
# On Windows only, enable irb --noreadline because of input problems --
# See CHEF-3284.
IRB.conf[:USE_READLINE] = false if Chef::Platform::windows?
Shell.start
~~~
shef:
~~~
begin
require "rubygems"
rescue LoadError
end
require "irb"
require "irb/completion"
require 'irb/ext/save-history'
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require "chef/shell"
Chef::Log.warn("DEPRECATED: The 'shef' program is renamed to 'chef-shell'")
Shell.start
~~~
當我們看到shef中倒數第二行代碼的時候:
~~~
Chef::Log.warn("DEPRECATED: The 'shef' program is renamed to 'chef-shell'")
~~~
可以看得出來,Chef::Log.warn ,應該是Chef的日志輸出,warn代表一個警告,里面的字符串意思是:過期聲明: 這個shef程序已經被重命名為chef-shell了。
好了,我們已經破案了。 只需要看chef-shell就好了。
chef-shell代碼中,require了irb及其組件,并且也加載了chef/shell文件,最后啟動了Shell.start命令。
chef-shell命令,實際上是開啟了一個加載了chef環境的irb交互shell界面。我們在安裝了chef的終端輸入這個命令就知道了:
~~~
$ chef-shell
loading configuration: none (standalone session)
Session type: standalone
Loading.....done.
This is the chef-shell.
Chef Version: 11.16.0
http://www.opscode.com/chef
http://docs.opscode.com/
run `help' for help, `exit' or ^D to quit.
Ohai2u vagrant@chef-node!
chef >
~~~
你可以輸入help查看幫助。
### lib
我們通過查看bin下面的文件,了解到,一個chef命令的執行,必須先加載lib/chef下面的相關文件,那么我們就去lib目錄下面看看。
lib目錄下面的文件組織也是有規范的:

這樣的組織結構,意味著,當我們使用這個gem的時候,直接:
~~~
require 'chef'
~~~
就可以了。 require 'chef', 這個命令會直接加載chef gem lib下面的chef.rb,我們看看chef.rb中的代碼:
~~~
require 'chef/version'
require 'chef/nil_argument'
require 'chef/mash'
require 'chef/exceptions'
require 'chef/log'
require 'chef/config'
require 'chef/providers'
require 'chef/resources'
require 'chef/shell_out'
require 'chef/daemon'
require 'chef/run_status'
require 'chef/handler'
require 'chef/handler/json_file'
require 'chef/monkey_patches/tempfile'
require 'chef/monkey_patches/string'
require 'chef/monkey_patches/numeric'
require 'chef/monkey_patches/object'
require 'chef/monkey_patches/file'
require 'chef/monkey_patches/uri'
~~~
chef.rb中就是一堆require, 加載了lib/chef目錄下的全部文件。
所以,chef.rb,算是一個入口。核心的代碼還在chef目錄下面。
但是我們從chef.rb文件中加載的文件名稱中,也可以看出chef整體架構的一個大概。
chef分為四部分,由空行來分隔:
~~~
#第一部分:
require 'chef/version'
require 'chef/nil_argument'
require 'chef/mash'
require 'chef/exceptions'
require 'chef/log'
require 'chef/config'
require 'chef/providers'
require 'chef/resources'
require 'chef/shell_out'
~~~
這部分似乎定義了Chef的版本、配置、日志、resources、providers、shell等應用層面的東西。
~~~
# 第二部分
require 'chef/daemon'
~~~
這一部分,似乎是定義了chef daemon的功能代碼。我們知道,chef-client可以以daemon模式運行。
~~~
# 第三部分
require 'chef/run_status'
require 'chef/handler'
require 'chef/handler/json_file'
~~~
這一部分,似乎是定義了chef的運行狀態、handler的信息, 用于處理chef的內部狀態信息。
~~~
require 'chef/monkey_patches/tempfile'
require 'chef/monkey_patches/string'
require 'chef/monkey_patches/numeric'
require 'chef/monkey_patches/object'
require 'chef/monkey_patches/file'
require 'chef/monkey_patches/uri'
~~~
這一部分,我們看到了monkey_patches 以及熟悉的Ruby內部類同名的文件,幾乎可以肯定,這是chef自己對于Ruby提供的類進行了monkey patch,添加了自己要用的方法。 我們在前面講類與模塊那一節,也用這個代碼舉過例子。
當然,以上都是目測, 讓我們繼續查看lib/chef下面的文件吧。
### lib/chef
我們打開lib/chef目錄,發現里面很多文件和文件夾, 跟我們上面根據chef.rb的猜想有點差別。那么我們把chef.rb中require的文件一個個看一下吧,看完后發現,并沒有囊括完chef目錄中的所有文件。
那這些文件都是干嘛的呢?
先來劇透一下吧。
如果你看過chef-server-webui,應該可以知道,在chef-server-webui里面也用到了chef,主要是用到了chef/rest這部分文件。
還有上面說過的bin目錄下的的命令,也需要用到chef/application.rb以及 chef/application/, chef/api_client/等下面的各種文件。
lib/chef下面還有一部分文件是用于測試的,那些我們就暫時不關心了。
我們繼續看看chef最重要的東西。
### Knife
knife是chef中不可或缺的工具。 我們通過knife命令和chef server、node交互。那么讓我們來看下knife的工作機制:
### 相關文件
knife 相關的最主要的文件是:
- lib/chef/knife.rb
- lib/chef/knife/*.rb
我們打開lib/chef/knife/目錄,可以看到里面定義了很多rb文件, 這里幾乎定義了全部的knife的命令實現,以及輔助方法。
如果想深入了解的話,具體看代碼吧。
### Chef Client
chef client的所有行為都在lib/chef/client.rb中。
### Chef中的DSL方法
構成recipe的那些chef resource,都在chef/resource.rb和chef/resource/目錄下面被定義。
尤其是在chef/resource/目錄下面,定義了我們常用的resource:
- file
- template
- execute
- directory
- template
- user
等等。具體可以再深入研究源碼。
- 序
- Chapter 1: 初識Chef
- 一些背景
- Chef vs Puppet
- Chapter 2: Chef應用
- Chef架構
- Chef能做什么
- Chef組件
- Chef環境安裝
- chef-server
- opscode-chef
- chef-solo
- Chef實戰
- 實戰前的必修理論
- 使用Chef
- Chapter 3: Ruby基礎
- 對象與方法
- 標識符
- 類與模塊
- 數據類型
- 真與假
- 控制語句
- 代碼塊
- Chapter 4: Chef源碼架構
- Rubygems與gem
- bundler
- Chef源碼組織
- Chapter 5: Rails基礎
- Rails是什么
- MVC架構
- Restful
- Rails組成與項目結構
- Chapter 6: Chef Server WebUI
- Chef Server Webui組織結構
- Chef Rest API
- 參考