sfn-serverspec 0.1.0
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 +7 -0
- data/CHANGELOG.md +2 -0
- data/LICENSE +13 -0
- data/README.md +79 -0
- data/lib/sfn-serverspec.rb +2 -0
- data/lib/sfn-serverspec/validator.rb +183 -0
- data/lib/sfn-serverspec/version.rb +5 -0
- data/sfn-serverspec.gemspec +18 -0
- metadata +113 -0
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
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,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,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:
|