sfn 3.0.10 → 3.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/docs/README.md +1 -0
- data/docs/lint.md +72 -0
- data/docs/sparkle-packs.md +75 -0
- data/lib/sfn.rb +1 -0
- data/lib/sfn/command.rb +1 -0
- data/lib/sfn/command/lint.rb +87 -0
- data/lib/sfn/config.rb +1 -0
- data/lib/sfn/config/lint.rb +28 -0
- data/lib/sfn/lint.rb +12 -0
- data/lib/sfn/lint/definition.rb +57 -0
- data/lib/sfn/lint/rule.rb +118 -0
- data/lib/sfn/lint/rule_set.rb +177 -0
- data/lib/sfn/version.rb +1 -1
- data/sfn.gemspec +1 -0
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 844e08d945b817d040cd227631e30a62bb2bc591
|
4
|
+
data.tar.gz: a0b766931a02119319fa21c1b1e9158706ec2320
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fcb7c461101e3c7de5e253e4e024c3cbcd05c41277ae258e325f696a4dc88c10e74d491ea932be375075e43e7587409402c7147d7aa78f235f1348fb9d800b5
|
7
|
+
data.tar.gz: f5944d0b75b1356a5467ca6180ccecd68e5661f888982726f13d1dad7b3f8e98ffbac0fd78ac267a18c7192c87749adb70e4c2137c0cd5e75488970b78dbcf22
|
data/CHANGELOG.md
CHANGED
data/docs/README.md
CHANGED
data/docs/lint.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
---
|
2
|
+
title: "Lint"
|
3
|
+
weight: 5
|
4
|
+
---
|
5
|
+
|
6
|
+
## Lint
|
7
|
+
|
8
|
+
The lint framework built within the sfn tool utilizes the JMESPath query language
|
9
|
+
for identifying patterns and apply validation rules.
|
10
|
+
|
11
|
+
### Lint RuleSets
|
12
|
+
|
13
|
+
#### Local
|
14
|
+
|
15
|
+
Create rule sets using the generator. Each file must contain a single
|
16
|
+
lint rule set. Below is a simple rule set used to flag non-AWS type
|
17
|
+
resources:
|
18
|
+
|
19
|
+
~~~ruby
|
20
|
+
# tests/lint/resource_type_check.rb
|
21
|
+
|
22
|
+
RuleSet.build(:test) do
|
23
|
+
rule :aws_resources_only do
|
24
|
+
definition 'Resources.[*][0][*].Type' do |search|
|
25
|
+
unless(search.nil?)
|
26
|
+
result = search.find_all{|i| !i.start_with?('AWS')}
|
27
|
+
result.empty? ? true : result
|
28
|
+
else
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
fail_message 'All types must be within AWS root namespace'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
~~~
|
37
|
+
|
38
|
+
#### Library
|
39
|
+
|
40
|
+
When a rule set is defined within a library, it must use the full constant namespace and
|
41
|
+
must register to allow access to the rule set:
|
42
|
+
|
43
|
+
~~~ruby
|
44
|
+
my_ruleset = Sfn::Lint::RuleSet.build(:test) do
|
45
|
+
rule :aws_resources_only do
|
46
|
+
definition 'Resources.[*][0][*].Type' do |search|
|
47
|
+
unless(search.nil?)
|
48
|
+
result = search.find_all{|i| !i.start_with?('AWS')}
|
49
|
+
result.empty? ? true : result
|
50
|
+
else
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
fail_message 'All types must be within AWS root namespace'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
Sfn::Lint::RuleSet.register(my_ruleset)
|
59
|
+
~~~
|
60
|
+
|
61
|
+
### Usage
|
62
|
+
|
63
|
+
By default `sfn` will apply any registered rule sets that are defined for the target provider.
|
64
|
+
|
65
|
+
#### Local
|
66
|
+
|
67
|
+
Provide a template and lint directory to the `lint` command. For example,
|
68
|
+
if lint rule sets are defined within `tests/lint`:
|
69
|
+
|
70
|
+
~~~
|
71
|
+
$ sfn lint --file my-template --lint-directory tests/lint
|
72
|
+
~~~
|
@@ -0,0 +1,75 @@
|
|
1
|
+
---
|
2
|
+
title: "SparklePacks"
|
3
|
+
weight: 4
|
4
|
+
anchors:
|
5
|
+
- title: "Enabling SparklePacks"
|
6
|
+
url: "#enabling-sparklepacks"
|
7
|
+
---
|
8
|
+
|
9
|
+
## What is a SparklePack?
|
10
|
+
|
11
|
+
SparklePacks are implemented as a feature of the SparkleFormation library,
|
12
|
+
providing a means to package SparkleFormation building blocks
|
13
|
+
and templates as reusable, redistributable software artifacts.
|
14
|
+
A SparklePack may package up any combination of SparkleFormation
|
15
|
+
[building blocks](http://www.sparkleformation.io/docs/sparkle_formation/building-blocks.html) and templates.
|
16
|
+
|
17
|
+
sfn supports loading SparklePacks distributed as [Ruby gems](http://www.sparkleformation.io/docs/sparkle_formation/sparkle-packs.html#distribution).
|
18
|
+
You can find published SparklePacks on the RubyGems site by
|
19
|
+
[searching for the sparkle-pack prefix](https://rubygems.org/search?query=sparkle-pack).
|
20
|
+
|
21
|
+
### Enabling SparklePacks
|
22
|
+
|
23
|
+
The following examples use the [sparkle-pack-aws-availability-zones](https://rubygems.org/gems/sparkle-pack-aws-availability-zones) gem.
|
24
|
+
In reviewing [the source code of that project on Github](https://github.com/hw-labs/sparkle-pack-aws-availability-zones),
|
25
|
+
note that it provides a [`zones` registry](https://github.com/hw-labs/sparkle-pack-aws-availability-zones/blob/v0.1.2/lib/sparkleformation/registry/get_azs.rb)
|
26
|
+
which uses the aws-sdk-core library to return an array of available AZs.
|
27
|
+
|
28
|
+
When using sfn with Bundler, we'll add any SparklePacks we
|
29
|
+
want to enable to the `sfn` group in our Gemfile:
|
30
|
+
|
31
|
+
~~~ruby
|
32
|
+
# Gemfile
|
33
|
+
source 'https://rubygems.org'
|
34
|
+
|
35
|
+
gem 'sfn'
|
36
|
+
|
37
|
+
group :sfn do
|
38
|
+
gem 'sparkle-pack-aws-availability-zones'
|
39
|
+
end
|
40
|
+
~~~
|
41
|
+
|
42
|
+
After running `bundle`, the SparklePack is installed but not yet enabled:
|
43
|
+
|
44
|
+
~~~
|
45
|
+
$ cat sparkleformation/zones_test.rb
|
46
|
+
SparkleFormation.new(:zones_test) do
|
47
|
+
zones registry!(:zones)
|
48
|
+
end
|
49
|
+
|
50
|
+
$ bundle exec sfn print --file zones_test
|
51
|
+
ERROR: SparkleFormation::Error::NotFound::Registry: Failed to locate item named: `zones`
|
52
|
+
~~~
|
53
|
+
|
54
|
+
Adding the gem to an array of `sparkle_packs` in
|
55
|
+
the `.sfn` configuration file will activate it for use:
|
56
|
+
|
57
|
+
~~~ruby
|
58
|
+
Configuration.new do
|
59
|
+
sparkle_pack [ 'sparkle-pack-aws-availability-zones' ]
|
60
|
+
end
|
61
|
+
~~~
|
62
|
+
|
63
|
+
Invoking `zones` registry in the template is now functional:
|
64
|
+
|
65
|
+
~~~
|
66
|
+
$ bundle exec sfn print --file zones_test
|
67
|
+
{
|
68
|
+
"Zones": [
|
69
|
+
"us-east-1a",
|
70
|
+
"us-east-1b",
|
71
|
+
"us-east-1c",
|
72
|
+
"us-east-1e"
|
73
|
+
]
|
74
|
+
}
|
75
|
+
~~~
|
data/lib/sfn.rb
CHANGED
data/lib/sfn/command.rb
CHANGED
@@ -17,6 +17,7 @@ module Sfn
|
|
17
17
|
autoload :Import, 'sfn/command/import'
|
18
18
|
autoload :Init, 'sfn/command/init'
|
19
19
|
autoload :Inspect, 'sfn/command/inspect'
|
20
|
+
autoload :Lint, 'sfn/command/lint'
|
20
21
|
autoload :List, 'sfn/command/list'
|
21
22
|
autoload :Print, 'sfn/command/print'
|
22
23
|
autoload :Promote, 'sfn/command/promote'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'sfn'
|
2
|
+
|
3
|
+
module Sfn
|
4
|
+
class Command
|
5
|
+
# Lint command
|
6
|
+
class Lint < Command
|
7
|
+
|
8
|
+
include Sfn::CommandModule::Base
|
9
|
+
include Sfn::CommandModule::Template
|
10
|
+
|
11
|
+
# Perform linting
|
12
|
+
def execute!
|
13
|
+
print_only_original = config[:print_only]
|
14
|
+
config[:print_only] = true
|
15
|
+
file = load_template_file
|
16
|
+
ui.info "#{ui.color("Template Linting (#{provider.connection.provider}): ", :bold)} #{config[:file].sub(Dir.pwd, '').sub(%r{^/}, '')}"
|
17
|
+
config[:print_only] = print_only_original
|
18
|
+
|
19
|
+
raw_template = parameter_scrub!(template_content(file))
|
20
|
+
|
21
|
+
if(config[:print_only])
|
22
|
+
ui.puts raw_template
|
23
|
+
else
|
24
|
+
result = lint_template(raw_template)
|
25
|
+
if(result == true)
|
26
|
+
ui.puts ui.color(' -> VALID', :green, :bold)
|
27
|
+
else
|
28
|
+
ui.puts ui.color(' -> INVALID', :red, :bold)
|
29
|
+
result.each do |failure|
|
30
|
+
ui.error "Result Set: #{ui.color(failure[:rule_set].name, :red, :bold)}"
|
31
|
+
failure[:failures].each do |f_msg|
|
32
|
+
ui.puts "#{ui.color(' *', :red, :bold)} #{f_msg}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
raise 'Linting failure'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Apply linting to given template
|
41
|
+
#
|
42
|
+
# @param template [Hash]
|
43
|
+
# @return [TrueClass, Array<Smash[:rule_set, :failures]>]
|
44
|
+
def lint_template(template)
|
45
|
+
results = rule_sets.map do |set|
|
46
|
+
result = set.apply(template)
|
47
|
+
unless(result == true)
|
48
|
+
Smash.new(:rule_set => set, :failures => result)
|
49
|
+
end
|
50
|
+
end.compact
|
51
|
+
results.empty? ? true : results
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Array<Sfn::Lint::RuleSet>]
|
55
|
+
def rule_sets
|
56
|
+
sets = [config[:lint_directory]].flatten.compact.map do |directory|
|
57
|
+
if(File.directory?(directory))
|
58
|
+
files = Dir.glob(File.join(directory, '**', '**', '*.rb'))
|
59
|
+
files.map do |path|
|
60
|
+
begin
|
61
|
+
Sfn::Lint.class_eval(
|
62
|
+
IO.read(path), path, 1
|
63
|
+
)
|
64
|
+
rescue
|
65
|
+
ui.warn "Failed to load detected file: #{path}"
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end.flatten.compact.find_all{|rs| rs.provider == provider.connection.provider}
|
71
|
+
unless(config[:local_rule_sets_only])
|
72
|
+
sets += Sfn::Lint::RuleSet.get_all(provider.connection.provider)
|
73
|
+
end
|
74
|
+
if(config[:disabled_rule_set])
|
75
|
+
disabled = [config[:disabled_rule_set]].flatten.compact
|
76
|
+
sets.delete_if{|i| disabled.include?(i.name.to_s) }
|
77
|
+
end
|
78
|
+
if(config[:enabled_rule_set])
|
79
|
+
enabled = [config[:enabled_rule_set]].flatten.compact
|
80
|
+
sets.delete_if{|i| enabled.include?(i.name.to_s) }
|
81
|
+
end
|
82
|
+
sets
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/sfn/config.rb
CHANGED
@@ -52,6 +52,7 @@ module Sfn
|
|
52
52
|
autoload :Import, 'sfn/config/import'
|
53
53
|
autoload :Init, 'sfn/config/init'
|
54
54
|
autoload :Inspect, 'sfn/config/inspect'
|
55
|
+
autoload :Lint, 'sfn/config/lint'
|
55
56
|
autoload :List, 'sfn/config/list'
|
56
57
|
autoload :Print, 'sfn/config/print'
|
57
58
|
autoload :Promote, 'sfn/config/promote'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'sfn'
|
2
|
+
|
3
|
+
module Sfn
|
4
|
+
class Config
|
5
|
+
# Lint command configuration
|
6
|
+
class Lint < Validate
|
7
|
+
attribute(
|
8
|
+
:lint_directory, String,
|
9
|
+
:description => 'Directory containing lint rule sets',
|
10
|
+
:multiple => true
|
11
|
+
)
|
12
|
+
attribute(
|
13
|
+
:disabled_rule_set, String,
|
14
|
+
:description => 'Disable rule set from being applied',
|
15
|
+
:multiple => true
|
16
|
+
)
|
17
|
+
attribute(
|
18
|
+
:enabled_rule_set, String,
|
19
|
+
:description => 'Only apply this rule set',
|
20
|
+
:multiple => true
|
21
|
+
)
|
22
|
+
attribute(
|
23
|
+
:local_rule_sets_only, [TrueClass, FalseClass],
|
24
|
+
:description => 'Only apply rule sets provided by lint directory'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/sfn/lint.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'sfn'
|
2
|
+
|
3
|
+
module Sfn
|
4
|
+
module Lint
|
5
|
+
# Lint defition
|
6
|
+
class Definition
|
7
|
+
|
8
|
+
# @return [String] search expression used for matching
|
9
|
+
attr_reader :search_expression
|
10
|
+
# @return [Proc-ish] must respond to #call
|
11
|
+
attr_reader :evaluator
|
12
|
+
# @return [Symbol] target provider
|
13
|
+
attr_reader :provider
|
14
|
+
|
15
|
+
# Create a new definition
|
16
|
+
#
|
17
|
+
# @param expr [String] search expression used for matching
|
18
|
+
# @param provider [String, Symbol] target provider
|
19
|
+
# @param evaluator [Proc] logic used to handle match
|
20
|
+
# @return [self]
|
21
|
+
def initialize(expr, provider=:aws, evaluator=nil, &block)
|
22
|
+
if(evaluator && block)
|
23
|
+
raise ArgumentError.new 'Only evaluator or block can be provided, not both.'
|
24
|
+
end
|
25
|
+
@provider = Bogo::Utility.snake(provider).to_sym
|
26
|
+
@search_expression = expr
|
27
|
+
@evaluator = evaluator || block
|
28
|
+
end
|
29
|
+
|
30
|
+
# Apply definition to template
|
31
|
+
#
|
32
|
+
# @param template [Hash] template being processed
|
33
|
+
# @return [TrueClass, Array<String>] true if passed. List of string results that failed
|
34
|
+
def apply(template)
|
35
|
+
result = JMESPath.search(search_expression, template)
|
36
|
+
run(result, template)
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
# Check result of search expression
|
42
|
+
#
|
43
|
+
# @param result [Object] result(s) of search expression
|
44
|
+
# @param template [Hash] full template
|
45
|
+
# @return [TrueClass, Array<String>] true if passed. List of string results that failed
|
46
|
+
# @note override this method when subclassing
|
47
|
+
def run(result, template)
|
48
|
+
unless(evaluator)
|
49
|
+
raise NotImplementedError.new 'No evaluator has been defined for this definition!'
|
50
|
+
end
|
51
|
+
evaluator.call(result, template)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'sfn'
|
2
|
+
|
3
|
+
module Sfn
|
4
|
+
module Lint
|
5
|
+
# Composition of definitions
|
6
|
+
class Rule
|
7
|
+
|
8
|
+
# @return [Symbol] name of rule
|
9
|
+
attr_reader :name
|
10
|
+
# @return [Array<Definition>] definitions composing rule
|
11
|
+
attr_reader :definitions
|
12
|
+
# @return [String] message describing failure
|
13
|
+
attr_reader :fail_message
|
14
|
+
# @return [Symbol] target provider
|
15
|
+
attr_reader :provider
|
16
|
+
|
17
|
+
# Create a new rule
|
18
|
+
#
|
19
|
+
# @param name [String, Symbol] name of rule
|
20
|
+
# @param definitions [Array<Definition>] definitions composing rule
|
21
|
+
# @param fail_message [String] message to describe failure
|
22
|
+
# @param provider [String, Symbol] target provider
|
23
|
+
# @return [self]
|
24
|
+
def initialize(name, definitions, fail_message, provider=:aws)
|
25
|
+
@name = name.to_sym
|
26
|
+
@definitions = definitions.dup.uniq.freeze
|
27
|
+
@fail_message = fail_message
|
28
|
+
@provider = Bogo::Utility.snake(provider).to_sym
|
29
|
+
validate_definitions!
|
30
|
+
end
|
31
|
+
|
32
|
+
# Generate the failure message for this rule with given failure
|
33
|
+
# result set.
|
34
|
+
def generate_fail_message(results)
|
35
|
+
msg = fail_message.dup
|
36
|
+
unless(results.empty?)
|
37
|
+
failed_items = results.map do |item|
|
38
|
+
f_item = item[:failures]
|
39
|
+
next if f_item.nil? || f_item == true || f_item == false
|
40
|
+
f_item
|
41
|
+
end.flatten.compact.map(&:to_s)
|
42
|
+
unless(failed_items.empty?)
|
43
|
+
msg = "#{msg} (failures: `#{failed_items.join('`, `')}`)"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
msg
|
47
|
+
end
|
48
|
+
|
49
|
+
# Apply all definitions to template
|
50
|
+
#
|
51
|
+
# @param template [Hash]
|
52
|
+
# @return [TrueClass, Array<Smash[:definition, :failures]>] true if passed. Definition failures if failed.
|
53
|
+
def apply(template)
|
54
|
+
results = definitions.map do |definition|
|
55
|
+
result = definition.apply(template)
|
56
|
+
result == true ? result : Smash.new(:definition => definition, :failures => result)
|
57
|
+
end
|
58
|
+
if(results.all?{|item| item == true})
|
59
|
+
true
|
60
|
+
else
|
61
|
+
results.delete_if{|item| item == true}
|
62
|
+
results
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if template passes this rule
|
67
|
+
#
|
68
|
+
# @param template [Hash]
|
69
|
+
# @return [TrueClass, FalseClass]
|
70
|
+
def pass?(template)
|
71
|
+
apply(template) == true
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check if template fails this rule
|
75
|
+
#
|
76
|
+
# @param template [Hash]
|
77
|
+
# @return [TrueClass, FalseClass]
|
78
|
+
def fail?(template)
|
79
|
+
!pass?(template)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add a new definition to the collection
|
83
|
+
#
|
84
|
+
# @param definition [Definition] new definition to add
|
85
|
+
# @return [self]
|
86
|
+
def add_definition(definition)
|
87
|
+
new_defs = definitions.dup
|
88
|
+
new_defs << definition
|
89
|
+
@definitions = new_defs.uniq.freeze
|
90
|
+
validate_definitions!
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
# Remove a definition from the collection
|
95
|
+
#
|
96
|
+
# @param definition [Definition] definition to remove
|
97
|
+
# @return [self]
|
98
|
+
def remove_definition(definition)
|
99
|
+
new_defs = definitions.dup
|
100
|
+
new_defs.delete(definition)
|
101
|
+
@definitions = new_defs.uniq.freeze
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
# Check that provided definitions provider match rule defined provider
|
106
|
+
def validate_definitions!
|
107
|
+
non_match = definitions.find_all do |definition|
|
108
|
+
definition.provider != provider
|
109
|
+
end
|
110
|
+
unless(non_match.empty?)
|
111
|
+
raise ArgumentError.new "Rule defines `#{provider}` as provider but includes definitions for " \
|
112
|
+
"non matching providers. (#{non_match.map(&:provider).map(&:to_s).uniq.sort.join(', ')})"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'sfn'
|
2
|
+
|
3
|
+
module Sfn
|
4
|
+
module Lint
|
5
|
+
# Named collection of rules
|
6
|
+
class RuleSet
|
7
|
+
|
8
|
+
# Helper class for ruleset generation
|
9
|
+
class Creator
|
10
|
+
|
11
|
+
attr_reader :items, :provider
|
12
|
+
|
13
|
+
def initialize(provider)
|
14
|
+
@provider = provider
|
15
|
+
@items = []
|
16
|
+
end
|
17
|
+
|
18
|
+
class RuleSet < Creator
|
19
|
+
|
20
|
+
def rule(name, &block)
|
21
|
+
r = Rule.new(provider)
|
22
|
+
r.instance_exec(&block)
|
23
|
+
items << Sfn::Lint::Rule.new(name, r.items, r.fail_message, provider)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class Rule < Creator
|
29
|
+
|
30
|
+
def definition(expr, evaluator=nil, &block)
|
31
|
+
items << Sfn::Lint::Definition.new(expr, provider, evaluator, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fail_message(val=nil)
|
35
|
+
unless(val.nil?)
|
36
|
+
@fail_message = val
|
37
|
+
end
|
38
|
+
@fail_message
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
|
47
|
+
@@_rule_set_registry = Smash.new
|
48
|
+
|
49
|
+
# RuleSet generator helper for quickly building simple rule sets
|
50
|
+
#
|
51
|
+
# @param name [String] name of rule set
|
52
|
+
# @param provider [String, Symbol] target provider
|
53
|
+
# @yieldblock rule set content
|
54
|
+
def build(name, provider=:aws, &block)
|
55
|
+
provider = Bogo::Utility.snake(provider).to_sym
|
56
|
+
rs = Creator::RuleSet.new(provider)
|
57
|
+
rs.instance_exec(&block)
|
58
|
+
self.new(name, provider, rs.items)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Register a rule set
|
62
|
+
#
|
63
|
+
# @param rule_set [RuleSet]
|
64
|
+
# @return [TrueClass]
|
65
|
+
def register(rule_set)
|
66
|
+
@@_rule_set_registry.set(rule_set.provider, rule_set.name, rule_set)
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get registered rule set
|
71
|
+
#
|
72
|
+
# @param name [String] name of rule set
|
73
|
+
# @param provider [String] target provider
|
74
|
+
# @return [RuleSet, NilClass]
|
75
|
+
def get(name, provider=:aws)
|
76
|
+
provider = Bogo::Utility.snake(provider)
|
77
|
+
@@_rule_set_registry.get(provider, name)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get all rule sets for specified provider
|
81
|
+
#
|
82
|
+
# @param provider [String] target provider
|
83
|
+
# @return [Array<RuleSet>]
|
84
|
+
def get_all(provider=:aws)
|
85
|
+
@@_rule_set_registry.fetch(provider, {}).values
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
include Bogo::Memoization
|
91
|
+
|
92
|
+
# @return [Symbol] name
|
93
|
+
attr_reader :name
|
94
|
+
# @return [Symbol] target provider
|
95
|
+
attr_reader :provider
|
96
|
+
# @return [Array<Rule>] rules of set
|
97
|
+
attr_reader :rules
|
98
|
+
|
99
|
+
# Create new rule set
|
100
|
+
#
|
101
|
+
# @param name [String, Symbol] name of rule set
|
102
|
+
# @param provider [String, Symbol] name of target provider
|
103
|
+
# @param rules [Array<Rule>] list of rules defining this set
|
104
|
+
# @return [self]
|
105
|
+
def initialize(name, provider=:aws, rules=[])
|
106
|
+
@name = name.to_sym
|
107
|
+
@provider = Bogo::Utility.snake(provider).to_sym
|
108
|
+
@rules = rules.dup.uniq.freeze
|
109
|
+
validate_rules!
|
110
|
+
end
|
111
|
+
|
112
|
+
# Add a new rule to the collection
|
113
|
+
#
|
114
|
+
# @param rule [Rule] new rule to add
|
115
|
+
# @return [self]
|
116
|
+
def add_rule(rule)
|
117
|
+
new_rules = rules.dup
|
118
|
+
new_rules << rule
|
119
|
+
@rules = new_rules.uniq.freeze
|
120
|
+
validate_rules!
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
# Remove a rule from the collection
|
125
|
+
#
|
126
|
+
# @param rule [Rule] rule to remove
|
127
|
+
# @return [self]
|
128
|
+
def remove_rule(rule)
|
129
|
+
new_rules = rules.dup
|
130
|
+
new_rules.delete(rule)
|
131
|
+
@rules = new_rules.uniq.freeze
|
132
|
+
self
|
133
|
+
end
|
134
|
+
|
135
|
+
# Apply rule set to template.
|
136
|
+
#
|
137
|
+
# @param template [Hash]
|
138
|
+
# @return [TrueClass, Array<String>] true on success, list failure messages on failure
|
139
|
+
def apply(template)
|
140
|
+
failures = collect_failures(template)
|
141
|
+
if(failures.empty?)
|
142
|
+
true
|
143
|
+
else
|
144
|
+
failures.map do |failure|
|
145
|
+
failure[:rule].generate_fail_message(failure[:result])
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Process template through rules defined in this set and
|
151
|
+
# store failure information
|
152
|
+
#
|
153
|
+
# @param template [Hash]
|
154
|
+
# @return [Array<Rule>] list of failures
|
155
|
+
def collect_failures(template)
|
156
|
+
results = rules.map do |rule|
|
157
|
+
result = rule.apply(template)
|
158
|
+
result == true ? true : Smash.new(:rule => rule, :result => result)
|
159
|
+
end
|
160
|
+
results.delete_if{|i| i == true}
|
161
|
+
results
|
162
|
+
end
|
163
|
+
|
164
|
+
# Check that provided rules provider match rule set defined provider
|
165
|
+
def validate_rules!
|
166
|
+
non_match = rules.find_all do |rule|
|
167
|
+
rule.provider != provider
|
168
|
+
end
|
169
|
+
unless(non_match.empty?)
|
170
|
+
raise ArgumentError.new "Rule set defines `#{provider}` as provider but includes rules for " \
|
171
|
+
"non matching providers. (#{non_match.map(&:provider).map(&:to_s).uniq.sort.join(', ')})"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
data/lib/sfn/version.rb
CHANGED
data/sfn.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.add_runtime_dependency 'miasma-open-stack', '>= 0.1.0', '< 0.3'
|
19
19
|
s.add_runtime_dependency 'miasma-rackspace', '>= 0.1.0', '< 0.3'
|
20
20
|
s.add_runtime_dependency 'miasma-google', '>= 0.1.0', '< 0.3'
|
21
|
+
s.add_runtime_dependency 'jmespath'
|
21
22
|
s.add_runtime_dependency 'net-ssh'
|
22
23
|
s.add_runtime_dependency 'sparkle_formation', '>= 3.0.3', '< 4'
|
23
24
|
s.add_runtime_dependency 'hashdiff', '~> 0.2.2'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sfn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Roberts
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bogo-cli
|
@@ -170,6 +170,20 @@ dependencies:
|
|
170
170
|
- - "<"
|
171
171
|
- !ruby/object:Gem::Version
|
172
172
|
version: '0.3'
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: jmespath
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
type: :runtime
|
181
|
+
prerelease: false
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
173
187
|
- !ruby/object:Gem::Dependency
|
174
188
|
name: net-ssh
|
175
189
|
requirement: !ruby/object:Gem::Requirement
|
@@ -317,7 +331,9 @@ files:
|
|
317
331
|
- docs/images/d_list.png
|
318
332
|
- docs/images/d_update.png
|
319
333
|
- docs/images/d_validate.png
|
334
|
+
- docs/lint.md
|
320
335
|
- docs/overview.md
|
336
|
+
- docs/sparkle-packs.md
|
321
337
|
- docs/usage.md
|
322
338
|
- docs/v/0.3.2/marked.js
|
323
339
|
- docs/v/bootstrap.min.css
|
@@ -347,6 +363,7 @@ files:
|
|
347
363
|
- lib/sfn/command/import.rb
|
348
364
|
- lib/sfn/command/init.rb
|
349
365
|
- lib/sfn/command/inspect.rb
|
366
|
+
- lib/sfn/command/lint.rb
|
350
367
|
- lib/sfn/command/list.rb
|
351
368
|
- lib/sfn/command/print.rb
|
352
369
|
- lib/sfn/command/promote.rb
|
@@ -369,11 +386,16 @@ files:
|
|
369
386
|
- lib/sfn/config/import.rb
|
370
387
|
- lib/sfn/config/init.rb
|
371
388
|
- lib/sfn/config/inspect.rb
|
389
|
+
- lib/sfn/config/lint.rb
|
372
390
|
- lib/sfn/config/list.rb
|
373
391
|
- lib/sfn/config/print.rb
|
374
392
|
- lib/sfn/config/promote.rb
|
375
393
|
- lib/sfn/config/update.rb
|
376
394
|
- lib/sfn/config/validate.rb
|
395
|
+
- lib/sfn/lint.rb
|
396
|
+
- lib/sfn/lint/definition.rb
|
397
|
+
- lib/sfn/lint/rule.rb
|
398
|
+
- lib/sfn/lint/rule_set.rb
|
377
399
|
- lib/sfn/monkey_patch.rb
|
378
400
|
- lib/sfn/monkey_patch/stack.rb
|
379
401
|
- lib/sfn/monkey_patch/stack/azure.rb
|