chef-dk 0.12.0 → 0.13.21

Sign up to get free protection for your applications and to get access to all the features.
@@ -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