chef-apply 0.3.3 → 0.4.6
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.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/chef-apply.gemspec +2 -1
- data/lib/chef_apply/action/base.rb +1 -0
- data/lib/chef_apply/action/converge_target.rb +11 -11
- data/lib/chef_apply/action/converge_target/ccr_failure_mapper.rb +100 -0
- data/lib/chef_apply/action/generate_local_policy.rb +1 -1
- data/lib/chef_apply/action/generate_temp_cookbook.rb +53 -53
- data/lib/chef_apply/action/generate_temp_cookbook/recipe_lookup.rb +124 -0
- data/lib/chef_apply/action/generate_temp_cookbook/temp_cookbook.rb +175 -0
- data/lib/chef_apply/action/install_chef.rb +8 -8
- data/lib/chef_apply/action/install_chef/minimum_chef_version.rb +85 -0
- data/lib/chef_apply/cli.rb +10 -9
- data/lib/chef_apply/cli/options.rb +2 -2
- data/lib/chef_apply/cli/validation.rb +2 -1
- data/lib/chef_apply/file_fetcher.rb +1 -1
- data/lib/chef_apply/log.rb +4 -6
- data/lib/chef_apply/startup.rb +7 -4
- data/lib/chef_apply/target_host.rb +10 -5
- data/lib/chef_apply/target_host/linux.rb +1 -1
- data/lib/chef_apply/target_resolver.rb +3 -1
- data/lib/chef_apply/telemeter.rb +1 -1
- data/lib/chef_apply/text/error_translation.rb +1 -1
- data/lib/chef_apply/text/text_wrapper.rb +2 -1
- data/lib/chef_apply/ui/error_printer.rb +15 -13
- data/lib/chef_apply/ui/plain_text_element.rb +1 -2
- data/lib/chef_apply/ui/plain_text_header.rb +1 -1
- data/lib/chef_apply/ui/terminal.rb +4 -4
- data/lib/chef_apply/version.rb +1 -1
- data/spec/spec_helper.rb +43 -3
- data/spec/unit/action/base_spec.rb +2 -1
- data/spec/unit/{errors → action/converge_target}/ccr_failure_mapper_spec.rb +12 -9
- data/spec/unit/action/converge_target_spec.rb +21 -22
- data/spec/unit/action/generate_local_policy_spec.rb +5 -5
- data/spec/unit/{recipe_lookup_spec.rb → action/generate_temp_cookbook/recipe_lookup_spec.rb} +10 -10
- data/spec/unit/{temp_cookbook_spec.rb → action/generate_temp_cookbook/temp_cookbook_spec.rb} +23 -24
- data/spec/unit/action/generate_temp_cookbook_spec.rb +4 -6
- data/spec/unit/{minimum_chef_version_spec.rb → action/install_chef/minimum_chef_version_spec.rb} +13 -13
- data/spec/unit/action/install_chef_spec.rb +4 -4
- data/spec/unit/cli/options_spec.rb +17 -17
- data/spec/unit/cli/validation_spec.rb +6 -3
- data/spec/unit/cli_spec.rb +12 -9
- data/spec/unit/log_spec.rb +1 -1
- data/spec/unit/startup_spec.rb +11 -12
- data/spec/unit/target_host/windows_spec.rb +1 -1
- data/spec/unit/target_host_spec.rb +3 -2
- data/spec/unit/telemeter_spec.rb +5 -5
- data/spec/unit/text/error_translation_spec.rb +11 -7
- data/spec/unit/ui/error_printer_spec.rb +6 -7
- metadata +24 -11
- data/Gemfile.lock +0 -400
- data/lib/chef_apply/errors/ccr_failure_mapper.rb +0 -93
- data/lib/chef_apply/minimum_chef_version.rb +0 -79
- data/lib/chef_apply/recipe_lookup.rb +0 -117
- data/lib/chef_apply/temp_cookbook.rb +0 -170
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79e301aab0d20ec072e87597869b55d24958945f0fb1cfa774841929cb5eaa80
|
4
|
+
data.tar.gz: e34804469b8ed16dfc9729f3229abc60d0714358146f04ca3a494a70dcef6a96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 251203d054f4ee1c8319b4b12e40a2778008f603e79530750875686facc0b2a8ec72e40425a1f643e91187882224b04af6a0c6d36698f7d341a35e104ed2c036
|
7
|
+
data.tar.gz: 107872f223933ca095df7e46afbf22f1260c05339531fb8c51c2e54007203e63f4e4ea4089f30abd75e2a20bd09bd1d4e56d44d21fb278e5e36e04d5db655ff7
|
data/Rakefile
CHANGED
data/chef-apply.gemspec
CHANGED
@@ -48,7 +48,8 @@ Gem::Specification.new do |spec|
|
|
48
48
|
# localization gem...
|
49
49
|
spec.add_dependency "toml-rb" # This isn't ideal because mixlib-config uses 'tomlrb'
|
50
50
|
# but that library does not support a dumper
|
51
|
-
spec.add_dependency "train" # remote connection management over ssh, winrm
|
51
|
+
spec.add_dependency "train", "~> 3.0" # remote connection management over ssh, winrm
|
52
|
+
spec.add_dependency "train-winrm" # winrm transports were pulled out into this plugin
|
52
53
|
spec.add_dependency "pastel" # A color library
|
53
54
|
spec.add_dependency "tty-spinner" # Pretty output for status updates in the CLI
|
54
55
|
spec.add_dependency "chef", ">= 15.0" # Needed to load cookbooks
|
@@ -26,7 +26,7 @@ module ChefApply::Action
|
|
26
26
|
|
27
27
|
def perform_action
|
28
28
|
local_policy_path = config.delete :local_policy_path
|
29
|
-
remote_tmp = target_host.temp_dir
|
29
|
+
remote_tmp = target_host.temp_dir
|
30
30
|
remote_dir_path = target_host.normalize_path(remote_tmp)
|
31
31
|
# Ensure the directory is owned by the connecting user,
|
32
32
|
# otherwise we won't be able to put things into it over scp as that user.
|
@@ -38,8 +38,8 @@ module ChefApply::Action
|
|
38
38
|
notify(:running_chef)
|
39
39
|
# TODO - just teach target_host how to run_chef?
|
40
40
|
cmd_str = run_chef_cmd(remote_dir_path,
|
41
|
-
|
42
|
-
|
41
|
+
File.basename(remote_config_path),
|
42
|
+
File.basename(remote_policy_path))
|
43
43
|
c = target_host.run_command(cmd_str)
|
44
44
|
target_host.del_dir(remote_dir_path)
|
45
45
|
if c.exit_status == 0
|
@@ -52,7 +52,7 @@ module ChefApply::Action
|
|
52
52
|
ChefApply::Log.error("Error running command [#{cmd_str}]")
|
53
53
|
ChefApply::Log.error("stdout: #{c.stdout}")
|
54
54
|
ChefApply::Log.error("stderr: #{c.stderr}")
|
55
|
-
handle_ccr_error
|
55
|
+
handle_ccr_error
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -63,7 +63,7 @@ module ChefApply::Action
|
|
63
63
|
target_host.upload_file(local_policy_path, remote_policy_path)
|
64
64
|
rescue RuntimeError => e
|
65
65
|
ChefApply::Log.error(e)
|
66
|
-
raise PolicyUploadFailed.new
|
66
|
+
raise PolicyUploadFailed.new
|
67
67
|
end
|
68
68
|
remote_policy_path
|
69
69
|
end
|
@@ -92,7 +92,7 @@ module ChefApply::Action
|
|
92
92
|
# (we don't set a location because we want output to
|
93
93
|
# go in stdout for reporting back to chef-apply)
|
94
94
|
log_settings = ChefApply::Config.log
|
95
|
-
|
95
|
+
unless log_settings.target_level.nil?
|
96
96
|
workstation_rb << <<~EOM
|
97
97
|
log_level :#{log_settings.target_level}
|
98
98
|
EOM
|
@@ -115,7 +115,7 @@ module ChefApply::Action
|
|
115
115
|
config_file.close
|
116
116
|
target_host.upload_file(config_file.path, remote_config_path)
|
117
117
|
rescue RuntimeError
|
118
|
-
raise ConfigUploadFailed.new
|
118
|
+
raise ConfigUploadFailed.new
|
119
119
|
ensure
|
120
120
|
config_file.unlink
|
121
121
|
end
|
@@ -126,14 +126,14 @@ module ChefApply::Action
|
|
126
126
|
remote_handler_path = File.join(remote_dir, "reporter.rb")
|
127
127
|
begin
|
128
128
|
# TODO - why don't we upload the original remote_handler_path instead of making a temp copy?
|
129
|
-
handler_file = Tempfile.new
|
129
|
+
handler_file = Tempfile.new
|
130
130
|
# TODO - ideally this is a resource in the gem, and not placed in with source files.
|
131
131
|
handler_file.write(File.read(File.join(__dir__, "reporter.rb")))
|
132
132
|
handler_file.close
|
133
133
|
target_host.upload_file(handler_file.path, remote_handler_path)
|
134
134
|
# TODO - should we be more specific in our error catch?
|
135
135
|
rescue RuntimeError
|
136
|
-
raise HandlerUploadFailed.new
|
136
|
+
raise HandlerUploadFailed.new
|
137
137
|
ensure
|
138
138
|
handler_file.unlink
|
139
139
|
end
|
@@ -159,7 +159,7 @@ module ChefApply::Action
|
|
159
159
|
end
|
160
160
|
|
161
161
|
def handle_ccr_error
|
162
|
-
require "chef_apply/
|
162
|
+
require "chef_apply/action/converge_target/ccr_failure_mapper"
|
163
163
|
mapper_opts = {}
|
164
164
|
content = target_host.fetch_file_contents(chef_report_path)
|
165
165
|
if content.nil?
|
@@ -175,7 +175,7 @@ module ChefApply::Action
|
|
175
175
|
ChefApply::Log.error("Remote chef-client error follows:")
|
176
176
|
ChefApply::Log.error(report["exception"])
|
177
177
|
end
|
178
|
-
mapper =
|
178
|
+
mapper = ConvergeTarget::CCRFailureMapper.new(report["exception"], mapper_opts)
|
179
179
|
mapper.raise_mapped_exception!
|
180
180
|
end
|
181
181
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2017 Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
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_apply/error"
|
19
|
+
|
20
|
+
module ChefApply
|
21
|
+
module Action
|
22
|
+
class ConvergeTarget
|
23
|
+
# This converts chef client run failures
|
24
|
+
# to human-friendly exceptions with detail
|
25
|
+
# and remediation steps based on the failure type.
|
26
|
+
class CCRFailureMapper
|
27
|
+
attr_reader :params
|
28
|
+
|
29
|
+
def initialize(exception, params)
|
30
|
+
@params = params
|
31
|
+
@cause_line = exception
|
32
|
+
end
|
33
|
+
|
34
|
+
def raise_mapped_exception!
|
35
|
+
if @cause_line.nil?
|
36
|
+
raise RemoteChefRunFailedToResolveError.new(params[:failed_report_path])
|
37
|
+
else
|
38
|
+
errid, *args = exception_args_from_cause
|
39
|
+
if errid.nil?
|
40
|
+
raise RemoteChefClientRunFailedUnknownReason.new
|
41
|
+
else
|
42
|
+
raise RemoteChefClientRunFailed.new(errid, *args)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Ideally we will write a custom handler to package up data we care
|
49
|
+
# about and present it more directly https://docs.chef.io/handlers.html
|
50
|
+
# For now, we'll just match the most common failures based on their
|
51
|
+
# messages.
|
52
|
+
def exception_args_from_cause
|
53
|
+
# Ordering is important below. Some earlier tests are more detailed
|
54
|
+
# cases of things that will match more general tests further down.
|
55
|
+
case @cause_line
|
56
|
+
when /.*had an error:(.*:)\s+(.*$)/
|
57
|
+
# Some invalid property value cases, among others.
|
58
|
+
["CHEFCCR002", $2]
|
59
|
+
when /.*Chef::Exceptions::ValidationFailed:\s+Option action must be equal to one of:\s+(.*)!\s+You passed :(.*)\./
|
60
|
+
# Invalid action - specialization of invalid property value, below
|
61
|
+
["CHEFCCR003", $2, $1]
|
62
|
+
when /.*Chef::Exceptions::ValidationFailed:\s+(.*)/
|
63
|
+
# Invalid resource property value
|
64
|
+
["CHEFCCR004", $1]
|
65
|
+
when /.*NameError: undefined local variable or method `(.+)' for cookbook.+/
|
66
|
+
# Invalid resource type in most cases
|
67
|
+
["CHEFCCR005", $1]
|
68
|
+
when /.*NoMethodError: undefined method `(.+)' for cookbook.+/
|
69
|
+
# Invalid resource type in most cases
|
70
|
+
["CHEFCCR005", $1]
|
71
|
+
when /.*undefined method `(.*)' for (.+)/
|
72
|
+
# Unknown resource property
|
73
|
+
["CHEFCCR006", $1, $2]
|
74
|
+
|
75
|
+
# Below would catch the general form of most errors, but the
|
76
|
+
# message itself in those lines is not generally aligned
|
77
|
+
# with the UX we want to provide.
|
78
|
+
# when /.*Exception|Error.*:\s+(.*)/
|
79
|
+
else
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class RemoteChefClientRunFailed < ChefApply::ErrorNoLogs
|
85
|
+
def initialize(id, *args); super(id, *args); end
|
86
|
+
end
|
87
|
+
|
88
|
+
class RemoteChefClientRunFailedUnknownReason < ChefApply::ErrorNoStack
|
89
|
+
def initialize(); super("CHEFCCR099"); end
|
90
|
+
end
|
91
|
+
|
92
|
+
class RemoteChefRunFailedToResolveError < ChefApply::ErrorNoStack
|
93
|
+
def initialize(path); super("CHEFCCR001", path); end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -49,7 +49,7 @@ module ChefApply::Action
|
|
49
49
|
require "chef-cli/policyfile_services/install"
|
50
50
|
require "chef-cli/ui"
|
51
51
|
@installer ||=
|
52
|
-
ChefCLI::PolicyfileServices::Install.new(ui: ChefCLI::UI.null
|
52
|
+
ChefCLI::PolicyfileServices::Install.new(ui: ChefCLI::UI.null, root_dir: @cookbook.path)
|
53
53
|
end
|
54
54
|
|
55
55
|
end
|
@@ -14,73 +14,73 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
#
|
17
|
-
|
18
17
|
require "chef_apply/action/base"
|
19
|
-
require "chef_apply/temp_cookbook"
|
20
18
|
require "chef_apply/error"
|
19
|
+
module ChefApply
|
20
|
+
module Action
|
21
|
+
class GenerateTempCookbook < Base
|
22
|
+
attr_reader :generated_cookbook
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
def self.from_options(opts)
|
25
|
+
if opts.key?(:recipe_spec)
|
26
|
+
GenerateCookbookFromRecipe.new(opts)
|
27
|
+
elsif opts.key?(:resource_name) &&
|
28
|
+
opts.key?(:resource_type) &&
|
29
|
+
opts.key?(:resource_properties)
|
30
|
+
GenerateCookbookFromResource.new(opts)
|
31
|
+
else
|
32
|
+
raise MissingOptions.new(opts)
|
33
|
+
end
|
34
|
+
end
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
opts.key?(:resource_type) &&
|
31
|
-
opts.key?(:resource_properties)
|
32
|
-
GenerateCookbookFromResource.new(opts)
|
33
|
-
else
|
34
|
-
raise MissingOptions.new(opts)
|
36
|
+
def initialize(options)
|
37
|
+
super(options)
|
38
|
+
require "chef_apply/action/generate_temp_cookbook/temp_cookbook"
|
39
|
+
@generated_cookbook ||= TempCookbook.new
|
35
40
|
end
|
36
|
-
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
+
def perform_action
|
43
|
+
notify(:generating)
|
44
|
+
generate
|
45
|
+
notify(:success)
|
46
|
+
end
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
notify(:success)
|
48
|
+
def generate
|
49
|
+
raise NotImplemented
|
50
|
+
end
|
47
51
|
end
|
48
52
|
|
49
|
-
|
50
|
-
|
53
|
+
class GenerateCookbookFromRecipe < GenerateTempCookbook
|
54
|
+
def generate
|
55
|
+
recipe_specifier = config.delete :recipe_spec
|
56
|
+
repo_paths = config.delete :cookbook_repo_paths
|
57
|
+
ChefApply::Log.debug("Beginning to look for recipe specified as #{recipe_specifier}")
|
58
|
+
if File.file?(recipe_specifier)
|
59
|
+
ChefApply::Log.debug("#{recipe_specifier} is a valid path to a recipe")
|
60
|
+
recipe_path = recipe_specifier
|
61
|
+
else
|
62
|
+
require "chef_apply/action/generate_temp_cookbook/recipe_lookup"
|
63
|
+
rl = RecipeLookup.new(repo_paths)
|
64
|
+
cookbook_path_or_name, optional_recipe_name = rl.split(recipe_specifier)
|
65
|
+
cookbook = rl.load_cookbook(cookbook_path_or_name)
|
66
|
+
recipe_path = rl.find_recipe(cookbook, optional_recipe_name)
|
67
|
+
end
|
68
|
+
generated_cookbook.from_existing_recipe(recipe_path)
|
69
|
+
end
|
51
70
|
end
|
52
|
-
end
|
53
71
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
recipe_path = recipe_specifier
|
62
|
-
else
|
63
|
-
require "chef_apply/recipe_lookup"
|
64
|
-
rl = ChefApply::RecipeLookup.new(repo_paths)
|
65
|
-
cookbook_path_or_name, optional_recipe_name = rl.split(recipe_specifier)
|
66
|
-
cookbook = rl.load_cookbook(cookbook_path_or_name)
|
67
|
-
recipe_path = rl.find_recipe(cookbook, optional_recipe_name)
|
72
|
+
class GenerateCookbookFromResource < GenerateTempCookbook
|
73
|
+
def generate
|
74
|
+
type = config.delete :resource_type
|
75
|
+
name = config.delete :resource_name
|
76
|
+
props = config.delete :resource_properties
|
77
|
+
ChefApply::Log.debug("Generating cookbook for ad-hoc resource #{type}[#{name}]")
|
78
|
+
generated_cookbook.from_resource(type, name, props)
|
68
79
|
end
|
69
|
-
generated_cookbook.from_existing_recipe(recipe_path)
|
70
80
|
end
|
71
|
-
end
|
72
81
|
|
73
|
-
|
74
|
-
|
75
|
-
type = config.delete :resource_type
|
76
|
-
name = config.delete :resource_name
|
77
|
-
props = config.delete :resource_properties
|
78
|
-
ChefApply::Log.debug("Generating cookbook for ad-hoc resource #{type}[#{name}]")
|
79
|
-
generated_cookbook.from_resource(type, name, props)
|
82
|
+
class MissingOptions < ChefApply::APIError
|
83
|
+
def initialize(*args); super("CHEFAPI001", *args); end
|
80
84
|
end
|
81
85
|
end
|
82
|
-
|
83
|
-
class MissingOptions < ChefApply::APIError
|
84
|
-
def initialize(*args); super("CHEFAPI001", *args); end
|
85
|
-
end
|
86
86
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2017 Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
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-config/config"
|
19
|
+
require "chef_apply/config"
|
20
|
+
require "chef_apply/error"
|
21
|
+
require "chef_apply/log"
|
22
|
+
require "chef_apply/action/base"
|
23
|
+
|
24
|
+
module ChefApply
|
25
|
+
module Action
|
26
|
+
class GenerateTempCookbook
|
27
|
+
# When users are trying to converge a local recipe on a remote target, there
|
28
|
+
# is a very specific (but expansive) set of things they can specify. This
|
29
|
+
# class encapsulates that logic for testing purposes. We either return
|
30
|
+
# a path to a recipe or we raise an error.
|
31
|
+
class RecipeLookup
|
32
|
+
|
33
|
+
attr_reader :cookbook_repo_paths
|
34
|
+
def initialize(cookbook_repo_paths)
|
35
|
+
@cookbook_repo_paths = cookbook_repo_paths
|
36
|
+
end
|
37
|
+
|
38
|
+
# The recipe specifier is provided by the customer as either a path OR
|
39
|
+
# a cookbook and optional recipe name.
|
40
|
+
def split(recipe_specifier)
|
41
|
+
recipe_specifier.split("::")
|
42
|
+
end
|
43
|
+
|
44
|
+
# Given a cookbook path or name, try to load that cookbook. Either return
|
45
|
+
# a cookbook object or raise an error.
|
46
|
+
def load_cookbook(path_or_name)
|
47
|
+
require "chef/exceptions"
|
48
|
+
if File.directory?(path_or_name)
|
49
|
+
cookbook_path = path_or_name
|
50
|
+
# First, is there a cookbook in the specified dir that matches?
|
51
|
+
require "chef/cookbook/cookbook_version_loader"
|
52
|
+
begin
|
53
|
+
v = Chef::Cookbook::CookbookVersionLoader.new(cookbook_path)
|
54
|
+
v.load!
|
55
|
+
cookbook = v.cookbook_version
|
56
|
+
rescue Chef::Exceptions::CookbookNotFoundInRepo
|
57
|
+
raise InvalidCookbook.new(cookbook_path)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
cookbook_name = path_or_name
|
61
|
+
# Second, is there a cookbook in their local repository that matches?
|
62
|
+
require "chef/cookbook_loader"
|
63
|
+
cb_loader = Chef::CookbookLoader.new(cookbook_repo_paths)
|
64
|
+
cb_loader.load_cookbooks
|
65
|
+
|
66
|
+
begin
|
67
|
+
cookbook = cb_loader[cookbook_name]
|
68
|
+
rescue Chef::Exceptions::CookbookNotFoundInRepo
|
69
|
+
cookbook_repo_paths.each do |repo_path|
|
70
|
+
cookbook_path = File.join(repo_path, cookbook_name)
|
71
|
+
if File.directory?(cookbook_path)
|
72
|
+
raise InvalidCookbook.new(cookbook_path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
raise CookbookNotFound.new(cookbook_name, cookbook_repo_paths)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
cookbook
|
79
|
+
end
|
80
|
+
|
81
|
+
# Find the specified recipe or default recipe if none is specified.
|
82
|
+
# Raise an error if recipe cannot be found.
|
83
|
+
def find_recipe(cookbook, recipe_name = nil)
|
84
|
+
recipes = cookbook.recipe_filenames_by_name
|
85
|
+
if recipe_name.nil?
|
86
|
+
default_recipe = recipes["default"]
|
87
|
+
raise NoDefaultRecipe.new(cookbook.root_dir, cookbook.name) if default_recipe.nil?
|
88
|
+
|
89
|
+
default_recipe
|
90
|
+
else
|
91
|
+
recipe = recipes[recipe_name]
|
92
|
+
raise RecipeNotFound.new(cookbook.root_dir, recipe_name, recipes.keys, cookbook.name) if recipe.nil?
|
93
|
+
|
94
|
+
recipe
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class InvalidCookbook < ChefApply::Error
|
99
|
+
def initialize(cookbook_path); super("CHEFVAL005", cookbook_path); end
|
100
|
+
end
|
101
|
+
|
102
|
+
class CookbookNotFound < ChefApply::Error
|
103
|
+
def initialize(cookbook_name, repo_paths)
|
104
|
+
repo_paths = repo_paths.join("\n")
|
105
|
+
super("CHEFVAL006", cookbook_name, repo_paths)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class NoDefaultRecipe < ChefApply::Error
|
110
|
+
def initialize(cookbook_path, cookbook_name); super("CHEFVAL007", cookbook_path, cookbook_name); end
|
111
|
+
end
|
112
|
+
|
113
|
+
class RecipeNotFound < ChefApply::Error
|
114
|
+
def initialize(cookbook_path, recipe_name, available_recipes, cookbook_name)
|
115
|
+
available_recipes.map! { |r| "'#{r}'" }
|
116
|
+
available_recipes = available_recipes.join(", ")
|
117
|
+
super("CHEFVAL008", cookbook_path, recipe_name, available_recipes, cookbook_name)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|