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

(CODEMGMT-1415) Sundry Improvements #1211

Merged
merged 7 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
Unreleased
----------

- (CODEMGMT-1415) Provide a `--incremental` flag to only sync those modules in a Puppetfile whose definitions have changed since last sync, or those whose versions could change. [#1200](https://github.com/puppetlabs/r10k/pull/1200)
- (CODEMGMT-1454) Ensure missing repo caches are re-synced [#1210](https://github.com/puppetlabs/r10k/pull/1210)
- (PF-2437) Allow token authentication to be used with the Forge. [#1192](https://github.com/puppetlabs/r10k/pull/1192)

Expand Down
2 changes: 1 addition & 1 deletion doc/dynamic-environments/usage.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ explicit, static version. These are released Forge versions, or Git modules usin
the `:tag`, or `:commit` keys. Git `:ref`s containing only the full 40 character
commit SHA will also be treated as static versions. Then invoke a deploy with:

r10k deploy environment production --modules --assume-unchanged
r10k deploy environment production --modules --incremental

- - -

Expand Down
176 changes: 176 additions & 0 deletions integration/tests/basic_functionality/basic_deployment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
require 'git_utils'
require 'r10k_utils'
require 'master_manipulator'


test_name 'Basic Environment Deployment Workflows'

# This isn't a block because we want to use the local variables throughout the file
step 'init'
@env_path = on(master, puppet('config print environmentpath')).stdout.rstrip
r10k_fqp = get_r10k_fqp(master)

control_repo_gitdir = '/git_repos/environments.git'
control_repo_worktree = '/root/environments'
last_commit = git_last_commit(master, control_repo_worktree)
git_provider = ENV['GIT_PROVIDER']

config_path = get_r10k_config_file_path(master)
config_backup_path = "#{config_path}.bak"

puppetfile1 =<<-EOS
mod 'puppetlabs/apache', '0.10.0'
mod 'puppetlabs/stdlib', '8.0.0'
EOS

r10k_conf = <<-CONF
cachedir: '/var/cache/r10k'
git:
provider: '#{git_provider}'
sources:
control:
basedir: "#{@env_path}"
remote: "#{control_repo_gitdir}"
deploy:
purge_levels: ['deployment','environment','puppetfile']

CONF


def and_stdlib_is_correct
metadata_path = "#{@env_path}/production/modules/stdlib/metadata.json"
on(master, "test -f #{metadata_path}", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 0, 'stdlib content has been inappropriately purged')
end
metadata_info = JSON.parse(on(master, "cat #{metadata_path}").stdout)
assert(metadata_info['version'] == '8.0.0', 'stdlib deployed to wrong version')
end

teardown do
on(master, "mv #{config_backup_path} #{config_path}")
clean_up_r10k(master, last_commit, control_repo_worktree)
end

step 'Set up r10k and control repo' do

# Backup and replace r10k config
on(master, "mv #{config_path} #{config_backup_path}")
create_remote_file(master, config_path, r10k_conf)

# Place our Puppetfile in the control repo's production branch
git_on(master, 'checkout production', control_repo_worktree)
create_remote_file(master, "#{control_repo_worktree}/Puppetfile", puppetfile1)
git_add_commit_push(master, 'production', 'add Puppetfile for Basic Deployment test', control_repo_worktree)

# Ensure the production environment will be deployed anew
on(master, "rm -rf #{@env_path}/production")
end

test_path = "#{@env_path}/production/modules/apache/metadata.json"
step 'Test initial environment deploy works' do
on(master, "#{r10k_fqp} deploy environment production --verbose=info") do |result|
assert(result.output =~ /.*Deploying module to .*apache.*/, 'Did not log apache deployment')
assert(result.output =~ /.*Deploying module to .*stdlib.*/, 'Did not log stdlib deployment')
end
on(master, "test -f #{test_path}", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 0, 'Expected module in Puppetfile was not installed')
end

and_stdlib_is_correct
end

original_apache_info = JSON.parse(on(master, "cat #{test_path}").stdout)

step 'Test second run of deploy updates control repo, but leaves moduledir untouched' do
puppetfile2 =<<-EOS
# Current latest of apache is 6.5.1 as of writing this test
mod 'puppetlabs/apache', :latest
mwaggett marked this conversation as resolved.
Show resolved Hide resolved
mod 'puppetlabs/stdlib', '8.0.0'
mod 'puppetlabs/concat', '7.0.0'
EOS

git_on(master, 'checkout production', control_repo_worktree)
create_remote_file(master, "#{control_repo_worktree}/Puppetfile", puppetfile2)
git_add_commit_push(master, 'production', 'add Puppetfile for Basic Deployment test', control_repo_worktree)

on(master, "#{r10k_fqp} deploy environment production --verbose=info") do |result|
refute(result.output =~ /.*Deploying module to .*apache.*/, 'Inappropriately updated apache')
refute(result.output =~ /.*Deploying module to .*stdlib.*/, 'Inappropriately updated stdlib')
end

on(master, "test -f #{test_path}", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 0, 'Expected module content in Puppetfile was inappropriately purged')
end

new_apache_info = JSON.parse(on(master, "cat #{test_path}").stdout)
on(master, "cat #{@env_path}/production/Puppetfile | grep ':latest'", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 0, 'Puppetfile not updated on subsequent r10k deploys')
end

assert(original_apache_info['version'] == new_apache_info['version'] &&
new_apache_info['version'] == '0.10.0',
'Module content updated on subsequent r10k invocations w/o providing --modules')

on(master, "test -f #{@env_path}/production/modules/concat/metadata.json", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 1, 'Module content deployed on subsequent r10k invocation w/o providing --modules')
end

and_stdlib_is_correct
end

step 'Test --modules updates modules' do
on(master, "#{r10k_fqp} deploy environment production --modules --verbose=info") do |result|
assert(result.output =~ /.*Deploying module to .*apache.*/, 'Did not log apache deployment')
assert(result.output =~ /.*Deploying module to .*stdlib.*/, 'Did not log stdlib deployment')
assert(result.output =~ /.*Deploying module to .*concat.*/, 'Did not log concat deployment')
end

on(master, "test -f #{test_path}", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 0, 'Expected module content in Puppetfile was inappropriately purged')
end

on(master, "test -f #{@env_path}/production/modules/concat/metadata.json", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 0, 'New module content was not deployed when providing --modules')
end

new_apache_info = JSON.parse(on(master, "cat #{test_path}").stdout)
apache_major_version = new_apache_info['version'].split('.').first.to_i
assert(apache_major_version > 5, 'Module not updated correctly using --modules')

and_stdlib_is_correct
end

step 'Test --modules --incremental deploys changed & dynamic modules, but not unchanged, static modules' do
puppetfile3 =<<-EOS
# Current latest of apache is 6.5.1 as of writing this test
mod 'puppetlabs/apache', :latest
mod 'puppetlabs/stdlib', '8.0.0'
mod 'puppetlabs/concat', '7.1.0'
EOS

git_on(master, 'checkout production', control_repo_worktree)
create_remote_file(master, "#{control_repo_worktree}/Puppetfile", puppetfile3)
git_add_commit_push(master, 'production', 'add Puppetfile for Basic Deployment test', control_repo_worktree)

on(master, "#{r10k_fqp} deploy environment production --modules --incremental --verbose=debug1") do |result|
Copy link
Contributor

Choose a reason for hiding this comment

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

Without a new Puppetfile, this deploy isn't actually changing anything, is it? Is there a better way we can test this behavior other than checking the log messages? Just trying to think in terms of actual workflows, since I don't imagine someone would run a deploy with --modules and then again with --modules --incremental without at least making some sort of change in between?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I struggled with this. The end result of running --modules vs --modules --incremental should be the same, modules that should be updated will be updated. However, the how it happens is different, with some modules being skipped completely and some still being checked. So the logging shows us at least that the static version checking is correct (for a forge module) and that unchanged static modules are skipped.

If you think I should I could include all the permutations of different types of versions as well as those that have changed definitions? Or are you mainly concerned about those modules whose definition changed on disk part? I could include one of those in the Puppetfile.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll add a module that is updated in this invocation to test the changeable vs static, changed vs static, unchanged. Let me know if you'd like more permutations of static version checking (fwiw, there is some unit tests around that: 6f46384#diff-1bc365ddca4e6e4a7ba95ff4d655c036faa16ec1025e35bc559476265a5719aaR344-R389)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think what you added looks good! I'm satisfied with the various permutations we test here.

assert(result.output =~ /.*Deploying module to .*apache.*/, 'Did not log apache deployment')
assert(result.output =~ /.*Deploying module to .*concat.*/, 'Did not log concat deployment')
assert(result.output =~ /.*Not updating module stdlib, assuming content unchanged.*/, 'Did not log notice of skipping stdlib')
end

on(master, "test -f #{test_path}", accept_all_exit_codes: true) do |result|
assert(result.exit_code == 0, 'Expected module content in Puppetfile was inappropriately purged')
end

new_apache_info = JSON.parse(on(master, "cat #{test_path}").stdout)
apache_major_version = new_apache_info['version'].split('.').first.to_i
assert(apache_major_version > 5, 'Module not updated correctly using --modules & --incremental')

concat_info = JSON.parse(on(master, "cat #{@env_path}/production/modules/concat/metadata.json").stdout)
concat_minor_version = concat_info['version'].split('.')[1].to_i
assert(concat_minor_version == 1, 'Module not updated correctly using --modules & --incremental')

and_stdlib_is_correct
end


4 changes: 2 additions & 2 deletions lib/r10k/action/deploy/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def initialize(opts, argv, settings = {})
default_branch_override: @default_branch_override,
generate_types: @generate_types || settings.dig(:deploy, :generate_types) || false,
preload_environments: true,
assume_unchanged: @assume_unchanged
incremental: @incremental
},
modules: {
exclude_spec: settings.dig(:deploy, :exclude_spec),
Expand Down Expand Up @@ -239,7 +239,7 @@ def allowed_initialize_opts
super.merge(puppetfile: :modules,
modules: :self,
cachedir: :self,
'assume-unchanged': :self,
incremental: :self,
'no-force': :self,
'exclude-spec': :self,
'generate-types': :self,
Expand Down
2 changes: 1 addition & 1 deletion lib/r10k/cli/deploy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def self.command

flag :p, :puppetfile, 'Deploy modules (deprecated, use -m)'
flag :m, :modules, 'Deploy modules'
flag nil, :'assume-unchanged', 'Assume previously deployed modules are unchanged'
flag nil, :incremental, 'Used with the --modules flag, only update those modules whose definition has changed or whose definition allows the version to float'
option nil, :'default-branch-override', 'Specify a branchname to override the default branch in the puppetfile',
argument: :required

Expand Down
14 changes: 11 additions & 3 deletions lib/r10k/environment/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class R10K::Environment::Base
# @return [String] The puppetfile name (relative)
attr_reader :puppetfile_name

attr_reader :managed_directories, :purge_exclusions, :desired_contents
attr_reader :managed_directories, :desired_contents

attr_reader :loader

Expand Down Expand Up @@ -70,14 +70,14 @@ def initialize(name, basedir, dirname, options = {})

@loader = R10K::ModuleLoader::Puppetfile.new(**loader_options)

if @overrides.dig(:environments, :assume_unchanged)
if @overrides.dig(:environments, :incremental)
@loader.load_metadata
end

@base_modules = nil
@purge_exclusions = nil
@managed_directories = [ @full_path ]
@desired_contents = []
@purge_exclusions = []
end

# Synchronize the given environment.
Expand Down Expand Up @@ -204,6 +204,14 @@ def determine_purge_exclusions(pf_managed_dirs = @puppetfile.managed_directo
list.to_a
end

def purge_exclusions
if @purge_exclusions.nil?
load_puppetfile_modules
end

@purge_exclusions
end

def generate_types!
argv = [R10K::Settings.puppet_path, 'generate', 'types', '--environment', dirname, '--environmentpath', basedir, '--config', R10K::Settings.puppet_conf]
subproc = R10K::Util::Subprocess.new(argv)
Expand Down
6 changes: 3 additions & 3 deletions lib/r10k/module_loader/puppetfile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ def initialize(basedir:,
@managed_directories = []
@desired_contents = []
@purge_exclusions = []

logger.info _("Using Puppetfile '%{puppetfile}'") % {puppetfile: @puppetfile_path}
logger.debug _("Using moduledir '%{moduledir}'") % {moduledir: @moduledir}
end

def load
Expand All @@ -60,6 +57,9 @@ def load
end

def load!
logger.info _("Using Puppetfile '%{puppetfile}'") % {puppetfile: @puppetfile_path}
logger.debug _("Using moduledir '%{moduledir}'") % {moduledir: @moduledir}

dsl = R10K::ModuleLoader::Puppetfile::DSL.new(self)
dsl.instance_eval(puppetfile_content(@puppetfile_path), @puppetfile_path)

Expand Down
10 changes: 5 additions & 5 deletions spec/unit/action/deploy/environment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@
described_class.new({ :'exclude-spec' => true }, [], {})
end

it 'can accept an assume-unchanged option' do
described_class.new({ :'assume-unchanged' => true }, [], {})
it 'can accept an incremental option' do
described_class.new({ :incremental => true }, [], {})
end

describe "initializing errors" do
Expand Down Expand Up @@ -127,7 +127,7 @@
end
end

describe "with assume-unchanged flag" do
describe "with incremental flag" do
let(:loader) do
instance_double("R10K::ModuleLoader::Puppetfile",
:load => {
Expand All @@ -147,11 +147,11 @@
and_return(loader).at_least(:once)
end

it "assume unchanged flag causes the module definitons to be preloaded by the loader" do
it "incremental flag causes the module definitons to be preloaded by the loader" do
expect(loader).to receive(:load_metadata).exactly(4).times
action = described_class.new({:config => "/some/nonexistent/path",
:modules => true,
:'assume-unchanged' => true},
:incremental => true},
[],
{})
action.call
Expand Down