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
1=== added file 'cloudinit/CloudConfig/cc_chef.py'
2--- cloudinit/CloudConfig/cc_chef.py 1970-01-01 00:00:00 +0000
3+++ cloudinit/CloudConfig/cc_chef.py 2011-06-30 22:52:32 +0000
4@@ -0,0 +1,80 @@
5+# vi: ts=4 expandtab
6+#
7+# Author: Avishai Ish-Shalom <avishai@fewbytes.com>
8+#
9+# This program is free software: you can redistribute it and/or modify
10+# it under the terms of the GNU General Public License version 3, as
11+# published by the Free Software Foundation.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# You should have received a copy of the GNU General Public License
19+# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+import os
21+import pwd
22+import socket
23+import subprocess
24+import StringIO
25+import ConfigParser
26+import cloudinit.CloudConfig as cc
27+import cloudinit.util as util
28+
29+ruby_packages = {'1.8': ('ruby', 'rubygems', 'ruby-dev', 'libopenssl-ruby'),
30+ '1.9.1': ('ruby1.9.1', 'ruby1.9.1-dev', 'libruby1.9.1'),
31+ '1.9': ('ruby1.9', 'ruby1.9-dev', 'libruby1.9') }
32+
33+def handle(name,cfg,cloud,log,args):
34+ # If there isn't a chef key in the configuration don't do anything
35+ if not cfg.has_key('chef'): return
36+ chef_cfg = cfg['chef']
37+
38+ # Install chef packages from selected source
39+ if not os.path.isfile('/usr/bin/chef-client'):
40+ if chef_cfg['install_type'] == "gems":
41+ if chef_cfg.has_key('version'):
42+ chef_version = chef_cfg['version']
43+ else:
44+ chef_version = None
45+ install_chef_from_gems(
46+ util.get_cfg_option_str(chef_cfg, 'ruby_version', '1.8'),
47+ chef_version)
48+ else:
49+ cc.install_packages(('chef',))
50+
51+ # set the validation cert
52+ if chef_cfg.has_key('validation_cert'):
53+ with open('/etc/chef/validation.cert', 'w') as validation_cert_fh:
54+ validation_cert_fh.write(chef_cfg['validation_cert'])
55+
56+ # create the chef config from template
57+ util.render_to_file('chef_client.rb', '/etc/chef/client.rb',
58+ {'server_url': chef_cfg['server_url'], 'validation_name': chef_cfg['validation_name'] || 'chef-validator'})
59+
60+ chef_args = ['-d']
61+ # set the firstboot json
62+ if chef_cfg.has_key('run_list'):
63+ with open('/etc/chef/firstboot.json') as firstboot_json_fh:
64+ firstboot_json_fh.write("{\n\"run_list\":\n[\n")
65+ for runlist_item in chef_cfg['run_list']:
66+ firstboot_json_fh.write(runlist_item + "\n")
67+ firstboot_json_fh.write("]\n\}")
68+ chef_args.append('-j /etc/chef/firstboot.json')
69+
70+ # and finally, run chef
71+ subprocess.check_call(['/usr/bin/chef-client'] + chef_args)
72+
73+def install_chef_from_gems(ruby_version, chef_version = None):
74+ cc.install_packages(ruby_packages[ruby_version])
75+ chef_version_arg = ""
76+ if chef_version: chef_version_arg = "-v %s" % chef_version
77+ subprocess.check_call([gem_bin,'install','chef',chef_version_arg, '--no-ri','--no-rdoc','--no-test','-q'])
78+ os.mkdirs('/etc/chef', '/var/log/chef', '/var/lib/chef', '/var/cache/chef', '/var/backups/chef', '/var/run/chef')
79+ os.symlink('/var/lib/gem/%s/bin/chef-client' % ruby_version, '/usr/bin/chef-client')
80+ # Ohai ruby plugin breaks if there is no ruby or gem binaries at /usr/bin, so
81+ try: os.symlink('/usr/bin/gem%s' % ruby_version, '/usr/bin/gem')
82+ except: pass
83+ try: os.symlink('/usr/bin/ruby%s' % ruby_version, '/usr/bin/ruby')
84+ except: pass
85
86=== modified file 'cloudinit/CloudConfig/cc_set_hostname.py'
87--- cloudinit/CloudConfig/cc_set_hostname.py 2011-01-26 14:03:46 +0000
88+++ cloudinit/CloudConfig/cc_set_hostname.py 2011-06-30 22:52:32 +0000
89@@ -24,7 +24,12 @@
90 return(True)
91
92 try:
93- hostname = util.get_cfg_option_str(cfg,"hostname",cloud.get_hostname())
94+ hostname_prefix = util.get_cfg_option_str(cfg, "hostname_prefix", None)
95+ hostname_attr = util.get_cfg_option_str(cfg, "hostname_attribute", "hostname")
96+ hostname_function = getattr(cloud, 'get_' + hostname_attr, None)
97+ if hostname_fucntion is None: hostname_fucntion = cloud.get_hostname
98+ hostname = util.get_cfg_option_str(cfg,"hostname", hostname_function)
99+ if hostname_prefix: hostname = hostname_prefix + "-" + hostname
100 set_hostname(hostname, log)
101 except Exception as e:
102 util.logexc(log)
103
104=== added file 'doc/examples/cloud-config-chef.txt'
105--- doc/examples/cloud-config-chef.txt 1970-01-01 00:00:00 +0000
106+++ doc/examples/cloud-config-chef.txt 2011-06-30 22:52:32 +0000
107@@ -0,0 +1,38 @@
108+#cloud-config
109+#
110+# This is an example file to automatically setup and run puppetd
111+# when the instance boots for the first time.
112+# Make sure that this file is valid yaml before starting instances.
113+# It should be passed as user-data when starting the instance.
114+
115+# The default is to install from packages. If you want the latest packages from Opscode, be sure to add their repo:
116+apt_mirror: http://apt.opscode.com/
117+
118+chef:
119+ # If you want to install from rubygems:
120+ install_type: "gems"
121+
122+ # Chef settings
123+ server_url: "https://chef.yourorg.com:4000"
124+
125+ # Default validation name is chef-validator
126+ validation_name: "yourorg-validator"
127+ validation_cert: |
128+ -----BEGIN CERTIFICATE-----
129+ MIICCTCCAXKgAwIBAgIBATANBgkqhkiG9w0BAQUFADANMQswCQYDVQQDDAJjYTAe
130+ Fw0xMDAyMTUxNzI5MjFaFw0xNTAyMTQxNzI5MjFaMA0xCzAJBgNVBAMMAmNhMIGf
131+ MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCu7Q40sm47/E1Pf+r8AYb/V/FWGPgc
132+ b014OmNoX7dgCxTDvps/h8Vw555PdAFsW5+QhsGr31IJNI3kSYprFQcYf7A8tNWu
133+ 1MASW2CfaEiOEi9F1R3R4Qlz4ix+iNoHiUDTjazw/tZwEdxaQXQVLwgTGRwVa+aA
134+ qbutJKi93MILLwIDAQABo3kwdzA4BglghkgBhvhCAQ0EKxYpUHVwcGV0IFJ1Ynkv
135+ T3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwDwYDVR0TAQH/BAUwAwEB/zAd
136+ BgNVHQ4EFgQUu4+jHB+GYE5Vxo+ol1OAhevspjAwCwYDVR0PBAQDAgEGMA0GCSqG
137+ SIb3DQEBBQUAA4GBAH/rxlUIjwNb3n7TXJcDJ6MMHUlwjr03BDJXKb34Ulndkpaf
138+ +GAlzPXWa7bO908M9I8RnPfvtKnteLbvgTK+h+zX1XCty+S2EQWk29i2AdoqOTxb
139+ hppiGMp0tT5Havu4aceCXiy2crVcudj3NFciy8X66SoECemW9UYDCb9T5D0d
140+ -----END CERTIFICATE-----
141+
142+ # A run list for a first boot json
143+ run_list:
144+ - "recipe[apache2]"
145+ - "role[db]"
146
147=== added file 'templates/chef_client.rb.tmpl'
148--- templates/chef_client.rb.tmpl 1970-01-01 00:00:00 +0000
149+++ templates/chef_client.rb.tmpl 2011-06-30 22:52:32 +0000
150@@ -0,0 +1,12 @@
151+log_level :info
152+log_location "/var/log/chef/client.log"
153+ssl_verify_mode :verify_none
154+validation_client_name "$validation_name"
155+validation_key "/etc/chef/validation.pem"
156+client_key "/etc/chef/client.pem"
157+chef_server_url "$server_url"
158+file_cache_path "/var/cache/chef"
159+file_backup_path "/var/backups/chef"
160+pid_file "/var/run/chef/client.pid"
161+Chef::Log::Formatter.show_time = true
162+
163
164=== added file 'tools/write-mime-multipart.py'
165--- tools/write-mime-multipart.py 1970-01-01 00:00:00 +0000
166+++ tools/write-mime-multipart.py 2011-06-30 22:52:32 +0000
167@@ -0,0 +1,51 @@
168+#! /usr/bin/env python
169+
170+import sys, os
171+import email
172+import mimetypes
173+import re
174+
175+mimetypes.types_map['.sh'] = 'text/x-shellscript'
176+cloud_config_mark_strings = { '#!': 'text/x-shellscript', '#include': 'text/x-include-url',
177+ '#cloud-config': 'text/cloud-config', '#upstart-job': 'text/upstart-job',
178+ '#cloud-boothook': 'text/cloud-boothook'
179+ }
180+def write_mime_multipart():
181+ multipart_msg = email.mime.Multipart.MIMEMultipart()
182+ for arg in sys.argv[1:]:
183+ if ',' in arg:
184+ (msg_file, msg_type) = arg.split(',')
185+ else:
186+ msg_file = arg
187+ msg_type = None
188+
189+ msg_file = os.path.expanduser(msg_file)
190+ if not os.path.isfile(msg_file):
191+ print >> sys.stderr, "Can't find file %s" % arg
192+ exit(1)
193+
194+ if not msg_type: msg_type = get_type_from_file(arg)
195+ msg = email.mime.base.MIMEBase(*msg_type.split('/'))
196+ msg.set_payload(open(msg_file, 'r').read())
197+ multipart_msg.attach(msg)
198+
199+ print multipart_msg.as_string()
200+
201+def get_type_from_file(filename):
202+ first_line = open(filename).readline()
203+ m = re.match('Content-Type: (\w+/\w+)', first_line)
204+ if m:
205+ return m.groups[1]
206+ else:
207+ for mark_string, mime_type in cloud_config_mark_strings.items():
208+ if first_line.startswith(mark_string):
209+ return mime_type
210+ return mimetypes.guess_type(filename)[0] or 'text/plain'
211+
212+if __name__ == '__main__':
213+ if len(sys.argv) == 1 or '-h' in sys.argv or '--help' in sys.argv:
214+ print "Usage: %s file1,application/cloud-config file2.sh ..." % os.path.basename(sys.argv[0])
215+ print "MIME Multipart message will be written to STDOUT"
216+ exit(0)
217+ write_mime_multipart()
218+