💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# Chef源码组织 我们这里讲的Chef源码,是指opscode发布在github的那个名为chef的项目 [chef in github: https://github.com/opscode/chef](https://github.com/opscode/chef) chef本身就是一个gem,它的源码组织结构,首先是一个gem的组织结构: ### 组织结构 ![chef-github](https://box.kancloud.cn/2015-08-23_55d9d7ae7a4f0.png) 基本上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-bin](https://box.kancloud.cn/2015-08-23_55d9d7ae99732.png) 我们看到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目录下面的文件组织也是有规范的: ![chef-lib](https://box.kancloud.cn/2015-08-23_55d9d7aeb0306.png) 这样的组织结构,意味着,当我们使用这个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 等等。具体可以再深入研究源码。