Skip to content

Commit 6bd73dd

Browse files
committed
Merge branch 'kpaulisse-parallel-tmpdir-cleans' into kpaulisse-mega-merge
2 parents 993943c + c444859 commit 6bd73dd

File tree

10 files changed

+195
-79
lines changed

10 files changed

+195
-79
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ The example above reflects the changes in the Puppet catalog from switching an u
5353
- [Requirements](/doc/requirements.md)
5454
- [Limitations](/doc/limitations.md)
5555
- [List of all command line options](/doc/optionsref.md)
56+
- [Environment variables](/doc/advanced-environment-variables.md)
5657

5758
### Project
5859

doc/advanced-environment-variables.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Environment variables
2+
3+
The following environment variables have special meaning to octocatalog-diff:
4+
5+
### `OCTOCATALOG_DIFF_CONFIG_FILE`
6+
7+
This environment variable is used to locate the configuration file for the CLI. The use of configuration files is described generally in:
8+
9+
- [Configuration](/doc/configuration.md)
10+
11+
### `OCTOCATALOG_DIFF_CUSTOM_VERSION`
12+
13+
When set, the `octocatalog-diff` CLI will display this as the version number within debugging, instead of the version number in the package. This is most useful if you want to use or include a git SHA in the version number.
14+
15+
```
16+
$ export OCTOCATALOG_DIFF_CUSTOM_VERSION="@$(git rev-parse HEAD)"
17+
$ octocatalog-diff -d ...
18+
D, [2017-10-12T08:57:46.454738 #35205] DEBUG -- : Running octocatalog-diff @504d7f3c91267e5193beb103caae5d4d8cebfee3 with ruby 2.3.1
19+
...
20+
```
21+
22+
### `OCTOCATALOG_DIFF_DEVELOPER_PATH`
23+
24+
When set, instead of loading libraries from the system or bundler location, libraries will be loaded from the specified value of this environment variable. This is used internally for development as we point users to unreleased code for debugging or testing.
25+
26+
```
27+
$ export OCTOCATALOG_DIFF_DEVELOPER_PATH=$HOME/git-checkouts/octocatalog-diff
28+
$ octocatalog-diff ...
29+
```
30+
31+
### `OCTOCATALOG_DIFF_TEMPDIR`
32+
33+
When set:
34+
35+
- `octocatalog-diff` will create all of its temporary directories within the specified directory.
36+
- `octocatalog-diff` will not attempt to remove any temporary directories it creates.
37+
38+
This is useful in the following situations:
39+
40+
- You are calling `octocatalog-diff` from within another program which is highly parallelized, and `at_exit` handlers are difficult to implement. Instead of figuring that all out (if it can even be figured out), you create a temporary directory before the parallelized logic, and remove it afterwards.
41+
42+
- You wish to debug intermediate output. For example, you may be instructed to set this variable and send some of the output to the project maintainers if you request assistance.
43+
44+
This variable is used internally for the parallelized logic for catalog compilation, but the value set from the environment will override any internal usage.
45+
46+
### `OCTOCATALOG_DIFF_VERSION`
47+
48+
This variable is used when building the gem, to override the default version. This is used for internal testing of `octocatalog-diff` before public releases. This variable is not useful outside the build context.
49+
50+
### `PUPPETDB_HOST`
51+
52+
This variable specifies the fully qualified domain name or IP address of the PuppetDB server.
53+
54+
Note: If `PUPPETDB_URL` is specified, then `PUPPETDB_HOST` is not consulted.
55+
56+
### `PUPPETDB_PORT`
57+
58+
This variable specifies the port number of the PuppetDB server.
59+
60+
Note: If `PUPPETDB_URL` is specified, then `PUPPETDB_PORT` is not consulted.
61+
62+
### `PUPPETDB_URL`
63+
64+
This variable specifies the URL to the PuppetDB server.
65+
66+
Example: `https://puppetdb.example.net:8081`
67+
68+
### `PUPPET_FACT_DIR`
69+
70+
This variable specifies the directory path where puppet fact files are stored. (Fact files must be named `<fqdn>.yaml` where `<fqdn>` is specified when running `octocatalog-diff`.)

lib/octocatalog-diff/catalog-util/builddir.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
require_relative '../facts'
66
require_relative 'enc'
7+
require_relative '../util/util'
78

89
module OctocatalogDiff
910
module CatalogUtil
@@ -38,9 +39,7 @@ class BuildDir
3839
# @param options [Hash] Options for class; see above description
3940
def initialize(options = {}, logger = nil)
4041
@options = options.dup
41-
@tempdir = Dir.mktmpdir('ocd-builddir-')
42-
at_exit { FileUtils.rm_rf(@tempdir) if File.directory?(@tempdir) }
43-
42+
@tempdir = OctocatalogDiff::Util::Util.temp_dir('ocd-builddir-')
4443
@factdir = nil
4544
@enc = nil
4645
@fact_file = nil

lib/octocatalog-diff/catalog/computed.rb

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
require_relative '../catalog-util/facts'
1212
require_relative '../util/puppetversion'
1313
require_relative '../util/scriptrunner'
14+
require_relative '../util/util'
1415

1516
module OctocatalogDiff
1617
class Catalog
@@ -69,23 +70,6 @@ def convert_file_resources(dry_run = false)
6970
OctocatalogDiff::CatalogUtil::FileResources.convert_file_resources(self, environment)
7071
end
7172

72-
private
73-
74-
# Private method: Clean up a checkout directory, if it exists
75-
def cleanup_checkout_dir(checkout_dir, logger)
76-
return unless File.directory?(checkout_dir)
77-
logger.debug("Cleaning up temporary directory #{checkout_dir}")
78-
# Sometimes this seems to break when handling the recursive removal when running under
79-
# a parallel environment. Trap and ignore the errors here if we don't care about them.
80-
begin
81-
FileUtils.remove_entry_secure checkout_dir
82-
# :nocov:
83-
rescue Errno::ENOTEMPTY, Errno::ENOENT => exc
84-
logger.debug "cleanup_checkout_dir(#{checkout_dir}) logged #{exc.class} - this can be ignored"
85-
# :nocov:
86-
end
87-
end
88-
8973
# Private method: Bootstrap a directory
9074
def bootstrap(logger)
9175
return if @builddir
@@ -99,9 +83,7 @@ def bootstrap(logger)
9983
tmphash[:basedir] = @options[:bootstrapped_dir]
10084
elsif @options[:branch] == '.'
10185
if @options[:bootstrap_current]
102-
tmphash[:basedir] = Dir.mktmpdir('ocd-bootstrap-basedir-')
103-
at_exit { cleanup_checkout_dir(tmphash[:basedir], logger) }
104-
86+
tmphash[:basedir] = OctocatalogDiff::Util::Util.temp_dir('ocd-bootstrap-basedir-')
10587
FileUtils.cp_r File.join(@options[:basedir], '.'), tmphash[:basedir]
10688

10789
o = @options.reject { |k, _v| k == :branch }.merge(path: tmphash[:basedir])
@@ -110,10 +92,8 @@ def bootstrap(logger)
11092
tmphash[:basedir] = @options[:basedir]
11193
end
11294
else
113-
checkout_dir = Dir.mktmpdir('ocd-bootstrap-checkout-')
114-
at_exit { cleanup_checkout_dir(checkout_dir, logger) }
115-
tmphash[:basedir] = checkout_dir
116-
OctocatalogDiff::CatalogUtil::Bootstrap.bootstrap_directory(@options.merge(path: checkout_dir), logger)
95+
tmphash[:basedir] = OctocatalogDiff::Util::Util.temp_dir('ocd-bootstrap-checkout-')
96+
OctocatalogDiff::CatalogUtil::Bootstrap.bootstrap_directory(@options.merge(path: tmphash[:basedir]), logger)
11797
end
11898

11999
# Create and populate the temporary directory

lib/octocatalog-diff/util/parallel.rb

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# this instead executes the tasks serially, but provides the same API as the parallel tasks.
77

88
require 'stringio'
9+
require_relative 'util'
910

1011
module OctocatalogDiff
1112
module Util
@@ -107,13 +108,14 @@ def self.run_tasks(task_array, logger = nil, parallelized = true, raise_exceptio
107108
# @return [Exception] First exception encountered by a child process; returns nil if no exceptions encountered.
108109
def self.run_tasks_parallel(result, task_array, logger)
109110
pidmap = {}
110-
ipc_tempdir = Dir.mktmpdir('ocd-ipc-')
111+
ipc_tempdir = OctocatalogDiff::Util::Util.temp_dir('ocd-ipc-')
111112

112113
# Child process forking
113114
task_array.each_with_index do |task, index|
114115
# simplecov doesn't see this because it's forked
115116
# :nocov:
116117
this_pid = fork do
118+
ENV['OCTOCATALOG_DIFF_TEMPDIR'] ||= ipc_tempdir
117119
task_result = execute_task(task, logger)
118120
File.open(File.join(ipc_tempdir, "#{Process.pid}.dat"), 'w') { |f| f.write Marshal.dump(task_result) }
119121
Kernel.exit! 0 # Kernel.exit! avoids at_exit from parents being triggered by children exiting
@@ -157,17 +159,6 @@ def self.run_tasks_parallel(result, task_array, logger)
157159
# If the process doesn't exist, that's fine.
158160
end
159161
end
160-
161-
retries = 0
162-
while File.directory?(ipc_tempdir) && retries < 10
163-
retries += 1
164-
begin
165-
FileUtils.remove_entry_secure ipc_tempdir
166-
rescue Errno::ENOTEMPTY, Errno::ENOENT # rubocop:disable Lint/HandleExceptions
167-
# Errno::ENOTEMPTY will trigger a retry because the directory exists
168-
# Errno::ENOENT will break the loop because the directory won't exist next time it's checked
169-
end
170-
end
171162
end
172163

173164
# Utility method! Not intended to be called from outside this class.

lib/octocatalog-diff/util/scriptrunner.rb

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
require 'open3'
77
require 'shellwords'
88

9+
require_relative 'util'
10+
911
module OctocatalogDiff
1012
module Util
1113
# This is a utility class to execute a built-in script.
@@ -91,14 +93,7 @@ def log(priority, message, logger = @logger)
9193
# @return [String] Path to tempfile containing script
9294
def temp_script(script)
9395
raise Errno::ENOENT, "Script '#{script}' not found" unless File.file?(script)
94-
temp_dir = Dir.mktmpdir('ocd-scriptrunner-')
95-
at_exit do
96-
begin
97-
FileUtils.remove_entry_secure temp_dir
98-
rescue Errno::ENOENT # rubocop:disable Lint/HandleExceptions
99-
# OK if the directory doesn't exist since we're trying to remove it anyway
100-
end
101-
end
96+
temp_dir = OctocatalogDiff::Util::Util.temp_dir('ocd-scriptrunner')
10297
temp_file = File.join(temp_dir, File.basename(script))
10398
File.open(temp_file, 'w') { |f| f.write(File.read(script)) }
10499
FileUtils.chmod 0o755, temp_file

lib/octocatalog-diff/util/util.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
# Handy methods that are not tied to one particular class
44

5+
require 'fileutils'
6+
57
module OctocatalogDiff
68
module Util
79
# Helper class to construct catalogs, performing all necessary steps such as
@@ -43,6 +45,51 @@ def self.deep_dup(object)
4345
safe_dup(object)
4446
end
4547
end
48+
49+
# Utility Method!
50+
# This creates a temporary directory. If the base directory is specified, then we
51+
# do not remove the temporary directory at exit, because we assume that something
52+
# else will remove the base directory.
53+
#
54+
# prefix - A String with the prefix for the temporary directory
55+
# basedir - A String with the directory in which to make the tempdir
56+
#
57+
# Returns the full path to the temporary directory.
58+
def self.temp_dir(prefix = 'ocd-', basedir = ENV['OCTOCATALOG_DIFF_TEMPDIR'])
59+
# If the base directory is specified, make sure it exists, and then create the
60+
# temporary directory within it.
61+
if basedir
62+
unless File.directory?(basedir)
63+
raise Errno::ENOENT, "temp_dir: Base dir #{basedir.inspect} does not exist!"
64+
end
65+
return Dir.mktmpdir(prefix, basedir)
66+
end
67+
68+
# If the base directory was not specified, then create a temporary directory, and
69+
# send the `at_exit` to clean it up at the conclusion.
70+
the_dir = Dir.mktmpdir(prefix)
71+
at_exit { remove_temp_dir(the_dir) }
72+
the_dir
73+
end
74+
75+
# Utility method!
76+
# Remove a directory recursively that has been used as a temporary directory. This
77+
# should be called within an `at_exit` handler, and is only intended to be called via the
78+
# `temp_dir` method above.
79+
#
80+
# dir - A String with the directory to remove.
81+
def self.remove_temp_dir(dir)
82+
retries = 0
83+
while File.directory?(dir) && retries < 10
84+
retries += 1
85+
begin
86+
FileUtils.remove_entry_secure(dir)
87+
rescue Errno::ENOTEMPTY, Errno::ENOENT # rubocop:disable Lint/HandleExceptions
88+
# Errno::ENOTEMPTY will trigger a retry because the directory exists
89+
# Errno::ENOENT will break the loop because the directory won't exist next time it's checked
90+
end
91+
end
92+
end
4693
end
4794
end
4895
end

spec/octocatalog-diff/tests/catalog/computed_spec.rb

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -292,30 +292,6 @@
292292
end
293293
end
294294

295-
describe '#cleanup_checkout_dir' do
296-
it 'should remove a directory if one exists' do
297-
opts = { node: 'foo', branch: 'bar', bootstrapped_dir: OctocatalogDiff::Spec.fixture_path('null') }
298-
obj = OctocatalogDiff::Catalog::Computed.new(opts)
299-
logger, _logger_str = OctocatalogDiff::Spec.setup_logger
300-
begin
301-
dir = Dir.mktmpdir
302-
expect(File.directory?(dir)).to eq(true)
303-
obj.send(:cleanup_checkout_dir, dir, logger)
304-
expect(File.directory?(dir)).to eq(false)
305-
ensure
306-
OctocatalogDiff::Spec.clean_up_tmpdir(dir)
307-
end
308-
end
309-
310-
it 'should not error if a directory does not exist' do
311-
opts = { node: 'foo', branch: 'bar', bootstrapped_dir: OctocatalogDiff::Spec.fixture_path('null') }
312-
obj = OctocatalogDiff::Catalog::Computed.new(opts)
313-
logger, _logger_str = OctocatalogDiff::Spec.setup_logger
314-
dir = OctocatalogDiff::Spec.fixture_path('null')
315-
expect { obj.send(:cleanup_checkout_dir, dir, logger) }.not_to raise_error
316-
end
317-
end
318-
319295
describe '#assert_that_puppet_environment_directory_exists' do
320296
before(:each) do
321297
allow(File).to receive(:"directory?").with('/tmp/assert/environments/yup').and_return(true)

spec/octocatalog-diff/tests/util/parallel_spec.rb

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
require 'logger'
66
require 'parallel'
77

8-
# rubocop:disable Style/GlobalVars
98
describe OctocatalogDiff::Util::Parallel do
109
before(:each) do
11-
$octocatalog_diff_util_parallel_spec_tempdir = Dir.mktmpdir
10+
ENV['OCTOCATALOG_DIFF_TEMPDIR'] = Dir.mktmpdir
1211
end
1312

1413
after(:each) do
15-
OctocatalogDiff::Spec.clean_up_tmpdir($octocatalog_diff_util_parallel_spec_tempdir)
14+
OctocatalogDiff::Spec.clean_up_tmpdir(ENV['OCTOCATALOG_DIFF_TEMPDIR'])
15+
ENV.delete('OCTOCATALOG_DIFF_TEMPDIR')
1616
end
1717

1818
context 'with parallel processing' do
@@ -50,13 +50,13 @@ def two(arg, _logger = nil)
5050
it 'should handle a task that fails after other successes' do
5151
class Foo
5252
def one(arg, _logger = nil)
53-
File.open(File.join($octocatalog_diff_util_parallel_spec_tempdir, 'one'), 'w') { |f| f.write '' }
53+
File.open(File.join(ENV['OCTOCATALOG_DIFF_TEMPDIR'], 'one'), 'w') { |f| f.write '' }
5454
'one ' + arg
5555
end
5656

5757
def two(_arg, _logger = nil)
5858
100.times do
59-
break if File.file?(File.join($octocatalog_diff_util_parallel_spec_tempdir, 'one'))
59+
break if File.file?(File.join(ENV['OCTOCATALOG_DIFF_TEMPDIR'], 'one'))
6060
sleep 0.1
6161
end
6262
# Sometimes the system will still handle the second process if it's near-simultaneous
@@ -90,7 +90,7 @@ def two(_arg, _logger = nil)
9090
class Foo
9191
def one(arg, _logger = nil)
9292
sleep 10
93-
File.open(File.join($octocatalog_diff_util_parallel_spec_tempdir, 'one'), 'w') { |f| f.write '' }
93+
File.open(File.join(ENV['OCTOCATALOG_DIFF_TEMPDIR'], 'one'), 'w') { |f| f.write '' }
9494
'one ' + arg
9595
end
9696

@@ -119,7 +119,7 @@ def two(_arg, _logger = nil)
119119
expect(two_result.exception).to be_a_kind_of(RuntimeError)
120120
expect(two_result.exception.message).to eq('Two failed')
121121

122-
expect(File.file?(File.join($octocatalog_diff_util_parallel_spec_tempdir, 'one'))).to eq(false)
122+
expect(File.file?(File.join(ENV['OCTOCATALOG_DIFF_TEMPDIR'], 'one'))).to eq(false)
123123
end
124124

125125
it 'should log debug messages' do
@@ -480,4 +480,3 @@ def validate(arg, _logger = nil, _extra_args = {})
480480
end
481481
end
482482
end
483-
# rubocop:enable Style/GlobalVars

0 commit comments

Comments
 (0)