chef-apply 0.3.3 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,79 +0,0 @@
|
|
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
|
-
class MinimumChefVersion
|
22
|
-
|
23
|
-
CONSTRAINTS = {
|
24
|
-
windows: {
|
25
|
-
13 => Gem::Version.new("13.10.4"),
|
26
|
-
14 => Gem::Version.new("14.4.22"),
|
27
|
-
},
|
28
|
-
linux: {
|
29
|
-
13 => Gem::Version.new("13.10.4"),
|
30
|
-
14 => Gem::Version.new("14.1.1"),
|
31
|
-
},
|
32
|
-
}.freeze
|
33
|
-
|
34
|
-
def self.check!(target, check_only)
|
35
|
-
begin
|
36
|
-
installed_version = target.installed_chef_version
|
37
|
-
rescue ChefApply::TargetHost::ChefNotInstalled
|
38
|
-
if check_only
|
39
|
-
raise ClientNotInstalled.new()
|
40
|
-
end
|
41
|
-
return :client_not_installed
|
42
|
-
end
|
43
|
-
|
44
|
-
os_constraints = CONSTRAINTS[target.base_os]
|
45
|
-
min_14_version = os_constraints[14]
|
46
|
-
min_13_version = os_constraints[13]
|
47
|
-
|
48
|
-
case
|
49
|
-
when installed_version >= Gem::Version.new("14.0.0") && installed_version < min_14_version
|
50
|
-
raise Client14Outdated.new(installed_version, min_14_version)
|
51
|
-
when installed_version >= Gem::Version.new("13.0.0") && installed_version < min_13_version
|
52
|
-
raise Client13Outdated.new(installed_version, min_13_version, min_14_version)
|
53
|
-
when installed_version < Gem::Version.new("13.0.0")
|
54
|
-
# If they have Chef < 13.0.0 installed we want to show them the easiest upgrade path -
|
55
|
-
# Chef 13 first and then Chef 14 since most customers cannot make the leap directly
|
56
|
-
# to 14.
|
57
|
-
raise Client13Outdated.new(installed_version, min_13_version, min_14_version)
|
58
|
-
end
|
59
|
-
|
60
|
-
:minimum_version_met
|
61
|
-
end
|
62
|
-
|
63
|
-
class ClientNotInstalled < ChefApply::ErrorNoLogs
|
64
|
-
def initialize(); super("CHEFINS002"); end
|
65
|
-
end
|
66
|
-
|
67
|
-
class Client13Outdated < ChefApply::ErrorNoLogs
|
68
|
-
def initialize(current_version, min_13_version, min_14_version)
|
69
|
-
super("CHEFINS003", current_version, min_13_version, min_14_version)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
class Client14Outdated < ChefApply::ErrorNoLogs
|
74
|
-
def initialize(current_version, target_version)
|
75
|
-
super("CHEFINS004", current_version, target_version)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,117 +0,0 @@
|
|
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
|
-
|
23
|
-
module ChefApply
|
24
|
-
# When users are trying to converge a local recipe on a remote target, there
|
25
|
-
# is a very specific (but expansive) set of things they can specify. This
|
26
|
-
# class encapsulates that logic for testing purposes. We either return
|
27
|
-
# a path to a recipe or we raise an error.
|
28
|
-
class RecipeLookup
|
29
|
-
|
30
|
-
attr_reader :cookbook_repo_paths
|
31
|
-
def initialize(cookbook_repo_paths)
|
32
|
-
@cookbook_repo_paths = cookbook_repo_paths
|
33
|
-
end
|
34
|
-
|
35
|
-
# The recipe specifier is provided by the customer as either a path OR
|
36
|
-
# a cookbook and optional recipe name.
|
37
|
-
def split(recipe_specifier)
|
38
|
-
recipe_specifier.split("::")
|
39
|
-
end
|
40
|
-
|
41
|
-
# Given a cookbook path or name, try to load that cookbook. Either return
|
42
|
-
# a cookbook object or raise an error.
|
43
|
-
def load_cookbook(path_or_name)
|
44
|
-
require "chef/exceptions"
|
45
|
-
if File.directory?(path_or_name)
|
46
|
-
cookbook_path = path_or_name
|
47
|
-
# First, is there a cookbook in the specified dir that matches?
|
48
|
-
require "chef/cookbook/cookbook_version_loader"
|
49
|
-
begin
|
50
|
-
v = Chef::Cookbook::CookbookVersionLoader.new(cookbook_path)
|
51
|
-
v.load!
|
52
|
-
cookbook = v.cookbook_version
|
53
|
-
rescue Chef::Exceptions::CookbookNotFoundInRepo
|
54
|
-
raise InvalidCookbook.new(cookbook_path)
|
55
|
-
end
|
56
|
-
else
|
57
|
-
cookbook_name = path_or_name
|
58
|
-
# Second, is there a cookbook in their local repository that matches?
|
59
|
-
require "chef/cookbook_loader"
|
60
|
-
cb_loader = Chef::CookbookLoader.new(cookbook_repo_paths)
|
61
|
-
cb_loader.load_cookbooks
|
62
|
-
|
63
|
-
begin
|
64
|
-
cookbook = cb_loader[cookbook_name]
|
65
|
-
rescue Chef::Exceptions::CookbookNotFoundInRepo
|
66
|
-
cookbook_repo_paths.each do |repo_path|
|
67
|
-
cookbook_path = File.join(repo_path, cookbook_name)
|
68
|
-
if File.directory?(cookbook_path)
|
69
|
-
raise InvalidCookbook.new(cookbook_path)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
raise CookbookNotFound.new(cookbook_name, cookbook_repo_paths)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
cookbook
|
76
|
-
end
|
77
|
-
|
78
|
-
# Find the specified recipe or default recipe if none is specified.
|
79
|
-
# Raise an error if recipe cannot be found.
|
80
|
-
def find_recipe(cookbook, recipe_name = nil)
|
81
|
-
recipes = cookbook.recipe_filenames_by_name
|
82
|
-
if recipe_name.nil?
|
83
|
-
default_recipe = recipes["default"]
|
84
|
-
raise NoDefaultRecipe.new(cookbook.root_dir, cookbook.name) if default_recipe.nil?
|
85
|
-
default_recipe
|
86
|
-
else
|
87
|
-
recipe = recipes[recipe_name]
|
88
|
-
raise RecipeNotFound.new(cookbook.root_dir, recipe_name, recipes.keys, cookbook.name) if recipe.nil?
|
89
|
-
recipe
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
class InvalidCookbook < ChefApply::Error
|
94
|
-
def initialize(cookbook_path); super("CHEFVAL005", cookbook_path); end
|
95
|
-
end
|
96
|
-
|
97
|
-
class CookbookNotFound < ChefApply::Error
|
98
|
-
def initialize(cookbook_name, repo_paths)
|
99
|
-
repo_paths = repo_paths.join("\n")
|
100
|
-
super("CHEFVAL006", cookbook_name, repo_paths)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class NoDefaultRecipe < ChefApply::Error
|
105
|
-
def initialize(cookbook_path, cookbook_name); super("CHEFVAL007", cookbook_path, cookbook_name); end
|
106
|
-
end
|
107
|
-
|
108
|
-
class RecipeNotFound < ChefApply::Error
|
109
|
-
def initialize(cookbook_path, recipe_name, available_recipes, cookbook_name)
|
110
|
-
available_recipes.map! { |r| "'#{r}'" }
|
111
|
-
available_recipes = available_recipes.join(", ")
|
112
|
-
super("CHEFVAL008", cookbook_path, recipe_name, available_recipes, cookbook_name)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
end
|
117
|
-
end
|
@@ -1,170 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright:: Copyright (c) 2018 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 "tmpdir"
|
19
|
-
require "fileutils"
|
20
|
-
require "chef_apply/log"
|
21
|
-
require "chef_apply/error"
|
22
|
-
|
23
|
-
module ChefApply
|
24
|
-
# This class knows how to create a local cookbook in a temp file, populate
|
25
|
-
# it with various recipes, attributes, config, etc. and delete it when the
|
26
|
-
# cookbook is no longer necessary
|
27
|
-
class TempCookbook
|
28
|
-
attr_reader :path, :descriptor, :from
|
29
|
-
def initialize
|
30
|
-
@path = Dir.mktmpdir("cw")
|
31
|
-
end
|
32
|
-
|
33
|
-
def from_existing_recipe(existing_recipe_path)
|
34
|
-
ext_name = File.extname(existing_recipe_path)
|
35
|
-
raise UnsupportedExtension.new(ext_name) unless ext_name == ".rb"
|
36
|
-
cb = cookbook_for_recipe(existing_recipe_path)
|
37
|
-
if cb
|
38
|
-
# Full existing cookbook - only needs policyfile
|
39
|
-
ChefApply::Log.debug("Found full cookbook at path '#{cb[:path]}' and using recipe '#{cb[:recipe_name]}'")
|
40
|
-
@descriptor = "#{cb[:name]}::#{cb[:recipe_name]}"
|
41
|
-
@from = "#{cb[:path]}"
|
42
|
-
recipe_name = cb[:recipe_name]
|
43
|
-
cb_name = cb[:name]
|
44
|
-
FileUtils.cp_r(cb[:path], path)
|
45
|
-
# cp_r copies the whole existing cookbook into the tempdir so need to reset our path
|
46
|
-
@path = File.join(path, File.basename(cb[:path]))
|
47
|
-
else
|
48
|
-
# Cookbook from single recipe not in a cookbook. We create the full cookbook
|
49
|
-
# structure including metadata, then generate policyfile. We set the cookbook
|
50
|
-
# name to the recipe name so hopefully this gives us better reporting info
|
51
|
-
# in the future
|
52
|
-
ChefApply::Log.debug("Found single recipe at path '#{existing_recipe_path}'")
|
53
|
-
recipe = File.basename(existing_recipe_path)
|
54
|
-
recipe_name = File.basename(recipe, ext_name)
|
55
|
-
cb_name = "cw_recipe"
|
56
|
-
@descriptor = "#{recipe_name}"
|
57
|
-
@from = existing_recipe_path
|
58
|
-
recipes_dir = generate_recipes_dir
|
59
|
-
# This has the potential to break if they specify a recipe without a .rb
|
60
|
-
# extension, but lets wait to deal with that bug until we encounter it
|
61
|
-
FileUtils.cp(existing_recipe_path, File.join(recipes_dir, recipe))
|
62
|
-
generate_metadata(cb_name)
|
63
|
-
end
|
64
|
-
generate_policyfile(cb_name, recipe_name)
|
65
|
-
end
|
66
|
-
|
67
|
-
def from_resource(resource_type, resource_name, properties)
|
68
|
-
# Generate a cookbook containing a single default recipe with the specified
|
69
|
-
# resource in it. Incloud the resource type in the cookbook name so hopefully
|
70
|
-
# this gives us better reporting info in the future.
|
71
|
-
@descriptor = "#{resource_type}[#{resource_name}]"
|
72
|
-
@from = "resource"
|
73
|
-
|
74
|
-
ChefApply::Log.debug("Generating cookbook for single resource '#{resource_type}[#{resource_name}]'")
|
75
|
-
name = "cw_#{resource_type}"
|
76
|
-
recipe_name = "default"
|
77
|
-
recipes_dir = generate_recipes_dir
|
78
|
-
File.open(File.join(recipes_dir, "#{recipe_name}.rb"), "w+") do |f|
|
79
|
-
f.print(create_resource_definition(resource_type, resource_name, properties))
|
80
|
-
end
|
81
|
-
generate_metadata(name)
|
82
|
-
generate_policyfile(name, recipe_name)
|
83
|
-
end
|
84
|
-
|
85
|
-
def delete
|
86
|
-
FileUtils.remove_entry path
|
87
|
-
end
|
88
|
-
|
89
|
-
def cookbook_for_recipe(existing_recipe_path)
|
90
|
-
metadata = File.expand_path(File.join(existing_recipe_path, "../../metadata.rb"))
|
91
|
-
if File.file?(metadata)
|
92
|
-
require "chef/cookbook/metadata"
|
93
|
-
m = Chef::Cookbook::Metadata.new
|
94
|
-
m.from_file(metadata)
|
95
|
-
{
|
96
|
-
name: m.name,
|
97
|
-
recipe_name: File.basename(existing_recipe_path, File.extname(existing_recipe_path)),
|
98
|
-
path: File.expand_path(File.join(metadata, "../")),
|
99
|
-
}
|
100
|
-
else
|
101
|
-
nil
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def generate_recipes_dir
|
106
|
-
recipes_path = File.join(path, "recipes")
|
107
|
-
FileUtils.mkdir_p(recipes_path)
|
108
|
-
recipes_path
|
109
|
-
end
|
110
|
-
|
111
|
-
def generate_metadata(name)
|
112
|
-
metadata_file = File.join(path, "metadata.rb")
|
113
|
-
File.open(metadata_file, "w+") do |f|
|
114
|
-
f.print("name \"#{name}\"\n")
|
115
|
-
end
|
116
|
-
metadata_file
|
117
|
-
end
|
118
|
-
|
119
|
-
def generate_policyfile(name, recipe_name)
|
120
|
-
policy_file = File.join(path, "Policyfile.rb")
|
121
|
-
if File.exist?(policy_file)
|
122
|
-
File.open(policy_file, "a") do |f|
|
123
|
-
# We override the specified run_list with the run_list we want.
|
124
|
-
# We append and put this at the end of the file so it overrides
|
125
|
-
# any specified run_list.
|
126
|
-
f.print("\n# Overriding run_list with command line specified value\n")
|
127
|
-
f.print("run_list \"#{name}::#{recipe_name}\"\n")
|
128
|
-
end
|
129
|
-
else
|
130
|
-
File.open(policy_file, "w+") do |f|
|
131
|
-
f.print("name \"#{name}_policy\"\n")
|
132
|
-
ChefApply::Config.chef.cookbook_repo_paths.each do |p|
|
133
|
-
f.print("default_source :chef_repo, \"#{p}\"\n")
|
134
|
-
end
|
135
|
-
f.print("default_source :supermarket\n")
|
136
|
-
f.print("run_list \"#{name}::#{recipe_name}\"\n")
|
137
|
-
f.print("cookbook \"#{name}\", path: \".\"\n")
|
138
|
-
end
|
139
|
-
end
|
140
|
-
policy_file
|
141
|
-
end
|
142
|
-
|
143
|
-
def create_resource_definition(resource_type, resource_name, properties)
|
144
|
-
r = "#{resource_type} '#{resource_name}'"
|
145
|
-
# lets format the properties into the correct syntax Chef expects
|
146
|
-
unless properties.empty?
|
147
|
-
r += " do\n"
|
148
|
-
properties.each do |k, v|
|
149
|
-
v = "'#{v}'" if v.is_a? String
|
150
|
-
r += " #{k} #{v}\n"
|
151
|
-
end
|
152
|
-
r += "end"
|
153
|
-
end
|
154
|
-
r += "\n"
|
155
|
-
r
|
156
|
-
end
|
157
|
-
|
158
|
-
def policyfile_lock_path
|
159
|
-
File.join(path, "Policyfile.lock.json")
|
160
|
-
end
|
161
|
-
|
162
|
-
def export_path
|
163
|
-
File.join(path, "export")
|
164
|
-
end
|
165
|
-
|
166
|
-
class UnsupportedExtension < ChefApply::ErrorNoLogs
|
167
|
-
def initialize(ext); super("CHEFVAL009", ext); end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|