chef 11.12.0.alpha.1 → 11.12.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/chef/api_client/registration.rb +46 -9
- data/lib/chef/application.rb +1 -0
- data/lib/chef/application/client.rb +25 -24
- data/lib/chef/client.rb +34 -0
- data/lib/chef/config.rb +11 -0
- data/lib/chef/cookbook/chefignore.rb +10 -2
- data/lib/chef/cookbook/metadata.rb +31 -3
- data/lib/chef/cookbook/synchronizer.rb +2 -2
- data/lib/chef/cookbook/syntax_check.rb +4 -4
- data/lib/chef/encrypted_data_bag_item.rb +37 -1
- data/lib/chef/exceptions.rb +1 -0
- data/lib/chef/guard_interpreter/default_guard_interpreter.rb +42 -0
- data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +122 -0
- data/lib/chef/http.rb +0 -1
- data/lib/chef/http/decompressor.rb +7 -4
- data/lib/chef/http/simple.rb +5 -0
- data/lib/chef/http/validate_content_length.rb +28 -12
- data/lib/chef/knife.rb +1 -0
- data/lib/chef/knife/client_bulk_delete.rb +48 -9
- data/lib/chef/knife/client_delete.rb +4 -4
- data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
- data/lib/chef/knife/cookbook_upload.rb +17 -7
- data/lib/chef/knife/core/bootstrap_context.rb +1 -1
- data/lib/chef/knife/core/ui.rb +42 -5
- data/lib/chef/knife/node_run_list_add.rb +31 -2
- data/lib/chef/knife/ssh.rb +44 -31
- data/lib/chef/knife/ssl_check.rb +213 -0
- data/lib/chef/knife/ssl_fetch.rb +145 -0
- data/lib/chef/mixin/deep_merge.rb +13 -5
- data/lib/chef/mixin/shell_out.rb +9 -3
- data/lib/chef/node.rb +23 -4
- data/lib/chef/node/immutable_collections.rb +32 -0
- data/lib/chef/platform/provider_mapping.rb +21 -18
- data/lib/chef/platform/query_helpers.rb +10 -2
- data/lib/chef/policy_builder/expand_node_object.rb +3 -6
- data/lib/chef/provider/cron.rb +25 -3
- data/lib/chef/provider/mount/mount.rb +1 -1
- data/lib/chef/provider/package/dpkg.rb +2 -1
- data/lib/chef/provider/package/windows.rb +80 -0
- data/lib/chef/provider/package/windows/msi.rb +69 -0
- data/lib/chef/provider/powershell_script.rb +19 -6
- data/lib/chef/provider/service/solaris.rb +11 -7
- data/lib/chef/resource.rb +18 -5
- data/lib/chef/resource/conditional.rb +20 -7
- data/lib/chef/resource/cron.rb +18 -2
- data/lib/chef/resource/execute.rb +0 -2
- data/lib/chef/resource/powershell_script.rb +23 -1
- data/lib/chef/resource/script.rb +25 -0
- data/lib/chef/resource/subversion.rb +4 -0
- data/lib/chef/resource/windows_package.rb +79 -0
- data/lib/chef/resource/windows_script.rb +0 -5
- data/lib/chef/resources.rb +1 -0
- data/lib/chef/rest.rb +6 -1
- data/lib/chef/run_context.rb +22 -2
- data/lib/chef/run_context/cookbook_compiler.rb +12 -0
- data/lib/chef/util/editor.rb +92 -0
- data/lib/chef/util/file_edit.rb +22 -54
- data/lib/chef/version.rb +2 -2
- data/lib/chef/win32/api/installer.rb +166 -0
- data/lib/chef/win32/version.rb +8 -0
- data/spec/data/standalone_cookbook/Gemfile +1 -0
- data/spec/data/standalone_cookbook/chefignore +9 -0
- data/spec/data/standalone_cookbook/recipes/default.rb +3 -0
- data/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/multi_json.rb +1 -0
- data/spec/functional/resource/powershell_spec.rb +262 -1
- data/spec/functional/win32/versions_spec.rb +3 -3
- data/spec/integration/knife/chefignore_spec.rb +1 -2
- data/spec/integration/knife/raw_spec.rb +8 -13
- data/spec/integration/knife/redirection_spec.rb +6 -14
- data/spec/integration/solo/solo_spec.rb +19 -0
- data/spec/support/shared/functional/windows_script.rb +1 -1
- data/spec/support/shared/integration/app_server_support.rb +42 -0
- data/spec/support/shared/integration/integration_helper.rb +1 -0
- data/spec/support/shared/unit/script_resource.rb +38 -0
- data/spec/unit/api_client/registration_spec.rb +109 -38
- data/spec/unit/application/client_spec.rb +48 -1
- data/spec/unit/cookbook/chefignore_spec.rb +10 -0
- data/spec/unit/cookbook/metadata_spec.rb +45 -1
- data/spec/unit/cookbook/syntax_check_spec.rb +28 -0
- data/spec/unit/cookbook_spec.rb +0 -10
- data/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb +56 -0
- data/spec/unit/http/simple_spec.rb +32 -0
- data/spec/unit/http/validate_content_length_spec.rb +187 -0
- data/spec/unit/knife/bootstrap_spec.rb +13 -4
- data/spec/unit/knife/client_bulk_delete_spec.rb +123 -38
- data/spec/unit/knife/client_delete_spec.rb +4 -4
- data/spec/unit/knife/cookbook_upload_spec.rb +181 -88
- data/spec/unit/knife/core/bootstrap_context_spec.rb +11 -1
- data/spec/unit/knife/core/ui_spec.rb +109 -38
- data/spec/unit/knife/node_run_list_add_spec.rb +24 -1
- data/spec/unit/knife/ssh_spec.rb +17 -6
- data/spec/unit/knife/ssl_check_spec.rb +187 -0
- data/spec/unit/knife/ssl_fetch_spec.rb +151 -0
- data/spec/unit/mixin/deep_merge_spec.rb +17 -0
- data/spec/unit/node/immutable_collections_spec.rb +55 -0
- data/spec/unit/node_spec.rb +9 -0
- data/spec/unit/platform/query_helpers_spec.rb +32 -0
- data/spec/unit/platform_spec.rb +193 -175
- data/spec/unit/policy_builder/expand_node_object_spec.rb +1 -1
- data/spec/unit/provider/cron_spec.rb +175 -1
- data/spec/unit/provider/mount/mount_spec.rb +33 -3
- data/spec/unit/provider/package/dpkg_spec.rb +4 -0
- data/spec/unit/provider/package/windows/msi_spec.rb +60 -0
- data/spec/unit/provider/package/windows_spec.rb +80 -0
- data/spec/unit/provider/service/macosx_spec.rb +3 -3
- data/spec/unit/provider/service/solaris_smf_service_spec.rb +35 -10
- data/spec/unit/pure_application_spec.rb +32 -0
- data/spec/unit/recipe_spec.rb +4 -0
- data/spec/unit/resource/conditional_spec.rb +13 -12
- data/spec/unit/resource/cron_spec.rb +7 -2
- data/spec/unit/resource/powershell_spec.rb +85 -2
- data/spec/unit/resource/subversion_spec.rb +5 -0
- data/spec/unit/resource/windows_package_spec.rb +74 -0
- data/spec/unit/resource_spec.rb +23 -1
- data/spec/unit/rest_spec.rb +15 -0
- data/spec/unit/run_context/cookbook_compiler_spec.rb +12 -0
- data/spec/unit/run_context_spec.rb +7 -0
- data/spec/unit/util/editor_spec.rb +152 -0
- data/spec/unit/util/file_edit_spec.rb +37 -1
- metadata +41 -30
data/lib/chef/resource/cron.rb
CHANGED
@@ -43,6 +43,7 @@ class Chef
|
|
43
43
|
@path = nil
|
44
44
|
@shell = nil
|
45
45
|
@home = nil
|
46
|
+
@time = nil
|
46
47
|
@environment = {}
|
47
48
|
end
|
48
49
|
|
@@ -121,13 +122,28 @@ class Chef
|
|
121
122
|
converted_arg = arg
|
122
123
|
end
|
123
124
|
begin
|
124
|
-
|
125
|
+
error_message = "You provided '#{arg}' as a weekday, acceptable values are "
|
126
|
+
error_message << Provider::Cron::WEEKDAY_SYMBOLS.map {|sym| ":#{sym.to_s}"}.join(', ')
|
127
|
+
error_message << " and a string in crontab format"
|
128
|
+
if (arg.is_a?(Symbol) && !Provider::Cron::WEEKDAY_SYMBOLS.include?(arg)) ||
|
129
|
+
(!arg.is_a?(Symbol) && integerize(arg) > 7) ||
|
130
|
+
(!arg.is_a?(Symbol) && integerize(arg) < 0)
|
131
|
+
raise RangeError, error_message
|
132
|
+
end
|
125
133
|
rescue ArgumentError
|
126
134
|
end
|
127
135
|
set_or_return(
|
128
136
|
:weekday,
|
129
137
|
converted_arg,
|
130
|
-
:kind_of => String
|
138
|
+
:kind_of => [String, Symbol]
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
def time(arg=nil)
|
143
|
+
set_or_return(
|
144
|
+
:time,
|
145
|
+
arg,
|
146
|
+
:equal_to => Chef::Provider::Cron::SPECIAL_TIME_VALUES
|
131
147
|
)
|
132
148
|
end
|
133
149
|
|
@@ -15,17 +15,39 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
|
19
18
|
require 'chef/resource/windows_script'
|
20
19
|
|
21
20
|
class Chef
|
22
21
|
class Resource
|
23
22
|
class PowershellScript < Chef::Resource::WindowsScript
|
24
23
|
|
24
|
+
set_guard_inherited_attributes(:architecture)
|
25
|
+
|
25
26
|
def initialize(name, run_context=nil)
|
26
27
|
super(name, run_context, :powershell_script, "powershell.exe")
|
28
|
+
@convert_boolean_return = false
|
29
|
+
end
|
30
|
+
|
31
|
+
def convert_boolean_return(arg=nil)
|
32
|
+
set_or_return(
|
33
|
+
:convert_boolean_return,
|
34
|
+
arg,
|
35
|
+
:kind_of => [ FalseClass, TrueClass ]
|
36
|
+
)
|
27
37
|
end
|
28
38
|
|
39
|
+
protected
|
40
|
+
|
41
|
+
# Allow callers evaluating guards to request default
|
42
|
+
# attribute values. This is needed to allow
|
43
|
+
# convert_boolean_return to be true in guard context by default,
|
44
|
+
# and false by default otherwise. When this mode becomes the
|
45
|
+
# default for this resource, this method can be removed since
|
46
|
+
# guard context and recipe resource context will have the
|
47
|
+
# same behavior.
|
48
|
+
def self.get_default_attributes(opts)
|
49
|
+
{:convert_boolean_return => true}
|
50
|
+
end
|
29
51
|
end
|
30
52
|
end
|
31
53
|
end
|
data/lib/chef/resource/script.rb
CHANGED
@@ -58,6 +58,31 @@ class Chef
|
|
58
58
|
)
|
59
59
|
end
|
60
60
|
|
61
|
+
def self.set_guard_inherited_attributes(*inherited_attributes)
|
62
|
+
@class_inherited_attributes = inherited_attributes
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.guard_inherited_attributes(*inherited_attributes)
|
66
|
+
# Similar to patterns elsewhere, return attributes from this
|
67
|
+
# class and superclasses as a form of inheritance
|
68
|
+
ancestor_attributes = []
|
69
|
+
|
70
|
+
if superclass.respond_to?(:guard_inherited_attributes)
|
71
|
+
ancestor_attributes = superclass.guard_inherited_attributes
|
72
|
+
end
|
73
|
+
|
74
|
+
ancestor_attributes.concat(@class_inherited_attributes ? @class_inherited_attributes : []).uniq
|
75
|
+
end
|
76
|
+
|
77
|
+
set_guard_inherited_attributes(
|
78
|
+
:cwd,
|
79
|
+
:environment,
|
80
|
+
:group,
|
81
|
+
:path,
|
82
|
+
:user,
|
83
|
+
:umask
|
84
|
+
)
|
85
|
+
|
61
86
|
end
|
62
87
|
end
|
63
88
|
end
|
@@ -32,6 +32,10 @@ class Chef
|
|
32
32
|
allowed_actions << :force_export
|
33
33
|
end
|
34
34
|
|
35
|
+
# Override exception to strip password if any, so it won't appear in logs and different Chef notifications
|
36
|
+
def custom_exception_message(e)
|
37
|
+
"#{self} (#{defined_at}) had an error: #{e.class.name}: #{svn_password ? e.message.gsub(svn_password, "[hidden_password]") : e.message}"
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Bryan McLellan <btm@loftninjas.org>
|
3
|
+
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/resource/package'
|
20
|
+
require 'chef/provider/package/windows'
|
21
|
+
require 'chef/win32/error' if RUBY_PLATFORM =~ /mswin|mingw|windows/
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Resource
|
25
|
+
class WindowsPackage < Chef::Resource::Package
|
26
|
+
|
27
|
+
provides :package, :on_platforms => ["windows"]
|
28
|
+
|
29
|
+
def initialize(name, run_context=nil)
|
30
|
+
super
|
31
|
+
@allowed_actions = [ :install, :remove ]
|
32
|
+
@provider = Chef::Provider::Package::Windows
|
33
|
+
@resource_name = :windows_package
|
34
|
+
@source ||= source(@package_name)
|
35
|
+
|
36
|
+
# Unique to this resource
|
37
|
+
@installer_type = nil
|
38
|
+
@timeout = 600
|
39
|
+
# In the past we accepted return code 127 for an unknown reason and 42 because of a bug
|
40
|
+
@returns = [ 0 ]
|
41
|
+
end
|
42
|
+
|
43
|
+
def installer_type(arg=nil)
|
44
|
+
set_or_return(
|
45
|
+
:installer_type,
|
46
|
+
arg,
|
47
|
+
:kind_of => [ String ]
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def timeout(arg=nil)
|
52
|
+
set_or_return(
|
53
|
+
:timeout,
|
54
|
+
arg,
|
55
|
+
:kind_of => [ String, Integer ]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def returns(arg=nil)
|
60
|
+
set_or_return(
|
61
|
+
:returns,
|
62
|
+
arg,
|
63
|
+
:kind_of => [ String, Integer, Array ]
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def source(arg=nil)
|
68
|
+
if arg == nil && self.instance_variable_defined?(:@source) == true
|
69
|
+
@source
|
70
|
+
else
|
71
|
+
raise ArgumentError, "Bad type for WindowsPackage resource, use a String" unless arg.is_a?(String)
|
72
|
+
Chef::Log.debug("#{package_name}: sanitizing source path '#{arg}'")
|
73
|
+
@source = ::File.absolute_path(arg).gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
@@ -52,11 +52,6 @@ class Chef
|
|
52
52
|
"cannot execute script with requested architecture '#{desired_architecture.to_s}' on a system with architecture '#{node_windows_architecture(node)}'"
|
53
53
|
end
|
54
54
|
end
|
55
|
-
|
56
|
-
def node
|
57
|
-
run_context && run_context.node
|
58
|
-
end
|
59
|
-
|
60
55
|
end
|
61
56
|
end
|
62
57
|
end
|
data/lib/chef/resources.rb
CHANGED
@@ -69,6 +69,7 @@ require 'chef/resource/template'
|
|
69
69
|
require 'chef/resource/timestamped_deploy'
|
70
70
|
require 'chef/resource/user'
|
71
71
|
require 'chef/resource/whyrun_safe_ruby_block'
|
72
|
+
require 'chef/resource/windows_package'
|
72
73
|
require 'chef/resource/yum_package'
|
73
74
|
require 'chef/resource/lwrp_base'
|
74
75
|
require 'chef/resource/bff_package'
|
data/lib/chef/rest.rb
CHANGED
@@ -57,6 +57,7 @@ class Chef
|
|
57
57
|
# http://localhost:4000, a call to +get_rest+ with 'nodes' will make an
|
58
58
|
# HTTP GET request to http://localhost:4000/nodes
|
59
59
|
def initialize(url, client_name=Chef::Config[:node_name], signing_key_filename=Chef::Config[:client_key], options={})
|
60
|
+
options = options.dup
|
60
61
|
options[:client_name] = client_name
|
61
62
|
options[:signing_key_filename] = signing_key_filename
|
62
63
|
super(url, options)
|
@@ -65,7 +66,6 @@ class Chef
|
|
65
66
|
@authenticator = Authenticator.new(options)
|
66
67
|
@request_id = RemoteRequestID.new(options)
|
67
68
|
|
68
|
-
@middlewares << ValidateContentLength.new(options)
|
69
69
|
@middlewares << JSONInput.new(options)
|
70
70
|
@middlewares << JSONToModelOutput.new(options)
|
71
71
|
@middlewares << CookieManager.new(options)
|
@@ -73,6 +73,11 @@ class Chef
|
|
73
73
|
@middlewares << @authenticator
|
74
74
|
@middlewares << @request_id
|
75
75
|
|
76
|
+
# ValidateContentLength should come after Decompressor
|
77
|
+
# because the order of middlewares is reversed when handling
|
78
|
+
# responses.
|
79
|
+
@middlewares << ValidateContentLength.new(options)
|
80
|
+
|
76
81
|
end
|
77
82
|
|
78
83
|
def signing_key_filename
|
data/lib/chef/run_context.rb
CHANGED
@@ -77,13 +77,15 @@ class Chef
|
|
77
77
|
@events = events
|
78
78
|
|
79
79
|
@node.run_context = self
|
80
|
+
|
81
|
+
@cookbook_compiler = nil
|
80
82
|
end
|
81
83
|
|
82
84
|
# Triggers the compile phase of the chef run. Implemented by
|
83
85
|
# Chef::RunContext::CookbookCompiler
|
84
86
|
def load(run_list_expansion)
|
85
|
-
|
86
|
-
|
87
|
+
@cookbook_compiler = CookbookCompiler.new(self, run_list_expansion, events)
|
88
|
+
@cookbook_compiler.compile
|
87
89
|
end
|
88
90
|
|
89
91
|
# Adds an immediate notification to the
|
@@ -141,6 +143,18 @@ class Chef
|
|
141
143
|
Chef::Log.debug("Loading Recipe #{recipe_name} via include_recipe")
|
142
144
|
|
143
145
|
cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name)
|
146
|
+
|
147
|
+
if unreachable_cookbook?(cookbook_name) # CHEF-4367
|
148
|
+
Chef::Log.warn(<<-ERROR_MESSAGE)
|
149
|
+
MissingCookbookDependency:
|
150
|
+
Recipe `#{recipe_name}` is not in the run_list, and cookbook '#{cookbook_name}'
|
151
|
+
is not a dependency of any cookbook in the run_list. To load this recipe,
|
152
|
+
first add a dependency on cookbook '#{cookbook_name}' in the cookbook you're
|
153
|
+
including it from in that cookbook's metadata.
|
154
|
+
ERROR_MESSAGE
|
155
|
+
end
|
156
|
+
|
157
|
+
|
144
158
|
if loaded_fully_qualified_recipe?(cookbook_name, recipe_short_name)
|
145
159
|
Chef::Log.debug("I am not loading #{recipe_name}, because I have already seen it.")
|
146
160
|
false
|
@@ -228,6 +242,12 @@ class Chef
|
|
228
242
|
cookbook.has_cookbook_file_for_node?(node, cb_file_name)
|
229
243
|
end
|
230
244
|
|
245
|
+
# Delegates to CookbookCompiler#unreachable_cookbook?
|
246
|
+
# Used to raise an error when attempting to load a recipe belonging to a
|
247
|
+
# cookbook that is not in the dependency graph. See also: CHEF-4367
|
248
|
+
def unreachable_cookbook?(cookbook_name)
|
249
|
+
@cookbook_compiler.unreachable_cookbook?(cookbook_name)
|
250
|
+
end
|
231
251
|
|
232
252
|
private
|
233
253
|
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
+
require 'set'
|
19
20
|
require 'chef/log'
|
20
21
|
require 'chef/recipe'
|
21
22
|
require 'chef/resource/lwrp_base'
|
@@ -149,6 +150,17 @@ class Chef
|
|
149
150
|
@events.recipe_load_complete
|
150
151
|
end
|
151
152
|
|
153
|
+
# Whether or not a cookbook is reachable from the set of cookbook given
|
154
|
+
# by the run_list plus those cookbooks' dependencies.
|
155
|
+
def unreachable_cookbook?(cookbook_name)
|
156
|
+
!reachable_cookbooks.include?(cookbook_name)
|
157
|
+
end
|
158
|
+
|
159
|
+
# All cookbooks in the dependency graph, returned as a Set.
|
160
|
+
def reachable_cookbooks
|
161
|
+
@reachable_cookbooks ||= Set.new(cookbook_order)
|
162
|
+
end
|
163
|
+
|
152
164
|
private
|
153
165
|
|
154
166
|
def load_attributes_from_cookbook(cookbook_name)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Chris Bandy (<bandy.chris@gmail.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
class Chef
|
20
|
+
class Util
|
21
|
+
class Editor
|
22
|
+
attr_reader :lines
|
23
|
+
|
24
|
+
def initialize(lines)
|
25
|
+
@lines = lines.to_a.clone
|
26
|
+
end
|
27
|
+
|
28
|
+
def append_line_after(search, line_to_append)
|
29
|
+
lines = []
|
30
|
+
|
31
|
+
@lines.each do |line|
|
32
|
+
lines << line
|
33
|
+
lines << line_to_append if line.match(search)
|
34
|
+
end
|
35
|
+
|
36
|
+
(lines.length - @lines.length).tap { @lines = lines }
|
37
|
+
end
|
38
|
+
|
39
|
+
def append_line_if_missing(search, line_to_append)
|
40
|
+
count = 0
|
41
|
+
|
42
|
+
unless @lines.find { |line| line.match(search) }
|
43
|
+
count = 1
|
44
|
+
@lines << line_to_append
|
45
|
+
end
|
46
|
+
|
47
|
+
count
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove_lines(search)
|
51
|
+
count = 0
|
52
|
+
|
53
|
+
@lines.delete_if do |line|
|
54
|
+
count += 1 if line.match(search)
|
55
|
+
end
|
56
|
+
|
57
|
+
count
|
58
|
+
end
|
59
|
+
|
60
|
+
def replace(search, replace)
|
61
|
+
count = 0
|
62
|
+
|
63
|
+
@lines.map! do |line|
|
64
|
+
if line.match(search)
|
65
|
+
count += 1
|
66
|
+
line.gsub!(search, replace)
|
67
|
+
else
|
68
|
+
line
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
count
|
73
|
+
end
|
74
|
+
|
75
|
+
def replace_lines(search, replace)
|
76
|
+
count = 0
|
77
|
+
|
78
|
+
@lines.map! do |line|
|
79
|
+
if line.match(search)
|
80
|
+
count += 1
|
81
|
+
replace
|
82
|
+
else
|
83
|
+
line
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
count
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
data/lib/chef/util/file_edit.rb
CHANGED
@@ -15,8 +15,8 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
|
+
require 'chef/util/editor'
|
18
19
|
require 'fileutils'
|
19
|
-
require 'tempfile'
|
20
20
|
|
21
21
|
class Chef
|
22
22
|
class Util
|
@@ -24,108 +24,76 @@ class Chef
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
|
27
|
+
attr_reader :editor, :original_pathname
|
28
28
|
|
29
29
|
public
|
30
30
|
|
31
31
|
def initialize(filepath)
|
32
|
+
raise ArgumentError, "File '#{filepath}' does not exist" unless File.exist?(filepath)
|
33
|
+
@editor = Editor.new(File.open(filepath, &:readlines))
|
32
34
|
@original_pathname = filepath
|
33
35
|
@file_edited = false
|
36
|
+
end
|
34
37
|
|
35
|
-
|
36
|
-
|
38
|
+
# return if file has been edited
|
39
|
+
def file_edited?
|
40
|
+
@file_edited
|
37
41
|
end
|
38
42
|
|
39
43
|
#search the file line by line and match each line with the given regex
|
40
44
|
#if matched, replace the whole line with newline.
|
41
45
|
def search_file_replace_line(regex, newline)
|
42
|
-
|
46
|
+
@changes = (editor.replace_lines(regex, newline) > 0) || @changes
|
43
47
|
end
|
44
48
|
|
45
49
|
#search the file line by line and match each line with the given regex
|
46
50
|
#if matched, replace the match (all occurances) with the replace parameter
|
47
51
|
def search_file_replace(regex, replace)
|
48
|
-
|
52
|
+
@changes = (editor.replace(regex, replace) > 0) || @changes
|
49
53
|
end
|
50
54
|
|
51
55
|
#search the file line by line and match each line with the given regex
|
52
56
|
#if matched, delete the line
|
53
57
|
def search_file_delete_line(regex)
|
54
|
-
|
58
|
+
@changes = (editor.remove_lines(regex) > 0) || @changes
|
55
59
|
end
|
56
60
|
|
57
61
|
#search the file line by line and match each line with the given regex
|
58
62
|
#if matched, delete the match (all occurances) from the line
|
59
63
|
def search_file_delete(regex)
|
60
|
-
|
64
|
+
search_file_replace(regex, '')
|
61
65
|
end
|
62
66
|
|
63
67
|
#search the file line by line and match each line with the given regex
|
64
68
|
#if matched, insert newline after each matching line
|
65
69
|
def insert_line_after_match(regex, newline)
|
66
|
-
|
70
|
+
@changes = (editor.append_line_after(regex, newline) > 0) || @changes
|
67
71
|
end
|
68
72
|
|
69
73
|
#search the file line by line and match each line with the given regex
|
70
74
|
#if not matched, insert newline at the end of the file
|
71
75
|
def insert_line_if_no_match(regex, newline)
|
72
|
-
|
76
|
+
@changes = (editor.append_line_if_missing(regex, newline) > 0) || @changes
|
77
|
+
end
|
78
|
+
|
79
|
+
def unwritten_changes?
|
80
|
+
!!@changes
|
73
81
|
end
|
74
82
|
|
75
83
|
#Make a copy of old_file and write new file out (only if file changed)
|
76
84
|
def write_file
|
77
|
-
|
78
|
-
# file_edited is false when there was no match in the whole file and thus no contents have changed.
|
79
|
-
if file_edited
|
85
|
+
if @changes
|
80
86
|
backup_pathname = original_pathname + ".old"
|
81
87
|
FileUtils.cp(original_pathname, backup_pathname, :preserve => true)
|
82
88
|
File.open(original_pathname, "w") do |newfile|
|
83
|
-
|
89
|
+
editor.lines.each do |line|
|
84
90
|
newfile.puts(line)
|
85
91
|
end
|
86
92
|
newfile.flush
|
87
93
|
end
|
94
|
+
@file_edited = true
|
88
95
|
end
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
#helper method to do the match, replace, delete, and insert operations
|
95
|
-
#command is the switch of delete, replace, and insert ('d', 'r', 'i')
|
96
|
-
#method is to control operation on whole line or only the match (1 for line, 2 for match)
|
97
|
-
def search_match(regex, replace, command, method)
|
98
|
-
|
99
|
-
#convert regex to a Regexp object (if not already is one) and store it in exp.
|
100
|
-
exp = Regexp.new(regex)
|
101
|
-
|
102
|
-
#loop through contents and do the appropriate operation depending on 'command' and 'method'
|
103
|
-
new_contents = []
|
104
|
-
|
105
|
-
contents.each do |line|
|
106
|
-
if line.match(exp)
|
107
|
-
self.file_edited = true
|
108
|
-
case
|
109
|
-
when command == 'r'
|
110
|
-
new_contents << ((method == 1) ? replace : line.gsub!(exp, replace))
|
111
|
-
when command == 'd'
|
112
|
-
if method == 2
|
113
|
-
new_contents << line.gsub!(exp, "")
|
114
|
-
end
|
115
|
-
when command == 'i'
|
116
|
-
new_contents << line
|
117
|
-
new_contents << replace unless method == 2
|
118
|
-
end
|
119
|
-
else
|
120
|
-
new_contents << line
|
121
|
-
end
|
122
|
-
end
|
123
|
-
if command == 'i' && method == 2 && ! file_edited
|
124
|
-
new_contents << replace
|
125
|
-
self.file_edited = true
|
126
|
-
end
|
127
|
-
|
128
|
-
self.contents = new_contents
|
96
|
+
@changes = false
|
129
97
|
end
|
130
98
|
end
|
131
99
|
end
|