octocatalog-diff 0.5.1
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/.version +1 -0
- data/LICENSE +20 -0
- data/README.md +82 -0
- data/bin/octocatalog-diff +75 -0
- data/doc/advanced-bootstrap.md +33 -0
- data/doc/advanced-cache-dir.md +24 -0
- data/doc/advanced-catalog-only.md +37 -0
- data/doc/advanced-ci.md +13 -0
- data/doc/advanced-dynamic-ignores.md +123 -0
- data/doc/advanced-future-parser.md +11 -0
- data/doc/advanced-ignores.md +224 -0
- data/doc/advanced-output-formats.md +96 -0
- data/doc/advanced-output-hacks.md +45 -0
- data/doc/advanced-override-facts.md +67 -0
- data/doc/advanced-pe-enc.md +52 -0
- data/doc/advanced-puppet-master.md +50 -0
- data/doc/advanced-puppet-versions.md +9 -0
- data/doc/advanced-storeconfigs.md +72 -0
- data/doc/advanced-using-without-git.md +15 -0
- data/doc/advanced.md +43 -0
- data/doc/basic.md +70 -0
- data/doc/configuration-enc.md +69 -0
- data/doc/configuration-hiera.md +103 -0
- data/doc/configuration-puppetdb.md +49 -0
- data/doc/configuration.md +51 -0
- data/doc/dev/README.md +1 -0
- data/doc/dev/coverage.md +34 -0
- data/doc/dev/how-to-add-options.md +83 -0
- data/doc/dev/integration-tests.md +63 -0
- data/doc/dev/releasing.md +19 -0
- data/doc/installation.md +49 -0
- data/doc/limitations.md +34 -0
- data/doc/optionsref.md +947 -0
- data/doc/requirements.md +16 -0
- data/doc/roadmap.md +26 -0
- data/doc/similar.md +17 -0
- data/doc/troubleshooting.md +54 -0
- data/lib/octocatalog-diff.rb +12 -0
- data/lib/octocatalog-diff/bootstrap.rb +53 -0
- data/lib/octocatalog-diff/catalog-diff/cli.rb +205 -0
- data/lib/octocatalog-diff/catalog-diff/cli/catalogs.rb +240 -0
- data/lib/octocatalog-diff/catalog-diff/cli/diffs.rb +145 -0
- data/lib/octocatalog-diff/catalog-diff/cli/helpers/fact_override.rb +99 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options.rb +173 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/basedir.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrap_environment.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrap_script.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrap_then_exit.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrapped_dirs.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/cached_master_dir.rb +21 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/catalog_only.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/color.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/compare_file_text.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/debug.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/display_datatype_changes.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/display_detail_add.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/display_source_file_line.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/enc.rb +31 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/existing_catalogs.rb +25 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/fact_file.rb +23 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/fact_override.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/facts_terminus.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/from_puppetdb.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/header.rb +24 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/hiera_config.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/hiera_path_strip.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/hostname.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/ignore.rb +24 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/ignore_attr.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/ignore_tags.rb +23 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/include_tags.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/master_cache_branch.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/output_file.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/output_format.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/parallel.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/parser.rb +48 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pass_env_vars.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_ssl_ca.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_ssl_client_cert.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_ssl_client_key.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_token.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_token_file.rb +17 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_url.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_binary.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_api_version.rb +20 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_ssl_ca.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_ssl_client_cert.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_ssl_client_key.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_ca.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_cert.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_key.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_password.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_password_file.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_url.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/quiet.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/retry_failed_catalog.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/safe_to_delete_cached_master_dir.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/storeconfigs.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/suppress_absent_file_details.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/to_from_branch.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/printer.rb +52 -0
- data/lib/octocatalog-diff/catalog-diff/differ.rb +615 -0
- data/lib/octocatalog-diff/catalog-diff/display.rb +125 -0
- data/lib/octocatalog-diff/catalog-diff/display/json.rb +25 -0
- data/lib/octocatalog-diff/catalog-diff/display/text.rb +452 -0
- data/lib/octocatalog-diff/catalog-util/bootstrap.rb +145 -0
- data/lib/octocatalog-diff/catalog-util/builddir.rb +289 -0
- data/lib/octocatalog-diff/catalog-util/cached_master_directory.rb +169 -0
- data/lib/octocatalog-diff/catalog-util/command.rb +96 -0
- data/lib/octocatalog-diff/catalog-util/enc.rb +77 -0
- data/lib/octocatalog-diff/catalog-util/enc/noop.rb +22 -0
- data/lib/octocatalog-diff/catalog-util/enc/pe.rb +99 -0
- data/lib/octocatalog-diff/catalog-util/enc/pe/v1.rb +61 -0
- data/lib/octocatalog-diff/catalog-util/enc/script.rb +88 -0
- data/lib/octocatalog-diff/catalog-util/facts.rb +89 -0
- data/lib/octocatalog-diff/catalog-util/fileresources.rb +83 -0
- data/lib/octocatalog-diff/catalog-util/git.rb +65 -0
- data/lib/octocatalog-diff/catalog.rb +209 -0
- data/lib/octocatalog-diff/catalog/computed.rb +205 -0
- data/lib/octocatalog-diff/catalog/json.rb +30 -0
- data/lib/octocatalog-diff/catalog/noop.rb +19 -0
- data/lib/octocatalog-diff/catalog/puppetdb.rb +82 -0
- data/lib/octocatalog-diff/catalog/puppetmaster.rb +121 -0
- data/lib/octocatalog-diff/external/pson/LICENSE +17 -0
- data/lib/octocatalog-diff/external/pson/README.md +20 -0
- data/lib/octocatalog-diff/external/pson/common.rb +370 -0
- data/lib/octocatalog-diff/external/pson/pure.rb +15 -0
- data/lib/octocatalog-diff/external/pson/pure/generator.rb +395 -0
- data/lib/octocatalog-diff/external/pson/pure/parser.rb +307 -0
- data/lib/octocatalog-diff/external/pson/version.rb +8 -0
- data/lib/octocatalog-diff/facts.rb +125 -0
- data/lib/octocatalog-diff/facts/json.rb +20 -0
- data/lib/octocatalog-diff/facts/puppetdb.rb +59 -0
- data/lib/octocatalog-diff/facts/yaml.rb +29 -0
- data/lib/octocatalog-diff/puppetdb.rb +163 -0
- data/lib/octocatalog-diff/util/colored.rb +20 -0
- data/lib/octocatalog-diff/util/httparty.rb +158 -0
- data/lib/octocatalog-diff/util/parallel.rb +170 -0
- data/lib/octocatalog-diff/util/puppetversion.rb +24 -0
- data/lib/octocatalog-diff/version.rb +7 -0
- metadata +386 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require_relative '../bootstrap'
|
|
2
|
+
require_relative '../util/parallel'
|
|
3
|
+
require_relative 'git'
|
|
4
|
+
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module OctocatalogDiff
|
|
8
|
+
module CatalogUtil
|
|
9
|
+
# Methods to bootstrap a directory. Intended to be called from catalog-diff/cli. This handles
|
|
10
|
+
# parallelization of bootstrap, and formats arguments as expected by the higher level bootstrap
|
|
11
|
+
# script.
|
|
12
|
+
class Bootstrap
|
|
13
|
+
# Exceptions that are anticipated can be caught in the calling class and tested
|
|
14
|
+
# for explicitly in spec tests.
|
|
15
|
+
class BootstrapError < RuntimeError; end
|
|
16
|
+
|
|
17
|
+
# Bootstrap directories specified by --bootstrapped-from-dir and --bootstrapped-to-dir
|
|
18
|
+
# command line options. Bootstrapping occurs in parallel. This takes no parameters (options come
|
|
19
|
+
# from options) and returns nothing (it raises an exception if something fails).
|
|
20
|
+
def self.bootstrap_directory_parallelizer(options, logger)
|
|
21
|
+
# What directories do we have to bootstrap?
|
|
22
|
+
dirs = []
|
|
23
|
+
|
|
24
|
+
unless options[:bootstrapped_from_dir].nil?
|
|
25
|
+
if options[:from_env] == '.'
|
|
26
|
+
message = 'Must specify a from-branch other than . when using --bootstrapped-from-dir!' \
|
|
27
|
+
' Please use "-f <from_branch>" argument.'
|
|
28
|
+
logger.error(message)
|
|
29
|
+
raise BootstrapError, message
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
opts = options.merge(branch: options[:from_env],
|
|
33
|
+
path: options[:bootstrapped_from_dir],
|
|
34
|
+
tag: 'from_dir',
|
|
35
|
+
dir: options[:basedir])
|
|
36
|
+
dirs << opts
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
unless options[:bootstrapped_to_dir].nil?
|
|
40
|
+
if options[:to_env] == '.'
|
|
41
|
+
message = 'Must specify a to-branch other than . when using --bootstrapped-to-dir!' \
|
|
42
|
+
' Please use "-t <to_branch>" argument.'
|
|
43
|
+
logger.error(message)
|
|
44
|
+
raise BootstrapError, message
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
opts = options.merge(branch: options[:to_env],
|
|
48
|
+
path: options[:bootstrapped_to_dir],
|
|
49
|
+
tag: 'to_dir')
|
|
50
|
+
dirs << opts
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# If there are no directories given, advise the user to supply the necessary options
|
|
54
|
+
if dirs.empty?
|
|
55
|
+
return unless options[:cached_master_dir].nil?
|
|
56
|
+
message = 'Specify one or more of --bootstrapped-from-dir / --bootstrapped-to-dir / --cached-master-dir' \
|
|
57
|
+
' when using --bootstrap_then_exit'
|
|
58
|
+
logger.error(message)
|
|
59
|
+
raise BootstrapError, message
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Bootstrap the directories in parallel. Since there are no results here that we
|
|
63
|
+
# care about, increment the success counter for each run that did not throw an exception.
|
|
64
|
+
tasks = dirs.map do |x|
|
|
65
|
+
OctocatalogDiff::Util::Parallel::Task.new(
|
|
66
|
+
method: method(:bootstrap_directory),
|
|
67
|
+
description: "bootstrap #{x[:tag]} #{x[:path]} for #{x[:branch]}",
|
|
68
|
+
args: x
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
logger.debug("Begin #{dirs.size} bootstrap(s)")
|
|
73
|
+
parallel_tasks = OctocatalogDiff::Util::Parallel.run_tasks(tasks, logger, options[:parallel])
|
|
74
|
+
parallel_tasks.each do |result|
|
|
75
|
+
if result.status
|
|
76
|
+
logger.debug("Success bootstrap_directory for #{result.args[:tag]}")
|
|
77
|
+
else
|
|
78
|
+
errmsg = "Failed bootstrap_directory for #{result.args[:tag]}: #{result.exception.class} #{result.exception.message}"
|
|
79
|
+
raise BootstrapError, errmsg
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Performs the actual bootstrap of a directory. Intended to be called by bootstrap_directory_parallelizer
|
|
85
|
+
# above, or as part of the parallelized catalog build process from catalog-diff/cli/catalogs.
|
|
86
|
+
# @param logger [Logger] Logger object
|
|
87
|
+
# @param dir_opts [Hash] Directory options: branch, path, tag
|
|
88
|
+
def self.bootstrap_directory(options, logger)
|
|
89
|
+
raise ArgumentError, ':path must be supplied' unless options[:path]
|
|
90
|
+
FileUtils.mkdir_p(options[:path]) unless Dir.exist?(options[:path])
|
|
91
|
+
git_checkout(logger, options) if options[:branch]
|
|
92
|
+
unless options[:bootstrap_script].nil?
|
|
93
|
+
install_bootstrap_script(logger, options)
|
|
94
|
+
run_bootstrap(logger, options)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Perform git checkout
|
|
99
|
+
# @param logger [Logger] Logger object
|
|
100
|
+
# @param dir_opts [Hash] Directory options: branch, path, tag
|
|
101
|
+
def self.git_checkout(logger, dir_opts)
|
|
102
|
+
logger.debug("Begin git checkout #{dir_opts[:basedir]}:#{dir_opts[:branch]} -> #{dir_opts[:path]}")
|
|
103
|
+
OctocatalogDiff::CatalogUtil::Git.check_out_git_archive(dir_opts.merge(logger: logger))
|
|
104
|
+
logger.debug("Success git checkout #{dir_opts[:basedir]}:#{dir_opts[:branch]} -> #{dir_opts[:path]}")
|
|
105
|
+
rescue OctocatalogDiff::CatalogUtil::Git::GitCheckoutError => exc
|
|
106
|
+
logger.error("Git checkout error: #{exc}")
|
|
107
|
+
raise BootstrapError, exc
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Install bootstrap script in the target directory. This allows proper bootstrapping from the
|
|
111
|
+
# latest version of the script, not the script that was in place at the time that directory's branch
|
|
112
|
+
# was committed.
|
|
113
|
+
# @param logger [Logger] Logger object
|
|
114
|
+
# @param opts [Hash] Directory options
|
|
115
|
+
def self.install_bootstrap_script(logger, opts)
|
|
116
|
+
# Verify that bootstrap file exists
|
|
117
|
+
src = File.join(opts[:basedir], opts[:bootstrap_script])
|
|
118
|
+
raise BootstrapError, "Bootstrap script #{src} does not exist" unless File.file?(src)
|
|
119
|
+
|
|
120
|
+
logger.debug('Begin install bootstrap script in target directory')
|
|
121
|
+
|
|
122
|
+
# Create destination directory if needed
|
|
123
|
+
dest = File.join(opts[:path], opts[:bootstrap_script])
|
|
124
|
+
dest_dir = File.dirname(dest)
|
|
125
|
+
FileUtils.mkdir_p(dest_dir) unless File.directory?(dest_dir)
|
|
126
|
+
|
|
127
|
+
# Copy file and make executable
|
|
128
|
+
FileUtils.cp src, dest
|
|
129
|
+
FileUtils.chmod 0o755, dest
|
|
130
|
+
logger.debug("Success: copied #{src} to #{dest}")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Execute the bootstrap.
|
|
134
|
+
# @param logger [Logger] Logger object
|
|
135
|
+
# @param opts [Hash] Directory options
|
|
136
|
+
def self.run_bootstrap(logger, opts)
|
|
137
|
+
logger.debug("Begin bootstrap with '#{opts[:bootstrap_script]}' in #{opts[:path]}")
|
|
138
|
+
result = OctocatalogDiff::Bootstrap.bootstrap(opts)
|
|
139
|
+
raise BootstrapError, "bootstrap failed for #{opts[:path]}: #{result[:output]}" unless (result[:status_code]).zero?
|
|
140
|
+
logger.debug("Success bootstrap in #{opts[:path]}")
|
|
141
|
+
result[:output]
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
require_relative '../facts'
|
|
4
|
+
require_relative 'enc'
|
|
5
|
+
|
|
6
|
+
module OctocatalogDiff
|
|
7
|
+
module CatalogUtil
|
|
8
|
+
# Represents a directory that is created such that a catalog can be compiled
|
|
9
|
+
# in it. This has the following major functions:
|
|
10
|
+
# - Create the temporary directory that will serve as the puppet configuration directory
|
|
11
|
+
# - Register a handler to remove the temporary directory upon exit
|
|
12
|
+
# - Install needed configuration files within the directory (e.g. puppetdb.conf)
|
|
13
|
+
# - Install the facts into the directory
|
|
14
|
+
# - Install 'environments/production' which is a symlink to the checkout of the puppet code
|
|
15
|
+
class BuildDir
|
|
16
|
+
# Allow the path to the temporary directory to be read
|
|
17
|
+
attr_reader :tempdir, :enc, :fact_file
|
|
18
|
+
|
|
19
|
+
# Constructor
|
|
20
|
+
# Options for constructor:
|
|
21
|
+
# :puppetdb_url [String] PuppetDB Server URLs
|
|
22
|
+
# :puppetdb_server_url_timeout [Fixnum] Timeout (seconds) for puppetdb.conf
|
|
23
|
+
# :facts [OctocatalogDiff::Facts] Facts object
|
|
24
|
+
# :fact_file [String] File from which to read facts
|
|
25
|
+
# :node [String] Node name
|
|
26
|
+
# :basedir [String] Directory containing puppet code
|
|
27
|
+
# :enc [String] ENC script file (can be relative or absolute path)
|
|
28
|
+
# :pe_enc_url [String] ENC URL (for Puppet Enterprise node classification service)
|
|
29
|
+
# :hiera_config [String] hiera configuration file (relative to base directory)
|
|
30
|
+
# :hiera_path_strip [String] string to strip off the beginning of :datadir
|
|
31
|
+
# :puppetdb_ssl_ca [String] Path to SSL CA certificate
|
|
32
|
+
# :puppetdb_ssl_client_key [String] String representation of SSL client key
|
|
33
|
+
# :puppetdb_ssl_client_cert [String] String representation of SSL client certificate
|
|
34
|
+
# :puppetdb_ssl_client_password [String] Password to unlock SSL private key
|
|
35
|
+
# @param options [Hash] Options for class; see above description
|
|
36
|
+
def initialize(options = {}, logger = nil)
|
|
37
|
+
@options = options.dup
|
|
38
|
+
@tempdir = Dir.mktmpdir
|
|
39
|
+
at_exit { FileUtils.rm_rf(@tempdir) if File.directory?(@tempdir) }
|
|
40
|
+
|
|
41
|
+
@factdir = nil
|
|
42
|
+
@enc = nil
|
|
43
|
+
@fact_file = nil
|
|
44
|
+
@node = options[:node]
|
|
45
|
+
|
|
46
|
+
create_structure
|
|
47
|
+
install_directory_symlink(logger, options[:basedir])
|
|
48
|
+
|
|
49
|
+
# These configurations are optional. Don't call the methods if parameters are nil.
|
|
50
|
+
unless options[:puppetdb_url].nil?
|
|
51
|
+
install_puppetdb_conf(logger, options[:puppetdb_url], options[:puppetdb_server_url_timeout])
|
|
52
|
+
install_routes_yaml(logger)
|
|
53
|
+
end
|
|
54
|
+
unless options[:hiera_config].nil?
|
|
55
|
+
install_hiera_config(logger, options[:hiera_config], options[:hiera_path_strip])
|
|
56
|
+
end
|
|
57
|
+
@fact_file = install_fact_file(logger, options) unless options.fetch(:facts_terminus, 'yaml') != 'yaml'
|
|
58
|
+
@enc = install_enc(logger) unless options[:enc].nil? && options[:pe_enc_url].nil?
|
|
59
|
+
install_ssl(logger, options) if options[:puppetdb_ssl_ca] || options[:puppetdb_ssl_client_cert]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Create common structure
|
|
63
|
+
def create_structure
|
|
64
|
+
%w(environments facts var var/ssl var/yaml var/yaml/facts).each do |dir|
|
|
65
|
+
Dir.mkdir(File.join(@tempdir, dir))
|
|
66
|
+
FileUtils.chmod 0o755, File.join(@tempdir, dir)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Install puppetdb.conf file in temporary directory
|
|
71
|
+
# @param server_urls [String] String for server_urls in puppetdb.conf
|
|
72
|
+
# @param server_url_timeout [Fixnum] Value for server_url_timeout in puppetdb.conf
|
|
73
|
+
def install_puppetdb_conf(logger, server_urls, server_url_timeout = 30)
|
|
74
|
+
unless server_urls.is_a?(String)
|
|
75
|
+
raise ArgumentError, "server_urls must be a string, got a: #{server_urls.class}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
server_url_timeout ||= 30 # If called with nil argument, supply default
|
|
79
|
+
unless server_url_timeout.is_a?(Fixnum)
|
|
80
|
+
raise ArgumentError, "server_url_timeout must be a fixnum, got a: #{server_url_timeout.class}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
puppetdb_conf = File.join(@tempdir, 'puppetdb.conf')
|
|
84
|
+
File.open(puppetdb_conf, 'w') do |f|
|
|
85
|
+
f.write "[main]\n"
|
|
86
|
+
f.write "server_urls = #{server_urls}\n"
|
|
87
|
+
f.write "server_url_timeout = #{server_url_timeout}\n"
|
|
88
|
+
end
|
|
89
|
+
logger.debug("Installed puppetdb.conf file at #{puppetdb_conf}")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Install routes.yaml file in temporary directory
|
|
93
|
+
# No parameters or return - thus just writes a file (and notes it to debugging log)
|
|
94
|
+
# Note: catalog cache => json avoids sending the compiled catalog to PuppetDB
|
|
95
|
+
# even if storeconfigs is enabled.
|
|
96
|
+
def install_routes_yaml(logger)
|
|
97
|
+
routes_yaml = File.join(@tempdir, 'routes.yaml')
|
|
98
|
+
routes_hash = {
|
|
99
|
+
'master' => {
|
|
100
|
+
'facts' => {
|
|
101
|
+
'terminus' => 'puppetdb',
|
|
102
|
+
'cache' => 'yaml'
|
|
103
|
+
},
|
|
104
|
+
'catalog' => {
|
|
105
|
+
'cache' => 'json'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
File.open(routes_yaml, 'w') { |f| f.write(routes_hash.to_yaml) }
|
|
110
|
+
logger.debug("Installed routes.yaml file at #{routes_yaml}")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Install the fact file in temporary directory
|
|
114
|
+
# @param options [Hash] Options
|
|
115
|
+
def install_fact_file(logger, options)
|
|
116
|
+
unless options[:facts_terminus].nil? || options[:facts_terminus] == 'yaml'
|
|
117
|
+
raise ArgumentError, "Called install_fact_file but :facts_terminus = #{options[:facts_terminus]}"
|
|
118
|
+
end
|
|
119
|
+
unless options[:node].is_a?(String) && !options[:node].empty?
|
|
120
|
+
raise ArgumentError, 'Called install_fact_file without node, or with an empty node'
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
facts = if options[:fact_file]
|
|
124
|
+
raise Errno::ENOENT, "Fact file #{options[:fact_file]} does not exist" unless File.file?(options[:fact_file])
|
|
125
|
+
fact_file_opts = { fact_file_string: File.read(options[:fact_file]) }
|
|
126
|
+
fact_file_opts[:backend] = Regexp.last_match(1).to_sym if options[:fact_file] =~ /.*\.(\w+)$/
|
|
127
|
+
OctocatalogDiff::Facts.new(fact_file_opts)
|
|
128
|
+
elsif options[:facts].is_a?(OctocatalogDiff::Facts)
|
|
129
|
+
options[:facts].dup
|
|
130
|
+
else
|
|
131
|
+
raise ArgumentError, 'No facts passed to "install_fact_file" method'
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if options[:fact_override].is_a?(Array)
|
|
135
|
+
options[:fact_override].each do |override|
|
|
136
|
+
old_value = facts.fact(override.key)
|
|
137
|
+
facts.override(override.key, override.value)
|
|
138
|
+
logger.debug("Override #{override.key} from #{old_value.inspect} to #{override.value.inspect}")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
fact_file_out = File.join(@tempdir, 'var', 'yaml', 'facts', "#{options[:node]}.yaml")
|
|
143
|
+
File.open(fact_file_out, 'w') { |f| f.write(facts.facts_to_yaml(options[:node])) }
|
|
144
|
+
logger.debug("Installed fact file at #{fact_file_out}")
|
|
145
|
+
fact_file_out
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Install symbolic link to puppet environment
|
|
149
|
+
# @param dir [String] Directory to link to
|
|
150
|
+
def install_directory_symlink(logger, dir)
|
|
151
|
+
raise ArgumentError, "Called install_directory_symlink with #{dir.class} argument" unless dir.is_a?(String)
|
|
152
|
+
raise Errno::ENOENT, "Specified directory #{dir} doesn't exist" unless File.directory?(dir)
|
|
153
|
+
|
|
154
|
+
environment_symlink = File.join(@tempdir, 'environments', 'production')
|
|
155
|
+
FileUtils.rm_f environment_symlink if File.exist?(environment_symlink)
|
|
156
|
+
FileUtils.symlink dir, environment_symlink
|
|
157
|
+
logger.debug("Symlinked #{environment_symlink} -> #{dir}")
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Install ENC
|
|
161
|
+
# @param enc [String] Path to ENC script, relative to checkout
|
|
162
|
+
def install_enc(logger)
|
|
163
|
+
raise ArgumentError, 'A node must be specified when using an ENC' unless @node.is_a?(String)
|
|
164
|
+
enc_obj = OctocatalogDiff::CatalogUtil::ENC.new(@options.merge(tempdir: @tempdir))
|
|
165
|
+
raise "Failed ENC: #{enc_obj.error_message}" if enc_obj.error_message
|
|
166
|
+
|
|
167
|
+
enc_path = File.join(@tempdir, 'enc.sh')
|
|
168
|
+
File.open(enc_path, 'w') do |f|
|
|
169
|
+
f.write "#!/bin/sh\n"
|
|
170
|
+
f.write "cat <<-EOF\n"
|
|
171
|
+
f.write enc_obj.content
|
|
172
|
+
f.write "\nEOF\n"
|
|
173
|
+
end
|
|
174
|
+
FileUtils.chmod 0o755, enc_path
|
|
175
|
+
|
|
176
|
+
logger.debug("Installed ENC to echo content, #{enc_obj.content.length} bytes")
|
|
177
|
+
enc_path
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Install hiera config file
|
|
181
|
+
# @param hiera_config [String] Path to file, relative to checkout
|
|
182
|
+
# @param hiera_path_strip [String] Prefix to strip off when munging file
|
|
183
|
+
def install_hiera_config(logger, hiera_config, hiera_path_strip)
|
|
184
|
+
# Validate hiera config file
|
|
185
|
+
unless hiera_config.is_a?(String)
|
|
186
|
+
raise ArgumentError, "Called install_hiera_config with a #{hiera_config.class} argument"
|
|
187
|
+
end
|
|
188
|
+
file_src = if hiera_config.start_with? '/'
|
|
189
|
+
hiera_config
|
|
190
|
+
elsif hiera_config =~ %r{^environments/production/}
|
|
191
|
+
File.join(@tempdir, hiera_config)
|
|
192
|
+
else
|
|
193
|
+
File.join(@tempdir, 'environments', 'production', hiera_config)
|
|
194
|
+
end
|
|
195
|
+
raise Errno::ENOENT, "hiera.yaml (#{file_src}) wasn't found" unless File.file?(file_src)
|
|
196
|
+
|
|
197
|
+
# Munge datadir in hiera config file
|
|
198
|
+
obj = YAML.load_file(file_src)
|
|
199
|
+
%w(yaml json).each do |key|
|
|
200
|
+
next unless obj.key?(key.to_sym)
|
|
201
|
+
next if obj[key.to_sym][:datadir].nil?
|
|
202
|
+
unless hiera_path_strip.nil?
|
|
203
|
+
rexp1 = Regexp.new('^' + hiera_path_strip)
|
|
204
|
+
obj[key.to_sym][:datadir].sub!(rexp1, @tempdir)
|
|
205
|
+
end
|
|
206
|
+
rexp2 = Regexp.new('%{(::)?environment}')
|
|
207
|
+
obj[key.to_sym][:datadir].sub!(rexp2, 'production')
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Write properly formatted hiera config file into temporary directory
|
|
211
|
+
File.open(File.join(@tempdir, 'hiera.yaml'), 'w') { |f| f.write(obj.to_yaml.gsub('!ruby/sym ', ':')) }
|
|
212
|
+
logger.debug("Installed hiera.yaml from #{file_src} to #{File.join(@tempdir, 'hiera.yaml')}")
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Install SSL certificate authority certificate, client key, and client certificate into the
|
|
216
|
+
# expected locations within Puppet's SSL directory. Note that if the client key has a password,
|
|
217
|
+
# this will write the key (without password) onto disk, because Puppet doesn't support unlocking
|
|
218
|
+
# the private key.
|
|
219
|
+
# @param logger [Logger] Logger object
|
|
220
|
+
# @param options [Hash] Options hash
|
|
221
|
+
def install_ssl(logger, options)
|
|
222
|
+
return unless options[:puppetdb_ssl_client_cert] || options[:puppetdb_ssl_client_key] || options[:puppetdb_ssl_ca]
|
|
223
|
+
|
|
224
|
+
# Create directory structure expected by Puppet
|
|
225
|
+
%w(var/ssl/certs var/ssl/private var/ssl/private_keys).each do |dir|
|
|
226
|
+
Dir.mkdir(File.join(@tempdir, dir))
|
|
227
|
+
FileUtils.chmod 0o700, File.join(@tempdir, dir)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# SSL client auth requested?
|
|
231
|
+
if options[:puppetdb_ssl_client_cert] || options[:puppetdb_ssl_client_key]
|
|
232
|
+
raise ArgumentError, '--puppetdb-ssl-ca must be provided for client auth' unless options[:puppetdb_ssl_ca]
|
|
233
|
+
raise ArgumentError, '--puppetdb-ssl-client-cert must be provided' unless options[:puppetdb_ssl_client_cert]
|
|
234
|
+
raise ArgumentError, '--puppetdb-ssl-client-key must be provided' unless options[:puppetdb_ssl_client_key]
|
|
235
|
+
install_ssl_client(logger, options)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# SSL CA provided?
|
|
239
|
+
install_ssl_ca(logger, options) if options[:puppetdb_ssl_ca]
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
private
|
|
243
|
+
|
|
244
|
+
# Install SSL certificate authority certificate
|
|
245
|
+
# @param logger [Logger] Logger object
|
|
246
|
+
# @param options [Hash] Options hash
|
|
247
|
+
def install_ssl_ca(logger, options)
|
|
248
|
+
ca_file = options[:puppetdb_ssl_ca]
|
|
249
|
+
raise Errno::ENOENT, 'SSL CA file does not exist' unless File.file?(ca_file)
|
|
250
|
+
ca_content = File.read(ca_file)
|
|
251
|
+
ca_outfile = File.join(@tempdir, 'var', 'ssl', 'certs', 'ca.pem')
|
|
252
|
+
File.open(ca_outfile, 'w') { |f| f.write(ca_content) }
|
|
253
|
+
logger.debug "Installed CA certificate in #{ca_outfile}"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Install SSL keypair for client certificate authentication
|
|
257
|
+
# @param logger [Logger] Logger object
|
|
258
|
+
# @param options [Hash] Options hash
|
|
259
|
+
def install_ssl_client(logger, options)
|
|
260
|
+
# Since Puppet always looks for the key and cert in a file named after the hostname, determine the
|
|
261
|
+
# hostname here for the purposes of naming the files.
|
|
262
|
+
require 'socket'
|
|
263
|
+
host = Socket.gethostname
|
|
264
|
+
install_ssl_client_cert(logger, host, options[:puppetdb_ssl_client_cert])
|
|
265
|
+
install_ssl_client_key(logger, host, options[:puppetdb_ssl_client_key])
|
|
266
|
+
install_ssl_client_password(logger, options[:puppetdb_ssl_client_password])
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def install_ssl_client_cert(logger, host, content)
|
|
270
|
+
cert_outfile = File.join(@tempdir, 'var', 'ssl', 'certs', "#{host}.pem")
|
|
271
|
+
File.open(cert_outfile, 'w') { |f| f.write(content) }
|
|
272
|
+
logger.debug "Installed SSL client certificate in #{cert_outfile}"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def install_ssl_client_key(logger, host, content)
|
|
276
|
+
key_outfile = File.join(@tempdir, 'var', 'ssl', 'private_keys', "#{host}.pem")
|
|
277
|
+
File.open(key_outfile, 'w') { |f| f.write(content) }
|
|
278
|
+
logger.debug "Installed SSL client key in #{key_outfile}"
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def install_ssl_client_password(logger, password)
|
|
282
|
+
return unless password
|
|
283
|
+
password_outfile = File.join(@tempdir, 'var', 'ssl', 'private', 'password')
|
|
284
|
+
File.open(password_outfile, 'w') { |f| f.write(password) }
|
|
285
|
+
logger.debug "Installed SSL client key password in #{password_outfile}"
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
require_relative 'bootstrap'
|
|
2
|
+
require_relative 'git'
|
|
3
|
+
require_relative '../catalog-diff/cli/catalogs'
|
|
4
|
+
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module OctocatalogDiff
|
|
8
|
+
module CatalogUtil
|
|
9
|
+
# Handle the bootstrapped and cached checkout of [master branch]. This is an optimization
|
|
10
|
+
# targeted at local development environments, since a frequent pattern is "run a catalog-diff
|
|
11
|
+
# between what I have here, and master."
|
|
12
|
+
#
|
|
13
|
+
# Please note that there could be a race condition here if this code was run in parallel (i.e.,
|
|
14
|
+
# the cached master directory is blown away and re-created when a Puppet catalog compile is in
|
|
15
|
+
# progress). Do not introduce this code to an environment where catalog-diff may be running in
|
|
16
|
+
# parallel unless you have accounted for this (or are willing to tolerate any errors).
|
|
17
|
+
class CachedMasterDirectory
|
|
18
|
+
# Set default branch. Can be overridden by options[:master_cache_branch].
|
|
19
|
+
DEFAULT_MASTER_BRANCH = 'origin/master'.freeze
|
|
20
|
+
|
|
21
|
+
# Get the master branch based on supplied options.
|
|
22
|
+
# @param options [Hash] Options hash
|
|
23
|
+
# @return [String] Master branch configured (defaults to DEFAULT_MASTER_BRANCH)
|
|
24
|
+
def self.master_branch(options = {})
|
|
25
|
+
options.fetch(:master_cache_branch, DEFAULT_MASTER_BRANCH)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# This is the entry point from the CLI (or anywhere else). Takes options hash and logger
|
|
29
|
+
# as arguments, sets up the cached master directory if required, and adjusts options hash
|
|
30
|
+
# accordingly. Returns nothing; raises exceptions for failures.
|
|
31
|
+
# @param options [Hash] Options hash from CLI
|
|
32
|
+
# @param logger [Logger] Logger object
|
|
33
|
+
def self.run(options, logger)
|
|
34
|
+
# If nobody asked for this, don't do anything
|
|
35
|
+
return if options[:cached_master_dir].nil?
|
|
36
|
+
|
|
37
|
+
# Verify that parameters are set up correctly and that at least one of the to-branch and
|
|
38
|
+
# from-branch is [master branch]. If not, it's not worthwhile to do any of the remaining
|
|
39
|
+
# tasks in this section.
|
|
40
|
+
return unless cached_master_applicable_to_this_run?(options)
|
|
41
|
+
|
|
42
|
+
# This directory was supposed to be created as part of the option setup. Make sure it exists
|
|
43
|
+
# as a sanity check.
|
|
44
|
+
Dir.mkdir options[:cached_master_dir], 0o755 unless Dir.exist?(options[:cached_master_dir])
|
|
45
|
+
|
|
46
|
+
# Determine if it's necessary to check out the git repo to the directory in question.
|
|
47
|
+
git_repo_checkout_bootstrap(options, logger) unless git_repo_checkout_current?(options, logger)
|
|
48
|
+
|
|
49
|
+
# Under --bootstrap-then-exit, don't adjust the options. (Otherwise code runs twice.)
|
|
50
|
+
return if options[:bootstrap_then_exit]
|
|
51
|
+
|
|
52
|
+
# Re-point any options to the cached directory.
|
|
53
|
+
%w(from to).each do |x|
|
|
54
|
+
next unless options["#{x}_env".to_sym] == master_branch(options)
|
|
55
|
+
logger.debug "Setting --bootstrapped-#{x}-dir=#{options[:cached_master_dir]}"
|
|
56
|
+
options["bootstrapped_#{x}_dir".to_sym] = options[:cached_master_dir]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# If a catalog was already compiled for the requested node, point to it directly to avoid
|
|
60
|
+
# re-compiling said catalog.
|
|
61
|
+
unless options[:node].nil?
|
|
62
|
+
catalog_path = File.join(options[:cached_master_dir], '.catalogs', options[:node] + '.json')
|
|
63
|
+
if File.file?(catalog_path)
|
|
64
|
+
%w(from to).each do |x|
|
|
65
|
+
next unless options["#{x}_env".to_sym] == master_branch(options)
|
|
66
|
+
next unless options["#{x}_catalog".to_sym].nil?
|
|
67
|
+
logger.debug "Setting --#{x}-catalog=#{catalog_path}"
|
|
68
|
+
options["#{x}_catalog".to_sym] = catalog_path
|
|
69
|
+
options["#{x}_catalog_compilation_dir".to_sym] = options[:cached_master_dir]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Determine if the cached master directory functionality is needed at all.
|
|
76
|
+
# @param options [Hash] Options hash from CLI
|
|
77
|
+
# @return [Boolean] whether to-branch and/or from-branch == [master branch]
|
|
78
|
+
def self.cached_master_applicable_to_this_run?(options)
|
|
79
|
+
return false if options[:cached_master_dir].nil?
|
|
80
|
+
target_branch = master_branch(options)
|
|
81
|
+
options.fetch(:from_env, '') == target_branch || options.fetch(:to_env, '') == target_branch
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Determine whether git repo checkout in the directory is current.
|
|
85
|
+
# To consider here: (a) is anything at all checked out; (b) is the correct SHA checked out?
|
|
86
|
+
# @param options [Hash] Options hash from CLI
|
|
87
|
+
# @param logger [Logger] Logger object
|
|
88
|
+
# @return [Boolean] whether git repo checkout in the directory is current
|
|
89
|
+
def self.git_repo_checkout_current?(options, logger)
|
|
90
|
+
shafile = File.join(options[:cached_master_dir], '.catalog-diff-master.sha')
|
|
91
|
+
return false unless File.file?(shafile)
|
|
92
|
+
bootstrapped_sha = File.read(shafile)
|
|
93
|
+
target_branch = master_branch(options)
|
|
94
|
+
branch_sha_opts = options.merge(branch: target_branch)
|
|
95
|
+
current_master_sha = OctocatalogDiff::CatalogUtil::Git.branch_sha(branch_sha_opts)
|
|
96
|
+
logger.debug "Cached master dir: bootstrapped=#{bootstrapped_sha}; current=#{current_master_sha}"
|
|
97
|
+
bootstrapped_sha.strip == current_master_sha.strip
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Check out [master branch] -> cached directory and bootstrap it
|
|
101
|
+
# @param options [Hash] Options hash from CLI
|
|
102
|
+
# @param logger [Logger] Logger object
|
|
103
|
+
def self.git_repo_checkout_bootstrap(options, logger)
|
|
104
|
+
# This directory isn't current so kill it
|
|
105
|
+
# Too dangerous if someone slips up on the command line:
|
|
106
|
+
# FileUtils.rm_rf options[:cached_master_dir] if Dir.exist?(options[:cached_master_dir])
|
|
107
|
+
shafile = File.join(options[:cached_master_dir], '.catalog-diff-master.sha')
|
|
108
|
+
target_branch = master_branch(options)
|
|
109
|
+
branch_sha_opts = options.merge(branch: target_branch)
|
|
110
|
+
current_master_sha = OctocatalogDiff::CatalogUtil::Git.branch_sha(branch_sha_opts)
|
|
111
|
+
|
|
112
|
+
if Dir.exist?(options[:cached_master_dir]) && File.exist?(shafile)
|
|
113
|
+
# If :cached_master_dir was set in a known-safe manner, safe_to_delete_cached_master_dir will
|
|
114
|
+
# allow the cleanup to take place automatically.
|
|
115
|
+
if options.fetch(:safe_to_delete_cached_master_dir, false) == options[:cached_master_dir]
|
|
116
|
+
FileUtils.rm_rf options[:cached_master_dir] if Dir.exist?(options[:cached_master_dir])
|
|
117
|
+
else
|
|
118
|
+
message = "To proceed, #{options[:cached_master_dir]} needs to be deleted, so it can be re-created."\
|
|
119
|
+
" I'm not yet deemed safe enough to do this for you though. Please jump out to a shell and run"\
|
|
120
|
+
" 'rm -rf #{options[:cached_master_dir]}' and then come back and try again. (Existing SHA:"\
|
|
121
|
+
" #{File.read(shafile).strip}; current master SHA: #{current_master_sha})"
|
|
122
|
+
raise Errno::EEXIST, message
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# This logic is similar to 'bootstrap-then-exit' (without the exit part). The
|
|
127
|
+
# bootstrap_then_exit handles creating this directory.
|
|
128
|
+
fake_options = options.dup
|
|
129
|
+
fake_options[:bootstrap_then_exit] = true
|
|
130
|
+
fake_options[:bootstrapped_from_dir] = options[:cached_master_dir]
|
|
131
|
+
fake_options[:bootstrapped_to_dir] = nil
|
|
132
|
+
fake_options[:from_env] = master_branch(options)
|
|
133
|
+
|
|
134
|
+
logger.debug 'Begin bootstrap cached master directory'
|
|
135
|
+
catalogs_obj = OctocatalogDiff::CatalogDiff::Cli::Catalogs.new(fake_options, logger)
|
|
136
|
+
catalogs_obj.bootstrap_then_exit
|
|
137
|
+
logger.debug 'Success bootstrap cached master directory'
|
|
138
|
+
|
|
139
|
+
# Write the SHA of [master branch], so git_repo_checkout_current? works next time
|
|
140
|
+
File.open(shafile, 'w') { |f| f.write(current_master_sha) }
|
|
141
|
+
logger.debug "Cached master directory bootstrapped to #{current_master_sha}"
|
|
142
|
+
|
|
143
|
+
# Create <dir>/.catalogs, to save any catalogs compiled along the way
|
|
144
|
+
catalog_dir = File.join(options[:cached_master_dir], '.catalogs')
|
|
145
|
+
Dir.mkdir catalog_dir unless File.directory?(catalog_dir)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Save a compiled catalog in the cached master directory. Does not die fatally if
|
|
149
|
+
# catalog is invalid or this isn't set up or whatever else.
|
|
150
|
+
# @param node [String] node name
|
|
151
|
+
# @param dir [String] cached master directory
|
|
152
|
+
# @param catalog [OctocatalogDiff::Catalog] Catalog object
|
|
153
|
+
# @return [Boolean] true if catalog was saved, false if not
|
|
154
|
+
def self.save_catalog_in_cache_dir(node, dir, catalog)
|
|
155
|
+
return false if dir.nil? || node.nil?
|
|
156
|
+
return false if catalog.nil? || !catalog.valid?
|
|
157
|
+
|
|
158
|
+
path = File.join(dir, '.catalogs')
|
|
159
|
+
return false unless Dir.exist?(path)
|
|
160
|
+
|
|
161
|
+
filepath = File.join(path, node + '.json')
|
|
162
|
+
return false if File.file?(filepath)
|
|
163
|
+
|
|
164
|
+
File.open(filepath, 'w') { |f| f.write(catalog.catalog_json) }
|
|
165
|
+
true
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|