Skip to content
This repository

Littlechef shouldn't rely on metadata as the definitive list of available recipes #81

Merged
merged 2 commits into from about 2 years ago

2 participants

David Evans Miquel Torres
David Evans

Currently, littlechef only sees recipes that are listed in a cookbook's metadata.json, but according to the OpsCode documentation this list is just used for supplying friendly descriptions for recipes and is not meant to be the definitive list of available recipes.

If you clone the official cookbooks repo you'll see that quite a few of the official cookbooks don't list all their recipes in the metadata. The below commands will print a list of cookbooks together with the number of recipes listed in their metadata and the number they actually contain:

git clone https://github.com/opscode/cookbooks.git
cd cookbooks
for cookbook in `find * -maxdepth 0 -type d`; \
  do echo -n "$cookbook: "; \
  grep -c ^recipe $cookbook/metadata.rb | tr '\n' ' \0'; \
  ls $cookbook/recipes/*.rb | wc -l; \
done
added some commits January 12, 2012
Test recipes are found even if not in metadata
The list of recipes included in the metadata is to add optional
descriptions to recipes, not to provide the definitive list.
See http://wiki.opscode.com/display/chef/Metadata#Metadata-recipe
2fb0f8e
Find all recipes, even those not listed in metadata 9598d5d
Miquel Torres
Owner

Indeed. I thought "UI aesthetics" referred only to the description of a recipe, not to the declaration itself. But it does appear under the "Optional Fields" section.

I'll have a closer look at your changes because they change quite a bit how recipes are gathered, but for sure it is better to follow Chef guidelines.

Miquel Torres
Owner

OK, your changes look good.

I also did some quick performance tests.
On a kitchen with around 50 cookbooks, fix list_recipes needed 0.010 seconds, in other words, instantaneous compared to python startup time.
With your changes, it took on average 0.013 sec, which is a totally acceptable increase.

Thanks a lot for the patch!

Miquel Torres tobami merged commit 3465f74 into from January 22, 2012
Miquel Torres tobami closed this January 22, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Jan 12, 2012
Test recipes are found even if not in metadata
The list of recipes included in the metadata is to add optional
descriptions to recipes, not to provide the definitive list.
See http://wiki.opscode.com/display/chef/Metadata#Metadata-recipe
2fb0f8e
Jan 13, 2012
Find all recipes, even those not listed in metadata 9598d5d
This page is out of date. Refresh to see the latest.
56  littlechef/lib.py
@@ -183,7 +183,7 @@ def get_recipes_in_cookbook(name):
183 183
     Returns a list of dictionaries
184 184
 
185 185
     """
186  
-    recipes = []
  186
+    recipes = {}
187 187
     path = None
188 188
     cookbook_exists = False
189 189
     metadata_exists = False
@@ -209,24 +209,18 @@ def get_recipes_in_cookbook(name):
209 209
                     abort(msg)
210 210
                 # Add each recipe defined in the cookbook
211 211
                 metadata_exists = True
  212
+                recipe_defaults = {
  213
+                    'description': '',
  214
+                    'version': cookbook.get('version'),
  215
+                    'dependencies': cookbook.get('dependencies', {}).keys(),
  216
+                    'attributes': cookbook.get('attributes', {})
  217
+                }
212 218
                 for recipe in cookbook.get('recipes', []):
213  
-                    recipes.append({
214  
-                        'name': recipe,
215  
-                        'description': cookbook['recipes'][recipe],
216  
-                        'version': cookbook.get('version'),
217  
-                        'dependencies': cookbook.get('dependencies', {}).keys(),
218  
-                        'attributes': cookbook.get('attributes', {}),
219  
-                        })
220  
-                # When a recipe has no default recipe (libraries?),
221  
-                # add one so that it is listed
222  
-                if not recipes:
223  
-                    recipes.append({
224  
-                        'name': name,
225  
-                        'description': 'This cookbook has no default recipe',
226  
-                        'version': cookbook.get('version'),
227  
-                        'dependencies': cookbook.get('dependencies', {}).keys(),
228  
-                        'attributes': cookbook.get('attributes', {})
229  
-                    })
  219
+                    recipes[recipe] = dict(
  220
+                        recipe_defaults,
  221
+                        name=recipe,
  222
+                        description=cookbook['recipes'][recipe]
  223
+                    )
230 224
             # Cookbook metadata.json was found, don't try next cookbook path
231 225
             # because metadata.json in site-cookbooks has preference
232 226
             break
@@ -237,7 +231,31 @@ def get_recipes_in_cookbook(name):
237 231
         abort('Unable to find cookbook "{0}"'.format(name))
238 232
     elif not metadata_exists:
239 233
         abort('Cookbook "{0}" has no metadata.json'.format(name))
240  
-    return recipes
  234
+    # Add recipes found in the 'recipes' directory but not listed
  235
+    # in the metadata
  236
+    for cookbook_path in cookbook_paths:
  237
+        recipes_dir = os.path.join(cookbook_path, name, 'recipes')
  238
+        if not os.path.isdir(recipes_dir):
  239
+            continue
  240
+        for basename in os.listdir(recipes_dir):
  241
+            fname, ext = os.path.splitext(basename)
  242
+            if ext != '.rb':
  243
+                continue
  244
+            if fname != 'default':
  245
+                recipe = '%s::%s' % (name, fname)
  246
+            else:
  247
+                recipe = name
  248
+            if recipe not in recipes:
  249
+                recipes[recipe] = dict(recipe_defaults, name=recipe)
  250
+    # When a recipe has no default recipe (libraries?),
  251
+    # add one so that it is listed
  252
+    if not recipes:
  253
+        recipes[name] = dict(
  254
+            recipe_defaults,
  255
+            name=name,
  256
+            description='This cookbook has no default recipe'
  257
+        )
  258
+    return recipes.values()
241 259
 
242 260
 
243 261
 def get_recipes_in_role(rolename):
3  tests/cookbooks/subversion/recipes/testrecipe.rb
... ...
@@ -0,0 +1,3 @@
  1
+# Empty recipe
  2
+# Just here so we can test whether littlechef picks up recipe files
  3
+# which aren't listed explicity in metadata.json
4  tests/test_lib.py
@@ -199,7 +199,7 @@ def test_nodes_with_recipe_in_env(self):
199 199
 
200 200
     def test_list_recipes(self):
201 201
         recipes = lib.get_recipes()
202  
-        self.assertEquals(len(recipes), 5)
  202
+        self.assertEquals(len(recipes), 6)
203 203
         self.assertEquals(recipes[1]['name'], 'subversion')
204 204
         self.assertEquals(recipes[1]['description'],
205 205
             'Includes the client recipe. Modified by site-cookbooks')
@@ -207,6 +207,8 @@ def test_list_recipes(self):
207 207
         self.assertEquals(recipes[2]['description'],
208 208
             'Subversion Client installs subversion and some extra svn libs')
209 209
         self.assertEquals(recipes[3]['name'], 'subversion::server')
  210
+        self.assertIn('subversion::testrecipe', [r['name'] for r in recipes])
  211
+        
210 212
 
211 213
 
212 214
     def test_import_plugin(self):
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.