Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add plugin machinery

Closes #72
  • Loading branch information...
commit 75c5d647cc30828b0b1aa092bea5d24e82d46504 1 parent 7bd332f
@tobami authored
View
2  littlechef/lib.py
@@ -40,7 +40,6 @@ def get_nodes(environment=None):
node = get_node(fqdn)
if environment is None or node.get('chef_environment') == environment:
# Add node name so that we can tell to which node it is
- node['name'] = fqdn
nodes.append(node)
return nodes
@@ -57,6 +56,7 @@ def get_node(name):
msg = 'LittleChef found the following error in'
msg += ' "{0}":\n {1}'.format(node_path, str(e))
abort(msg)
+ node['name'] = name
return node
View
59 littlechef/runner.py
@@ -134,7 +134,7 @@ def deploy_chef(gems="no", ask="yes", version="0.10",
distro_type=None, distro=None, stop_client='yes'):
"""Install chef-solo on a node"""
if not env.host_string:
- abort('no node specified\nUsage: fix node:MYNODE deploy_chef')
+ abort('no node specified\nUsage: fix node:MYNODES deploy_chef')
chef_versions = ["0.9", "0.10"]
if version not in chef_versions:
abort('Wrong Chef version specified. Valid versions are {0}'.format(
@@ -168,10 +168,11 @@ def recipe(recipe):
"""Apply the given recipe to a node
Sets the run_list to the given recipe
If no nodes/hostname.json file exists, it creates one
+
"""
# Check that a node has been selected
if not env.host_string:
- abort('no node specified\nUsage: fix node:MYNODE recipe:MYRECIPE')
+ abort('no node specified\nUsage: fix node:MYNODES recipe:MYRECIPE')
lib.print_header(
"Applying recipe '{0}' on node {1}".format(recipe, env.host_string))
@@ -189,7 +190,7 @@ def role(role):
"""
# Check that a node has been selected
if not env.host_string:
- abort('no node specified\nUsage: fix node:MYNODE role:MYROLE')
+ abort('no node specified\nUsage: fix node:MYNODES role:MYROLE')
lib.print_header(
"Applying role '{0}' to {1}".format(role, env.host_string))
@@ -200,30 +201,34 @@ def role(role):
chef.sync_node(data)
-@hosts('setup')
-def get_ips():
- """Ping all nodes and update their 'ipaddress' field"""
- import subprocess
- for node in lib.get_nodes():
- # For each node, ping the hostname
- env.host_string = node['name']
- proc = subprocess.Popen(['ping', '-c', '1', node['name']],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- resp, error = proc.communicate()
- if not error:
- # Split output into lines and parse the first line to get the IP
- ip = lib.parse_ip(resp.split("\n")[0])
- if not ip:
- print "Warning: could not get IP address from node {0}".format(
- node['name'])
- continue
- print "Node {0} has IP {1}".format(node['name'], ip)
- # Update with the ipaddress field in the corresponding node.json
- del node['name']
- node['ipaddress'] = ip
- os.remove(chef.save_config(node, ip))
- else:
- print "Warning: could not resolve node {0}".format(node['name'])
+def plugin(name):
+ """Executes the selected plugin
+ Plugins are expected to be found in the kitchen's 'plugins' directory
+
+ """
+ if not env.host_string:
+ abort('no node specified\nUsage: fix node:MYNODES plugin:MYPLUGIN')
+ path = os.path.join("plugins", name + ".py")
+ if not os.path.exists(path):
+ abort("Sorry, could not find '{0}.py' in the plugin directory".format(
+ name))
+ import imp
+ try:
+ with open(path, 'rb') as f:
+ plug = imp.load_module(
+ "p_" + name, f, name + '.py',
+ ('.py', 'rb', imp.PY_SOURCE)
+ )
+ except Exception as e:
+ error = "Found plugin '{0}',".format(name)
+ error += " but it seems to have a syntax error: {0}".format(str(e))
+ abort(error)
+ print("Executing plugin '{0}' on {1}".format(name, env.host_string))
+ node = lib.get_node(env.host_string)
+ if 'name' not in node:
+ node['name'] = env.host_string
+ plug.execute(node)
+ print("Finished executing plugin")
@hosts('api')
View
4 tests/plugins/bad.py
@@ -0,0 +1,4 @@
+"""Bad LittleChef plugin"""
+
+def execute():
+ I am a syntax error
View
4 tests/plugins/dummy.py
@@ -0,0 +1,4 @@
+"""Dummy LittleChef plugin"""
+
+def execute():
+ print "Worked!"
View
13 tests/test_command.py
@@ -118,7 +118,8 @@ def test_one_node(self):
resp, error = self.execute([fix, 'node:testnode2'])
self.assertTrue("== Configuring testnode2 ==" in resp)
# Will try to configure testnode2 and will fail DNS lookup
- self.assertTrue("tal error: Name lookup failed for testnode2" in error)
+ self.assertTrue("tal error: Name lookup failed for testnode2" in error,
+ error)
def test_several_nodes(self):
"""Should try to configure two nodes"""
@@ -147,6 +148,16 @@ def test_role(self):
self.assertTrue("== Applying role 'base' to testnode1 ==" in resp)
self.assertTrue("tal error: Name lookup failed for testnode1" in error)
+ def test_plugin(self):
+ """Should execute the given plugin"""
+ resp, error = self.execute([fix, 'node:testnode1', 'plugin:notthere'])
+ expected = "Sorry, could not find 'notthere.py' in the plugin directory"
+ self.assertTrue(expected in error, resp + error)
+
+ resp, error = self.execute([fix, 'node:testnode1', 'plugin:dummy'])
+ expected = "Executing plugin '{0}' on {1}".format("dummy", "testnode1")
+ self.assertTrue(expected in resp)
+
class TestCookbook(BaseTest):
def test_list_recipes(self):
Please sign in to comment.
Something went wrong with that request. Please try again.