inspec-objects 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f2d91b8de5c038e32f3fdc85c5e0bf9049c0b0f8c135698e044981c1ffaf9d64
4
+ data.tar.gz: 7a352f55c25135485071c4e1402f9e5112bba602c35ec5af9849e67cf8bcaa72
5
+ SHA512:
6
+ metadata.gz: 4ff6c9705631223bc28143443728b532de4bbcd55129e1034f572d04701023bfff3f11074fd3d10d5208a02e319bac192bd6516f01b517b73f86de36ea42b413
7
+ data.tar.gz: bd614f113e93f4da49060beefdfa2bea2c23799b66e81c170e2ce7ea1bde2079ffb196d975df45e4779e35c3695584d63049c1f5b3fb0ee58119dfe3edd12c8d
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "inspec-objects", path: "."
4
+
5
+ group :test do
6
+ gem "chefstyle", "0.13.0"
7
+ gem "minitest", "~> 5.5"
8
+ gem "rake", ">= 10"
9
+ end
@@ -0,0 +1,16 @@
1
+ # inspec-objects
2
+ [![Gem Version](https://badge.fury.io/rb/inspec-objects.svg)](https://badge.fury.io/rb/inspec-objects)
3
+
4
+ **Umbrella Project**: [InSpec](https://github.com/chef/chef-oss-practices/blob/master/projects/inspec.md)
5
+
6
+ **Project State**: [Maintained](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md#maintained)
7
+
8
+ **Issues [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: None
9
+
10
+ **Pull Request [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: None
11
+
12
+ These classes were migrated out of InSpec, specifically [inspec/lib/objects](https://github.com/inspec/inspec/tree/master/lib/inspec/objects).
13
+
14
+ InSpec does not uses these classes directly. They can be used to programmatically create InSpec profiles.
15
+
16
+ No further documentation or support is available.
@@ -0,0 +1,22 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require "inspec-objects/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "inspec-objects"
8
+ spec.version = InspecObjects::VERSION
9
+ spec.authors = ["Dominik Richter", "Christoph Hartmann"]
10
+ spec.email = ["dominik.richter@gmail.com", "chris@lollyrock.com"]
11
+ spec.summary = "InSpec Objects library"
12
+ spec.description = "Library that provides an API for programmatically creating InSpec profiles"
13
+ spec.homepage = "https://github.com/inspec/inspec-objects"
14
+ spec.license = "Apache-2.0"
15
+
16
+ spec.files = %w{
17
+ README.md inspec-objects.gemspec Gemfile
18
+ } + Dir.glob("{lib}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
19
+
20
+ spec.require_paths = ["lib"]
21
+ spec.add_dependency "inspec-core"
22
+ end
@@ -0,0 +1,3 @@
1
+ module InspecObjects
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,14 @@
1
+ module Inspec
2
+ module Object
3
+ autoload :Tag, "inspec/objects/tag"
4
+ autoload :Control, "inspec/objects/control"
5
+ autoload :Describe, "inspec/objects/describe"
6
+ autoload :EachLoop, "inspec/objects/each_loop"
7
+ autoload :List, "inspec/objects/list"
8
+ autoload :OrTest, "inspec/objects/or_test"
9
+ autoload :RubyHelper, "inspec/objects/ruby_helper"
10
+ autoload :Test, "inspec/objects/test"
11
+ autoload :Value, "inspec/objects/value"
12
+ autoload :Input, "inspec/objects/input"
13
+ end
14
+ end
@@ -0,0 +1,78 @@
1
+ module Inspec::Object
2
+ class Control
3
+ attr_accessor :id, :title, :descriptions, :impact, :tests, :tags, :refs, :only_if
4
+ def initialize
5
+ @tests = []
6
+ @tags = []
7
+ @refs = []
8
+ @descriptions = {}
9
+ end
10
+
11
+ def add_test(t)
12
+ @tests.push(t)
13
+ end
14
+
15
+ def add_tag(t)
16
+ @tags.push(t)
17
+ end
18
+
19
+ def to_hash
20
+ {
21
+ id: id,
22
+ title: title,
23
+ descriptions: descriptions,
24
+ impact: impact,
25
+ tests: tests.map(&:to_hash),
26
+ tags: tags.map(&:to_hash),
27
+ }
28
+ end
29
+
30
+ def to_ruby # rubocop:disable Metrics/AbcSize
31
+ res = ["control #{id.inspect} do"]
32
+ res.push " title #{title.inspect}" unless title.to_s.empty?
33
+ descriptions.each do |label, text|
34
+ if label == :default
35
+ next if text.nil? || (text == "") # don't render empty/nil desc
36
+
37
+ res.push " desc #{prettyprint_text(text, 2)}"
38
+ else
39
+ res.push " desc #{label.to_s.inspect}, #{prettyprint_text(text, 2)}"
40
+ end
41
+ end
42
+ res.push " impact #{impact}" unless impact.nil?
43
+ tags.each { |t| res.push(indent(t.to_ruby, 2)) }
44
+ refs.each { |t| res.push(" ref #{print_ref(t)}") }
45
+ res.push " only_if { #{only_if} }" if only_if
46
+ tests.each { |t| res.push(indent(t.to_ruby, 2)) }
47
+ res.push "end"
48
+ res.join("\n")
49
+ end
50
+
51
+ private
52
+
53
+ def print_ref(x)
54
+ return x.inspect if x.is_a?(String)
55
+ raise "Cannot process the ref: #{x}" unless x.is_a?(Hash)
56
+
57
+ "(" + x.inspect + ")"
58
+ end
59
+
60
+ # Pretty-print a text block of InSpec code
61
+ #
62
+ # @param s [String] should not be empty
63
+ # @param depth [Int] indentation length for multiline text blocks
64
+ # @return [String] pretty-printed textblock
65
+ def prettyprint_text(s, depth)
66
+ txt = s.to_s.inspect.gsub('\n', "\n")
67
+ return txt unless txt.include?("\n")
68
+
69
+ middle = indent(txt[1..-2], depth + 2)
70
+ txt[0] + "\n" + middle + "\n" + " " * depth + txt[-1]
71
+ end
72
+
73
+ def indent(txt, d)
74
+ dt = " " * d
75
+ dt + txt.gsub("\n", "\n" + dt)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,99 @@
1
+ module Inspec::Object
2
+ class Describe
3
+ # Internal helper to structure test objects.
4
+ # Should not be exposed to the user as it is hidden behind
5
+ # `add_test`, `to_hash`, and `to_ruby` in Inspec::Object::Describe
6
+ Test = Struct.new(:its, :matcher, :expectation, :negated) do
7
+ def negate!
8
+ self.negated = !negated
9
+ end
10
+
11
+ def to_ruby
12
+ itsy = "it"
13
+ unless its.nil?
14
+ if its.is_a? Array
15
+ itsy = "its(" + its.inspect + ")"
16
+ else
17
+ itsy = "its(" + its.to_s.inspect + ")"
18
+ end
19
+ end
20
+ naughty = negated ? "_not" : ""
21
+ xpect = if expectation.nil?
22
+ ""
23
+ elsif expectation.class == Regexp
24
+ # without this, xpect values like / \/zones\// will not be parsed properly
25
+ "(#{expectation.inspect})"
26
+ else
27
+ " " + expectation.inspect
28
+ end
29
+ format("%s { should%s %s%s }", itsy, naughty, matcher, xpect)
30
+ end
31
+ end
32
+
33
+ # A qualifier describing the resource that will be tested. It may consist
34
+ # of the resource identification and multiple accessors to pinpoint the data
35
+ # the user wants to test.
36
+ attr_accessor :qualifier
37
+
38
+ # An array of individual tests for the qualifier. Every entry will be
39
+ # translated into an `it` or `its` clause.
40
+ attr_accessor :tests
41
+
42
+ # Optional variables which are used by tests.
43
+ attr_accessor :variables
44
+
45
+ # Optional method to skip this describe block altogether. If `skip` is
46
+ # defined it takes precendence and will print the skip statement instead
47
+ # of adding other tests.
48
+ attr_accessor :skip
49
+
50
+ include RubyHelper
51
+
52
+ def initialize
53
+ @qualifier = []
54
+ @tests = []
55
+ @variables = []
56
+ end
57
+
58
+ def add_test(its, matcher, expectation, opts = {})
59
+ test = Inspec::Object::Describe::Test.new(its, matcher, expectation, opts[:negated])
60
+ tests.push(test)
61
+ test
62
+ end
63
+
64
+ def to_ruby
65
+ return rb_skip unless skip.nil?
66
+
67
+ rb_describe
68
+ end
69
+
70
+ def to_hash
71
+ { qualifier: qualifier, tests: tests.map(&:to_h), variables: variables, skip: skip }
72
+ end
73
+
74
+ def resource
75
+ return nil if qualifier.empty? || qualifier[0].empty? || qualifier[0][0].empty?
76
+
77
+ qualifier[0][0]
78
+ end
79
+
80
+ private
81
+
82
+ def rb_describe
83
+ vars = variables.map(&:to_ruby).join("\n")
84
+ vars += "\n" unless vars.empty?
85
+
86
+ objarr = @qualifier
87
+ objarr = [["unknown object".inspect]] if objarr.nil? || objarr.empty?
88
+ obj = objarr.map { |q| ruby_qualifier(q) }.join(".")
89
+
90
+ rbtests = tests.map(&:to_ruby).join("\n ")
91
+ format("%sdescribe %s do\n %s\nend", vars, obj, rbtests)
92
+ end
93
+
94
+ def rb_skip
95
+ obj = @qualifier || skip.inspect
96
+ format("describe %s do\n skip %s\nend", obj, skip.inspect)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,34 @@
1
+ module Inspec::Object
2
+ class EachLoop < List
3
+ attr_reader :variables
4
+ attr_accessor :tests
5
+ def initialize
6
+ super
7
+ @tests = []
8
+ @variables = []
9
+ end
10
+
11
+ def add_test(t = nil)
12
+ t ||= Test.new
13
+ t.qualifier[0] = ["entry"]
14
+ @tests.push(t)
15
+ t
16
+ end
17
+
18
+ def negate!
19
+ @tests.each(&:negate!)
20
+ end
21
+
22
+ def to_hash
23
+ { qualifier: qualifier, test: @test }
24
+ end
25
+
26
+ def to_ruby
27
+ vars = variables.map(&:to_ruby).join("\n")
28
+ vars += "\n" unless vars.empty?
29
+ obj = super
30
+ all_tests = @tests.map(&:to_ruby).join("\n").gsub("\n", "\n ")
31
+ format("%s%s.each do |entry|\n %s\nend", vars, obj, all_tests)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ require "inspec/input"
2
+
3
+ module Inspec::Object
4
+ class Input < ::Inspec::Input
5
+
6
+ # NOTE: No initialize method or accessors for the reasons listed above
7
+
8
+ #--------------------------------------------------------------------------#
9
+ # Marshalling
10
+ #--------------------------------------------------------------------------#
11
+
12
+ def to_hash
13
+ as_hash = { name: name, options: {} }
14
+ %i{description title identifier type required value}.each do |field|
15
+ val = send(field)
16
+ next if val.nil?
17
+
18
+ as_hash[:options][field] = val
19
+ end
20
+ as_hash
21
+ end
22
+
23
+ def ruby_var_identifier
24
+ identifier || "attr_" + name.downcase.strip.gsub(/\s+/, "-").gsub(/[^\w-]/, "")
25
+ end
26
+
27
+ def to_ruby
28
+ res = ["#{ruby_var_identifier} = attribute('#{name}',{"]
29
+ res.push " title: '#{title}'," unless title.to_s.empty?
30
+ res.push " value: #{value.inspect}," unless value.to_s.empty?
31
+ # to_ruby may generate code that is to be used by older versions of inspec.
32
+ # Anything older than 3.4 will not recognize the value: option, so
33
+ # send the default: option as well. See #3759
34
+ res.push " default: #{value.inspect}," unless value.to_s.empty?
35
+ res.push " description: '#{description}'," unless description.to_s.empty?
36
+ res.push "})"
37
+ res.join("\n")
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ module Inspec::Object
2
+ class List < Value
3
+ def map
4
+ raise "Inspec::Object::List.map needs to be called with a block" unless block_given?
5
+
6
+ t = List.new
7
+ t.qualifier = [["x"]]
8
+ yield(t)
9
+ return if t.qualifier == [["x"]]
10
+
11
+ @qualifier.push(["map", "{ |x| #{t.to_ruby} }"])
12
+ self
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ module Inspec::Object
2
+ class OrTest
3
+ attr_reader :tests
4
+ def initialize(tests)
5
+ @tests = tests
6
+ @negated = false
7
+ end
8
+
9
+ def skip
10
+ nil
11
+ end
12
+
13
+ def negate!
14
+ @negated = !@negated
15
+ end
16
+
17
+ def to_ruby
18
+ if @negated
19
+ # We don't use the describe.one wrapper when negated because:
20
+ # !(test1 || test2) same as (!test1 && !test2) where && is implicit in inspec
21
+ all_tests = @tests.map do |test|
22
+ test.negate!
23
+ test
24
+ end.map(&:to_ruby).join("\n")
25
+
26
+ all_tests
27
+ else
28
+ all_tests = @tests.map(&:to_ruby).join("\n").gsub("\n", "\n ")
29
+
30
+ format("describe.one do\n %s\nend", all_tests)
31
+ end
32
+ end
33
+
34
+ def to_hash
35
+ { describe_one: @tests.map(&:to_hash) }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ module Inspec::Object
2
+ module RubyHelper
3
+ def ruby_qualifier(q)
4
+ if q.length <= 1
5
+ q[0]
6
+ elsif q[0] == "map" && q.length == 2
7
+ q[0] + " " + q[1]
8
+ else
9
+ q[0] + "(" + q[1..-1].map(&:inspect).join(", ") + ")"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Inspec::Object
2
+ class Tag
3
+ attr_accessor :key, :value
4
+
5
+ def initialize(key, value)
6
+ @key = key
7
+ @value = value
8
+ end
9
+
10
+ def to_hash
11
+ {
12
+ name: key,
13
+ value: value,
14
+ }
15
+ end
16
+
17
+ def to_ruby
18
+ "tag #{key}: #{value.inspect}"
19
+ end
20
+
21
+ def to_s
22
+ "Tag #{key} with #{value}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,87 @@
1
+ module Inspec::Object
2
+ class Test
3
+ attr_accessor :qualifier, :matcher, :expectation, :skip, :negated, :variables, :only_if
4
+ include RubyHelper
5
+
6
+ def initialize
7
+ @qualifier = []
8
+ @negated = false
9
+ @variables = []
10
+ end
11
+
12
+ def negate!
13
+ @negated = !@negated
14
+ end
15
+
16
+ def to_ruby
17
+ return rb_skip unless skip.nil?
18
+
19
+ rb_describe
20
+ end
21
+
22
+ def to_hash
23
+ { qualifier: qualifier, matcher: matcher, expectation: expectation, skip: skip, negated: negated }
24
+ end
25
+
26
+ def resource
27
+ @resource ||=
28
+ if qualifier.empty? || qualifier[0].empty? || qualifier[0][0].empty?
29
+ nil
30
+ else
31
+ qualifier[0][0]
32
+ end
33
+ end
34
+
35
+ def remove_expectation
36
+ remove_instance_variable(:@expectation)
37
+ end
38
+
39
+ private
40
+
41
+ def describe_chain
42
+ return nil if @qualifier.empty?
43
+
44
+ resource = @qualifier.length > 1 ? @qualifier[0..-2] : [@qualifier[0]]
45
+ res = resource.map { |q| ruby_qualifier(q) }.join(".")
46
+ xres = nil
47
+
48
+ if @qualifier.length > 1
49
+ last = @qualifier[-1]
50
+ last_call = last.is_a?(Array) ? last[0].to_s : ""
51
+ if last.length == 1 && last_call !~ /^to_.$/ && !last_call.include?("[") && !last_call.empty?
52
+ # this will go in its()
53
+ xres = last_call
54
+ else
55
+ res += "." + ruby_qualifier(last) unless last_call.empty?
56
+ end
57
+ end
58
+
59
+ [res, xres]
60
+ end
61
+
62
+ def rb_describe
63
+ only_if_clause = "only_if { #{only_if} }\n" if only_if
64
+ vars = variables.map(&:to_ruby).join("\n")
65
+ vars += "\n" unless vars.empty?
66
+ res, xtra = describe_chain
67
+ itsy = xtra.nil? ? "it" : "its(" + xtra.to_s.inspect + ")"
68
+ naughty = @negated ? "_not" : ""
69
+ xpect = if !defined?(@expectation)
70
+ ""
71
+ elsif @expectation.class == Regexp
72
+ # without this, xpect values like / \/zones\// will not be parsed properly
73
+ "(#{@expectation.inspect})"
74
+ elsif xpect != ""
75
+ " " + expectation.inspect
76
+ end
77
+ format("%s%sdescribe %s do\n %s { should%s %s%s }\nend",
78
+ only_if_clause, vars, res, itsy, naughty, matcher, xpect)
79
+ end
80
+
81
+ def rb_skip
82
+ dc = describe_chain
83
+ obj = dc.nil? ? skip.inspect : dc[0]
84
+ format("describe %s do\n skip %s\nend", obj, skip.inspect)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,25 @@
1
+ module Inspec::Object
2
+ class Value
3
+ include ::Inspec::Object::RubyHelper
4
+
5
+ attr_accessor :qualifier
6
+ attr_accessor :skip
7
+ attr_accessor :variable
8
+
9
+ def initialize(qualifiers = [])
10
+ @qualifier = qualifiers
11
+ @variable = nil
12
+ end
13
+
14
+ def to_ruby
15
+ res = @variable.nil? ? "" : "#{@variable} = "
16
+ res + @qualifier.map { |x| ruby_qualifier(x) }.join(".")
17
+ end
18
+
19
+ def name_variable(cache = [])
20
+ @variable = Array("a".."z").find { |x| !cache.include?(x) }
21
+ cache.push(@variable)
22
+ @variable
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: inspec-objects
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dominik Richter
8
+ - Christoph Hartmann
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-11-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: inspec-core
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ description: Library that provides an API for programmatically creating InSpec profiles
29
+ email:
30
+ - dominik.richter@gmail.com
31
+ - chris@lollyrock.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - Gemfile
37
+ - README.md
38
+ - inspec-objects.gemspec
39
+ - lib/inspec-objects/version.rb
40
+ - lib/inspec/objects.rb
41
+ - lib/inspec/objects/control.rb
42
+ - lib/inspec/objects/describe.rb
43
+ - lib/inspec/objects/each_loop.rb
44
+ - lib/inspec/objects/input.rb
45
+ - lib/inspec/objects/list.rb
46
+ - lib/inspec/objects/or_test.rb
47
+ - lib/inspec/objects/ruby_helper.rb
48
+ - lib/inspec/objects/tag.rb
49
+ - lib/inspec/objects/test.rb
50
+ - lib/inspec/objects/value.rb
51
+ homepage: https://github.com/inspec/inspec-objects
52
+ licenses:
53
+ - Apache-2.0
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubygems_version: 3.0.3
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: InSpec Objects library
74
+ test_files: []