chef-dk 0.12.0 → 0.13.21

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.
@@ -1,115 +1,115 @@
1
- #
2
- # Copyright:: Copyright (c) 2014 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
- module ChefDK
19
-
20
- # CommandsMap maintains a mapping of subcommand names to the files where
21
- # those commands are defined and the classes that implement the commands.
22
- #
23
- # In ruby it's more typical to handle this sort of thing using conventions
24
- # and metaprogramming. We've implemented this approach in the past and
25
- # decided against it here:
26
- # 1. Performance. As the CLI suite grows, you have to load more and more
27
- # code, including dependencies that are installed by rubygems, etc. This gets
28
- # slow, and CLI apps need to be fast.
29
- # 2. You can workaround the above by having a convention mapping filename to
30
- # command name, but then you have to do a lot of work to list all of the
31
- # commands, which is actually a common thing to do.
32
- # 3. Other ways to mitigate the performance issue (loading deps lazily) have
33
- # their own complications and tradeoffs and don't fully solve the problem.
34
- # 4. It's not actually that much work to maintain the mapping.
35
- #
36
- # ## Adding new commands globally:
37
- #
38
- # A "singleton-ish" instance of this class is stored as ChefDK.commands_map.
39
- # You can configure a multiple commands at once in a block using
40
- # ChefDK.commands, like so:
41
- #
42
- # ChefDK.commands do |c|
43
- # # assigns `chef my-command` to the class ChefDK::Command::MyCommand.
44
- # # The "require path" is inferred to be "chef-dk/command/my_command"
45
- # c.builtin("my-command", :MyCommand)
46
- #
47
- # # Set the require path explicitly:
48
- # c.builtin("weird-command", :WeirdoClass, require_path: "chef-dk/command/this_is_cray")
49
- #
50
- # # You can add a description that will show up in `chef -h` output (recommended):
51
- # c.builtin("documented-cmd", :DocumentedCmd, desc: "A short description")
52
- # end
53
- #
54
- class CommandsMap
55
- NULL_ARG = Object.new
56
-
57
- CommandSpec = Struct.new(:name, :constant_name, :require_path, :description)
58
-
59
- class CommandSpec
60
-
61
- def instantiate
62
- require require_path
63
- command_class = ChefDK::Command.const_get(constant_name)
64
- command_class.new
65
- end
66
-
67
- end
68
-
69
- attr_reader :command_specs
70
-
71
- def initialize
72
- @command_specs = {}
73
- end
74
-
75
- def builtin(name, constant_name, require_path: NULL_ARG, desc: "")
76
- if null?(require_path)
77
- snake_case_path = name.gsub("-", "_")
78
- require_path = "chef-dk/command/#{snake_case_path}"
79
- end
80
- command_specs[name] = CommandSpec.new(name, constant_name, require_path, desc)
81
- end
82
-
83
- def instantiate(name)
84
- spec_for(name).instantiate
85
- end
86
-
87
- def have_command?(name)
88
- command_specs.key?(name)
89
- end
90
-
91
- def command_names
92
- command_specs.keys
93
- end
94
-
95
- def spec_for(name)
96
- command_specs[name]
97
- end
98
-
99
- private
100
-
101
- def null?(argument)
102
- argument.equal?(NULL_ARG)
103
- end
104
- end
105
-
106
- def self.commands_map
107
- @commands_map ||= CommandsMap.new
108
- end
109
-
110
- def self.commands
111
- yield commands_map
112
- end
113
- end
114
-
115
-
1
+ #
2
+ # Copyright:: Copyright (c) 2016 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
+ module ChefDK
19
+
20
+ # CommandsMap maintains a mapping of subcommand names to the files where
21
+ # those commands are defined and the classes that implement the commands.
22
+ #
23
+ # In ruby it's more typical to handle this sort of thing using conventions
24
+ # and metaprogramming. We've implemented this approach in the past and
25
+ # decided against it here:
26
+ # 1. Performance. As the CLI suite grows, you have to load more and more
27
+ # code, including dependencies that are installed by rubygems, etc. This gets
28
+ # slow, and CLI apps need to be fast.
29
+ # 2. You can workaround the above by having a convention mapping filename to
30
+ # command name, but then you have to do a lot of work to list all of the
31
+ # commands, which is actually a common thing to do.
32
+ # 3. Other ways to mitigate the performance issue (loading deps lazily) have
33
+ # their own complications and tradeoffs and don't fully solve the problem.
34
+ # 4. It's not actually that much work to maintain the mapping.
35
+ #
36
+ # ## Adding new commands globally:
37
+ #
38
+ # A "singleton-ish" instance of this class is stored as ChefDK.commands_map.
39
+ # You can configure a multiple commands at once in a block using
40
+ # ChefDK.commands, like so:
41
+ #
42
+ # ChefDK.commands do |c|
43
+ # # assigns `chef my-command` to the class ChefDK::Command::MyCommand.
44
+ # # The "require path" is inferred to be "chef-dk/command/my_command"
45
+ # c.builtin("my-command", :MyCommand)
46
+ #
47
+ # # Set the require path explicitly:
48
+ # c.builtin("weird-command", :WeirdoClass, require_path: "chef-dk/command/this_is_cray")
49
+ #
50
+ # # You can add a description that will show up in `chef -h` output (recommended):
51
+ # c.builtin("documented-cmd", :DocumentedCmd, desc: "A short description")
52
+ # end
53
+ #
54
+ class CommandsMap
55
+ NULL_ARG = Object.new
56
+
57
+ CommandSpec = Struct.new(:name, :constant_name, :require_path, :description)
58
+
59
+ class CommandSpec
60
+
61
+ def instantiate
62
+ require require_path
63
+ command_class = ChefDK::Command.const_get(constant_name)
64
+ command_class.new
65
+ end
66
+
67
+ end
68
+
69
+ attr_reader :command_specs
70
+
71
+ def initialize
72
+ @command_specs = {}
73
+ end
74
+
75
+ def builtin(name, constant_name, require_path: NULL_ARG, desc: "")
76
+ if null?(require_path)
77
+ snake_case_path = name.gsub("-", "_")
78
+ require_path = "chef-dk/command/#{snake_case_path}"
79
+ end
80
+ command_specs[name] = CommandSpec.new(name, constant_name, require_path, desc)
81
+ end
82
+
83
+ def instantiate(name)
84
+ spec_for(name).instantiate
85
+ end
86
+
87
+ def have_command?(name)
88
+ command_specs.key?(name)
89
+ end
90
+
91
+ def command_names
92
+ command_specs.keys
93
+ end
94
+
95
+ def spec_for(name)
96
+ command_specs[name]
97
+ end
98
+
99
+ private
100
+
101
+ def null?(argument)
102
+ argument.equal?(NULL_ARG)
103
+ end
104
+ end
105
+
106
+ def self.commands_map
107
+ @commands_map ||= CommandsMap.new
108
+ end
109
+
110
+ def self.commands
111
+ yield commands_map
112
+ end
113
+ end
114
+
115
+
@@ -1,194 +1,198 @@
1
- #
2
- # Copyright:: Copyright (c) 2014 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-dk/exceptions'
19
- require 'chef-dk/helpers'
20
-
21
- # https://github.com/bundler/bundler/issues/4368
22
- # As of rubygems 2.6.2, Rubygems will call Bundler::SpecSet#size, which does
23
- # not exist as of Bundler 1.11.2.
24
- #
25
- # `#size` and `#length` should be synonyms in idiomatic ruby so we alias to
26
- # make bundler and rubygems play nicely.
27
- if Object.const_defined?(:Bundler) &&
28
- Bundler.const_defined?(:SpecSet) &&
29
- Bundler::SpecSet.instance_methods.include?(:length) &&
30
- !Bundler::SpecSet.instance_methods.include?(:size)
31
-
32
- module Bundler
33
- class SpecSet
34
-
35
- alias_method :size, :length
36
-
37
- end
38
- end
39
-
40
- end
41
-
42
- module ChefDK
43
- class ComponentTest
44
-
45
- class NullTestResult
46
- def exitstatus
47
- 0
48
- end
49
-
50
- def stdout
51
- ""
52
- end
53
-
54
- def stderr
55
- ""
56
- end
57
- end
58
-
59
- DEFAULT_TEST = lambda { |context| NullTestResult.new }
60
-
61
- include Helpers
62
-
63
- attr_accessor :base_dir
64
-
65
- attr_writer :omnibus_root
66
-
67
- attr_reader :name
68
-
69
- def initialize(name)
70
- @name = name
71
- @unit_test = DEFAULT_TEST
72
- @integration_test = DEFAULT_TEST
73
- @smoke_test = DEFAULT_TEST
74
- @base_dir = nil
75
- @gem_name_for_base_dir = nil
76
- end
77
-
78
- def unit_test(&test_block)
79
- @unit_test = test_block
80
- end
81
-
82
- def run_unit_test
83
- instance_eval(&@unit_test)
84
- end
85
-
86
- def integration_test(&test_block)
87
- @integration_test = test_block
88
- end
89
-
90
- def run_integration_test
91
- instance_eval(&@integration_test)
92
- end
93
-
94
- def smoke_test(&test_block)
95
- @smoke_test = test_block
96
- end
97
-
98
- def run_smoke_test
99
- instance_eval(&@smoke_test)
100
- end
101
-
102
- def sh(command, options={})
103
- combined_opts = default_command_options.merge(options)
104
-
105
- # Env is a hash, so it needs to be merged separately
106
- if options.key?(:env)
107
- combined_opts[:env] = default_command_options[:env].merge(options[:env])
108
- end
109
- system_command(command, combined_opts)
110
- end
111
-
112
- # Just like #sh but raises an error if the the command returns an
113
- # unexpected exit code.
114
- #
115
- # Most verification steps just run a single command, then
116
- # ChefDK::Command::Verify#invoke_tests handles the results by inspecting
117
- # the return value of the test block. For tests that run a lot of commands,
118
- # this is inconvenient so you can use #sh! instead.
119
- def sh!(*args)
120
- sh(*args).tap { |result| result.error! }
121
- end
122
-
123
- def run_in_tmpdir(command, options={})
124
- tmpdir do |dir|
125
- options[:cwd] = dir
126
- sh(command, options)
127
- end
128
- end
129
-
130
- def tmpdir
131
- Dir.mktmpdir do |tmpdir|
132
- yield tmpdir
133
- end
134
- end
135
-
136
- def assert_present!
137
- unless File.exists?( component_path )
138
- raise MissingComponentError.new(name, "Could not find #{component_path}")
139
- end
140
- rescue Gem::LoadError => e
141
- raise MissingComponentError.new(name, e)
142
- end
143
-
144
- def default_command_options
145
- {
146
- :cwd => component_path,
147
- :env => {
148
- # Add the embedded/bin to the PATH so that bundle executable can
149
- # be found while running the tests.
150
- path_variable_key => omnibus_path
151
- },
152
- :timeout => 3600
153
- }
154
- end
155
-
156
- def component_path
157
- if base_dir
158
- File.join(omnibus_apps_dir, base_dir)
159
- elsif gem_base_dir
160
- gem_base_dir
161
- else
162
- raise "`base_dir` or `gem_base_dir` must be defined for component `#{name}`"
163
- end
164
- end
165
-
166
- def gem_base_dir
167
- return nil if @gem_name_for_base_dir.nil?
168
- # There is no way to say "give me the latest prerelease OR normal version of this gem.
169
- # So we first ask if there is a normal version, and if there is not, we ask if there
170
- # is a prerelease version. ">= 0.a" is how we ask for a prerelease version, because a
171
- # prerelease version is defined as "any version with a letter in it."
172
- gem = Gem::Specification.find_by_name(@gem_name_for_base_dir)
173
- gem ||= Gem::Specification.find_by_name(@gem_name_for_base_dir, '>= 0.a')
174
- gem.gem_dir
175
- end
176
-
177
- def gem_base_dir=(gem_name)
178
- @gem_name_for_base_dir = gem_name
179
- end
180
-
181
- def omnibus_root
182
- @omnibus_root or raise "`omnibus_root` must be set before running tests"
183
- end
184
-
185
- def omnibus_path
186
- [omnibus_bin_dir, omnibus_embedded_bin_dir, ENV['PATH']].join(File::PATH_SEPARATOR)
187
- end
188
-
189
- def path_variable_key
190
- ENV.keys.grep(/\Apath\Z/i).first
191
- end
192
-
193
- end
194
- end
1
+ #
2
+ # Copyright:: Copyright (c) 2014 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-dk/exceptions'
19
+ require 'chef-dk/helpers'
20
+
21
+ # https://github.com/bundler/bundler/issues/4368
22
+ # As of rubygems 2.6.2, Rubygems will call Bundler::SpecSet#size, which does
23
+ # not exist as of Bundler 1.11.2.
24
+ #
25
+ # `#size` and `#length` should be synonyms in idiomatic ruby so we alias to
26
+ # make bundler and rubygems play nicely.
27
+ if Object.const_defined?(:Bundler) &&
28
+ Bundler.const_defined?(:SpecSet) &&
29
+ Bundler::SpecSet.instance_methods.include?(:length) &&
30
+ !Bundler::SpecSet.instance_methods.include?(:size)
31
+
32
+ module Bundler
33
+ class SpecSet
34
+
35
+ alias_method :size, :length
36
+
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ module ChefDK
43
+ class ComponentTest
44
+
45
+ class NullTestResult
46
+ def exitstatus
47
+ 0
48
+ end
49
+
50
+ def stdout
51
+ ""
52
+ end
53
+
54
+ def stderr
55
+ ""
56
+ end
57
+ end
58
+
59
+ DEFAULT_TEST = lambda { |context| NullTestResult.new }
60
+
61
+ include Helpers
62
+
63
+ attr_accessor :base_dir
64
+
65
+ attr_writer :omnibus_root
66
+
67
+ attr_reader :name
68
+
69
+ def initialize(name)
70
+ @name = name
71
+ @unit_test = DEFAULT_TEST
72
+ @integration_test = DEFAULT_TEST
73
+ @smoke_test = DEFAULT_TEST
74
+ @base_dir = nil
75
+ @gem_name_for_base_dir = nil
76
+ end
77
+
78
+ def unit_test(&test_block)
79
+ @unit_test = test_block
80
+ end
81
+
82
+ def run_unit_test
83
+ instance_eval(&@unit_test)
84
+ end
85
+
86
+ def integration_test(&test_block)
87
+ @integration_test = test_block
88
+ end
89
+
90
+ def run_integration_test
91
+ instance_eval(&@integration_test)
92
+ end
93
+
94
+ def smoke_test(&test_block)
95
+ @smoke_test = test_block
96
+ end
97
+
98
+ def run_smoke_test
99
+ instance_eval(&@smoke_test)
100
+ end
101
+
102
+ def bin(binary)
103
+ File.join(omnibus_embedded_bin_dir, binary)
104
+ end
105
+
106
+ def sh(command, options={})
107
+ combined_opts = default_command_options.merge(options)
108
+
109
+ # Env is a hash, so it needs to be merged separately
110
+ if options.key?(:env)
111
+ combined_opts[:env] = default_command_options[:env].merge(options[:env])
112
+ end
113
+ system_command(command, combined_opts)
114
+ end
115
+
116
+ # Just like #sh but raises an error if the the command returns an
117
+ # unexpected exit code.
118
+ #
119
+ # Most verification steps just run a single command, then
120
+ # ChefDK::Command::Verify#invoke_tests handles the results by inspecting
121
+ # the return value of the test block. For tests that run a lot of commands,
122
+ # this is inconvenient so you can use #sh! instead.
123
+ def sh!(*args)
124
+ sh(*args).tap { |result| result.error! }
125
+ end
126
+
127
+ def run_in_tmpdir(command, options={})
128
+ tmpdir do |dir|
129
+ options[:cwd] = dir
130
+ sh(command, options)
131
+ end
132
+ end
133
+
134
+ def tmpdir
135
+ Dir.mktmpdir do |tmpdir|
136
+ yield tmpdir
137
+ end
138
+ end
139
+
140
+ def assert_present!
141
+ unless File.exists?( component_path )
142
+ raise MissingComponentError.new(name, "Could not find #{component_path}")
143
+ end
144
+ rescue Gem::LoadError => e
145
+ raise MissingComponentError.new(name, e)
146
+ end
147
+
148
+ def default_command_options
149
+ {
150
+ :cwd => component_path,
151
+ :env => {
152
+ # Add the embedded/bin to the PATH so that bundle executable can
153
+ # be found while running the tests.
154
+ path_variable_key => omnibus_path
155
+ },
156
+ :timeout => 3600
157
+ }
158
+ end
159
+
160
+ def component_path
161
+ if base_dir
162
+ File.join(omnibus_apps_dir, base_dir)
163
+ elsif gem_base_dir
164
+ gem_base_dir
165
+ else
166
+ raise "`base_dir` or `gem_base_dir` must be defined for component `#{name}`"
167
+ end
168
+ end
169
+
170
+ def gem_base_dir
171
+ return nil if @gem_name_for_base_dir.nil?
172
+ # There is no way to say "give me the latest prerelease OR normal version of this gem.
173
+ # So we first ask if there is a normal version, and if there is not, we ask if there
174
+ # is a prerelease version. ">= 0.a" is how we ask for a prerelease version, because a
175
+ # prerelease version is defined as "any version with a letter in it."
176
+ gem = Gem::Specification.find_by_name(@gem_name_for_base_dir)
177
+ gem ||= Gem::Specification.find_by_name(@gem_name_for_base_dir, '>= 0.a')
178
+ gem.gem_dir
179
+ end
180
+
181
+ def gem_base_dir=(gem_name)
182
+ @gem_name_for_base_dir = gem_name
183
+ end
184
+
185
+ def omnibus_root
186
+ @omnibus_root or raise "`omnibus_root` must be set before running tests"
187
+ end
188
+
189
+ def omnibus_path
190
+ [omnibus_bin_dir, omnibus_embedded_bin_dir, ENV['PATH']].join(File::PATH_SEPARATOR)
191
+ end
192
+
193
+ def path_variable_key
194
+ ENV.keys.grep(/\Apath\Z/i).first
195
+ end
196
+
197
+ end
198
+ end