PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/chef/lib/chef/application/solo.rb

http://github.com/opscode/chef
Ruby | 219 lines | 174 code | 28 blank | 17 comment | 11 complexity | 8240937113ec8a8e0064dde9d31923e7 MD5 | raw file
Possible License(s): Apache-2.0, CC0-1.0
  1. #
  2. # Author:: AJ Christensen (<aj@opscode.com>)
  3. # Author:: Mark Mzyk (mmzyk@opscode.com)
  4. # Copyright:: Copyright (c) 2008 Opscode, Inc.
  5. # License:: Apache License, Version 2.0
  6. #
  7. # Licensed under the Apache License, Version 2.0 (the "License");
  8. # you may not use this file except in compliance with the License.
  9. # You may obtain a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS,
  15. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18. require 'chef'
  19. require 'chef/application'
  20. require 'chef/client'
  21. require 'chef/config'
  22. require 'chef/daemon'
  23. require 'chef/log'
  24. require 'chef/rest'
  25. require 'open-uri'
  26. require 'fileutils'
  27. class Chef::Application::Solo < Chef::Application
  28. option :config_file,
  29. :short => "-c CONFIG",
  30. :long => "--config CONFIG",
  31. :default => Chef::Config.platform_specfic_path('/etc/chef/solo.rb'),
  32. :description => "The configuration file to use"
  33. option :log_level,
  34. :short => "-l LEVEL",
  35. :long => "--log_level LEVEL",
  36. :description => "Set the log level (debug, info, warn, error, fatal)",
  37. :proc => lambda { |l| l.to_sym }
  38. option :log_location,
  39. :short => "-L LOGLOCATION",
  40. :long => "--logfile LOGLOCATION",
  41. :description => "Set the log file location, defaults to STDOUT",
  42. :proc => nil
  43. option :help,
  44. :short => "-h",
  45. :long => "--help",
  46. :description => "Show this message",
  47. :on => :tail,
  48. :boolean => true,
  49. :show_options => true,
  50. :exit => 0
  51. option :user,
  52. :short => "-u USER",
  53. :long => "--user USER",
  54. :description => "User to set privilege to",
  55. :proc => nil
  56. option :group,
  57. :short => "-g GROUP",
  58. :long => "--group GROUP",
  59. :description => "Group to set privilege to",
  60. :proc => nil
  61. option :daemonize,
  62. :short => "-d",
  63. :long => "--daemonize",
  64. :description => "Daemonize the process",
  65. :proc => lambda { |p| true }
  66. option :interval,
  67. :short => "-i SECONDS",
  68. :long => "--interval SECONDS",
  69. :description => "Run chef-client periodically, in seconds",
  70. :proc => lambda { |s| s.to_i }
  71. option :json_attribs,
  72. :short => "-j JSON_ATTRIBS",
  73. :long => "--json-attributes JSON_ATTRIBS",
  74. :description => "Load attributes from a JSON file or URL",
  75. :proc => nil
  76. option :node_name,
  77. :short => "-N NODE_NAME",
  78. :long => "--node-name NODE_NAME",
  79. :description => "The node name for this client",
  80. :proc => nil
  81. option :splay,
  82. :short => "-s SECONDS",
  83. :long => "--splay SECONDS",
  84. :description => "The splay time for running at intervals, in seconds",
  85. :proc => lambda { |s| s.to_i }
  86. option :recipe_url,
  87. :short => "-r RECIPE_URL",
  88. :long => "--recipe-url RECIPE_URL",
  89. :description => "Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache.",
  90. :proc => nil
  91. option :version,
  92. :short => "-v",
  93. :long => "--version",
  94. :description => "Show chef version",
  95. :boolean => true,
  96. :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"},
  97. :exit => 0
  98. attr_reader :chef_solo_json
  99. def initialize
  100. super
  101. @chef_solo = nil
  102. @chef_solo_json = nil
  103. end
  104. def reconfigure
  105. super
  106. Chef::Config[:solo] = true
  107. if Chef::Config[:daemonize]
  108. Chef::Config[:interval] ||= 1800
  109. end
  110. if Chef::Config[:json_attribs]
  111. begin
  112. json_io = case Chef::Config[:json_attribs]
  113. when /^(http|https):\/\//
  114. @rest = Chef::REST.new(Chef::Config[:json_attribs], nil, nil)
  115. @rest.get_rest(Chef::Config[:json_attribs], true).open
  116. else
  117. open(Chef::Config[:json_attribs])
  118. end
  119. rescue SocketError => error
  120. Chef::Application.fatal!("I cannot connect to #{Chef::Config[:json_attribs]}", 2)
  121. rescue Errno::ENOENT => error
  122. Chef::Application.fatal!("I cannot find #{Chef::Config[:json_attribs]}", 2)
  123. rescue Errno::EACCES => error
  124. Chef::Application.fatal!("Permissions are incorrect on #{Chef::Config[:json_attribs]}. Please chmod a+r #{Chef::Config[:json_attribs]}", 2)
  125. rescue Exception => error
  126. Chef::Application.fatal!("Got an unexpected error reading #{Chef::Config[:json_attribs]}: #{error.message}", 2)
  127. end
  128. begin
  129. @chef_solo_json = Chef::JSONCompat.from_json(json_io.read)
  130. json_io.close unless json_io.closed?
  131. rescue JSON::ParserError => error
  132. Chef::Application.fatal!("Could not parse the provided JSON file (#{Chef::Config[:json_attribs]})!: " + error.message, 2)
  133. end
  134. end
  135. if Chef::Config[:recipe_url]
  136. cookbooks_path = Array(Chef::Config[:cookbook_path]).detect{|e| e =~ /\/cookbooks\/*$/ }
  137. recipes_path = File.expand_path(File.join(cookbooks_path, '..'))
  138. target_file = File.join(recipes_path, 'recipes.tgz')
  139. Chef::Log.debug "Creating path #{recipes_path} to extract recipes into"
  140. FileUtils.mkdir_p recipes_path
  141. path = File.join(recipes_path, 'recipes.tgz')
  142. File.open(path, 'wb') do |f|
  143. open(Chef::Config[:recipe_url]) do |r|
  144. f.write(r.read)
  145. end
  146. end
  147. Chef::Mixin::Command.run_command(:command => "tar zxvfC #{path} #{recipes_path}")
  148. end
  149. end
  150. def setup_application
  151. Chef::Daemon.change_privilege
  152. end
  153. def run_application
  154. if Chef::Config[:daemonize]
  155. Chef::Daemon.daemonize("chef-client")
  156. end
  157. loop do
  158. begin
  159. if Chef::Config[:splay]
  160. splay = rand Chef::Config[:splay]
  161. Chef::Log.debug("Splay sleep #{splay} seconds")
  162. sleep splay
  163. end
  164. @chef_solo = Chef::Client.new(@chef_solo_json)
  165. @chef_solo.run
  166. @chef_solo = nil
  167. if Chef::Config[:interval]
  168. Chef::Log.debug("Sleeping for #{Chef::Config[:interval]} seconds")
  169. sleep Chef::Config[:interval]
  170. else
  171. Chef::Application.exit! "Exiting", 0
  172. end
  173. rescue SystemExit => e
  174. raise
  175. rescue Exception => e
  176. if Chef::Config[:interval]
  177. Chef::Log.error("#{e.class}: #{e}")
  178. Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
  179. Chef::Log.fatal("Sleeping for #{Chef::Config[:interval]} seconds before trying again")
  180. sleep Chef::Config[:interval]
  181. retry
  182. else
  183. Chef::Application.debug_stacktrace(e)
  184. Chef::Application.fatal!("#{e.class}: #{e.message}", 1)
  185. end
  186. ensure
  187. GC.start
  188. end
  189. end
  190. end
  191. end