beaker-pe 1.23.0 → 1.24.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MjlmYWQxODA1M2Q3NzI3ZTM0YWNkNzk0Y2E4MDI1YWJmMzVmNTc4ZA==
4
+ MDkyZjU4ZGQwODQwNWY4NGZlODZiMjRjY2NmOTlhOTNjMDc0ODM2Mg==
5
5
  data.tar.gz: !binary |-
6
- YWFiNmVkMTJiNDM1YTk1ODQ2MTk1ZDRlZWI3ZDJmYzM4MzQ3YjcxYg==
6
+ MDk4NzJiMGM3ZGYzYjdlZjlkZmFkZGM3MjY5ZjY1Y2VkOGE1MWY5MQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MTg4NTllZTMzNjBiMTZiZTM4YmUyYzQxNDllNDM4OTU4Yjg3ZDc0NzI0ODA0
10
- MTRjOTA3N2Y1ZDg2YWM1ZmU1NGRiYjY1ZDFmYjY2ZGVjZjZlYjMyMTRkNGEy
11
- MmZjNzkwNzcyYjIzYmI4ZjUxNjhmNGI1NmRjMjAxNmNkZGI2MTU=
9
+ NjhkMmEzMjE5NTZhMDgzZWM5NDA2MDA1ZTlmYzZjMGNlMzBmMmNjNTlkZjRj
10
+ ODE4YTEwYmViMDkwY2ZkM2FmMmNkNDQ3NTIzMGEyZmZkNWVhZDRhOWMxMDdl
11
+ NDRkMDRiMmIzNjE4NDNhNGVmMWMzODIwMTU5NGU2MmI5ZDZhZDQ=
12
12
  data.tar.gz: !binary |-
13
- YTJkOWVkYzc4ZjhhYWRkZmRlZWFkOTM2OTY1NmVjMjA1ZWRmYmUzODkzOWU2
14
- MThkYzYxOTJkMmM2MTc0NDk1NzE0ODhhMmNiNDkyNjVmN2EzZjZjNTJkMjFh
15
- ZmE0NzJiYmIwNWM4OTJkYzEwZGM0MmE1ZTE4ZTllOTc2N2Y0MTE=
13
+ MmRiMThhNWE3ZmJhNjExMDQ5NjEwNjhjNzAxOTg2ZWIwZTcxNGIwZTk0YjY4
14
+ ZWJkMGI0NTJlYzA5ZWI5NmNkYWQ0MzY1MDU3YmVmMzUxOTY3NjRkZWExMGYw
15
+ ODYxN2MyNWI5Y2E2ZTE4MzkzMDhlYjg5M2RiYjgyZDZjMTQwZWQ=
@@ -101,6 +101,30 @@ module Beaker
101
101
  end
102
102
  end
103
103
 
104
+ # Return agent nodes with 'lb_connect' role that are not loadbalancers
105
+ def loadbalancer_connecting_agents
106
+ lb_connect_nodes = select_hosts(roles: ['lb_connect'])
107
+ lb_connect_agents = lb_connect_nodes.reject { |h| h['roles'].include?('loadbalancer')}
108
+ end
109
+
110
+ # Returns true if loadbalncer exists and is configured with 'lb_connect' role
111
+ def lb_connect_loadbalancer_exists?
112
+ if (any_hosts_as?('loadbalancer'))
113
+ lb_node = select_hosts(roles: ['loadbalancer'])
114
+ lb_node.first['roles'].include?('lb_connect')
115
+ end
116
+ end
117
+
118
+ #Returns loadbalancer if host is an agent and loadbalancer has lb_connect role
119
+ #@param [Host] agent host with lb_connect role
120
+ def get_lb_downloadhost(host)
121
+ downloadhost = master
122
+ if( ! host['roles'].include?('loadbalancer') && lb_connect_loadbalancer_exists?)
123
+ downloadhost = loadbalancer
124
+ end
125
+ downloadhost
126
+ end
127
+
104
128
  # Generate the command line string needed to from a frictionless puppet-agent
105
129
  # install on this host in a PE environment.
106
130
  #
@@ -124,6 +148,14 @@ module Beaker
124
148
  end
125
149
  end
126
150
 
151
+ # If this is an agent node configured to connect to the loadbalancer
152
+ # using 'lb_connect' role, then use loadbalancer in the download url
153
+ # instead of master (PE-22051)
154
+ downloadhost = master
155
+ if( host['roles'].include?('lb_connect'))
156
+ downloadhost = get_lb_downloadhost(host)
157
+ end
158
+
127
159
  pe_debug = host[:pe_debug] || opts[:pe_debug] ? ' -x' : ''
128
160
  use_puppet_ca_cert = host[:use_puppet_ca_cert] || opts[:use_puppet_ca_cert]
129
161
 
@@ -135,7 +167,7 @@ module Beaker
135
167
  cert_validator = '[Net.ServicePointManager]::ServerCertificateValidationCallback = {\\$true}'
136
168
  end
137
169
 
138
- cmd = %Q{powershell -c "cd #{host['working_dir']};#{cert_validator};\\$webClient = New-Object System.Net.WebClient;\\$webClient.DownloadFile('https://#{master}:8140/packages/current/install.ps1', '#{host['working_dir']}/install.ps1');#{host['working_dir']}/install.ps1 -verbose #{frictionless_install_opts.join(' ')}"}
170
+ cmd = %Q{powershell -c "cd #{host['working_dir']};#{cert_validator};\\$webClient = New-Object System.Net.WebClient;\\$webClient.DownloadFile('https://#{downloadhost}:8140/packages/current/install.ps1', '#{host['working_dir']}/install.ps1');#{host['working_dir']}/install.ps1 -verbose #{frictionless_install_opts.join(' ')}"}
139
171
  else
140
172
  curl_opts = %w{--tlsv1 -O}
141
173
  if use_puppet_ca_cert
@@ -144,7 +176,7 @@ module Beaker
144
176
  curl_opts << '-k'
145
177
  end
146
178
 
147
- cmd = "FRICTIONLESS_TRACE='true'; export FRICTIONLESS_TRACE; cd #{host['working_dir']} && curl #{curl_opts.join(' ')} https://#{master}:8140/packages/current/install.bash && bash#{pe_debug} install.bash #{frictionless_install_opts.join(' ')}".strip
179
+ cmd = "FRICTIONLESS_TRACE='true'; export FRICTIONLESS_TRACE; cd #{host['working_dir']} && curl #{curl_opts.join(' ')} https://#{downloadhost}:8140/packages/current/install.bash && bash#{pe_debug} install.bash #{frictionless_install_opts.join(' ')}".strip
148
180
  end
149
181
 
150
182
  return cmd
@@ -376,33 +408,41 @@ module Beaker
376
408
  else
377
409
  _console_dispatcher = get_console_dispatcher_for_beaker_pe!
378
410
 
379
- # Check if we've already created a frictionless agent node group
380
- # to avoid errors creating the same node group when the beaker hosts file contains
381
- # multiple hosts with the same platform
382
- node_group = _console_dispatcher.get_node_group_by_name('Beaker Frictionless Agent')
383
- if node_group.nil? || node_group.empty?
384
- node_group = {}
385
- node_group['name'] = "Beaker Frictionless Agent"
386
- # Pin the master to the node
387
- node_group['rule'] = [ "and", [ '=', 'name', master.to_s ]]
388
- node_group['classes'] ||= {}
389
- end
411
+ # Add pe_repo packages to 'PE Master' group
412
+ # This change will alow us to test the standard workflow where we add pe_repo packages
413
+ # to PE Master group itself. This will also make it easy to download the packages to
414
+ # compilemasters(PE-22051)
415
+ # Previosuly, we were creating a new node group 'Beaker Frictionless Agent' and adding
416
+ # pe_repo packages to that group
417
+ #
418
+ node_group = _console_dispatcher.get_node_group_by_name('PE Master')
390
419
 
391
420
  # add the pe_repo platform class if it's not already present
392
- if ! node_group['classes'].include?(klass)
393
- node_group['classes'][klass] = {}
394
-
395
- _console_dispatcher.create_new_node_group_model(node_group)
396
- # The puppet agent run that will download the agent tarballs to the master can sometimes fail with
397
- # curl errors if there is a network hiccup. Use beakers `retry_on` method to retry up to
398
- # three times to avoid failing the entire test pipeline due to a network blip
399
- retry_opts = {
400
- :desired_exit_codes => [0,2],
401
- :max_retries => 3,
402
- # Beakers retry_on method wants the verbose value to be a string, not a bool.
403
- :verbose => 'true'
404
- }
405
- retry_on(master, puppet("agent -t"), retry_opts)
421
+ if (node_group)
422
+ if( ! node_group['classes'].include?(klass))
423
+ node_group['classes'][klass] = {}
424
+ _console_dispatcher.create_new_node_group_model(node_group)
425
+
426
+ # The puppet agent run that will download the agent tarballs to the master can sometimes fail with
427
+ # curl errors if there is a network hiccup. Use beakers `retry_on` method to retry up to
428
+ # three times to avoid failing the entire test pipeline due to a network blip
429
+ retry_opts = {
430
+ :desired_exit_codes => [0,2],
431
+ :max_retries => 3,
432
+ # Beakers retry_on method wants the verbose value to be a string, not a bool.
433
+ :verbose => 'true'
434
+ }
435
+ retry_on(master, puppet("agent -t"), retry_opts)
436
+
437
+ # Download the agent tarballs to compile masters
438
+ hosts.each do |h|
439
+ if h['roles'].include?('compile_master')
440
+ retry_on(h, puppet("agent -t"), retry_opts)
441
+ end
442
+ end
443
+ end
444
+ else
445
+ raise "Failed to add pe_repo packages, PE Master node group is not available"
406
446
  end
407
447
  end
408
448
  end
@@ -598,16 +638,24 @@ module Beaker
598
638
 
599
639
  if agent_only_check_needed && hosts_agent_only.include?(host) || install_via_msi?(host)
600
640
  host['type'] = 'aio'
601
- install_puppet_agent_pe_promoted_repo_on(host, {
641
+ install_params = {
602
642
  :puppet_agent_version => get_puppet_agent_version(host, opts),
603
643
  :puppet_agent_sha => host[:puppet_agent_sha] || opts[:puppet_agent_sha],
604
644
  :pe_ver => host[:pe_ver] || opts[:pe_ver],
605
- :puppet_collection => host[:puppet_collection] || opts[:puppet_collection]
606
- })
645
+ :puppet_collection => host[:puppet_collection] || opts[:puppet_collection],
646
+ :pe_promoted_builds_url => host[:pe_promoted_builds_url] || opts[:pe_promoted_builds_url]
647
+ }
648
+ install_params.delete(:pe_promoted_builds_url) if install_params[:pe_promoted_builds_url].nil?
649
+ install_puppet_agent_pe_promoted_repo_on(host, install_params)
607
650
  # 1 since no certificate found and waitforcert disabled
608
651
  acceptable_exit_codes = [0, 1]
609
652
  acceptable_exit_codes << 2 if opts[:type] == :upgrade
610
- setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes)
653
+ if masterless
654
+ configure_type_defaults_on(host)
655
+ on host, puppet_agent('-t'), :acceptable_exit_codes => acceptable_exit_codes
656
+ else
657
+ setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes)
658
+ end
611
659
  #Windows allows frictionless installs starting with PE Davis, if frictionless we need to skip this step
612
660
  elsif (host['platform'] =~ /windows/ && !(host['roles'].include?('frictionless')) || install_via_msi?(host))
613
661
  opts = { :debug => host[:pe_debug] || opts[:pe_debug] }
@@ -1541,6 +1589,34 @@ module Beaker
1541
1589
  end
1542
1590
  end
1543
1591
  end
1592
+
1593
+ # Method to install just the agent nodes
1594
+ # This method can be called only after installing PE on all other nodes
1595
+ # including infrastructure nodes, loadbalancer, hubs and spokes
1596
+ # PE-22051
1597
+ # @param [Array] agent only nodes from Beaker hosts
1598
+ # @param [Hash] opts The Beaker options hash
1599
+ #
1600
+ def install_agents_only_on(agentnodes, opts)
1601
+ if( ! agentnodes.empty?)
1602
+ configure_type_defaults_on(agentnodes)
1603
+ agentnodes.each do |host|
1604
+ if host['platform'] != master['platform']
1605
+ deploy_frictionless_to_master(host)
1606
+ end
1607
+ install_ca_cert_on(host, opts)
1608
+ on(host, installer_cmd(host, opts))
1609
+ end
1610
+ sign_certificate_for(agentnodes)
1611
+ stop_agent_on(agentnodes, :run_in_parallel => true)
1612
+ on agentnodes, puppet_agent('-t'), :acceptable_exit_codes => [0,2], :run_in_parallel => true
1613
+ agentnodes.select {|agent| agent['platform'] =~ /windows/}.each do |agent|
1614
+ client_datadir = agent.puppet['client_datadir']
1615
+ on(agent, puppet("resource file \"#{client_datadir}\" ensure=absent force=true"))
1616
+ end
1617
+ end
1618
+ end
1619
+
1544
1620
  end
1545
1621
  end
1546
1622
  end
@@ -3,7 +3,7 @@ module Beaker
3
3
  module PE
4
4
 
5
5
  module Version
6
- STRING = '1.23.0'
6
+ STRING = '1.24.0'
7
7
  end
8
8
 
9
9
  end
@@ -60,6 +60,17 @@ describe ClassMixedWithDSLInstallUtils do
60
60
  :working_dir => '/tmp',
61
61
  :dist => 'puppet-enterprise-3.7.1-rc0-78-gffc958f-eos-4-i386' } ) }
62
62
 
63
+ let(:lei_hosts) { make_hosts( { :pe_ver => '3.0',
64
+ :platform => 'linux',
65
+ :roles => [ 'agent' ],
66
+ :type => 'pe'}, 4 ) }
67
+ let(:lb_test_hosts) { lei_hosts[0][:roles] = ['master', 'database', 'dashboard']
68
+ lei_hosts[1][:roles] = ['loadbalancer', 'lb_connect']
69
+ lei_hosts[2][:roles] = ['compile_master']
70
+ lei_hosts[3][:roles] = ['frictionless', 'lb_connect']
71
+ lei_hosts[3][:working_dir] = '/tmp'
72
+ lei_hosts }
73
+
63
74
  context '#prep_host_for_upgrade' do
64
75
 
65
76
  it 'sets per host options before global options' do
@@ -188,6 +199,22 @@ describe ClassMixedWithDSLInstallUtils do
188
199
  end
189
200
  end
190
201
 
202
+ describe 'loadbalancer_connecting_agents' do
203
+ it 'no hosts are chosen if there are no agents with lb_connect role' do
204
+ allow( subject ).to receive(:hosts).and_return([])
205
+ end
206
+ it 'chooses agents with lb_connect role' do
207
+ allow( subject ).to receive(:lb_test_hosts).and_return([lb_test_hosts[3]])
208
+ end
209
+
210
+ end
211
+
212
+ describe 'get_lb_downloadhost' do
213
+ it 'choose lb_connect loadbalancer, if there is one' do
214
+ allow( subject ).to receive(:lb_test_hosts[3]).and_return([lb_test_hosts[1]])
215
+ end
216
+ end
217
+
191
218
  describe 'frictionless_agent_installer_cmd' do
192
219
  let(:host) do
193
220
  the_host = unixhost.dup
@@ -265,6 +292,18 @@ describe ClassMixedWithDSLInstallUtils do
265
292
  "\""
266
293
  expect( subject.frictionless_agent_installer_cmd( host, {}, '2016.4.0' ) ).to eq(expecting)
267
294
  end
295
+
296
+ it 'generates a frictionless install command with loadbalancer as download host' do
297
+ hosts = lb_test_hosts
298
+ expect( subject ).to receive( :get_lb_downloadhost ).with(lb_test_hosts[3]).and_return( 'testloadbalancer' )
299
+ expecting = [
300
+ "FRICTIONLESS_TRACE='true'",
301
+ "export FRICTIONLESS_TRACE",
302
+ "cd /tmp && curl --tlsv1 -O -k https://testloadbalancer:8140/packages/current/install.bash && bash install.bash"
303
+ ].join("; ")
304
+
305
+ expect( subject.frictionless_agent_installer_cmd( lb_test_hosts[3], {}, '2016.4.0' ) ).to eq(expecting)
306
+ end
268
307
  end
269
308
 
270
309
  describe 'install_ca_cert_on' do
@@ -1079,8 +1118,14 @@ describe ClassMixedWithDSLInstallUtils do
1079
1118
  describe '#deploy_frictionless_to_master' do
1080
1119
  let(:master) { make_host('master', :pe_ver => '2017.2', :platform => 'ubuntu-16.04-x86_64', :roles => ['master', 'database', 'dashboard']) }
1081
1120
  let(:agent) { make_host('agent', :pe_ver => '2017.2', :platform => 'el-7-x86_64', :roles => ['frictionless']) }
1121
+ let(:compile_master) { make_host('agent', :pe_ver => '2017.2', :roles => ['frictionless', 'compile_master']) }
1082
1122
  let(:dispatcher) { double('dispatcher') }
1083
- let(:node_group) { {} }
1123
+ let(:node_group) do
1124
+ {
1125
+ 'classes' => {
1126
+ },
1127
+ }
1128
+ end
1084
1129
 
1085
1130
  before :each do
1086
1131
  allow(subject).to receive(:retry_on)
@@ -1090,12 +1135,12 @@ describe ClassMixedWithDSLInstallUtils do
1090
1135
 
1091
1136
  allow(dispatcher).to receive(:get_node_group_by_name).and_return(node_group)
1092
1137
  allow(dispatcher).to receive(:create_new_node_group_model) {|model| node_group.update(model)}
1138
+ allow(subject).to receive(:compile_masters).and_return([compile_master])
1093
1139
  end
1094
1140
 
1095
- it 'adds the right pe_repo class to the Beaker Frictionless Agent group' do
1141
+ it 'adds the right pe_repo class to the PE Master group' do
1096
1142
  subject.deploy_frictionless_to_master(agent)
1097
1143
 
1098
- expect(node_group['rule']).to eq(['and', ['=', 'name', 'master']])
1099
1144
  expect(node_group['classes']).to include('pe_repo::platform::el_7_x86_64')
1100
1145
  end
1101
1146
 
@@ -1216,10 +1261,12 @@ describe ClassMixedWithDSLInstallUtils do
1216
1261
  hosts = make_hosts({
1217
1262
  :pe_ver => '4.0',
1218
1263
  :roles => ['agent'],
1219
- }, 3)
1264
+ }, 4)
1220
1265
  hosts[0][:roles] = ['master', 'database', 'dashboard']
1221
1266
  hosts[1][:platform] = 'windows'
1222
1267
  hosts[2][:platform] = Beaker::Platform.new('el-6-x86_64')
1268
+ hosts[2][:pe_promoted_builds_url] = nil
1269
+ hosts[3][:pe_promoted_builds_url] = 'test-url'
1223
1270
 
1224
1271
  allow( subject ).to receive( :hosts ).and_return( hosts )
1225
1272
  allow( subject ).to receive( :options ).and_return(Beaker::Options::Presets.new.presets)
@@ -1248,19 +1295,29 @@ describe ClassMixedWithDSLInstallUtils do
1248
1295
  expect( subject ).to receive( :install_puppet_agent_pe_promoted_repo_on ).with(
1249
1296
  hosts[1],
1250
1297
  {
1251
- :puppet_agent_version => pa_version,
1252
- :puppet_agent_sha => nil,
1253
- :pe_ver => hosts[1][:pe_ver],
1254
- :puppet_collection => nil
1298
+ :puppet_agent_version => pa_version,
1299
+ :puppet_agent_sha => nil,
1300
+ :pe_ver => hosts[1][:pe_ver],
1301
+ :puppet_collection => nil,
1255
1302
  }
1256
1303
  ).once
1257
1304
  expect( subject ).to receive( :install_puppet_agent_pe_promoted_repo_on ).with(
1258
1305
  hosts[2],
1259
1306
  {
1260
- :puppet_agent_version => pa_version,
1261
- :puppet_agent_sha => nil,
1262
- :pe_ver => hosts[2][:pe_ver],
1263
- :puppet_collection => nil
1307
+ :puppet_agent_version => pa_version,
1308
+ :puppet_agent_sha => nil,
1309
+ :pe_ver => hosts[2][:pe_ver],
1310
+ :puppet_collection => nil,
1311
+ }
1312
+ ).once
1313
+ expect( subject ).to receive( :install_puppet_agent_pe_promoted_repo_on ).with(
1314
+ hosts[3],
1315
+ {
1316
+ :puppet_agent_version => pa_version,
1317
+ :puppet_agent_sha => nil,
1318
+ :pe_ver => hosts[3][:pe_ver],
1319
+ :puppet_collection => nil,
1320
+ :pe_promoted_builds_url => 'test-url'
1264
1321
  }
1265
1322
  ).once
1266
1323
  hosts.each do |host|
@@ -1325,7 +1382,7 @@ describe ClassMixedWithDSLInstallUtils do
1325
1382
  :puppet_agent_version => pa_version,
1326
1383
  :puppet_agent_sha => nil,
1327
1384
  :pe_ver => hosts[1][:pe_ver],
1328
- :puppet_collection => nil
1385
+ :puppet_collection => nil,
1329
1386
  }
1330
1387
  ).once
1331
1388
  expect( subject ).to receive( :on ).with( hosts[2], /puppet-enterprise-installer/ ).once
@@ -1392,10 +1449,10 @@ describe ClassMixedWithDSLInstallUtils do
1392
1449
  allow( subject ).to receive(
1393
1450
  :install_puppet_agent_pe_promoted_repo_on
1394
1451
  ).with( hosts[1], {
1395
- :puppet_agent_version => pa_version,
1396
- :puppet_agent_sha => nil,
1397
- :pe_ver => hosts[1][:pe_ver],
1398
- :puppet_collection => nil
1452
+ :puppet_agent_version => pa_version,
1453
+ :puppet_agent_sha => nil,
1454
+ :pe_ver => hosts[1][:pe_ver],
1455
+ :puppet_collection => nil,
1399
1456
  } )
1400
1457
  # expect( subject ).to receive( :on ).with( hosts[2], /puppet-enterprise-installer/ ).once
1401
1458
  hosts.each do |host|
@@ -1446,6 +1503,22 @@ describe ClassMixedWithDSLInstallUtils do
1446
1503
  allow(subject).to receive(:sign_certificate_for)
1447
1504
  end
1448
1505
 
1506
+ describe 'install_agents_only_on' do
1507
+ let(:lb_agent) { make_host('agent', :pe_ver => '2016.4', :platform => 'debian-7-x86_64', :roles => ['frictionless', 'lb_connect']) }
1508
+
1509
+ before :each do
1510
+ allow(subject).to receive(:on)
1511
+ allow(subject).to receive(:configure_type_defaults_on)
1512
+ allow(subject).to receive(:deploy_frictionless_to_master)
1513
+ allow(subject).to receive(:install_agents_only_on)
1514
+
1515
+ allow(subject).to receive(:installer_cmd).with(lb_agent, anything()).and_return("install lb agent")
1516
+
1517
+ allow(subject).to receive(:stop_agent_on)
1518
+ allow(subject).to receive(:sign_certificate_for)
1519
+ end
1520
+ end
1521
+
1449
1522
  describe 'configuring frictionless installer' do
1450
1523
  it "skips the master's platform" do
1451
1524
  expect(subject).not_to receive(:deploy_frictionless_to_master)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beaker-pe
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.23.0
4
+ version: 1.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppetlabs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-15 00:00:00.000000000 Z
11
+ date: 2017-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -227,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
227
  version: '0'
228
228
  requirements: []
229
229
  rubyforge_project:
230
- rubygems_version: 2.4.6
230
+ rubygems_version: 2.4.8
231
231
  signing_key:
232
232
  specification_version: 4
233
233
  summary: Beaker PE DSL Helpers!