Skip to content

Commit 3da6357

Browse files
author
Kevin Reeuwijk
authored
v0.6.0 - patch_group and scheduling improvements (#21)
* support multiple schedules and week values * update docs and changelog * bump version
1 parent 484fe96 commit 3da6357

File tree

7 files changed

+123
-65
lines changed

7 files changed

+123
-65
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## Release 0.6.0
6+
7+
**Features**
8+
- Adds support for providing an array of values to the `patch_group` attribute of the `patching_as_code` class
9+
- Adds support for providing an array of values to the `count_of_week` parameter in a patch schedule
10+
511
## Release 0.5.0
612

713
**Features**

README.md

+18-4
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,14 @@ This will change the behavior to also declare the `pe_patch` class, and match it
6565

6666
## Usage
6767

68-
To control which patch group a node belongs to, you need to set the `patch_group` parameter of the class.
68+
To control which patch group(s) a node belongs to, you need to set the `patch_group` parameter of the class.
6969
It is highly recommended to use Hiera to set the correct value for each node, for example:
7070
```
7171
patching_as_code::patch_group: early
7272
```
73-
The module provides 5 patch groups out of the box:
73+
The module provides 6 patch groups out of the box:
7474
```
75+
weekly: patches each Thursday of the month, between 09:00 and 11:00, performs a reboot if needed
7576
testing: patches every 2nd Thursday of the month, between 07:00 and 09:00, performs a reboot if needed
7677
early: patches every 3rd Monday of the month, between 20:00 and 22:00, performs a reboot if needed
7778
primary: patches every 3rd Friday of the month, between 22:00 and 00:00, performs a reboot if needed
@@ -84,6 +85,19 @@ always: patches immediately when a patch is available, can patch in any agent
8485
never: never performs any patching and does not reboot
8586
```
8687

88+
If you want to assign a node to multiple patch groups, specify an array of values in Hiera:
89+
```
90+
patching_as_code::patch_group:
91+
- testing
92+
- early
93+
```
94+
or, in flow style:
95+
```
96+
patching_as_code::patch_group: [ testing, early ]
97+
```
98+
99+
Note: if you assign a node to multiple patch groups, the value for the patch group provided to `pe_patch`/`os_patching` will be a space-separated list of the assigned patch groups. This is because `pe_patch`/`os_patching` do not natively support multiple patch groups, so we work around this by converting our list a single string that `pe_patch`/`os_patching` can work with. This is purely for cosmetic purposes and does not affect the functionality of either solution.
100+
87101
### Customizing the patch groups
88102

89103
You can customize the patch groups to whatever you need. To do so, simply copy the `patching_as_code::patch_schedule` hash from the `data/common.yaml` in this module, and paste it into your own Hiera store (recommended to place it in your Hiera's own `common.yaml`). This Hiera value will now override the defaults that the module provides. Customize the hash to your needs.
@@ -101,7 +115,7 @@ patching_as_code::patch_schedule:
101115
For example, say you want to have the following 2 patch groups:
102116
```
103117
group1: patches every 2nd Sunday of the month, between 10:00 and 11:00, max 1 time, reboots if needed
104-
group2: patches every 3nd Monday of the month, between 20:00 and 22:00, max 3 times, does not reboot
118+
group2: patches every 3nd and 4th Monday of the month, between 20:00 and 22:00, max 3 times, does not reboot
105119
```
106120
then define the hash as follows:
107121
```
@@ -114,7 +128,7 @@ patching_as_code::patch_schedule:
114128
reboot: ifneeded
115129
group2:
116130
day_of_week: Monday
117-
count_of_week: 3
131+
count_of_week: [3,4]
118132
hours: 20:00 - 22:00
119133
max_runs: 3
120134
reboot: never

REFERENCE.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ The following parameters are available in the `patching_as_code` class.
4343

4444
##### `patch_group`
4545

46-
Data type: `String`
46+
Data type: `Variant[String,Array[String]]`
4747

48-
Name of the patch_group for this node. Must match one of the patch groups in $patch_schedule
48+
Name(s) of the patch_group(s) for this node. Must match one or more of the patch groups in $patch_schedule
49+
To assign multiple patch groups, provide this parameter as an array
4950

5051
##### `patch_schedule`
5152

@@ -56,7 +57,7 @@ Hash of available patch_schedules. Default schedules are in /data/common.yaml of
5657
Options:
5758

5859
* **:day_of_week** `String`: Day of the week to patch, valid options: 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
59-
* **:count_of_week** `Integer`: Which week in the month to patch, use a number between 1 and 4
60+
* **:count_of_week** `Variant[Integer,Array[Integer]]`: Which week(s) in the month to patch, use number(s) between 1 and 5
6061
* **:hours** `String`: Which hours on patch day to patch, define a range as 'HH:MM - HH:MM'
6162
* **:max_runs** `String`: How many Puppet runs during the patch window can Puppet install patches. Must be at least 1.
6263
* **:reboot** `String`: Reboot behavior, valid options: 'always', 'never', 'ifneeded'

data/common.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
---
22
patching_as_code::patch_group: primary
33
patching_as_code::patch_schedule:
4+
weekly:
5+
day_of_week: Thursday
6+
count_of_week: [1,2,3,4,5]
7+
hours: 09:00 - 11:00
8+
max_runs: 4
9+
reboot: ifneeded
410
testing:
511
day_of_week: Thursday
612
count_of_week: 2

functions/is_patchday.pp

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
function patching_as_code::is_patchday(
22
Enum['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'] $day_of_week,
3-
Integer $week_iteration
3+
Variant[Integer, Array] $week_iteration
44
){
55
$day_number = $day_of_week ? {
66
'Monday' => 1,
@@ -25,11 +25,21 @@ function patching_as_code::is_patchday(
2525
$firstocc = 1 + $day_number - $som_weekday
2626
}
2727

28-
# Calculate date of patch day
29-
$patchday = $firstocc + (( $week_iteration - 1 ) * 7 )
28+
# Calculate dates of valid patch days
29+
case type($week_iteration, 'generalized') {
30+
Type[Integer]: {
31+
$patchdays = Array($firstocc + (( $week_iteration - 1 ) * 7 ), true)
32+
}
33+
Type[Array[Integer]]: {
34+
$patchdays = $week_iteration.map |$week| { $firstocc + (( $week - 1 ) * 7 ) }
35+
}
36+
default: {
37+
fail('The count_of_week parameter of the patch_schedule must be configured as an integer or an array of integers!')
38+
}
39+
}
3040

31-
# Return true if today is patch day
32-
if $patchday == $dayofmonth {
41+
# Return true if today is a patch day
42+
if $dayofmonth in $patchdays {
3343
true
3444
} else {
3545
false

manifests/init.pp

+73-52
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
# use_pe_patch => false
1515
# }
1616
#
17-
# @param [String] patch_group
18-
# Name of the patch_group for this node. Must match one of the patch groups in $patch_schedule
17+
# @param Variant[String,Array[String]] patch_group
18+
# Name(s) of the patch_group(s) for this node. Must match one or more of the patch groups in $patch_schedule
19+
# To assign multiple patch groups, provide this parameter as an array
1920
# @param [Hash] patch_schedule
2021
# Hash of available patch_schedules. Default schedules are in /data/common.yaml of this module
2122
# @option patch_schedule [String] :day_of_week
2223
# Day of the week to patch, valid options: 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
23-
# @option patch_schedule [Integer] :count_of_week
24-
# Which week in the month to patch, use a number between 1 and 4
24+
# @option patch_schedule [Variant[Integer,Array[Integer]]] :count_of_week
25+
# Which week(s) in the month to patch, use number(s) between 1 and 5
2526
# @option patch_schedule [String] :hours
2627
# Which hours on patch day to patch, define a range as 'HH:MM - HH:MM'
2728
# @option patch_schedule [String] :max_runs
@@ -74,25 +75,30 @@
7475
# When disabled (default), patches are not installed over a metered link.
7576
#
7677
class patching_as_code(
77-
String $patch_group,
78-
Hash $patch_schedule,
79-
Array $blocklist,
80-
Array $allowlist,
81-
Array $unsafe_process_list,
82-
Hash $pre_patch_commands,
83-
Hash $post_patch_commands,
84-
Hash $pre_reboot_commands,
85-
Optional[String] $plan_patch_fact = undef,
86-
Optional[Boolean] $enable_patching = true,
87-
Optional[Boolean] $security_only = false,
88-
Optional[Boolean] $use_pe_patch = true,
89-
Optional[Boolean] $classify_pe_patch = false,
90-
Optional[Boolean] $patch_on_metered_links = false,
78+
Variant[String,Array[String]] $patch_group,
79+
Hash $patch_schedule,
80+
Array $blocklist,
81+
Array $allowlist,
82+
Array $unsafe_process_list,
83+
Hash $pre_patch_commands,
84+
Hash $post_patch_commands,
85+
Hash $pre_reboot_commands,
86+
Optional[String] $plan_patch_fact = undef,
87+
Optional[Boolean] $enable_patching = true,
88+
Optional[Boolean] $security_only = false,
89+
Optional[Boolean] $use_pe_patch = true,
90+
Optional[Boolean] $classify_pe_patch = false,
91+
Optional[Boolean] $patch_on_metered_links = false,
9192
) {
92-
# Verify the $patch_group value points to a valid patch schedule
93-
unless $patch_schedule[$patch_group] or $patch_group in ['always', 'never'] {
94-
fail("Patch group ${patch_group} is not valid as no associated schedule was found!
95-
Ensure the patching_as_code::patch_schedule parameter contains a schedule for this patch group.")
93+
# Ensure we work with a $patch_groups array for further processing
94+
$patch_groups = Array($patch_group, true)
95+
96+
# Verify if all of $patch_groups point to a valid patch schedule
97+
$patch_groups.each |$pg| {
98+
unless $patch_schedule[$pg] or $pg in ['always', 'never'] {
99+
fail("Patch group ${pg} is not valid as no associated schedule was found!
100+
Ensure the patching_as_code::patch_schedule parameter contains a schedule for this patch group.")
101+
}
96102
}
97103

98104
# Verify the puppet_confdir from the puppetlabs/puppet_agent module is present
@@ -123,28 +129,28 @@
123129
if $classify_pe_patch {
124130
# Only classify pe_patch if $classify_pe_patch == true
125131
class { 'pe_patch':
126-
patch_group => $patch_group,
132+
patch_group => join($patch_groups, ' '),
127133
}
128134
}
129135
} else {
130136
$patch_fact = 'os_patching'
131137
class { 'os_patching':
132-
patch_window => $patch_group,
138+
patch_window => join($patch_groups, ' '),
133139
}
134140
}
135141
}
136142
'pe_patch': {
137143
# Received the patch_fact from a plan run, use it directly
138144
$patch_fact = 'pe_patch'
139145
class { 'pe_patch':
140-
patch_group => $patch_group,
146+
patch_group => join($patch_groups, ' '),
141147
}
142148
}
143149
'os_patching': {
144150
# Received the patch_fact from a plan run, use it directly
145151
$patch_fact = 'os_patching'
146152
class { 'os_patching':
147-
patch_window => $patch_group,
153+
patch_window => join($patch_groups, ' '),
148154
}
149155
}
150156
default: { fail('Unsupported value for plan_patch_fact parameter!') }
@@ -155,34 +161,46 @@
155161
ensure_packages('yum-utils')
156162
}
157163

158-
# Determine if today is Patch Day for this node's $patch_group
159-
case $patch_group {
160-
'always': {
161-
$bool_patch_day = true
162-
schedule { 'Patching as Code - Patch Window':
163-
range => '00:00 - 23:59',
164-
repeat => 1440
165-
}
166-
$_reboot = 'ifneeded'
164+
# Determine if today is Patch Day for this node's $patch_groups
165+
if 'never' in $patch_groups {
166+
$bool_patch_day = false
167+
schedule { 'Patching as Code - Patch Window':
168+
period => 'never',
167169
}
168-
'never': {
169-
$bool_patch_day = false
170-
schedule { 'Patching as Code - Patch Window':
171-
period => 'never',
170+
$_reboot = 'never'
171+
$active_pg = 'never'
172+
} elsif 'always' in $patch_groups {
173+
$bool_patch_day = true
174+
schedule { 'Patching as Code - Patch Window':
175+
range => '00:00 - 23:59',
176+
repeat => 1440
177+
}
178+
$_reboot = 'ifneeded'
179+
$active_pg = 'always'
180+
} else {
181+
$pg_info = $patch_groups.map |$pg| {
182+
{
183+
'name' => $pg,
184+
'is_patch_day' => patching_as_code::is_patchday(
185+
$patch_schedule[$pg]['day_of_week'],
186+
$patch_schedule[$pg]['count_of_week']
187+
)
172188
}
173-
$_reboot = 'never'
174189
}
175-
default: {
176-
$bool_patch_day = patching_as_code::is_patchday(
177-
$patch_schedule[$patch_group]['day_of_week'],
178-
$patch_schedule[$patch_group]['count_of_week']
179-
)
190+
$active_pg = $pg_info.reduce(undef) |$memo, $value| {
191+
if $value['is_patch_day'] == true { $value['name'] } else { $memo }
192+
}
193+
$bool_patch_day = type($active_pg,'generalized') ? {
194+
Type[String] => true,
195+
default => false
196+
}
197+
if $bool_patch_day {
180198
schedule { 'Patching as Code - Patch Window':
181-
range => $patch_schedule[$patch_group]['hours'],
182-
weekday => $patch_schedule[$patch_group]['day_of_week'],
183-
repeat => $patch_schedule[$patch_group]['max_runs']
199+
range => $patch_schedule[$active_pg]['hours'],
200+
weekday => $patch_schedule[$active_pg]['day_of_week'],
201+
repeat => $patch_schedule[$active_pg]['max_runs']
184202
}
185-
$_reboot = $patch_schedule[$patch_group]['reboot']
203+
$_reboot = $patch_schedule[$active_pg]['reboot']
186204
}
187205
}
188206

@@ -196,9 +214,12 @@
196214
blocklist => $blocklist,
197215
enable_patching => $enable_patching,
198216
patch_fact => $patch_fact,
199-
patch_group => $patch_group,
200-
patch_schedule => if $patch_schedule[$patch_group] == undef { 'none' }
201-
else { $patch_schedule[$patch_group] },
217+
patch_group => $patch_groups,
218+
patch_schedule => if $active_pg in ['always', 'never'] {
219+
{ $active_pg => 'N/A' }
220+
} else {
221+
$patch_schedule.filter |$item| { $item[0] in $patch_groups }
222+
},
202223
post_patch_commands => $post_patch_commands,
203224
pre_patch_commands => $pre_patch_commands,
204225
pre_reboot_commands => $pre_reboot_commands,

metadata.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "puppetlabs-patching_as_code",
3-
"version": "0.5.0",
3+
"version": "0.6.0",
44
"author": "puppetlabs",
55
"summary": "Automated patching through desired state code",
66
"license": "Apache-2.0",

0 commit comments

Comments
 (0)