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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1dbf7d97959e1120a0d7b1194371c15b7a483ebd
4
- data.tar.gz: beaddca76c30c1d453449939de41693a6ec84554
3
+ metadata.gz: 844e08d945b817d040cd227631e30a62bb2bc591
4
+ data.tar.gz: a0b766931a02119319fa21c1b1e9158706ec2320
5
5
  SHA512:
6
- metadata.gz: ab20f19b50ed8db52d845c51a2194720b875627225d2a44574124b8a06e5a87ebc55382520e4368e068776703d3649738f99e6bfc41b0f074735a5eb33152496
7
- data.tar.gz: 5bce247327ec8b0cbd793c3b28a9b4a994dda012f9f5fb0031e48e17a1306826ff8678c90a7255ffada556c9d35af4677ce577c7fa9b3679b3fd08fff0ea529a
6
+ metadata.gz: 1fcb7c461101e3c7de5e253e4e024c3cbcd05c41277ae258e325f696a4dc88c10e74d491ea932be375075e43e7587409402c7147d7aa78f235f1348fb9d800b5
7
+ data.tar.gz: f5944d0b75b1356a5467ca6180ccecd68e5661f888982726f13d1dad7b3f8e98ffbac0fd78ac267a18c7192c87749adb70e4c2137c0cd5e75488970b78dbcf22
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # v3.0.12
2
+ * [feature] New linting foundation and `lint` command (#208)
3
+
1
4
  # v3.0.10
2
5
  * [fix] Template prompting within collections (#204)
3
6
  * [enhancement] Stack parameter matching via configuration (#203)
data/docs/README.md CHANGED
@@ -13,3 +13,4 @@
13
13
  - [Enabling Callbacks](callbacks.md#enabling-callbacks)
14
14
  - [Builtin Callbacks](callbacks.md#builtin-callbacks)
15
15
  - [Custom Callbacks](callbacks.md#custom-callbacks)
16
+ - [SparklePacks](sparkle-packs.md)
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
@@ -17,5 +17,6 @@ module Sfn
17
17
  autoload :Command, 'sfn/command'
18
18
  autoload :CommandModule, 'sfn/command_module'
19
19
  autoload :Planner, 'sfn/planner'
20
+ autoload :Lint, 'sfn/lint'
20
21
 
21
22
  end
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,12 @@
1
+ require 'sfn'
2
+ require 'jmespath'
3
+
4
+ module Sfn
5
+ module Lint
6
+
7
+ autoload :Definition, 'sfn/lint/definition'
8
+ autoload :Rule, 'sfn/lint/rule'
9
+ autoload :RuleSet, 'sfn/lint/rule_set'
10
+
11
+ end
12
+ end
@@ -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
@@ -1,4 +1,4 @@
1
1
  module Sfn
2
2
  # Current library version
3
- VERSION = Gem::Version.new('3.0.10')
3
+ VERSION = Gem::Version.new('3.0.12')
4
4
  end
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.10
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-19 00:00:00.000000000 Z
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