Merge lp:~harlowja/cloud-init/better-chef-module into lp:~cloud-init-dev/cloud-init/trunk
- better-chef-module
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1036 |
Proposed branch: | lp:~harlowja/cloud-init/better-chef-module |
Merge into: | lp:~cloud-init-dev/cloud-init/trunk |
Diff against target: |
562 lines (+414/-67) 4 files modified
cloudinit/config/cc_chef.py (+247/-58) cloudinit/util.py (+4/-0) templates/chef_client.rb.tmpl (+42/-9) tests/unittests/test_handler/test_handler_chef.py (+121/-0) |
To merge this branch: | bzr merge lp:~harlowja/cloud-init/better-chef-module |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
cloud-init Commiters | Pending | ||
Review via email: mp+238040@code.launchpad.net |
Commit message
Description of the change
Increase the robustness/
Add the following adjustments to the chef template and module:
- Make it so that the chef directories can be provided (defaults
to the existing directories)
- Make the params much more configurable, and if a parameter is
provided in the chef configuration it will override existing template
parameters.
- Make the template skip lines if the values are None in the configuration
so that template lines can be removed if/when this is desirable.
- Allow the firstboot json path to be configurable (defaults to the
existing location).
- Adds a basic set of tests to ensure that good things are happening.
- Make a helper function to tell if already installed.
- Have the install routine not run chef after installed but have it instead
return a result to tell the caller to run the chef program once completed.
- Use the generated_by() utility function to give the ruby template a
better header comment.
- Set special parameters after selecting the basic chef parameters.
- Allow for the running after install and run arguments to be configured.
- Allow the omnibus url fetching retries to be configurable.
- Move the chef running to its own helper function
- 1027. By Joshua Harlow
-
Fix newline added at end of file
- 1028. By Joshua Harlow
-
Add a few template delete tests
- 1029. By Joshua Harlow
-
Move the installation code to its own function
- 1030. By Joshua Harlow
-
Some more reworkings
- Make a helper function to tell if already installed.
- Have the install routine not run chef after installed
but have it instead return a result to tell the caller
to run the chef program once completed. - 1031. By Joshua Harlow
-
More adjustments
- Use the generated_by() utility function to
give the ruby template a better header comment
- Set special parameters after selecting the basic
chef parameters. - 1032. By Joshua Harlow
-
Allow for the running after install and run arguments to be configured
Instead of only running the client when installed from gems, allow it
to be ran from other install modes as well (if configured) and allow the
arguments that are passed to the client when ran to be altered (if so
desired). - 1033. By Joshua Harlow
-
Allow the omnibus url fetching retries to be configurable
Jer (jeremiah-wuenschel) : | # |
Joshua Harlow (harlowja) : | # |
Jer (jeremiah-wuenschel) wrote : | # |
- 1034. By Joshua Harlow
-
Use the util function to get the chef base directories
- 1035. By Joshua Harlow
-
Always ensure we create the /etc/chef dir
- 1036. By Joshua Harlow
-
Move the chef running to its own helper function
- 1037. By Joshua Harlow
-
Ensure that any template paths have associated directories
When the template provides a path, make sure that before the
template is written that the path that is now in the template
has the associated directory created (if not already created). - 1038. By Joshua Harlow
-
Have the caller find the param paths instead of the param creator
- 1039. By Joshua Harlow
-
Follow the same constant variable naming scheme for the path tpl keys
- 1040. By Joshua Harlow
-
Allow running even if installed
Standardize on using the chef_cfg key 'exec' which can be used
when installing to tell the caller to run the chef client or can
also be used if the client is already installed and its requested
to be ran.To retain existing behavior 'exec' does not by default assume to
be true, unless explicitly provided or a gems mode install is
requested. - 1041. By Joshua Harlow
-
Add a comment explaining the param path logic
- 1042. By Joshua Harlow
-
Retain the old behavior for mandatory keys
The keys 'server_url' and 'validation_name' were
previously mandatory, we should retain that behavior
for now. - 1043. By Joshua Harlow
-
Add a post-run method that can be used to delete validation.pem files
For those who run chef in non-daemon mode, they would like to delete
the validation.pem file if chef finishes as expected to remove that file
from existing in an easy to read manner. - 1044. By Joshua Harlow
-
Use the key contants in the default key => value set
- 1045. By Joshua Harlow
-
Prefer immutable structures
- 1046. By Joshua Harlow
-
Update chef module docstring to reflect the new style
Preview Diff
1 | === modified file 'cloudinit/config/cc_chef.py' |
2 | --- cloudinit/config/cc_chef.py 2014-08-26 18:50:11 +0000 |
3 | +++ cloudinit/config/cc_chef.py 2014-11-22 01:13:36 +0000 |
4 | @@ -18,6 +18,57 @@ |
5 | # You should have received a copy of the GNU General Public License |
6 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
7 | |
8 | +""" |
9 | +**Summary:** module that configures, starts and installs chef. |
10 | + |
11 | +**Description:** This module enables chef to be installed (from packages or |
12 | +from gems, or from omnibus). Before this occurs chef configurations are |
13 | +written to disk (validation.pem, client.pem, firstboot.json, client.rb), |
14 | +and needed chef folders/directories are created (/etc/chef and /var/log/chef |
15 | +and so-on). Then once installing proceeds correctly if configured chef will |
16 | +be started (in daemon mode or in non-daemon mode) and then once that has |
17 | +finished (if ran in non-daemon mode this will be when chef finishes |
18 | +converging, if ran in daemon mode then no further actions are possible since |
19 | +chef will have forked into its own process) then a post run function can |
20 | +run that can do finishing activities (such as removing the validation pem |
21 | +file). |
22 | + |
23 | +It can be configured with the following option structure:: |
24 | + |
25 | + chef: |
26 | + directories: (defaulting to /etc/chef, /var/log/chef, /var/lib/chef, |
27 | + /var/cache/chef, /var/backups/chef, /var/run/chef) |
28 | + validation_key or validation_cert: (optional string to be written to |
29 | + /etc/chef/validation.pem) |
30 | + firstboot_path: (path to write run_list and initial_attributes keys that |
31 | + should also be present in this configuration, defaults |
32 | + to /etc/chef/firstboot.json) |
33 | + exec: boolean to run or not run chef (defaults to false, unless |
34 | + a gem installed is requested |
35 | + where this will then default |
36 | + to true) |
37 | + |
38 | + chef.rb template keys (if falsey, then will be skipped and not |
39 | + written to /etc/chef/client.rb) |
40 | + |
41 | + chef: |
42 | + client_key: |
43 | + environment: |
44 | + file_backup_path: |
45 | + file_cache_path: |
46 | + json_attribs: |
47 | + log_level: |
48 | + log_location: |
49 | + node_name: |
50 | + pid_file: |
51 | + server_url: |
52 | + show_time: |
53 | + ssl_verify_mode: |
54 | + validation_key: |
55 | + validation_name: |
56 | +""" |
57 | + |
58 | +import itertools |
59 | import json |
60 | import os |
61 | |
62 | @@ -27,16 +78,107 @@ |
63 | |
64 | RUBY_VERSION_DEFAULT = "1.8" |
65 | |
66 | -CHEF_DIRS = [ |
67 | +CHEF_DIRS = tuple([ |
68 | '/etc/chef', |
69 | '/var/log/chef', |
70 | '/var/lib/chef', |
71 | '/var/cache/chef', |
72 | '/var/backups/chef', |
73 | '/var/run/chef', |
74 | -] |
75 | +]) |
76 | +REQUIRED_CHEF_DIRS = tuple([ |
77 | + '/etc/chef', |
78 | +]) |
79 | |
80 | OMNIBUS_URL = "https://www.opscode.com/chef/install.sh" |
81 | +OMNIBUS_URL_RETRIES = 5 |
82 | + |
83 | +CHEF_VALIDATION_PEM_PATH = '/etc/chef/validation.pem' |
84 | +CHEF_FB_PATH = '/etc/chef/firstboot.json' |
85 | +CHEF_RB_TPL_DEFAULTS = { |
86 | + # These are ruby symbols... |
87 | + 'ssl_verify_mode': ':verify_none', |
88 | + 'log_level': ':info', |
89 | + # These are not symbols... |
90 | + 'log_location': '/var/log/chef/client.log', |
91 | + 'validation_key': CHEF_VALIDATION_PEM_PATH, |
92 | + 'client_key': "/etc/chef/client.pem", |
93 | + 'json_attribs': CHEF_FB_PATH, |
94 | + 'file_cache_path': "/var/cache/chef", |
95 | + 'file_backup_path': "/var/backups/chef", |
96 | + 'pid_file': "/var/run/chef/client.pid", |
97 | + 'show_time': True, |
98 | +} |
99 | +CHEF_RB_TPL_BOOL_KEYS = frozenset(['show_time']) |
100 | +CHEF_RB_TPL_PATH_KEYS = frozenset([ |
101 | + 'log_location', |
102 | + 'validation_key', |
103 | + 'client_key', |
104 | + 'file_cache_path', |
105 | + 'json_attribs', |
106 | + 'file_cache_path', |
107 | + 'pid_file', |
108 | +]) |
109 | +CHEF_RB_TPL_KEYS = list(CHEF_RB_TPL_DEFAULTS.keys()) |
110 | +CHEF_RB_TPL_KEYS.extend(CHEF_RB_TPL_BOOL_KEYS) |
111 | +CHEF_RB_TPL_KEYS.extend(CHEF_RB_TPL_PATH_KEYS) |
112 | +CHEF_RB_TPL_KEYS.extend([ |
113 | + 'server_url', |
114 | + 'node_name', |
115 | + 'environment', |
116 | + 'validation_name', |
117 | +]) |
118 | +CHEF_RB_TPL_KEYS = frozenset(CHEF_RB_TPL_KEYS) |
119 | +CHEF_RB_PATH = '/etc/chef/client.rb' |
120 | +CHEF_EXEC_PATH = '/usr/bin/chef-client' |
121 | +CHEF_EXEC_DEF_ARGS = tuple(['-d', '-i', '1800', '-s', '20']) |
122 | + |
123 | + |
124 | +def is_installed(): |
125 | + if not os.path.isfile(CHEF_EXEC_PATH): |
126 | + return False |
127 | + if not os.access(CHEF_EXEC_PATH, os.X_OK): |
128 | + return False |
129 | + return True |
130 | + |
131 | + |
132 | +def post_run_chef(chef_cfg, log): |
133 | + delete_pem = util.get_cfg_option_bool(chef_cfg, |
134 | + 'delete_validation_post_exec', |
135 | + default=False) |
136 | + if delete_pem and os.path.isfile(CHEF_VALIDATION_PEM_PATH): |
137 | + os.unlink(CHEF_VALIDATION_PEM_PATH) |
138 | + |
139 | + |
140 | +def get_template_params(iid, chef_cfg, log): |
141 | + params = CHEF_RB_TPL_DEFAULTS.copy() |
142 | + # Allow users to overwrite any of the keys they want (if they so choose), |
143 | + # when a value is None, then the value will be set to None and no boolean |
144 | + # or string version will be populated... |
145 | + for (k, v) in chef_cfg.items(): |
146 | + if k not in CHEF_RB_TPL_KEYS: |
147 | + log.debug("Skipping unknown chef template key '%s'", k) |
148 | + continue |
149 | + if v is None: |
150 | + params[k] = None |
151 | + else: |
152 | + # This will make the value a boolean or string... |
153 | + if k in CHEF_RB_TPL_BOOL_KEYS: |
154 | + params[k] = util.get_cfg_option_bool(chef_cfg, k) |
155 | + else: |
156 | + params[k] = util.get_cfg_option_str(chef_cfg, k) |
157 | + # These ones are overwritten to be exact values... |
158 | + params.update({ |
159 | + 'generated_by': util.make_header(), |
160 | + 'node_name': util.get_cfg_option_str(chef_cfg, 'node_name', |
161 | + default=iid), |
162 | + 'environment': util.get_cfg_option_str(chef_cfg, 'environment', |
163 | + default='_default'), |
164 | + # These two are mandatory... |
165 | + 'server_url': chef_cfg['server_url'], |
166 | + 'validation_name': chef_cfg['validation_name'], |
167 | + }) |
168 | + return params |
169 | |
170 | |
171 | def handle(name, cfg, cloud, log, _args): |
172 | @@ -49,7 +191,10 @@ |
173 | chef_cfg = cfg['chef'] |
174 | |
175 | # Ensure the chef directories we use exist |
176 | - for d in CHEF_DIRS: |
177 | + chef_dirs = util.get_cfg_option_list(chef_cfg, 'directories') |
178 | + if not chef_dirs: |
179 | + chef_dirs = list(CHEF_DIRS) |
180 | + for d in itertools.chain(chef_dirs, REQUIRED_CHEF_DIRS): |
181 | util.ensure_dir(d) |
182 | |
183 | # Set the validation key based on the presence of either 'validation_key' |
184 | @@ -57,64 +202,108 @@ |
185 | # takes precedence |
186 | for key in ('validation_key', 'validation_cert'): |
187 | if key in chef_cfg and chef_cfg[key]: |
188 | - util.write_file('/etc/chef/validation.pem', chef_cfg[key]) |
189 | + util.write_file(CHEF_VALIDATION_PEM_PATH, chef_cfg[key]) |
190 | break |
191 | |
192 | # Create the chef config from template |
193 | template_fn = cloud.get_template_filename('chef_client.rb') |
194 | if template_fn: |
195 | iid = str(cloud.datasource.get_instance_id()) |
196 | - params = { |
197 | - 'server_url': chef_cfg['server_url'], |
198 | - 'node_name': util.get_cfg_option_str(chef_cfg, 'node_name', iid), |
199 | - 'environment': util.get_cfg_option_str(chef_cfg, 'environment', |
200 | - '_default'), |
201 | - 'validation_name': chef_cfg['validation_name'] |
202 | - } |
203 | - templater.render_to_file(template_fn, '/etc/chef/client.rb', params) |
204 | - else: |
205 | - log.warn("No template found, not rendering to /etc/chef/client.rb") |
206 | - |
207 | - # set the firstboot json |
208 | - initial_json = {} |
209 | - if 'run_list' in chef_cfg: |
210 | - initial_json['run_list'] = chef_cfg['run_list'] |
211 | - if 'initial_attributes' in chef_cfg: |
212 | - initial_attributes = chef_cfg['initial_attributes'] |
213 | - for k in list(initial_attributes.keys()): |
214 | - initial_json[k] = initial_attributes[k] |
215 | - util.write_file('/etc/chef/firstboot.json', json.dumps(initial_json)) |
216 | - |
217 | + params = get_template_params(iid, chef_cfg, log) |
218 | + # Do a best effort attempt to ensure that the template values that |
219 | + # are associated with paths have there parent directory created |
220 | + # before they are used by the chef-client itself. |
221 | + param_paths = set() |
222 | + for (k, v) in params.items(): |
223 | + if k in CHEF_RB_TPL_PATH_KEYS and v: |
224 | + param_paths.add(os.path.dirname(v)) |
225 | + util.ensure_dirs(param_paths) |
226 | + templater.render_to_file(template_fn, CHEF_RB_PATH, params) |
227 | + else: |
228 | + log.warn("No template found, not rendering to %s", |
229 | + CHEF_RB_PATH) |
230 | + |
231 | + # Set the firstboot json |
232 | + fb_filename = util.get_cfg_option_str(chef_cfg, 'firstboot_path', |
233 | + default=CHEF_FB_PATH) |
234 | + if not fb_filename: |
235 | + log.info("First boot path empty, not writing first boot json file") |
236 | + else: |
237 | + initial_json = {} |
238 | + if 'run_list' in chef_cfg: |
239 | + initial_json['run_list'] = chef_cfg['run_list'] |
240 | + if 'initial_attributes' in chef_cfg: |
241 | + initial_attributes = chef_cfg['initial_attributes'] |
242 | + for k in list(initial_attributes.keys()): |
243 | + initial_json[k] = initial_attributes[k] |
244 | + util.write_file(fb_filename, json.dumps(initial_json)) |
245 | + |
246 | + # Try to install chef, if its not already installed... |
247 | + force_install = util.get_cfg_option_bool(chef_cfg, |
248 | + 'force_install', default=False) |
249 | + if not is_installed() or force_install: |
250 | + run = install_chef(cloud, chef_cfg, log) |
251 | + elif is_installed(): |
252 | + run = util.get_cfg_option_bool(chef_cfg, 'exec', default=False) |
253 | + else: |
254 | + run = False |
255 | + if run: |
256 | + run_chef(chef_cfg, log) |
257 | + post_run_chef(chef_cfg, log) |
258 | + |
259 | + |
260 | +def run_chef(chef_cfg, log): |
261 | + log.debug('Running chef-client') |
262 | + cmd = [CHEF_EXEC_PATH] |
263 | + if 'exec_arguments' in chef_cfg: |
264 | + cmd_args = chef_cfg['exec_arguments'] |
265 | + if isinstance(cmd_args, (list, tuple)): |
266 | + cmd.extend(cmd_args) |
267 | + elif isinstance(cmd_args, (str, basestring)): |
268 | + cmd.append(cmd_args) |
269 | + else: |
270 | + log.warn("Unknown type %s provided for chef" |
271 | + " 'exec_arguments' expected list, tuple," |
272 | + " or string", type(cmd_args)) |
273 | + cmd.extend(CHEF_EXEC_DEF_ARGS) |
274 | + else: |
275 | + cmd.extend(CHEF_EXEC_DEF_ARGS) |
276 | + util.subp(cmd, capture=False) |
277 | + |
278 | + |
279 | +def install_chef(cloud, chef_cfg, log): |
280 | # If chef is not installed, we install chef based on 'install_type' |
281 | - if (not os.path.isfile('/usr/bin/chef-client') or |
282 | - util.get_cfg_option_bool(chef_cfg, |
283 | - 'force_install', default=False)): |
284 | - |
285 | - install_type = util.get_cfg_option_str(chef_cfg, 'install_type', |
286 | - 'packages') |
287 | - if install_type == "gems": |
288 | - # this will install and run the chef-client from gems |
289 | - chef_version = util.get_cfg_option_str(chef_cfg, 'version', None) |
290 | - ruby_version = util.get_cfg_option_str(chef_cfg, 'ruby_version', |
291 | - RUBY_VERSION_DEFAULT) |
292 | - install_chef_from_gems(cloud.distro, ruby_version, chef_version) |
293 | - # and finally, run chef-client |
294 | - log.debug('Running chef-client') |
295 | - util.subp(['/usr/bin/chef-client', |
296 | - '-d', '-i', '1800', '-s', '20'], capture=False) |
297 | - elif install_type == 'packages': |
298 | - # this will install and run the chef-client from packages |
299 | - cloud.distro.install_packages(('chef',)) |
300 | - elif install_type == 'omnibus': |
301 | - url = util.get_cfg_option_str(chef_cfg, "omnibus_url", OMNIBUS_URL) |
302 | - content = url_helper.readurl(url=url, retries=5) |
303 | - with util.tempdir() as tmpd: |
304 | - # use tmpd over tmpfile to avoid 'Text file busy' on execute |
305 | - tmpf = "%s/chef-omnibus-install" % tmpd |
306 | - util.write_file(tmpf, str(content), mode=0700) |
307 | - util.subp([tmpf], capture=False) |
308 | - else: |
309 | - log.warn("Unknown chef install type %s", install_type) |
310 | + install_type = util.get_cfg_option_str(chef_cfg, 'install_type', |
311 | + 'packages') |
312 | + run = util.get_cfg_option_bool(chef_cfg, 'exec', default=False) |
313 | + if install_type == "gems": |
314 | + # This will install and run the chef-client from gems |
315 | + chef_version = util.get_cfg_option_str(chef_cfg, 'version', None) |
316 | + ruby_version = util.get_cfg_option_str(chef_cfg, 'ruby_version', |
317 | + RUBY_VERSION_DEFAULT) |
318 | + install_chef_from_gems(cloud.distro, ruby_version, chef_version) |
319 | + # Retain backwards compat, by preferring True instead of False |
320 | + # when not provided/overriden... |
321 | + run = util.get_cfg_option_bool(chef_cfg, 'exec', default=True) |
322 | + elif install_type == 'packages': |
323 | + # This will install and run the chef-client from packages |
324 | + cloud.distro.install_packages(('chef',)) |
325 | + elif install_type == 'omnibus': |
326 | + # This will install as a omnibus unified package |
327 | + url = util.get_cfg_option_str(chef_cfg, "omnibus_url", OMNIBUS_URL) |
328 | + retries = max(0, util.get_cfg_option_int(chef_cfg, |
329 | + "omnibus_url_retries", |
330 | + default=OMNIBUS_URL_RETRIES)) |
331 | + content = url_helper.readurl(url=url, retries=retries) |
332 | + with util.tempdir() as tmpd: |
333 | + # Use tmpdir over tmpfile to avoid 'text file busy' on execute |
334 | + tmpf = "%s/chef-omnibus-install" % tmpd |
335 | + util.write_file(tmpf, str(content), mode=0700) |
336 | + util.subp([tmpf], capture=False) |
337 | + else: |
338 | + log.warn("Unknown chef install type '%s'", install_type) |
339 | + run = False |
340 | + return run |
341 | |
342 | |
343 | def get_ruby_packages(version): |
344 | @@ -133,9 +322,9 @@ |
345 | util.sym_link('/usr/bin/ruby%s' % ruby_version, '/usr/bin/ruby') |
346 | if chef_version: |
347 | util.subp(['/usr/bin/gem', 'install', 'chef', |
348 | - '-v %s' % chef_version, '--no-ri', |
349 | - '--no-rdoc', '--bindir', '/usr/bin', '-q'], capture=False) |
350 | + '-v %s' % chef_version, '--no-ri', |
351 | + '--no-rdoc', '--bindir', '/usr/bin', '-q'], capture=False) |
352 | else: |
353 | util.subp(['/usr/bin/gem', 'install', 'chef', |
354 | - '--no-ri', '--no-rdoc', '--bindir', |
355 | - '/usr/bin', '-q'], capture=False) |
356 | + '--no-ri', '--no-rdoc', '--bindir', |
357 | + '/usr/bin', '-q'], capture=False) |
358 | |
359 | === modified file 'cloudinit/util.py' |
360 | --- cloudinit/util.py 2014-10-29 19:39:04 +0000 |
361 | +++ cloudinit/util.py 2014-11-22 01:13:36 +0000 |
362 | @@ -399,6 +399,10 @@ |
363 | return val |
364 | |
365 | |
366 | +def get_cfg_option_int(yobj, key, default=0): |
367 | + return int(get_cfg_option_str(yobj, key, default=default)) |
368 | + |
369 | + |
370 | def system_info(): |
371 | return { |
372 | 'platform': platform.platform(), |
373 | |
374 | === modified file 'templates/chef_client.rb.tmpl' |
375 | --- templates/chef_client.rb.tmpl 2014-03-05 23:05:59 +0000 |
376 | +++ templates/chef_client.rb.tmpl 2014-11-22 01:13:36 +0000 |
377 | @@ -9,17 +9,50 @@ |
378 | validation_name: XYZ |
379 | server_url: XYZ |
380 | -#} |
381 | -log_level :info |
382 | -log_location "/var/log/chef/client.log" |
383 | -ssl_verify_mode :verify_none |
384 | +{{generated_by}} |
385 | +{# |
386 | +The reason these are not in quotes is because they are ruby |
387 | +symbols that will be placed inside here, and not actual strings... |
388 | +#} |
389 | +{% if log_level %} |
390 | +log_level {{log_level}} |
391 | +{% endif %} |
392 | +{% if ssl_verify_mode %} |
393 | +ssl_verify_mode {{ssl_verify_mode}} |
394 | +{% endif %} |
395 | +{% if log_location %} |
396 | +log_location "{{log_location}}" |
397 | +{% endif %} |
398 | +{% if validation_name %} |
399 | validation_client_name "{{validation_name}}" |
400 | -validation_key "/etc/chef/validation.pem" |
401 | -client_key "/etc/chef/client.pem" |
402 | +{% endif %} |
403 | +{% if validation_key %} |
404 | +validation_key "{{validation_key}}" |
405 | +{% endif %} |
406 | +{% if client_key %} |
407 | +client_key "{{client_key}}" |
408 | +{% endif %} |
409 | +{% if server_url %} |
410 | chef_server_url "{{server_url}}" |
411 | +{% endif %} |
412 | +{% if environment %} |
413 | environment "{{environment}}" |
414 | +{% endif %} |
415 | +{% if node_name %} |
416 | node_name "{{node_name}}" |
417 | -json_attribs "/etc/chef/firstboot.json" |
418 | -file_cache_path "/var/cache/chef" |
419 | -file_backup_path "/var/backups/chef" |
420 | -pid_file "/var/run/chef/client.pid" |
421 | +{% endif %} |
422 | +{% if json_attribs %} |
423 | +json_attribs "{{json_attribs}}" |
424 | +{% endif %} |
425 | +{% if file_cache_path %} |
426 | +file_cache_path "{{file_cache_path}}" |
427 | +{% endif %} |
428 | +{% if file_backup_path %} |
429 | +file_backup_path "{{file_backup_path}}" |
430 | +{% endif %} |
431 | +{% if pid_file %} |
432 | +pid_file "{{pid_file}}" |
433 | +{% endif %} |
434 | +{% if show_time %} |
435 | Chef::Log::Formatter.show_time = true |
436 | +{% endif %} |
437 | |
438 | === added file 'tests/unittests/test_handler/test_handler_chef.py' |
439 | --- tests/unittests/test_handler/test_handler_chef.py 1970-01-01 00:00:00 +0000 |
440 | +++ tests/unittests/test_handler/test_handler_chef.py 2014-11-22 01:13:36 +0000 |
441 | @@ -0,0 +1,121 @@ |
442 | +import json |
443 | +import os |
444 | + |
445 | +from cloudinit.config import cc_chef |
446 | + |
447 | +from cloudinit import cloud |
448 | +from cloudinit import distros |
449 | +from cloudinit import helpers |
450 | +from cloudinit import util |
451 | +from cloudinit.sources import DataSourceNone |
452 | + |
453 | +from .. import helpers as t_help |
454 | + |
455 | +import logging |
456 | + |
457 | +LOG = logging.getLogger(__name__) |
458 | + |
459 | + |
460 | +class TestChef(t_help.FilesystemMockingTestCase): |
461 | + def setUp(self): |
462 | + super(TestChef, self).setUp() |
463 | + self.tmp = self.makeDir(prefix="unittest_") |
464 | + |
465 | + def fetch_cloud(self, distro_kind): |
466 | + cls = distros.fetch(distro_kind) |
467 | + paths = helpers.Paths({}) |
468 | + distro = cls(distro_kind, {}, paths) |
469 | + ds = DataSourceNone.DataSourceNone({}, distro, paths, None) |
470 | + return cloud.Cloud(ds, paths, {}, distro, None) |
471 | + |
472 | + def test_no_config(self): |
473 | + self.patchUtils(self.tmp) |
474 | + self.patchOS(self.tmp) |
475 | + |
476 | + cfg = {} |
477 | + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) |
478 | + for d in cc_chef.CHEF_DIRS: |
479 | + self.assertFalse(os.path.isdir(d)) |
480 | + |
481 | + def test_basic_config(self): |
482 | + # This should create a file of the format... |
483 | + """ |
484 | + # Created by cloud-init v. 0.7.6 on Sat, 11 Oct 2014 23:57:21 +0000 |
485 | + log_level :info |
486 | + ssl_verify_mode :verify_none |
487 | + log_location "/var/log/chef/client.log" |
488 | + validation_client_name "bob" |
489 | + validation_key "/etc/chef/validation.pem" |
490 | + client_key "/etc/chef/client.pem" |
491 | + chef_server_url "localhost" |
492 | + environment "_default" |
493 | + node_name "iid-datasource-none" |
494 | + json_attribs "/etc/chef/firstboot.json" |
495 | + file_cache_path "/var/cache/chef" |
496 | + file_backup_path "/var/backups/chef" |
497 | + pid_file "/var/run/chef/client.pid" |
498 | + Chef::Log::Formatter.show_time = true |
499 | + """ |
500 | + tpl_file = util.load_file('templates/chef_client.rb.tmpl') |
501 | + self.patchUtils(self.tmp) |
502 | + self.patchOS(self.tmp) |
503 | + |
504 | + util.write_file('/etc/cloud/templates/chef_client.rb.tmpl', tpl_file) |
505 | + cfg = { |
506 | + 'chef': { |
507 | + 'server_url': 'localhost', |
508 | + 'validation_name': 'bob', |
509 | + }, |
510 | + } |
511 | + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) |
512 | + for d in cc_chef.CHEF_DIRS: |
513 | + self.assertTrue(os.path.isdir(d)) |
514 | + c = util.load_file(cc_chef.CHEF_RB_PATH) |
515 | + for k, v in cfg['chef'].items(): |
516 | + self.assertIn(v, c) |
517 | + for k, v in cc_chef.CHEF_RB_TPL_DEFAULTS.items(): |
518 | + if isinstance(v, basestring): |
519 | + self.assertIn(v, c) |
520 | + c = util.load_file(cc_chef.CHEF_FB_PATH) |
521 | + self.assertEqual({}, json.loads(c)) |
522 | + |
523 | + def test_firstboot_json(self): |
524 | + self.patchUtils(self.tmp) |
525 | + self.patchOS(self.tmp) |
526 | + |
527 | + cfg = { |
528 | + 'chef': { |
529 | + 'server_url': 'localhost', |
530 | + 'validation_name': 'bob', |
531 | + 'run_list': ['a', 'b', 'c'], |
532 | + 'initial_attributes': { |
533 | + 'c': 'd', |
534 | + } |
535 | + }, |
536 | + } |
537 | + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) |
538 | + c = util.load_file(cc_chef.CHEF_FB_PATH) |
539 | + self.assertEqual( |
540 | + { |
541 | + 'run_list': ['a', 'b', 'c'], |
542 | + 'c': 'd', |
543 | + }, json.loads(c)) |
544 | + |
545 | + def test_template_deletes(self): |
546 | + tpl_file = util.load_file('templates/chef_client.rb.tmpl') |
547 | + self.patchUtils(self.tmp) |
548 | + self.patchOS(self.tmp) |
549 | + |
550 | + util.write_file('/etc/cloud/templates/chef_client.rb.tmpl', tpl_file) |
551 | + cfg = { |
552 | + 'chef': { |
553 | + 'server_url': 'localhost', |
554 | + 'validation_name': 'bob', |
555 | + 'json_attribs': None, |
556 | + 'show_time': None, |
557 | + }, |
558 | + } |
559 | + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) |
560 | + c = util.load_file(cc_chef.CHEF_RB_PATH) |
561 | + self.assertNotIn('json_attribs', c) |
562 | + self.assertNotIn('Formatter.show_time', c) |
Corrected a diff comment.