puppetlabs_spec_helper 5.0.1 → 5.0.2
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/.rubocop.yml +0 -3
- data/lib/puppetlabs_spec_helper/puppetlabs_spec/files.rb +43 -41
- data/lib/puppetlabs_spec_helper/puppetlabs_spec/fixtures.rb +43 -41
- data/lib/puppetlabs_spec_helper/puppetlabs_spec/puppet_internals.rb +34 -32
- data/lib/puppetlabs_spec_helper/tasks/check_symlinks.rb +41 -37
- data/lib/puppetlabs_spec_helper/tasks/fixtures.rb +349 -348
- data/lib/puppetlabs_spec_helper/version.rb +1 -1
- metadata +18 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50d161a5158e33840d6ce49a9135ff7929f210f03fdbc9def2f1b9dfb1f9eecd
|
4
|
+
data.tar.gz: bcef9675b43f702105c6fae5c4494198454bccc37466eb329f10830742f242c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a834921021d78348d7a3f25f36282e58b2650dd6c8382db65e20a7f45e81fae0ee9b3e39eacb9e9d79f241c2a76d1461e0acbc8a7d996d44dc18061857ab36df
|
7
|
+
data.tar.gz: 3f3fb17e7db1bcf81f4d33c7cb723f88f5f5d0c29c6cb43c610232366f5a2c2346e95dbafc975d692f6419ccc20684bbf38bc6f35a8749c2c73d6e79dd4cc784
|
data/.rubocop.yml
CHANGED
@@ -36,9 +36,6 @@ Style/BlockDelimiters:
|
|
36
36
|
Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to
|
37
37
|
be consistent then.
|
38
38
|
EnforcedStyle: braces_for_chaining
|
39
|
-
Style/ClassAndModuleChildren:
|
40
|
-
Description: Compact style reduces the required amount of indentation.
|
41
|
-
EnforcedStyle: compact
|
42
39
|
Style/EmptyElse:
|
43
40
|
Description: Enforce against empty else clauses, but allow `nil` for clarity.
|
44
41
|
EnforcedStyle: empty
|
@@ -6,56 +6,58 @@ require 'fileutils'
|
|
6
6
|
require 'tempfile'
|
7
7
|
require 'pathname'
|
8
8
|
|
9
|
-
|
10
|
-
module
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
module PuppetlabsSpec
|
10
|
+
# A support module for testing files.
|
11
|
+
module Files
|
12
|
+
# This code exists only to support tests that run as root, pretty much.
|
13
|
+
# Once they have finally been eliminated this can all go... --daniel 2011-04-08
|
14
|
+
def self.in_tmp(path)
|
15
|
+
tempdir = Dir.tmpdir
|
16
|
+
|
17
|
+
Pathname.new(path).ascend do |dir|
|
18
|
+
return true if File.identical?(tempdir, dir)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
false
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
def self.cleanup
|
25
|
+
$global_tempfiles ||= []
|
26
|
+
while (path = $global_tempfiles.pop)
|
27
|
+
raise "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path)
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
begin
|
30
|
+
FileUtils.rm_r path, secure: true
|
31
|
+
rescue Errno::ENOENT
|
32
|
+
# nothing to do
|
33
|
+
end
|
32
34
|
end
|
33
35
|
end
|
34
|
-
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
def make_absolute(path)
|
38
|
+
path = File.expand_path(path)
|
39
|
+
path[0] = 'c' if Puppet.features.microsoft_windows?
|
40
|
+
path
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def tmpfilename(name)
|
44
|
+
# Generate a temporary file, just for the name...
|
45
|
+
source = Tempfile.new(name)
|
46
|
+
path = source.path
|
47
|
+
source.close!
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
# ...record it for cleanup,
|
50
|
+
$global_tempfiles ||= []
|
51
|
+
$global_tempfiles << File.expand_path(path)
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
# ...and bam.
|
54
|
+
path
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
def tmpdir(name)
|
58
|
+
path = tmpfilename(name)
|
59
|
+
FileUtils.mkdir_p(path)
|
60
|
+
path
|
61
|
+
end
|
60
62
|
end
|
61
63
|
end
|
@@ -1,53 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# Returns the path to your relative fixture dir. So if your spec test is
|
13
|
-
# <project>/spec/unit/facter/foo_spec.rb then your relative dir will be
|
14
|
-
# <project>/spec/fixture/unit/facter/foo
|
15
|
-
def my_fixture_dir
|
16
|
-
callers = caller
|
17
|
-
while (line = callers.shift)
|
18
|
-
next unless (found = line.match(%r{/spec/(.*)_spec\.rb:}))
|
19
|
-
|
20
|
-
return fixtures(found[1])
|
3
|
+
module PuppetlabsSpec
|
4
|
+
# This module provides some helper methods to assist with fixtures. It's
|
5
|
+
# methods are designed to help when you have a conforming fixture layout so we
|
6
|
+
# get project consistency.
|
7
|
+
module Fixtures
|
8
|
+
# Returns the joined path of the global FIXTURE_DIR plus any path given to it
|
9
|
+
def fixtures(*rest)
|
10
|
+
File.join(PuppetlabsSpec::FIXTURE_DIR, *rest)
|
21
11
|
end
|
22
|
-
raise "sorry, I couldn't work out your path from the caller stack!"
|
23
|
-
end
|
24
12
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
13
|
+
# Returns the path to your relative fixture dir. So if your spec test is
|
14
|
+
# <project>/spec/unit/facter/foo_spec.rb then your relative dir will be
|
15
|
+
# <project>/spec/fixture/unit/facter/foo
|
16
|
+
def my_fixture_dir
|
17
|
+
callers = caller
|
18
|
+
while (line = callers.shift)
|
19
|
+
next unless (found = line.match(%r{/spec/(.*)_spec\.rb:}))
|
20
|
+
|
21
|
+
return fixtures(found[1])
|
22
|
+
end
|
23
|
+
raise "sorry, I couldn't work out your path from the caller stack!"
|
31
24
|
end
|
32
25
|
|
33
|
-
file
|
34
|
-
|
26
|
+
# Given a name, returns the full path of a file from your relative fixture
|
27
|
+
# dir as returned by my_fixture_dir.
|
28
|
+
def my_fixture(name)
|
29
|
+
file = File.join(my_fixture_dir, name)
|
30
|
+
unless File.readable? file
|
31
|
+
raise "fixture '#{name}' for #{my_fixture_dir} is not readable"
|
32
|
+
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
def my_fixture_read(name)
|
39
|
-
File.read(my_fixture(name))
|
40
|
-
end
|
34
|
+
file
|
35
|
+
end
|
41
36
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
if files.empty?
|
47
|
-
raise "fixture '#{glob}' for #{my_fixture_dir} had no files!"
|
37
|
+
# Return the contents of the file using read when given a name. Uses
|
38
|
+
# my_fixture to work out the relative path.
|
39
|
+
def my_fixture_read(name)
|
40
|
+
File.read(my_fixture(name))
|
48
41
|
end
|
49
42
|
|
50
|
-
block
|
51
|
-
|
43
|
+
# Provides a block mechanism for iterating across the files in your fixture
|
44
|
+
# area.
|
45
|
+
def my_fixtures(glob = '*', flags = 0, &block)
|
46
|
+
files = Dir.glob(File.join(my_fixture_dir, glob), flags)
|
47
|
+
if files.empty?
|
48
|
+
raise "fixture '#{glob}' for #{my_fixture_dir} had no files!"
|
49
|
+
end
|
50
|
+
|
51
|
+
block && files.each(&block)
|
52
|
+
files
|
53
|
+
end
|
52
54
|
end
|
53
55
|
end
|
@@ -4,41 +4,43 @@
|
|
4
4
|
# 'puppetlabs_spec_helper/puppet_spec_helper' library
|
5
5
|
require 'puppetlabs_spec_helper/puppet_spec_helper'
|
6
6
|
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
module PuppetlabsSpec
|
8
|
+
# PuppetInternals provides a set of methods that interface
|
9
|
+
# with internal puppet implementations.
|
10
|
+
module PuppetInternals
|
11
|
+
def resource(parts = {})
|
12
|
+
resource_type = parts[:type] || :hostclass
|
13
|
+
resource_name = parts[:name] || 'testing'
|
14
|
+
Puppet::Resource::Type.new(resource_type, resource_name)
|
15
|
+
end
|
16
|
+
module_function :resource
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def compiler(parts = {})
|
19
|
+
compiler_node = parts[:node] || node
|
20
|
+
Puppet::Parser::Compiler.new(compiler_node)
|
21
|
+
end
|
22
|
+
module_function :compiler
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
def node(parts = {})
|
25
|
+
node_name = parts[:name] || 'testinghost'
|
26
|
+
options = parts[:options] || {}
|
27
|
+
node_environment = Puppet::Node::Environment.create(parts[:environment] || 'test', [])
|
28
|
+
options[:environment] = node_environment
|
29
|
+
Puppet::Node.new(node_name, options)
|
30
|
+
end
|
31
|
+
module_function :node
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
# Return a method instance for a given function. This is primarily useful
|
34
|
+
# for rspec-puppet
|
35
|
+
def function_method(name, parts = {})
|
36
|
+
scope = parts[:scope] || scope()
|
37
|
+
# Ensure the method instance is defined by side-effect of checking if it
|
38
|
+
# exists. This is a hack, but at least it's a hidden hack and not an
|
39
|
+
# exposed hack.
|
40
|
+
return nil unless Puppet::Parser::Functions.function(name)
|
40
41
|
|
41
|
-
|
42
|
+
scope.method("function_#{name}".to_sym)
|
43
|
+
end
|
44
|
+
module_function :function_method
|
42
45
|
end
|
43
|
-
module_function :function_method
|
44
46
|
end
|
@@ -2,48 +2,52 @@
|
|
2
2
|
|
3
3
|
require 'pathspec'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
5
|
+
module PuppetlabsSpecHelper
|
6
|
+
module Tasks
|
7
|
+
# Helpers for validating symlinks.
|
8
|
+
class CheckSymlinks
|
9
|
+
DEFAULT_IGNORED = [
|
10
|
+
'/.git/',
|
11
|
+
'/.bundle/',
|
12
|
+
'/vendor/',
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
IGNORE_LIST_FILES = [
|
16
|
+
'.pdkignore',
|
17
|
+
'.gitignore',
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
def check(dir = Dir.pwd)
|
21
|
+
dir = Pathname.new(dir) unless dir.is_a?(Pathname)
|
22
|
+
results = []
|
23
|
+
|
24
|
+
dir.each_child(true) do |child|
|
25
|
+
next if ignored?(child.to_s)
|
26
|
+
|
27
|
+
if child.symlink?
|
28
|
+
results << child
|
29
|
+
elsif child.directory? && child.basename.to_s !~ %r{^(\.git|\.?bundle)$}
|
30
|
+
results.concat(check(child))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
results
|
29
35
|
end
|
30
|
-
end
|
31
|
-
|
32
|
-
results
|
33
|
-
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
+
def ignored?(path)
|
38
|
+
path = "#{path}/" if File.directory?(path)
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
+
!ignore_pathspec.match_paths([path], Dir.pwd).empty?
|
41
|
+
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
def ignore_pathspec
|
44
|
+
@ignore_pathspec ||= PathSpec.new(DEFAULT_IGNORED).tap do |pathspec|
|
45
|
+
IGNORE_LIST_FILES.each do |f|
|
46
|
+
next unless File.file?(f) && File.readable?(f)
|
45
47
|
|
46
|
-
|
48
|
+
File.open(f, 'r') { |fd| pathspec.add(fd) }
|
49
|
+
end
|
50
|
+
end
|
47
51
|
end
|
48
52
|
end
|
49
53
|
end
|
@@ -4,406 +4,407 @@ require 'yaml'
|
|
4
4
|
require 'open3'
|
5
5
|
require 'json'
|
6
6
|
|
7
|
-
|
8
|
-
module
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
# @return [String] - the name of current module
|
18
|
-
def module_name
|
19
|
-
raise ArgumentError unless File.file?('metadata.json') && File.readable?('metadata.json')
|
20
|
-
|
21
|
-
metadata = JSON.parse(File.read('metadata.json'))
|
22
|
-
metadata_name = metadata.fetch('name', nil) || ''
|
23
|
-
|
24
|
-
raise ArgumentError if metadata_name.empty?
|
7
|
+
module PuppetlabsSpecHelper
|
8
|
+
module Tasks
|
9
|
+
# Helpers for working with fixtures.
|
10
|
+
module FixtureHelpers
|
11
|
+
# This is a helper for the self-symlink entry of fixtures.yml
|
12
|
+
def source_dir
|
13
|
+
Dir.pwd
|
14
|
+
end
|
25
15
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
16
|
+
# @return [String] - the name of current module
|
17
|
+
def module_name
|
18
|
+
raise ArgumentError unless File.file?('metadata.json') && File.readable?('metadata.json')
|
30
19
|
|
31
|
-
|
32
|
-
|
33
|
-
raise ArgumentError unless File.file?(metadata_path) && File.readable?(metadata_path)
|
20
|
+
metadata = JSON.parse(File.read('metadata.json'))
|
21
|
+
metadata_name = metadata.fetch('name', nil) || ''
|
34
22
|
|
35
|
-
|
36
|
-
metadata.fetch('version', nil) || '0.0.1'
|
37
|
-
rescue JSON::ParserError, ArgumentError
|
38
|
-
logger.warn "Failed to find module version at path #{path}"
|
39
|
-
'0.0.1'
|
40
|
-
end
|
23
|
+
raise ArgumentError if metadata_name.empty?
|
41
24
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
# }}
|
47
|
-
def repositories
|
48
|
-
@repositories ||= fixtures('repositories') || {}
|
49
|
-
end
|
25
|
+
metadata_name.split('-').last
|
26
|
+
rescue JSON::ParserError, ArgumentError
|
27
|
+
File.basename(Dir.pwd).split('-').last
|
28
|
+
end
|
50
29
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# "ref"=>nil, "branch"=>nil, "scm"=>nil,
|
55
|
-
# "flags"=>"--module_repository=https://myforge.example.com/", "subdir"=>nil}}
|
56
|
-
def forge_modules
|
57
|
-
@forge_modules ||= fixtures('forge_modules') || {}
|
58
|
-
end
|
30
|
+
def module_version(path)
|
31
|
+
metadata_path = File.join(path, 'metadata.json')
|
32
|
+
raise ArgumentError unless File.file?(metadata_path) && File.readable?(metadata_path)
|
59
33
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
34
|
+
metadata = JSON.parse(File.read(metadata_path))
|
35
|
+
metadata.fetch('version', nil) || '0.0.1'
|
36
|
+
rescue JSON::ParserError, ArgumentError
|
37
|
+
logger.warn "Failed to find module version at path #{path}"
|
38
|
+
'0.0.1'
|
39
|
+
end
|
64
40
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
41
|
+
# @return [Hash] - returns a hash of all the fixture repositories
|
42
|
+
# @example
|
43
|
+
# {"puppetlabs-stdlib"=>{"target"=>"https://gitlab.com/puppetlabs/puppet-stdlib.git",
|
44
|
+
# "ref"=>nil, "branch"=>"main", "scm"=>nil,
|
45
|
+
# }}
|
46
|
+
def repositories
|
47
|
+
@repositories ||= fixtures('repositories') || {}
|
48
|
+
end
|
69
49
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
50
|
+
# @return [Hash] - returns a hash of all the fixture forge modules
|
51
|
+
# @example
|
52
|
+
# {"puppetlabs-stdlib"=>{"target"=>"spec/fixtures/modules/stdlib",
|
53
|
+
# "ref"=>nil, "branch"=>nil, "scm"=>nil,
|
54
|
+
# "flags"=>"--module_repository=https://myforge.example.com/", "subdir"=>nil}}
|
55
|
+
def forge_modules
|
56
|
+
@forge_modules ||= fixtures('forge_modules') || {}
|
57
|
+
end
|
74
58
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
'.fixtures.yml'
|
80
|
-
elsif File.exist?('.fixtures.yaml')
|
81
|
-
'.fixtures.yaml'
|
82
|
-
else
|
83
|
-
false
|
84
|
-
end
|
59
|
+
# @return [Hash] - a hash of symlinks specified in the fixtures file
|
60
|
+
def symlinks
|
61
|
+
@symlinks ||= fixtures('symlinks') || {}
|
62
|
+
end
|
85
63
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
{ 'fixtures' => {} }
|
91
|
-
end
|
92
|
-
rescue Errno::ENOENT
|
93
|
-
raise("Fixtures file not found: '#{fixtures_yaml}'")
|
94
|
-
rescue Psych::SyntaxError => e
|
95
|
-
raise("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
|
96
|
-
end
|
64
|
+
# @return [Hash] - returns a hash with the module name and the source directory
|
65
|
+
def auto_symlink
|
66
|
+
{ module_name => "\#{source_dir}" }
|
67
|
+
end
|
97
68
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
69
|
+
# @return [Boolean] - true if the os is a windows system
|
70
|
+
def windows?
|
71
|
+
!!File::ALT_SEPARATOR
|
72
|
+
end
|
102
73
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
74
|
+
def fixtures(category)
|
75
|
+
fixtures_yaml = if ENV['FIXTURES_YML']
|
76
|
+
ENV['FIXTURES_YML']
|
77
|
+
elsif File.exist?('.fixtures.yml')
|
78
|
+
'.fixtures.yml'
|
79
|
+
elsif File.exist?('.fixtures.yaml')
|
80
|
+
'.fixtures.yaml'
|
81
|
+
else
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
begin
|
86
|
+
fixtures = if fixtures_yaml
|
87
|
+
YAML.load_file(fixtures_yaml) || { 'fixtures' => {} }
|
88
|
+
else
|
89
|
+
{ 'fixtures' => {} }
|
90
|
+
end
|
91
|
+
rescue Errno::ENOENT
|
92
|
+
raise("Fixtures file not found: '#{fixtures_yaml}'")
|
93
|
+
rescue Psych::SyntaxError => e
|
94
|
+
raise("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
|
95
|
+
end
|
108
96
|
|
109
|
-
|
97
|
+
unless fixtures.include?('fixtures')
|
98
|
+
# File is non-empty, but does not specify fixtures
|
99
|
+
raise("No 'fixtures' entries found in '#{fixtures_yaml}'; required")
|
100
|
+
end
|
110
101
|
|
111
|
-
|
112
|
-
|
113
|
-
|
102
|
+
fixture_defaults = if fixtures.include? 'defaults'
|
103
|
+
fixtures['defaults']
|
104
|
+
else
|
105
|
+
{}
|
106
|
+
end
|
114
107
|
|
115
|
-
|
116
|
-
if fixtures.include?(category) && !fixtures[category].nil?
|
117
|
-
defaults = { 'target' => 'spec/fixtures/modules' }
|
108
|
+
fixtures = fixtures['fixtures']
|
118
109
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
defaults = defaults.merge(fixture_defaults[category])
|
123
|
-
end
|
110
|
+
if fixtures['symlinks'].nil?
|
111
|
+
fixtures['symlinks'] = auto_symlink
|
112
|
+
end
|
124
113
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
114
|
+
result = {}
|
115
|
+
if fixtures.include?(category) && !fixtures[category].nil?
|
116
|
+
defaults = { 'target' => 'spec/fixtures/modules' }
|
117
|
+
|
118
|
+
# load defaults from the `.fixtures.yml` `defaults` section
|
119
|
+
# for the requested category and merge them into my defaults
|
120
|
+
if fixture_defaults.include? category
|
121
|
+
defaults = defaults.merge(fixture_defaults[category])
|
122
|
+
end
|
123
|
+
|
124
|
+
fixtures[category].each do |fixture, opts|
|
125
|
+
# convert a simple string fixture to a hash, by
|
126
|
+
# using the string fixture as the `repo` option of the hash.
|
127
|
+
if opts.instance_of?(String)
|
128
|
+
opts = { 'repo' => opts }
|
129
|
+
end
|
130
|
+
# there should be a warning or something if it's not a hash...
|
131
|
+
next unless opts.instance_of?(Hash)
|
132
|
+
|
133
|
+
# merge our options into the defaults to get the
|
134
|
+
# final option list
|
135
|
+
opts = defaults.merge(opts)
|
136
|
+
|
137
|
+
next unless include_repo?(opts['puppet_version'])
|
138
|
+
|
139
|
+
# rubocop:disable Security/Eval
|
140
|
+
# TODO: Remove eval
|
141
|
+
real_target = eval("\"#{opts['target']}\"", binding, __FILE__, __LINE__) # evaluating target reference in this context (see auto_symlink)
|
142
|
+
real_source = eval("\"#{opts['repo']}\"", binding, __FILE__, __LINE__) # evaluating repo reference in this context (see auto_symlink)
|
143
|
+
|
144
|
+
result[real_source] = validate_fixture_hash!(
|
145
|
+
'target' => File.join(real_target, fixture),
|
146
|
+
'ref' => opts['ref'] || opts['tag'],
|
147
|
+
'branch' => opts['branch'],
|
148
|
+
'scm' => opts['scm'],
|
149
|
+
'flags' => opts['flags'],
|
150
|
+
'subdir' => opts['subdir'],
|
151
|
+
)
|
152
|
+
end
|
130
153
|
end
|
131
|
-
|
132
|
-
next unless opts.instance_of?(Hash)
|
133
|
-
|
134
|
-
# merge our options into the defaults to get the
|
135
|
-
# final option list
|
136
|
-
opts = defaults.merge(opts)
|
137
|
-
|
138
|
-
next unless include_repo?(opts['puppet_version'])
|
139
|
-
|
140
|
-
# rubocop:disable Security/Eval
|
141
|
-
# TODO: Remove eval
|
142
|
-
real_target = eval("\"#{opts['target']}\"", binding, __FILE__, __LINE__) # evaluating target reference in this context (see auto_symlink)
|
143
|
-
real_source = eval("\"#{opts['repo']}\"", binding, __FILE__, __LINE__) # evaluating repo reference in this context (see auto_symlink)
|
144
|
-
|
145
|
-
result[real_source] = validate_fixture_hash!(
|
146
|
-
'target' => File.join(real_target, fixture),
|
147
|
-
'ref' => opts['ref'] || opts['tag'],
|
148
|
-
'branch' => opts['branch'],
|
149
|
-
'scm' => opts['scm'],
|
150
|
-
'flags' => opts['flags'],
|
151
|
-
'subdir' => opts['subdir'],
|
152
|
-
)
|
154
|
+
result
|
153
155
|
end
|
154
|
-
end
|
155
|
-
result
|
156
|
-
end
|
157
156
|
|
158
|
-
|
159
|
-
|
160
|
-
|
157
|
+
def validate_fixture_hash!(hash)
|
158
|
+
# Can only validate git based scm
|
159
|
+
return hash unless hash['scm'] == 'git'
|
161
160
|
|
162
|
-
|
163
|
-
|
161
|
+
# Forward slashes in the ref aren't allowed. And is probably a branch name.
|
162
|
+
raise ArgumentError, "The ref for #{hash['target']} is invalid (Contains a forward slash). If this is a branch name, please use the 'branch' setting instead." if hash['ref'].include?('/')
|
164
163
|
|
165
|
-
|
166
|
-
|
164
|
+
hash
|
165
|
+
end
|
167
166
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
167
|
+
def include_repo?(version_range)
|
168
|
+
if version_range && defined?(SemanticPuppet)
|
169
|
+
puppet_spec = Gem::Specification.find_by_name('puppet')
|
170
|
+
puppet_version = SemanticPuppet::Version.parse(puppet_spec.version.to_s)
|
172
171
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
172
|
+
constraint = SemanticPuppet::VersionRange.parse(version_range)
|
173
|
+
constraint.include?(puppet_version)
|
174
|
+
else
|
175
|
+
true
|
176
|
+
end
|
177
|
+
end
|
179
178
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
179
|
+
def clone_repo(scm, remote, target, _subdir = nil, ref = nil, branch = nil, flags = nil)
|
180
|
+
args = []
|
181
|
+
case scm
|
182
|
+
when 'hg'
|
183
|
+
args.push('clone')
|
184
|
+
args.push('-b', branch) if branch
|
185
|
+
args.push(flags) if flags
|
186
|
+
args.push(remote, target)
|
187
|
+
when 'git'
|
188
|
+
args.push('clone')
|
189
|
+
args.push('--depth 1') unless ref
|
190
|
+
args.push('-b', branch) if branch
|
191
|
+
args.push(flags) if flags
|
192
|
+
args.push(remote, target)
|
193
|
+
else
|
194
|
+
raise "Unfortunately #{scm} is not supported yet"
|
195
|
+
end
|
196
|
+
result = system("#{scm} #{args.flatten.join ' '}")
|
197
|
+
unless File.exist?(target)
|
198
|
+
raise "Failed to clone #{scm} repository #{remote} into #{target}"
|
199
|
+
end
|
201
200
|
|
202
|
-
|
203
|
-
|
201
|
+
result
|
202
|
+
end
|
204
203
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
204
|
+
def update_repo(scm, target)
|
205
|
+
args = case scm
|
206
|
+
when 'hg'
|
207
|
+
['pull']
|
208
|
+
when 'git'
|
209
|
+
['fetch'].tap do |git_args|
|
210
|
+
git_args << '--unshallow' if shallow_git_repo?
|
211
|
+
end
|
212
|
+
else
|
213
|
+
raise "Unfortunately #{scm} is not supported yet"
|
214
|
+
end
|
215
|
+
system("#{scm} #{args.flatten.join(' ')}", chdir: target)
|
216
|
+
end
|
218
217
|
|
219
|
-
|
220
|
-
|
221
|
-
|
218
|
+
def shallow_git_repo?
|
219
|
+
File.file?(File.join('.git', 'shallow'))
|
220
|
+
end
|
222
221
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
222
|
+
def revision(scm, target, ref)
|
223
|
+
args = []
|
224
|
+
case scm
|
225
|
+
when 'hg'
|
226
|
+
args.push('update', '--clean', '-r', ref)
|
227
|
+
when 'git'
|
228
|
+
args.push('reset', '--hard', ref)
|
229
|
+
else
|
230
|
+
raise "Unfortunately #{scm} is not supported yet"
|
231
|
+
end
|
232
|
+
result = system("#{scm} #{args.flatten.join ' '}", chdir: target)
|
233
|
+
raise "Invalid ref #{ref} for #{target}" unless result
|
234
|
+
end
|
236
235
|
|
237
|
-
|
238
|
-
|
239
|
-
|
236
|
+
def valid_repo?(scm, target, remote)
|
237
|
+
return false unless File.directory?(target)
|
238
|
+
return true if scm == 'hg'
|
240
239
|
|
241
|
-
|
240
|
+
return true if git_remote_url(target) == remote
|
242
241
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
242
|
+
warn "Git remote for #{target} has changed, recloning repository"
|
243
|
+
FileUtils.rm_rf(target)
|
244
|
+
false
|
245
|
+
end
|
247
246
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
247
|
+
def git_remote_url(target)
|
248
|
+
output, status = Open3.capture2e('git', '--git-dir', File.join(target, '.git'), 'ls-remote', '--get-url', 'origin')
|
249
|
+
status.success? ? output.strip : nil
|
250
|
+
end
|
252
251
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
252
|
+
def remove_subdirectory(target, subdir)
|
253
|
+
return if subdir.nil?
|
254
|
+
Dir.mktmpdir do |tmpdir|
|
255
|
+
FileUtils.mv(Dir.glob("#{target}/#{subdir}/{.[^\.]*,*}"), tmpdir)
|
256
|
+
FileUtils.rm_rf("#{target}/#{subdir}")
|
257
|
+
FileUtils.mv(Dir.glob("#{tmpdir}/{.[^\.]*,*}"), target.to_s)
|
258
|
+
end
|
259
|
+
end
|
261
260
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
261
|
+
# creates a logger so we can log events with certain levels
|
262
|
+
def logger
|
263
|
+
unless @logger
|
264
|
+
require 'logger'
|
265
|
+
level = if ENV['ENABLE_LOGGER']
|
266
|
+
Logger::DEBUG
|
267
|
+
else
|
268
|
+
Logger::INFO
|
269
|
+
end
|
270
|
+
@logger = Logger.new($stderr)
|
271
|
+
@logger.level = level
|
272
|
+
end
|
273
|
+
@logger
|
274
|
+
end
|
276
275
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
276
|
+
def module_working_directory
|
277
|
+
# The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
|
278
|
+
# becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
|
279
|
+
# This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
|
280
|
+
File.expand_path(ENV['MODULE_WORKING_DIR'] || 'spec/fixtures/work-dir')
|
281
|
+
end
|
283
282
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
283
|
+
# returns the current thread count that is currently active
|
284
|
+
# a status of false or nil means the thread completed
|
285
|
+
# so when anything else we count that as a active thread
|
286
|
+
# @return [Integer] - current thread count
|
287
|
+
def current_thread_count(items)
|
288
|
+
active_threads = items.select do |_item, opts|
|
289
|
+
if opts[:thread]
|
290
|
+
opts[:thread].status
|
291
|
+
else
|
292
|
+
false
|
293
|
+
end
|
294
|
+
end
|
295
|
+
logger.debug "Current thread count #{active_threads.count}"
|
296
|
+
active_threads.count
|
294
297
|
end
|
295
|
-
end
|
296
|
-
logger.debug "Current thread count #{active_threads.count}"
|
297
|
-
active_threads.count
|
298
|
-
end
|
299
298
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
299
|
+
# @summary Set a limit on the amount threads used, defaults to 10
|
300
|
+
# MAX_FIXTURE_THREAD_COUNT can be used to set this limit
|
301
|
+
# @return [Integer] - returns the max_thread_count
|
302
|
+
def max_thread_limit
|
303
|
+
@max_thread_limit ||= (ENV['MAX_FIXTURE_THREAD_COUNT'] || 10).to_i
|
304
|
+
end
|
306
305
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
306
|
+
# @param items [Hash] - a hash of either repositories or forge modules
|
307
|
+
# @param [Block] - the method you wish to use to download the item
|
308
|
+
def download_items(items)
|
309
|
+
items.each do |remote, opts|
|
310
|
+
# get the current active threads that are alive
|
311
|
+
count = current_thread_count(items)
|
312
|
+
if count < max_thread_limit
|
313
|
+
logger.debug "New Thread started for #{remote}"
|
314
|
+
# start up a new thread and store it in the opts hash
|
315
|
+
opts[:thread] = Thread.new do
|
316
|
+
yield(remote, opts)
|
317
|
+
end
|
318
|
+
else
|
319
|
+
# the last thread started should be the longest wait
|
320
|
+
item, item_opts = items.reverse.find { |_i, o| o.key?(:thread) }
|
321
|
+
logger.debug "Waiting on #{item}"
|
322
|
+
item_opts[:thread].join # wait for the thread to finish
|
323
|
+
# now that we waited lets try again
|
324
|
+
redo
|
325
|
+
end
|
318
326
|
end
|
319
|
-
|
320
|
-
|
321
|
-
item, item_opts = items.reverse.find { |_i, o| o.key?(:thread) }
|
322
|
-
logger.debug "Waiting on #{item}"
|
323
|
-
item_opts[:thread].join # wait for the thread to finish
|
324
|
-
# now that we waited lets try again
|
325
|
-
redo
|
327
|
+
# wait for all the threads to finish
|
328
|
+
items.each { |_remote, opts| opts[:thread].join }
|
326
329
|
end
|
327
|
-
end
|
328
|
-
# wait for all the threads to finish
|
329
|
-
items.each { |_remote, opts| opts[:thread].join }
|
330
|
-
end
|
331
330
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
331
|
+
# @param target [String] - the target directory
|
332
|
+
# @param link [String] - the name of the link you wish to create
|
333
|
+
# works on windows and linux
|
334
|
+
def setup_symlink(target, link)
|
335
|
+
link = link['target']
|
336
|
+
return if File.symlink?(link)
|
337
|
+
|
338
|
+
logger.info("Creating symlink from #{link} to #{target}")
|
339
|
+
if windows?
|
340
|
+
target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
|
341
|
+
if Dir.respond_to?(:create_junction)
|
342
|
+
Dir.create_junction(link, target)
|
343
|
+
else
|
344
|
+
system("call mklink /J \"#{link.tr('/', '\\')}\" \"#{target.tr('/', '\\')}\"")
|
345
|
+
end
|
346
|
+
else
|
347
|
+
FileUtils.ln_sf(target, link)
|
348
|
+
end
|
346
349
|
end
|
347
|
-
else
|
348
|
-
FileUtils.ln_sf(target, link)
|
349
|
-
end
|
350
|
-
end
|
351
350
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
351
|
+
# @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
|
352
|
+
# @param [String] - the remote url or namespace/name of the module to download
|
353
|
+
# @param [Hash] - list of options such as version, branch, ref
|
354
|
+
def download_repository(remote, opts)
|
355
|
+
scm = 'git'
|
356
|
+
target = opts['target']
|
357
|
+
subdir = opts['subdir']
|
358
|
+
ref = opts['ref']
|
359
|
+
scm = opts['scm'] if opts['scm']
|
360
|
+
branch = opts['branch'] if opts['branch']
|
361
|
+
flags = opts['flags']
|
362
|
+
if valid_repo?(scm, target, remote)
|
363
|
+
update_repo(scm, target)
|
364
|
+
else
|
365
|
+
clone_repo(scm, remote, target, subdir, ref, branch, flags)
|
366
|
+
end
|
367
|
+
revision(scm, target, ref) if ref
|
368
|
+
remove_subdirectory(target, subdir) if subdir
|
369
|
+
end
|
371
370
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
371
|
+
# @return [String] - the spec/fixtures/modules directory in the module root folder
|
372
|
+
def module_target_dir
|
373
|
+
@module_target_dir ||= File.expand_path('spec/fixtures/modules')
|
374
|
+
end
|
376
375
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
376
|
+
# @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
|
377
|
+
# @param [String] - the remote url or namespace/name of the module to download
|
378
|
+
# @param [Hash] - list of options such as version
|
379
|
+
def download_module(remote, opts)
|
380
|
+
ref = ''
|
381
|
+
flags = ''
|
382
|
+
if opts.instance_of?(String)
|
383
|
+
target = opts
|
384
|
+
elsif opts.instance_of?(Hash)
|
385
|
+
target = opts['target']
|
386
|
+
ref = " --version #{opts['ref']}" unless opts['ref'].nil?
|
387
|
+
flags = " #{opts['flags']}" if opts['flags']
|
388
|
+
end
|
390
389
|
|
391
|
-
|
390
|
+
return false if File.directory?(target) && (ref.empty? || opts['ref'] == module_version(target))
|
392
391
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
392
|
+
# The PMT cannot handle multi threaded runs due to cache directory collisons
|
393
|
+
# so we randomize the directory instead.
|
394
|
+
# Does working_dir even need to be passed?
|
395
|
+
Dir.mktmpdir do |working_dir|
|
396
|
+
command = "puppet module install#{ref}#{flags} --ignore-dependencies" \
|
397
|
+
' --force' \
|
398
|
+
" --module_working_dir \"#{working_dir}\"" \
|
399
|
+
" --target-dir \"#{module_target_dir}\" \"#{remote}\""
|
401
400
|
|
402
|
-
|
403
|
-
|
401
|
+
unless system(command)
|
402
|
+
raise "Failed to install module #{remote} to #{module_target_dir}"
|
403
|
+
end
|
404
|
+
end
|
405
|
+
$CHILD_STATUS.success?
|
404
406
|
end
|
405
407
|
end
|
406
|
-
$CHILD_STATUS.success?
|
407
408
|
end
|
408
409
|
end
|
409
410
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppetlabs_spec_helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-01-
|
12
|
+
date: 2023-01-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mocha
|
@@ -29,30 +29,42 @@ dependencies:
|
|
29
29
|
name: pathspec
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - "
|
32
|
+
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0.2'
|
35
|
+
- - "<"
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.0.0
|
35
38
|
type: :runtime
|
36
39
|
prerelease: false
|
37
40
|
version_requirements: !ruby/object:Gem::Requirement
|
38
41
|
requirements:
|
39
|
-
- - "
|
42
|
+
- - ">="
|
40
43
|
- !ruby/object:Gem::Version
|
41
44
|
version: '0.2'
|
45
|
+
- - "<"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.0.0
|
42
48
|
- !ruby/object:Gem::Dependency
|
43
49
|
name: puppet-lint
|
44
50
|
requirement: !ruby/object:Gem::Requirement
|
45
51
|
requirements:
|
46
|
-
- - "
|
52
|
+
- - ">="
|
47
53
|
- !ruby/object:Gem::Version
|
48
54
|
version: 2.5.2
|
55
|
+
- - "<"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 4.0.0
|
49
58
|
type: :runtime
|
50
59
|
prerelease: false
|
51
60
|
version_requirements: !ruby/object:Gem::Requirement
|
52
61
|
requirements:
|
53
|
-
- - "
|
62
|
+
- - ">="
|
54
63
|
- !ruby/object:Gem::Version
|
55
64
|
version: 2.5.2
|
65
|
+
- - "<"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 4.0.0
|
56
68
|
- !ruby/object:Gem::Dependency
|
57
69
|
name: puppet-syntax
|
58
70
|
requirement: !ruby/object:Gem::Requirement
|