inspec-objects 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.
@@ -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: []