poise-file 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.kitchen.yml +3 -0
  4. data/.travis.yml +64 -0
  5. data/.yardopts +7 -0
  6. data/.yo-rc.json +7 -0
  7. data/CHANGELOG.md +5 -0
  8. data/Gemfile +32 -0
  9. data/LICENSE +201 -0
  10. data/README.md +102 -0
  11. data/Rakefile +17 -0
  12. data/lib/poise_file.rb +21 -0
  13. data/lib/poise_file/cheftie.rb +18 -0
  14. data/lib/poise_file/resources.rb +27 -0
  15. data/lib/poise_file/resources/poise_file.rb +242 -0
  16. data/lib/poise_file/version.rb +21 -0
  17. data/poise-file.gemspec +42 -0
  18. data/test/cookbook/metadata.rb +18 -0
  19. data/test/cookbook/recipes/default.rb +36 -0
  20. data/test/gemfiles/chef-12.1.gemfile +23 -0
  21. data/test/gemfiles/chef-12.10.gemfile +23 -0
  22. data/test/gemfiles/chef-12.11.gemfile +23 -0
  23. data/test/gemfiles/chef-12.12.gemfile +22 -0
  24. data/test/gemfiles/chef-12.13.gemfile +22 -0
  25. data/test/gemfiles/chef-12.14.gemfile +19 -0
  26. data/test/gemfiles/chef-12.15.gemfile +19 -0
  27. data/test/gemfiles/chef-12.16.gemfile +19 -0
  28. data/test/gemfiles/chef-12.17.gemfile +19 -0
  29. data/test/gemfiles/chef-12.18.gemfile +19 -0
  30. data/test/gemfiles/chef-12.19.gemfile +19 -0
  31. data/test/gemfiles/chef-12.2.gemfile +23 -0
  32. data/test/gemfiles/chef-12.3.gemfile +23 -0
  33. data/test/gemfiles/chef-12.4.gemfile +24 -0
  34. data/test/gemfiles/chef-12.5.gemfile +23 -0
  35. data/test/gemfiles/chef-12.6.gemfile +23 -0
  36. data/test/gemfiles/chef-12.7.gemfile +23 -0
  37. data/test/gemfiles/chef-12.8.gemfile +23 -0
  38. data/test/gemfiles/chef-12.9.gemfile +23 -0
  39. data/test/gemfiles/chef-12.gemfile +19 -0
  40. data/test/gemfiles/chef-13.0.gemfile +19 -0
  41. data/test/gemfiles/chef-13.gemfile +19 -0
  42. data/test/gemfiles/master.gemfile +26 -0
  43. data/test/integration/default/serverspec/default_spec.rb +54 -0
  44. data/test/spec/resources/poise_file_spec.rb +328 -0
  45. data/test/spec/spec_helper.rb +19 -0
  46. metadata +183 -0
@@ -0,0 +1,17 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'poise_boiler/rakefile'
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+
18
+ module PoiseFile
19
+ autoload :Resources, 'poise_file/resources'
20
+ autoload :VERSION, 'poise_file/version'
21
+ end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'poise_file/resources'
@@ -0,0 +1,27 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'poise_file/resources/poise_file'
19
+
20
+
21
+ module PoiseFile
22
+ # Chef resources and providers for poise-file.
23
+ #
24
+ # @since 1.0.0
25
+ module Resources
26
+ end
27
+ end
@@ -0,0 +1,242 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'chef/file_content_management/content_base'
19
+ require 'chef/file_content_management/tempfile'
20
+ require 'chef/resource/file'
21
+ require 'chef/provider/file'
22
+ require 'poise'
23
+
24
+
25
+ module PoiseFile
26
+ module Resources
27
+ # (see PoiseFile::Resource)
28
+ # @since 1.0.0
29
+ module PoiseFile
30
+ # A `poise_file` resource to write out a file with some nice helpers.
31
+ #
32
+ # @provides poise_file
33
+ # @action create
34
+ # @action delete
35
+ # @action touch
36
+ # @action create_if_missing
37
+ # @example
38
+ # poise_file '/etc/myapp.json' do
39
+ # content listen: 80, debug: false
40
+ # end
41
+ class Resource < Chef::Resource::File
42
+ include Poise
43
+ provides(:poise_file)
44
+ actions(:create, :delete, :touch, :create_if_missing)
45
+
46
+ # @!attribute content
47
+ # Format to write the file in. `text` is the same as the core `file`
48
+ # resource, `json` writes the content as a JSON object, `yaml` as a
49
+ # YAML object.
50
+ # @return [String, Symbol]
51
+ attribute(:content, kind_of: [String, Hash, Array])
52
+
53
+ # @!attribute format
54
+ # Format to write the file in. `text` is the same as the core `file`
55
+ # resource, `json` writes the content as a JSON object, `yaml` as a
56
+ # YAML object.
57
+ # @return [String, Symbol]
58
+ attribute(:format, kind_of: [String, Symbol], default: lazy { default_format })
59
+
60
+ # @!attribute pattern
61
+ # Regular expression pattern to use for an in-place update of the file.
62
+ # If given a Proc it should take two arguments, the current content of
63
+ # the file and the resource object.
64
+ # @see #pattern_location
65
+ # @return [String, Regexp, Proc, nil, false]
66
+ attribute(:pattern, kind_of: [String, Regexp, Proc, NilClass, FalseClass], default: nil)
67
+
68
+ # @!attribute pattern_location
69
+ # Location to insert the {#content} data at. Must be one of:
70
+ # * replace: Overwrite the pattern.
71
+ # * replace_or_add: Overwrite the patter or append to the file if the
72
+ # pattern is not present.
73
+ # * before: Insert the {#content} immediately before the pattern if it
74
+ # doesn't already exist.
75
+ # * after: Insert the {#content} immediately after the pattern if it
76
+ # doesn't already exist.
77
+ # @see #pattern
78
+ # @return [String, Symbol]
79
+ attribute(:pattern_location, kind_of: [String, Symbol], default: 'replace_or_add')
80
+
81
+ private
82
+
83
+ # Find the default format based on the file path.
84
+ #
85
+ # @api private
86
+ # @return [String]
87
+ def default_format
88
+ # If we have a pattern, ignore the format system by default. If we
89
+ # have string content, it's just raw content by default.
90
+ return 'text' if pattern || content.is_a?(String)
91
+ case path
92
+ when /\.json$/
93
+ 'json'
94
+ when /\.ya?ml$/
95
+ 'yaml'
96
+ else
97
+ 'text'
98
+ end
99
+ end
100
+ end
101
+
102
+ # File content class for `poise_file`.
103
+ #
104
+ # @api private
105
+ # @see Resource
106
+ class Content < Chef::FileContentManagement::ContentBase
107
+ # Required abstract method for ContentBase. Builds the new content of
108
+ # file in a tempfile or returns nil to not touch the file content.
109
+ #
110
+ # @return [Chef::FileContentManagement::Tempfile, nil]
111
+ def file_for_provider
112
+ if @new_resource.content
113
+ if @new_resource.pattern && @new_resource.format.to_s != 'text'
114
+ raise ArgumentError.new("Cannot use `pattern` property and `format` property together")
115
+ end
116
+
117
+ tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
118
+ content = if @new_resource.pattern
119
+ content_for_pattern
120
+ else
121
+ content_for_format
122
+ end
123
+ tempfile.write(content)
124
+ tempfile.close
125
+ tempfile
126
+ else
127
+ nil
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ # Build the content when using the edit-in-place mode.
134
+ #
135
+ # @api private
136
+ # @return [String]
137
+ def content_for_pattern
138
+ # Get the base content to start from.
139
+ existing_content = if ::File.exist?(@new_resource.path)
140
+ IO.read(@new_resource.path)
141
+ else
142
+ # Pretend the file is empty if it doesn't already exist.
143
+ ''
144
+ end
145
+
146
+ # If we were given a proc, use it.
147
+ if @new_resource.pattern.is_a?(Proc)
148
+ return @new_resource.pattern.call(existing_content, @new_resource)
149
+ end
150
+
151
+ # Build the pattern.
152
+ pattern = if @new_resource.pattern.is_a?(Regexp)
153
+ # Should this dup the pattern because weird tracking stuff?
154
+ @new_resource.pattern
155
+ else
156
+ # Deal with newlines at the end of a line because $ matches before
157
+ # newline, not after.
158
+ pattern_string = if @new_resource.content.end_with?("\n")
159
+ @new_resource.pattern.gsub(/\$\Z/, "$\n?")
160
+ else
161
+ @new_resource.pattern
162
+ end
163
+ # Ruby will show a warning if trying to add options to an existing
164
+ # Regexp instance so only use that if it's a string.
165
+ Regexp.new(pattern_string, Regexp::MULTILINE)
166
+ end
167
+
168
+ # Run the pattern operation.
169
+ case @new_resource.pattern_location.to_s
170
+ when 'replace'
171
+ # Overwrite the matched section.
172
+ existing_content.gsub!(pattern, @new_resource.content) || existing_content
173
+ when 'replace_or_add'
174
+ # Overwrite the pattern if it matches otherwise append.
175
+ existing_content.gsub!(pattern, @new_resource.content) || (existing_content << @new_resource.content)
176
+ when 'before'
177
+ # Insert the content before the pattern if it doesn't already exist.
178
+ match = pattern.match(existing_content)
179
+ if match
180
+ if match.pre_match.end_with?(@new_resource.content)
181
+ existing_content
182
+ else
183
+ '' << match.pre_match << @new_resource.content << match[0] << match.post_match
184
+ end
185
+ else
186
+ existing_content
187
+ end
188
+ when 'after'
189
+ # Insert the content after the pattern if it doesn't already exist.
190
+ match = pattern.match(existing_content)
191
+ if match
192
+ if match.post_match.start_with?(@new_resource.content)
193
+ existing_content
194
+ else
195
+ '' << match.pre_match << match[0] << @new_resource.content << match.post_match
196
+ end
197
+ else
198
+ existing_content
199
+ end
200
+ else
201
+ raise ArgumentError.new("Unknown file pattern location #{@new_resource.pattern_location.inspect}")
202
+ end
203
+ end
204
+
205
+ # Build the content when using format mode (i.e. when not using the
206
+ # edit-in-place mode).
207
+ #
208
+ # @api private
209
+ # @return [String]
210
+ def content_for_format
211
+ case @new_resource.format.to_s
212
+ when 'json'
213
+ require 'chef/json_compat'
214
+ # Make sure we include the trailing newline because YAML has one.
215
+ Chef::JSONCompat.to_json_pretty(@new_resource.content) + "\n"
216
+ when 'yaml'
217
+ require 'yaml'
218
+ YAML.dump(@new_resource.content)
219
+ when 'text'
220
+ @new_resource.content
221
+ else
222
+ raise ArgumentError.new("Unknown file format #{@new_resource.format.inspect}")
223
+ end
224
+ end
225
+ end
226
+
227
+ # Provider for `poise_file`.
228
+ #
229
+ # @see Resource
230
+ # @provides poise_file
231
+ class Provider < Chef::Provider::File
232
+ include Poise
233
+ provides(:poise_file)
234
+
235
+ def initialize(new_resource, run_context)
236
+ @content_class = Content
237
+ super
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+
19
+ module PoiseFile
20
+ VERSION = '1.0.0'
21
+ end
@@ -0,0 +1,42 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ lib = File.expand_path('../lib', __FILE__)
18
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
19
+ require 'poise_file/version'
20
+
21
+ Gem::Specification.new do |spec|
22
+ spec.name = 'poise-file'
23
+ spec.version = PoiseFile::VERSION
24
+ spec.authors = ['Noah Kantrowitz']
25
+ spec.email = %w{noah@coderanger.net}
26
+ spec.description = 'A Chef cookbook for advanced file managent.'
27
+ spec.summary = spec.description
28
+ spec.homepage = 'https://github.com/poise/poise-file'
29
+ spec.license = 'Apache-2.0'
30
+ spec.metadata['platforms'] = 'any'
31
+
32
+ spec.files = `git ls-files`.split($/)
33
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
34
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
35
+ spec.require_paths = %w{lib}
36
+
37
+ spec.add_dependency 'chef', '>= 12.1', '< 14'
38
+ spec.add_dependency 'halite', '~> 1.0'
39
+ spec.add_dependency 'poise', '~> 2.0'
40
+
41
+ spec.add_development_dependency 'poise-boiler', '~> 1.0'
42
+ end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ name 'poise-file_test'
18
+ depends 'poise-file'
@@ -0,0 +1,36 @@
1
+ #
2
+ # Copyright 2017, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ poise_file '/poise_test' do
18
+ content "I'm a little teapot\n"
19
+ end
20
+
21
+ poise_file '/poise_test.json' do
22
+ content ['short and stout', {here: 'is my handle'}]
23
+ end
24
+
25
+ poise_file '/poise_test.yml' do
26
+ content 'here' => 'is my spout', 'when' => ['I', 'get', 'all', 'steamed', 'up']
27
+ end
28
+
29
+ file '/poise_test_pattern' do
30
+ content "I must shout\ntip me over\n"
31
+ end
32
+
33
+ poise_file '/poise_test_pattern' do
34
+ content 'yell'
35
+ pattern 'shout'
36
+ end