sfn 3.0.10 → 3.0.12
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 +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
|