Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(maint) Do not use visitors to synchronize modules #1161

Merged
merged 11 commits into from
May 10, 2021
7 changes: 7 additions & 0 deletions lib/r10k/action/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ class Base

attr_accessor :settings

# @param opts [Hash] A hash of options defined in #allowed_initialized_opts
# and managed by the SetOps mixin within the Action::Base class.
# Corresponds to the CLI flags and options.
# @param argv [CRI::ArgumentList] A list-like collection of the remaining
# arguments to the CLI invocation (after removing flags and options).
# @param settings [Hash] A hash of configuration loaded from the relevant
# config (r10k.yaml).
def initialize(opts, argv, settings)
@opts = opts
@argv = argv
Expand Down
7 changes: 7 additions & 0 deletions lib/r10k/action/deploy/display.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ class Display < R10K::Action::Base

include R10K::Action::Deploy::DeployHelpers

# @param opts [Hash] A hash of options defined in #allowed_initialized_opts
# and managed by the SetOps mixin within the Action::Base class.
# Corresponds to the CLI flags and options.
# @param argv [CRI::ArgumentList] A list-like collection of the remaining
# arguments to the CLI invocation (after removing flags and options).
# @param settings [Hash] A hash of configuration loaded from the relevant
# config (r10k.yaml).
def initialize(opts, argv, settings)
super

Expand Down
26 changes: 17 additions & 9 deletions lib/r10k/action/deploy/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ class Environment < R10K::Action::Base

attr_reader :settings

# @param opts [Hash] A hash of options defined in #allowed_initialized_opts
# and managed by the SetOps mixin within the Action::Base class.
# Corresponds to the CLI flags and options.
# @param argv [CRI::ArgumentList] A list-like collection of the remaining
# arguments to the CLI invocation (after removing flags and options).
# @param settings [Hash] A hash of configuration loaded from the relevant
# config (r10k.yaml).
def initialize(opts, argv, settings)
super

Expand Down Expand Up @@ -51,11 +58,17 @@ def initialize(opts, argv, settings)
def call
@visit_ok = true

expect_config!
deployment = R10K::Deployment.new(@settings)
check_write_lock!(@settings)
begin
expect_config!
deployment = R10K::Deployment.new(@settings)
check_write_lock!(@settings)

deployment.accept(self)
rescue => e
@visit_ok = false
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
end

deployment.accept(self)
@visit_ok
end

Expand Down Expand Up @@ -182,11 +195,6 @@ def visit_puppetfile(puppetfile)
end
end

def visit_module(mod)
logger.info _("Deploying %{origin} content %{path}") % {origin: mod.origin, path: mod.path}
mod.sync(force: @settings.dig(:overrides, :modules, :force))
end

def write_environment_info!(environment, started_at, success)
module_deploys =
begin
Expand Down
47 changes: 26 additions & 21 deletions lib/r10k/action/deploy/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ class Module < R10K::Action::Base

attr_reader :settings

# @param opts [Hash] A hash of options defined in #allowed_initialized_opts
# and managed by the SetOps mixin within the Action::Base class.
# Corresponds to the CLI flags and options.
# @param argv [CRI::ArgumentList] A list-like collection of the remaining
# arguments to the CLI invocation (after removing flags and options).
# @param settings [Hash] A hash of configuration loaded from the relevant
# config (r10k.yaml).
def initialize(opts, argv, settings)

super

requested_env = @opts[:environment] ? [@opts[:environment].gsub(/\W/, '_')] : []
Expand All @@ -28,7 +34,7 @@ def initialize(opts, argv, settings)
generate_types: @generate_types
},
modules: {
requested_modules: @argv,
requested_modules: @argv.map.to_a,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug in that was exposed with further functional testing. @argv here acts like an array but is actually a CRI::ArgumentList that includes the Enumerable module. This causes problems with logging that calls inspect on the value. This change was accidentally rolled into another refactor, but I can break it out if that's best.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to add a docs comment to this method indicating the type as it comes in?

# force here is used to make it easier to reason about
force: !@no_force
},
Expand All @@ -40,12 +46,17 @@ def initialize(opts, argv, settings)

def call
@visit_ok = true
begin
expect_config!
deployment = R10K::Deployment.new(@settings)
check_write_lock!(@settings)

deployment.accept(self)
rescue => e
@visit_ok = false
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
end

expect_config!
deployment = R10K::Deployment.new(@settings)
check_write_lock!(@settings)

deployment.accept(self)
@visit_ok
end

Expand All @@ -67,7 +78,15 @@ def visit_environment(environment)
logger.debug1(_("Only updating modules in environment(s) %{opt_env} skipping environment %{env_path}") % {opt_env: requested_envs.inspect, env_path: environment.path})
else
logger.debug1(_("Updating modules %{modules} in environment %{env_path}") % {modules: @settings.dig(:overrides, :modules, :requested_modules).inspect, env_path: environment.path})

yield

requested_mods = @settings.dig(:overrides, :modules, :requested_modules) || []
generate_types = @settings.dig(:overrides, :environments, :generate_types)
if generate_types && !((environment.modules.map(&:name) & requested_mods).empty?)
logger.debug("Generating puppet types for environment '#{environment.dirname}'...")
environment.generate_types!
end
end
end

Expand All @@ -76,20 +95,6 @@ def visit_puppetfile(puppetfile)
yield
end

def visit_module(mod)
requested_mods = @settings.dig(:overrides, :modules, :requested_modules)
if requested_mods.include?(mod.name)
logger.info _("Deploying module %{mod_path}") % {mod_path: mod.path}
mod.sync(force: @settings.dig(:overrides, :modules, :force))
if mod.environment && @settings.dig(:overrides, :environments, :generate_types)
logger.debug("Generating puppet types for environment '#{mod.environment.dirname}'...")
mod.environment.generate_types!
end
else
logger.debug1(_("Only updating modules %{modules}, skipping module %{mod_name}") % {modules: requested_mods.inspect, mod_name: mod.name})
end
end

def allowed_initialize_opts
super.merge(environment: true,
cachedir: :self,
Expand Down
27 changes: 11 additions & 16 deletions lib/r10k/action/puppetfile/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ class Install < R10K::Action::Base

def call
@visit_ok = true
pf = R10K::Puppetfile.new(@root,
{moduledir: @moduledir,
puppetfile_path: @puppetfile,
force: @force})
pf.accept(self)
begin
pf = R10K::Puppetfile.new(@root,
{moduledir: @moduledir,
puppetfile_path: @puppetfile,
force: @force || false})
pf.accept(self)
rescue => e
@visit_ok = false
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
end

@visit_ok
end

Expand All @@ -33,17 +39,6 @@ def visit_puppetfile(pf)
pf.purge_exclusions).purge!
end

def visit_module(mod)
@force ||= false
logger.info _("Updating module %{mod_path}") % {mod_path: mod.path}

if mod.respond_to?(:desired_ref) && mod.desired_ref == :control_branch
logger.warn _("Cannot track control repo branch for content '%{name}' when not part of a 'deploy' action, will use default if available." % {name: mod.name})
end

mod.sync(force: @force)
end

def allowed_initialize_opts
super.merge(root: :self, puppetfile: :self, moduledir: :self, force: :self )
end
Expand Down
8 changes: 4 additions & 4 deletions lib/r10k/content_synchronizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ module ContentSynchronizer
def self.serial_accept(modules, visitor, loader)
visitor.visit(:puppetfile, loader) do
modules.each do |mod|
mod.accept(visitor)
mod.sync
end
end
end

def self.concurrent_accept(modules, visitor, loader, pool_size, logger)
logger.debug _("Updating modules with %{pool_size} threads") % {pool_size: pool_size}
mods_queue = modules_queue(modules, visitor, loader)
thread_pool = pool_size.times.map { visitor_thread(visitor, mods_queue, logger) }
thread_pool = pool_size.times.map { sync_thread(mods_queue, logger) }
thread_exception = nil

# If any threads raise an exception the deployment is considered a failure.
Expand Down Expand Up @@ -42,11 +42,11 @@ def self.modules_queue(modules, visitor, loader)
end
end

def self.visitor_thread(visitor, mods_queue, logger)
def self.sync_thread(mods_queue, logger)
Thread.new do
begin
while mods = mods_queue.pop(true) do
mods.each {|mod| mod.accept(visitor) }
mods.each { |mod| mod.sync }
end
rescue ThreadError => e
logger.debug _("Module thread %{id} exiting: %{message}") % {message: e.message, id: Thread.current.object_id}
Expand Down
6 changes: 3 additions & 3 deletions lib/r10k/environment/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ def initialize(name, basedir, dirname, options = {})
@dirname = dirname
@options = options
@puppetfile_name = options.delete(:puppetfile_name)
@overrides = options.delete(:overrides)
@overrides = options.delete(:overrides) || {}

@full_path = File.join(@basedir, @dirname)
@path = Pathname.new(File.join(@basedir, @dirname))

@puppetfile = R10K::Puppetfile.new(@full_path,
{overrides: @options[:overrides],
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug that was uncaught earlier, but exposed with the additional testing that comes from attempting to actually use this data at the Module level. It accidentally got rolled into a refactor commit but I'm happy to break it out if that would be better.

force: @options.dig(:overrides, :modules, :force),
{overrides: @overrides,
force: @overrides.dig(:modules, :force),
puppetfile_name: @puppetfile_name})
@puppetfile.environment = self
end
Expand Down
28 changes: 15 additions & 13 deletions lib/r10k/environment/with_modules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def module_conflicts?(mod_b)
def accept(visitor)
visitor.visit(:environment, self) do
@modules.each do |mod|
mod.accept(visitor)
mod.sync
end

puppetfile.accept(visitor)
Expand All @@ -87,25 +87,27 @@ def accept(visitor)

def load_modules(module_hash)
module_hash.each do |name, args|
if !args.is_a?(Hash)
args = { version: args }
end

add_module(name, args)
end
end

# @param [String] name
# @param [*Object] args
# @param [Hash] args
def add_module(name, args)
if args.is_a?(Hash)
# symbolize keys in the args hash
args = args.inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo }
args[:overrides] = @overrides

if install_path = args.delete(:install_path)
install_path = resolve_install_path(install_path)
validate_install_path(install_path, name)
end
end
# symbolize keys in the args hash
args = args.inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo }
args[:overrides] = @overrides

install_path ||= @moduledir
if install_path = args.delete(:install_path)
install_path = resolve_install_path(install_path)
validate_install_path(install_path, name)
else
install_path = @moduledir
end

# Keep track of all the content this environment is managing to enable purging.
@managed_content[install_path] = Array.new unless @managed_content.has_key?(install_path)
Expand Down
2 changes: 1 addition & 1 deletion lib/r10k/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def self.register(klass)
#
# @param [String] name The unique name of the module
# @param [String] basedir The root to install the module in
# @param [Object] args An arbitary value or set of values that specifies the implementation
# @param [Hash] args An arbitary Hash that specifies the implementation
# @param [R10K::Environment] environment Optional environment that this module is a part of
#
# @return [Object < R10K::Module] A member of the implementing subclass
Expand Down
18 changes: 17 additions & 1 deletion lib/r10k/module/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ def initialize(title, dirname, args, environment=nil)
@owner, @name = parse_title(@title)
@path = Pathname.new(File.join(@dirname, @name))
@environment = environment
@overrides = args.delete(:overrides) || {}
@origin = 'external' # Expect Puppetfile or R10k::Environment to set this to a specific value

@requested_modules = @overrides.dig(:modules, :requested_modules) || []
@should_sync = (@requested_modules.empty? || @requested_modules.include?(@name))
end

# @deprecated
Expand All @@ -61,11 +65,22 @@ def full_path
end

# Synchronize this module with the indicated state.
# @abstract
# @param [Hash] opts Deprecated
def sync(opts={})
raise NotImplementedError
end

def should_sync?
if @should_sync
logger.info _("Deploying module to %{path}") % {path: path}
true
else
logger.debug1(_("Only updating modules %{modules}, skipping module %{name}") % {modules: @requested_modules.inspect, name: name})
false
end
end


# Return the desired version of this module
# @abstract
def version
Expand All @@ -87,6 +102,7 @@ def status
raise NotImplementedError
end

# Deprecated
def accept(visitor)
visitor.visit(:module, self)
end
Expand Down
Loading