chef-gen-flavor-base 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/History.md +6 -0
- data/LICENSE +17 -0
- data/Manifest.txt +50 -0
- data/README.md +818 -0
- data/chef-gen-flavor-base.gemspec +84 -0
- data/lib/chef_gen/flavor_base.rb +185 -0
- data/lib/chef_gen/flavor_base/copy_helpers.rb +45 -0
- data/lib/chef_gen/flavor_base/resource_helpers.rb +110 -0
- data/lib/chef_gen/snippet/actions_taken.rb +82 -0
- data/lib/chef_gen/snippet/attributes.rb +35 -0
- data/lib/chef_gen/snippet/chef_spec.rb +121 -0
- data/lib/chef_gen/snippet/cookbook_base.rb +135 -0
- data/lib/chef_gen/snippet/debugging.rb +26 -0
- data/lib/chef_gen/snippet/example_file.rb +36 -0
- data/lib/chef_gen/snippet/example_template.rb +36 -0
- data/lib/chef_gen/snippet/git_init.rb +36 -0
- data/lib/chef_gen/snippet/guard.rb +70 -0
- data/lib/chef_gen/snippet/next_steps.rb +44 -0
- data/lib/chef_gen/snippet/no_clobber.rb +62 -0
- data/lib/chef_gen/snippet/recipes.rb +35 -0
- data/lib/chef_gen/snippet/resource_provider.rb +37 -0
- data/lib/chef_gen/snippet/standard_ignore.rb +85 -0
- data/lib/chef_gen/snippet/style_foodcritic.rb +73 -0
- data/lib/chef_gen/snippet/style_rubocop.rb +115 -0
- data/lib/chef_gen/snippet/style_tailor.rb +64 -0
- data/lib/chef_gen/snippet/test_kitchen.rb +129 -0
- data/lib/chef_gen/snippet_base.rb +46 -0
- data/lib/chef_gen/snippets.rb +24 -0
- data/shared/snippet/attributes/templates/default/attributes_default_rb.erb +8 -0
- data/shared/snippet/chef_spec/templates/default/_rspec.erb +2 -0
- data/shared/snippet/chef_spec/templates/default/spec_chef_runner_context_rb.erb +36 -0
- data/shared/snippet/chef_spec/templates/default/spec_recipes_default_spec_rb.erb +7 -0
- data/shared/snippet/chef_spec/templates/default/spec_spec_helper_rb.erb +95 -0
- data/shared/snippet/cookbook_base/templates/default/Berksfile.erb +12 -0
- data/shared/snippet/cookbook_base/templates/default/CHANGELOG_md.erb +5 -0
- data/shared/snippet/cookbook_base/templates/default/Gemfile.erb +18 -0
- data/shared/snippet/cookbook_base/templates/default/README_md.erb +92 -0
- data/shared/snippet/cookbook_base/templates/default/Rakefile.erb +20 -0
- data/shared/snippet/cookbook_base/templates/default/metadata_rb.erb +13 -0
- data/shared/snippet/example_file/files/default/files_default_example_conf +34 -0
- data/shared/snippet/example_template/files/default/templates_default_example_conf_erb +22 -0
- data/shared/snippet/guard/templates/default/Guardfile.erb +16 -0
- data/shared/snippet/recipes/templates/default/recipes_default_rb.erb +9 -0
- data/shared/snippet/resource_provider/templates/default/providers_default_rb.erb +20 -0
- data/shared/snippet/resource_provider/templates/default/resources_default_rb.erb +9 -0
- data/shared/snippet/style_rubocop/templates/default/_rubocop_yml.erb +31 -0
- data/shared/snippet/test_kitchen/libraries/kitchen_helper.rb +13 -0
- data/shared/snippet/test_kitchen/templates/default/_kitchen_yml.erb +30 -0
- data/shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_recipes_default_spec_rb.erb +16 -0
- data/shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_spec_helper_rb.erb +7 -0
- metadata +372 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8de1c97585788c79d1613949960f551e08333be8
|
4
|
+
data.tar.gz: b6192add19f7a575a67f9a4304a5247b82eae21b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f816f07eef02675086c44b518f784a5df10ef4a6e60cc92acc9fd0e013ff312af5d3fee8d46be1b622eb4f30403be6d8df27e8dd8cbc54d10f297bab8d8e0ee9
|
7
|
+
data.tar.gz: d5a5b0dee4f4bc59af10859e3fc37772e44a62a7e987f5fac527cd09fe476b327bd50bdcad8feb5f1774dccb8c698df2b31b900e0f1bc9db04e52d62cb465ac1
|
data/History.md
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
# Changelog for chef-gen-flavor-base
|
2
|
+
|
3
|
+
## 0.9.0
|
4
|
+
|
5
|
+
* extracted ChefGen::FlavorBase and the snippets in the namespace ChefGen::Snippets into a seperate gem
|
6
|
+
* change the mechanism for composing snippets into a flavor from Ruby module inclusion to a declarative method. This allows flavor authors to control the order that snippets are applied. Previously, snippet order was whatever random based on the return of `Object#public_methods`
|
data/LICENSE
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# THE APACHE LICENSE, v2
|
2
|
+
|
3
|
+
Copyright 2015 Nordstrom, Inc.
|
4
|
+
|
5
|
+
Copyright 2015 James FitzGibbon
|
6
|
+
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
8
|
+
use this file except in compliance with the License. You may obtain a copy
|
9
|
+
of the License at
|
10
|
+
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
15
|
+
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
16
|
+
License for the specific language governing permissions and limitations
|
17
|
+
under the License.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
History.md
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.md
|
5
|
+
chef-gen-flavor-base.gemspec
|
6
|
+
lib/chef_gen/flavor_base.rb
|
7
|
+
lib/chef_gen/flavor_base/copy_helpers.rb
|
8
|
+
lib/chef_gen/flavor_base/resource_helpers.rb
|
9
|
+
lib/chef_gen/snippet/actions_taken.rb
|
10
|
+
lib/chef_gen/snippet/attributes.rb
|
11
|
+
lib/chef_gen/snippet/chef_spec.rb
|
12
|
+
lib/chef_gen/snippet/cookbook_base.rb
|
13
|
+
lib/chef_gen/snippet/debugging.rb
|
14
|
+
lib/chef_gen/snippet/example_file.rb
|
15
|
+
lib/chef_gen/snippet/example_template.rb
|
16
|
+
lib/chef_gen/snippet/git_init.rb
|
17
|
+
lib/chef_gen/snippet/guard.rb
|
18
|
+
lib/chef_gen/snippet/next_steps.rb
|
19
|
+
lib/chef_gen/snippet/no_clobber.rb
|
20
|
+
lib/chef_gen/snippet/recipes.rb
|
21
|
+
lib/chef_gen/snippet/resource_provider.rb
|
22
|
+
lib/chef_gen/snippet/standard_ignore.rb
|
23
|
+
lib/chef_gen/snippet/style_foodcritic.rb
|
24
|
+
lib/chef_gen/snippet/style_rubocop.rb
|
25
|
+
lib/chef_gen/snippet/style_tailor.rb
|
26
|
+
lib/chef_gen/snippet/test_kitchen.rb
|
27
|
+
lib/chef_gen/snippet_base.rb
|
28
|
+
lib/chef_gen/snippets.rb
|
29
|
+
shared/snippet/attributes/templates/default/attributes_default_rb.erb
|
30
|
+
shared/snippet/chef_spec/templates/default/_rspec.erb
|
31
|
+
shared/snippet/chef_spec/templates/default/spec_chef_runner_context_rb.erb
|
32
|
+
shared/snippet/chef_spec/templates/default/spec_recipes_default_spec_rb.erb
|
33
|
+
shared/snippet/chef_spec/templates/default/spec_spec_helper_rb.erb
|
34
|
+
shared/snippet/cookbook_base/templates/default/Berksfile.erb
|
35
|
+
shared/snippet/cookbook_base/templates/default/CHANGELOG_md.erb
|
36
|
+
shared/snippet/cookbook_base/templates/default/Gemfile.erb
|
37
|
+
shared/snippet/cookbook_base/templates/default/README_md.erb
|
38
|
+
shared/snippet/cookbook_base/templates/default/Rakefile.erb
|
39
|
+
shared/snippet/cookbook_base/templates/default/metadata_rb.erb
|
40
|
+
shared/snippet/example_file/files/default/files_default_example_conf
|
41
|
+
shared/snippet/example_template/files/default/templates_default_example_conf_erb
|
42
|
+
shared/snippet/guard/templates/default/Guardfile.erb
|
43
|
+
shared/snippet/recipes/templates/default/recipes_default_rb.erb
|
44
|
+
shared/snippet/resource_provider/templates/default/providers_default_rb.erb
|
45
|
+
shared/snippet/resource_provider/templates/default/resources_default_rb.erb
|
46
|
+
shared/snippet/style_rubocop/templates/default/_rubocop_yml.erb
|
47
|
+
shared/snippet/test_kitchen/libraries/kitchen_helper.rb
|
48
|
+
shared/snippet/test_kitchen/templates/default/_kitchen_yml.erb
|
49
|
+
shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_recipes_default_spec_rb.erb
|
50
|
+
shared/snippet/test_kitchen/templates/default/test_integration_default_serverspec_spec_helper_rb.erb
|
data/README.md
ADDED
@@ -0,0 +1,818 @@
|
|
1
|
+
# chef-gen-flavor-base
|
2
|
+
|
3
|
+
* home :: https://github.com/jf647/chef-gen-flavor-base
|
4
|
+
* license :: [Apache2](http://www.apache.org/licenses/LICENSE-2.0)
|
5
|
+
* gem version :: [![Gem Version](https://badge.fury.io/rb/chef-gen-flavor-base.png)](http://badge.fury.io/rb/chef-gen-flavor-base)
|
6
|
+
* build status :: [![Circle CI](https://circleci.com/gh/jf647/chef-gen-flavor-base.svg?style=svg)](https://circleci.com/gh/jf647/chef-gen-flavor-base)
|
7
|
+
* code climate :: [![Code Climate](https://codeclimate.com/github/jf647/chef-gen-flavor-base/badges/gpa.svg)](https://codeclimate.com/github/jf647/chef-gen-flavor-base)
|
8
|
+
* docs :: [![Inline docs](http://inch-ci.org/github/jf647/chef-gen-flavor-base.svg?branch=master)](http://inch-ci.org/github/jf647/chef-gen-flavor-base)
|
9
|
+
|
10
|
+
## DESCRIPTION
|
11
|
+
|
12
|
+
chef-gen-flavor-base is a base class to make it easy to create 'flavors'
|
13
|
+
for use with
|
14
|
+
[chef-gen-flavors](https://github.com/jf647/chef-gen-flavors).
|
15
|
+
|
16
|
+
chef-gen-flavors plugs into the 'chef generate' command provided by
|
17
|
+
ChefDK to let you provide an alternate template for cookbooks and other
|
18
|
+
chef components.
|
19
|
+
|
20
|
+
This gem simply provides a class your flavor can derive from; templates
|
21
|
+
are provided by separate gems, which you can host privately for use
|
22
|
+
within your organization or publicly for the Chef community to use.
|
23
|
+
|
24
|
+
An example flavor that demonstrates how to use this gem is distributed
|
25
|
+
separately:
|
26
|
+
[chef-gen-flavor-example](https://github.com/jf647/chef-gen-flavor-example)
|
27
|
+
|
28
|
+
At present this is focused primarily on providing templates for
|
29
|
+
generation of cookbooks, as this is where most organization-specific
|
30
|
+
customization takes place. Support for the other artifacts that ChefDK
|
31
|
+
can generate may work, but is not the focus of early releases.
|
32
|
+
|
33
|
+
## INSTALLATION
|
34
|
+
|
35
|
+
You should not install this gem directly; it will be installed as a
|
36
|
+
dependency of a flavor gem. If you are developing a flavor, declare
|
37
|
+
this gem a dependency of your gem in your gemspec file:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
Gem::Specification.new do |s|
|
41
|
+
s.name = 'chef-gen-flavor-awesome'
|
42
|
+
s.add_runtime_dependency('chef-gen-flavor-base', ['~> 0.9'])
|
43
|
+
...
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
Then make bundler read your gemspec:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
source 'https://rubygems.org/'
|
51
|
+
gemspec
|
52
|
+
```
|
53
|
+
|
54
|
+
And use bundler to install your dependencies:
|
55
|
+
|
56
|
+
```bash
|
57
|
+
$ bundle
|
58
|
+
```
|
59
|
+
|
60
|
+
## ANATOMY OF A FLAVOR
|
61
|
+
|
62
|
+
First, some terminology:
|
63
|
+
|
64
|
+
* a `flavor` is a class that chef-gen-flavors can use to create a directory suitable for passing as the value of `chefdk.generator_cookbook`.
|
65
|
+
* `setup mode` is the life of a flavor object constructed by chef-gen-flavors
|
66
|
+
* `generate mode` is the life of a flavor object constructed by ChefDK
|
67
|
+
* a `snippet` is a class that augments a flavor by defining hooks into the setup and generate workflow. Snippets tend to have a specific purpose, like adding support for Test Kitchen or Rubocop to a cookbook template.
|
68
|
+
* setup hooks are called during setup mode to add files (typically templates) to a temporary directory set up by chef-gen-flavors
|
69
|
+
* resource hooks are called during generate mode to declare chef resources, typically of type `directory`, `cookbook_file` and `template`.
|
70
|
+
|
71
|
+
Flavors and Snippets are typically distributed as Rubygems. Because
|
72
|
+
snippets are useful to more than one flavor, they are usually
|
73
|
+
distributed separately. This gem contains many generic snippets that
|
74
|
+
you can use to start creating a flavor to suit your needs.
|
75
|
+
|
76
|
+
## A CHEF GENERATE RUN
|
77
|
+
|
78
|
+
A flavor is constructed twice when you run `chef generate`:
|
79
|
+
|
80
|
+
First, chef-gen-flavor-base constructs the object, passing a temporary
|
81
|
+
directory where files can be copied to. This is called setup mode.
|
82
|
+
chef-gen-flavors then calls `#add_content` in the flavor, which
|
83
|
+
constructs each snippet. Along the way, various setup hooks are called
|
84
|
+
that lets flavors and snippets modify the files copied into the
|
85
|
+
temporary directory.
|
86
|
+
|
87
|
+
Then, chef-dk constructs the object (or more accurately, your generator
|
88
|
+
recipe does), passing the type of chef artifact being created (e.g.
|
89
|
+
'cookbook') and a reference to the recipe that resources should be added
|
90
|
+
to. This is called generate mode. The generator recipe then calls
|
91
|
+
`#declare_resources`, which constructs each snippet. Like setup mode,
|
92
|
+
various generate hooks are called to let flavors and snippets modify the
|
93
|
+
resources that are used to generate your cookbook
|
94
|
+
|
95
|
+
## DIRECTORY LAYOUT
|
96
|
+
|
97
|
+
This example defines a flavor named `Example`. The full source for this
|
98
|
+
example is [available on
|
99
|
+
rubygems](https://rubygems.org/gems/chef-gen-flavor-example).
|
100
|
+
|
101
|
+
The directory structure of a flavor looks like this:
|
102
|
+
|
103
|
+
```
|
104
|
+
chef-gen-flavor-example
|
105
|
+
├── chef-gen-flavor-example.gemspec
|
106
|
+
├── lib
|
107
|
+
│ └── chef_gen
|
108
|
+
│ └── flavor
|
109
|
+
│ └── example.rb
|
110
|
+
└── shared
|
111
|
+
└── flavor
|
112
|
+
└── example
|
113
|
+
├── metadata.rb
|
114
|
+
└── recipes
|
115
|
+
└── cookbook.rb
|
116
|
+
```
|
117
|
+
|
118
|
+
(we are intentionally skipping over some of the ancillary files like
|
119
|
+
Rakefile, Gemfile, and unit tests)
|
120
|
+
|
121
|
+
## FLAVOR FILES
|
122
|
+
|
123
|
+
The file `lib/chef_gen/flavor/example.rb` contains the definition of the
|
124
|
+
'example' flavor. It requires the base flavor and the core snippets,
|
125
|
+
and inherits from FlavorBase:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
require 'chef_gen/flavor_base'
|
129
|
+
require 'chef_gen/snippets'
|
130
|
+
|
131
|
+
module ChefGen
|
132
|
+
module Flavor
|
133
|
+
class Example < ChefGen::FlavorBase
|
134
|
+
NAME = 'example'
|
135
|
+
...
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
In the constructor, it declares which snippets to mix in, then calls
|
142
|
+
initialize in the base class. Order is important here - the base class
|
143
|
+
constructs the snippets, so if you call it first, nothing gets
|
144
|
+
initialized.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
def initialize(temp_path: nil, target_path: nil, type: nil, recipe: nil)
|
148
|
+
super
|
149
|
+
@snippets << ChefGen::Snippet::CookbookBase
|
150
|
+
@snippets << ChefGen::Snippet::Attributes
|
151
|
+
...
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
The flavor also defines the do_add_content hook to copy files into the temporary
|
156
|
+
directory that chef-gen-flavors will pass to ChefDK:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
do_add_content do
|
160
|
+
@tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
`#add_content` is defined in FlavorBase, and is the mechanism by which
|
165
|
+
the contents hooks in the snippets get called, so it is important to
|
166
|
+
also call the method in the base class.
|
167
|
+
|
168
|
+
`#static_content_path` is a helper method that returns the standard
|
169
|
+
path to static content, which is
|
170
|
+
`$FILE/../../../shared/flavor/$FLAVORNAME`. For example, when called
|
171
|
+
as in the example above (which is in `lib/chef_gen/flavor/example.rb`),
|
172
|
+
the path returned is `shared/flavor/example`.
|
173
|
+
|
174
|
+
For a simple flavor that just uses the shared snippets, the static
|
175
|
+
content will typically just be a `metadata.rb` file and the generator
|
176
|
+
recipes (`recipes/cookbook.rb` in our example). It is important that
|
177
|
+
the cookbook name in metadata.rb match the name of the flavor, or
|
178
|
+
ChefDK will not be able to find the cookbook:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
name 'example'
|
182
|
+
version '0.0.0'
|
183
|
+
```
|
184
|
+
|
185
|
+
The generator recipes typically just construct an object and call
|
186
|
+
`#declare_resources` on it:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
ChefGen::Flavor::Awesome.new(
|
190
|
+
type: 'cookbook', recipe: self
|
191
|
+
).declare_resources
|
192
|
+
```
|
193
|
+
|
194
|
+
`#declare_resources` is defined in FlavorBase, and is the mechanism by
|
195
|
+
which the contents hooks in the snippets get called, so it is important
|
196
|
+
to also call the method in the base class. A flavor can also declare
|
197
|
+
resources at generate time:
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
do_declare_resources do
|
201
|
+
@recipe.send(:directory, 'test')
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
205
|
+
It is more common for resources to be declared via snippets though.
|
206
|
+
|
207
|
+
## SIMPLE RESOURCES
|
208
|
+
|
209
|
+
FlavorBase provides five accessors for adding simple resources to a
|
210
|
+
recipe when running in generate mode:
|
211
|
+
|
212
|
+
* directories
|
213
|
+
* files
|
214
|
+
* files_if_missing
|
215
|
+
* templates
|
216
|
+
* templates_if_missing
|
217
|
+
|
218
|
+
Each element of these lists is a path to the target in the generated
|
219
|
+
cookbook, so to set up a template to create a recipes/default.rb file,
|
220
|
+
just push that value onto the @templates_if_missing list:
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
do_declare_resourced do
|
224
|
+
templates_if_missing << File.join('recipes', 'default.rb')
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
The source of files and directories is a mangled form of the
|
229
|
+
destination, with slashes and dots replaced by underscores. Templates
|
230
|
+
additionally have `.erb` appended to the source. The template resource
|
231
|
+
set up by the above declaration looks like this:
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
template "#{dest_path}/recipes/default.rb" do
|
235
|
+
source 'recipes_default_rb.erb'
|
236
|
+
helpers ChefDK::Generator::TemplateHelper
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
If you need to construct more complex files and templates, you can call
|
241
|
+
`#add_files` or `#add_templates` directly; look at the CookbookBase
|
242
|
+
snippet for examples of how this is done.
|
243
|
+
|
244
|
+
## FLAVOR HOOKS
|
245
|
+
|
246
|
+
Flavors have two main entry points: `#add_content`, called during setup
|
247
|
+
mode and `#declare_resources`, called during generate mode. In each mode,
|
248
|
+
there are various hook points to allow flavors and snippets to modify what
|
249
|
+
happens.
|
250
|
+
|
251
|
+
Hooks are called in the context of the flavor object. Some hooks are
|
252
|
+
passed arguments.
|
253
|
+
|
254
|
+
In practice, the `do_add_content` and `do_declare_resources` hooks are
|
255
|
+
where most customization takes place.
|
256
|
+
|
257
|
+
Note that when declaring a hook directly in a flavor, you just add the
|
258
|
+
block as part of the class definition:
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
module ChefGen
|
262
|
+
module Flavor
|
263
|
+
class AwesomeOverride1 < ChefGen::Flavor::Awesome
|
264
|
+
NAME = 'awesome_override1'
|
265
|
+
|
266
|
+
before_copy_content do
|
267
|
+
@tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
To get the same effect in a snippet, you have to add the hook to the
|
275
|
+
flavor's class using one of the initializers:
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
module ChefGen
|
279
|
+
module Snippet
|
280
|
+
class BeforeCopyContent < ChefGen::SnippetBase
|
281
|
+
NAME = 'before_copy_content'
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
def initialize_generate
|
286
|
+
@flavor.class.before_copy_content do
|
287
|
+
@tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
```
|
294
|
+
|
295
|
+
### COMMON HOOKS
|
296
|
+
|
297
|
+
These hooks are called in both setup and generate mode:
|
298
|
+
|
299
|
+
#### before_initialize
|
300
|
+
|
301
|
+
Called before any flavor initialization has taken place in
|
302
|
+
ChefGen::FlavorBase.
|
303
|
+
|
304
|
+
#### after_initialize
|
305
|
+
|
306
|
+
Called after flavor initialization has taken place in
|
307
|
+
ChefGen::FlavorBase.
|
308
|
+
|
309
|
+
#### before_snippet_construct
|
310
|
+
|
311
|
+
Called before snippets are constructed.
|
312
|
+
|
313
|
+
#### after_snippet_construct
|
314
|
+
|
315
|
+
Called afetr snippets have been constructed and marked as active.
|
316
|
+
|
317
|
+
### SETUP HOOKS
|
318
|
+
|
319
|
+
These hooks are called in setup mode when `#add_content` is called.
|
320
|
+
|
321
|
+
#### before_add_content
|
322
|
+
|
323
|
+
Called immediately before the do_add_content hooks are invoked.
|
324
|
+
|
325
|
+
#### do_add_content
|
326
|
+
|
327
|
+
Called to add content to the temporary directory. Typically this is
|
328
|
+
used to copy static files (templates, etc.) from a flavor or snippet.
|
329
|
+
|
330
|
+
#### after_add_content
|
331
|
+
|
332
|
+
Called immediately after the do_add_content hooks are invoked.
|
333
|
+
|
334
|
+
#### before_copy_content
|
335
|
+
|
336
|
+
Called immediately before `#copy_content` is invoked to copy files to the
|
337
|
+
temporary directory.
|
338
|
+
|
339
|
+
#### after_copy_content
|
340
|
+
|
341
|
+
Called immediately after `#copy_content` is invoked. Typically this is
|
342
|
+
used to replace a file that comes from a parent flavor or another
|
343
|
+
snippet, ensuring that the replacement gets copied over the other file.
|
344
|
+
|
345
|
+
### GENERATE HOOKS
|
346
|
+
|
347
|
+
These hooks are called in generate mode when `#declare_resources` is
|
348
|
+
called.
|
349
|
+
|
350
|
+
#### before_declare_resources
|
351
|
+
|
352
|
+
Called immediately before the do_declare_resources are invoked.
|
353
|
+
|
354
|
+
#### do_declare_resources
|
355
|
+
|
356
|
+
Called to add resources to the recipe. Typically this is used to
|
357
|
+
manipulate the @directories, @files and @templates lists, but more
|
358
|
+
complex use cases are possible.
|
359
|
+
|
360
|
+
#### after_declare_resources
|
361
|
+
|
362
|
+
Called immediately after the do_declare_resources are invoked.
|
363
|
+
|
364
|
+
#### before_add_resources
|
365
|
+
|
366
|
+
Called immediately before `#add_resources` adds the entries in the
|
367
|
+
@directories, @files, and @templates lists to the recipe.
|
368
|
+
|
369
|
+
#### after_add_resources
|
370
|
+
|
371
|
+
Called immediately after `#add_resources` adds the entries in the
|
372
|
+
@directories, @files, and @templates lists to the recipe.
|
373
|
+
|
374
|
+
#### before_add_directories(directories)
|
375
|
+
|
376
|
+
Called before `#add_resources` adds directories to the recipe. Passed a
|
377
|
+
list of directories that will be added.
|
378
|
+
|
379
|
+
#### after_add_directories(directories)
|
380
|
+
|
381
|
+
Called after `#add_resources` adds directories to the recipe. Passed a
|
382
|
+
list of directories that will be added. Can be used for reporting
|
383
|
+
purposes (see the ActionsTaken snippet for an example).
|
384
|
+
|
385
|
+
#### before_add_files(files, resource_action, type, attrs)
|
386
|
+
|
387
|
+
Called before `#add_resources` adds files to the recipe. Passed a list
|
388
|
+
of files, the action to give the resource, the resource type (:file or
|
389
|
+
:cookbook_file) and a hash of additional attributes. Can be used to
|
390
|
+
implement protection schemes (see the NoClobber snippet for an example).
|
391
|
+
|
392
|
+
#### after_add_files(files, resource_action, type, attrs)
|
393
|
+
|
394
|
+
Called after `#add_resources` adds files to the recipe. Passed a list
|
395
|
+
of files, the action to give the resource, the resource type (:file or
|
396
|
+
:cookbook_file) and a hash of additional attributes. Can be used for
|
397
|
+
reporting purposes (see the ActionsTaken snippet for an example).
|
398
|
+
|
399
|
+
#### before_add_templates(templates, resource_action, attrs)
|
400
|
+
|
401
|
+
Called before `#add_resources` adds templates to the recipe. Passed a
|
402
|
+
list of files, the action to give the resource and a hash of additional
|
403
|
+
attributes. Can be used to implement protection schemes (see the
|
404
|
+
NoClobber snippet for an example).
|
405
|
+
|
406
|
+
#### after_add_templates(templates, resource_action, attrs)
|
407
|
+
|
408
|
+
Called after `#add_resources` adds templates to the recipe. Passed a
|
409
|
+
list of files, the action to give the resource and a hash of additional
|
410
|
+
attributes. Can be used for reporting purposes (see the ActionsTaken
|
411
|
+
snippet for an example).
|
412
|
+
|
413
|
+
## SNIPPET FILES
|
414
|
+
|
415
|
+
A snippet is a class that derives from `ChefGen::SnippetBase`:
|
416
|
+
|
417
|
+
```ruby
|
418
|
+
require 'chef_gen/snippet_base'
|
419
|
+
|
420
|
+
module ChefGen
|
421
|
+
module Snippet
|
422
|
+
class Attributes < ChefGen::SnippetBase
|
423
|
+
NAME = 'attributes'
|
424
|
+
...
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
```
|
429
|
+
|
430
|
+
A snippet typically alters a flavor in one of three ways: by adding
|
431
|
+
accessors, such as a list of gems to write to a Gemfile:
|
432
|
+
|
433
|
+
The snippet base class provides a constructor that invokes
|
434
|
+
`#initialize_setup` in setup mode and `#intialize_generate` in generate
|
435
|
+
mode.
|
436
|
+
|
437
|
+
Snippets can add accessors to the flavor:
|
438
|
+
|
439
|
+
```ruby
|
440
|
+
def initialize_generate
|
441
|
+
@flavor.class.send(:attr_accessor, :cookbook_gems)
|
442
|
+
@flavor.cookbook_gems = { 'rake' => '~> 10.4' }
|
443
|
+
end
|
444
|
+
```
|
445
|
+
|
446
|
+
Or add a hook to copy content to the temporary directory. Like a
|
447
|
+
flavor, `ChefGen::SnippetBase` provides a `#static_content_path` method
|
448
|
+
that finds the standard location of files packaged with the snippet,
|
449
|
+
using the name.
|
450
|
+
|
451
|
+
```ruby
|
452
|
+
def initialize_setup
|
453
|
+
snippet_content_path = File.expand_path(File.join(static_content_path(__FILE__))) + '/.'
|
454
|
+
@flavor.class.do_add_content do
|
455
|
+
tocopy << [snippet_content_path]
|
456
|
+
end
|
457
|
+
end
|
458
|
+
```
|
459
|
+
|
460
|
+
(Note how the static content path is calculated outside the hook block,
|
461
|
+
to ensure that we use `#static_content_path` defined in SnippetBase. If
|
462
|
+
it were inside the hook block, which executes in the scope of the flavor
|
463
|
+
object, then `#static_content_path` in FlavorBase would be used.)
|
464
|
+
|
465
|
+
A snippet might also add a hook to declare resources at generate time:
|
466
|
+
|
467
|
+
```ruby
|
468
|
+
def initialize_generate
|
469
|
+
@flavor.class.do_declare_resources do
|
470
|
+
directories << 'spec'
|
471
|
+
directories << File.join('spec', 'recipes')
|
472
|
+
templates << '.rspec'
|
473
|
+
templates_if_missing << File.join('spec', 'spec_helper.rb')
|
474
|
+
templates_if_missing << File.join('spec', 'recipes', 'default_spec.rb')
|
475
|
+
templates_if_missing << File.join('spec', 'chef_runner_context.rb')
|
476
|
+
end
|
477
|
+
end
|
478
|
+
```
|
479
|
+
|
480
|
+
## BUILT-IN SNIPPETS
|
481
|
+
|
482
|
+
chef-gen-flavor-base comes with snippets that can be mixed and matched
|
483
|
+
to create a complete cookbook skeleton. Also take a look at the
|
484
|
+
[example flavor](https://github.com/jf647/chef-gen-flavor-example) to
|
485
|
+
see how to use them.
|
486
|
+
|
487
|
+
For full documentation, run `bundle exec rake doc` in the source repo
|
488
|
+
then open `doc/index.html`.
|
489
|
+
|
490
|
+
### CookbookBase
|
491
|
+
|
492
|
+
* declares and provides a template for the basic files that any cookbook needs:
|
493
|
+
* Gemfile
|
494
|
+
* Berksfile
|
495
|
+
* Rakefile
|
496
|
+
* metadata.rb
|
497
|
+
* README.md
|
498
|
+
* CHANGELOG.md
|
499
|
+
* provides an accessor `#cookbook_gems` which is a hash with gem names as keys and constraints as values, defaulting to rake and berkshelf
|
500
|
+
* provides an accessor `#gem_sources`, which is a list preloaded with https://rubygems.org
|
501
|
+
* provides an accessor `#berks_sources`, which is a list preloaded with https://supermarket.chef.io
|
502
|
+
* provides an accessor `#rake_tasks`, which is a hash with task names as keys and full task definitions as values
|
503
|
+
|
504
|
+
### ActionsTaken
|
505
|
+
|
506
|
+
* provides an accessor `#actions_taken`, which is a list of strings representing what actions the flavor took
|
507
|
+
* adds before hooks to `#add_files`, `#add_templates` and `#add_directories` to add those actons to the actions_taken list
|
508
|
+
* adds an after hook to `#declare_resources` that adds a ruby_block to the recipe to report on what actions the flavor took
|
509
|
+
|
510
|
+
Any resources that you add to a flavor manually (not using the add_*
|
511
|
+
helpers) should be added to the actions_taken list manually:
|
512
|
+
|
513
|
+
```ruby
|
514
|
+
def initialize_generate
|
515
|
+
@recipe.send(:execute, 'initialize git repo') do
|
516
|
+
command('git init .')
|
517
|
+
end
|
518
|
+
actions_taken << 'initialize git repo' if snippet?('actions_taken')
|
519
|
+
```
|
520
|
+
|
521
|
+
### Attributes
|
522
|
+
|
523
|
+
* declares and provides a template for attributes/default.rb
|
524
|
+
|
525
|
+
### ChefSpec
|
526
|
+
|
527
|
+
* declares and provides a template for ChefSpec files:
|
528
|
+
* .rspec
|
529
|
+
* spec/spec_helper.rb
|
530
|
+
* spec/chef_run_context.rb
|
531
|
+
* spec/recipes/default_spec.rb
|
532
|
+
* adds the ChefSpec gems
|
533
|
+
* adds the ChefSpec rake tasks
|
534
|
+
* adds the ChefSpec guard set
|
535
|
+
|
536
|
+
### Debugging
|
537
|
+
|
538
|
+
* adds the pry, pry-byebug and pry-stack_explorer gems, to make debugging cookbooks easier
|
539
|
+
|
540
|
+
### ExampleFile
|
541
|
+
|
542
|
+
* declares and provides a cookbook_file for files/default/example.conf
|
543
|
+
|
544
|
+
### ExampleTemplate
|
545
|
+
|
546
|
+
* declares and provides a cookbook_file for templates/default/example.conf.erb
|
547
|
+
|
548
|
+
### GitInit
|
549
|
+
|
550
|
+
* executes 'git init .' after generation if git is installed and the `--skip-git-init` switch has not been passed
|
551
|
+
|
552
|
+
### Guard
|
553
|
+
|
554
|
+
* provides an accessor `#guard_sets`, which is a hash with set names as keys and full set definitions as values
|
555
|
+
|
556
|
+
### NextSteps
|
557
|
+
|
558
|
+
* provides an accessor `#next_steps`, which can be used to display some guidance to the user on how to proceed
|
559
|
+
|
560
|
+
To set next steps in a recipe, define it in your generate recipe like so:
|
561
|
+
|
562
|
+
```ruby
|
563
|
+
f = ChefGen::Flavor::Awesome.new(
|
564
|
+
type: 'cookbook', recipe: self
|
565
|
+
)
|
566
|
+
f.class.after_declare_resources do
|
567
|
+
self.next_steps = <<END
|
568
|
+
|
569
|
+
go forth and conquer
|
570
|
+
|
571
|
+
END
|
572
|
+
end
|
573
|
+
f.declare_resources
|
574
|
+
```
|
575
|
+
|
576
|
+
### NoClobber
|
577
|
+
|
578
|
+
* provides an accessor `#fail_on_clobber`, which is a boolean. If true, then attempts to add files or templates with an action of :create (i.e. not @files_if_missing or @templates_if_missing) will cause the generate to fail. To allow for files to be clobbered, users need to pass `-a clobber` to `chef generate`
|
579
|
+
|
580
|
+
### Recipes
|
581
|
+
|
582
|
+
* declares and provides a cookbook_file for recipes/default.rb
|
583
|
+
|
584
|
+
### ResourceProvider
|
585
|
+
|
586
|
+
* declares and provides a cookbook_file for:
|
587
|
+
* resources/default.rb
|
588
|
+
* providers/default.rb
|
589
|
+
|
590
|
+
### StandardIgnore
|
591
|
+
|
592
|
+
* declares the two ignore files used by chef cookbook repos:
|
593
|
+
* chefignore
|
594
|
+
* .gitignore
|
595
|
+
* provides two accessors: `#chefignore_patterns` and `#gitignore_patterns`, both of which are lists of patterns to write to `chefignore` and `.gitignore` respectively
|
596
|
+
* preloads the standard ignore patterns for chefignore and .gitignore
|
597
|
+
|
598
|
+
### StyleFoodcritic
|
599
|
+
|
600
|
+
* adds the Foodcritic gems
|
601
|
+
* adds the Foodcritic rake task
|
602
|
+
* adds the Foodcritic guard set
|
603
|
+
|
604
|
+
### StyleRubocop
|
605
|
+
|
606
|
+
* declares and provides a template for .rubocop.yml
|
607
|
+
* adds the Rubocop gems
|
608
|
+
* adds the Rubocop rake task
|
609
|
+
* adds the Rubocop guard set
|
610
|
+
|
611
|
+
### StyleTailor
|
612
|
+
|
613
|
+
* adds the Tailor gems
|
614
|
+
* adds the Tailor rake task
|
615
|
+
|
616
|
+
### TestKitchen
|
617
|
+
|
618
|
+
* declares and provides a template for ChefSpec files:
|
619
|
+
* .kitchen.yml
|
620
|
+
* test/integration/default/serverspec/spec_helper.rb
|
621
|
+
* test/integration/default/serverspec/recipes/default_spec.rb
|
622
|
+
* adds the Test Kitchen gems
|
623
|
+
* adds the Test Kitchen rake tasks
|
624
|
+
* adds the Test Kitchen guard set
|
625
|
+
|
626
|
+
## SNIPPET DEPENDENCIES
|
627
|
+
|
628
|
+
Sometimes snippets need to depend on features provided by another
|
629
|
+
snippet. For example, the CookbookBase snippet will add patterns to the
|
630
|
+
.gitignore and chefignore files, but only if the StandardIgnore snippet
|
631
|
+
has also been included in the flavor. A common pattern is to test
|
632
|
+
whether a snippet has been enabled during generation like so:
|
633
|
+
|
634
|
+
```ruby
|
635
|
+
def initialize_generate
|
636
|
+
@flavor.class.do_declare_resources do
|
637
|
+
chefignore_patterns << '.rubocop.yml' if snippet?('standard_ignore')
|
638
|
+
end
|
639
|
+
end
|
640
|
+
```
|
641
|
+
|
642
|
+
## EXAMPLE OVERRIDES
|
643
|
+
|
644
|
+
Because flavors and snippets are just ruby classes, you can override
|
645
|
+
them using normal object techniques. It is important to call the
|
646
|
+
superclass method in FlavorBase or SnippetBase, as this is where the
|
647
|
+
important work tends to happen, but you have some flexibility around
|
648
|
+
whether you call it before or after the code in your flavor or snippet.
|
649
|
+
|
650
|
+
### Deriving one flavor from another
|
651
|
+
|
652
|
+
Rather than inheriting from ChefGen::FlavorBase, your flavor can inherit
|
653
|
+
from another flavor that inherits from ChefGen::FlavorBase. In this way
|
654
|
+
you get all of the declarations and content of your parent flavor, plus
|
655
|
+
have the ability to tweak and change one or two little things that don't
|
656
|
+
fit your environment.
|
657
|
+
|
658
|
+
```ruby
|
659
|
+
module ChefGen
|
660
|
+
module Flavor
|
661
|
+
class MoarAwesome < ChefGen::Flavor::Awesome
|
662
|
+
NAME = 'moar_awesome'
|
663
|
+
end
|
664
|
+
end
|
665
|
+
end
|
666
|
+
```
|
667
|
+
|
668
|
+
### Removing a snippet from the parent flavor
|
669
|
+
|
670
|
+
Perhaps a flavor does everything you want, but there's one thing you
|
671
|
+
don't like about it. You can remove that snippet from the list of
|
672
|
+
snippets by hooking after_initialize:
|
673
|
+
|
674
|
+
```ruby
|
675
|
+
module ChefGen
|
676
|
+
module Flavor
|
677
|
+
class LessAwesome < ChefGen::Flavor::Awesome
|
678
|
+
NAME = 'less_awesome'
|
679
|
+
|
680
|
+
after_initialize do
|
681
|
+
@snippets.reject! do |e|
|
682
|
+
e == ChefGen::Snippet::ExampleFile || e == ChefGen::Snippet::ExampleTemplate
|
683
|
+
end
|
684
|
+
end
|
685
|
+
end
|
686
|
+
end
|
687
|
+
end
|
688
|
+
```
|
689
|
+
|
690
|
+
### Changing a template file in the parent flavor
|
691
|
+
|
692
|
+
If you want to keep a declaration but change the source file used for a
|
693
|
+
flavor, you can hook before_copy_content (or after_add_content):
|
694
|
+
|
695
|
+
```ruby
|
696
|
+
module ChefGen
|
697
|
+
module Flavor
|
698
|
+
class AlmostPerfect < ChefGen::Flavor::Awesome
|
699
|
+
NAME = 'almost_perfect'
|
700
|
+
|
701
|
+
before_copy_content do
|
702
|
+
@tocopy << [File.expand_path(File.join(static_content_path(__FILE__))) + '/.']
|
703
|
+
end
|
704
|
+
end
|
705
|
+
end
|
706
|
+
end
|
707
|
+
```
|
708
|
+
|
709
|
+
This example assumes that the awesome flavor includes the TestKitchen
|
710
|
+
snippet (which declares the serverspec spec_helper.rb file) and that you
|
711
|
+
package an alternate template with your flavor in
|
712
|
+
`shared/flavor/almost_perfect/templates/default/test_integration_default_serverspec_spec_helper_rb.erb`
|
713
|
+
|
714
|
+
### Adding a Rake task
|
715
|
+
|
716
|
+
To add an additional Rake task to a flavor:
|
717
|
+
|
718
|
+
```ruby
|
719
|
+
module ChefGen
|
720
|
+
module Flavor
|
721
|
+
class NeedsATask < ChefGen::Flavor::Awesome
|
722
|
+
NAME = 'needs_a_task'
|
723
|
+
|
724
|
+
do_declare_resources do
|
725
|
+
rake_tasks['foo'] = <<'END'
|
726
|
+
task :foo do
|
727
|
+
sh 'echo foo'
|
728
|
+
end
|
729
|
+
END
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
```
|
736
|
+
|
737
|
+
### Removing a resource using a snippet
|
738
|
+
|
739
|
+
Here, we've used a snippet to remove resources from an snippet that
|
740
|
+
was run earlier:
|
741
|
+
|
742
|
+
```ruby
|
743
|
+
require 'chef_gen/flavor_base'
|
744
|
+
require 'chef_gen/snippet_base'
|
745
|
+
|
746
|
+
module ChefGen
|
747
|
+
module Snippet
|
748
|
+
class RemoveExamples < ChefGen::SnippetBase
|
749
|
+
NAME = 'remove_examples'
|
750
|
+
|
751
|
+
private
|
752
|
+
|
753
|
+
def initialize_generate
|
754
|
+
@flavor.class.after_declare_resources do
|
755
|
+
directories.reject! do |e|
|
756
|
+
%w(files files/default templates templates/default).include?(e)
|
757
|
+
end
|
758
|
+
files_if_missing.reject! do |e|
|
759
|
+
%w(files/default/example.conf templates/default/example.conf.erb).include?(e)
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
end
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
module ChefGen
|
768
|
+
module Flavor
|
769
|
+
class DoNotLikeExamples < ChefGen::Flavor::Awesome
|
770
|
+
NAME = 'do_not_like_examples'
|
771
|
+
|
772
|
+
def initialize(temp_path: nil, type: nil, recipe: nil)
|
773
|
+
super
|
774
|
+
@snippets << ChefGen::Snippet::RemoveExamples
|
775
|
+
end
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
```
|
780
|
+
|
781
|
+
Note that when we manipulate the resource lists in a snippet, we have
|
782
|
+
to use `@flavor.resource_type`, not just `@resource_type`, as the
|
783
|
+
resource lists are members of the flavor, not the snippet.
|
784
|
+
|
785
|
+
## TESTING A FLAVOR
|
786
|
+
|
787
|
+
chef-gen-flavors provides a number of useful step definitions for Aruba
|
788
|
+
(a CLI driver for Cucumber) to make it easier to test flavors. To access
|
789
|
+
these definitions, add the following line to your
|
790
|
+
`features/support/env.rb` file:
|
791
|
+
|
792
|
+
require 'chef_gen/flavors/cucumber'
|
793
|
+
|
794
|
+
For an example of how to use these steps in your features, refer to the
|
795
|
+
features in the `features` directory and the fixtures in
|
796
|
+
`spec/support/fixtues`.
|
797
|
+
|
798
|
+
## AUTHOR
|
799
|
+
|
800
|
+
[James FitzGibbon](https://github.com/jf647)
|
801
|
+
|
802
|
+
## LICENSE
|
803
|
+
|
804
|
+
Copyright 2015 Nordstrom, Inc.
|
805
|
+
|
806
|
+
Copyright 2015 James FitzGibbon
|
807
|
+
|
808
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
809
|
+
not use this file except in compliance with the License. You may obtain
|
810
|
+
a copy of the License at
|
811
|
+
|
812
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
813
|
+
|
814
|
+
Unless required by applicable law or agreed to in writing, software
|
815
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
816
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
817
|
+
See the License for the specific language governing permissions and
|
818
|
+
limitations under the License.
|