Skip to content

Commit b74bb85

Browse files
committed
Adding docs for yaml recipes
Signed-off-by: John McCrae <[email protected]>
1 parent 0c01b9e commit b74bb85

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

content/recipes_in_yaml.md

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
# Chef YAML Recipes: Complete Guide
2+
3+
## Overview
4+
5+
Chef YAML recipes provide an alternative way to define Chef resources using YAML syntax instead of Ruby. This feature was introduced to make Chef recipes more accessible to users who are more comfortable with declarative YAML syntax than Ruby code.
6+
7+
## Basic Structure
8+
9+
YAML recipes must follow a specific structure:
10+
11+
````yaml
12+
---
13+
resources:
14+
- type: "resource_type"
15+
name: "resource_name"
16+
property1: value1
17+
property2: value2
18+
- type: "another_resource_type"
19+
name: "another_resource_name"
20+
property1: value1
21+
property2: value2
22+
````
23+
24+
## File Naming and Location
25+
26+
YAML recipes can be placed in the same locations as Ruby recipes:
27+
28+
- **Standard recipe location**: `cookbooks/mycookbook/recipes/default.yml` or `cookbooks/mycookbook/recipes/default.yaml`
29+
- **Named recipes**: `cookbooks/mycookbook/recipes/web.yml` or `cookbooks/mycookbook/recipes/database.yaml`
30+
- **Root-level recipe alias**: `cookbooks/mycookbook/recipe.yml` or `cookbooks/mycookbook/recipe.yaml` (acts as default recipe)
31+
32+
### File Extension Support
33+
34+
Both `.yml` and `.yaml` extensions are supported. However, if both `default.yml` and `default.yaml` exist in the same cookbook, Chef will raise an `AmbiguousYAMLFile` error requiring you to remove one of them.
35+
36+
## Required Structure and Restrictions
37+
38+
### 1. Top-Level Resources Hash
39+
40+
Every YAML recipe **must** contain a top-level `resources` key that contains an array of resource declarations:
41+
42+
````yaml
43+
# ✅ CORRECT
44+
---
45+
resources:
46+
- type: "file"
47+
name: "/tmp/hello.txt"
48+
content: "Hello World"
49+
50+
# ❌ INCORRECT - Missing resources key
51+
---
52+
- type: "file"
53+
name: "/tmp/hello.txt"
54+
content: "Hello World"
55+
56+
# ❌ INCORRECT - Wrong structure
57+
---
58+
files:
59+
- type: "file"
60+
name: "/tmp/hello.txt"
61+
content: "Hello World"
62+
````
63+
64+
### 2. Resource Declaration Format
65+
66+
Each resource in the array must have:
67+
68+
- **`type`**: The Chef resource type (string)
69+
- **`name`**: The resource name/identifier (string)
70+
- **Additional properties**: Resource-specific properties as key-value pairs
71+
72+
````yaml
73+
resources:
74+
- type: "package"
75+
name: "nginx"
76+
action: "install"
77+
version: "1.18.0"
78+
- type: "service"
79+
name: "nginx"
80+
action: ["enable", "start"]
81+
````
82+
83+
### 3. Single Document Limitation
84+
85+
YAML recipes support only **one YAML document** per file. Multiple documents separated by `---` are not allowed:
86+
87+
````yaml
88+
# ❌ INCORRECT - Multiple documents not supported
89+
---
90+
resources:
91+
- type: "file"
92+
name: "/tmp/file1.txt"
93+
---
94+
resources:
95+
- type: "file"
96+
name: "/tmp/file2.txt"
97+
````
98+
99+
## Major Limitations
100+
101+
### 1. No Ruby Code Blocks
102+
103+
YAML recipes cannot contain Ruby code blocks, which significantly limits their functionality compared to Ruby recipes:
104+
105+
````ruby
106+
# ❌ Cannot be expressed in YAML - Ruby blocks not supported
107+
template "/etc/nginx/nginx.conf" do
108+
source "nginx.conf.erb"
109+
variables({
110+
worker_processes: node['cpu']['total']
111+
})
112+
notifies :restart, "service[nginx]", :delayed
113+
only_if { node['platform'] == 'ubuntu' }
114+
end
115+
````
116+
117+
### 2. No Conditional Logic
118+
119+
YAML recipes cannot include conditional logic like `if`, `unless`, `only_if`, or `not_if` with Ruby expressions:
120+
121+
````yaml
122+
# ❌ Cannot include complex conditionals
123+
resources:
124+
- type: "package"
125+
name: "nginx"
126+
# Cannot do: only_if { node['platform'] == 'ubuntu' }
127+
````
128+
129+
### 3. No Node Attribute Access
130+
131+
YAML recipes cannot directly access node attributes or perform Ruby evaluations:
132+
133+
````yaml
134+
# ❌ Cannot access node attributes dynamically
135+
resources:
136+
- type: "user"
137+
name: "webapp"
138+
# Cannot do: home "/home/#{node['webapp']['user']}"
139+
home: "/home/webapp" # Must be static
140+
````
141+
142+
### 4. No Resource Notifications
143+
144+
Complex resource relationships and notifications cannot be expressed:
145+
146+
````yaml
147+
# ❌ Cannot express notifications between resources
148+
resources:
149+
- type: "template"
150+
name: "/etc/nginx/nginx.conf"
151+
source: "nginx.conf.erb"
152+
# Cannot do: notifies :restart, "service[nginx]", :delayed
153+
````
154+
155+
### 5. No Include/Require Functionality
156+
157+
YAML recipes cannot include other recipes or libraries:
158+
159+
````yaml
160+
# ❌ Cannot include other recipes
161+
# include_recipe "cookbook::other_recipe"
162+
````
163+
164+
## Examples
165+
166+
### Basic File Management
167+
168+
````yaml
169+
---
170+
resources:
171+
- type: "directory"
172+
name: "/opt/myapp"
173+
owner: "myapp"
174+
group: "myapp"
175+
mode: "0755"
176+
recursive: true
177+
178+
- type: "file"
179+
name: "/opt/myapp/config.txt"
180+
content: "This is a configuration file"
181+
owner: "myapp"
182+
group: "myapp"
183+
mode: "0644"
184+
````
185+
186+
### Package and Service Management
187+
188+
````yaml
189+
---
190+
resources:
191+
- type: "package"
192+
name: "nginx"
193+
action: "install"
194+
195+
- type: "package"
196+
name: "curl"
197+
action: "install"
198+
199+
- type: "service"
200+
name: "nginx"
201+
action: ["enable", "start"]
202+
````
203+
204+
### User Management
205+
206+
````yaml
207+
---
208+
resources:
209+
- type: "group"
210+
name: "developers"
211+
gid: 3000
212+
213+
- type: "user"
214+
name: "alice"
215+
uid: 2001
216+
gid: 3000
217+
home: "/home/alice"
218+
shell: "/bin/bash"
219+
action: "create"
220+
````
221+
222+
### Template with Static Variables
223+
224+
````yaml
225+
---
226+
resources:
227+
- type: "template"
228+
name: "/etc/myapp/config.yml"
229+
source: "config.yml.erb"
230+
owner: "root"
231+
group: "root"
232+
mode: "0644"
233+
# Note: Variables must be static, cannot use node attributes
234+
````
235+
236+
## Working with YAML Recipes
237+
238+
### Converting Between Ruby and YAML
239+
240+
Chef provides a `knife yaml convert` command to convert YAML recipes to Ruby:
241+
242+
```powershell
243+
knife yaml convert recipes/default.yml recipes/default.rb
244+
```
245+
246+
**Note**: Converting from Ruby to YAML is not supported due to the limitations of YAML format.
247+
248+
### File Processing
249+
250+
YAML recipes are processed by the `from_yaml_file` method in the `Chef::Recipe` class, which:
251+
252+
1. Validates the file exists and is readable
253+
2. Checks for single document requirement
254+
3. Parses YAML using safe loading
255+
4. Validates the required `resources` structure
256+
5. Converts to internal hash representation
257+
6. Creates Chef resources using `declare_resource`
258+
259+
## Best Practices
260+
261+
### When to Use YAML Recipes
262+
263+
YAML recipes are best suited for:
264+
265+
- **Simple, static configurations**
266+
- **Declarative resource management**
267+
- **Teams preferring YAML over Ruby**
268+
- **Basic infrastructure as code**
269+
270+
### When NOT to Use YAML Recipes
271+
272+
Avoid YAML recipes when you need:
273+
274+
- **Dynamic node attribute access**
275+
- **Conditional logic**
276+
- **Resource notifications**
277+
- **Complex data transformations**
278+
- **Integration with Ruby libraries**
279+
- **Advanced Chef DSL features**
280+
281+
### Migration Strategy
282+
283+
1. Start with simple, static resources in YAML
284+
2. Use Ruby recipes for complex logic
285+
3. Consider hybrid approach: YAML for simple resources, Ruby for complex ones
286+
4. Use `knife yaml convert` to understand Ruby equivalents
287+
288+
## Error Handling
289+
290+
Common errors when working with YAML recipes:
291+
292+
### Missing Resources Key
293+
294+
```text
295+
ArgumentError: YAML recipe 'recipes/default.yml' must contain a top-level 'resources' hash (YAML sequence), i.e. 'resources:'
296+
```
297+
298+
### Multiple Documents
299+
300+
```text
301+
ArgumentError: YAML recipe 'recipes/default.yml' contains multiple documents, only one is supported
302+
```
303+
304+
### Ambiguous File Extensions
305+
306+
```text
307+
Chef::Exceptions::AmbiguousYAMLFile: Found both default.yml and default.yaml in cookbook, update the cookbook to remove one
308+
```
309+
310+
## Conclusion
311+
312+
YAML recipes provide a simplified way to define Chef resources for basic use cases. While they have significant limitations compared to Ruby recipes, they can be valuable for teams that prefer declarative YAML syntax and don't require advanced Chef DSL features. For complex scenarios involving dynamic logic, node attributes, or resource relationships, Ruby recipes remain the preferred approach.
313+
314+
For most production environments, a hybrid approach using both YAML recipes for simple static configurations and Ruby recipes for complex logic provides the best balance of simplicity and functionality.

0 commit comments

Comments
 (0)