Class: OctocatalogDiff::CatalogUtil::CachedMasterDirectory

Inherits:
Object
  • Object
show all
Defined in:
lib/octocatalog-diff/catalog-util/cached_master_directory.rb

Overview

Handle the bootstrapped and cached checkout of [master branch]. This is an optimization targeted at local development environments, since a frequent pattern is “run a catalog-diff between what I have here, and master.”

Please note that there could be a race condition here if this code was run in parallel (i.e., the cached master directory is blown away and re-created when a Puppet catalog compile is in progress). Do not introduce this code to an environment where catalog-diff may be running in parallel unless you have accounted for this (or are willing to tolerate any errors).

Constant Summary collapse

DEFAULT_MASTER_BRANCH =

Set default branch. Can be overridden by options.

'origin/master'.freeze

Class Method Summary collapse

Class Method Details

.cached_master_applicable_to_this_run?(options) ⇒ Boolean

Determine if the cached master directory functionality is needed at all.

Parameters:

  • options (Hash)

    Options hash from CLI

Returns:

  • (Boolean)

    whether to-branch and/or from-branch == [master branch]


80
81
82
83
84
# File 'lib/octocatalog-diff/catalog-util/cached_master_directory.rb', line 80

def self.cached_master_applicable_to_this_run?(options)
  return false if options[:cached_master_dir].nil?
  target_branch = master_branch(options)
  options.fetch(:from_env, '') == target_branch || options.fetch(:to_env, '') == target_branch
end

.git_repo_checkout_bootstrap(options, logger) ⇒ Object

Check out [master branch] -> cached directory and bootstrap it

Parameters:

  • options (Hash)

    Options hash from CLI

  • logger (Logger)

    Logger object


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/octocatalog-diff/catalog-util/cached_master_directory.rb', line 105

def self.git_repo_checkout_bootstrap(options, logger)
  # This directory isn't current so kill it
  # Too dangerous if someone slips up on the command line:
  # FileUtils.rm_rf options[:cached_master_dir] if Dir.exist?(options[:cached_master_dir])
  shafile = File.join(options[:cached_master_dir], '.catalog-diff-master.sha')
  target_branch = master_branch(options)
  branch_sha_opts = options.merge(branch: target_branch)
  current_master_sha = OctocatalogDiff::CatalogUtil::Git.branch_sha(branch_sha_opts)

  if Dir.exist?(options[:cached_master_dir]) && File.exist?(shafile)
    # If :cached_master_dir was set in a known-safe manner, safe_to_delete_cached_master_dir will
    # allow the cleanup to take place automatically.
    if options.fetch(:safe_to_delete_cached_master_dir, false) == options[:cached_master_dir]
      FileUtils.rm_rf options[:cached_master_dir] if Dir.exist?(options[:cached_master_dir])
    else
      message = "To proceed, #{options[:cached_master_dir]} needs to be deleted, so it can be re-created."\
                " I'm not yet deemed safe enough to do this for you though. Please jump out to a shell and run"\
                " 'rm -rf #{options[:cached_master_dir]}' and then come back and try again. (Existing SHA:"\
                " #{File.read(shafile).strip}; current master SHA: #{current_master_sha})"
      raise Errno::EEXIST, message
    end
  end

  # This logic is similar to 'bootstrap-then-exit' (without the exit part). The
  # bootstrap_then_exit handles creating this directory.
  fake_options = options.dup
  fake_options[:bootstrap_then_exit] = true
  fake_options[:bootstrapped_from_dir] = options[:cached_master_dir]
  fake_options[:bootstrapped_to_dir] = nil
  fake_options[:from_env] = master_branch(options)

  logger.debug 'Begin bootstrap cached master directory'
  catalogs_obj = OctocatalogDiff::Util::Catalogs.new(fake_options, logger)
  catalogs_obj.bootstrap_then_exit
  logger.debug 'Success bootstrap cached master directory'

  # Write the SHA of [master branch], so git_repo_checkout_current? works next time
  File.open(shafile, 'w') { |f| f.write(current_master_sha) }
  logger.debug "Cached master directory bootstrapped to #{current_master_sha}"

  # Create <dir>/.catalogs, to save any catalogs compiled along the way
  catalog_dir = File.join(options[:cached_master_dir], '.catalogs')
  Dir.mkdir catalog_dir unless File.directory?(catalog_dir)
end

.git_repo_checkout_current?(options, logger) ⇒ Boolean

Determine whether git repo checkout in the directory is current. To consider here: (a) is anything at all checked out; (b) is the correct SHA checked out?

Parameters:

  • options (Hash)

    Options hash from CLI

  • logger (Logger)

    Logger object

Returns:

  • (Boolean)

    whether git repo checkout in the directory is current


91
92
93
94
95
96
97
98
99
100
# File 'lib/octocatalog-diff/catalog-util/cached_master_directory.rb', line 91

def self.git_repo_checkout_current?(options, logger)
  shafile = File.join(options[:cached_master_dir], '.catalog-diff-master.sha')
  return false unless File.file?(shafile)
  bootstrapped_sha = File.read(shafile)
  target_branch = master_branch(options)
  branch_sha_opts = options.merge(branch: target_branch)
  current_master_sha = OctocatalogDiff::CatalogUtil::Git.branch_sha(branch_sha_opts)
  logger.debug "Cached master dir: bootstrapped=#{bootstrapped_sha}; current=#{current_master_sha}"
  bootstrapped_sha.strip == current_master_sha.strip
end

.master_branch(options = {}) ⇒ String

Get the master branch based on supplied options.

Parameters:

  • options (Hash) (defaults to: {})

    Options hash

Returns:

  • (String)

    Master branch configured (defaults to DEFAULT_MASTER_BRANCH)


26
27
28
# File 'lib/octocatalog-diff/catalog-util/cached_master_directory.rb', line 26

def self.master_branch(options = {})
  options.fetch(:master_cache_branch, DEFAULT_MASTER_BRANCH)
end

.run(options, logger) ⇒ Object

This is the entry point from the CLI (or anywhere else). Takes options hash and logger as arguments, sets up the cached master directory if required, and adjusts options hash accordingly. Returns nothing; raises exceptions for failures.

Parameters:

  • options (Hash)

    Options hash from CLI

  • logger (Logger)

    Logger object


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/octocatalog-diff/catalog-util/cached_master_directory.rb', line 35

def self.run(options, logger)
  # If nobody asked for this, don't do anything
  return if options[:cached_master_dir].nil?

  # Verify that parameters are set up correctly and that at least one of the to-branch and
  # from-branch is [master branch]. If not, it's not worthwhile to do any of the remaining
  # tasks in this section.
  return unless cached_master_applicable_to_this_run?(options)

  # This directory was supposed to be created as part of the option setup. Make sure it exists
  # as a sanity check.
  Dir.mkdir options[:cached_master_dir], 0o755 unless Dir.exist?(options[:cached_master_dir])

  # Determine if it's necessary to check out the git repo to the directory in question.
  git_repo_checkout_bootstrap(options, logger) unless git_repo_checkout_current?(options, logger)

  # Under --bootstrap-then-exit, don't adjust the options. (Otherwise code runs twice.)
  return if options[:bootstrap_then_exit]

  # Re-point any options to the cached directory.
  %w(from to).each do |x|
    next unless options["#{x}_env".to_sym] == master_branch(options)
    logger.debug "Setting --bootstrapped-#{x}-dir=#{options[:cached_master_dir]}"
    options["bootstrapped_#{x}_dir".to_sym] = options[:cached_master_dir]
  end

  # If a catalog was already compiled for the requested node, point to it directly to avoid
  # re-compiling said catalog.
  unless options[:node].nil?
    catalog_path = File.join(options[:cached_master_dir], '.catalogs', options[:node] + '.json')
    if File.file?(catalog_path)
      %w(from to).each do |x|
        next unless options["#{x}_env".to_sym] == master_branch(options)
        next unless options["#{x}_catalog".to_sym].nil?
        logger.debug "Setting --#{x}-catalog=#{catalog_path}"
        options["#{x}_catalog".to_sym] = catalog_path
        options["#{x}_catalog_compilation_dir".to_sym] = options[:cached_master_dir]
      end
    end
  end
end

.save_catalog_in_cache_dir(node, dir, catalog) ⇒ Boolean

Save a compiled catalog in the cached master directory. Does not die fatally if catalog is invalid or this isn't set up or whatever else.

Parameters:

Returns:

  • (Boolean)

    true if catalog was saved, false if not


156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/octocatalog-diff/catalog-util/cached_master_directory.rb', line 156

def self.save_catalog_in_cache_dir(node, dir, catalog)
  return false if dir.nil? || node.nil?
  return false if catalog.nil? || !catalog.valid?

  path = File.join(dir, '.catalogs')
  return false unless Dir.exist?(path)

  filepath = File.join(path, node + '.json')
  return false if File.file?(filepath)

  File.open(filepath, 'w') { |f| f.write(catalog.catalog_json) }
  true
end