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

(PDK-508) implement autorequire and friends #29

Merged
merged 3 commits into from
Mar 13, 2018
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
7 changes: 7 additions & 0 deletions .dependency_decisions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,10 @@
:why: https://github.com/masover/blankslate/blob/f0a73b80c34bc3ad94dc3195d12a227a8fe30019/MIT-LICENSE
:versions: []
:when: 2017-11-17 14:05:09.534340925 Z
- - :license
- puppetlabs_spec_helper
- Apache 2.0
- :who: DavidS
:why:
:versions: []
:when: 2018-03-09 18:04:29.175843919 Z
8 changes: 8 additions & 0 deletions .fixtures.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file can be used to install module depdencies for unit testing
# See https://github.com/puppetlabs/puppetlabs_spec_helper#using-fixtures for details
---
fixtures:
forge_modules:
# stdlib: "puppetlabs/stdlib"
symlinks:
test_module: "#{source_dir}/spec/fixtures/test_module"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@

# rspec failure tracking
.rspec_status

# puppetlabs_spec_helper automatic fixtures
spec/fixtures/modules/test_module
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ group :tests do
gem 'rubocop-rspec'
gem 'rubocop'
gem 'simplecov-console'
# the test gems required for module testing
gem 'puppetlabs_spec_helper', github: 'DavidS/puppetlabs_spec_helper', ref: 'refactor'
gem 'rspec-puppet'
end

group :development do
Expand All @@ -39,5 +42,5 @@ end
if ENV['PUPPET_GEM_VERSION']
gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION'])
else
gem 'puppet', git: 'https://github.com/DavidS/puppet', ref: 'device-apply'
gem 'puppet', github: 'DavidS/puppet', ref: 'device-apply'
end
5 changes: 4 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'bundler/gem_tasks'
require 'puppetlabs_spec_helper/tasks/fixtures'

task :default => :spec

Expand All @@ -13,7 +14,9 @@ end
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec) do |t|
excludes = ['fixtures/**/*.rb']
Rake::Task[:spec_prep].invoke
# thanks to the fixtures/modules/ symlinks this needs to exclude fixture modules explicitely
excludes = ['fixtures/**/*.rb,fixtures/modules/*/**/*.rb']
if RUBY_PLATFORM == 'java'
excludes += ['acceptance/**/*.rb', 'integration/**/*.rb', 'puppet/resource_api/*_context_spec.rb', 'puppet/util/network_device/simple/device_spec.rb']
t.rspec_opts = '--tag ~agent_test'
Expand Down
18 changes: 18 additions & 0 deletions lib/puppet/resource_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,24 @@ def my_provider
def context
self.class.context
end

[:autorequire, :autobefore, :autosubscribe, :autonotify].each do |auto|
next unless definition[auto]

definition[auto].each do |type, values|
Puppet.debug("Registering #{auto} for #{type}: #{values.inspect}")
send(auto, type.downcase.to_sym) do
[values].flatten.map do |v|
match = %r{\A\$(.*)\Z}.match(v) if v.is_a? String
if match.nil?
v
else
self[match[1].to_sym]
end
end
end
end
end
end
end
module_function :register_type
Expand Down
14 changes: 14 additions & 0 deletions spec/classes/autorequire_cycle_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'spec_helper'

RSpec.describe 'test_module::autorequire_cycle' do
context 'with make_cycle => false' do
let(:params) { { make_cycle: false } }

it { is_expected.to compile }
end
context 'with make_cycle => true' do
let(:params) { { make_cycle: true } }

it { is_expected.not_to compile }
end
end
Empty file added spec/fixtures/manifests/site.pp
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'puppet/resource_api'
require 'puppet/resource_api/simple_provider'

# Implementation for the test_autorequire type using the Resource API.
class Puppet::Provider::TestAutorequire::TestAutorequire < Puppet::ResourceApi::SimpleProvider
def get(_context)
[
{
name: 'foo',
ensure: :present,
},
{
name: 'bar',
ensure: :present,
},
]
end

def create(context, name, should)
context.notice("Creating '#{name}' with #{should.inspect}")
end

def update(context, name, should)
context.notice("Updating '#{name}' with #{should.inspect}")
end

def delete(context, name)
context.notice("Deleting '#{name}'")
end
end
29 changes: 29 additions & 0 deletions spec/fixtures/test_module/lib/puppet/type/test_autorequire.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'puppet/resource_api'

Puppet::ResourceApi.register_type(
name: 'test_autorequire',
docs: <<-EOS,
This type provides Puppet with the capabilities to manage ...
EOS
features: [],
attributes: {
ensure: {
type: 'Enum[present, absent]',
desc: 'Whether this resource should be present or absent on the target system.',
default: 'present',
},
name: {
type: 'String',
desc: 'The name of the resource you want to manage.',
behaviour: :namevar,
},
target: {
type: 'String',
desc: 'The resource to autorequire.',
behaviour: :parameter,
},
},
autorequire: {
test_autorequire: '$target',
},
)
23 changes: 23 additions & 0 deletions spec/fixtures/test_module/manifests/autorequire_cycle.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# test_module::autorequire_cycle
#
# This class is used to test autorequires.
# With make_cycle set to false, this should compile without errors or cycles. When make_cycle is set to true, autorequires will be used to
# construct a dependency cycle. This makes it possible to test exactly the function of the autorequires implementation.
#
# @summary This class is used to test autorequires.
#
# @example
# include test_module::autorequire_cycle
class test_module::autorequire_cycle (
Boolean $make_cycle
) {
test_autorequire { "a":
target => "b",
}
test_autorequire { "b":
target => "c",
}
test_autorequire { "c":
target => $make_cycle ? { true => "a", false => undef },
}
}
14 changes: 14 additions & 0 deletions spec/fixtures/test_module/spec/classes/autorequire_cycle_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'spec_helper'

RSpec.describe 'test_module::autorequire_cycle' do
context 'with make_cycle => false' do
let(:params) { { make_cycle: false } }

it { is_expected.to compile }
end
context 'with make_cycle => true' do
let(:params) { { make_cycle: true } }

it { is_expected.not_to compile }
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'spec_helper'

# TODO: needs some cleanup/helper to avoid this misery
module Puppet::Provider::TestAutorequire; end
require 'puppet/provider/test_autorequire/test_autorequire'

RSpec.describe Puppet::Provider::TestAutorequire::TestAutorequire do
subject(:provider) { described_class.new }

let(:context) { instance_double('Puppet::ResourceApi::BaseContext', 'context') }

describe '#get' do
it 'processes resources' do
expect(provider.get(context)).to eq [
{
name: 'foo',
ensure: :present,
},
{
name: 'bar',
ensure: :present,
},
]
end
end

describe 'create(context, name, should)' do
it 'creates the resource' do
expect(context).to receive(:notice).with(%r{\ACreating 'a'})

provider.create(context, 'a', name: 'a', ensure: 'present')
end
end

describe 'update(context, name, should)' do
it 'updates the resource' do
expect(context).to receive(:notice).with(%r{\AUpdating 'foo'})

provider.update(context, 'foo', name: 'foo', ensure: 'present')
end
end

describe 'delete(context, name, should)' do
it 'deletes the resource' do
expect(context).to receive(:notice).with(%r{\ADeleting 'foo'})

provider.delete(context, 'foo')
end
end
end
51 changes: 51 additions & 0 deletions spec/puppet/resource_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@
desc: 'a optional string value',
},
},
autorequire: {
var: '$test_string',
},
autobefore: {
const: 'value',
},
autosubscribe: {
list: %w[foo bar],
},
autonotify: {
mixed: [10, '$test_integer'],
},
}
end

Expand All @@ -101,6 +113,45 @@
it { is_expected.not_to be_nil }
it { expect(type.properties.first.doc).to match %r{the description} }
it { expect(type.properties.first.name).to eq :test_string }

def extract_values(function)
result = []
type.send(function) do |_type, values|
# rely on the fact that the resource api is doing `self[]` internally to find the value
# see https://github.com/puppetlabs/puppet/blob/9f2c143962803a72c68f35be3462944e851bcdce/lib/puppet/type.rb#L2143
# for details
result += { test_string: 'foo', test_integer: 100 }.instance_eval(&values)
end
result
end

describe 'autorequire' do
it('yields the block for `var`') { expect { |b| type.eachautorequire(&b) }.to yield_with_args(:var, be_a(Proc)) }
it 'the yielded block returns the `test_string` value' do
expect(extract_values(:eachautorequire)).to eq ['foo']
end
end

describe 'autobefore' do
it('yields the block for `const`') { expect { |b| type.eachautobefore(&b) }.to yield_with_args(:const, be_a(Proc)) }
it('the yielded block returns the constant "value"') do
expect(extract_values(:eachautobefore)).to eq ['value']
end
end

describe 'autosubscribe' do
it('yields the block for `list`') { expect { |b| type.eachautosubscribe(&b) }.to yield_with_args(:list, be_a(Proc)) }
it('the yielded block returns the multiple values') do
expect(extract_values(:eachautosubscribe)).to eq %w[foo bar]
end
end

describe 'autonotify' do
it('yields the block for `mixed`') { expect { |b| type.eachautonotify(&b) }.to yield_with_args(:mixed, be_a(Proc)) }
it('the yielded block returns multiple integer values') do
expect(extract_values(:eachautonotify)).to eq [10, 100]
end
end
end

describe 'an instance of this type' do
Expand Down
4 changes: 4 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

require 'bundler/setup'
require 'puppet/resource_api'
require 'puppetlabs_spec_helper/module_spec_helper'

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
Expand All @@ -42,4 +43,7 @@
config.expect_with :rspec do |c|
c.syntax = :expect
end

# override legacy default from puppetlabs_spec_helper
config.mock_with :rspec
end