sfn-serverspec 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3b8afe3d9e73b49ff634ee667f86b20e64ee3d7b
4
+ data.tar.gz: d99d8bc34c182d9e732b62eb190035ca472195e7
5
+ SHA512:
6
+ metadata.gz: 9beffad9fb727f1fd25a4429d52ffe04b9859f4cfbd5c0c0c68d2da706f35fe7c7b03873f1642fa8d742670538e792c19b5caeb2e4f391db26899809ed8e48a4
7
+ data.tar.gz: 2271aac2052a33acd71c7cf9539fec2d26f9449c25af33636a4efb69d357f575c5242a22ff39096a7453f1b5103e3ae830e11123d1e34d99174855cd2c3a8b0c
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # v0.1.0
2
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2015 Heavy Water Operations LLC.
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.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # SparkleFormation ServerSpec Callback
2
+
3
+ sfn-serverspec is a [callback](http://www.sparkleformation.io/docs/sfn/callbacks.html) for [sfn](https://github.com/sparkleformation/sfn) which executes [Serverspec](http://serverspec.org) assertions against stack compute resources after successful stack creation.
4
+
5
+ ## Usage
6
+
7
+ ### Enable
8
+
9
+ Callbacks are configured via the `.sfn` configuration file. First, the callback must be enabled:
10
+
11
+ ```ruby
12
+ Configuration.new do
13
+ callbacks do
14
+ require ['sfn-serverspec']
15
+ default ['serverspec_validator']
16
+ end
17
+ end
18
+ ```
19
+
20
+ ### Configure
21
+
22
+ Some configuration of callback behavior is available via the `.sfn` file:
23
+
24
+ ```ruby
25
+ Configuration.new do
26
+ sfn_serverspec do
27
+ global_spec_patterns [File.join(Dir.pwd, 'spec/base/*_spec.rb')]
28
+ ssh_proxy_command 'ssh ec2-user@server.example.com nc %h %p'
29
+ ssh_user 'ubuntu'
30
+ end
31
+ end
32
+ ```
33
+
34
+ All of the following settings are optional:
35
+
36
+ * `global_spec_patterns` - Array of strings specifying file paths for specs, defaults to `[]`
37
+ * `ssh_proxy_command` - String passed as the [proxy command for remote SSH connection](http://continuousimprovement.me/code/2014/12/03/serverspec-behind-jump-server.html) to the target compute resource, defaults to `nil`
38
+ * `ssh_user` - Username for remote SSH connection, defaults to `ec2-user`
39
+ * `ssh_port` - Port for remote SSH connection, defaults to `22`
40
+ * `ssh_key_paths` - Array of strings describing paths to one or more ssh private keys, defaults to `[]`
41
+ * `ssh_key_passphrase` - String used as passphrase for any encrypted ssh private keys defined in `ssh_key_paths`, defaults to `nil`
42
+
43
+ Additional configuration may be provided in a SparkleFormation template at the resource level:
44
+
45
+ ```ruby
46
+ resources(:my_cool_app_asg) do
47
+ type 'AWS::AutoScaling::AutoScalingGroup'
48
+ properties do
49
+ # ...
50
+ end
51
+ serverspec do
52
+ spec_patterns [File.join(Dir.pwd, '../spec/my_cool_app/*_spec.rb')]
53
+ end
54
+ end
55
+ ```
56
+
57
+ For each resource with a `serverspec` block defined, the resource-level value of `spec_patterns` is combined with the value of `global_spec_patterns` from the .sfn config file to yield a list of specs which will be executed against a given resource.
58
+
59
+ The following configuration options specified at the resource level will override the same options specified in the .sfn config file:
60
+
61
+ * `ssh_user`
62
+ * `ssh_port`
63
+ * `ssh_proxy_command`
64
+ * `ssh_key_paths`
65
+ * `ssh_key_passphrase`
66
+
67
+
68
+ ## Caveats
69
+
70
+ Providing `serverspec` configuration on compute resources works in a manner similar to sfn's built-in Stack Policy callback. Specifically, the `serverspec` key and its contents are removed from the template during compile, cached by the callback and executed after completion of stack creation.
71
+
72
+ As of this writing, the callback only processes Serverspec configuration on `AWS::AutoScaling::AutoScalingGroup` and `AWS::EC2::Instance` resources.
73
+
74
+ Non-compute resources with `serverspec` configuration will not be processed by this callback, and will probably cause a validation error when trying to validate or create a stack from the template.
75
+
76
+ ## Info
77
+
78
+ * Repository: [https://github.com/sparkleformation/sfn-serverspec](https://github.com/sparkleformation/sfn-serverspec)
79
+ * IRC: Freenode @ #sparkleformation
@@ -0,0 +1,2 @@
1
+ require 'sfn-serverspec/version'
2
+ require 'sfn-serverspec/validator'
@@ -0,0 +1,183 @@
1
+ require 'json'
2
+ require 'net/ssh'
3
+ require 'net/ssh/proxy/command'
4
+ require 'rspec'
5
+ require 'rspec/core/formatters/documentation_formatter'
6
+ require 'serverspec'
7
+ require 'sfn'
8
+
9
+ module Sfn
10
+ # This is an sfn callback
11
+ class Callback
12
+ # Validate stack resources against Serverspec assertions
13
+ class ServerspecValidator < Callback
14
+ # @return [Smash] cached policies
15
+ attr_reader :policies
16
+
17
+ # Overload to init policy cache
18
+ #
19
+ # @return [self]
20
+ def initialize(*args)
21
+ super
22
+ @policies = Smash.new
23
+ end
24
+
25
+ def after_create(*args)
26
+ log = Logger.new(STDOUT)
27
+ log.level = Logger.const_get(
28
+ config.fetch(:sfn_serverspec, :log_level, :info).to_s.upcase
29
+ )
30
+
31
+ policies.each do |resource, r_config|
32
+ resource_config = r_config.dump!.to_smash(:snake)
33
+
34
+ ssh_proxy_command = resource_config.fetch(
35
+ :ssh_proxy_command,
36
+ config.fetch(:sfn_serverspec, :ssh_proxy_command, nil)
37
+ )
38
+
39
+ ssh_key_paths = [
40
+ resource_config.fetch(
41
+ :ssh_key_paths,
42
+ config.fetch(:sfn_serverspec, :ssh_key_paths, nil)
43
+ )
44
+ ].flatten.compact
45
+
46
+ ssh_key_passphrase = resource_config.fetch(
47
+ :ssh_key_passphrase,
48
+ config.fetch(:sfn_serverspec, :ssh_key_passphrase, nil)
49
+ )
50
+
51
+ instances = expand_compute_resource(args.first[:api_stack], resource)
52
+
53
+ instances.each do |instance|
54
+ target_host = case ssh_proxy_command.nil?
55
+ when true
56
+ instance.addresses_public.first.address
57
+ when false
58
+ instance.addresses_private.first.address
59
+ end
60
+
61
+ begin
62
+ rspec_config = RSpec.configuration
63
+ rspec_config.reset
64
+ rspec_config.reset_filters
65
+ RSpec.world.reset
66
+
67
+ rspec_formatter = RSpec::Core::Formatters::DocumentationFormatter.new(
68
+ rspec_config.output_stream
69
+ )
70
+
71
+ rspec_reporter = RSpec::Core::Reporter.new(rspec_config)
72
+
73
+ rspec_config.instance_variable_set(:@reporter, rspec_reporter)
74
+ rspec_loader = rspec_config.send(:formatter_loader)
75
+ rspec_notifications = rspec_loader.send(
76
+ :notifications_for,
77
+ RSpec::Core::Formatters::DocumentationFormatter
78
+ )
79
+ rspec_reporter.register_listener(
80
+ rspec_formatter,
81
+ *rspec_notifications
82
+ )
83
+
84
+ global_specs = [
85
+ config.fetch(:sfn_serverspec, :global_spec_patterns, [])
86
+ ].flatten.compact
87
+
88
+ resource_specs = [
89
+ resource_config.fetch(:spec_patterns, [])
90
+ ].flatten.compact
91
+
92
+ spec_patterns = global_specs + resource_specs
93
+
94
+ log.debug "spec loading patterns: #{spec_patterns.inspect}"
95
+ log.debug "using SSH proxy commmand #{ssh_proxy_command}" unless ssh_proxy_command.nil?
96
+ log.info "running specs against #{target_host}"
97
+
98
+ Specinfra.configuration.backend :ssh
99
+ Specinfra.configuration.request_pty true
100
+ Specinfra.configuration.host target_host
101
+
102
+ connection_options = {
103
+ user: resource_config.fetch(
104
+ :ssh_user,
105
+ config.fetch(:sfn_serverspec, :ssh_user, 'ec2-user')
106
+ ),
107
+ port: resource_config.fetch(
108
+ :ssh_port,
109
+ config.fetch(:sfn_serverspec, :ssh_port, 22)
110
+ )
111
+ }
112
+
113
+ unless ssh_proxy_command.nil?
114
+ connection_options[:proxy] = Net::SSH::Proxy::Command.new(ssh_proxy_command)
115
+ log.debug "using ssh proxy command: #{ssh_proxy_command}"
116
+ end
117
+
118
+ unless ssh_key_paths.empty?
119
+ connection_options[:keys] = ssh_key_paths
120
+ log.debug "using ssh key paths #{connection_options[:keys]} exclusively"
121
+ end
122
+
123
+ unless ssh_key_passphrase.nil?
124
+ connection_options[:passphrase] = ssh_key_passphrase
125
+ end
126
+
127
+ Specinfra.configuration.ssh_options connection_options
128
+
129
+ RSpec::Core::Runner.run(spec_patterns.map { |p| Dir.glob(p) })
130
+
131
+ rescue => e
132
+ log.error "Something unexpected happened when running rspec: #{e.inspect}"
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ COMPUTE_RESOURCE_TYPES = ['AWS::EC2::Instance', 'AWS::AutoScaling::AutoScalingGroup']
140
+
141
+ # Generate policy for stack, collate policies in cache
142
+ #
143
+ # @return [nil]
144
+ def template(info)
145
+ compiled_stack = info[:sparkle_stack].compile
146
+
147
+ compiled_stack.resources.keys!.each do |r|
148
+ r_object = compiled_stack.resources[r]
149
+ if COMPUTE_RESOURCE_TYPES.include?(r_object['Type']) && r_object['Serverspec']
150
+ @policies.set(r, r_object.delete!('Serverspec'))
151
+ end
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ # look up stack resource by name, return array of expanded compute instances
158
+ #
159
+ # @param stack [Miasma::Models::Orchestration::Stack]
160
+ # @param name [String]
161
+ # @return [Array<Miasma::Models::Compute::Server>]
162
+ def expand_compute_resource(stack, name)
163
+ compute_resource = stack.resources.all.detect do |resource|
164
+ resource.logical_id == name
165
+ end
166
+
167
+ if compute_resource.within?(:compute, :server)
168
+ [compute_resource.instance.expand]
169
+ else
170
+ compute_resource.expand.servers.map(&:expand)
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ # Override the `#set` method provided by Serverspec to ensure any
177
+ # `spec_helper.rb` files do not clobber our configuration setup
178
+
179
+ alias :serverspec_set :set
180
+
181
+ def set(*args)
182
+ serverspec_set(args.first)
183
+ end
@@ -0,0 +1,5 @@
1
+ # SfnServerspec internal constants
2
+ module SfnServerspec
3
+ # Current version
4
+ VERSION = Gem::Version.new('0.1.0')
5
+ end
@@ -0,0 +1,18 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
+ require 'sfn-serverspec/version'
3
+ Gem::Specification.new do |s|
4
+ s.name = 'sfn-serverspec'
5
+ s.version = SfnServerspec::VERSION.version
6
+ s.summary = 'Executes Serverspec assertions against stack compute resources'
7
+ s.author = 'Heavy Water Operations'
8
+ s.email = 'support@heavywater.io'
9
+ s.homepage = 'http://github.com/sparkleformation/sfn-serverspec'
10
+ s.description = 'Executes Serverspec assertions against stack compute resources'
11
+ s.license = 'Apache-2.0'
12
+ s.require_path = 'lib'
13
+ s.add_runtime_dependency 'sfn', '>= 1.0.0', '< 2.0'
14
+ s.add_runtime_dependency 'serverspec', '~> 2.24'
15
+ s.add_development_dependency 'rake'
16
+ s.add_development_dependency 'rubocop', '~> 0.35.0'
17
+ s.files = Dir['{lib,bin,docs}/**/*'] + %w(sfn-serverspec.gemspec README.md CHANGELOG.md LICENSE)
18
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sfn-serverspec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Heavy Water Operations
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sfn
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: serverspec
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.24'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.24'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rubocop
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 0.35.0
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 0.35.0
75
+ description: Executes Serverspec assertions against stack compute resources
76
+ email: support@heavywater.io
77
+ executables: []
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - CHANGELOG.md
82
+ - LICENSE
83
+ - README.md
84
+ - lib/sfn-serverspec.rb
85
+ - lib/sfn-serverspec/validator.rb
86
+ - lib/sfn-serverspec/version.rb
87
+ - sfn-serverspec.gemspec
88
+ homepage: http://github.com/sparkleformation/sfn-serverspec
89
+ licenses:
90
+ - Apache-2.0
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.4.8
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Executes Serverspec assertions against stack compute resources
112
+ test_files: []
113
+ has_rdoc: