chef-umami 0.0.3

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.
@@ -0,0 +1,50 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'chef'
16
+
17
+ module Umami
18
+ class Client
19
+
20
+ attr_reader :client
21
+ def initialize
22
+ @client = client
23
+ end
24
+
25
+ def client
26
+ @client ||= Chef::Client.new
27
+ end
28
+
29
+ # Perform the steps required prior to compiling resources, including
30
+ # running Ohai and building up the node object.
31
+ def prep
32
+ client.run_ohai
33
+ client.load_node # from the server
34
+ client.build_node
35
+ end
36
+
37
+ # Execute the compile phase of a Chef client run.
38
+ def compile
39
+ prep
40
+ client.setup_run_context
41
+ end
42
+
43
+ # TODO: This can only be called after #prep completes successfully.
44
+ # Add some check to determine if the client is actually prepped.
45
+ def resource_collection
46
+ client.run_status.run_context.resource_collection
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Umami
16
+
17
+ class InvalidPolicyfileLockFilename < StandardError
18
+ end
19
+
20
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Umami
16
+ module Helper
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Umami
16
+ module Helper
17
+ module FileTools
18
+
19
+ require 'fileutils'
20
+ require 'rubocop'
21
+
22
+ def write_file(path = nil, content = '')
23
+ parent_dir = File.dirname(path)
24
+ FileUtils.mkdir_p(parent_dir) unless ::File.exist?(parent_dir)
25
+ f = File.open(path, 'w') # Write with prejudice.
26
+ f.write(content)
27
+ f.close
28
+ end
29
+
30
+ # Call Rubocop to ensure proper indentation and thus legibility.
31
+ def enforce_styling(path = 'spec/umami/')
32
+ puts "Running Rubocop over '#{path}' to enforce styling..."
33
+ r = RuboCop::CLI.new
34
+ # Don't output to STDOUT.
35
+ args = [
36
+ '--only', 'Style/IndentationWidth,Style/IndentationConsistency',
37
+ '--auto-correct',
38
+ '--out', '/dev/null',
39
+ path
40
+ ]
41
+ r.run(args)
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,153 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Umami
16
+ module Helper
17
+ module InSpec
18
+
19
+ # Call on a resource's #identity method to help describe the resource.
20
+ # This saves us from having to know/code the identity attribute for each
21
+ # resource (i.e. File is :path, User is :username, etc).
22
+ def desciption(resource)
23
+ identity = resource.identity
24
+ if identity.is_a? Hash # #identity could return a Hash. Take the first value.
25
+ identity = identity.values.first
26
+ end
27
+ "describe #{resource.declared_type}('#{identity}') do"
28
+ end
29
+
30
+ # All test methods should follow the naming convention 'test_<resource type>'
31
+ # 1. The methods should build up an array of lines defining the test.
32
+ # 1. The first element should be the result of a call to
33
+ # #description(resource) except in cases where it is not appropriate
34
+ # (i.e. testing a directory resource requires defining a file test).
35
+ # 2. The method should should return a string joined by newlines.
36
+ #
37
+ #def test_wutang(resource)
38
+ # test = [desciption(resource)]
39
+ # test << "it { should be_financially_sound }"
40
+ # test << "it { should be_diverisified }"
41
+ # test.join("\n")
42
+ #end
43
+
44
+ # InSpec can evaluate if a gem is installed via the system `gem` (default)
45
+ # or via some other `gem` binary, defined by either the path to the gem
46
+ # binary of a symbol representing that context.
47
+ def test_gem_package(resource, gem_binary=nil)
48
+ package_name = resource.package_name
49
+ if gem_binary
50
+ if gem_binary.is_a? Symbol
51
+ gem_binary = gem_binary.inspect # Stringify the symbol.
52
+ else
53
+ gem_binary = "'#{gem_binary}'"
54
+ end
55
+ test = ["Gem '#{package_name}' is installed via the #{gem_binary} gem"]
56
+ test << "describe gem('#{package_name}', #{gem_binary}) do"
57
+ else
58
+ test = ["Gem '#{package_name}' is installed via the #{gem_binary} gem"]
59
+ test << "describe gem('#{package_name}') do"
60
+ end
61
+ test << 'it { should be_installed }'
62
+ test << 'end'
63
+ test.join("\n")
64
+ end
65
+
66
+ def test_chef_gem(resource)
67
+ test_gem_package(resource, ':chef')
68
+ end
69
+
70
+ def test_cron(resource)
71
+ test = [desciption(resource)]
72
+ cron_entry = "#{resource.minute} " \
73
+ "#{resource.hour} " \
74
+ "#{resource.day} " \
75
+ "#{resource.month} " \
76
+ "#{resource.weekday} " \
77
+ "#{resource.command}"
78
+ test << "it { should have_entry('#{cron_entry}').with_user('#{resource.user}') }"
79
+ test << "end"
80
+ test.join("\n")
81
+ end
82
+
83
+ def test_file(resource)
84
+ test = ["describe file('#{resource.path}') do"]
85
+ if resource.declared_type =~ /directory/
86
+ test << "it { should be_directory }"
87
+ else
88
+ test << "it { should be_file }"
89
+ end
90
+ # Sometimes we see GIDs instead of group names.
91
+ if !resource.group.nil?
92
+ unless resource.group.is_a?(String) && resource.group.empty?
93
+ test << "it { should be_grouped_into '#{resource.group}' }"
94
+ end
95
+ end
96
+ # Guard for UIDs versus usernames as well.
97
+ if !resource.owner.nil?
98
+ unless resource.owner.is_a?(String) && resource.owner.empty?
99
+ test << "it { should be_owned_by '#{resource.owner}' }"
100
+ end
101
+ end
102
+ if !resource.mode.nil?
103
+ unless resource.mode.is_a?(String) && !resource.mode.empty?
104
+ test << "it { should be_mode '#{resource.mode}' }"
105
+ end
106
+ end
107
+ test << "end"
108
+ test.join("\n")
109
+ end
110
+ alias_method :test_cookbook_file, :test_file
111
+ alias_method :test_directory, :test_file
112
+ alias_method :test_remote_file, :test_file
113
+ alias_method :test_remote_directory, :test_file
114
+ alias_method :test_template, :test_file
115
+
116
+ def test_group(resource)
117
+ test = [desciption(resource)]
118
+ test << "it { should exist }"
119
+ test << "end"
120
+ test.join("\n")
121
+ end
122
+
123
+ def test_package(resource)
124
+ test = [desciption(resource)]
125
+ if !resource.version.nil? && !resource.version.empty?
126
+ test << "it { should be_installed.with_version('#{resource.version}') }"
127
+ else
128
+ test << "it { should be_installed }"
129
+ end
130
+ test << "end"
131
+ test.join("\n")
132
+ end
133
+
134
+ def test_user(resource)
135
+ test = [desciption(resource)]
136
+ test << "it { should exist }"
137
+ # Guard for GIDs rather than strings. Chef aliases the #group method
138
+ # to the #gid method.
139
+ if !resource.gid.nil?
140
+ unless resource.gid.is_a?(String) && !resource.gid.empty?
141
+ test << "it { should belong_to_primary_group '#{resource.gid}' }"
142
+ end
143
+ end
144
+ if !resource.home.nil? && !resource.home.empty?
145
+ test << "it { should have_home_directory '#{resource.home}' }"
146
+ end
147
+ test << "end"
148
+ test.join("\n")
149
+ end
150
+
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,71 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Umami
16
+ module Helper
17
+ module OS
18
+ require 'rbconfig'
19
+
20
+ # Attempt to determine the appropriate platform to use when instantiating
21
+ # a ChefSpec runner object.
22
+ # Refer to https://github.com/chefspec/fauxhai/blob/master/PLATFORMS.md
23
+ def os
24
+ @os ||= (
25
+ host_os = RbConfig::CONFIG['host_os']
26
+ case host_os
27
+ when /aix/
28
+ # `oslevel` => '7.1.0.0'
29
+ version = `oslevel`[0..2]
30
+ {platform: 'aix', version: version}
31
+ when /darwin|mac os/
32
+ version = `sw_vers -productVersion`.chomp
33
+ {platform: 'mac_os_x', version: version}
34
+ when /linux/
35
+ # Perform very basic tests to determine distribution.
36
+ if File.exist?('/etc/centos-release')
37
+ version = File.read('/etc/redhat-release').split[2]
38
+ {platform: 'centos', version: version}
39
+ elsif File.exist?('/etc/redhat-release') # True for CentOS too...
40
+ version = File.read('/etc/redhat-release').split[6]
41
+ {platform: 'redhat', version: version}
42
+ else
43
+ {platform: 'centos', version: '7.3.1611'} # Default to something reasonably sane.
44
+ end
45
+ when /solaris/
46
+ version = `uname -r`.chomp # Release level (i.e. 5.11).
47
+ {platform: 'solaris', version: version}
48
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
49
+ # Refer to https://en.wikipedia.org/wiki/Ver_(command)
50
+ win_version = `ver`.chomp.split("Version ")[1].gsub(/]/, '')
51
+ case win_version
52
+ when /^6.1.7/
53
+ version = '2008R2' # Also Win 7
54
+ when /^6.2/
55
+ version = '2012' # Also Win 8
56
+ when /^6.3/
57
+ version = '2012R2' # Also Win 8.1
58
+ when /^(6.4|10)/
59
+ version = '10'
60
+ end
61
+ {platform: 'windows', version: version}
62
+ else
63
+ # Default to something reasonably sane.
64
+ {platform: 'centos', version: '7.3.1611'}
65
+ end
66
+ )
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Umami
16
+ module Logger
17
+
18
+ # Print messages.
19
+ # TODO: Flesh this out so it supports different levels (i.e. info, warn).
20
+ def log(msg = '', level = nil)
21
+ puts msg
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Umami
16
+ class Policyfile
17
+ end
18
+ end
@@ -0,0 +1,107 @@
1
+ # Copyright 2017 Bloomberg Finance, L.P.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'chef-dk/policyfile_services/install'
16
+ require 'chef-dk/policyfile_services/export_repo'
17
+ require 'chef-dk/ui'
18
+ require 'tmpdir' # Extends Dir
19
+
20
+ module Umami
21
+ class Policyfile
22
+ class Exporter
23
+
24
+ attr_reader :chef_config_file
25
+ attr_reader :cookbook_dir
26
+ attr_reader :export_root
27
+ attr_reader :export_path
28
+ attr_accessor :policyfile_lock_file
29
+ attr_reader :policyfile
30
+
31
+ def initialize(policyfile_lock_file = nil, cookbook_dir = nil, policyfile = nil)
32
+ @policyfile = policyfile
33
+ @export_root = Dir.mktmpdir('umami-')
34
+ # We need the target dir named the same as the source dir so that `chef` commands
35
+ # work as happily programatically as they would via the command line.
36
+ # This is because the commands assume they're being run from within a cookbook
37
+ # directory.
38
+ @export_path = File.join(export_root, cookbook_dir)
39
+ @chef_config_file = "#{export_path}/.chef/config.rb"
40
+ end
41
+
42
+ def policyfile
43
+ @policyfile
44
+ end
45
+
46
+ def ui
47
+ @ui ||= ChefDK::UI.new
48
+ end
49
+
50
+ # Execute `chef install` to ensure we get a fresh, clean Policyfile lock
51
+ # file on each run.
52
+ def install_policy
53
+ puts "Generating a new Policyfile from '#{policyfile}'..."
54
+ install_service = ChefDK::PolicyfileServices::Install.new(
55
+ policyfile: policyfile,
56
+ ui: ui
57
+ )
58
+ @policyfile_lock_file = install_service.storage_config.policyfile_lock_filename
59
+ install_service.run
60
+ end
61
+
62
+ def fake_client_key
63
+ "#{export_path}/umami.pem"
64
+ end
65
+
66
+ def cp_fake_client_key
67
+ # Create a fake client cert based on a dummy cert we have laying around.
68
+ fake_client_key_src = File.join(File.dirname(__FILE__), %w(.. .. .. support umami.pem))
69
+ FileUtils.cp(fake_client_key_src, fake_client_key)
70
+ end
71
+
72
+ def update_chef_config
73
+ File.open(chef_config_file, 'a') do |f|
74
+ f.puts "chef_server_url 'http://127.0.0.1:8889'"
75
+ f.puts "cookbook_path ['#{export_path}/cookbook_artifacts']"
76
+ f.puts "client_key '#{fake_client_key}'"
77
+ f.puts "node_name 'umami-node'"
78
+ end
79
+ end
80
+
81
+ # Export the cookbook and prepare a chef-zero-compatible directory.
82
+ # We'll use this as a temporary launch pad for things, as needed, akin
83
+ # to test-kitchen's sandbox.
84
+ def export
85
+ install_policy
86
+ export_service = ChefDK::PolicyfileServices::ExportRepo.new(
87
+ policyfile: policyfile_lock_file,
88
+ export_dir: export_path
89
+ )
90
+ begin
91
+ export_service.run
92
+ rescue ChefDK::PolicyfileExportRepoError => e
93
+ puts "\nFAILED TO EXPORT POLICYFILE: #{e.message} (#{e.class})"
94
+ puts "CAUSE: #{e.cause}"
95
+ puts "BACKTRACE:"
96
+ e.backtrace.each do |line|
97
+ puts "\t#{line}"
98
+ end
99
+ exit(1)
100
+ end
101
+ cp_fake_client_key
102
+ update_chef_config
103
+ end
104
+
105
+ end
106
+ end
107
+ end