test-kitchen 1.7.0 → 1.7.1.dev
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/.cane +8 -8
- data/.gitattributes +3 -0
- data/.github/ISSUE_TEMPLATE.md +55 -55
- data/.gitignore +28 -28
- data/.kitchen.ci.yml +23 -23
- data/.kitchen.proxy.yml +27 -27
- data/.rubocop.yml +3 -3
- data/.travis.yml +70 -70
- data/.yardopts +3 -3
- data/Berksfile +3 -3
- data/CHANGELOG.md +1090 -1083
- data/CONTRIBUTING.md +14 -14
- data/Gemfile +19 -19
- data/Gemfile.proxy_tests +4 -4
- data/Guardfile +42 -42
- data/LICENSE +15 -15
- data/MAINTAINERS.md +23 -23
- data/README.md +135 -135
- data/Rakefile +61 -61
- data/appveyor.yml +44 -44
- data/features/kitchen_action_commands.feature +164 -164
- data/features/kitchen_command.feature +16 -16
- data/features/kitchen_console_command.feature +34 -34
- data/features/kitchen_defaults.feature +38 -38
- data/features/kitchen_diagnose_command.feature +96 -96
- data/features/kitchen_driver_create_command.feature +64 -64
- data/features/kitchen_driver_discover_command.feature +25 -25
- data/features/kitchen_help_command.feature +16 -16
- data/features/kitchen_init_command.feature +274 -274
- data/features/kitchen_list_command.feature +104 -104
- data/features/kitchen_login_command.feature +62 -62
- data/features/kitchen_sink_command.feature +30 -30
- data/features/kitchen_test_command.feature +88 -88
- data/features/step_definitions/gem_steps.rb +36 -36
- data/features/step_definitions/git_steps.rb +5 -5
- data/features/step_definitions/output_steps.rb +5 -5
- data/features/support/env.rb +75 -75
- data/lib/kitchen.rb +150 -150
- data/lib/kitchen/base64_stream.rb +55 -55
- data/lib/kitchen/cli.rb +419 -419
- data/lib/kitchen/collection.rb +55 -55
- data/lib/kitchen/color.rb +65 -65
- data/lib/kitchen/command.rb +185 -185
- data/lib/kitchen/command/action.rb +45 -45
- data/lib/kitchen/command/console.rb +58 -58
- data/lib/kitchen/command/diagnose.rb +92 -92
- data/lib/kitchen/command/driver_discover.rb +105 -105
- data/lib/kitchen/command/exec.rb +41 -41
- data/lib/kitchen/command/list.rb +119 -119
- data/lib/kitchen/command/login.rb +43 -43
- data/lib/kitchen/command/sink.rb +54 -54
- data/lib/kitchen/command/test.rb +51 -51
- data/lib/kitchen/config.rb +322 -322
- data/lib/kitchen/configurable.rb +529 -529
- data/lib/kitchen/data_munger.rb +959 -959
- data/lib/kitchen/diagnostic.rb +141 -141
- data/lib/kitchen/driver.rb +56 -56
- data/lib/kitchen/driver/base.rb +134 -134
- data/lib/kitchen/driver/dummy.rb +108 -108
- data/lib/kitchen/driver/proxy.rb +72 -72
- data/lib/kitchen/driver/ssh_base.rb +357 -357
- data/lib/kitchen/errors.rb +229 -229
- data/lib/kitchen/generator/driver_create.rb +177 -177
- data/lib/kitchen/generator/init.rb +296 -296
- data/lib/kitchen/instance.rb +662 -662
- data/lib/kitchen/lazy_hash.rb +142 -142
- data/lib/kitchen/loader/yaml.rb +349 -349
- data/lib/kitchen/logger.rb +423 -423
- data/lib/kitchen/logging.rb +56 -56
- data/lib/kitchen/login_command.rb +52 -52
- data/lib/kitchen/metadata_chopper.rb +52 -52
- data/lib/kitchen/platform.rb +67 -67
- data/lib/kitchen/provisioner.rb +54 -54
- data/lib/kitchen/provisioner/base.rb +236 -236
- data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
- data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
- data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
- data/lib/kitchen/provisioner/chef_apply.rb +124 -124
- data/lib/kitchen/provisioner/chef_base.rb +341 -341
- data/lib/kitchen/provisioner/chef_solo.rb +88 -88
- data/lib/kitchen/provisioner/chef_zero.rb +245 -245
- data/lib/kitchen/provisioner/dummy.rb +79 -79
- data/lib/kitchen/provisioner/shell.rb +138 -138
- data/lib/kitchen/rake_tasks.rb +63 -63
- data/lib/kitchen/shell_out.rb +93 -93
- data/lib/kitchen/ssh.rb +276 -276
- data/lib/kitchen/state_file.rb +120 -120
- data/lib/kitchen/suite.rb +51 -51
- data/lib/kitchen/thor_tasks.rb +66 -66
- data/lib/kitchen/transport.rb +54 -54
- data/lib/kitchen/transport/base.rb +176 -176
- data/lib/kitchen/transport/dummy.rb +79 -79
- data/lib/kitchen/transport/ssh.rb +364 -364
- data/lib/kitchen/transport/winrm.rb +486 -486
- data/lib/kitchen/util.rb +147 -147
- data/lib/kitchen/verifier.rb +55 -55
- data/lib/kitchen/verifier/base.rb +235 -235
- data/lib/kitchen/verifier/busser.rb +277 -277
- data/lib/kitchen/verifier/dummy.rb +79 -79
- data/lib/kitchen/verifier/shell.rb +101 -101
- data/lib/kitchen/version.rb +21 -21
- data/lib/vendor/hash_recursive_merge.rb +82 -82
- data/spec/kitchen/base64_stream_spec.rb +77 -77
- data/spec/kitchen/cli_spec.rb +56 -56
- data/spec/kitchen/collection_spec.rb +80 -80
- data/spec/kitchen/color_spec.rb +54 -54
- data/spec/kitchen/config_spec.rb +408 -408
- data/spec/kitchen/configurable_spec.rb +1095 -1095
- data/spec/kitchen/data_munger_spec.rb +2694 -2694
- data/spec/kitchen/diagnostic_spec.rb +129 -129
- data/spec/kitchen/driver/base_spec.rb +121 -121
- data/spec/kitchen/driver/dummy_spec.rb +199 -199
- data/spec/kitchen/driver/proxy_spec.rb +138 -138
- data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
- data/spec/kitchen/driver_spec.rb +112 -112
- data/spec/kitchen/errors_spec.rb +309 -309
- data/spec/kitchen/instance_spec.rb +1419 -1419
- data/spec/kitchen/lazy_hash_spec.rb +117 -117
- data/spec/kitchen/loader/yaml_spec.rb +774 -774
- data/spec/kitchen/logger_spec.rb +429 -429
- data/spec/kitchen/logging_spec.rb +59 -59
- data/spec/kitchen/login_command_spec.rb +68 -68
- data/spec/kitchen/metadata_chopper_spec.rb +82 -82
- data/spec/kitchen/platform_spec.rb +89 -89
- data/spec/kitchen/provisioner/base_spec.rb +386 -386
- data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
- data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1161
- data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
- data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
- data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
- data/spec/kitchen/provisioner/shell_spec.rb +566 -566
- data/spec/kitchen/provisioner_spec.rb +107 -107
- data/spec/kitchen/shell_out_spec.rb +150 -150
- data/spec/kitchen/ssh_spec.rb +693 -693
- data/spec/kitchen/state_file_spec.rb +129 -129
- data/spec/kitchen/suite_spec.rb +62 -62
- data/spec/kitchen/transport/base_spec.rb +89 -89
- data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
- data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
- data/spec/kitchen/transport_spec.rb +112 -112
- data/spec/kitchen/util_spec.rb +165 -165
- data/spec/kitchen/verifier/base_spec.rb +362 -362
- data/spec/kitchen/verifier/busser_spec.rb +610 -610
- data/spec/kitchen/verifier/dummy_spec.rb +99 -99
- data/spec/kitchen/verifier/shell_spec.rb +160 -160
- data/spec/kitchen/verifier_spec.rb +120 -120
- data/spec/kitchen_spec.rb +114 -114
- data/spec/spec_helper.rb +85 -85
- data/spec/support/powershell_max_size_spec.rb +40 -40
- data/support/busser_install_command.ps1 +14 -14
- data/support/busser_install_command.sh +14 -14
- data/support/chef-client-zero.rb +77 -77
- data/support/chef_base_init_command.ps1 +18 -18
- data/support/chef_base_init_command.sh +2 -2
- data/support/chef_base_install_command.ps1 +85 -85
- data/support/chef_base_install_command.sh +229 -229
- data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
- data/support/chef_zero_prepare_command_legacy.sh +10 -10
- data/support/download_helpers.sh +109 -109
- data/support/dummy-validation.pem +27 -27
- data/templates/driver/CHANGELOG.md.erb +3 -3
- data/templates/driver/Gemfile.erb +3 -3
- data/templates/driver/README.md.erb +64 -64
- data/templates/driver/Rakefile.erb +21 -21
- data/templates/driver/driver.rb.erb +23 -23
- data/templates/driver/gemspec.erb +29 -29
- data/templates/driver/gitignore.erb +17 -17
- data/templates/driver/license_apachev2.erb +15 -15
- data/templates/driver/license_lgplv3.erb +16 -16
- data/templates/driver/license_mit.erb +22 -22
- data/templates/driver/license_reserved.erb +5 -5
- data/templates/driver/tailor.erb +4 -4
- data/templates/driver/travis.yml.erb +11 -11
- data/templates/driver/version.rb.erb +12 -12
- data/templates/init/chefignore.erb +1 -1
- data/templates/init/kitchen.yml.erb +18 -18
- data/test-kitchen.gemspec +62 -62
- data/test/integration/default/default_spec.rb +3 -3
- data/testing_windows.md +37 -37
- metadata +5 -4
data/lib/kitchen/lazy_hash.rb
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
|
-
#
|
|
5
|
-
# Copyright (C) 2013, Fletcher Nichol
|
|
6
|
-
#
|
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
-
# you may not use this file except in compliance with the License.
|
|
9
|
-
# You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
# See the License for the specific language governing permissions and
|
|
17
|
-
# limitations under the License.
|
|
18
|
-
|
|
19
|
-
require "delegate"
|
|
20
|
-
|
|
21
|
-
module Kitchen
|
|
22
|
-
|
|
23
|
-
# A modifed Hash object that may contain callables as a value which must be
|
|
24
|
-
# executed in the context of another object. This allows for delayed
|
|
25
|
-
# evaluation of a hash value while still looking and largely feeling like a
|
|
26
|
-
# normal Ruby Hash.
|
|
27
|
-
#
|
|
28
|
-
# @example normal hash accessing with regular values
|
|
29
|
-
#
|
|
30
|
-
# data = {
|
|
31
|
-
# :symbol => true,
|
|
32
|
-
# "string" => "stuff"
|
|
33
|
-
# }
|
|
34
|
-
# context = "any object"
|
|
35
|
-
# lazy = Kitchen::Hash.new(data, context)
|
|
36
|
-
#
|
|
37
|
-
# lazy[:symbol] # => true
|
|
38
|
-
# lazy.fetch("string") # => "stuff"
|
|
39
|
-
#
|
|
40
|
-
# @example hash with callable blocks as values
|
|
41
|
-
#
|
|
42
|
-
# data = {
|
|
43
|
-
# :lambda => ->(c) { c.length },
|
|
44
|
-
# :proc => Proc.new { |c| c.reverse },
|
|
45
|
-
# :simple => "value"
|
|
46
|
-
# }
|
|
47
|
-
# context = "any object"
|
|
48
|
-
# lazy = Kitchen::Hash.new(data, context)
|
|
49
|
-
#
|
|
50
|
-
# lazy[:lambda] # => 10
|
|
51
|
-
# lazy.fetch(:proc) # => "tcejbo yna"
|
|
52
|
-
# lazy[:simple] # => "value"
|
|
53
|
-
#
|
|
54
|
-
# @author Fletcher Nichol <fnichol@nichol.ca>
|
|
55
|
-
class LazyHash < SimpleDelegator
|
|
56
|
-
include Enumerable
|
|
57
|
-
|
|
58
|
-
# Creates a new LazyHash using a Hash-like object to populate itself and
|
|
59
|
-
# an object that can be used as context in value-callable blocks. The
|
|
60
|
-
# context object can be used to compute values for keys at the time of
|
|
61
|
-
# fetching the value.
|
|
62
|
-
#
|
|
63
|
-
# @param obj [Hash, Object] a hash-like object
|
|
64
|
-
# @param context [Object] an object that can be used to compute values
|
|
65
|
-
def initialize(obj, context)
|
|
66
|
-
@context = context
|
|
67
|
-
super(obj)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Retrieves the rendered value object corresponding to the key object. If
|
|
71
|
-
# not found, returns the default value.
|
|
72
|
-
#
|
|
73
|
-
# @param key [Object] hash key
|
|
74
|
-
# @return [Object, nil] the value for key or the default value if key is
|
|
75
|
-
# not found
|
|
76
|
-
def [](key)
|
|
77
|
-
proc_or_val(__getobj__[key])
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Returns a rendered value from the hash for the given key. If the key
|
|
81
|
-
# can't be found, there are several options: With no other arguments, it
|
|
82
|
-
# will raise an KeyError exception; if default is given, then that will be
|
|
83
|
-
# returned; if the optional code block is specified, then that will be run
|
|
84
|
-
# and its result returned.
|
|
85
|
-
#
|
|
86
|
-
# @param key [Object] hash key
|
|
87
|
-
# @param default [Object] default value if key is not set (optional)
|
|
88
|
-
# @return [Object, nil] the value for the key or the default value if key
|
|
89
|
-
# is not found
|
|
90
|
-
# @raise [KeyError] if the key is not found
|
|
91
|
-
def fetch(key, default = :__undefined__, &block)
|
|
92
|
-
case default
|
|
93
|
-
when :__undefined__
|
|
94
|
-
proc_or_val(__getobj__.fetch(key, &block))
|
|
95
|
-
else
|
|
96
|
-
proc_or_val(__getobj__.fetch(key, default, &block))
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Returns a new Hash with all keys and rendered values of the LazyHash.
|
|
101
|
-
#
|
|
102
|
-
# @return [Hash] a new hash
|
|
103
|
-
def to_hash
|
|
104
|
-
hash = Hash.new
|
|
105
|
-
__getobj__.keys.each { |key| hash[key] = self[key] }
|
|
106
|
-
hash
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Yields each key/value pair to the provided block. Returns a new
|
|
110
|
-
# Hash with only the keys and rendered values for which the block
|
|
111
|
-
# returns true.
|
|
112
|
-
#
|
|
113
|
-
# @return [Hash] a new hash
|
|
114
|
-
def select(&block)
|
|
115
|
-
to_hash.select(&block)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# If no block provided, returns an enumerator over the keys and
|
|
119
|
-
# rendered values in the underlying object. If a block is
|
|
120
|
-
# provided, calls the block once for each [key, rendered_value]
|
|
121
|
-
# pair in the underlying object.
|
|
122
|
-
#
|
|
123
|
-
# @return [Enumerator, Array]
|
|
124
|
-
def each(&block)
|
|
125
|
-
to_hash.each(&block)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
private
|
|
129
|
-
|
|
130
|
-
# Returns an object or invokes call with context if object is callable.
|
|
131
|
-
#
|
|
132
|
-
# @return [Object] an object
|
|
133
|
-
# @api private
|
|
134
|
-
def proc_or_val(thing)
|
|
135
|
-
if thing.respond_to?(:call)
|
|
136
|
-
thing.call(@context)
|
|
137
|
-
else
|
|
138
|
-
thing
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (C) 2013, Fletcher Nichol
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
|
|
19
|
+
require "delegate"
|
|
20
|
+
|
|
21
|
+
module Kitchen
|
|
22
|
+
|
|
23
|
+
# A modifed Hash object that may contain callables as a value which must be
|
|
24
|
+
# executed in the context of another object. This allows for delayed
|
|
25
|
+
# evaluation of a hash value while still looking and largely feeling like a
|
|
26
|
+
# normal Ruby Hash.
|
|
27
|
+
#
|
|
28
|
+
# @example normal hash accessing with regular values
|
|
29
|
+
#
|
|
30
|
+
# data = {
|
|
31
|
+
# :symbol => true,
|
|
32
|
+
# "string" => "stuff"
|
|
33
|
+
# }
|
|
34
|
+
# context = "any object"
|
|
35
|
+
# lazy = Kitchen::Hash.new(data, context)
|
|
36
|
+
#
|
|
37
|
+
# lazy[:symbol] # => true
|
|
38
|
+
# lazy.fetch("string") # => "stuff"
|
|
39
|
+
#
|
|
40
|
+
# @example hash with callable blocks as values
|
|
41
|
+
#
|
|
42
|
+
# data = {
|
|
43
|
+
# :lambda => ->(c) { c.length },
|
|
44
|
+
# :proc => Proc.new { |c| c.reverse },
|
|
45
|
+
# :simple => "value"
|
|
46
|
+
# }
|
|
47
|
+
# context = "any object"
|
|
48
|
+
# lazy = Kitchen::Hash.new(data, context)
|
|
49
|
+
#
|
|
50
|
+
# lazy[:lambda] # => 10
|
|
51
|
+
# lazy.fetch(:proc) # => "tcejbo yna"
|
|
52
|
+
# lazy[:simple] # => "value"
|
|
53
|
+
#
|
|
54
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
|
55
|
+
class LazyHash < SimpleDelegator
|
|
56
|
+
include Enumerable
|
|
57
|
+
|
|
58
|
+
# Creates a new LazyHash using a Hash-like object to populate itself and
|
|
59
|
+
# an object that can be used as context in value-callable blocks. The
|
|
60
|
+
# context object can be used to compute values for keys at the time of
|
|
61
|
+
# fetching the value.
|
|
62
|
+
#
|
|
63
|
+
# @param obj [Hash, Object] a hash-like object
|
|
64
|
+
# @param context [Object] an object that can be used to compute values
|
|
65
|
+
def initialize(obj, context)
|
|
66
|
+
@context = context
|
|
67
|
+
super(obj)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Retrieves the rendered value object corresponding to the key object. If
|
|
71
|
+
# not found, returns the default value.
|
|
72
|
+
#
|
|
73
|
+
# @param key [Object] hash key
|
|
74
|
+
# @return [Object, nil] the value for key or the default value if key is
|
|
75
|
+
# not found
|
|
76
|
+
def [](key)
|
|
77
|
+
proc_or_val(__getobj__[key])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns a rendered value from the hash for the given key. If the key
|
|
81
|
+
# can't be found, there are several options: With no other arguments, it
|
|
82
|
+
# will raise an KeyError exception; if default is given, then that will be
|
|
83
|
+
# returned; if the optional code block is specified, then that will be run
|
|
84
|
+
# and its result returned.
|
|
85
|
+
#
|
|
86
|
+
# @param key [Object] hash key
|
|
87
|
+
# @param default [Object] default value if key is not set (optional)
|
|
88
|
+
# @return [Object, nil] the value for the key or the default value if key
|
|
89
|
+
# is not found
|
|
90
|
+
# @raise [KeyError] if the key is not found
|
|
91
|
+
def fetch(key, default = :__undefined__, &block)
|
|
92
|
+
case default
|
|
93
|
+
when :__undefined__
|
|
94
|
+
proc_or_val(__getobj__.fetch(key, &block))
|
|
95
|
+
else
|
|
96
|
+
proc_or_val(__getobj__.fetch(key, default, &block))
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns a new Hash with all keys and rendered values of the LazyHash.
|
|
101
|
+
#
|
|
102
|
+
# @return [Hash] a new hash
|
|
103
|
+
def to_hash
|
|
104
|
+
hash = Hash.new
|
|
105
|
+
__getobj__.keys.each { |key| hash[key] = self[key] }
|
|
106
|
+
hash
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Yields each key/value pair to the provided block. Returns a new
|
|
110
|
+
# Hash with only the keys and rendered values for which the block
|
|
111
|
+
# returns true.
|
|
112
|
+
#
|
|
113
|
+
# @return [Hash] a new hash
|
|
114
|
+
def select(&block)
|
|
115
|
+
to_hash.select(&block)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# If no block provided, returns an enumerator over the keys and
|
|
119
|
+
# rendered values in the underlying object. If a block is
|
|
120
|
+
# provided, calls the block once for each [key, rendered_value]
|
|
121
|
+
# pair in the underlying object.
|
|
122
|
+
#
|
|
123
|
+
# @return [Enumerator, Array]
|
|
124
|
+
def each(&block)
|
|
125
|
+
to_hash.each(&block)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
# Returns an object or invokes call with context if object is callable.
|
|
131
|
+
#
|
|
132
|
+
# @return [Object] an object
|
|
133
|
+
# @api private
|
|
134
|
+
def proc_or_val(thing)
|
|
135
|
+
if thing.respond_to?(:call)
|
|
136
|
+
thing.call(@context)
|
|
137
|
+
else
|
|
138
|
+
thing
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
data/lib/kitchen/loader/yaml.rb
CHANGED
|
@@ -1,349 +1,349 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
|
-
#
|
|
5
|
-
# Copyright (C) 2013, Fletcher Nichol
|
|
6
|
-
#
|
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
-
# you may not use this file except in compliance with the License.
|
|
9
|
-
# You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
# See the License for the specific language governing permissions and
|
|
17
|
-
# limitations under the License.
|
|
18
|
-
|
|
19
|
-
require "erb"
|
|
20
|
-
require "vendor/hash_recursive_merge"
|
|
21
|
-
|
|
22
|
-
if RUBY_VERSION <= "1.9.3"
|
|
23
|
-
# ensure that Psych and not Syck is used for Ruby 1.9.2
|
|
24
|
-
require "yaml"
|
|
25
|
-
YAML::ENGINE.yamler = "psych"
|
|
26
|
-
end
|
|
27
|
-
require "safe_yaml/load"
|
|
28
|
-
|
|
29
|
-
module Kitchen
|
|
30
|
-
|
|
31
|
-
module Loader
|
|
32
|
-
|
|
33
|
-
# YAML file loader for Test Kitchen configuration. This class is
|
|
34
|
-
# responisble for parsing the main YAML file and the local YAML if it
|
|
35
|
-
# exists. Local file configuration will win over the default configuration.
|
|
36
|
-
# The client of this class should not require any YAML loading or parsing
|
|
37
|
-
# logic.
|
|
38
|
-
#
|
|
39
|
-
# @author Fletcher Nichol <fnichol@nichol.ca>
|
|
40
|
-
class YAML
|
|
41
|
-
|
|
42
|
-
# Creates a new loader that can parse and load YAML files.
|
|
43
|
-
#
|
|
44
|
-
# @param options [Hash] configuration for a new loader
|
|
45
|
-
# @option options [String] :project_config path to the Kitchen
|
|
46
|
-
# config YAML file (default: `./.kitchen.yml`)
|
|
47
|
-
# @option options [String] :local_config path to the Kitchen local
|
|
48
|
-
# config YAML file (default: `./.kitchen.local.yml`)
|
|
49
|
-
# @option options [String] :global_config path to the Kitchen global
|
|
50
|
-
# config YAML file (default: `$HOME/.kitchen/config.yml`)
|
|
51
|
-
# @option options [String] :process_erb whether or not to process YAML
|
|
52
|
-
# through an ERB processor (default: `true`)
|
|
53
|
-
# @option options [String] :process_local whether or not to process a
|
|
54
|
-
# local kitchen YAML file, if it exists (default: `true`)
|
|
55
|
-
def initialize(options = {})
|
|
56
|
-
@config_file =
|
|
57
|
-
File.expand_path(options[:project_config] || default_config_file)
|
|
58
|
-
@local_config_file =
|
|
59
|
-
File.expand_path(options[:local_config] || default_local_config_file)
|
|
60
|
-
@global_config_file =
|
|
61
|
-
File.expand_path(options[:global_config] || default_global_config_file)
|
|
62
|
-
|
|
63
|
-
@process_erb = options.fetch(:process_erb, true)
|
|
64
|
-
@process_local = options.fetch(:process_local, true)
|
|
65
|
-
@process_global = options.fetch(:process_global, true)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Reads, parses, and merges YAML configuration files and returns a Hash
|
|
69
|
-
# of tne merged data.
|
|
70
|
-
#
|
|
71
|
-
# @return [Hash] merged configuration data
|
|
72
|
-
def read
|
|
73
|
-
if !File.exist?(config_file)
|
|
74
|
-
raise UserError, "Kitchen YAML file #{config_file} does not exist."
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
Util.symbolized_hash(combined_hash)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Returns a Hash of configuration and other useful diagnostic information.
|
|
81
|
-
#
|
|
82
|
-
# @return [Hash] a diagnostic hash
|
|
83
|
-
def diagnose
|
|
84
|
-
result = Hash.new
|
|
85
|
-
result[:process_erb] = @process_erb
|
|
86
|
-
result[:process_local] = @process_local
|
|
87
|
-
result[:process_global] = @process_global
|
|
88
|
-
result[:global_config] = diagnose_component(:global_yaml, global_config_file)
|
|
89
|
-
result[:project_config] = diagnose_component(:yaml, config_file)
|
|
90
|
-
result[:local_config] = diagnose_component(:local_yaml, local_config_file)
|
|
91
|
-
result[:combined_config] = diagnose_component(:combined_hash)
|
|
92
|
-
result
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
private
|
|
96
|
-
|
|
97
|
-
# @return [String] the absolute path to the Kitchen config YAML file
|
|
98
|
-
# @api private
|
|
99
|
-
attr_reader :config_file
|
|
100
|
-
|
|
101
|
-
# @return [String] the absolute path to the Kitchen local config YAML
|
|
102
|
-
# file
|
|
103
|
-
# @api private
|
|
104
|
-
attr_reader :local_config_file
|
|
105
|
-
|
|
106
|
-
# @return [String] the absolute path to the Kitchen global config YAML
|
|
107
|
-
# file
|
|
108
|
-
# @api private
|
|
109
|
-
attr_reader :global_config_file
|
|
110
|
-
|
|
111
|
-
# Performed a prioritized recursive merge of several source Hashes and
|
|
112
|
-
# returns a new merged Hash. There are 3 sources of configuration data:
|
|
113
|
-
#
|
|
114
|
-
# 1. local config
|
|
115
|
-
# 2. project config
|
|
116
|
-
# 3. global config
|
|
117
|
-
#
|
|
118
|
-
# The merge order is local -> project -> global, meaning that elements at
|
|
119
|
-
# the top of the above list will be merged last, and have greater
|
|
120
|
-
# precedence than elements at the bottom of the list.
|
|
121
|
-
#
|
|
122
|
-
# @return [Hash] a new merged Hash
|
|
123
|
-
# @api private
|
|
124
|
-
def combined_hash
|
|
125
|
-
y = if @process_global
|
|
126
|
-
normalize(global_yaml).rmerge(normalize(yaml))
|
|
127
|
-
else
|
|
128
|
-
normalize(yaml)
|
|
129
|
-
end
|
|
130
|
-
@process_local ? y.rmerge(normalize(local_yaml)) : y
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Loads and returns the Kitchen config YAML as a Hash.
|
|
134
|
-
#
|
|
135
|
-
# @return [Hash] the config hash
|
|
136
|
-
# @api private
|
|
137
|
-
def yaml
|
|
138
|
-
parse_yaml_string(yaml_string(config_file), config_file)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Loads and returns the Kitchen local config YAML as a Hash.
|
|
142
|
-
#
|
|
143
|
-
# @return [Hash] the config hash
|
|
144
|
-
# @api private
|
|
145
|
-
def local_yaml
|
|
146
|
-
parse_yaml_string(yaml_string(local_config_file), local_config_file)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Loads and returns the Kitchen global config YAML as a Hash.
|
|
150
|
-
#
|
|
151
|
-
# @return [Hash] the config hash
|
|
152
|
-
# @api private
|
|
153
|
-
def global_yaml
|
|
154
|
-
parse_yaml_string(yaml_string(global_config_file), global_config_file)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
# Loads a file to a string and optionally passes it through an ERb
|
|
158
|
-
# process.
|
|
159
|
-
#
|
|
160
|
-
# @return [String] a file's contents as a string
|
|
161
|
-
# @api private
|
|
162
|
-
def yaml_string(file)
|
|
163
|
-
string = read_file(file)
|
|
164
|
-
|
|
165
|
-
@process_erb ? process_erb(string, file) : string
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# Passes a string through ERb to evaulate any ERb blocks.
|
|
169
|
-
#
|
|
170
|
-
# @param string [String] the string to process
|
|
171
|
-
# @param file [String] an absolute path to the file represented as the
|
|
172
|
-
# passed in string, used for error reporting
|
|
173
|
-
# @return [String] a new string, passed through an ERb process
|
|
174
|
-
# @raise [UserError] if an ERb parsing error occurs
|
|
175
|
-
# @api private
|
|
176
|
-
def process_erb(string, file)
|
|
177
|
-
tpl = ERB.new(string)
|
|
178
|
-
tpl.filename = file
|
|
179
|
-
tpl.result
|
|
180
|
-
rescue => e
|
|
181
|
-
raise UserError, "Error parsing ERB content in #{file} " \
|
|
182
|
-
"(#{e.class}: #{e.message}).\n" \
|
|
183
|
-
"Please run `kitchen diagnose --no-instances --loader' to help " \
|
|
184
|
-
"debug your issue."
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Reads a file and returns its contents as a string.
|
|
188
|
-
#
|
|
189
|
-
# @param file [String] a path to a file
|
|
190
|
-
# @return [String] the files contents, or an empty string if the file
|
|
191
|
-
# does not exist
|
|
192
|
-
# @api private
|
|
193
|
-
def read_file(file)
|
|
194
|
-
File.exist?(file.to_s) ? IO.read(file) : ""
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
# Determines the default absolute path to the Kitchen config YAML file,
|
|
198
|
-
# based on current working directory.
|
|
199
|
-
#
|
|
200
|
-
# @return [String] an absolute path to a Kitchen config YAML file
|
|
201
|
-
# @api private
|
|
202
|
-
def default_config_file
|
|
203
|
-
File.join(Dir.pwd, ".kitchen.yml")
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
# Determines the default absolute path to the Kitchen local YAML file,
|
|
207
|
-
# based on the base Kitchen config YAML file.
|
|
208
|
-
#
|
|
209
|
-
# @return [String] an absolute path to a Kitchen local YAML file
|
|
210
|
-
# @api private
|
|
211
|
-
def default_local_config_file
|
|
212
|
-
config_file.sub(/(#{File.extname(config_file)})$/, '.local\1')
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
# Determines the default absolute path to the Kitchen global YAML file,
|
|
216
|
-
# based on the base Kitchen config YAML file.
|
|
217
|
-
#
|
|
218
|
-
# @return [String] an absolute path to a Kitchen global YAML file
|
|
219
|
-
# @api private
|
|
220
|
-
def default_global_config_file
|
|
221
|
-
File.join(File.expand_path(ENV["HOME"]), ".kitchen", "config.yml")
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# Generate a diganose Hash for a particular YAML file Hash. If an error
|
|
225
|
-
# occurs when loading the data, then a failure hash will be inserted
|
|
226
|
-
# into the `:raw_data` sub-hash.
|
|
227
|
-
#
|
|
228
|
-
# @param component [Symbol] a YAML source component
|
|
229
|
-
# @param file [String] the absolute path to a file which is used for
|
|
230
|
-
# reporting (default: `nil`)
|
|
231
|
-
# @return [Hash] a hash data structure
|
|
232
|
-
# @api private
|
|
233
|
-
def diagnose_component(component, file = nil)
|
|
234
|
-
return if file && !File.exist?(file)
|
|
235
|
-
|
|
236
|
-
hash = begin
|
|
237
|
-
send(component)
|
|
238
|
-
rescue => e
|
|
239
|
-
failure_hash(e, file)
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
{ :filename => file, :raw_data => hash }
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
# Generates a Hash respresenting a failure, given an Exception object.
|
|
246
|
-
#
|
|
247
|
-
# @param e [Exception] an exception
|
|
248
|
-
# @param file [String] the absolute path to a file (default: `nil`)
|
|
249
|
-
# @return [Hash] a hash data structure
|
|
250
|
-
# @api private
|
|
251
|
-
def failure_hash(e, file = nil)
|
|
252
|
-
result = {
|
|
253
|
-
:error => {
|
|
254
|
-
:exception => e.inspect,
|
|
255
|
-
:message => e.message,
|
|
256
|
-
:backtrace => e.backtrace
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
result[:error][:raw_file] = IO.read(file) unless file.nil?
|
|
260
|
-
result
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
# Destructively modify an object containing one or more hashes so that
|
|
264
|
-
# the resulting formatted data can be consumed upstream.
|
|
265
|
-
#
|
|
266
|
-
# @param obj [Object] an object
|
|
267
|
-
# @return [Object] an object
|
|
268
|
-
# @api private
|
|
269
|
-
def normalize(obj)
|
|
270
|
-
if obj.is_a?(Hash)
|
|
271
|
-
obj.inject(Hash.new) { |h, (k, v)| normalize_hash(h, k, v); h }
|
|
272
|
-
else
|
|
273
|
-
obj
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
# Normalizes certain keys in the root of a data hash to be a proper
|
|
278
|
-
# sub-hash in all cases. Specifically handled are the following cases:
|
|
279
|
-
#
|
|
280
|
-
# * If the value for certain keys (`"driver"`, `"provisioner"`,
|
|
281
|
-
# `"busser"`) are set to `nil`, a new Hash will be put in its place.
|
|
282
|
-
# * If the value for certain keys is a String, then the value is
|
|
283
|
-
# converted to a new Hash with a default key pointing to the original
|
|
284
|
-
# String.
|
|
285
|
-
#
|
|
286
|
-
# Given a hash:
|
|
287
|
-
#
|
|
288
|
-
# { "driver" => nil }
|
|
289
|
-
#
|
|
290
|
-
# this method would return:
|
|
291
|
-
#
|
|
292
|
-
# { "driver" => {} }
|
|
293
|
-
#
|
|
294
|
-
# Given a hash:
|
|
295
|
-
#
|
|
296
|
-
# { :driver => "coolbeans" }
|
|
297
|
-
#
|
|
298
|
-
# this method would return:
|
|
299
|
-
#
|
|
300
|
-
# { :name => { "driver" => "coolbeans" } }
|
|
301
|
-
#
|
|
302
|
-
#
|
|
303
|
-
# @param hash [Hash] the Hash to normalize
|
|
304
|
-
# @param key [Symbol] the key to normalize
|
|
305
|
-
# @param value [Object] the value to normalize
|
|
306
|
-
# @api private
|
|
307
|
-
def normalize_hash(hash, key, value)
|
|
308
|
-
case key
|
|
309
|
-
when "driver", "provisioner", "busser"
|
|
310
|
-
hash[key] = if value.nil?
|
|
311
|
-
Hash.new
|
|
312
|
-
elsif value.is_a?(String)
|
|
313
|
-
default_key = key == "busser" ? "version" : "name"
|
|
314
|
-
{ default_key => value }
|
|
315
|
-
else
|
|
316
|
-
normalize(value)
|
|
317
|
-
end
|
|
318
|
-
else
|
|
319
|
-
hash[key] = normalize(value)
|
|
320
|
-
end
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
# Parses a YAML string and returns a Hash.
|
|
324
|
-
#
|
|
325
|
-
# @param string [String] a yaml document as a string
|
|
326
|
-
# @param file_name [String] an absolute path to the file represented as
|
|
327
|
-
# the passed in string, used for error reporting
|
|
328
|
-
# @return [Hash] a hash
|
|
329
|
-
# @raise [UserError] if the string document cannot be parsed
|
|
330
|
-
# @api private
|
|
331
|
-
def parse_yaml_string(string, file_name)
|
|
332
|
-
return Hash.new if string.nil? || string.empty?
|
|
333
|
-
|
|
334
|
-
result = SafeYAML.load(string) || Hash.new
|
|
335
|
-
unless result.is_a?(Hash)
|
|
336
|
-
raise UserError, "Error parsing #{file_name} as YAML " \
|
|
337
|
-
"(Result of parse was not a Hash, but was a #{result.class}).\n" \
|
|
338
|
-
"Please run `kitchen diagnose --no-instances --loader' to help " \
|
|
339
|
-
"debug your issue."
|
|
340
|
-
end
|
|
341
|
-
result
|
|
342
|
-
rescue SyntaxError, Psych::SyntaxError
|
|
343
|
-
raise UserError, "Error parsing #{file_name} as YAML.\n" \
|
|
344
|
-
"Please run `kitchen diagnose --no-instances --loader' to help " \
|
|
345
|
-
"debug your issue."
|
|
346
|
-
end
|
|
347
|
-
end
|
|
348
|
-
end
|
|
349
|
-
end
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (C) 2013, Fletcher Nichol
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
|
|
19
|
+
require "erb"
|
|
20
|
+
require "vendor/hash_recursive_merge"
|
|
21
|
+
|
|
22
|
+
if RUBY_VERSION <= "1.9.3"
|
|
23
|
+
# ensure that Psych and not Syck is used for Ruby 1.9.2
|
|
24
|
+
require "yaml"
|
|
25
|
+
YAML::ENGINE.yamler = "psych"
|
|
26
|
+
end
|
|
27
|
+
require "safe_yaml/load"
|
|
28
|
+
|
|
29
|
+
module Kitchen
|
|
30
|
+
|
|
31
|
+
module Loader
|
|
32
|
+
|
|
33
|
+
# YAML file loader for Test Kitchen configuration. This class is
|
|
34
|
+
# responisble for parsing the main YAML file and the local YAML if it
|
|
35
|
+
# exists. Local file configuration will win over the default configuration.
|
|
36
|
+
# The client of this class should not require any YAML loading or parsing
|
|
37
|
+
# logic.
|
|
38
|
+
#
|
|
39
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
|
40
|
+
class YAML
|
|
41
|
+
|
|
42
|
+
# Creates a new loader that can parse and load YAML files.
|
|
43
|
+
#
|
|
44
|
+
# @param options [Hash] configuration for a new loader
|
|
45
|
+
# @option options [String] :project_config path to the Kitchen
|
|
46
|
+
# config YAML file (default: `./.kitchen.yml`)
|
|
47
|
+
# @option options [String] :local_config path to the Kitchen local
|
|
48
|
+
# config YAML file (default: `./.kitchen.local.yml`)
|
|
49
|
+
# @option options [String] :global_config path to the Kitchen global
|
|
50
|
+
# config YAML file (default: `$HOME/.kitchen/config.yml`)
|
|
51
|
+
# @option options [String] :process_erb whether or not to process YAML
|
|
52
|
+
# through an ERB processor (default: `true`)
|
|
53
|
+
# @option options [String] :process_local whether or not to process a
|
|
54
|
+
# local kitchen YAML file, if it exists (default: `true`)
|
|
55
|
+
def initialize(options = {})
|
|
56
|
+
@config_file =
|
|
57
|
+
File.expand_path(options[:project_config] || default_config_file)
|
|
58
|
+
@local_config_file =
|
|
59
|
+
File.expand_path(options[:local_config] || default_local_config_file)
|
|
60
|
+
@global_config_file =
|
|
61
|
+
File.expand_path(options[:global_config] || default_global_config_file)
|
|
62
|
+
|
|
63
|
+
@process_erb = options.fetch(:process_erb, true)
|
|
64
|
+
@process_local = options.fetch(:process_local, true)
|
|
65
|
+
@process_global = options.fetch(:process_global, true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Reads, parses, and merges YAML configuration files and returns a Hash
|
|
69
|
+
# of tne merged data.
|
|
70
|
+
#
|
|
71
|
+
# @return [Hash] merged configuration data
|
|
72
|
+
def read
|
|
73
|
+
if !File.exist?(config_file)
|
|
74
|
+
raise UserError, "Kitchen YAML file #{config_file} does not exist."
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
Util.symbolized_hash(combined_hash)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns a Hash of configuration and other useful diagnostic information.
|
|
81
|
+
#
|
|
82
|
+
# @return [Hash] a diagnostic hash
|
|
83
|
+
def diagnose
|
|
84
|
+
result = Hash.new
|
|
85
|
+
result[:process_erb] = @process_erb
|
|
86
|
+
result[:process_local] = @process_local
|
|
87
|
+
result[:process_global] = @process_global
|
|
88
|
+
result[:global_config] = diagnose_component(:global_yaml, global_config_file)
|
|
89
|
+
result[:project_config] = diagnose_component(:yaml, config_file)
|
|
90
|
+
result[:local_config] = diagnose_component(:local_yaml, local_config_file)
|
|
91
|
+
result[:combined_config] = diagnose_component(:combined_hash)
|
|
92
|
+
result
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
# @return [String] the absolute path to the Kitchen config YAML file
|
|
98
|
+
# @api private
|
|
99
|
+
attr_reader :config_file
|
|
100
|
+
|
|
101
|
+
# @return [String] the absolute path to the Kitchen local config YAML
|
|
102
|
+
# file
|
|
103
|
+
# @api private
|
|
104
|
+
attr_reader :local_config_file
|
|
105
|
+
|
|
106
|
+
# @return [String] the absolute path to the Kitchen global config YAML
|
|
107
|
+
# file
|
|
108
|
+
# @api private
|
|
109
|
+
attr_reader :global_config_file
|
|
110
|
+
|
|
111
|
+
# Performed a prioritized recursive merge of several source Hashes and
|
|
112
|
+
# returns a new merged Hash. There are 3 sources of configuration data:
|
|
113
|
+
#
|
|
114
|
+
# 1. local config
|
|
115
|
+
# 2. project config
|
|
116
|
+
# 3. global config
|
|
117
|
+
#
|
|
118
|
+
# The merge order is local -> project -> global, meaning that elements at
|
|
119
|
+
# the top of the above list will be merged last, and have greater
|
|
120
|
+
# precedence than elements at the bottom of the list.
|
|
121
|
+
#
|
|
122
|
+
# @return [Hash] a new merged Hash
|
|
123
|
+
# @api private
|
|
124
|
+
def combined_hash
|
|
125
|
+
y = if @process_global
|
|
126
|
+
normalize(global_yaml).rmerge(normalize(yaml))
|
|
127
|
+
else
|
|
128
|
+
normalize(yaml)
|
|
129
|
+
end
|
|
130
|
+
@process_local ? y.rmerge(normalize(local_yaml)) : y
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Loads and returns the Kitchen config YAML as a Hash.
|
|
134
|
+
#
|
|
135
|
+
# @return [Hash] the config hash
|
|
136
|
+
# @api private
|
|
137
|
+
def yaml
|
|
138
|
+
parse_yaml_string(yaml_string(config_file), config_file)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Loads and returns the Kitchen local config YAML as a Hash.
|
|
142
|
+
#
|
|
143
|
+
# @return [Hash] the config hash
|
|
144
|
+
# @api private
|
|
145
|
+
def local_yaml
|
|
146
|
+
parse_yaml_string(yaml_string(local_config_file), local_config_file)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Loads and returns the Kitchen global config YAML as a Hash.
|
|
150
|
+
#
|
|
151
|
+
# @return [Hash] the config hash
|
|
152
|
+
# @api private
|
|
153
|
+
def global_yaml
|
|
154
|
+
parse_yaml_string(yaml_string(global_config_file), global_config_file)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Loads a file to a string and optionally passes it through an ERb
|
|
158
|
+
# process.
|
|
159
|
+
#
|
|
160
|
+
# @return [String] a file's contents as a string
|
|
161
|
+
# @api private
|
|
162
|
+
def yaml_string(file)
|
|
163
|
+
string = read_file(file)
|
|
164
|
+
|
|
165
|
+
@process_erb ? process_erb(string, file) : string
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Passes a string through ERb to evaulate any ERb blocks.
|
|
169
|
+
#
|
|
170
|
+
# @param string [String] the string to process
|
|
171
|
+
# @param file [String] an absolute path to the file represented as the
|
|
172
|
+
# passed in string, used for error reporting
|
|
173
|
+
# @return [String] a new string, passed through an ERb process
|
|
174
|
+
# @raise [UserError] if an ERb parsing error occurs
|
|
175
|
+
# @api private
|
|
176
|
+
def process_erb(string, file)
|
|
177
|
+
tpl = ERB.new(string)
|
|
178
|
+
tpl.filename = file
|
|
179
|
+
tpl.result
|
|
180
|
+
rescue => e
|
|
181
|
+
raise UserError, "Error parsing ERB content in #{file} " \
|
|
182
|
+
"(#{e.class}: #{e.message}).\n" \
|
|
183
|
+
"Please run `kitchen diagnose --no-instances --loader' to help " \
|
|
184
|
+
"debug your issue."
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Reads a file and returns its contents as a string.
|
|
188
|
+
#
|
|
189
|
+
# @param file [String] a path to a file
|
|
190
|
+
# @return [String] the files contents, or an empty string if the file
|
|
191
|
+
# does not exist
|
|
192
|
+
# @api private
|
|
193
|
+
def read_file(file)
|
|
194
|
+
File.exist?(file.to_s) ? IO.read(file) : ""
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Determines the default absolute path to the Kitchen config YAML file,
|
|
198
|
+
# based on current working directory.
|
|
199
|
+
#
|
|
200
|
+
# @return [String] an absolute path to a Kitchen config YAML file
|
|
201
|
+
# @api private
|
|
202
|
+
def default_config_file
|
|
203
|
+
File.join(Dir.pwd, ".kitchen.yml")
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Determines the default absolute path to the Kitchen local YAML file,
|
|
207
|
+
# based on the base Kitchen config YAML file.
|
|
208
|
+
#
|
|
209
|
+
# @return [String] an absolute path to a Kitchen local YAML file
|
|
210
|
+
# @api private
|
|
211
|
+
def default_local_config_file
|
|
212
|
+
config_file.sub(/(#{File.extname(config_file)})$/, '.local\1')
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Determines the default absolute path to the Kitchen global YAML file,
|
|
216
|
+
# based on the base Kitchen config YAML file.
|
|
217
|
+
#
|
|
218
|
+
# @return [String] an absolute path to a Kitchen global YAML file
|
|
219
|
+
# @api private
|
|
220
|
+
def default_global_config_file
|
|
221
|
+
File.join(File.expand_path(ENV["HOME"]), ".kitchen", "config.yml")
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Generate a diganose Hash for a particular YAML file Hash. If an error
|
|
225
|
+
# occurs when loading the data, then a failure hash will be inserted
|
|
226
|
+
# into the `:raw_data` sub-hash.
|
|
227
|
+
#
|
|
228
|
+
# @param component [Symbol] a YAML source component
|
|
229
|
+
# @param file [String] the absolute path to a file which is used for
|
|
230
|
+
# reporting (default: `nil`)
|
|
231
|
+
# @return [Hash] a hash data structure
|
|
232
|
+
# @api private
|
|
233
|
+
def diagnose_component(component, file = nil)
|
|
234
|
+
return if file && !File.exist?(file)
|
|
235
|
+
|
|
236
|
+
hash = begin
|
|
237
|
+
send(component)
|
|
238
|
+
rescue => e
|
|
239
|
+
failure_hash(e, file)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
{ :filename => file, :raw_data => hash }
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Generates a Hash respresenting a failure, given an Exception object.
|
|
246
|
+
#
|
|
247
|
+
# @param e [Exception] an exception
|
|
248
|
+
# @param file [String] the absolute path to a file (default: `nil`)
|
|
249
|
+
# @return [Hash] a hash data structure
|
|
250
|
+
# @api private
|
|
251
|
+
def failure_hash(e, file = nil)
|
|
252
|
+
result = {
|
|
253
|
+
:error => {
|
|
254
|
+
:exception => e.inspect,
|
|
255
|
+
:message => e.message,
|
|
256
|
+
:backtrace => e.backtrace
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
result[:error][:raw_file] = IO.read(file) unless file.nil?
|
|
260
|
+
result
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Destructively modify an object containing one or more hashes so that
|
|
264
|
+
# the resulting formatted data can be consumed upstream.
|
|
265
|
+
#
|
|
266
|
+
# @param obj [Object] an object
|
|
267
|
+
# @return [Object] an object
|
|
268
|
+
# @api private
|
|
269
|
+
def normalize(obj)
|
|
270
|
+
if obj.is_a?(Hash)
|
|
271
|
+
obj.inject(Hash.new) { |h, (k, v)| normalize_hash(h, k, v); h }
|
|
272
|
+
else
|
|
273
|
+
obj
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Normalizes certain keys in the root of a data hash to be a proper
|
|
278
|
+
# sub-hash in all cases. Specifically handled are the following cases:
|
|
279
|
+
#
|
|
280
|
+
# * If the value for certain keys (`"driver"`, `"provisioner"`,
|
|
281
|
+
# `"busser"`) are set to `nil`, a new Hash will be put in its place.
|
|
282
|
+
# * If the value for certain keys is a String, then the value is
|
|
283
|
+
# converted to a new Hash with a default key pointing to the original
|
|
284
|
+
# String.
|
|
285
|
+
#
|
|
286
|
+
# Given a hash:
|
|
287
|
+
#
|
|
288
|
+
# { "driver" => nil }
|
|
289
|
+
#
|
|
290
|
+
# this method would return:
|
|
291
|
+
#
|
|
292
|
+
# { "driver" => {} }
|
|
293
|
+
#
|
|
294
|
+
# Given a hash:
|
|
295
|
+
#
|
|
296
|
+
# { :driver => "coolbeans" }
|
|
297
|
+
#
|
|
298
|
+
# this method would return:
|
|
299
|
+
#
|
|
300
|
+
# { :name => { "driver" => "coolbeans" } }
|
|
301
|
+
#
|
|
302
|
+
#
|
|
303
|
+
# @param hash [Hash] the Hash to normalize
|
|
304
|
+
# @param key [Symbol] the key to normalize
|
|
305
|
+
# @param value [Object] the value to normalize
|
|
306
|
+
# @api private
|
|
307
|
+
def normalize_hash(hash, key, value)
|
|
308
|
+
case key
|
|
309
|
+
when "driver", "provisioner", "busser"
|
|
310
|
+
hash[key] = if value.nil?
|
|
311
|
+
Hash.new
|
|
312
|
+
elsif value.is_a?(String)
|
|
313
|
+
default_key = key == "busser" ? "version" : "name"
|
|
314
|
+
{ default_key => value }
|
|
315
|
+
else
|
|
316
|
+
normalize(value)
|
|
317
|
+
end
|
|
318
|
+
else
|
|
319
|
+
hash[key] = normalize(value)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Parses a YAML string and returns a Hash.
|
|
324
|
+
#
|
|
325
|
+
# @param string [String] a yaml document as a string
|
|
326
|
+
# @param file_name [String] an absolute path to the file represented as
|
|
327
|
+
# the passed in string, used for error reporting
|
|
328
|
+
# @return [Hash] a hash
|
|
329
|
+
# @raise [UserError] if the string document cannot be parsed
|
|
330
|
+
# @api private
|
|
331
|
+
def parse_yaml_string(string, file_name)
|
|
332
|
+
return Hash.new if string.nil? || string.empty?
|
|
333
|
+
|
|
334
|
+
result = SafeYAML.load(string) || Hash.new
|
|
335
|
+
unless result.is_a?(Hash)
|
|
336
|
+
raise UserError, "Error parsing #{file_name} as YAML " \
|
|
337
|
+
"(Result of parse was not a Hash, but was a #{result.class}).\n" \
|
|
338
|
+
"Please run `kitchen diagnose --no-instances --loader' to help " \
|
|
339
|
+
"debug your issue."
|
|
340
|
+
end
|
|
341
|
+
result
|
|
342
|
+
rescue SyntaxError, Psych::SyntaxError
|
|
343
|
+
raise UserError, "Error parsing #{file_name} as YAML.\n" \
|
|
344
|
+
"Please run `kitchen diagnose --no-instances --loader' to help " \
|
|
345
|
+
"debug your issue."
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|