summaryrefslogtreecommitdiff
path: root/chef/bin/chef-client
blob: 84845edfff8d78ed0138ce0901a7ad0f651519f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env ruby
#
# ./chef-client-new - Build a meal with chef
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

$: << File.join(File.dirname(__FILE__), "..", "lib")

require 'optparse'
require 'rubygems'
require 'chef'
require 'chef/client'
require 'chef/daemon'
require 'json'

config = {
  :config_file => "/etc/chef/client.rb"
}
opts = OptionParser.new do |opts|
  opts.banner = "Usage: #{$0} (options)"
  client_option_hash = { 
    :config_file=> {  :short=>"-c CONFIG",        :long=>"--config CONFIG",       :description=>"The Chef Config file to use", :proc=>nil },
    :user=>        {  :short=>"-u USER",          :long=>"--user USER",           :description=>"User to change uid to before daemonizing", :proc=>nil },
    :group=>       {  :short=>"-g GROUP",         :long=>"--group GROUP",         :description=>"Group to change gid to before daemonizing", :proc=>nil },
    :daemonize=>   {  :short=>"-d",               :long=>"--daemonize",           :description=>"Daemonize the process", :proc=> lambda { |p| true} },
    :interval=>    {  :short=>"-i SECONDS",       :long=>"--interval SECONDS",    :description=>"Run chef-client periodically, in seconds", :proc=>nil },
    :json_attribs=>{  :short=>"-j JSON_ATTRIBS",  :long=>"--json-attributes JSON_ATTRIBS", :description=>"Load attributes from a JSON file", :proc=>nil },
    :node_name=>   {  :short=>"-N NODE_NAME",     :long=>"--node-name NODE_NAME", :description=>"The node name for this client", :proc=>nil },
    :splay=>       {  :short=>"-s SECONDS",       :long=>"--splay SECONDS",       :description=>"The splay time for running at intervals, in seconds", :proc=>nil },
    :log_level=>   {  :short=>"-l LEVEL",         :long=>"--loglevel LEVEL",      :description=>"Set the log level (debug, info, warn, error, fatal)", :proc=>lambda { |p| p.to_sym} },
    :log_location=>{  :short=>"-L LOGLOCATION",   :long=>"--logfile LOGLOCATION", :description=>"Set the log file location, defaults to STDOUT - recommended for daemonizing", :proc=>nil },
    :validation_token=>{ :short=>"-t TOKEN",      :long=>"--token TOKEN",         :description=>"Set the openid validation token", :proc=>nil },
  }

  client_option_hash.each do |opt_key, opt_val|
    opts.on(opt_val[:short],opt_val[:long],opt_val[:description]) do |c|
      config[opt_key] = (opt_val[:proc] && opt_val[:proc].call(c)) || c
    end
  end
  
  opts.on_tail("-h", "--help", "Show this message") do
    puts opts
    exit 0
  end
end
opts.parse!(ARGV)

trap("INT") { Chef.fatal!("SIGINT received, stopping", 2) }
trap("HUP") { 
  Chef::Log.info("SIGHUP received, reloading configuration")
  Chef::Config.from_file(config[:config_file])
  Chef::Config.configure { |c| c.merge!(config) }
}

unless File.exists?(config[:config_file]) and File.readable?(config[:config_file])
  Chef.fatal!("I cannot find or read the config file: #{config[:config_file]}", 1)
end

if config[:json_attribs]
  if File.exists?(config[:json_attribs]) and File.readable?(config[:json_attribs])
    config[:json_attribs] = JSON.parse(IO.read(config[:json_attribs]))
  else
    Chef.fatal!("I cannot find or read #{config[:json_attribs]}", 2)
  end
end

Chef::Config.from_file(config[:config_file])
Chef::Config.configure { |c| c.merge!(config) }

Chef::Daemon.change_privilege

Chef::Log.init(Chef::Config[:log_location])
Chef::Log.level(Chef::Config[:log_level])
Chef::Config[:delay] = 0 

if Chef::Config[:daemonize]
  # We want to set the interval to half an hour, if one is not set.
  Chef::Config[:interval] ||= 1800
  Chef::Daemon.daemonize("chef-client")
end

if Chef::Config[:interval]
  Chef::Config[:delay] = Chef::Config[:interval].to_i + (Chef::Config[:splay] ? rand(Chef::Config[:splay]) : 0)
end

loop do
  begin
    c = Chef::Client.new
    c.json_attribs = Chef::Config[:json_attribs]
    c.validation_token = Chef::Config[:validation_token]
    c.node_name = Chef::Config[:node_name]
    c.run
    if Chef::Config[:interval]
      Chef::Log.debug("Sleeping for #{Chef::Config[:delay]} seconds")
      sleep Chef::Config[:delay]
    else
      exit 0
    end
  rescue SystemExit => e
    raise
  rescue Exception => e
    if Chef::Config[:interval]
      Chef::Log.error("#{e.class}")
      Chef::Log.fatal("#{e}\n#{e.backtrace.join("\n")}")
      Chef::Log.fatal("Sleeping for #{Chef::Config[:delay]} seconds before trying again")
      sleep Chef::Config[:delay]
      retry
    else
      raise
    end
  end
end