Last active
January 17, 2022 08:03
-
-
Save gucki/6b81030910fb5fca034559991b840a9f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'bolt/error' | |
# Quick and dirty implementation of a file sync (instead of simple file upload) using rsync under the hood for puppet bolt. | |
# Use it the same was as file_upload, ex. sync_file("profiles/lb01/", "/etc/keepalived/", ["lb01-1", "lb01-2"]). | |
# Most of the code is just copied from the original upload_file method and adjusted as needed. | |
# This function does nothing if the list of targets is empty. | |
# | |
# > **Note:** Not available in apply block | |
Puppet::Functions.create_function(:sync_file, Puppet::Functions::InternalFunction) do | |
# Upload a file or directory. | |
# @param source A source path, either an absolute path or a modulename/filename selector for a | |
# file or directory in $MODULEROOT/files. | |
# @param destination An absolute path on the target(s). | |
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns. | |
# @param options A hash of additional options. | |
# @option options [Boolean] _catch_errors Whether to catch raised errors. | |
# @option options [String] _run_as User to run as using privilege escalation. | |
# @return A list of results, one entry per target. | |
# @example Upload a local file to Linux targets and change owner to 'root' | |
# sync_file('/var/tmp/payload.tgz', '/tmp/payload.tgz', $targets, '_run_as' => 'root') | |
# @example Upload a module file to a Windows target | |
# sync_file('postgres/default.conf', 'C:/ProgramData/postgres/default.conf', $target) | |
dispatch :sync_file do | |
scope_param | |
param 'String[1]', :source | |
param 'String[1]', :destination | |
param 'Boltlib::TargetSpec', :targets | |
optional_param 'Hash[String[1], Any]', :options | |
end | |
# Upload a file or directory, logging the provided description. | |
# @param source A source path, either an absolute path or a modulename/filename selector for a | |
# file or directory in $MODULEROOT/files. | |
# @param destination An absolute path on the target(s). | |
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns. | |
# @param description A description to be output when calling this function. | |
# @param options A hash of additional options. | |
# @option options [Boolean] _catch_errors Whether to catch raised errors. | |
# @option options [String] _run_as User to run as using privilege escalation. | |
# @return A list of results, one entry per target. | |
# @example Upload a file | |
# sync_file('/var/tmp/payload.tgz', '/tmp/payload.tgz', $targets, 'Uploading payload to unpack') | |
dispatch :sync_file_with_description do | |
scope_param | |
param 'String[1]', :source | |
param 'String[1]', :destination | |
param 'Boltlib::TargetSpec', :targets | |
param 'String', :description | |
optional_param 'Hash[String[1], Any]', :options | |
end | |
def sync_file(scope, source, destination, targets, options = {}) | |
sync_file_with_description(scope, source, destination, targets, nil, options) | |
end | |
def sync_file_with_description(scope, source, destination, targets, description = nil, options = {}) | |
unless Puppet[:tasks] | |
raise Puppet::ParseErrorWithIssue | |
.from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'sync_file') | |
end | |
options = options.select { |opt| opt.start_with?('_') }.transform_keys { |k| k.sub(/^_/, '').to_sym } | |
options[:description] = description if description | |
executor = Puppet.lookup(:bolt_executor) | |
inventory = Puppet.lookup(:bolt_inventory) | |
# Send Analytics Report | |
executor.report_function_call(self.class.name) | |
# Find the file path if it exists, otherwise return nil | |
found = Bolt::Util.find_file_from_scope(source, scope) | |
unless found && Puppet::FileSystem.exist?(found) | |
raise Puppet::ParseErrorWithIssue.from_issue_and_stack( | |
Puppet::Pops::Issues::NO_SUCH_FILE_OR_DIRECTORY, file: source | |
) | |
end | |
executor.report_file_source(self.class.name, source) | |
# Ensure that that given targets are all Target instances | |
targets = inventory.get_targets(targets) | |
if targets.empty? | |
call_function('debug', "Simulating file upload of '#{found}' - no targets given - no action taken") | |
Bolt::ResultSet.new([]) | |
else | |
file_line = Puppet::Pops::PuppetStack.top_of_stack | |
success = sync_file_real(targets, found, destination, options, file_line) | |
if !success && !options[:catch_errors] | |
raise Bolt::RunFailure.new(r, 'sync_file', source) | |
end | |
end | |
end | |
def sync_file_real(targets, source, destination, options = {}, position = []) | |
executor = Puppet.lookup(:bolt_executor) | |
description = options.fetch(:description, "sync from #{source} to #{destination}") | |
executor.log_action(description, targets) do | |
options[:run_as] = executor.run_as if executor.run_as && !options.key?(:run_as) | |
targets.each do |target| | |
return false unless system("rsync -av --delete-during #{source} #{target.host}:#{destination}") | |
end | |
end | |
true | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment