kitchen-microwave 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'fileutils'
22
+ require 'kitchen/loader/yaml'
23
+ require 'kitchen/util'
24
+ require 'vendor/hash_recursive_merge'
25
+ require 'yaml'
26
+ require_relative '../microwave/driver'
27
+ require_relative '../microwave/platforms'
28
+ require_relative '../microwave/provisioner'
29
+ require_relative '../microwave/suites'
30
+ require_relative '../microwave/transport'
31
+ require_relative '../microwave/verifier'
32
+
33
+ module Kitchen
34
+ module Loader
35
+ # A customized version of Test Kitchen's YAML loader to allow
36
+ # auto-generating a config.
37
+ #
38
+ # @author Jonathan Hartman <jonathan.hartman@socrata.com>
39
+ class Microwave < YAML
40
+ #
41
+ # Write out the generated hash for this loader after instantiating it.
42
+ #
43
+ # (see Kitchen::Loader::YAML.initialize)
44
+ #
45
+ def initialize(options = {})
46
+ super
47
+ write!
48
+ end
49
+
50
+ #
51
+ # Don't bother raising an error if the .kitchen.yml doesn't exist.
52
+ #
53
+ # (see Kitchen::Loader::YAML#read)
54
+ #
55
+ def read
56
+ Util.symbolized_hash(combined_hash)
57
+ end
58
+
59
+ #
60
+ # Write the merged config out to a file for easier debugging.
61
+ #
62
+ def write!
63
+ path = File.expand_path('.kitchen/.microwave.yml',
64
+ File.dirname(@config_file))
65
+ FileUtils.mkdir_p(File.dirname(path))
66
+ File.write(path, Util.stringified_hash(read).to_yaml)
67
+ end
68
+
69
+ #
70
+ # Add a fourth layer of config at the top and then merge the regular
71
+ # three layers of .kitchen.ymls into it. Then delete any excluded
72
+ # platforms that have been set globally via:
73
+ #
74
+ # ---
75
+ # microwave:
76
+ # excludes:
77
+ # - platform: ubuntu
78
+ # version: 14.04
79
+ # chef: 14
80
+ # - platform: debian
81
+ # # version: implicit "all"
82
+ # # chef: implicit "all"
83
+ #
84
+ # (see Kitchen::Loader::YAML#combined_hash)
85
+ #
86
+ def combined_hash
87
+ hsh = microwave_hash.rmerge(super)
88
+ @excludes = hsh['microwave']['excludes']
89
+ hsh
90
+ end
91
+
92
+ #
93
+ # Return the new top layer we're going to merge all the traditional YAML
94
+ # layers into. The new layer should have:
95
+ #
96
+ # * An empty set of platforms to be excluded
97
+ # * A static, simple driver config for dokken
98
+ # * A static, simple transport config for dokken
99
+ # * A static, simple provisioner config for dokken
100
+ # * A static, simple verifier config for dokken
101
+ # * A set of platforms generated from metadata.rb's supported
102
+ # platforms*Chef versions
103
+ # * A set of suites generated from recipes present in the `test` wrapper
104
+ # cookbook
105
+ #
106
+ # @return [Hash] the top-level Microwave hash for the config
107
+ def microwave_hash
108
+ path = File.dirname(@config_file)
109
+ normalize(
110
+ 'microwave' => { 'excludes' => [] },
111
+ 'driver' => Kitchen::Microwave::Driver.new,
112
+ 'transport' => Kitchen::Microwave::Transport.new,
113
+ 'provisioner' => Kitchen::Microwave::Provisioner.new,
114
+ 'verifier' => Kitchen::Microwave::Verifier.new,
115
+ 'platforms' => Kitchen::Microwave::Platforms.new(path, @excludes),
116
+ 'suites' => Kitchen::Microwave::Suites.new(path)
117
+ )
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ module Kitchen
22
+ module Microwave
23
+ # The driver config is just a hash with a couple static values.
24
+ #
25
+ # @author Jonathan Hartman <jonathan.hartman@socrata.com>
26
+ class Driver < Hash
27
+ #
28
+ # Configure the driver section to use dokken. We do enough with
29
+ # networking that requires privileged mode to default it to true.
30
+ #
31
+ # @return [Kitchen::Microwave::Driver] the new driver object
32
+ #
33
+ def initialize
34
+ self['name'] = 'dokken'
35
+ self['privileged'] = true
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN yum -y install upstart initscripts"
5
+ ],
6
+ "pid_one_command": "/sbin/init"
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN yum -y install systemd"
5
+ ],
6
+ "pid_one_command": "/lib/systemd/systemd"
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN yum -y install upstart initscripts"
5
+ ],
6
+ "pid_one_command": "/sbin/init"
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [],
4
+ "pid_one_command": "/lib/systemd/systemd"
5
+ }
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN echo DISTRIB_CODENAME=jessie > /etc/lsb-release"
5
+ ],
6
+ "pid_one_command": "/lib/systemd/systemd"
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN echo DISTRIB_CODENAME=stretch > /etc/lsb-release",
5
+ "RUN apt-get update",
6
+ "RUN apt-get -y install systemd"
7
+ ],
8
+ "pid_one_command": "/lib/systemd/systemd"
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN dnf -y install procps"
5
+ ],
6
+ "pid_one_command": "/lib/systemd/systemd"
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN dpkg-divert --remove /sbin/initctl",
5
+ "RUN ln -sf /sbin/initctl.distrib /sbin/initctl"
6
+ ],
7
+ "pid_one_command": "/sbin/init"
8
+ }
9
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [],
4
+ "pid_one_command": "/lib/systemd/systemd"
5
+ }
6
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "driver": {
3
+ "intermediate_instructions": [
4
+ "RUN apt-get update",
5
+ "RUN apt-get -y install systemd"
6
+
7
+ ],
8
+ "pid_one_command": "/lib/systemd/systemd"
9
+ }
10
+ }
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'chef/cookbook/metadata'
22
+ require 'json'
23
+ require 'vendor/hash_recursive_merge'
24
+
25
+ module Kitchen
26
+ module Microwave
27
+ # The platforms config is more complex, needs to be generated based on what
28
+ # the cookbook metadata advertises support for.
29
+ #
30
+ # @author Jonathan Hartman <jonathan.hartman@socrata.com>
31
+ class Platforms < Array
32
+ # The major Chef versions we could potentially test against in cookbooks
33
+ # that support them.
34
+ POTENTIAL_CHEFS ||= %w[14 13 12].freeze
35
+
36
+ # The platforms and versions we could potentiall test against in cookbooks
37
+ # that support them.
38
+ POTENTIAL_PLATFORMS ||= {
39
+ 'ubuntu' => %w[18.04 16.04 14.04],
40
+ 'debian' => %w[9 8],
41
+ 'centos' => %w[7 6],
42
+ 'amazonlinux' => %w[2 1],
43
+ 'fedora' => %w[28]
44
+ }.freeze
45
+
46
+ #
47
+ # Build a set of platforms based on the metadata's advertised supported
48
+ # platforms*Chef versions. Each platform gets a config to install the
49
+ # appropriate init system and start it as PID 1, and do anything else
50
+ # that might be common to make it behave enough like a regular VM for
51
+ # testing.
52
+ #
53
+ # Instantiating requires a path to the cookdook under test and can
54
+ # accept an optional set of excluded platforms of the format:
55
+ #
56
+ # [
57
+ # {
58
+ # 'name' => 'ubuntu',
59
+ # 'version' => '14.04',
60
+ # 'chef' => '14'
61
+ # },
62
+ # {
63
+ # 'name' => 'debian',
64
+ # # 'version' => 'implicit "all"'
65
+ # # 'chef' => 'implicit "all"'
66
+ # }
67
+ #
68
+ #
69
+ # @param [String] cookbook_dir path to the cookbook under test
70
+ # @param [Array<Hash>] an array of platform information to exclude
71
+ #
72
+ # @return [Kitchen::Microwave::Platforms] the new platforms object
73
+ #
74
+ def initialize(cookbook_dir, excludes = [])
75
+ @cookbook_dir = cookbook_dir
76
+ @excludes = excludes || []
77
+
78
+ supported_chefs.each do |chef|
79
+ supported_platforms.each do |name, versions|
80
+ versions.each do |version|
81
+ self << platform_for(name, version, chef) unless \
82
+ excluded?(name, version, chef)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ #
89
+ # Build the stanza for a single platform+version+Chef version, including
90
+ # any intermediate steps we use to make the Docker container behave more
91
+ # like a real VM.
92
+ #
93
+ # @param [String] name the platform name
94
+ # @param [String] version the platform version
95
+ # @param [String] chef the Chef version
96
+ #
97
+ # @return [Hash] a single platform stanza
98
+ #
99
+ def platform_for(name, version, chef)
100
+ file = File.expand_path("platforms/#{name}/#{version}.json", __dir__)
101
+ data = JSON.parse(File.read(file))
102
+ {
103
+ 'name' => "#{name}-#{version}-chef-#{chef}",
104
+ 'driver' => {
105
+ 'image' => "#{name}:#{version}",
106
+ 'chef_version' => chef
107
+ }
108
+ }.rmerge(data)
109
+ end
110
+
111
+ #
112
+ # Determine if a particular platform name, version, and chef combo is
113
+ # globally excluded.
114
+ #
115
+ # @param [String] name the platform name
116
+ # @param [String] version the platform version
117
+ # @param [String] chef the Chef version
118
+ #
119
+ # @return [TrueClass, FalseClass] whether it is excluded
120
+ #
121
+ def excluded?(name, version, chef)
122
+ @excludes.each do |exc|
123
+ return true if ['', name].include?(exc['name'].to_s) && \
124
+ ['', version].include?(exc['version'].to_s) && \
125
+ ['', chef].include?(exc['chef'].to_s)
126
+ end
127
+ false
128
+ end
129
+
130
+ #
131
+ # Look at the supported platforms in the metadata and figure out what
132
+ # platforms and versions should be tested.
133
+ #
134
+ # @return [Hash] a hash of platform => [version1, version2...]
135
+ #
136
+ def supported_platforms
137
+ @supported_platforms ||= metadata_platforms
138
+ .each_with_object({}) do |(name, req), hsh|
139
+ req = Gem::Requirement.new(req)
140
+ POTENTIAL_PLATFORMS[name].to_a.each do |version|
141
+ hsh[name] ||= []
142
+ hsh[name] << version if req.satisfied_by?(Gem::Version.new(version))
143
+ end
144
+ end
145
+ end
146
+
147
+ #
148
+ # Look at the supported Chef versions in the metadata to figure out which
149
+ # should be tested against.
150
+ #
151
+ # @return [Array<String>] an array of major Chef versions/Docker images
152
+ #
153
+ def supported_chefs
154
+ @supported_chefs ||= POTENTIAL_CHEFS.select do |chef|
155
+ metadata.chef_versions.find do |cv|
156
+ # The Docker tag e.g. "14" should always be the newest release of
157
+ # that major version and satisfy e.g. "~> 14.2".
158
+ Gem::Requirement.new(cv.requirement.to_s.split('.').first)
159
+ .satisfied_by?(Gem::Version.new(chef))
160
+ end
161
+ end
162
+ end
163
+
164
+ #
165
+ # Pull the supported platforms out of the metadata and do any required
166
+ # transformations to them.
167
+ #
168
+ # @return [Hash] a hash of platform_name => requirement_string
169
+ #
170
+ def metadata_platforms
171
+ @metadata_platforms ||= begin
172
+ metadata.platforms.each_with_object({}) do |(name, req), hsh|
173
+ # Amazon Linux is named "amazon" in Chef metadata but "amazonlinux"
174
+ # in Docker Hub.
175
+ name = 'amazonlinux' if name == 'amazon'
176
+
177
+ # Assume that CentOS' e.g. "7" tag in Docker Hub will satisfy an
178
+ # e.g. "~> 7.4" requirement string.
179
+ req = req.split('.').first if name == 'centos'
180
+
181
+ hsh[name] = req
182
+ end
183
+ end
184
+ end
185
+
186
+ #
187
+ # Read and store the local cookbook's metadata.
188
+ #
189
+ # @return [Chef::Cookbook::Metadata] the cookbook metadata
190
+ #
191
+ def metadata
192
+ @metadata ||= begin
193
+ md = Chef::Cookbook::Metadata.new
194
+ md.from_file(File.join(@cookbook_dir, 'metadata.rb'))
195
+ md
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ module Kitchen
22
+ module Microwave
23
+ # The provisioner config is just a hash with a single static value.
24
+ #
25
+ # @author Jonathan Hartman <jonathan.hartman@socrata.com>
26
+ class Provisioner < Hash
27
+ #
28
+ # Configure the provisioner section to use dokken.
29
+ #
30
+ # @return [Kitchen::Microwave::Provisioner] the new provisioner object
31
+ #
32
+ def initialize
33
+ self['name'] = 'dokken'
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ module Kitchen
22
+ module Microwave
23
+ # The suites config can be generated by examining the `test` wrapper
24
+ # cookbook.
25
+ #
26
+ # @author Jonathan Hartman <jonathan.hartman@socrata.com>
27
+ class Suites < Array
28
+ #
29
+ # Examine the cookbook's test harness and consider any public (i.e. not
30
+ # named with a leading "_") recipe to be a test suite.
31
+ #
32
+ # @param [String] cookbook_dir path to the cookbook under test
33
+ #
34
+ # @return [Kitchen::Microwave::Suites] the new suites object
35
+ #
36
+ def initialize(cookbook_dir)
37
+ glob = File.join(cookbook_dir,
38
+ 'test/fixtures/cookbooks/test/recipes/[a-z0-9]*.rb')
39
+ Dir.glob(glob).sort.each do |ste|
40
+ self << {
41
+ 'name' => File.basename(ste, '.rb'),
42
+ 'run_list' => %W[recipe[test::#{File.basename(ste, '.rb')}]]
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ module Kitchen
22
+ module Microwave
23
+ # The transport config is just a hash with a single static value.
24
+ #
25
+ # @author Jonathan Hartman <jonathan.hartman@socrata.com>
26
+ class Transport < Hash
27
+ #
28
+ # Configure the transport section to use dokken.
29
+ #
30
+ # @return [Kitchen::Microwave::Transport] the new transport object
31
+ #
32
+ def initialize
33
+ self['name'] = 'dokken'
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ module Kitchen
22
+ module Microwave
23
+ # The verifier config is just a hash with a couple static values.
24
+ #
25
+ # @author Jonathan Hartman <jonathan.hartman@socrata.com>
26
+ class Verifier < Hash
27
+ #
28
+ # Configure the verifier section to use inspec and point at
29
+ # `/opt/verifier` for Dokken.
30
+ #
31
+ # @return [Kitchen::Microwave::Verifier] the new verifier object
32
+ #
33
+ def initialize
34
+ self['name'] = 'inspec'
35
+ self['root_path'] = '/opt/verifier'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Author:: Jonathan Hartman (<jonathan.hartman@socrata.com>)
5
+ #
6
+ # Copyright (C) 2018, Socrata, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ module Kitchen
22
+ # Microwave's VERSION information.
23
+ module Microwave
24
+ MAJOR = 0
25
+ MINOR = 1
26
+ PATCH = 0
27
+ VERSION = [MAJOR, MINOR, PATCH].join('.')
28
+ end
29
+ end