anthill-librarian-puppet 3.0.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.md +337 -0
- data/bin/librarian-puppet +7 -0
- data/lib/librarian/puppet.rb +36 -0
- data/lib/librarian/puppet/action.rb +2 -0
- data/lib/librarian/puppet/action/install.rb +26 -0
- data/lib/librarian/puppet/action/resolve.rb +21 -0
- data/lib/librarian/puppet/cli.rb +110 -0
- data/lib/librarian/puppet/dependency.rb +18 -0
- data/lib/librarian/puppet/dsl.rb +92 -0
- data/lib/librarian/puppet/environment.rb +66 -0
- data/lib/librarian/puppet/extension.rb +9 -0
- data/lib/librarian/puppet/lockfile.rb +39 -0
- data/lib/librarian/puppet/source.rb +4 -0
- data/lib/librarian/puppet/source/forge.rb +181 -0
- data/lib/librarian/puppet/source/forge/repo.rb +158 -0
- data/lib/librarian/puppet/source/forge/repo_v1.rb +92 -0
- data/lib/librarian/puppet/source/forge/repo_v3.rb +62 -0
- data/lib/librarian/puppet/source/git.rb +74 -0
- data/lib/librarian/puppet/source/githubtarball.rb +130 -0
- data/lib/librarian/puppet/source/githubtarball/repo.rb +172 -0
- data/lib/librarian/puppet/source/local.rb +176 -0
- data/lib/librarian/puppet/source/path.rb +12 -0
- data/lib/librarian/puppet/source/repo.rb +38 -0
- data/lib/librarian/puppet/templates/Puppetfile +25 -0
- data/lib/librarian/puppet/util.rb +78 -0
- data/lib/librarian/puppet/version.rb +5 -0
- metadata +232 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'librarian/action/resolve'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
module Puppet
|
5
|
+
module Action
|
6
|
+
class Resolve < Librarian::Action::Resolve
|
7
|
+
include Librarian::Puppet::Util
|
8
|
+
|
9
|
+
def run
|
10
|
+
super
|
11
|
+
manifests = environment.lock.manifests.select{ |m| m.name }
|
12
|
+
dupes = manifests.group_by{ |m| module_name(m.name) }.select { |k, v| v.size > 1 }
|
13
|
+
dupes.each do |k,v|
|
14
|
+
warn("Dependency on module '#{k}' is fullfilled by multiple modules and only one will be used: #{v.map{|m|m.name}}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'librarian/helpers'
|
2
|
+
|
3
|
+
require 'librarian/cli'
|
4
|
+
require 'librarian/puppet'
|
5
|
+
require 'librarian/puppet/action'
|
6
|
+
|
7
|
+
module Librarian
|
8
|
+
module Puppet
|
9
|
+
class Cli < Librarian::Cli
|
10
|
+
include Librarian::Puppet::Util
|
11
|
+
|
12
|
+
module Particularity
|
13
|
+
def root_module
|
14
|
+
Puppet
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
include Particularity
|
19
|
+
extend Particularity
|
20
|
+
|
21
|
+
source_root Pathname.new(__FILE__).dirname.join("templates")
|
22
|
+
|
23
|
+
def init
|
24
|
+
copy_file environment.specfile_name
|
25
|
+
|
26
|
+
if File.exists? ".gitignore"
|
27
|
+
gitignore = File.read('.gitignore').split("\n")
|
28
|
+
else
|
29
|
+
gitignore = []
|
30
|
+
end
|
31
|
+
|
32
|
+
gitignore << ".tmp/" unless gitignore.include? ".tmp/"
|
33
|
+
gitignore << "modules/" unless gitignore.include? "modules/"
|
34
|
+
|
35
|
+
File.open(".gitignore", 'w') do |f|
|
36
|
+
f.puts gitignore.join("\n")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "install", "Resolves and installs all of the dependencies you specify."
|
41
|
+
option "quiet", :type => :boolean, :default => false
|
42
|
+
option "verbose", :type => :boolean, :default => false
|
43
|
+
option "line-numbers", :type => :boolean, :default => false
|
44
|
+
option "clean", :type => :boolean, :default => false
|
45
|
+
option "strip-dot-git", :type => :boolean
|
46
|
+
option "path", :type => :string
|
47
|
+
option "destructive", :type => :boolean, :default => false
|
48
|
+
option "local", :type => :boolean, :default => false
|
49
|
+
option "use-v1-api", :type => :boolean, :default => true
|
50
|
+
option "use-short-cache-path", :type => :boolean, :default => false
|
51
|
+
def install
|
52
|
+
|
53
|
+
ensure!
|
54
|
+
clean! if options["clean"]
|
55
|
+
unless options["destructive"].nil?
|
56
|
+
environment.config_db.local['destructive'] = options['destructive'].to_s
|
57
|
+
end
|
58
|
+
if options.include?("strip-dot-git")
|
59
|
+
strip_dot_git_val = options["strip-dot-git"] ? "1" : nil
|
60
|
+
environment.config_db.local["install.strip-dot-git"] = strip_dot_git_val
|
61
|
+
end
|
62
|
+
if options.include?("path")
|
63
|
+
environment.config_db.local["path"] = options["path"]
|
64
|
+
end
|
65
|
+
|
66
|
+
environment.config_db.local['use-v1-api'] = options['use-v1-api'] ? '1' : nil
|
67
|
+
environment.config_db.local['mode'] = options['local'] ? 'local' : nil
|
68
|
+
environment.config_db.local['use-short-cache-path'] = options['use-short-cache-path'] ? '1' : nil
|
69
|
+
|
70
|
+
resolve!
|
71
|
+
debug { "Install: dependencies resolved"}
|
72
|
+
install!
|
73
|
+
end
|
74
|
+
|
75
|
+
# only used to replace / to - in the module names
|
76
|
+
def update(*names)
|
77
|
+
warn("Usage of module/name is deprecated, use module-name") if names.any? {|n| n.include?("/")}
|
78
|
+
super(*names.map{|n| normalize_name(n)})
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "package", "Cache the puppet modules in vendor/puppet/cache."
|
82
|
+
option "quiet", :type => :boolean, :default => false
|
83
|
+
option "verbose", :type => :boolean, :default => false
|
84
|
+
option "line-numbers", :type => :boolean, :default => false
|
85
|
+
option "clean", :type => :boolean, :default => false
|
86
|
+
option "strip-dot-git", :type => :boolean
|
87
|
+
option "path", :type => :string
|
88
|
+
option "destructive", :type => :boolean, :default => false
|
89
|
+
def package
|
90
|
+
environment.vendor!
|
91
|
+
install
|
92
|
+
end
|
93
|
+
|
94
|
+
def version
|
95
|
+
say "librarian-puppet v#{Librarian::Puppet::VERSION}"
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# override the actions to use our own
|
101
|
+
|
102
|
+
def install!(options = { })
|
103
|
+
Action::Install.new(environment, options).run
|
104
|
+
end
|
105
|
+
def resolve!(options = { })
|
106
|
+
Action::Resolve.new(environment, options).run
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Librarian
|
2
|
+
module Puppet
|
3
|
+
|
4
|
+
class Dependency < Librarian::Dependency
|
5
|
+
|
6
|
+
include Librarian::Puppet::Util
|
7
|
+
|
8
|
+
def initialize(name, requirement, source)
|
9
|
+
# Issue #235 fail if forge source is not defined
|
10
|
+
raise Error, "forge entry is not defined in Puppetfile" if source.instance_of?(Array) && source.empty?
|
11
|
+
|
12
|
+
super(normalize_name(name), requirement, source)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'librarian/dsl'
|
2
|
+
require 'librarian/dsl/target'
|
3
|
+
require 'librarian/puppet/source'
|
4
|
+
require 'librarian/puppet/dependency'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Puppet
|
8
|
+
class Dsl < Librarian::Dsl
|
9
|
+
|
10
|
+
FORGE_URL = "https://forgeapi.puppetlabs.com"
|
11
|
+
|
12
|
+
dependency :mod
|
13
|
+
|
14
|
+
source :forge => Source::Forge
|
15
|
+
source :git => Source::Git
|
16
|
+
source :path => Source::Path
|
17
|
+
source :github_tarball => Source::GitHubTarball
|
18
|
+
|
19
|
+
def default_specfile
|
20
|
+
Proc.new do
|
21
|
+
forge FORGE_URL
|
22
|
+
metadata
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.dependency_type
|
27
|
+
Librarian::Puppet::Dependency
|
28
|
+
end
|
29
|
+
|
30
|
+
def post_process_target(target)
|
31
|
+
# save the default forge defined
|
32
|
+
default_forge = target.sources.select {|s| s.is_a? Librarian::Puppet::Source::Forge}.first
|
33
|
+
Librarian::Puppet::Source::Forge.default = default_forge || Librarian::Puppet::Source::Forge.from_lock_options(environment, :remote => FORGE_URL)
|
34
|
+
end
|
35
|
+
|
36
|
+
def receiver(target)
|
37
|
+
Receiver.new(target)
|
38
|
+
end
|
39
|
+
|
40
|
+
class Receiver < Librarian::Dsl::Receiver
|
41
|
+
attr_reader :specfile, :working_path
|
42
|
+
|
43
|
+
# save the specfile and call librarian
|
44
|
+
def run(specfile = nil)
|
45
|
+
@working_path = specfile.kind_of?(Pathname) ? specfile.parent : Pathname.new(Dir.pwd)
|
46
|
+
@specfile = specfile
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
# implement the 'modulefile' syntax for Puppetfile
|
51
|
+
def modulefile
|
52
|
+
f = modulefile_path
|
53
|
+
raise Error, "Modulefile file does not exist: #{f}" unless File.exists?(f)
|
54
|
+
File.read(f).lines.each do |line|
|
55
|
+
regexp = /\s*dependency\s+('|")([^'"]+)\1\s*(?:,\s*('|")([^'"]+)\3)?/
|
56
|
+
regexp =~ line && mod($2, $4)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# implement the 'metadata' syntax for Puppetfile
|
61
|
+
def metadata
|
62
|
+
f = working_path.join('metadata.json')
|
63
|
+
unless File.exists?(f)
|
64
|
+
msg = "Metadata file does not exist: #{f}"
|
65
|
+
# try modulefile, in case we don't have a Puppetfile and we are using the default template
|
66
|
+
if File.exists?(modulefile_path)
|
67
|
+
modulefile
|
68
|
+
return
|
69
|
+
else
|
70
|
+
raise Error, msg
|
71
|
+
end
|
72
|
+
end
|
73
|
+
begin
|
74
|
+
json = JSON.parse(File.read(f))
|
75
|
+
rescue JSON::ParserError => e
|
76
|
+
raise Error, "Unable to parse json file #{f}: #{e}"
|
77
|
+
end
|
78
|
+
dependencyList = json['dependencies']
|
79
|
+
dependencyList.each do |d|
|
80
|
+
mod(d['name'], d['version_requirement'])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def modulefile_path
|
87
|
+
working_path.join('Modulefile')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "librarian/environment"
|
2
|
+
require "librarian/puppet/dsl"
|
3
|
+
require "librarian/puppet/source"
|
4
|
+
require "librarian/puppet/lockfile"
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Puppet
|
8
|
+
class Environment < Librarian::Environment
|
9
|
+
|
10
|
+
def adapter_name
|
11
|
+
"puppet"
|
12
|
+
end
|
13
|
+
|
14
|
+
def lockfile
|
15
|
+
Lockfile.new(self, lockfile_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ephemeral_lockfile
|
19
|
+
Lockfile.new(self, nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
def tmp_path
|
23
|
+
part = config_db["tmp"] || ".tmp"
|
24
|
+
project_path.join(part)
|
25
|
+
end
|
26
|
+
|
27
|
+
def install_path
|
28
|
+
part = config_db["path"] || "modules"
|
29
|
+
project_path.join(part)
|
30
|
+
end
|
31
|
+
|
32
|
+
def vendor_path
|
33
|
+
project_path.join('vendor/puppet')
|
34
|
+
end
|
35
|
+
|
36
|
+
def vendor_cache
|
37
|
+
vendor_path.join('cache')
|
38
|
+
end
|
39
|
+
|
40
|
+
def vendor_source
|
41
|
+
vendor_path.join('source')
|
42
|
+
end
|
43
|
+
|
44
|
+
def vendor!
|
45
|
+
vendor_cache.mkpath unless vendor_cache.exist?
|
46
|
+
vendor_source.mkpath unless vendor_source.exist?
|
47
|
+
end
|
48
|
+
|
49
|
+
def vendor?
|
50
|
+
vendor_path.exist?
|
51
|
+
end
|
52
|
+
|
53
|
+
def local?
|
54
|
+
config_db['mode'] == 'local'
|
55
|
+
end
|
56
|
+
|
57
|
+
def use_v1_api
|
58
|
+
config_db['use-v1-api']
|
59
|
+
end
|
60
|
+
|
61
|
+
def use_short_cache_path
|
62
|
+
config_db['use-short-cache-path']
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Extend Lockfile to normalize module names from acme/mod to acme-mod
|
2
|
+
module Librarian
|
3
|
+
module Puppet
|
4
|
+
class Lockfile < Librarian::Lockfile
|
5
|
+
|
6
|
+
# Extend the parser to normalize module names in old .lock files, converting / to -
|
7
|
+
class Parser < Librarian::Lockfile::Parser
|
8
|
+
|
9
|
+
include Librarian::Puppet::Util
|
10
|
+
|
11
|
+
def extract_and_parse_sources(lines)
|
12
|
+
sources = super
|
13
|
+
sources.each do |source|
|
14
|
+
source[:manifests] = Hash[source[:manifests].map do |name,manifest|
|
15
|
+
[normalize_name(name), manifest]
|
16
|
+
end]
|
17
|
+
end
|
18
|
+
sources
|
19
|
+
end
|
20
|
+
|
21
|
+
def extract_and_parse_dependencies(lines, manifests_index)
|
22
|
+
# when looking up in manifests_index normalize the name beforehand
|
23
|
+
class << manifests_index
|
24
|
+
include Librarian::Puppet::Util
|
25
|
+
alias_method :old_lookup, :[]
|
26
|
+
define_method(:[]) { |k| self.old_lookup(normalize_name(k)) }
|
27
|
+
end
|
28
|
+
super(lines, manifests_index)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def load(string)
|
34
|
+
Parser.new(environment).parse(string)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'librarian/puppet/util'
|
3
|
+
require 'librarian/puppet/source/forge/repo_v1'
|
4
|
+
require 'librarian/puppet/source/forge/repo_v3'
|
5
|
+
|
6
|
+
module Librarian
|
7
|
+
module Puppet
|
8
|
+
module Source
|
9
|
+
class Forge
|
10
|
+
include Librarian::Puppet::Util
|
11
|
+
|
12
|
+
class << self
|
13
|
+
LOCK_NAME = 'FORGE'
|
14
|
+
|
15
|
+
def default=(source)
|
16
|
+
@@default = source
|
17
|
+
end
|
18
|
+
|
19
|
+
def default
|
20
|
+
@@default
|
21
|
+
end
|
22
|
+
|
23
|
+
def lock_name
|
24
|
+
LOCK_NAME
|
25
|
+
end
|
26
|
+
|
27
|
+
def from_lock_options(environment, options)
|
28
|
+
new(environment, options[:remote], options.reject { |k, v| k == :remote })
|
29
|
+
end
|
30
|
+
|
31
|
+
def from_spec_args(environment, uri, options)
|
32
|
+
recognised_options = []
|
33
|
+
unrecognised_options = options.keys - recognised_options
|
34
|
+
unless unrecognised_options.empty?
|
35
|
+
raise Error, "unrecognised options: #{unrecognised_options.join(", ")}"
|
36
|
+
end
|
37
|
+
|
38
|
+
new(environment, uri, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def client_api_version()
|
42
|
+
version = 1
|
43
|
+
pe_version = Librarian::Puppet.puppet_version.match(/\(Puppet Enterprise (.+)\)/)
|
44
|
+
|
45
|
+
# Puppet 3.6.0+ uses api v3
|
46
|
+
if Librarian::Puppet::puppet_gem_version >= Gem::Version.create('3.6.0.a')
|
47
|
+
version = 3
|
48
|
+
# Puppet enterprise 3.2.0+ uses api v3
|
49
|
+
elsif pe_version and Gem::Version.create(pe_version[1].strip) >= Gem::Version.create('3.2.0')
|
50
|
+
version = 3
|
51
|
+
end
|
52
|
+
return version
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_accessor :environment
|
58
|
+
private :environment=
|
59
|
+
attr_reader :uri
|
60
|
+
|
61
|
+
def initialize(environment, uri, options = {})
|
62
|
+
self.environment = environment
|
63
|
+
|
64
|
+
if uri =~ %r{^http(s)?://forge\.puppetlabs\.com}
|
65
|
+
uri = "https://forgeapi.puppetlabs.com"
|
66
|
+
warn { "Replacing Puppet Forge API URL to use v3 #{uri}. You should update your Puppetfile" }
|
67
|
+
end
|
68
|
+
|
69
|
+
@uri = URI::parse(uri)
|
70
|
+
@cache_path = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
clean_uri(uri).to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
def ==(other)
|
78
|
+
other &&
|
79
|
+
self.class == other.class &&
|
80
|
+
self.uri == other.uri
|
81
|
+
end
|
82
|
+
|
83
|
+
alias :eql? :==
|
84
|
+
|
85
|
+
def hash
|
86
|
+
self.to_s.hash
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_spec_args
|
90
|
+
[clean_uri(uri).to_s, {}]
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_lock_options
|
94
|
+
{:remote => clean_uri(uri).to_s}
|
95
|
+
end
|
96
|
+
|
97
|
+
def pinned?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
101
|
+
def unpin!
|
102
|
+
end
|
103
|
+
|
104
|
+
def install!(manifest)
|
105
|
+
manifest.source == self or raise ArgumentError
|
106
|
+
|
107
|
+
debug { "Installing #{manifest}" }
|
108
|
+
|
109
|
+
name = manifest.name
|
110
|
+
version = manifest.version
|
111
|
+
install_path = install_path(name)
|
112
|
+
repo = repo(name)
|
113
|
+
|
114
|
+
repo.install_version! version, install_path
|
115
|
+
end
|
116
|
+
|
117
|
+
def manifest(name, version, dependencies)
|
118
|
+
manifest = Manifest.new(self, name)
|
119
|
+
manifest.version = version
|
120
|
+
manifest.dependencies = dependencies
|
121
|
+
manifest
|
122
|
+
end
|
123
|
+
|
124
|
+
def cache_path
|
125
|
+
@cache_path ||= begin
|
126
|
+
if environment.use_short_cache_path
|
127
|
+
# To shorten the cache path to avoid #17
|
128
|
+
# take only the first 7 digits of the SHA1 checksum of the forge URI
|
129
|
+
# (short Git commit hash approach)
|
130
|
+
dir = Digest::SHA1.hexdigest("#{uri.host}#{uri.path}")[0..6]
|
131
|
+
else
|
132
|
+
dir = "#{uri.host}#{uri.path}".gsub(/[^0-9a-z\-_]/i, '_')
|
133
|
+
end
|
134
|
+
environment.cache_path.join("source/puppet/forge/#{dir}")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def install_path(name)
|
139
|
+
environment.install_path.join(module_name(name))
|
140
|
+
end
|
141
|
+
|
142
|
+
def fetch_version(name, version_uri)
|
143
|
+
versions = repo(name).versions
|
144
|
+
if versions.include? version_uri
|
145
|
+
version_uri
|
146
|
+
else
|
147
|
+
versions.first
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def fetch_dependencies(name, version, version_uri)
|
152
|
+
repo(name).dependencies(version).map do |k, v|
|
153
|
+
v = Librarian::Dependency::Requirement.new(v).to_gem_requirement
|
154
|
+
Dependency.new(k, v, nil)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def manifests(name)
|
159
|
+
repo(name).manifests
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def repo(name)
|
165
|
+
@repo ||= {}
|
166
|
+
|
167
|
+
unless @repo[name]
|
168
|
+
# If we are using the official Forge then use API v3, otherwise use the preferred api
|
169
|
+
# as defined by the CLI option use_v1_api
|
170
|
+
if uri.hostname =~ /\.puppetlabs\.com$/ || !environment.use_v1_api
|
171
|
+
@repo[name] = RepoV3.new(self, name)
|
172
|
+
else
|
173
|
+
@repo[name] = RepoV1.new(self, name)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
@repo[name]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|