Merge lp:~avishai-ish-shalom/cloud-init/chef into lp:~cloud-init-dev/cloud-init/trunk

Proposed by Avishai Ish-Shalom
Status: Merged
Merged at revision: 407
Proposed branch: lp:~avishai-ish-shalom/cloud-init/chef
Merge into: lp:~cloud-init-dev/cloud-init/trunk
Diff against target: 218 lines (+187/-1)
5 files modified
cloudinit/CloudConfig/cc_chef.py (+80/-0)
cloudinit/CloudConfig/cc_set_hostname.py (+6/-1)
doc/examples/cloud-config-chef.txt (+38/-0)
templates/chef_client.rb.tmpl (+12/-0)
tools/write-mime-multipart.py (+51/-0)
To merge this branch: bzr merge lp:~avishai-ish-shalom/cloud-init/chef
Reviewer Review Type Date Requested Status
Mike Moulton (community) Needs Fixing
Avishai Ish-Shalom (community) Needs Resubmitting
Scott Moser Needs Information
Review via email: mp+66528@code.launchpad.net

This proposal supersedes a proposal from 2011-04-29.

Description of the change

Added hostname prefix option, custom hostname attribute.
Added chef plugin.

To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

Avishai,
  Thank you very much for taking the time to put this together. I'd like to merge this for Oneiric.

  I have a couple comments
a.) Why the change to cloudinit/CloudConfig/cc_set_hostname.py ?
    You summarized that as "Allow configurable hostname prefix and hostname attribute", but I don't initially see how its related to the rest of the commits. If its valuable, we can merge it, but I'd like that to be done separately if it is not related.
b.) You added 'tools/write-mime-multipart.py'
   This file has been moved to cloud-utils (http://launchpad.net/cloud-utils) .

  Thanks again, I'm looking forward to hearing back from you. Sorry for the slow reply.

review: Needs Information
Revision history for this message
Avishai Ish-Shalom (avishai-ish-shalom) wrote :

A. it's an unrelated feature. being a git man i thought a branch can be
done in the local repo with bazzar, then i got lazy and just left the
first commit. i've since saw the error of my ways and will branch a
different repo in the future (unless you can tell me what's the bazzar
equivalent of feature branch is).

On 18/07/11 18:29, Scott Moser wrote:
> Review: Needs Information
> Avishai,
> Thank you very much for taking the time to put this together. I'd like to merge this for Oneiric.
>
> I have a couple comments
> a.) Why the change to cloudinit/CloudConfig/cc_set_hostname.py ?
> You summarized that as "Allow configurable hostname prefix and hostname attribute", but I don't initially see how its related to the rest of the commits. If its valuable, we can merge it, but I'd like that to be done separately if it is not related.
> b.) You added 'tools/write-mime-multipart.py'
> This file has been moved to cloud-utils (http://launchpad.net/cloud-utils) .
>
> Thanks again, I'm looking forward to hearing back from you. Sorry for the slow reply.
>

Revision history for this message
Scott Moser (smoser) wrote :

On Mon, 18 Jul 2011, Avishai Ish-Shalom wrote:

> A. it's an unrelated feature.

OK, then I can just drop it in the merge, or you can modify it back.

Revision history for this message
Mike Moulton (mmoulton) wrote :

I seem to be unable to use the chef feature as included in the Oneiric Beta 1. I have noticed the following errors with this:

- If 'install_type' is undefined, the process fails

- 'firstboot.json' fails to write as it does not yet exist. Assume the missing 'w' param is needed.

- 'validation_cert' writes to '/etc/chef/validation.cert', however to the best of my knowledge chef-client does not know about this file. Instead, it's looking for '/etc/chef/validation.pem' as defined in the client.rb template.

- There seems to be a problem with 'subprocess.check_call([gem_bin', this failed for me, saying 'gem_bin' could not be found. Things work better if you do not install from gems.

I'm not familiar with how to submit patches via launchpad.net, otherwise I would have included fixes for these items.

I would love to get these items fixed for potential inclusion in Oneiric before it goes stable. Let me know what I can do to help.

review: Needs Fixing
Revision history for this message
Avishai Ish-Shalom (avishai-ish-shalom) wrote :

Fixed review rejects in 397.

review: Needs Resubmitting
Revision history for this message
Mike Moulton (mmoulton) wrote :

Avishai, thank you for the quick turn around. Your changes look like they will address all of my issues.

Scott, is it too late to get this into Oneiric?

review: Approve
Revision history for this message
Scott Moser (smoser) wrote :

On Thu, 8 Sep 2011, Mike Moulton wrote:

> Review: Approve
>
> Avishai, thank you for the quick turn around. Your changes look like they will address all of my issues.
>

Mike sorry for the run-around, but can you
a.) please actually test the code path. I can make you a cloud-init deb if needed.
  The cleanest path to do that is probably to
  - launch instance with no user-data
  - install new cloud-init
  - rm -Rf /var/lib/cloud
  - mkdir -p /var/lib/cloud/data/seed/nocloud-net
  - populate /var/lib/cloud/data/seed/nocloud-net/meta-data and user-data
    with the same user data you would have used
  - reboot
  - check
b.) open a bug for this? and describe what user-data you gave.

We will get this into oneiric.

Revision history for this message
Mike Moulton (mmoulton) wrote :

Scott, no problem, I will test this. If you could make a deb, that would help.

Revision history for this message
Scott Moser (smoser) wrote :

On Thu, 8 Sep 2011, Mike Moulton wrote:

> Scott, no problem, I will test this. If you could make a deb, that would help.

can you please try
http://people.canonical.com/~smoser/cloud-init_0.6.2~chef0_all.deb

Revision history for this message
Mike Moulton (mmoulton) wrote :

I was able to get this to work with the following patch to cc_chef.py:

root@ip-10-168-67-228:~# diff /usr/lib/python2.7/dist-packages/cloudinit/CloudConfig/cc_chef.py /tmp/cc_chef.py
35c35
< install_type = cfg.get_cfg_option_str("install_type", "packages")
---
> install_type = util.get_cfg_option_str(chef_cfg, "install_type", "packages")
37c37
< if install_type = "gems":
---
> if install_type == "gems":
65,66c65,66
< firstboot_json_fh.write(runlist_item + "\n")
< firstboot_json_fh.write("]\n\}")
---
> firstboot_json_fh.write("\"" + runlist_item + "\"\n")
> firstboot_json_fh.write("]\n}")

Can either Scott or Avishai apply this?

I will create a bug as requested with the user data I used to get this to work. Please note, the user data will not be testable by others without their own valid chef server and validator key.

review: Needs Fixing
Revision history for this message
Mike Moulton (mmoulton) wrote :

I have submitted the following bug https://bugs.launchpad.net/cloud-init/+bug/845161 to address this issue.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'cloudinit/CloudConfig/cc_chef.py'
--- cloudinit/CloudConfig/cc_chef.py 1970-01-01 00:00:00 +0000
+++ cloudinit/CloudConfig/cc_chef.py 2011-06-30 22:52:32 +0000
@@ -0,0 +1,80 @@
1# vi: ts=4 expandtab
2#
3# Author: Avishai Ish-Shalom <avishai@fewbytes.com>
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 3, as
7# published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16import os
17import pwd
18import socket
19import subprocess
20import StringIO
21import ConfigParser
22import cloudinit.CloudConfig as cc
23import cloudinit.util as util
24
25ruby_packages = {'1.8': ('ruby', 'rubygems', 'ruby-dev', 'libopenssl-ruby'),
26 '1.9.1': ('ruby1.9.1', 'ruby1.9.1-dev', 'libruby1.9.1'),
27 '1.9': ('ruby1.9', 'ruby1.9-dev', 'libruby1.9') }
28
29def handle(name,cfg,cloud,log,args):
30 # If there isn't a chef key in the configuration don't do anything
31 if not cfg.has_key('chef'): return
32 chef_cfg = cfg['chef']
33
34 # Install chef packages from selected source
35 if not os.path.isfile('/usr/bin/chef-client'):
36 if chef_cfg['install_type'] == "gems":
37 if chef_cfg.has_key('version'):
38 chef_version = chef_cfg['version']
39 else:
40 chef_version = None
41 install_chef_from_gems(
42 util.get_cfg_option_str(chef_cfg, 'ruby_version', '1.8'),
43 chef_version)
44 else:
45 cc.install_packages(('chef',))
46
47 # set the validation cert
48 if chef_cfg.has_key('validation_cert'):
49 with open('/etc/chef/validation.cert', 'w') as validation_cert_fh:
50 validation_cert_fh.write(chef_cfg['validation_cert'])
51
52 # create the chef config from template
53 util.render_to_file('chef_client.rb', '/etc/chef/client.rb',
54 {'server_url': chef_cfg['server_url'], 'validation_name': chef_cfg['validation_name'] || 'chef-validator'})
55
56 chef_args = ['-d']
57 # set the firstboot json
58 if chef_cfg.has_key('run_list'):
59 with open('/etc/chef/firstboot.json') as firstboot_json_fh:
60 firstboot_json_fh.write("{\n\"run_list\":\n[\n")
61 for runlist_item in chef_cfg['run_list']:
62 firstboot_json_fh.write(runlist_item + "\n")
63 firstboot_json_fh.write("]\n\}")
64 chef_args.append('-j /etc/chef/firstboot.json')
65
66 # and finally, run chef
67 subprocess.check_call(['/usr/bin/chef-client'] + chef_args)
68
69def install_chef_from_gems(ruby_version, chef_version = None):
70 cc.install_packages(ruby_packages[ruby_version])
71 chef_version_arg = ""
72 if chef_version: chef_version_arg = "-v %s" % chef_version
73 subprocess.check_call([gem_bin,'install','chef',chef_version_arg, '--no-ri','--no-rdoc','--no-test','-q'])
74 os.mkdirs('/etc/chef', '/var/log/chef', '/var/lib/chef', '/var/cache/chef', '/var/backups/chef', '/var/run/chef')
75 os.symlink('/var/lib/gem/%s/bin/chef-client' % ruby_version, '/usr/bin/chef-client')
76 # Ohai ruby plugin breaks if there is no ruby or gem binaries at /usr/bin, so
77 try: os.symlink('/usr/bin/gem%s' % ruby_version, '/usr/bin/gem')
78 except: pass
79 try: os.symlink('/usr/bin/ruby%s' % ruby_version, '/usr/bin/ruby')
80 except: pass
081
=== modified file 'cloudinit/CloudConfig/cc_set_hostname.py'
--- cloudinit/CloudConfig/cc_set_hostname.py 2011-01-26 14:03:46 +0000
+++ cloudinit/CloudConfig/cc_set_hostname.py 2011-06-30 22:52:32 +0000
@@ -24,7 +24,12 @@
24 return(True)24 return(True)
2525
26 try:26 try:
27 hostname = util.get_cfg_option_str(cfg,"hostname",cloud.get_hostname())27 hostname_prefix = util.get_cfg_option_str(cfg, "hostname_prefix", None)
28 hostname_attr = util.get_cfg_option_str(cfg, "hostname_attribute", "hostname")
29 hostname_function = getattr(cloud, 'get_' + hostname_attr, None)
30 if hostname_fucntion is None: hostname_fucntion = cloud.get_hostname
31 hostname = util.get_cfg_option_str(cfg,"hostname", hostname_function)
32 if hostname_prefix: hostname = hostname_prefix + "-" + hostname
28 set_hostname(hostname, log)33 set_hostname(hostname, log)
29 except Exception as e:34 except Exception as e:
30 util.logexc(log)35 util.logexc(log)
3136
=== added file 'doc/examples/cloud-config-chef.txt'
--- doc/examples/cloud-config-chef.txt 1970-01-01 00:00:00 +0000
+++ doc/examples/cloud-config-chef.txt 2011-06-30 22:52:32 +0000
@@ -0,0 +1,38 @@
1#cloud-config
2#
3# This is an example file to automatically setup and run puppetd
4# when the instance boots for the first time.
5# Make sure that this file is valid yaml before starting instances.
6# It should be passed as user-data when starting the instance.
7
8# The default is to install from packages. If you want the latest packages from Opscode, be sure to add their repo:
9apt_mirror: http://apt.opscode.com/
10
11chef:
12 # If you want to install from rubygems:
13 install_type: "gems"
14
15 # Chef settings
16 server_url: "https://chef.yourorg.com:4000"
17
18 # Default validation name is chef-validator
19 validation_name: "yourorg-validator"
20 validation_cert: |
21 -----BEGIN CERTIFICATE-----
22 MIICCTCCAXKgAwIBAgIBATANBgkqhkiG9w0BAQUFADANMQswCQYDVQQDDAJjYTAe
23 Fw0xMDAyMTUxNzI5MjFaFw0xNTAyMTQxNzI5MjFaMA0xCzAJBgNVBAMMAmNhMIGf
24 MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCu7Q40sm47/E1Pf+r8AYb/V/FWGPgc
25 b014OmNoX7dgCxTDvps/h8Vw555PdAFsW5+QhsGr31IJNI3kSYprFQcYf7A8tNWu
26 1MASW2CfaEiOEi9F1R3R4Qlz4ix+iNoHiUDTjazw/tZwEdxaQXQVLwgTGRwVa+aA
27 qbutJKi93MILLwIDAQABo3kwdzA4BglghkgBhvhCAQ0EKxYpUHVwcGV0IFJ1Ynkv
28 T3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwDwYDVR0TAQH/BAUwAwEB/zAd
29 BgNVHQ4EFgQUu4+jHB+GYE5Vxo+ol1OAhevspjAwCwYDVR0PBAQDAgEGMA0GCSqG
30 SIb3DQEBBQUAA4GBAH/rxlUIjwNb3n7TXJcDJ6MMHUlwjr03BDJXKb34Ulndkpaf
31 +GAlzPXWa7bO908M9I8RnPfvtKnteLbvgTK+h+zX1XCty+S2EQWk29i2AdoqOTxb
32 hppiGMp0tT5Havu4aceCXiy2crVcudj3NFciy8X66SoECemW9UYDCb9T5D0d
33 -----END CERTIFICATE-----
34
35 # A run list for a first boot json
36 run_list:
37 - "recipe[apache2]"
38 - "role[db]"
039
=== added file 'templates/chef_client.rb.tmpl'
--- templates/chef_client.rb.tmpl 1970-01-01 00:00:00 +0000
+++ templates/chef_client.rb.tmpl 2011-06-30 22:52:32 +0000
@@ -0,0 +1,12 @@
1log_level :info
2log_location "/var/log/chef/client.log"
3ssl_verify_mode :verify_none
4validation_client_name "$validation_name"
5validation_key "/etc/chef/validation.pem"
6client_key "/etc/chef/client.pem"
7chef_server_url "$server_url"
8file_cache_path "/var/cache/chef"
9file_backup_path "/var/backups/chef"
10pid_file "/var/run/chef/client.pid"
11Chef::Log::Formatter.show_time = true
12
013
=== added file 'tools/write-mime-multipart.py'
--- tools/write-mime-multipart.py 1970-01-01 00:00:00 +0000
+++ tools/write-mime-multipart.py 2011-06-30 22:52:32 +0000
@@ -0,0 +1,51 @@
1#! /usr/bin/env python
2
3import sys, os
4import email
5import mimetypes
6import re
7
8mimetypes.types_map['.sh'] = 'text/x-shellscript'
9cloud_config_mark_strings = { '#!': 'text/x-shellscript', '#include': 'text/x-include-url',
10 '#cloud-config': 'text/cloud-config', '#upstart-job': 'text/upstart-job',
11 '#cloud-boothook': 'text/cloud-boothook'
12 }
13def write_mime_multipart():
14 multipart_msg = email.mime.Multipart.MIMEMultipart()
15 for arg in sys.argv[1:]:
16 if ',' in arg:
17 (msg_file, msg_type) = arg.split(',')
18 else:
19 msg_file = arg
20 msg_type = None
21
22 msg_file = os.path.expanduser(msg_file)
23 if not os.path.isfile(msg_file):
24 print >> sys.stderr, "Can't find file %s" % arg
25 exit(1)
26
27 if not msg_type: msg_type = get_type_from_file(arg)
28 msg = email.mime.base.MIMEBase(*msg_type.split('/'))
29 msg.set_payload(open(msg_file, 'r').read())
30 multipart_msg.attach(msg)
31
32 print multipart_msg.as_string()
33
34def get_type_from_file(filename):
35 first_line = open(filename).readline()
36 m = re.match('Content-Type: (\w+/\w+)', first_line)
37 if m:
38 return m.groups[1]
39 else:
40 for mark_string, mime_type in cloud_config_mark_strings.items():
41 if first_line.startswith(mark_string):
42 return mime_type
43 return mimetypes.guess_type(filename)[0] or 'text/plain'
44
45if __name__ == '__main__':
46 if len(sys.argv) == 1 or '-h' in sys.argv or '--help' in sys.argv:
47 print "Usage: %s file1,application/cloud-config file2.sh ..." % os.path.basename(sys.argv[0])
48 print "MIME Multipart message will be written to STDOUT"
49 exit(0)
50 write_mime_multipart()
51