├── .github └── workflows │ └── ci_tests.yml ├── .travis.yml ├── HACKING.md ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── NEWS ├── README.md ├── auter ├── auter.aptModule ├── auter.conf ├── auter.conf.man ├── auter.cron ├── auter.help2man-sections ├── auter.spec ├── auter.yumdnfModule ├── buildGuide.md ├── contrib ├── README.md ├── conditionalReboot │ ├── 99-conditionalReboot.sh │ └── README.md └── configsnap │ ├── 01-configsnap-pre │ ├── 50-configsnap-post-apply │ ├── 50-configsnap-pre-reboot │ ├── 99-configsnap-post-reboot │ └── README.md ├── debian ├── auter.install ├── auter.manpages ├── changelog ├── compat ├── control ├── copyright ├── dirs ├── postinst ├── prerm └── rules └── tests ├── .aspell_auter_dictionary ├── 01-spellcheck.sh ├── 05-shellcheck.sh ├── 10-rpmbuild.sh ├── 11-container-rpmbuild.sh ├── 20-debuild.sh ├── 21-container-debuild.sh └── _helpers /.github/workflows/ci_tests.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | 12 | - name: Run 01-spellcheck.sh 13 | run: ./tests/01-spellcheck.sh 14 | - name: Run 05-shellcheck.sh 15 | run: ./tests/05-shellcheck.sh 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: bash 3 | before_install: 4 | - sudo apt-get update 5 | - sudo apt-get install -y aspell aspell-en 6 | jobs: 7 | include: 8 | - stage: Basic Tests 9 | name: SpellCheck 10 | script: ./tests/01-spellcheck.sh 11 | - name: ShellCheck 12 | script: ./tests/05-shellcheck.sh 13 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please feel free to first start a 4 | discussion either via issue or our mailing list 5 | [auter-devel@rackspace.com](mailto://auter-devel@rackspace.com) before working 6 | on the change. 7 | 8 | ## Pull Requests 9 | 10 | - Ensure all tests pass on your forked branch prior to submitting the PR; if 11 | necessary add words flagged by the 01-spellcheck.sh test to the dictionary 12 | - All pull requests should be made against the develop branch 13 | - Update the help and man pages with any additional functionality being added by 14 | the PR; try to do this in independent commits to make reviewing easier 15 | - Update the file list in 01-spellcheck.sh if adding a new documentation file 16 | - Don't bump version numbers - this will be done during a release 17 | 18 | ### Code standards 19 | 20 | We adhere to Google's [shell style guide](https://google.github.io/styleguide/shell.xml). 21 | Historically one of the exceptions was to declare all variables in capitals, not just 22 | global variables, but work is ongoing to gradually conform to the style guide. 23 | 24 | ### Testing changes 25 | 26 | For all changes, the following should be considered the bare minimum to be 27 | tested: 28 | 29 | ```bash 30 | auter --disable # deletes /var/lib/auter/enabled, prints "auter disabled" 31 | auter --status # prints "auter is currently disabled" 32 | auter --enable # touches /var/lib/auter/enabled, prints "auter enabled" 33 | auter --status # prints "auter is currently enabled and not running" 34 | auter --help # shows help message 35 | auter --version # prints "auter VERSIONNUMBER" 36 | man auter # check the contents of the man page reflect the relevant changes 37 | man auter.conf # if relevant, check the contents of the man page reflect the changes 38 | ``` 39 | 40 | If no updates are available, try downgrading a package like openssl or curl 41 | which often has several versions available. 42 | 43 | RHEL based distributions: 44 | 45 | ```bash 46 | $ yum --showduplicates list curl 47 | $ yum downgrade curl libcurl 48 | ``` 49 | 50 | Debian based distributions: 51 | 52 | ```bash 53 | # This will give you a list of packages that have multiple versions available in the repos: 54 | for PKG in $(dpkg --list | awk '{print $2}'); do 55 | VERSIONS="$(apt-cache showpkg $PKG | awk '/Versions:/,/^Reverse Depends:/ {if ($1 ~ /^[0-9]/) print $1}' | xargs)" 56 | 57 | [[ $(echo ${VERSIONS} | wc -w) -gt 1 ]] && echo "$PKG -% $VERSIONS" 58 | done | column -t -s "%" 59 | 60 | $ apt-get update 61 | $ apt-get install = 62 | ``` 63 | 64 | _Note: You may need to also downgrade dependencies._ 65 | 66 | #### Pre/Post script functionality 67 | 68 | Use the following to setup the pre/post scripts: 69 | 70 | ```bash 71 | echo 'logger custom pre prep script ran' > /etc/auter/pre-prep.d/pre_prep_script 72 | echo 'logger custom post prep script ran' > /etc/auter/post-prep.d/post_prep_script 73 | echo 'logger custom pre apply script ran' > /etc/auter/pre-apply.d/pre_apply_script 74 | echo 'logger custom post apply script ran' > /etc/auter/post-apply.d/post_apply_script 75 | echo 'logger custom pre reboot script ran' > /etc/auter/pre-reboot.d/pre_reboot_script 76 | echo 'logger custom post reboot script ran' > /etc/auter/post-reboot.d/post_reboot_script 77 | chmod +x /etc/auter/*.d/*script 78 | ``` 79 | 80 | #### General guidelines for testing 81 | 82 | 1. Run the base commands directly via command line testing for both positive and 83 | negative outcomes 84 | 2. Run the base commands via `at` job, again testing both positive and negative 85 | outcomes 86 | 3. Test the effects of the change in the function. Again, if possible, test 87 | success and failure conditions 88 | 4. Test all functionality in Auter that is affected by the code changes. 89 | 90 | 91 | #### Release Process 92 | 93 | 1. A new issue should be raised with the title of "Prep for Release" 94 | 2. Ensure all required pull requests for the new release have been merged to the 95 | develop branch 96 | 3. A maintainer with admin rights to the master branch in rackerlabs/auter 97 | should create a new branch based from the develop branch: 98 | 99 | ```sh 100 | git clone --branch=develop https://github.com/rackerlabs/auter.git rackerlabs/auter 101 | cd rackerlabs/auter 102 | git checkout -b Release- 103 | git push origin Release- 104 | ``` 105 | 106 | 4. Any new pull requests should be made to the develop branch. The only changes 107 | to the Release- should be fixing of any review issues for that 108 | branch. 109 | 5. Full testing for all supported distributions should be carried out and 110 | tracked in a github project created for the release. Example: 111 | https://github.com/rackerlabs/auter/projects/1 112 | 6. Once all testing has been completed for Release-, the reviewer 113 | should merge the Release- branch to both master and develop 114 | branches. 115 | 7. Tag a new release named using template 116 | . (Eg: Release 0.11) 117 | 118 | 119 | ##### Testing template 120 | 121 | This should be completed for all supported distributions in all pull requests to 122 | the master branch before being merged. 123 | 124 | ``` 125 | # 126 | # Steps taken to create install file: 127 | ``` 128 | ``` 129 | --- 130 | # Test 1: Basic auter status tests 131 | 1) Do a fresh install of auter 132 | 2) Execute: `auter --status` 133 | Checks: 134 | - __[ pass/fail ]__ Exit code 0 135 | - __[ pass/fail ]__ Prints "auter is currently enabled and not running" to screen 136 | - __[ pass/fail ]__ /var/lib/auter/enabled exists 137 | 138 | 3) Execute: `auter --disable` 139 | Checks: 140 | - __[ pass/fail ]__ Exit code 0 141 | - __[ pass/fail ]__ Prints "INFO: auter disabled" to screen 142 | - __[ pass/fail ]__ /var/lib/auter/enabled does not exist 143 | 144 | 4) Execute: `auter --status` 145 | Checks: 146 | - __[ pass/fail ]__ Exit code 0 147 | - __[ pass/fail ]__ Prints "auter is currently disabled" to screen 148 | - __[ pass/fail ]__ /var/lib/auter/enabled does not exist 149 | 150 | 5) Execute: `auter --enable` 151 | - __[ pass/fail ]__ Exit code 0 152 | - __[ pass/fail ]__ Prints "INFO: auter enabled" to screen 153 | - __[ pass/fail ]__ /var/lib/auter/enabled exists 154 | 155 | 6) Execute: `auter --help` 156 | - __[ pass/fail ]__ Exit code 0 157 | - __[ pass/fail ]__ Prints the auter help to screen 158 | 159 | 7) Execute: `auter` (no arguments provided) 160 | - __[ pass/fail ]__ Exit code 0 161 | - __[ pass/fail ]__ Prints the auter help to screen 162 | 163 | 164 | 8) Execute: `auter --version` 165 | - __[ pass/fail ]__ Exit code 0 166 | - __[ pass/fail ]__ Prints the correct auter version 167 | --- 168 | 169 | # Test 2: Manual process - default config [ PASS/FAIL ] 170 | ### Config settings 171 | 1) `egrep -v "^$|^#" /etc/auter/auter.conf` 172 | ``` 173 | 174 | ``` 175 | 2) Prepare custom scripts: Execute: 176 | ``` 177 | echo 'logger custom pre prep script ran' > /etc/auter/pre-prep.d/pre_prep_script 178 | echo 'logger custom post prep script ran' > /etc/auter/post-prep.d/post_prep_script 179 | echo 'logger custom pre apply script ran' > /etc/auter/pre-apply.d/pre_apply_script 180 | echo 'logger custom post apply script ran' > /etc/auter/post-apply.d/post_apply_script 181 | echo 'logger custom pre reboot script ran' > /etc/auter/pre-reboot.d/pre_reboot_script 182 | echo 'logger custom post reboot script ran' > /etc/auter/post-reboot.d/post_reboot_script 183 | chmod +x /etc/auter/*.d/*script 184 | ``` 185 | 3) Execute: `auter --prep` 186 | - __[ pass/fail ]__ prints the following block to stdout: 187 | ``` 188 | INFO: Running with: /usr/bin/auter --prep 189 | INFO: Running in an interactive shell, disabling all random sleeps 190 | INFO: Running Pre-Prep script /etc/auter/pre-prep.d/pre_prep_script 191 | INFO: Updates downloaded 192 | INFO: Running Post-Prep script /etc/auter/post-prep.d/post_prep_script 193 | ``` 194 | - __[ pass/fail ]__ **_/var/lib/auter/last-prep-output-default_** contains yum download-only output 195 | - __[ pass/fail ]__ updates downloaded to **_/var/cache/yum/..._** 196 | - __[ pass/fail ]__ pre/post prep scripts ran successfully with messages logged to syslog 197 | ##### Output from prep: 198 | ``` 199 | 200 | ``` 201 | 4) Execute `auter --apply` 202 | - __[ pass/fail ]__ prints the following block to stdout: Note: For debian based distros there will not be a transaction ID 203 | ``` 204 | INFO: Running with: /usr/bin/auter --apply 205 | INFO: Running in an interactive shell, disabling all random sleeps 206 | INFO: Running Pre-Apply script /etc/auter/pre-apply.d/pre_apply_script 207 | INFO: Applying updates 208 | INFO: Running Post-Apply script /etc/auter/post-apply.d/pre_apply_script 209 | INFO: Updates complete (yum Transaction ID : ). You may need to reboot for some updates to take effect 210 | INFO: Auter successfully ran at 211 | ``` 212 | - __[ pass/fail ]__ expected updates were applied. Check **_/var/log/apt/history.log_** or **_/var/log/yum.log_** 213 | - __[ pass/fail ]__ **_/var/lib/auter/last-apply-output-default_** contains update info 214 | - __[ pass/fail ]__ no updates available after running. Check `yum update << /etc/auter/pre-prep.d/pre_prep_script 258 | echo 'logger custom post prep script ran' > /etc/auter/post-prep.d/post_prep_script 259 | echo 'logger custom pre apply script ran' > /etc/auter/pre-apply.d/pre_apply_script 260 | echo 'logger custom post apply script ran' > /etc/auter/post-apply.d/post_apply_script 261 | echo 'logger custom pre reboot script ran' > /etc/auter/pre-reboot.d/pre_reboot_script 262 | echo 'logger custom post reboot script ran' > /etc/auter/post-reboot.d/post_reboot_script 263 | chmod +x /etc/auter/*.d/*script 264 | ``` 265 | 3) Adjust the MAXDELAY value to avoid extended sleep times 266 | ``` 267 | sed -i 's/MAXDELAY.*$/MAXDELAY="60"/g' /etc/auter/auter.conf 268 | ``` 269 | 4) Schedule a cron job for prep to run in 5 minutes and watch the logs: 270 | ``` 271 | echo "$(date --date="5 minutes" +%_M" "%_H" "%d" "%_m" *") root $(which auter) --prep --stdout" > /etc/cron.d/auter-prep 272 | tail -n0 -f /var/log/messages or tail -f /var/log/syslog or journalctl -f 273 | ``` 274 | After auter has completed the prep: 275 | - __[ pass/fail ]__ Expected logs: 276 | ``` 277 | auter: INFO: Running with: /usr/bin/auter --prep --stdout 278 | auter: INFO: Running Pre-Prep script /etc/auter/pre-prep.d/pre_prep_script 279 | root: custom pre prep script ran 280 | auter: INFO: Updates downloaded 281 | auter: INFO: Running Post-Prep script /etc/auter/post-prep.d/post_prep_scrip 282 | root: custom post prep script ran 283 | ``` 284 | - __[ pass/fail ]__ **_/var/lib/auter/last-prep-output-default_** contains yum download-only output 285 | - __[ pass/fail ]__ updates downloaded to **_/var/cache/yum/…_** or **_/var/cache/apt/archives/..._** 286 | - __[ pass/fail ]__ pre/post prep scripts ran successfully with messages logged to syslog 287 | - __[ pass/fail ]__ mail sent to root user with stdout output from auter. Debian will log stdout to syslog rather than mail 288 | Output from logs: 289 | ``` 290 | 291 | ``` 292 | 5) Schedule a cron job for apply to run in 5 minutes and watch the logs: 293 | ``` 294 | echo "$(date --date="5 minutes" +%_M" "%_H" "%d" "%_m" *") root $(which auter) --apply --stdout" > /etc/cron.d/auter-apply 295 | tail -n0 -f /var/log/messages or tail -f /var/log/syslog or journalctl -f 296 | ``` 297 | After auter has completed the apply: Note: For debian based distros there will not be a transaction ID 298 | - __[ pass/fail ]__ Expected logs: 299 | ``` 300 | auter: INFO: Running with: /usr/bin/auter --apply --stdout 301 | auter: INFO: Running Pre-Apply script /etc/auter/pre-apply.d/pre_apply_script 302 | root: custom pre apply script ran 303 | auter: INFO: Applying updates 304 | auter: INFO: Running Post-Apply script /etc/auter/post-apply.d/pre_apply_script 305 | root: custom post apply script ran 306 | auter: INFO: Updates complete (yum Transaction ID : 86). You may need to reboot for some updates to take effect 307 | auter: INFO: Auter successfully ran at 2018-07-18T14:59:24+0000 308 | ``` 309 | - __[ pass/fail ]__ no updates available after running 310 | - __[ pass/fail ]__ pre/post scripts ran successfully with messages logged to syslog 311 | - __[ pass/fail ]__ mail sent to root user with stdout output from auter. Debian will log stdout to syslog rather than mail 312 | Output from logs: 313 | ``` 314 | 315 | ``` 316 | 6) Schedule a cron job to run auter --reboot in 5 minutes and watch the logs: 317 | ``` 318 | echo "$(date --date="5 minutes" +%_M" "%_H" "%d" "%_m" *") root $(which auter) --reboot --stdout" > /etc/cron.d/auter-reboot 319 | tail -n0 -f /var/log/messages or tail -f /var/log/syslog or journalctl -f 320 | ``` 321 | - __[ pass/fail ]__ Expected logs: 322 | ``` 323 | INFO: Running with: /usr/bin/auter --reboot --stdout 324 | INFO: Running Pre-Reboot script /etc/auter/pre-reboot.d/98-configsnap-pre-reboot 325 | INFO: Running Pre-Reboot script /etc/auter/pre-reboot.d/pre_reboot_script 326 | custom pre reboot script ran 327 | INFO: Adding post-reboot-hook to run scripts under /etc/auter/post-reboot.d to /etc/cron.d/auter-postreboot-default 328 | INFO: Rebooting server 329 | ``` 330 | - __[ pass/fail ]__ pre-reboot scripts ran successfully 331 | - __[ pass/fail ]__ Wall message is printed 332 | - __[ pass/fail ]__ mail sent to root user with stdout output from auter. Debian will log stdout to syslog rather than mail 333 | - __[ pass/fail ]__ Server reboots 334 | Output from logs: 335 | ``` 336 | 337 | ``` 338 | 7) After the server has booted, it may take up to 2 minutes for auter logs to appear. watch the logs: 339 | ``` 340 | egrep "auter:|custom" /var/log/messages | awk '/auter --reboot/,0' 341 | or 342 | egrep "auter:|custom" /var/log/syslog | awk '/auter --reboot/,0' 343 | or 344 | journalctl -S today | egrep auter|custom | awk '/auter --reboot/,0' 345 | ``` 346 | - __[ pass/fail ]__ Expected logs: 347 | ``` 348 | auter: INFO: Running with: /usr/bin/auter --reboot --stdout 349 | auter: INFO: Running Pre-Reboot script /etc/auter/pre-reboot.d/pre_reboot_script 350 | root: custom pre reboot script ran 351 | auter: INFO: Adding post-reboot-hook to run scripts under /etc/auter/post-reboot.d to /etc/cron.d/auter-postreboot-default 352 | auter: INFO: Rebooting server 353 | auter: INFO: Running with: /usr/bin/auter --postreboot --config /etc/auter/auter.conf 354 | auter: INFO: Removed post-reboot hook: /etc/cron.d/auter-postreboot-default 355 | auter: INFO: Running Post-Reboot script /etc/auter/post-reboot.d/post_reboot_script 356 | root: custom post reboot script ran 357 | ``` 358 | - __[ pass/fail ]__ post-reboot scripts ran successfully 359 | - __[ pass/fail ]__ output from auter also mailed to the root user on CentOS, output logged to syslog on Fedora 360 | Output from logs: 361 | ``` 362 | 363 | ``` 364 | #### new functionality testing 365 | ### Config settings 366 | 1) `egrep -v "^$|^#" /etc/auter/auter.conf` 367 | ``` 368 | 369 | ``` 370 | 2) Details of new feature 371 | 372 | 3) Test command 373 | Expected outcome: 374 | ``` 375 | 376 | ``` 377 | 4) Next Test: 378 | etc... 379 | ``` 380 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | Project auter is maintained by the auter team. Bug reports & assistance can be 2 | found at our email list [auter-devel@rackspace.com](mailto://auter-devel@rackspace.com). 3 | 4 | Original authors at Rackspace (http://www.rackspace.co.uk): 5 | - Cameron Beere 6 | - Piers Cornwell 7 | - Mike Frost 8 | - Paolo Gigante 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME := "auter" 2 | BUILD_FILES = auter.conf auter.cron auter.help2man-sections Makefile 3 | BUILD_FILES += auter.yumdnfModule auter.aptModule auter.conf.man 4 | BUILD_FILES += LICENSE README.md MAINTAINERS.md NEWS 5 | EXEC_FILES = auter 6 | 7 | # package info 8 | UPSTREAM := "https://github.com/rackerlabs/${NAME}.git" 9 | VERSION := $(shell git tag -l | sort -V | tail -n 1) 10 | RELEASE := $(shell gawk '/^Release:\s+/{print gensub(/%.*/,"","g",$$2)}' ${NAME}.spec) 11 | COMMIT := $(shell git log --pretty=format:'%h' -n 1) 12 | DATE := $(shell date --iso-8601) 13 | DATELONG := $(shell date --iso-8601=seconds) 14 | 15 | # build info 16 | BUILD_ROOT := "BUILD" 17 | BUILD_DIR := "${NAME}-${VERSION}" 18 | OUT_DIR := "${HOME}/output" 19 | DIST := $(shell python -c "import platform; print(platform.linux_distribution()[0])") 20 | 21 | .PHONY: deb rpm variables clean 22 | 23 | all: 24 | 25 | deb: ${BUILD_FILES} ${EXEC_FILES} 26 | install -d ${BUILD_ROOT}/$@/${BUILD_DIR}/ 27 | cp -rpv debian ${BUILD_ROOT}/$@/${BUILD_DIR}/ 28 | install -m 0644 ${BUILD_FILES} ${BUILD_ROOT}/$@/${BUILD_DIR}/ 29 | install ${EXEC_FILES} ${BUILD_ROOT}/$@/${BUILD_DIR}/ 30 | install -m 0644 auter.conf.man ${BUILD_ROOT}/$@/${BUILD_DIR}/auter.conf.5 31 | install -m 0644 -T auter.aptModule ${BUILD_ROOT}/$@/${BUILD_DIR}/auter.module 32 | tar -C ${BUILD_ROOT}/$@ -czf ${BUILD_ROOT}/$@/${NAME}_${VERSION}.orig.tar.gz ${BUILD_DIR} 33 | cd ${BUILD_ROOT}/$@/${BUILD_DIR} \ 34 | && debuild -i -us -uc -b 35 | 36 | rpm: ${BUILD_FILES} ${EXEC_FILES} 37 | install -d ${BUILD_ROOT}/$@/${BUILD_DIR}/ 38 | install -m 0644 ${BUILD_FILES} ${BUILD_ROOT}/$@/${BUILD_DIR}/ 39 | install ${EXEC_FILES} ${BUILD_ROOT}/$@/${BUILD_DIR}/ 40 | echo "%_topdir /auter/${BUILD_ROOT}/$@/rpmbuild" > ~/.rpmmacros 41 | rpmdev-setuptree 42 | tar -v -czf ${BUILD_ROOT}/$@/rpmbuild/SOURCES/${VERSION}.tar.gz -C ${BUILD_ROOT}/$@/ ${BUILD_DIR} 43 | install -m 0644 auter.spec ${BUILD_ROOT}/$@/rpmbuild/SPECS/ 44 | rpmbuild -ba ${BUILD_ROOT}/$@/rpmbuild/SPECS/auter.spec 45 | 46 | variables: 47 | @echo "DIST: ${DIST}" 48 | @echo "NAME: ${NAME}" 49 | @echo "VERSION: ${VERSION}" 50 | @echo "RELEASE: ${RELEASE}" 51 | @echo "COMMIT: ${COMMIT}" 52 | @echo "DATE: ${DATE}" 53 | @echo "DATELONG: ${DATELONG}" 54 | @echo "BUILD_DIR: ${BUILD_DIR}" 55 | 56 | clean: 57 | $(RM) -r ${BUILD_ROOT} 58 | 59 | 60 | # vim: noet: 61 | 62 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Auter NEWS 2 | 3 | 0.12 4 | * Added --skip-all-scripts to skip the executions of all custom scripts 5 | * Added --skip-scripts-by-phase to skip the executions of custom scripts for the specified phase 6 | * Added --skip-scripts-by-phase to skip the executions of custom scripts by name 7 | * Added man page for auter.conf 8 | * Updated auter.aptModule to reflect changes in auter.yumdnfModule 9 | * added no-wall option 10 | * Fix for --status when run as non-root user 11 | * Logs auter output in /var/lib/auter/ when no updates are available 12 | * Minor improvements to rotation of output files in /var/lib/auter/ 13 | 14 | 0.11 15 | * Updated documentation and references to include apt for Ubuntu/debian 16 | * Removed debugging message that was printed during apt update 17 | * Added "Valid Options" in auter.conf 18 | * Added the pre/post prep directories in auter.conf 19 | * Added retention and rotation for last-prep-output and last-apply-output files in /var/lib/auter 20 | * Corrected file permissions for the auter-postreboot cron file 21 | * Added --stdout option to force output to stdout even if there is no active tty 22 | * Added a package manager lock file check before prep and apply functions call the package manager 23 | * Improved checks to confirm prepared patches are still required 24 | 25 | 0.10 26 | * Added pre and post prep script hooks 27 | * Added a pidfile and process check to --status 28 | * Added a auter success log with a last run timestamp 29 | * Clear pidfile if the process is no longer running when disabling auter 30 | * Added auter.aptModule for ubuntu/debian support 31 | * Added checks to confirm updates that were staged are still pending 32 | 33 | 0.9 34 | * Capture package manager output 35 | * Document the auter --reboot cron job 36 | * Remove last-update file 37 | * Add description text to the lock file 38 | * Add error checking during prep 39 | * Split out package manager specific code 40 | 41 | 0.8 42 | * Added ONLYINSTALLFROMPREP option 43 | 44 | 0.7 45 | * Updated the .spec file according to Fedora's guidelines 46 | * Moved scriptdir from /var/lib/auter to /etc/auter 47 | * Categorize log messages as INFO, WARNING or ERROR 48 | * Remove pre-built man page 49 | 50 | 0.6 51 | * Initial public release 52 | * Add maintainers file 53 | 54 | 0.5 55 | * Added transaction ID logging 56 | * Disable random sleeps when running from a tty 57 | * Rename variables to be package manager agnostic 58 | * Add cron examples for @reboot jobs 59 | * Update default auter config file location 60 | * Remove example script files 61 | * Disable cronjobs & enable lockfile on installation 62 | * Switch to using pre/post script directories instead of files 63 | * Add better handling for option parsing 64 | * Added CONFIGSET variable used to distinguish between distinct configs 65 | * Various bugfixes 66 | 67 | 0.4 68 | * Support DNF 69 | * Add HACKING.md 70 | * Exit if custom config file doesn't exist 71 | * Change post reboot script to use cron instead of rc.local 72 | * Report if there are no updates at prep time 73 | * Record prep and apply output 74 | * Updated man page 75 | 76 | 0.3 77 | * Better defined exit codes 78 | * Added bounds check for MAXDELAY 79 | * Updated documentation with more details about configuration options 80 | * Fixed logging error if downloadonly is not available 81 | 82 | 0.2 83 | * Locking 84 | * Trap Ctrl+C during dangerous section 85 | * Add --status flag 86 | * Move reboot script to /etc/rc.d/rc.local 87 | * Add random delay 88 | * Change from sysv service to --enable/--disable 89 | * Added warnings when pre/post hooks exist but are not executable 90 | * Removed yum transaction support 91 | * Added pid locking to prevent multiple instances of auter running at the same time 92 | 93 | 0.1 94 | * Initial release 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Auter 3 | 4 | > Automatic Update Transaction Execution by Rackspace 5 | 6 | 7 | Automatic updates for RHEL and Debian based distributions with the ability to 8 | pre-download packages, run hooks, and perform automated reboots. 9 | 10 | ## Why use Auter? 11 | It is important to maintain regular system patching on Linux servers to keep up 12 | with the latest bug and security fixes; some of these updates will require 13 | service or server reboots which is where Auter fits in. 14 | 15 | Auter provides a flexible, host-based, solution for updating system packages 16 | via the distributions default package manager. It is possible to configure 17 | independent configuration profiles that can be run individually either manually 18 | on the command line, or scheduled using cron jobs. This would allow weekly 19 | updates to take place without a reboot, and a monthly patching schedule for the 20 | kernel and other core services that require a reboot. 21 | 22 | Auter is also capable of caching available updates, and subsequently only 23 | installing from the cache. This allows package versions to be synchronized 24 | across environments that have different installation dates. 25 | 26 | There are also cases when other options are more suitable: 27 | - I want to update nightly and handle reboots manually: yum-cron or 28 | dnf-automatic 29 | - I want to manage updates via a central management console: RHN Satellite, 30 | or configuration management systems such as Chef or Puppet 31 | 32 | ## Installation 33 | Auter is available for RHEL and its derivatives via the EPEL repository. 34 | ```bash 35 | $ yum/dnf install auter 36 | ``` 37 | 38 | There isn't currently a package maintained for Debian, however we provide a 39 | `.deb` package on the [releases page](https://github.com/rackerlabs/auter/releases). 40 | 41 | ## Setup 42 | All Auter configuration information is stored in `/etc/auter/auter.conf`; it 43 | allows you to set basic options such as the sleep delay (`MAXDELAY`), and 44 | whether automatic reboots should take place (`AUTOREBOOT`). 45 | 46 | _More information can be found on the [Wiki](https://github.com/rackerlabs/auter/wiki/Configuration)._ 47 | 48 | ## Usage 49 | Auter can be run manually: 50 | ```bash 51 | $ auter --prep 52 | $ auter --apply 53 | ``` 54 | or via cron: 55 | ```bash 56 | # Prep Every Friday at 22:00 57 | 0 22 * * Fri root /usr/bin/auter --prep 58 | # Apply Every Saturday at 23:00 59 | 0 23 * * Sat root /usr/bin/auter --apply 60 | ``` 61 | _For more examples and usage, please refer to the [Wiki]( https://github.com/rackerlabs/auter/wiki/Usage)._ 62 | 63 | ## Contributing 64 | Please read 65 | [HACKING.md](https://github.com/rackerlabs/auter/blob/master/HACKING.md) for 66 | details on how to contribute, and the process for submitting pull requests. 67 | 68 | ## Maintainers 69 | - Nick Rhodes 70 | - Paolo Gigante 71 | 72 | See also the list of original 73 | [contributors](https://github.com/rackerlabs/auter/blob/master/MAINTAINERS.md) 74 | who started the project. 75 | 76 | ## Related projects 77 | - [Auter Manager](https://github.com/rackerlabs/auter-manager): Simplify the 78 | installation of Auter across multiple servers with a single master 79 | configuration file written in CSV. 80 | -------------------------------------------------------------------------------- /auter: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # auter is a yum-cron type package which implements automatic updates on an 4 | # individual server with features such as predownloading packages and reboots. 5 | # 6 | # 7 | # Copyright 2016 Rackspace, Inc. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use 10 | # this file except in compliance with the License. You may obtain a copy of the 11 | # License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software distributed 16 | # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 17 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 18 | # specific language governing permissions and limitations under the License. 19 | # 20 | 21 | 22 | declare -r -x AUTERVERSION="1.0.0" 23 | declare -r -x SCRIPTDIR="/etc/auter" 24 | declare -r -x DATADIR="/var/lib/auter" 25 | declare -r -x LOCKFILE="${DATADIR}/enabled" 26 | declare -r -x PIDFILE="/var/run/auter/auter.pid" 27 | 28 | # Set default options - these can be overridden in the config file or with a command line argument 29 | declare -x -l AUTOREBOOT="no" 30 | declare -x -i REBOOTCALL=0 31 | declare -x -a PACKAGEMANAGEROPTIONS 32 | declare -x -l PREDOWNLOADUPDATES="yes" 33 | declare -x -l ONLYINSTALLFROMPREP="no" 34 | declare -x CONFIGFILE="/etc/auter/auter.conf" 35 | declare -x DOWNLOADDIR="/var/cache/auter" 36 | declare -x -i MAXDELAY=3600 37 | declare -x CONFIGSET="default" 38 | declare -x ROTATE="5" 39 | declare -x PREPREPSCRIPTDIR="${SCRIPTDIR}/pre-prep.d" 40 | declare -x POSTPREPSCRIPTDIR="${SCRIPTDIR}/post-prep.d" 41 | declare -x PREAPPLYSCRIPTDIR="${SCRIPTDIR}/pre-apply.d" 42 | declare -x POSTAPPLYSCRIPTDIR="${SCRIPTDIR}/post-apply.d" 43 | declare -x PREREBOOTSCRIPTDIR="${SCRIPTDIR}/pre-reboot.d" 44 | declare -x POSTREBOOTSCRIPTDIR="${SCRIPTDIR}/post-reboot.d" 45 | declare -x -i SKIPALLSCRIPTS=0 46 | declare -x -a SKIPPHASESCRIPTS 47 | declare -x -a SKIPSCRIPTNAMES 48 | 49 | # Adding extra super-user PATHS if required 50 | for p in /usr/local/sbin /usr/sbin /sbin; do 51 | [[ ! $PATH =~ (^|:)$p(:|$) ]] && declare -x PATH="$p:$PATH" 52 | done 53 | 54 | function default_signal_handling() { 55 | trap 'rm -f "$PIDFILE"' SIGINT SIGTERM 56 | } 57 | 58 | # The man page is generated in part from the print_help() text by running: 59 | # help2man --include=auter.help2man --no-info ./auter > auter.man 60 | function print_help() { 61 | echo "Usage: auter [--enable|--disable|--status|--prep|--apply|--reboot|--postreboot] [OPTIONS] 62 | 63 | Automatic Update Transaction Execution by Rackspace. A wrapper around cron and 64 | yum/dnf/apt to manage system updates with the ability to configure automatic 65 | reboots and custom scripts. 66 | 67 | Actions: 68 | -e, --enable Enable auter 69 | -d, --disable Disable auter. Also deletes unused pidfile if it exists 70 | -s, --status Show whether enabled or disabled 71 | --prep Pre-download updates before applying 72 | --apply Apply updates, and reboot if AUTOREBOOT=yes 73 | --reboot Reboot system including pre/post reboot scripts 74 | --postreboot Run post reboot script 75 | 76 | Options: 77 | --config=FILE Specify the full path to an auter config file. Defaults to 78 | /etc/auter/auter.conf 79 | --stdout Always log to STDOUT, regardless of not having a tty 80 | --maxdelay Override MAXDELAY from the command line 81 | --skip-all-scripts 82 | Skip the executions of all custom scripts (Default in /etc/auter/*.d/) 83 | 84 | --skip-scripts-by-phase=PHASE 85 | Skip the execution of the custom scripts for the specified 86 | phase. You can specify myltiple phases. 87 | 88 | Valid Phases: pre-prep, post-prep, pre-apply, post-apply, 89 | pre-reboot, post-reboot. 90 | 91 | Example: --skip-scripts-by-phase=\"pre-prep,post-apply,pre-reboot\" 92 | 93 | --skip-scripts-by-name=SCRIPTNAME 94 | Skip specific scripts by name. You can specify myltiple phases. 95 | 96 | Example: --skip-scripts-by-name=\"10-configsnap-pre, 20-startApp.sh\" 97 | 98 | --no-wall If possible, suppress shutdown wall messages in the reboot phase 99 | -h, --help Show this help text 100 | -v, --version Show the version" 101 | } 102 | 103 | function logit() { 104 | # If running on a tty, or the --stdout option is provided, print to screen 105 | ( tty -s || [[ $STDOUT ]] ) && echo "$1" 106 | logger -p info -t auter "$1" 107 | } 108 | 109 | function read_config() { 110 | if [[ -f "$CONFIGFILE" ]]; then 111 | source "$CONFIGFILE" 112 | elif [[ "$CUSTOMCONFIG" ]]; then 113 | logit "ERROR: Custom config file $CONFIGFILE does not exist" 114 | quit 5 115 | else 116 | logit "WARNING: Using default config values." 117 | fi 118 | 119 | IFS=' ' read -r -a PACKAGEMANAGEROPTIONS <<< "${PACKAGEMANAGEROPTIONS[*]}" 120 | } 121 | 122 | function rotate_file { 123 | local output_file="$1" 124 | 125 | #Rotate old file 126 | for i in $(seq $((ROTATE-1)) -1 1); do 127 | [[ -e "$output_file.$i" ]] && mv -f "$output_file.$i" "$output_file.$((i+1))" 128 | done 129 | 130 | # Move base files to basefile.1 131 | [[ -e "$output_file" ]] && mv "$output_file" "$output_file.1" 132 | 133 | readarray -t remove_files <<< "$(find "$DATADIR" -type f -name "$(basename "$output_file").*" | awk -F'.' -v s="$ROTATE" '($NF+1)>s')" 134 | # Finally check for extra file from the rotation, and remove if it exists 135 | [[ -n "${remove_files[*]}" ]] && rm -f "${remove_files[@]}" 136 | } 137 | 138 | function run_script { 139 | local script="$1" 140 | local phase="$2" 141 | if [[ "$SKIPALLSCRIPTS" -eq 0 ]]; then 142 | local skip=0 143 | # Check if the phase scripts have been skipped with --skip-scripts-by-phase 144 | if [[ -n "${SKIPPHASESCRIPTS[*]}" ]]; then 145 | for _skipphasescript in "${SKIPPHASESCRIPTS[@]}"; do 146 | [[ "${_skipphasescript,,}" == *"${phase,,}"* ]] && skip=1 147 | done 148 | fi 149 | 150 | # Check if the script has been excluded with --skip-scripts-by-name 151 | if [[ -n "${SKIPSCRIPTNAMES[*]}" ]]; then 152 | for _skipscriptname in "${SKIPSCRIPTNAMES[@]}"; do 153 | [[ ${script,,} == *"${_skipscriptname,,}"* ]] && skip=1 154 | done 155 | fi 156 | 157 | if [[ "$skip" -eq 0 ]]; then 158 | if [[ -x "$script" ]] && [[ -f "$script" ]]; then 159 | logit "INFO: Running $phase script $script" 160 | $script 161 | local RC=$? 162 | if [[ "$RC" -ne 0 ]]; then 163 | logit "ERROR: $phase script $script exited with non-zero exit code $RC. Aborting auter run." 164 | quit 8 165 | fi 166 | elif [[ -f "$script" ]]; then 167 | logit "ERROR: $phase script $script exists but the execute bit is not set. Skipping." 168 | fi 169 | else 170 | logit "INFO: Skipping script $script" 171 | fi 172 | else 173 | logit "INFO: The --skip-all-scripts flag was used. NOT executing $script as part of the $phase phase" 174 | fi 175 | } 176 | 177 | # Check whether yum, or dnf is available 178 | function check_package_manager() { 179 | for pkg_mgr in dnf yum apt-get; do 180 | command -v "$pkg_mgr" && return 0 181 | done 182 | return 1 183 | } 184 | 185 | function reboot_server() { 186 | for _script in "$PREREBOOTSCRIPTDIR"/*; do 187 | run_script "$_script" "Pre-Reboot" 188 | done 189 | 190 | if [[ -d "${POSTREBOOTSCRIPTDIR}" ]]; then 191 | logit "INFO: Creating post-reboot hook /etc/cron.d/auter-postreboot-${CONFIGSET}" 192 | echo -e "@reboot root /usr/bin/auter --postreboot --config ${CONFIGFILE}" > "/etc/cron.d/auter-postreboot-${CONFIGSET}" 193 | chown root:root "/etc/cron.d/auter-postreboot-${CONFIGSET}" 194 | chmod 0644 "/etc/cron.d/auter-postreboot-${CONFIGSET}" 195 | fi 196 | 197 | logit "INFO: Rebooting server" 198 | if [[ $NOWALLMSG -eq 1 ]] && shutdown --help | grep -q "no-wall"; then 199 | /sbin/shutdown --no-wall -r +2 "auter: System reboot to apply updates" &>/dev/null & 200 | else 201 | /sbin/shutdown -r +2 "auter: System reboot to apply updates" &>/dev/null & 202 | fi 203 | } 204 | 205 | function post_reboot() { 206 | logit "INFO: Removed post-reboot hook: /etc/cron.d/auter-postreboot-$CONFIGSET" 207 | rm -f "/etc/cron.d/auter-postreboot-$CONFIGSET" 208 | tty -s || sleep 300 209 | 210 | for _script in "$POSTREBOOTSCRIPTDIR"/*; do 211 | run_script "$_script" "Post-Reboot" 212 | done 213 | } 214 | 215 | function print_status() { 216 | if [[ -f "$LOCKFILE" ]] && [[ -f "$PIDFILE" ]]; then 217 | if currentpidstatus="$(kill -0 "$(cat $PIDFILE)" 2>&1 )"; then 218 | echo "auter is currently enabled and running" 219 | elif [[ "$currentpidstatus" == *"No such process"* ]]; then 220 | echo "auter is currently enabled and pid file exists but process is dead" 221 | elif [[ "$currentpidstatus" == *"Operation not permitted"* ]]; then 222 | echo "auter is enabled but permission denied on $PIDFILE. Run 'auter --status' as root" 223 | fi 224 | elif [[ -f "$LOCKFILE" ]] && [[ ! -f "$PIDFILE" ]]; then 225 | echo "auter is currently enabled and not running" 226 | else 227 | echo "auter is currently disabled" 228 | fi 229 | } 230 | 231 | # Needed to cleanup our PID file. The only argument is the exit code to use. 232 | function quit() { 233 | [[ -f "$PIDFILE" ]] && rm -f "$PIDFILE" 234 | exit "$1" 235 | } 236 | 237 | function log_last_run() { 238 | logit "INFO: Auter successfully ran at $(date -Iseconds)" 239 | } 240 | 241 | 242 | # 243 | # Main 244 | # 245 | 246 | # Make sure we trap signals and clean up the PID before exiting 247 | default_signal_handling 248 | 249 | ARGS="$*" 250 | if ! OPTS=$(getopt -n "$0" -o 'edhvs' --long 'prep,apply,enable,disable,reboot,postreboot,version,help,stdout,no-wall,status,config:,skip-all-scripts,skip-scripts-by-phase:,skip-scripts-by-name:,maxdelay:' -- "$@"); then 251 | echo "See '$0 --help' for valid options." 252 | quit 1 253 | fi 254 | 255 | eval set -- "$OPTS" 256 | unset OPTS 257 | 258 | _required=false 259 | while true ; do 260 | case "$1" in 261 | '-h'|'--help') 262 | print_help 263 | quit 0 264 | ;; 265 | '-v'|'--version') 266 | echo "auter ${AUTERVERSION}" 267 | exit 0 268 | ;; 269 | '--stdout') 270 | STDOUT=1 271 | shift 272 | ;; 273 | '--no-wall') 274 | NOWALLMSG=1 275 | shift 276 | ;; 277 | '--maxdelay') 278 | __MAXDELAY="$2" 279 | shift 2 280 | ;; 281 | '--config') 282 | CONFIGSET="" 283 | CONFIGFILE="$2" 284 | CUSTOMCONFIG=1 285 | shift 2 286 | ;; 287 | '--prep') 288 | _required=true 289 | PREP=1 290 | shift 291 | ;; 292 | '--apply') 293 | _required=true 294 | APPLY=1 295 | shift 296 | ;; 297 | '--reboot') 298 | _required=true 299 | REBOOTCALL=1 300 | shift 301 | ;; 302 | '--postreboot') 303 | _required=true 304 | POSTREBOOT=1 305 | shift 306 | ;; 307 | '-e'|'--enable') 308 | _required=true 309 | ENABLE=1 310 | shift 311 | ;; 312 | '-d'|'--disable') 313 | _required=true 314 | DISABLE=1 315 | shift 316 | ;; 317 | '--skip-all-scripts') 318 | SKIPALLSCRIPTS=1 319 | shift 320 | ;; 321 | '--skip-scripts-by-phase') 322 | IFS=',' read -r -a SKIPPHASESCRIPTS <<< "$2" 323 | shift 2 324 | ;; 325 | '--skip-scripts-by-name') 326 | IFS=',' read -r -a SKIPSCRIPTNAMES <<< "$2" 327 | shift 2 328 | ;; 329 | '-s'|'--status') 330 | print_status 331 | quit 0 332 | ;; 333 | '--') 334 | shift 335 | break 336 | ;; 337 | *) 338 | quit 1 339 | ;; 340 | esac 341 | done 342 | 343 | if ! $_required; then 344 | echo "Auter must be run with one of the following: 345 | $0 [--enable|--disable|--status|--prep|--apply|--reboot|--postreboot]" 346 | echo "See auter --help for details." 347 | quit 1 348 | fi 349 | unset _required 350 | 351 | declare -x PACKAGE_MANAGER 352 | if ! PACKAGE_MANAGER=$(check_package_manager); then logit "ERROR: Cannot find yum, dnf or apt-get"; quit 7; fi 353 | 354 | # Do this after option processing so --help and --status still work. 355 | if [[ "$(whoami)" != "root" ]]; then 356 | echo "Script must be run as root" 357 | quit 5 358 | fi 359 | 360 | if [[ ! -d "$DATADIR" ]]; then 361 | logit "FATAL ERROR: auter DATADIR $DATADIR does not exist." 362 | quit 5 363 | fi 364 | 365 | if [[ "$ENABLE" ]] ; then 366 | touch "$LOCKFILE" 367 | echo "DO NOT DELETE THIS FILE. This file is automatically generated by auter. To disable auter, run auter --disable instead." > "$LOCKFILE" 368 | logit "INFO: auter enabled" 369 | quit 0 370 | fi 371 | 372 | if [[ "$DISABLE" ]] ; then 373 | rm -f "$LOCKFILE" 374 | if [[ -f "$PIDFILE" ]] && ! kill -0 "$(cat $PIDFILE)" &>/dev/null; then 375 | rm -f "$PIDFILE" 376 | logit "INFO: auter disabled and cleared pid file" 377 | else 378 | logit "INFO: auter disabled" 379 | fi 380 | quit 0 381 | fi 382 | 383 | if [[ ! -f "$LOCKFILE" ]]; then 384 | logit "WARNING: auter disabled. Please run auter --enable to enable automatic updates." 385 | quit 4 386 | fi 387 | 388 | # PID file checking to make sure multiple copies of auter don't run at once. 389 | PIDDIR=$(dirname "$PIDFILE") 390 | if [[ ! -d "$PIDDIR" ]]; then 391 | install -m 755 -o root -g root -d "$PIDDIR" 392 | fi 393 | 394 | # Note: ALL script exits after this block must use the quit() function instead so the PIDfile is cleaned up. 395 | if [[ -f "$PIDFILE" ]]; then 396 | logit "ERROR: auter is already running or $PIDFILE exists." 397 | quit 6 398 | else 399 | echo "$$" > "$PIDFILE" 400 | fi 401 | 402 | read_config 403 | 404 | # CONFIGSET needs to be set if we're using a custom configuration file. 405 | if [[ -z "$CONFIGSET" ]]; then 406 | logit "ERROR: You must specify the CONFIGSET variable in custom config file $CONFIGFILE to avoid naming collisions" 407 | quit 5 408 | fi 409 | 410 | if [[ "$ONLYINSTALLFROMPREP" == "yes" ]]; then 411 | if [[ ! -d "$DOWNLOADDIR/$CONFIGSET" ]]; then 412 | install -m 755 -o root -g root -d "$DOWNLOADDIR/$CONFIGSET" 413 | elif [[ $(stat -c %G%U%a "$DOWNLOADDIR") != rootroot[0-9][0-9][0145] ]]; then 414 | logit "ERROR: $DOWNLOADDIR/$CONFIGSET does not have the correct permissions." 415 | quit 3 416 | fi 417 | fi 418 | 419 | # Validate the SKIPPHASESCRIPTS values 420 | if [[ -n "${SKIPPHASESCRIPTS[*]}" ]]; then 421 | for _skipphasescript in "${SKIPPHASESCRIPTS[@]}"; do 422 | if [[ "$_skipphasescript" =~ ^(pre|post)-(prep|apply|reboot)$ ]]; then 423 | logit "INFO: The --skip-scripts-by-phase argument was used. Skipping $_skipphasescript scripts" 424 | else 425 | logit "ERROR: The --skip-scripts-by-phase argument was used with an invalid option: '$_skipphasescript'. Exiting" 426 | quit 1 427 | fi 428 | done 429 | fi 430 | 431 | logit "INFO: Running with: $0 $ARGS" 432 | 433 | # If --maxdelay is set on the command line, override the config file. 434 | if [[ -n "$__MAXDELAY" ]] 435 | then 436 | MAXDELAY="$__MAXDELAY" 437 | logit "INFO: Overriding MAXDELAY from command line: --maxdelay=$__MAXDELAY" 438 | else 439 | tty -s && MAXDELAY=1 && logit "INFO: Running in an interactive shell, disabling all random sleeps" 440 | fi 441 | [[ "$MAXDELAY" -lt 1 ]] && MAXDELAY=1 442 | 443 | # There is an explicit quit here to avoid auter automatically running any 444 | # other unexpected functions. 445 | [[ "$POSTREBOOT" ]] && post_reboot && quit 0 446 | 447 | # Source the module for the specific package manager. 448 | . /usr/lib/auter/auter.module 449 | 450 | # The following 3 functions are provided by the previously sourced /usr/lib/auter/auter.module 451 | # Run the package manager specific check for locks 452 | [[ "$PREP" ]] && prepare_updates 453 | [[ "$APPLY" ]] && apply_updates 454 | 455 | [[ $REBOOTCALL -eq 1 ]] && reboot_server 456 | 457 | quit 0 458 | -------------------------------------------------------------------------------- /auter.aptModule: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This is a script that is intended to only be called by /usr/bin/auter and 3 | # contains linux package manager specific code for auter. 4 | 5 | # This is the apt-get version of this script intended for Ubuntu/Debian 6 | 7 | # Exit if this script is executed directly 8 | if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then 9 | echo "ERROR: This script is used by auter and should not be executed directly. Exiting" 10 | fi 11 | 12 | function check_package_manager_lock() { 13 | # Set default values if the variables are undefined 14 | [[ $PACKAGEMANAGERLOCKRETRIES ]] || PACKAGEMANAGERLOCKRETRIES=5 15 | [[ $PACKAGEMANAGERLOCKWAITTIME ]] || PACKAGEMANAGERLOCKWAITTIME=60 16 | 17 | # This is a place holder function pending code for a check for apt-get locks 18 | } 19 | 20 | function prepare_updates() { 21 | # Run any pre-prep scripts 22 | for _script in "$PREPREPSCRIPTDIR"/*; do 23 | run_script "$_script" "Pre-Prep" 24 | done 25 | 26 | prepoutput="$(date '+%F %T')\\n" 27 | if [[ "$PREDOWNLOADUPDATES" == "yes" ]]; then 28 | if [[ $(man "$PACKAGE_MANAGER" | grep -c download-only) -gt 0 ]]; then 29 | $PACKAGE_MANAGER update &>/dev/null 30 | # Check if there are any errors when checking for updates 31 | local error_count available_package_count 32 | 33 | 34 | error_count=$("$PACKAGE_MANAGER" -u upgrade --assume-no "${PACKAGEMANAGEROPTIONS[@]}" | grep -c '^[WE]:') 35 | available_package_count=$("$PACKAGE_MANAGER" -u upgrade --assume-no "${PACKAGEMANAGEROPTIONS[@]}" | awk '/upgraded,.*newly installed,/ {sum=$1+$3} END {print sum}') 36 | 37 | if [[ $error_count -eq 0 ]]; then 38 | # If there are packages to be installed then download them. 39 | if [[ "$available_package_count" -gt 0 ]]; then 40 | sleep_delay=$((RANDOM % MAXDELAY)) 41 | [[ $sleep_delay -gt 1 ]] && logit "INFO: Sleeping for $sleep_delay seconds" 42 | sleep $sleep_delay 43 | if [[ "$ONLYINSTALLFROMPREP" == "yes" ]]; then 44 | [[ -d "$DOWNLOADDIR/$CONFIGSET" ]] || mkdir -p "$DOWNLOADDIR/$CONFIGSET" 45 | downloadoption=("-o" "dir::cache::archives=$DOWNLOADDIR/$CONFIGSET") 46 | rm -f "$DOWNLOADDIR/$CONFIGSET"/*.deb 47 | DOWNLOADLOGMSG=" to $DOWNLOADDIR/$CONFIGSET" 48 | fi 49 | declare -x debian_frontend=noninteractive 50 | prepoutput=$("$PACKAGE_MANAGER" "${PACKAGEMANAGEROPTIONS[@]}" "${downloadoption[@]}" --download-only dist-upgrade -y 2>&1) 51 | if [[ $(echo "$prepoutput" | grep -c '^[WE]:') -gt 0 ]]; then 52 | logit "ERROR: There were errors returned by \`$PACKAGE_MANAGER ${PACKAGEMANAGEROPTIONS[*]} ${downloadoption[*]} --download-only dist-upgrade -y\`. Exiting." 53 | prepoutput+="\\nSTATUS:FAILED:Errors returned by package manager" 54 | else 55 | logit "INFO: Updates downloaded$DOWNLOADLOGMSG" 56 | prepoutput+="\\nSTATUS:SUCCESS:Package download complete" 57 | fi 58 | else [[ "$available_package_count" -eq 0 ]] 59 | logit "INFO: No updates are available to be downloaded." 60 | prepoutput+="STATUS:SUCCESS:No updates available" 61 | fi 62 | else 63 | logit "ERROR: There were errors returned by \`$PACKAGE_MANAGER -u upgrade --assume-no ${PACKAGEMANAGEROPTIONS[*]}\`. Exiting." 64 | prepoutput+="\\nSTATUS:FAILED:Errors returned by package manager" 65 | fi 66 | else 67 | if [[ "$ONLYINSTALLFROMPREP" == "yes" ]]; then 68 | logit "ERROR: downloadoption set to 'yes' but the '--downloadonly' option is not available in the current version of $PACKAGE_MANAGER" 69 | quit 3 70 | else 71 | logit "WARNING: downloadonly option is not available" 72 | prepoutput+="\\nSTATUS:Download only not available" 73 | fi 74 | fi 75 | else 76 | prepoutput+=$("$PACKAGE_MANAGER" "${PACKAGEMANAGEROPTIONS[@]}" -s dist-upgrade 2>&1) 77 | fi 78 | rotate_file "$DATADIR/last-prep-output-$CONFIGSET" 79 | [[ "$prepoutput" ]] && echo -e "$prepoutput" > "$DATADIR/last-prep-output-$CONFIGSET" 80 | 81 | # Run any post-prep scripts 82 | for _script in "$POSTPREPSCRIPTDIR"/*; do 83 | run_script "$_script" "Post-Prep" 84 | done 85 | 86 | } 87 | 88 | function apply_updates() { 89 | # Prevent dialog box as we are not running the update in interactive mode 90 | declare -x debian_frontend=noninteractive 91 | 92 | applyoutput="$(date '+%F %T')\\n" 93 | # Set the list of debs to be installed 94 | if [[ "$ONLYINSTALLFROMPREP" == "yes" ]]; then 95 | local available_packages available_package_count error_count 96 | 97 | if [[ $(find "$DOWNLOADDIR/$CONFIGSET" -name "*.deb" | wc -l) -gt 0 ]]; then 98 | available_packages=$("$PACKAGE_MANAGER" -u --just-print install --assume-no "${PACKAGEMANAGEROPTIONS[@]}" "${DOWNLOADDIR}/${CONFIGSET}"/*.deb 2>&1) 99 | echo "$available_packages" >"$DATADIR/last-apply-output-$CONFIGSET" 100 | available_package_count=$(echo "$available_packages" | awk '/upgraded,.*newly installed,/ {sum=$1+$3} END {print sum}') 101 | debs=("$DOWNLOADDIR/$CONFIGSET/"*.deb) 102 | else 103 | available_package_count=0 104 | fi 105 | 106 | # When passing DEBs to apt-get, the update verb won't install any that aren't already 107 | # installed (i.e. dependencies of other packages). Instead we need to use install. 108 | updateaction="install" 109 | else 110 | available_packages=$($PACKAGE_MANAGER -u upgrade --assume-no "${PACKAGEMANAGEROPTIONS[@]}" 2>&1) 111 | echo "$available_packages" >"$DATADIR/last-apply-output-$CONFIGSET" 112 | error_count=$(echo "$available_packages" | grep -c '^[WE]:') 113 | available_package_count=$(echo "$available_packages" | awk '/upgraded,.*newly installed,/ {sum=$1+$3} END {print sum}') 114 | updateaction="upgrade" 115 | fi 116 | 117 | if [[ $error_count -eq 0 ]]; then 118 | if [[ "$available_package_count" -gt 0 ]]; then 119 | local packages_before packages_after 120 | 121 | # Sleep before running pre-scripts and updates 122 | sleep_delay=$((RANDOM % MAXDELAY)) 123 | [[ $sleep_delay -gt 1 ]] && logit "INFO: Sleeping for $sleep_delay seconds" 124 | sleep $sleep_delay 125 | 126 | for _script in "$PREAPPLYSCRIPTDIR"/*; do 127 | run_script "$_script" "Pre-Apply" 128 | done 129 | 130 | logit "INFO: Applying updates" 131 | packages_before=$(dpkg --list) 132 | 133 | # We don't want to allow the user to interrupt a yum/dnf/apt transaction or Bad Things Happen. 134 | echo "Trying to update" 135 | trap '' SIGINT SIGTERM 136 | if applyoutput=$("$PACKAGE_MANAGER" "$updateaction" "${PACKAGEMANAGEROPTIONS[@]}" -y "${debs[@]}" 2>&1); then 137 | applyoutput+="\\nSTATUS:SUCCESS:Package updates applied" 138 | else 139 | applyoutput+="\\nSTATUS:FAILED:Package updates failed" 140 | fi 141 | rotate_file "$DATADIR/last-apply-output-$CONFIGSET" 142 | echo -e "$applyoutput" &>"$DATADIR/last-apply-output-$CONFIGSET" 143 | default_signal_handling 144 | 145 | packages_after=$(dpkg --list) 146 | 147 | if [[ "$packages_before" == "$packages_after" ]]; then 148 | logit "WARNING: No updates were applied. $(echo "$applyoutput" | grep 'upgraded,.*installed,')" 149 | quit 3 150 | fi 151 | 152 | logit "INFO: Updates complete. You may need to reboot for some updates to take effect" 153 | log_last_run 154 | 155 | for _script in "$POSTAPPLYSCRIPTDIR"/*; do 156 | run_script "$_script" "Post-Apply" 157 | done 158 | 159 | # Excluding this check because the REBOOTCALL variable is used by the main auter script 160 | # shellcheck disable=SC2034 161 | [[ "$AUTOREBOOT" == "yes" ]] && REBOOTCALL=1 162 | 163 | else 164 | logit "INFO: No updates are available to be applied." 165 | applyoutput+="\\nSTATUS:SUCCESS:No updates available" 166 | echo -e "$applyoutput" > "$DATADIR/last-apply-output-$CONFIGSET" 167 | log_last_run 168 | fi 169 | else 170 | logit "ERROR: Exit status $RC returned by \`$PACKAGE_MANAGER -u upgrade --assume-no ${PACKAGEMANAGEROPTIONS[*]}\`. Exiting." 171 | applyoutput+="\\nSTATUS:FAILED:Updates failed with status $RC" 172 | echo -e "$applyoutput" > "$DATADIR/last-apply-output-$CONFIGSET" 173 | quit 3 174 | fi 175 | } 176 | -------------------------------------------------------------------------------- /auter.conf: -------------------------------------------------------------------------------- 1 | # Configuration file for auter 2 | 3 | # Set a tag which will be used for files related to this configuration. 4 | # Log files and postreboot jobs will both be named using this tag. 5 | # IMPORTANT: This auter tag MUST BE UNIQUE on a system. 6 | # Valid options: String with no special characters or spaces 7 | CONFIGSET="default" 8 | 9 | # Set whether server will reboot automatically after --apply. A warning 10 | # message will show for logged in users 2 mins before the reboot. The 11 | # system will only be rebooted if one or more updates were applied 12 | # successfully, and if all of the custom pre-apply, post-apply and pre-reboot 13 | # scripts exit with a non-zero exit code. It also accepts a space separated 14 | # list of packages in standard regexp format. The matching is against package 15 | # name only, e.g. kernel will match only the kernel-X.X.X-X.rpm package. 16 | # Valid Options: yes/no/kernel.* auter zsh 17 | AUTOREBOOT="no" 18 | 19 | # Options to pass to the package manager (yum/dnf/apt) when the update/upgrade 20 | # action is called. For example, this can be used to disable excludes or omit 21 | # packages. If dnf is installed, it will be the preferred package manager. 22 | # Valid Options: Space separated packagemanager options 23 | PACKAGEMANAGEROPTIONS="" 24 | 25 | # If the --downloadonly option is available then updates will be downloaded 26 | # during --prep 27 | # Valid Options: yes/no 28 | PREDOWNLOADUPDATES="yes" 29 | 30 | # Option to only install the downloaded packages. This option should be used 31 | # when getting multiple servers to apply the same update list (eg to keep a 32 | # uat/staging/prod environment in sync), or when you would like to review the 33 | # updates ahead of applying them. Not setting this option will result in auter 34 | # installing the latest available packages at the time apply runs. 35 | # NOTE: The PREDOWNLOADUPDATES must be set to "yes" for this to take effect. 36 | # The directory must be owned by user and group 'root' and must not be writable 37 | # by other. 38 | # Valid Options: yes/no 39 | ONLYINSTALLFROMPREP="no" 40 | 41 | # MAXDELAY is upper limit of a random time to wait before querying repositories. 42 | # This applies to downloading updates (--prep) and installing updates (--apply). 43 | # This is used to stagger load on the repository servers. Default is 44 | # 3600 seconds so yum/dnf waits a random time between 1 and 3600 seconds. 45 | # Value is set in seconds 46 | # Valid Options: Positive Integer 47 | MAXDELAY="3600" 48 | 49 | # If there is a package manager lock then auter can be configured to wait for 50 | # the lock to be released. There are 2 configurables. The wait time is set in 51 | # PACKAGEMANAGERLOCKWAITTIME set in seconds. You can also define how many 52 | # times to attemt before auter exits 53 | PACKAGEMANAGERLOCKWAITTIME="60" 54 | PACKAGEMANAGERLOCKRETRIES="5" 55 | 56 | # Directories containing scripts to execute before and after updates are prepared, 57 | # applied, and before/after a reboot (if applicable). Scripts in these 58 | # directories must be executable (+x) for auter to run them. If any pre/post 59 | # scripts run by auter exit with a non-zero exit code, auter will exit 60 | # immediately before running any further actions. 61 | PREPREPSCRIPTDIR="/etc/auter/pre-prep.d" 62 | POSTPREPSCRIPTDIR="/etc/auter/post-prep.d" 63 | PREAPPLYSCRIPTDIR="/etc/auter/pre-apply.d" 64 | POSTAPPLYSCRIPTDIR="/etc/auter/post-apply.d" 65 | PREREBOOTSCRIPTDIR="/etc/auter/pre-reboot.d" 66 | POSTREBOOTSCRIPTDIR="/etc/auter/post-reboot.d" 67 | -------------------------------------------------------------------------------- /auter.conf.man: -------------------------------------------------------------------------------- 1 | .TH AUTER.CONF "5" "May 2018" "auter 0.11" "File Formats" 2 | .SH NAME 3 | auter.conf \- Config file for auter 4 | .SH SYNOPSIS 5 | /etc/auter/auter.conf 6 | .SH DESCRIPTION 7 | This is the default config file for auter. 8 | .SH OPTIONS 9 | 10 | .TP 11 | .B CONFIGSET 12 | This is the name tag for this config file. Log files, auter generated files and postreboot jobs will be named using this tag. 13 | \fB IMPORTANT:\fR This auter tag MUST BE UNIQUE on a system 14 | \fB Valid options:\fR String with no special characters or spaces 15 | \fB Examples:\fR 16 | CONFIGSET="default" 17 | CONFIGSET="kernelonly" 18 | CONFIGSET="dailyupdate" 19 | 20 | .TP 21 | .B AUTOREBOOT 22 | This controls if auter will automatically reboot the server after updates have been installed. The system will only be rebooted if one or more updates were applied successfully. If you require a reboot even when no updates were applied, leave AUTOREBOOT set to "no" and add the --reboot option to the auter command. 23 | \fB NOTES:\fR 24 | - If yum fails to complete the transaction, auter will exit before the reboot phase is initiated 25 | - If there are no updates available (relevant to this profile) auter will not initiate the reboot 26 | - All relevant pre-apply, post-apply and pre-reboot scripts must return a zero exit code 27 | \fB Valid options:\fR yes/no 28 | 29 | .TP 30 | .B PACKAGEMANAGEROPTIONS 31 | Options to pass to the (relevant) package manager (yum/dnf/apt) when the update/upgrade action is called. If dnf is installed, it will be the preferred package manager. 32 | \fB Valid options:\fR Space separated package manager options 33 | \fB Examples:\fR 34 | PACKAGEMANAGEROPTIONS="--disableexcludes=all" 35 | PACKAGEMANAGEROPTIONS="--disablerepo=epel" 36 | PACKAGEMANAGEROPTIONS="--enablerepo=epel --exclude=auter*" 37 | 38 | .TP 39 | .B PREDOWNLOADUPDATES 40 | If the --downloadonly option is available then updates will be downloaded during the --prep prep stage 41 | \fB NOTES:\fR 42 | - This is only useful if you run the prep and apply phase's are run at different times. 43 | - If you intend to use ONLYINSTALLFROMPREP this needs to be set to yes 44 | \fB Valid options:\fR yes/no 45 | 46 | .TP 47 | .B ONLYINSTALLFROMPREP 48 | Option to only install the downloaded packages. This option should be used when getting multiple servers to apply the same update list (eg to keep a uat/staging/prod environment in sync), or when you would like to review the updates ahead of applying them. Not setting this option will result in auter installing the latest available packages at the time apply runs. 49 | \fB NOTES:\fR 50 | - The PREDOWNLOADUPDATES must be set to "yes" for this to take effect. 51 | - The directory must be owned by user and group 'root' and must not be writable by other. 52 | \fB Valid options:\fR yes/no 53 | 54 | .TP 55 | .B MAXDELAY 56 | MAXDELAY is upper limit of a random time to wait before querying repositories. This applies to downloading updates (--prep) and installing updates (--apply). This is used to stagger load on the repository servers. Default is 3600 seconds so yum/dnf waits a random time between 1 and 3600 seconds. 57 | \fB NOTES:\fR 58 | - Value is set in seconds 59 | - This delay is ignored when auter is manually executed 60 | \fB Valid options:\fR 1-3600 61 | \fB Default:\fR 3600 62 | \fB Examples:\fR 63 | MAXDELAY=60 64 | 65 | .TP 66 | .B PACKAGEMANAGERLOCKWAITTIME 67 | If there is a package manager lock then auter can be configured to wait for the lock to be released. PACKAGEMANAGERLOCKWAITTIME is the amount of time that auter will wait before retrying the update. 68 | \fB Valid options:\fR Any number greater than 1 69 | \fB Default:\fR 60 70 | 71 | .TP 72 | .B PACKAGEMANAGERLOCKRETRIES 73 | If there is a package manager lock then auter can be configured to wait for the lock to be released. PACKAGEMANAGERLOCKRETRIES is the amount of retry attempts that will be made. 74 | \fB Valid options:\fR Any number greater than 1 75 | \fB Default:\fR 5 76 | 77 | 78 | .TP 79 | .B Custom script hooks 80 | These are the options for setting directories containing scripts to execute before and after updates are prepared, applied, and before/after a reboot (if applicable). 81 | \fB NOTES:\fR 82 | - If you are using multiple auter profiles which require different scripts to be executed, this is where you should specify the script directories. 83 | - Scripts in these directories must be executable (+x) for auter to run them 84 | - If any scripts run by auter exit with a non-zero exit code, auter will exit immediately before running any further actions 85 | 86 | .B PREPREPSCRIPTDIR 87 | \fB Default:\fR /etc/auter/pre-prep.d 88 | 89 | .B POSTPREPSCRIPTDIR 90 | \fB Default:\fR /etc/auter/post-prep.d 91 | 92 | .B PREAPPLYSCRIPTDIR 93 | \fB Default:\fR /etc/auter/pre-apply.d 94 | 95 | .B POSTAPPLYSCRIPTDIR 96 | \fB Default:\fR /etc/auter/post-apply.d 97 | 98 | .B PREREBOOTSCRIPTDIR 99 | \fB Default:\fR /etc/auter/pre-reboot.d 100 | 101 | .B POSTREBOOTSCRIPTDIR 102 | \fB Default:\fR /etc/auter/post-reboot.d 103 | 104 | .SH "REPORTING BUGS" 105 | .TP 106 | Please report any bugs by raising an issue against the github page or email us at: 107 | https://github.com/rackerlabs/auter/issues 108 | auter-devel@rackspace.com 109 | 110 | .SH "SEE ALSO" 111 | auter(1), cron(8), crontab(5), yum(8) 112 | -------------------------------------------------------------------------------- /auter.cron: -------------------------------------------------------------------------------- 1 | # Cron schedule for auter 2 | 3 | # Preparing package downloads: 4 | # Example of running the prep stage. This pre-downloads updates to make 5 | # the apply phase quicker 6 | 7 | # On the 2nd of every month 8 | # 30 1 2 * * root /usr/bin/auter --prep 9 | 10 | # Every day at 00:30 11 | # 30 0 * * * root /usr/bin/auter --prep 12 | 13 | # Every Sunday at 22:00 14 | # 0 22 * * Sun root /usr/bin/auter --prep 15 | 16 | # First Tuesday of the month 17 | # 15 3 * * Tue root [ $(date +\%d) -le 7 ] && /usr/bin/auter --prep 18 | ######################################## 19 | 20 | # Executing updates: 21 | # Example of executing the updates on the server. This may have a different 22 | # schedule for each server on your infrastructure to avoid overlapping 23 | # downtime on servers which serve the same function, e.g. if you don't want to 24 | # update all your web servers at the same time. 25 | 26 | # On the 14th of every month 27 | # 30 1 14 * * root /usr/bin/auter --apply 28 | 29 | # Another way for first Tuesday 30 | # 30 1 1-7 * * root [ $(date +\%a) = "Tue" ] && /usr/bin/auter --apply 31 | 32 | # Third Sunday 33 | # 30 1 15-21 * * root [ $(date +\%a) = "Sun" ] && /usr/bin/auter --apply 34 | 35 | # Every day at 2.30am 36 | # 30 2 * * * root /usr/bin/auter --apply 37 | ######################################## 38 | 39 | # Example of executing updates when AUTOREBOOT is set to no in /etc/auter/auter.conf. 40 | # In this example the prep is run every Friday, apply is run every Saturday and 41 | # reboot is run every Sunday. Note that the reboot will run the auter pre-reboot 42 | # and post-reboot scripts (See man page for more details). 43 | 44 | # Prep Every Friday at 22:00 45 | # 0 22 * * Fri root /usr/bin/auter --prep 46 | 47 | # Apply Every Saturday at 23:00 48 | # 0 23 * * Sat root /usr/bin/auter --apply 49 | 50 | # Reboot Every Sunday at 10:00 51 | # 0 10 * * Sun root /usr/bin/auter --reboot 52 | ######################################## 53 | 54 | # Running at boot time: 55 | # You can schedule auter to run at boot time using a @reboot cron job. An 56 | # @reboot cron job will run on both a reboot and cold boot. 57 | 58 | # @reboot root /usr/bin/auter --apply 59 | 60 | 61 | # Notes on scheduling: 62 | # - If no cron schedule matches what you need, comment out all cron jobs and create 'at' jobs instead 63 | # - Be aware that for example the third Friday may come before the third Wednesday 64 | -------------------------------------------------------------------------------- /auter.help2man-sections: -------------------------------------------------------------------------------- 1 | [Directories] 2 | 3 | Default Directory locations are listed below; custom paths can be specified in the config file. The use of additional, non-default, config files is required when running more than one auter profile. 4 | 5 | .SS 6 | Pre/Post Hooks 7 | 8 | If any scripts in the pre/post hook directories are not executable, they will be skipped by auter and a warning containing the filename will be logged. If any scripts in the pre/post hook directories exit with a non-zero exit code an error will be logged with the filename and exit code, and auter will abort. 9 | 10 | .TP 11 | .I /etc/auter/pre-prep.d 12 | Directory containing scripts to be executed before downloading updates 13 | 14 | .TP 15 | .I /etc/auter/post-prep.d 16 | Directory containing scripts to be executed after downloading updates 17 | 18 | .TP 19 | .I /etc/auter/pre-apply.d 20 | Directory containing scripts to be executed before applying updates 21 | 22 | .TP 23 | .I /etc/auter/post-apply.d 24 | Directory containing scripts to be executed after applying updates 25 | 26 | .TP 27 | .I /etc/auter/pre-reboot.d 28 | Directory containing scripts to be executed before rebooting 29 | 30 | .TP 31 | .I /etc/auter/post-reboot.d 32 | Directory containing scripts to be executed after rebooting 33 | 34 | There is a hard coded delay of 5 minutes post-reboot to allow the system to become fully ready before the post-reboot scripts are executed. 35 | 36 | .SS Hook Execution Order 37 | 38 | Files in the pre/post script directories are parsed in sorted lexical order. That is, /etc/auter/pre-apply.d/01_first will be parsed before /etc/auter/pre-apply.d/10_second. Be aware that because the sorting is lexical, not numeric, /etc/auter/pre-apply.d/1_whoops would be loaded after /etc/pre-apply.d/10_second. Using a consistent number of leading zeroes in the file names can be used to avoid such problems. For example the following execution order would occur for pre-apply scripts: 39 | 40 | .RS 41 | .nf 42 | pre-apply.d/00-first.sh 43 | pre-apply.d/99-second.sh 44 | pre-apply.d/AA-fourth.sh 45 | pre-apply.d/aa-third.sh 46 | pre-apply.d/zz-fifth.sh 47 | pre-apply.d/ZZ-sixth.sh 48 | .RE 49 | 50 | Note that the ordering is case-insensitive so AA-fourth.sh will be run before aa-third.sh. 51 | 52 | [files] 53 | 54 | .TP 55 | .I /etc/auter/auter.conf 56 | Default config file location. Use the --config option to pass an alternative config file to use within /etc/auter. 57 | 58 | .TP 59 | .I /etc/cron.d/auter 60 | The default cron file with examples 61 | 62 | .TP 63 | .I /usr/bin/auter 64 | The main auter script which contains linux generic code 65 | 66 | .TP 67 | .I /usr/lib/auter/auter.module 68 | The auter module for the relevant package manager 69 | 70 | .TP 71 | .I /usr/share/doc/auter-0.11/LICENSE 72 | The Apache License 73 | 74 | .TP 75 | .I /usr/share/doc/auter-0.11/MAINTAINERS.md 76 | A list of maintainers and mailing list 77 | 78 | .TP 79 | .I /usr/share/doc/auter-0.11/NEWS 80 | The NEWS file and changelog for auter 81 | 82 | [Enable/Disable] 83 | 84 | To provide an easy way to switch on/off all auter jobs, use auter --enable or auter --disable to set the lockfile /var/lib/auter/enabled. Auter will check for the presence of this file before running. 85 | 86 | This is also the correct method for cleaning up an unused pidfile. 87 | 88 | [Rebooting] 89 | 90 | Rebooting is an essential part of applying updates, in order to ensure the updated packages are in use. An update to the kernel will always need a reboot. Some application updates, for example Apache, will restart the service. However often libraries are updated, such as openssl, which don't force a reboot of services that use them. Rebooting guarantees that every update is in use by running services. You can either enable AUTOREBOOT=yes in the config file (the default is AUTOREBOOT=no), to reboot after --apply, or you can schedule a separate schedule for --reboot via a second cronjob. A 2 minute warning is emitted before the server is rebooted, and this can be cancelled by a superuser with shutdown -c. 91 | 92 | [Exit Codes] 93 | 94 | 0 = updates successful / no updates available 95 | 3 = issue related to yum / dnf / apt-get command 96 | 4 = auter disabled via lockfile 97 | 5 = script not run as root / DATADIR does not exist / config file does not exist 98 | 6 = auter is already running / PIDFILE exists 99 | 7 = yum, dnf or apt-get was not found 100 | 8 = a pre/post hook exited with a non-zero error code 101 | 102 | [Authors] 103 | .TP 104 | This documentation was primarily written by: 105 | Paolo Gigante 106 | Piers Cornwell 107 | Cameron Beere 108 | 109 | [Reporting Bugs] 110 | .TP 111 | Please report any bugs by raising an issue against the github page or email us at: 112 | https://github.com/rackerlabs/auter/issues 113 | auter-devel@rackspace.com 114 | 115 | [see also] 116 | auter.conf(5), cron(8), crontab(5) 117 | 118 | -------------------------------------------------------------------------------- /auter.spec: -------------------------------------------------------------------------------- 1 | Name: auter 2 | Version: 1.0.0 3 | Release: 2%{?dist} 4 | Summary: Prepare and apply updates 5 | License: ASL 2.0 6 | URL: https://github.com/rackerlabs/%{name} 7 | Source0: https://github.com/rackerlabs/%{name}/archive/%{version}.tar.gz 8 | BuildArch: noarch 9 | BuildRequires: help2man 10 | %if 0%{?fedora} >= 15 || 0%{?rhel} >= 7 11 | BuildRequires: systemd 12 | %endif 13 | Requires: crontabs 14 | %if 0%{?fedora} >= 18 15 | Requires: dnf 16 | %else 17 | Requires: yum 18 | %endif 19 | 20 | %description 21 | auter (optionally) pre-downloads updates and then runs automatically on a 22 | set schedule, optionally rebooting to finish applying the updates. 23 | 24 | %prep 25 | %setup -q 26 | 27 | %build 28 | help2man --section=1 ./auter -N -o auter.man -n "Automatic Update Transaction Execution by Rackspace" --include=auter.help2man-sections 29 | 30 | %install 31 | %if 0%{?fedora} >= 15 || 0%{?rhel} >= 7 32 | mkdir -p %{buildroot}%{_tmpfilesdir} 33 | echo "d %{_rundir}/%{name} 0755 root root -" > %{buildroot}%{_tmpfilesdir}/%{name}.conf 34 | mkdir -p %{buildroot}%{_rundir}/%{name} 35 | touch %{buildroot}%{_rundir}/%{name}/%{name}.pid 36 | %else 37 | mkdir -p %{buildroot}%{_localstatedir}/run/%{name} 38 | touch %{buildroot}%{_localstatedir}/run/%{name}/%{name}.pid 39 | %endif 40 | 41 | install -d -p -m 0755 \ 42 | %{buildroot}%{_sharedstatedir}/%{name} \ 43 | %{buildroot}%{_var}/cache/%{name} \ 44 | %{buildroot}%{_sysconfdir}/%{name}/pre-reboot.d \ 45 | %{buildroot}%{_sysconfdir}/%{name}/post-reboot.d \ 46 | %{buildroot}%{_sysconfdir}/%{name}/pre-apply.d \ 47 | %{buildroot}%{_sysconfdir}/%{name}/post-apply.d \ 48 | %{buildroot}%{_sysconfdir}/%{name}/pre-prep.d \ 49 | %{buildroot}%{_sysconfdir}/%{name}/post-prep.d 50 | 51 | install -D -p -m 0755 %{name} %{buildroot}%{_bindir}/%{name} 52 | install -D -p -m 0644 %{name}.cron %{buildroot}%{_sysconfdir}/cron.d/%{name} 53 | install -D -p -m 0755 %{name}.yumdnfModule %{buildroot}%{_usr}/lib/%{name}/auter.module 54 | install -D -p -m 0644 %{name}.man %{buildroot}%{_mandir}/man1/%{name}.1 55 | install -D -p -m 0644 %{name}.conf.man %{buildroot}%{_mandir}/man5/%{name}.conf.5 56 | install -D -p -m 0644 %{name}.conf %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf 57 | 58 | %post 59 | # If this is the first time install, create the lockfile 60 | if [ $1 -eq 1 ]; then 61 | /usr/bin/auter --enable 62 | fi 63 | exit 0 64 | 65 | %preun 66 | # If this is a complete removal, then remove lockfile 67 | if [ $1 -eq 0 ]; then 68 | /usr/bin/auter --disable 69 | fi 70 | exit 0 71 | 72 | %files 73 | %{!?_licensedir:%global license %doc} 74 | %license LICENSE 75 | %doc README.md 76 | %doc NEWS 77 | %doc MAINTAINERS.md 78 | %{_mandir}/man1/%{name}.1* 79 | %{_mandir}/man5/%{name}.conf.5* 80 | %{_sharedstatedir}/%{name} 81 | %dir %{_sysconfdir}/%{name} 82 | %dir %{_var}/cache/auter 83 | %dir %{_sysconfdir}/%{name}/pre-reboot.d 84 | %dir %{_sysconfdir}/%{name}/post-reboot.d 85 | %dir %{_sysconfdir}/%{name}/pre-apply.d 86 | %dir %{_sysconfdir}/%{name}/post-apply.d 87 | %dir %{_sysconfdir}/%{name}/pre-prep.d 88 | %dir %{_sysconfdir}/%{name}/post-prep.d 89 | %dir %{_usr}/lib/%{name} 90 | %config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf 91 | %config(noreplace) %{_sysconfdir}/cron.d/%{name} 92 | %{_bindir}/%{name} 93 | %{_usr}/lib/%{name}/auter.module 94 | %if 0%{?el6} 95 | %dir %{_localstatedir}/run/%{name}/ 96 | %ghost %{_localstatedir}/run/%{name}/%{name}.pid 97 | %else 98 | %dir %{_rundir}/%{name}/ 99 | %ghost %{_rundir}/%{name}/%{name}.pid 100 | %{_tmpfilesdir}/%{name}.conf 101 | %endif 102 | 103 | %changelog 104 | * Wed Jul 24 2019 Fedora Release Engineering - 1.0.0-2 105 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild 106 | 107 | * Fri Mar 29 2019 Nick Rhodes 1.0.0-1 108 | - #220 Introduce package dependant reboots using AUTOREBOOT option 109 | - #215 Remove rpm and deb package build tests from travis 110 | - #223 Parallelize the travis jobs 111 | - #224/#225 Clean up ShellCheck warnings 112 | 113 | * Tue Mar 05 2019 Nick Rhodes 0.12.3-1 114 | - #214 Log a machine readable status to the last-{prep,apply} output files 115 | 116 | * Tue Feb 12 2019 Nick Rhodes 0.12.2-1 117 | - #207 check for process matching PID file content 118 | 119 | * Thu Jan 31 2019 Fedora Release Engineering - 0.12.1-2 120 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild 121 | 122 | * Tue Dec 11 2018 Nick Rhodes 0.12.1-1 123 | - Add max-delay option to override MAXDLAY via command line 124 | - Redirect stderr to stdout and capture in APPLYOUTPUT variable 125 | 126 | * Thu Jul 12 2018 Paolo Gigante 0.12-1 127 | - Added --skip-all-scripts to skip the executions of all custom scripts 128 | - Added --skip-scripts-by-phase to skip the executions of custom scripts for the specified phase 129 | - Added --skip-scripts-by-phase to skip the executions of custom scripts by name 130 | - Added man page for auter.conf 131 | - Updated auter.aptModule to reflect changes in auter.yumdnfModule 132 | - added no-wall option 133 | - Fix for --status when run as non-root user 134 | - Logs auter output in /var/lib/auter/ when no updates are available 135 | - Minor improvements to rotation of output files in /var/lib/auter/ 136 | 137 | * Fri Mar 16 2018 Nick Rhodes 0.11-5 138 | - Hotfix for the AUTOREBOOT issue 139 | 140 | * Fri Mar 16 2018 Paolo Gigante 0.11-4 141 | - Updated documentation and references to include apt for Ubuntu/debian 142 | - Removed debugging message that was printed during apt update 143 | - Added "Valid Options" in auter.conf 144 | - Added the pre/post prep directories in auter.conf 145 | - Added retention and rotation for last-prep-output and last-apply-output files in /var/lib/auter 146 | - Corrected file permissions for the auter-postreboot cron file 147 | - Added --stdout option to force output to stdout even if there is no active tty 148 | - Added a package manager lock file check before prep and apply functions call the package manager 149 | - Improved checks to confirm prepared patches are still required 150 | - Adjusted some string arguments to arrays for better handling 151 | 152 | * Mon Oct 30 2017 Paolo Gigante 0.10-1 153 | - Added pre and post prep script hooks 154 | - Added a pidfile and process check to --status 155 | - Added a auter success log with a last run timestamp 156 | - Clear pidfile if the process is no longer running when disabling auter 157 | - Added auter.aptModule for ubuntu/debian support 158 | 159 | * Thu Mar 09 2017 Piers Cornwell 0.9-1 160 | - Capture package manager output 161 | - Document the auter --reboot cron job 162 | - Remove last-update file 163 | - Add description text to the lock file 164 | - Add error checking during prep 165 | - Split out package manager specific code 166 | 167 | * Mon Nov 14 2016 Piers Cornwell 0.8-1 168 | - Release version 0.8 169 | - Added ONLYINSTALLFROMPREP option 170 | 171 | * Thu Aug 04 2016 Piers Cornwell 0.7-1 172 | - Release version 0.7 173 | - Updated the .spec file according to Fedora's guidelines 174 | - Moved scriptdir from /var/lib/auter to /etc/auter 175 | - Categorise log messages as INFO, WARNING or ERROR 176 | - Remove pre-built man page 177 | 178 | * Wed Jul 06 2016 Cameron Beere 0.6-1 179 | - Release version 0.6 180 | - Add maintainers file 181 | 182 | * Thu Apr 28 2016 Cameron Beere 0.5-1 183 | - Release version 0.5 184 | - Added transaction ID logging 185 | - Disable random sleepis when running from a tty 186 | - Rename variables to be package manager agnostic 187 | - Add cron examples for @reboot jobs 188 | - Update default auter config file location 189 | - Remove example script files 190 | - Disable cronjobs & enable lockfile on installation 191 | - Switch to using pre/post script directories instead of files 192 | - Add better handling for option parsing 193 | - Added CONFIGSET variable used to distinguish between distinct configs 194 | - Various bugfixes 195 | 196 | * Wed Mar 23 2016 Piers Cornwell 0.4-1 197 | - Release version 0.4 198 | - Support DNF 199 | - Add HACKING.md 200 | - Exit if custom config file doesn't exist 201 | - Change post reboot script to use cron instead of rc.local 202 | - Report if there are no updates at prep time 203 | - Record prep and apply output 204 | - Updated man page 205 | 206 | * Mon Mar 14 2016 Paolo Gigante 0.3-1 207 | - Release version 0.3 208 | - Better defined exit codes 209 | - Added bounds check for MAXDELAY 210 | - Updated documentation with more details about configuration options 211 | - Fixed logging error if downloadonly is not available 212 | 213 | * Thu Mar 10 2016 Piers Cornwell 0.2-1 214 | - Release version 0.2 215 | - Locking 216 | - Trap Ctrl+C during dangerous section 217 | - Add --status flag 218 | - Move reboot script to /etc/rc.d/rc.local 219 | - Add random delay 220 | - Change from sysv service to --enable/--disable 221 | - Added warnings when pre/post hooks exist but are not executable 222 | - Removed yum transaction support 223 | - Added pid locking to prevent multiple instances of auter running at the same time 224 | 225 | * Wed Mar 02 2016 Mike Frost 0.1-1 226 | - Release version 0.1 227 | -------------------------------------------------------------------------------- /auter.yumdnfModule: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This is a script that is intended to only be called by /usr/bin/auter and 3 | # contains linux package manager specific code for auter. 4 | 5 | 6 | # This is the yum/dnf version of this script intended for redhat/fedora/CentOS 7 | 8 | # Exit if this script is executed directly 9 | if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then 10 | echo "ERROR: This script is used by auter and should not be executed directly. Exiting" 11 | fi 12 | 13 | function check_package_manager_lock() { 14 | # Set default values if the variables are undefined 15 | [[ "$PACKAGEMANAGERLOCKRETRIES" ]] || PACKAGEMANAGERLOCKRETRIES=5 16 | [[ "$PACKAGEMANAGERLOCKRETRIES" -lt 1 ]] && PACKAGEMANAGERLOCKRETRIES=5 17 | 18 | [[ "$PACKAGEMANAGERLOCKWAITTIME" ]] || PACKAGEMANAGERLOCKWAITTIME=60 19 | [[ "$PACKAGEMANAGERLOCKWAITTIME" -lt 1 ]] && PACKAGEMANAGERLOCKWAITTIME=60 20 | 21 | if [[ -f /var/run/yum.pid ]]; then 22 | if { pgrep --pidfile /var/run/yum.pid &>/dev/null ; } ; then 23 | for _lockcheckattempt in $(seq 1 $((PACKAGEMANAGERLOCKRETRIES + 1))); do 24 | # If this has reached the retry limit configured then abort 25 | if [[ $_lockcheckattempt -eq $((PACKAGEMANAGERLOCKRETRIES + 1)) ]]; then 26 | logit "ERROR: Final attempt to wait for yum to release lock file failed. Aborting auter run" 27 | quit 3 28 | fi 29 | 30 | logit "INFO: /var/run/yum.pid exists and process runnning. Waiting $PACKAGEMANAGERLOCKWAITTIME seconds for yum lock to be released: Attempt $_lockcheckattempt of $PACKAGEMANAGERLOCKRETRIES" 31 | sleep $PACKAGEMANAGERLOCKWAITTIME 32 | { pgrep --pidfile /var/run/yum.pid &>/dev/null ; } || { logit "INFO: yum lock has been released" && break ; } 33 | done 34 | fi 35 | fi 36 | } 37 | 38 | function prepare_updates() { 39 | # Check for yum.lock file 40 | check_package_manager_lock 41 | local prepoutput 42 | local -i rc 43 | 44 | prepoutput="$(date '+%F %T')\\n" 45 | # Run any pre-prep scripts 46 | for _script in "$PREPREPSCRIPTDIR"/*; do 47 | run_script "$_script" "Pre-Prep" 48 | done 49 | 50 | if [[ "$PREDOWNLOADUPDATES" == "yes" ]]; then 51 | if [[ $("$PACKAGE_MANAGER" --help | grep -c downloadonly) -gt 0 ]]; then 52 | # Remove the old rpms from the auter cache 53 | [[ "$ONLYINSTALLFROMPREP" == "yes" ]] && rm -f "$DOWNLOADDIR"/"$CONFIGSET"/*.rpm 54 | 55 | prepoutput+=$("$PACKAGE_MANAGER" check-update "${PACKAGEMANAGEROPTIONS[@]}" 2>&1) 56 | rc=$? 57 | 58 | # If check-update has an exit code of 100, updates are available. 59 | if [[ "$rc" -eq 100 ]]; then 60 | # Run any pre-prep scripts 61 | sleep_delay=$((RANDOM % MAXDELAY)) 62 | [[ $sleep_delay -gt 1 ]] && logit "INFO: Sleeping for $sleep_delay seconds" 63 | sleep $sleep_delay 64 | if [[ "$ONLYINSTALLFROMPREP" == "yes" ]]; then 65 | downloadoption=("--downloaddir=$DOWNLOADDIR/$CONFIGSET") 66 | 67 | # DNF doesn't support downloaddir, so instead we download to the default 68 | # location and copy out the way 69 | if [[ "$PACKAGE_MANAGER" == "dnf" ]]; then 70 | find /var/cache/dnf -name "*.rpm" -exec rm -f {} \; 71 | downloadoption=() 72 | fi 73 | DOWNLOADLOGMSG=" to $DOWNLOADDIR/$CONFIGSET" 74 | fi 75 | 76 | 77 | if prepoutput+="$("$PACKAGE_MANAGER" "${PACKAGEMANAGEROPTIONS[@]}" "${downloadoption[@]}" update --downloadonly -y 2>&1)"; then 78 | if [[ "$ONLYINSTALLFROMPREP" == "yes" && "$PACKAGE_MANAGER" == "dnf" ]]; then 79 | find /var/cache/dnf -name "*.rpm" -exec mv {} "$DOWNLOADDIR/$CONFIGSET" \; 80 | fi 81 | logit "INFO: Updates downloaded$DOWNLOADLOGMSG" 82 | read -d '\n' -ra pkglist <<< "$(awk '/Package.*Arch/,/Transaction Summary/{if ($2 ~ /x86|686|noarch/) print $1}' <<< "$prepoutput")" 83 | prepoutput+="\\nSTATUS:SUCCESS:Package download complete:${#pkglist[@]}" 84 | else 85 | logit "ERROR: Updates could not be pre-downloaded$DOWNLOADLOGMSG. See the $DATADIR/last-prep-output-$CONFIGSET file for details." 86 | prepoutput+="\\nSTATUS:FAILED:Package download failed" 87 | fi 88 | 89 | elif [[ "$rc" -eq 1 ]]; then 90 | logit "ERROR: Exit status $rc returned by \`$PACKAGE_MANAGER ${PACKAGEMANAGEROPTIONS[*]} ${downloadoption[*]} update --downloadonly -y\`. Exiting." 91 | prepoutput+="\\nSTATUS:FAILED:Yum failed with status $rc" 92 | else 93 | logit "INFO: No updates are available to be downloaded." 94 | prepoutput+="\\nSTATUS:SUCCESS:No updates available" 95 | fi 96 | else 97 | if [[ "${ONLYINSTALLFROMPREP}" == "yes" ]]; then 98 | logit "ERROR: DOWNLOADOPTION set to 'yes' but the '--downloadonly' option is not available in the current version of $PACKAGE_MANAGER" 99 | quit 3 100 | else 101 | logit "WARNING: downloadonly option is not available" 102 | prepoutput+="\\nSTATUS:FAILED:Download only not available" 103 | fi 104 | fi 105 | else 106 | prepoutput+="$("$PACKAGE_MANAGER" "${PACKAGEMANAGEROPTIONS[@]}" check-update 2>&1)" 107 | fi 108 | rotate_file "$DATADIR/last-prep-output-$CONFIGSET" 109 | [[ "$prepoutput" ]] && echo -e "$prepoutput" > "$DATADIR/last-prep-output-$CONFIGSET" 110 | 111 | # Run any post-prep scripts 112 | for _script in "$POSTPREPSCRIPTDIR"/*; do 113 | run_script "$_script" "Post-Prep" 114 | done 115 | } 116 | 117 | function apply_updates() { 118 | # Default rc to 0 which means no updates are available 119 | local -i rc=0 120 | local applyoutput updateaction history_before history_after transactionid 121 | 122 | applyoutput="$(date '+%F %T')\\n" 123 | # Set the list of rpms to be installed 124 | if [[ "$ONLYINSTALLFROMPREP" == "yes" ]]; then 125 | # Check if there are updates staged for this configset 126 | if [[ $(find "$DOWNLOADDIR/$CONFIGSET" -name "*.rpm" | wc -l) -gt 0 ]]; then 127 | local rpms=() 128 | # Check if the updates staged are still required 129 | for _package in "$DOWNLOADDIR/$CONFIGSET"/*.rpm; do 130 | # Check if the local rpm is an update to the installed package and if it is then add the file to rpms 131 | # list. We are using rpm directly for this check because "yum check-update" does not support local rpm 132 | # files. This also avoids connecting to the repos. And finally this avoids an issue when updating the 133 | # kernel to the same version. 134 | rpm -U --nodeps --test "$_package" &>/dev/null && rpms+=("$_package") 135 | done 136 | 137 | # We are manually setting the rc to 100 if there are updates to be applied. This is the return code that 138 | # yum check-update would return if there were outstanding updates 139 | [[ "${rpms[*]}" ]] && rc=100 140 | fi 141 | # When passing RPM files to dnf/yum, the update verb won't install any that aren't already 142 | # installed (i.e. dependencies of other packages). Instead we need to use install. 143 | updateaction="install" 144 | else 145 | applyoutput+=$("$PACKAGE_MANAGER" check-update "${PACKAGEMANAGEROPTIONS[@]}" 2>&1) 146 | rc=$? 147 | updateaction="update" 148 | fi 149 | 150 | # If check-update has an exit code of 100, updates are available. 151 | if [[ "$rc" -eq 100 ]]; then 152 | # Sleep before continuing with pre-scripts and updates 153 | sleep_delay=$((RANDOM % MAXDELAY)) 154 | [[ $sleep_delay -gt 1 ]] && logit "INFO: Sleeping for $sleep_delay seconds" 155 | sleep $sleep_delay 156 | 157 | # Check for yum.lock file 158 | check_package_manager_lock 159 | 160 | for _script in "$PREAPPLYSCRIPTDIR"/*; do 161 | run_script "$_script" "Pre-Apply" 162 | done 163 | 164 | logit "INFO: Applying updates" 165 | history_before=$($PACKAGE_MANAGER history list) 166 | 167 | # We don't want to allow the user to interrupt a yum/dnf transaction or Bad Things Happen. 168 | trap '' SIGINT SIGTERM 169 | rotate_file "$DATADIR/last-apply-output-$CONFIGSET" 170 | 171 | if applyoutput+=$("$PACKAGE_MANAGER" "$updateaction" -y "${PACKAGEMANAGEROPTIONS[@]}" "${rpms[@]}" 2>&1); then 172 | read -d '\n' -ra pkglist <<< "$( sed -r 's/^[[:space:]]+//g' <<< "$applyoutput" | awk '/(Updating|Upgrading|Installing)[[:space:]]+/{print $3}')" 173 | applyoutput+="\\nSTATUS:SUCCESS:Package updates applied:${#pkglist[@]}" 174 | else 175 | applyoutput+="\\nSTATUS:FAILED:Package updates failed" 176 | fi 177 | 178 | default_signal_handling 179 | echo -e "$applyoutput" > "$DATADIR/last-apply-output-$CONFIGSET" 180 | 181 | history_after=$($PACKAGE_MANAGER history list) 182 | 183 | for _script in "$POSTAPPLYSCRIPTDIR"/*; do 184 | run_script "$_script" "Post-Apply" 185 | done 186 | 187 | if [[ "$history_before" == "$history_after" ]]; then 188 | logit "ERROR: Updates failed. See the $DATADIR/last-apply-output-$CONFIGSET-$(date +%F) file for details. Exiting." 189 | cp "$DATADIR/last-apply-output-$CONFIGSET" "$DATADIR/last-apply-output-$CONFIGSET-$(date +%F)" 190 | quit 3 191 | fi 192 | 193 | transactionid=$($PACKAGE_MANAGER history info 2>&1 | grep "Transaction ID") 194 | logit "INFO: Updates complete ($PACKAGE_MANAGER $transactionid). You may need to reboot for some updates to take effect" 195 | log_last_run 196 | 197 | # Excluding this check because the REBOOTCALL variable is used by the main auter script 198 | # shellcheck disable=SC2034 199 | if [[ "$AUTOREBOOT" == "yes" ]]; then 200 | REBOOTCALL=1 201 | elif [[ ! "$AUTOREBOOT" == "no" ]]; then 202 | for _pkg in "${pkglist[@]}"; do 203 | if [[ "${_pkg%-*-*}" =~ ^(${AUTOREBOOT// /\|})$ ]]; then 204 | REBOOTCALL=1 205 | break 206 | fi 207 | done 208 | fi 209 | 210 | # Remove the old rpms from the auter cache 211 | [[ "$ONLYINSTALLFROMPREP" == "yes" ]] && rm -f "$DOWNLOADDIR/$CONFIGSET"/*.rpm 212 | 213 | elif [[ "$rc" -eq 0 ]]; then 214 | logit "INFO: No updates are available to be applied." 215 | applyoutput+="\\nSTATUS:SUCCESS:No updates available" 216 | echo -e "$applyoutput" > "$DATADIR/last-apply-output-$CONFIGSET" 217 | log_last_run 218 | else 219 | logit "ERROR: Exit status $rc returned by \`$PACKAGE_MANAGER check-update ${PACKAGEMANAGEROPTIONS[*]}\`. Exiting." 220 | applyoutput+="\\nSTATUS:FAILED:Yum failed with status $rc" 221 | echo -e "$applyoutput" > "$DATADIR/last-apply-output-$CONFIGSET" 222 | quit 3 223 | fi 224 | } 225 | -------------------------------------------------------------------------------- /buildGuide.md: -------------------------------------------------------------------------------- 1 | # Build Guide 2 | *** 3 | This is a walkthrough to manually build the install files for auter. This is not the release process, this is specifically for building the .rpm or .deb files for testing purposes. 4 | 5 | **Better way of generating the files:** 6 | - Install docker-ce on your PC 7 | - from the auter directory execute the following: 8 | - For rpm file: 9 | ```# tests/10-rpmbuild.sh``` 10 | - For deb file (This is only once PR#119 has been merged): 11 | ```# 20-debuild.sh``` 12 | 13 | # Manual steps 14 | **** 15 | **Steps to create a .deb file:** 16 | 1) Build a debian 9 cloud server 17 | 2) Update the system and reboot: 18 | ``` 19 | # apt-get update && apt-get upgrade 20 | # reboot 21 | ``` 22 | 3) Install the required tools: 23 | ``` 24 | # apt-get update && apt install debhelper devscripts build-essential vim dh-make help2man 25 | ``` 26 | 27 | 4) Create a build user and switch to that account 28 | notes: 29 | - "adduser" NOT "useradd" 30 | - Use a valid password 31 | - Other details can be blanks 32 | ``` 33 | # adduser builder 34 | # su - builder 35 | ``` 36 | 5) Clone the auter repo and switch to the required tagged version: 37 | ``` 38 | # git clone git@github.com:rackerlabs/auter.git 39 | # cd auter 40 | # git checkout eg: git checkout 0.11 41 | ``` 42 | 6) Make the sources files 43 | ``` 44 | # make deb 45 | # cd auter- 46 | ``` 47 | 7) build the package: 48 | 7.a) To build an unsigned package: 49 | ``` 50 | # debuild -us -uc 51 | ``` 52 | 7.b) To build a gpg signed package: 53 | ``` 54 | # debuild -S -sa -k$(gpg --list-key --with-colons | awk -F: '/^pub:/ { print $5 }') 55 | ``` 56 | *** 57 | **Steps to create a .rpm file:** 58 | 1) Build a CentOS cloud server (6 or 7) 59 | 60 | 2) Install the required packages: 61 | ``` 62 | yum -y install rpm-build elfutils-libelf rpm-libs rpm-python gcc make help2man sudo 63 | ``` 64 | 3) Add a build user: 65 | ``` 66 | useradd builduser 67 | ``` 68 | 4) Make the build directories: 69 | ``` 70 | mkdir -p /home/builduser/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} 71 | ``` 72 | 5) Create the .macros config: 73 | ``` 74 | echo '%_topdir %(echo $HOME)/rpmbuild' > /home/builduser/.rpmmacros 75 | ``` 76 | 6) Clone the auter repo and switch to the required tagged version: 77 | ``` 78 | # git clone git@github.com:rackerlabs/auter.git 79 | # cd auter 80 | # git checkout eg: git checkout 0.11 81 | ``` 82 | 7) Make the sources files and copy them to /home/builduser: 83 | ``` 84 | make sources 85 | cp auter-*.tar.gz /home/builduser/ 86 | cp auter.spec /home/builduser/rpmbuild/SPECS 87 | chown -R builduser.builduser /home/builduser 88 | ``` 89 | 8) Switch to the builduser account and extract the sources 90 | ``` 91 | su - builduser 92 | cd /home/builduser 93 | ``` 94 | 9) Move the sources into the correct location with the correct sources name 95 | ``` 96 | mv auter*.tar.gz /home/builduser/rpmbuild/SOURCES/$(awk '/Version/ {print $2}' /home/builduser/rpmbuild/SPECS/auter.spec).tar.gz 97 | ``` 98 | 10) Start building the rpm 99 | ``` 100 | cd /home/builduser/rpmbuild/SPECS 101 | rpmbuild -ba auter.spec 102 | ``` 103 | *** 104 | -------------------------------------------------------------------------------- /contrib/README.md: -------------------------------------------------------------------------------- 1 | # Auter contrib directory 2 | 3 | This directory contains useful scripts, config files and/or cron schedules which can be used as examples for more complex and interesting implementations of auter as well as useful scripts which are specifically created for the the auter pre/post functions. 4 | 5 | 6 | # WARNING!!! 7 | 8 | These scripts are not part of the default auter package. These scripts are entirely optional and are provided AS IS without guarantee or of any kind. Entire risk arising out of the use of any contrib scripts and documentation remains with you. With that in mind, please always ensure that you entirely review and understand what the script is doing before implementing. 9 | 10 | While these scripts may be updated, expanded and new scripts added, this is not the primary focus of Auter. 11 | 12 | 13 | # Contributing 14 | 15 | As mentioned before, contrib scripts are not the primary focus of the project however please do feel free to raise feature requests if you have any ideas or better still, fork the auter repo and send us a pull request with your proposed contrib script. 16 | 17 | If you do find any problems with any of the contrib scripts, please raise an issue in this repo with a title prefix of "CONTRIB: ". 18 | 19 | # Guidelines 20 | - This collection of scripts should be generic enough to be used on any server or environment. 21 | - A new directory should be created for each new purpose 22 | - In each directory there MUST have a README.md file created with the layout provided below 23 | - If there are any prerequisites or configuration requirements or options for the scripts to work, they should be documented in the README.md 24 | - Scripts should be explicitly include the interpreter line ie: #!/bin/sh or #!/usr/bin/python 25 | - Scripts should be set as executable before uploading 26 | - Scripts MUST return an exit code of 0 for a successful run. Any exceptions will cause auter to fail 27 | - Script names should contain: 28 | - A prefixed number to be used for execution order 29 | - Either be a brief description or the package that is being called 30 | - Reference to the intended phase 31 | - Optional: The interpreter suffix is not required but may be useful ie: 32 | - 50-notifyAvailablePatches-post-prep 33 | - 01-configsnap.pre-apply 34 | - 50-removeFromAppPool.pre-reboot 35 | - 70-appStartConfirmation.post-reboot.sh 36 | - It is also highly recommended that script names follow the xx-filename (where xx are padded digits) naming convention to ensure correct script execution order. See the man page for more information. 37 | 38 | 39 | # README.md layout template 40 | ``` 41 | # 42 | 43 | 44 | 45 | # Script Details 46 | 47 | Language: 48 | Supported OS: CENTOS>=6 RHEL>=6 Fedora>=26 Ubuntu>=16.04 Debian>=9 49 | Additional setup required: 50 | Dependencies: 51 | 52 | # Description 53 | 54 | Detailed description of the scripts including the purpose of the scripts 55 | 56 | # Pre-requisites and dependencies 57 | 58 | Details of any packages that need to be installed and any config that is required 59 | 60 | # Files and explanations 61 | 62 | This should be a list of files that should be included in the directory and an explanation of what each file does 63 | 64 | # Any additional information or sections 65 | ``` 66 | 67 | # Git Pull Request guidelines 68 | 69 | - Code reviews will be done for all files for all pull requests 70 | - Take this as constructive feedback and do not be discouraged if changes are requested 71 | - Code reviews may take a while however feel free to comment on the PR if you want to give it a nudge 72 | - Code reviews will assess logic, code structure, naming convention and style. Please ensure you are following the google guidelines: https://google.github.io/styleguide/ 73 | -------------------------------------------------------------------------------- /contrib/conditionalReboot/99-conditionalReboot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set static variables 4 | ############################################################################### 5 | # This is a space separated list of applications which will always require a 6 | # reboot if updated. 7 | # Examples: 8 | # APPLIST="" 9 | # APPLIST="kernel*" 10 | # APPLIST="kernel* *lib*" 11 | APPLIST="kernel*" 12 | ############################################################################### 13 | 14 | # Function definitions 15 | ############################################################################### 16 | function logit() { 17 | # If running on a tty, or the --stdout option is provided, print to screen 18 | tty -s && echo "$1" 19 | logger -p info -t auter "$1" 20 | } 21 | ############################################################################### 22 | 23 | # Calculated variables 24 | ############################################################################### 25 | DISTRIBUTION="$(python -c "import platform; print(platform.linux_distribution()[0])")" 26 | REBOOTREQURIRED=() 27 | 28 | function _deleted_file_check() { 29 | if LSOFEXEC="$(PATH=/usr/sbin:/usr/local/sbin:$PATH command -v lsof 2>/dev/null)"; then 30 | LIBCHECK=$(${LSOFEXEC} | grep lib | grep DEL) 31 | [[ -n "${LIBCHECK}" ]] && REBOOTREQURIRED+=("detected deleted libraries") 32 | else 33 | logit "$0 - lsof not found: unable to check running libraries" 34 | fi 35 | } 36 | 37 | function _check_applist() { 38 | local _pkglist=("$@") 39 | 40 | if [[ -n "${APPLIST}" ]]; then 41 | for _packagematch in $APPLIST; do 42 | # Excluding SC2001 for readability purposes 43 | # shellcheck disable=SC2001 44 | _packagematch=$(sed 's/*/.*/g' <<< "$_packagematch") 45 | for _package in "${_pkglist[@]}"; do 46 | if [[ "$_package" =~ $_packagematch ]]; then 47 | REBOOTREQURIRED+=("package $_package was updated and is in the $0 APPLIST config") 48 | fi 49 | done 50 | done 51 | fi 52 | } 53 | 54 | # This is primarily for Debian and Ubuntu 55 | [[ -f /var/run/reboot-required ]] && REBOOTREQURIRED+=("/var/run/reboot-required exists") 56 | 57 | _deleted_file_check 58 | 59 | if [[ "${DISTRIBUTION}" =~ CentOS|Red\ Hat|Fedora|Oracle\ Linux ]]; then 60 | _check_applist "$(awk -F" : " '/Running transaction$/,/Updated:/ {print $2}' /var/lib/auter/last-apply-output-default | awk '{print $1}' | sort -u)" 61 | 62 | 63 | [[ -f /sbin/grubby ]] && GRUBBYEXEC="/sbin/grubby" 64 | [[ -f /usr/sbin/grubby ]] && GRUBBYEXEC="/usr/sbin/grubby" 65 | [[ -n "$GRUBBYEXEC" ]] && DEFKERNELVERSION=$($GRUBBYEXEC --default-kernel | sed 's/^.*vmlinuz-//g') 66 | if [[ -n $DEFKERNELVERSION ]]; then 67 | [[ ! "$DEFKERNELVERSION" == "$(uname -r)" ]] && REBOOTREQURIRED+=("Default kernel ${DEFKERNELVERSION} does not match running kernel $(uname -r)") 68 | fi 69 | 70 | 71 | if [[ -f /usr/bin/needs-restarting ]]; then 72 | if needs-restarting -h | grep -E -q "^[[:space:]]*-r"; then 73 | needs-restarting -r &>/dev/null || REBOOTREQURIRED+=("/usr/bin/needs-restarting -r assessment") 74 | else 75 | [[ $(needs-restarting | wc -l) -gt 0 ]] && REBOOTREQURIRED+=("/usr/bin/needs-restarting assesment") 76 | fi 77 | fi 78 | 79 | elif [[ "$DISTRIBUTION" =~ debian|Ubuntu ]]; then 80 | _check_applist "$(grep "$(date +%Y-%m-%d)" /var/log/dpkg.log | awk '{if ($3=="upgrade" || $3=="install") {print $4}}')" 81 | 82 | else 83 | logit "Distribution not detected by $0. Exiting" 84 | exit 1 85 | fi 86 | 87 | # Reboot the server using auter 88 | if [[ -n "${REBOOTREQURIRED[*]}" ]]; then 89 | logit "$0 assessed that the server needs to be rebooted. The assessments that triggered this requirement are:" 90 | for _rebootmatch in "${REBOOTREQURIRED[@]}"; do 91 | logit "Rebooting because $_rebootmatch" 92 | done 93 | logit "Reboot required, rebooting server after running auter process completes" 94 | # Not valid as PIDFILE has been exported, and will be expanded in subshell 95 | # shellcheck disable=SC2016 96 | (timeout 600 bash -c 'while test -f "$PIDFILE"; do sleep 5; done; auter --reboot') & 97 | else 98 | logit "Reboot not required" 99 | fi 100 | -------------------------------------------------------------------------------- /contrib/conditionalReboot/README.md: -------------------------------------------------------------------------------- 1 | # conditionalReboot.sh 2 | 3 | # Short Description 4 | This script will assess the running distribution and do some basic checks to assess if a reboot is required. The main advantage of using this script is that it will reboot the server using `auter --reboot` which will also run any pre-reboot and post-reboot scripts. 5 | 6 | 7 | # Script Details 8 | 9 | Language: BASH 10 | Supported OS: CENTOS>=6 RHEL>=6 Fedora>=26 Ubuntu>=16.04 Debian>=9 11 | Additional setup required: NO 12 | Dependencies: python, lsof 13 | 14 | 15 | # Description 16 | 17 | 1. This script will assess what Linux distribution is running based on the python "platform" library. 18 | 2. Identify the list of packages that were updated 19 | 3. If OS is RHEL/CentOS/Fedora/Oracle Linux: 20 | * run `/usr/bin/needs-restarting` if it exists 21 | * Compare the running kernel to the default kernel from grub.conf 22 | 3. If OS is Debian/Ubuntu check if file `/var/run/reboot-required` exists 23 | 4. Check if any deleted (updated) libraries have open file handles 24 | 5. Check if any user-defined applications were updated (see APPLIST definition in script) 25 | 6. If a reboot is required, log the reason and start a new process which does the following: 26 | * Check if auter has completed (Check the pidfile) 27 | * Execute `auter --reboot` 28 | 29 | 30 | # Pre-requisites and dependencies 31 | 32 | `/usr/bin/python` must exist. If only python3 is installed on the system, ensure there is a symlink at `/usr/lib/python` 33 | 34 | # Files and explanations 35 | 36 | This script should be put in relevant the post-apply.d directory (default is `/etc/auter/post-apply.d/`). 37 | 38 | 39 | # Any additional information or sections 40 | 41 | None 42 | -------------------------------------------------------------------------------- /contrib/configsnap/01-configsnap-pre: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ -f /usr/sbin/configsnap ]] && /usr/sbin/configsnap --silent -d /root -t auter-configsnap-"$(date +%Y-%m-%d)" -p pre 3 | -------------------------------------------------------------------------------- /contrib/configsnap/50-configsnap-post-apply: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ -f /usr/sbin/configsnap ]] && /usr/sbin/configsnap -d /root -t auter-configsnap-"$(date +%Y-%m-%d)" -p post-apply &> /root/auter-configsnap-"$(date +%Y-%m-%d)"/configsnap/post-apply.compare 3 | -------------------------------------------------------------------------------- /contrib/configsnap/50-configsnap-pre-reboot: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if find /root/auter-configsnap-"$(date +%Y-%m-%d)"/configsnap/ -name "*.pre" &>/dev/null; then 3 | [[ -f /usr/sbin/configsnap ]] && /usr/sbin/configsnap --silent -d /root -t auter-configsnap-"$(date +%Y-%m-%d)" -p pre-reboot 4 | exit 0 5 | else 6 | logger -p info -t "$(basename "$0")" "INFO: Configsnap pre-apply files missing, running $0 for post-reboot diff." 7 | [[ -f /usr/sbin/configsnap ]] && /usr/sbin/configsnap --silent -d /root -t auter-configsnap-"$(date +%Y-%m-%d)" -p pre 8 | exit 0 9 | fi 10 | 11 | -------------------------------------------------------------------------------- /contrib/configsnap/99-configsnap-post-reboot: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ -f /usr/sbin/configsnap ]] && /usr/sbin/configsnap -d /root -t auter-configsnap-"$(date +%Y-%m-%d)" -p post-reboot &> /root/auter-configsnap-"$(date +%Y-%m-%d)"/configsnap/post-reboot.compare 3 | -------------------------------------------------------------------------------- /contrib/configsnap/README.md: -------------------------------------------------------------------------------- 1 | # configsnap 2 | 3 | Run configsnap at each major phase of auter. Configsnap records useful system state information and creates backups of some configuration files. These can be compared at different points in time based on tags. 4 | 5 | # Script Details 6 | 7 | Language: bash 8 | 9 | Supported OS: CENTOS>=6 RHEL>=6 Fedora>=26 10 | 11 | Additional setup required: YES 12 | 13 | Dependencies: configsnap 14 | 15 | # Description 16 | 17 | This set of scripts will run configsnap at the following points: 18 | - pre-apply 19 | - post-apply 20 | - pre-reboot 21 | - post-reboot 22 | 23 | Note that the pre-reboot script is only useful when the AUTOREBOOT=no 24 | 25 | # Pre-requisites and dependencies 26 | 27 | - configsnap needs to be installed: 28 | ``` 29 | yum install epel-release 30 | yum install configsnap 31 | ``` 32 | 33 | # Files and explanations 34 | 35 | - /etc/auter/pre-apply.d/01-configsnap-pre : Captured data before the server is updated 36 | - /etc/auter/post-apply.d/50-configsnap-post-apply : Captured data after the server has been updated 37 | - /etc/auter/pre-reboot.d/50-configsnap-pre-reboot : Captured data before the server is rebooted 38 | - /etc/auter/post-reboot.d/99-configsnap-post-reboot : Captured data before the server has been rebooted 39 | 40 | # Configsnap options used 41 | 42 | - The base directory for configsnap files has been set to /root 43 | - The tag has been set to auter-configsnap-$(date +%Y-%m-%d) 44 | - Note that this will need to be adjusted if updates span over the change of a day ie: 45 | apply runs at 23:00 46 | reboot runs at 02:00 47 | - The --silent option has specifically NOT been used for all scripts except pre-apply. This is because the output is redirected to .compare files in /root/auter-configsnap-$(date +%Y-%m-%d)/configsnap/ 48 | -------------------------------------------------------------------------------- /debian/auter.install: -------------------------------------------------------------------------------- 1 | auter /usr/sbin 2 | auter.conf /etc/auter 3 | auter.module /usr/lib/auter 4 | -------------------------------------------------------------------------------- /debian/auter.manpages: -------------------------------------------------------------------------------- 1 | auter.1 2 | auter.conf.5 3 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | auter (1.0.0) unstable; urgency=low 2 | 3 | * #220 Introduce package dependent reboots using AUTOREBOOT option 4 | * #215 Remove rpm and deb package build tests from travis 5 | * #223 Parallelize the travis jobs 6 | * #224/#225 Clean up ShellCheck warnings 7 | 8 | -- Nick Rhodes Fri, 29 Mar 2019 16:00:22 +0000 9 | 10 | auter (0.12.3) unstable; urgency=low 11 | 12 | * #214 Log a machine readable status to the last-{prep,apply} output files 13 | 14 | -- Nick Rhodes Tue, 05 Mar 2019 15:51:29 +0000 15 | 16 | auter (0.12.2) unstable; urgency=low 17 | 18 | * #207 check for process matching PID file content 19 | 20 | -- Nick Rhodes Tue, 12 Feb 2019 14:19:01 +0000 21 | 22 | auter (0.12.1) unstable; urgency=low 23 | 24 | * Add max-delay option to override MAXDLAY via command line 25 | * Redirect stderr to stdout and capture in APPLYOUTPUT variable 26 | 27 | -- Nick Rhodes Tue, 11 Dec 2018 15:02:24 +0000 28 | 29 | auter (0.12.0) unstable; urgency=low 30 | 31 | * Added --skip-all-scripts to skip the executions of all custom scripts 32 | * Added --skip-scripts-by-phase to skip the executions of custom scripts for the specified phase 33 | * Added --skip-scripts-by-phase to skip the executions of custom scripts by name 34 | * Added man page for auter.conf 35 | * Updated auter.aptModule to reflect changes in auter.yumdnfModule 36 | * added no-wall option 37 | * Fix for --status when run as non-root user 38 | * Logs auter output in /var/lib/auter/ when no updates are available 39 | * Minor improvements to rotation of output files in /var/lib/auter/ 40 | 41 | -- Paolo Gigante Thu, 12 Jul 2018 16:07:45 +0000 42 | 43 | auter (0.11.1) unstable; urgency=low 44 | 45 | * Hotfix for the AUTOREBOOT issue 46 | 47 | -- Nick Rhodes Fri, 16 Mar 2018 14:24:53 +0000 48 | 49 | auter (0.11.0) unstable; urgency=low 50 | 51 | * Updated documentation and references to include apt for Ubuntu/debian 52 | * Removed debugging message that was printed during apt update 53 | * Added "Valid Options" in auter.conf 54 | * Added the pre/post prep directories in auter.conf 55 | * Added retention and rotation for last-prep-output and last-apply-output files in /var/lib/auter 56 | * Corrected file permissions for the auter-postreboot cron file 57 | * Added --stdout option to force output to stdout even if there is no active tty 58 | * Added a package manager lock file check before prep and apply functions call the package manager 59 | * Improved checks to confirm prepared patches are still required 60 | * Adjusted some string arguments to arrays for better handling 61 | 62 | -- Paolo Gigante Fri, 16 Mar 2018 16:11:04 +0000 63 | 64 | auter (0.10.0) unstable; urgency=low 65 | 66 | * Added pre and post prep script hooks 67 | * Added a pidfile and process check to --status 68 | * Added a auter success log with a last run timestamp 69 | * Clear pidfile if the process is no longer running when disabling auter 70 | * Added auter.aptModule for ubuntu/debian support 71 | 72 | -- Paolo Gigante Mon, 30 Oct 2017 15:19:10 +0000 73 | 74 | auter (0.9.0) unstable; urgency=low 75 | 76 | * Capture package manager output 77 | * Document the auter --reboot cron job 78 | * Remove last-update file 79 | * Add description text to the lock file 80 | * Add error checking during prep 81 | * Split out package manager specific code 82 | 83 | -- Piers Cornwell Thu, 09 Mar 2017 16:14:41 +0000 84 | 85 | auter (0.8.0) unstable; urgency=low 86 | 87 | * Release version 0.8 88 | * Added ONLYINSTALLFROMPREP option 89 | 90 | -- Piers Cornwell Mon, 14 Nov 2016 15:38:50 +0000 91 | 92 | auter (0.7.0) unstable; urgency=low 93 | 94 | * Release version 0.7 95 | * Moved scriptdir from /var/lib/auter to /etc/auter 96 | * Categorise log messages as INFO, WARNING or ERROR 97 | * Remove pre-built man page 98 | 99 | -- Piers Cornwell Thu, 04 Aug 2016 15:18:02 +0000 100 | 101 | auter (0.6.0) unstable; urgency=low 102 | 103 | * Release version 0.6 104 | * Add maintainers file 105 | 106 | -- Cameron Beere Wed, 06 Jul 2016 14:08:19 +0000 107 | 108 | auter (0.5.0) unstable; urgency=low 109 | 110 | * Release version 0.5 111 | * Added transaction ID logging 112 | * Disable random sleepis when running from a tty 113 | * Rename variables to be package manager agnostic 114 | * Add cron examples for @reboot jobs 115 | * Update default auter config file location 116 | * Remove example script files 117 | * Disable cronjobs & enable lockfile on installation 118 | * Switch to using pre/post script directories instead of files 119 | * Add better handling for option parsing 120 | * Added CONFIGSET variable used to distinguish between distinct configs 121 | * Various bugfixes 122 | 123 | -- Cameron Beere Thu, 28 Apr 2016 15:55:47 +0000 124 | 125 | auter (0.4.0) unstable; urgency=low 126 | 127 | * Release version 0.4 128 | * Support DNF 129 | * Add HACKING.md 130 | * Exit if custom config file doesn't exist 131 | * Change post reboot script to use cron instead of rc.local 132 | * Report if there are no updates at prep time 133 | * Record prep and apply output 134 | * Updated man page 135 | 136 | -- Piers Cornwell Wed, 23 Mar 2016 14:11:08 +0000 137 | 138 | auter (0.3.0) unstable; urgency=low 139 | 140 | * Release version 0.3 141 | * Better defined exit codes 142 | * Added bounds check for MAXDELAY 143 | * Updated documentation with more details about configuration options 144 | * Fixed logging error if downloadonly is not available 145 | 146 | -- Paolo Gigante Mon, 14 Mar 2016 16:03:16 +0000 147 | 148 | auter (0.2.0) unstable; urgency=low 149 | 150 | * Release version 0.2 151 | * Locking 152 | * Trap Ctrl+C during dangerous section 153 | * Add --status flag 154 | * Move reboot script to /etc/rc.d/rc.local 155 | * Add random delay 156 | * Change from sysv service to --enable/--disable 157 | * Added warnings when pre/post hooks exist but are not executable 158 | * Removed yum transaction support 159 | * Added pid locking to prevent multiple instances of auter running at the same time 160 | 161 | -- Piers Cornwell Thu, 10 Mar 2016 14:54:42 +0000 162 | 163 | auter (0.1.0) unstable; urgency=low 164 | 165 | * Release version 0.1 166 | 167 | -- Mike Frost Wed, 02 Mar 2016 15:04:49 +0000 168 | 169 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: auter 2 | Maintainer: Paolo Gigante 3 | Section: misc 4 | Priority: optional 5 | Standards-Version: 3.9.7 6 | Build-Depends: debhelper (>= 9) 7 | 8 | Package: auter 9 | Architecture: all 10 | Depends: ${shlibs:Depends}, ${misc:Depends} 11 | Description: Automatic updates for Redhat and Debian based Linux servers 12 | Auter is an update tool that can manage scheduled automatic updates using cron. 13 | Auter has the ability to run pre/post script hooks, pre-download packages and 14 | reboot after updates have been applied. 15 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: auter 3 | Source: https://github.com/rackerlabs/auter 4 | 5 | Files: * 6 | Copyright: 2016 Rackspace, Inc. 7 | License: Apache 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | . 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | . 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | . 20 | On Debian systems, the complete text of the Apache License 2.0 can 21 | be found in "/usr/share/common-licenses/Apache-2.0" 22 | -------------------------------------------------------------------------------- /debian/dirs: -------------------------------------------------------------------------------- 1 | /var/lib/auter 2 | /etc/auter/pre-prep.d 3 | /etc/auter/post-prep.d 4 | /etc/auter/pre-apply.d 5 | /etc/auter/post-apply.d 6 | /etc/auter/pre-reboot.d 7 | /etc/auter/post-reboot.d 8 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | # If this is the first time install, create the lockfile 2 | /usr/sbin/auter --enable 3 | #DEBHELPER# 4 | -------------------------------------------------------------------------------- /debian/prerm: -------------------------------------------------------------------------------- 1 | /usr/sbin/auter --disable 2 | #DEBHELPER# 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | override_dh_auto_build: 4 | /usr/bin/help2man --section=1 ./auter -N -o auter.1 -n "Automatic Update Transaction Execution by Rackspace" --include=auter.help2man-sections 5 | 6 | %: 7 | dh $@ 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/.aspell_auter_dictionary: -------------------------------------------------------------------------------- 1 | personal_ws-1.1 en 189 2 | DOWNLOADLOGMSG 3 | sublicense 4 | Gigante 5 | tmpfilesdir 6 | sed 7 | downloaddir 8 | PACKAGEMANAGERLOCKWAITTIME 9 | debhelper 10 | aptModule 11 | buildroot 12 | Testing 13 | BuildArch 14 | crontabs 15 | cd 16 | removeFromAppPool 17 | epel 18 | VERSIONNUMBER 19 | ar 20 | MERCHANTABILITY 21 | pwd 22 | disableexcludes 23 | SPELLINGMISTAKESCOUNT 24 | dh 25 | auter 26 | cp 27 | config 28 | eg 29 | realpath 30 | el 31 | readonly 32 | dev 33 | fi 34 | DATADIR 35 | POSTREBOOTSCRIPTDIR 36 | LOCKCHECKATTEMPT 37 | eq 38 | awk 39 | authorized 40 | FRONTEND 41 | showvariables 42 | licensable 43 | noarch 44 | PREPREPSCRIPTDIR 45 | devscripts 46 | ie 47 | rundir 48 | configset 49 | PREAPPLYSCRIPTDIR 50 | dir 51 | debuild 52 | Paolo 53 | LASTRUNTIME 54 | UAT 55 | postreboot 56 | datelong 57 | AUTERVARIABLES 58 | changelog 59 | logit 60 | pidfile 61 | PIDfile 62 | dirname 63 | configs 64 | bindir 65 | zsh 66 | cron 67 | Beere 68 | dnf 69 | esac 70 | md 71 | TESTDIR 72 | BuildRequires 73 | sharedstatedir 74 | ne 75 | timestamp 76 | POSTPREPSCRIPTDIR 77 | rootroot 78 | repo 79 | systemd 80 | debian 81 | lt 82 | PREPOUTPUT 83 | ifeq 84 | cronjobs 85 | downloadonly 86 | whoami 87 | ubuntu 88 | mv 89 | filename 90 | xargs 91 | notifyAvailablePatches 92 | rc 93 | chmod 94 | Cornwell 95 | sbin 96 | stdout 97 | lsb 98 | sa 99 | rf 100 | lockfile 101 | TESTVAR 102 | EXITCODE 103 | RHEL 104 | UPDATEACTION 105 | crontab 106 | configsnap 107 | qp 108 | czf 109 | yumdnfModule 110 | endif 111 | contrib 112 | mandir 113 | PIDDIR 114 | distributionrelease 115 | DOWNLOADDIRLASTRUNTIME 116 | customizable 117 | mkdir 118 | uc 119 | PACKAGEMANAGEROPTIONS 120 | cronjob 121 | PREREBOOTSCRIPTDIR 122 | repos 123 | dpkg 124 | localstatedir 125 | ve 126 | su 127 | wc 128 | scriptingterms 129 | openssl 130 | CUSTOMCONFIG 131 | SIGTERM 132 | mins 133 | sysconfdir 134 | elif 135 | ONLYINSTALLFROMPREP 136 | DOWNLOADOPTION 137 | configfile 138 | bugfixes 139 | tty 140 | Ctrl 141 | PREDOWNLOADUPDATES 142 | ws 143 | lintian 144 | maxdepth 145 | REBOOTCALL 146 | usr 147 | SPELLINGMISTAKES 148 | txt 149 | untagged 150 | pid 151 | egrep 152 | sysv 153 | Iseconds 154 | google 155 | RPMs 156 | rpms 157 | Rackspace 158 | eval 159 | MAXDELAY 160 | basefile 161 | AUTOREBOOT 162 | gpg 163 | useradd 164 | CentOSf 165 | scriptdir 166 | yyyy 167 | shellcheck 168 | getopt 169 | SIGINT 170 | configsets 171 | ARGS 172 | adduser 173 | conf 174 | PACKAGEMANAGERLOCKRETRIES 175 | nodeps 176 | zcf 177 | github 178 | Licensor 179 | MAXDELAYONLYINSTALLFROMPREP 180 | buildguide 181 | Makefile 182 | customize 183 | noninteractive 184 | POSTAPPLYSCRIPTDIR 185 | pre 186 | DEBs 187 | aspell 188 | chown 189 | AUTERVERSION 190 | TRANSACTIONID 191 | builduser 192 | sudo 193 | xzf 194 | qq 195 | td 196 | APPLIST 197 | symlink 198 | dailyupdate 199 | disablerepo 200 | kernelonly 201 | uat 202 | CentOS 203 | PRs 204 | showduplicates 205 | uptime 206 | syslog 207 | systemctl 208 | journalctl 209 | libcurl 210 | distros 211 | showpkg 212 | enablerepo 213 | cancelled 214 | linux 215 | aa 216 | rackerlabs 217 | ba 218 | gcc 219 | libelf 220 | rpmbuild 221 | rpmmacros 222 | SRPMS 223 | topdir 224 | walkthrough 225 | libs 226 | elfutils 227 | spellcheck 228 | startup 229 | RHN 230 | CSV 231 | -------------------------------------------------------------------------------- /tests/01-spellcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname "$0")"/_helpers 4 | 5 | run_cmd "Check for aspell" command -v aspell 6 | 7 | EXITCODE=0 8 | 9 | FILELIST=("$AUTERDIR/auter.conf.man") 10 | FILELIST+=("$AUTERDIR/auter.help2man-sections") 11 | FILELIST+=("$AUTERDIR/HACKING.md") 12 | FILELIST+=("$AUTERDIR/README.md") 13 | FILELIST+=("$AUTERDIR/NEWS") 14 | FILELIST+=("$AUTERDIR/buildGuide.md") 15 | FILELIST+=("$AUTERDIR/contrib/README.md") 16 | 17 | for _file in "${FILELIST[@]}"; do 18 | if aspel_out="$(aspell -a --personal="$AUTERDIR"/tests/.aspell_auter_dictionary 2>&1 < "$_file")"; then 19 | spelling_mistakes="$(awk '/^&/{print $2}' <<< "$aspel_out")" 20 | if [[ -n "$spelling_mistakes" ]]; then 21 | log_fail "$_file failed SpellCheck" 22 | EXITCODE=1 23 | sort <<< "$spelling_mistakes" | uniq -c 24 | else 25 | log_success "$_file passed SpellCheck" 26 | fi 27 | 28 | else 29 | log_fail "$aspel_out" 30 | exit 1 31 | fi 32 | 33 | done 34 | exit $EXITCODE 35 | -------------------------------------------------------------------------------- /tests/05-shellcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname "$0")"/_helpers 4 | 5 | EXITCODE=0 6 | 7 | # Create a list of script files to be checked. 8 | readarray -t FILELIST < <(find "$AUTERDIR" -type f -not -path '*/\.*') 9 | 10 | for _file in "${FILELIST[@]}"; do 11 | grep -q '^#!/.*sh' "$_file" && SCRIPTSTOTEST+=("$_file") 12 | done 13 | 14 | # SC2102 is related to https://github.com/koalaman/shellcheck/issues/682. This 15 | # was previously removed from the online checker but still exists in the 16 | # standalone package. 17 | sc_excl=("SC2102") 18 | sc_excl+=("SC1090" "SC1091") # Can't follow source 19 | #sc_excl+=("SC2181") # Check RC directly 20 | 21 | for _script in "${SCRIPTSTOTEST[@]}"; do 22 | if shellcheck_output="$(shellcheck -e "$(IFS=','; echo "${sc_excl[*]}")" "$_script")"; then 23 | log_success "$_script passed ShellCheck" 24 | else 25 | log_fail "$_script failed ShellCheck" 26 | awk '{printf "| %s\n",$0}' <<< "$shellcheck_output" 27 | echo "-----------------------------------------------------------------------" 28 | EXITCODE=1 29 | fi 30 | done 31 | exit $EXITCODE 32 | -------------------------------------------------------------------------------- /tests/10-rpmbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname "$0")"/_helpers 4 | 5 | log_info "AUTERDIR: $AUTERDIR" 6 | log_info "AUTERPARENTDIR: $AUTERPARENTDIR" 7 | log_info "VERSION: $VERSION" 8 | 9 | for RELEASE in 6 7; do 10 | # Build the docker container 11 | run_cmd "Start CentOS-$RELEASE container" \ 12 | docker run -td --rm=true --name auter-rpmbuild-"$RELEASE" \ 13 | -e VERSION="$VERSION" \ 14 | -v "$AUTERDIR":/root/auter \ 15 | centos:"$RELEASE" 16 | 17 | PACKAGELIST=("gcc" "rpm-build" "rpm-devel" "rpmlint") 18 | PACKAGELIST+=("make" "python" "bash" "coreutils") 19 | PACKAGELIST+=("diffutils" "patch" "rpmdevtools" "help2man") 20 | 21 | run_cmd "Install ${PACKAGELIST[*]}" \ 22 | docker exec "auter-rpmbuild-$RELEASE" yum -y -q -e 0 install "${PACKAGELIST[@]}" 23 | 24 | log_info "Handing over to container to build rpm package" 25 | run_cmd "Executed 11-container-rpmbuild.sh" \ 26 | docker exec "auter-rpmbuild-$RELEASE" /root/auter/tests/11-container-rpmbuild.sh 27 | 28 | run_cmd "Stopping CentOS-$RELEASE container" \ 29 | docker stop "auter-rpmbuild-$RELEASE" 30 | done 31 | -------------------------------------------------------------------------------- /tests/11-container-rpmbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | cd /root || exit 5 | useradd -m builduser 6 | su - builduser -c 'rpmdev-setuptree' 7 | 8 | cp -a auter "auter-$VERSION" 9 | tar --group=builduser --owner=builduser -czf "$VERSION.tar.gz" "auter-$VERSION" 10 | cp -v "$VERSION.tar.gz" /home/builduser/rpmbuild/SOURCES/ 11 | cp -v auter/auter.spec /home/builduser/rpmbuild/SPECS/ 12 | chown -R builduser. /home/builduser/ 13 | 14 | su - builduser -c 'rpmbuild -ba rpmbuild/SPECS/auter.spec' 15 | -------------------------------------------------------------------------------- /tests/20-debuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$(dirname "$0")"/_helpers 4 | 5 | export RELEASE="18.04" 6 | log_info "AUTERDIR: $AUTERDIR" 7 | log_info "AUTERPARENTDIR: $AUTERPARENTDIR" 8 | log_info "VERSION: $VERSION" 9 | 10 | # build the container 11 | # 12 | run_cmd "Start Ubuntu $RELEASE container" \ 13 | docker run -td --rm=true --name auter-debuild-test-"$RELEASE" \ 14 | -e DEBIAN_FRONTEND=noninteractive \ 15 | -v "$AUTERDIR":/root/auter \ 16 | ubuntu:"$RELEASE" 17 | 18 | PACKAGELIST=("sudo" "git" "make" "help2man") 19 | PACKAGELIST+=("lsb-release" "lintian" "devscripts" "debhelper") 20 | 21 | run_cmd "Update package info" \ 22 | docker exec auter-debuild-test-"$RELEASE" apt-get update -qq 23 | 24 | run_cmd "Install ${PACKAGELIST[*]}" \ 25 | docker exec auter-debuild-test-"$RELEASE" apt-get install -qq "${PACKAGELIST[@]}" 26 | 27 | run_cmd "Check for /usr/bin/python link" \ 28 | docker exec auter-debuild-test-"$RELEASE" ln -s /usr/bin/python3 /usr/bin/python &>/dev/null|| true 29 | 30 | log_info "Handing over to container to build deb package" 31 | run_cmd "Executed /root/auter/tests/21-container-debuild.sh" \ 32 | docker exec auter-debuild-test-"$RELEASE" /root/auter/tests/21-container-debuild.sh 33 | 34 | run_cmd "Stop Ubuntu $RELEASE container" \ 35 | docker stop auter-debuild-test-"$RELEASE" 36 | -------------------------------------------------------------------------------- /tests/21-container-debuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Building" 4 | cd /root/auter || exit 5 | 6 | make deb 7 | cd "$(find . -maxdepth 1 -type d | grep auter | grep -v orig)" || (echo "Failed to cd to auter directory created by Makefile" && exit 1) 8 | debuild -us -uc >/dev/null 9 | -------------------------------------------------------------------------------- /tests/_helpers: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | red='\033[31m' 4 | grn='\033[32m' 5 | ylw='\033[33m' 6 | rst='\033[0m' 7 | 8 | log_success () { printf "[%b OK %b] %s\\n" "$grn" "$rst" "$1"; } 9 | log_fail () { printf "[%b FAIL %b] %s\\n" "$red" "$rst" "$1"; } 10 | log_info () { printf "[%b INFO %b] %s\\n" "$ylw" "$rst" "$1"; } 11 | 12 | function quit() { 13 | docker ps -aq | xargs docker stop 14 | exit "${1:-1}" 15 | } 16 | export -f quit 17 | 18 | function run_cmd () 19 | { 20 | local tag="$1"; shift 21 | if output="$("${@}" 2>&1)"; then 22 | log_success "$tag" 23 | return 0 24 | else 25 | log_fail "$tag" 26 | awk '{printf "| %s\n",$0}' <<< "$output" 27 | echo "-----------------------------------------------------------------------" 28 | quit 1 29 | fi 30 | } 31 | export -f run_cmd 32 | 33 | TESTDIR="$(dirname "$(realpath "$0")")" 34 | AUTERDIR="$(dirname "$TESTDIR")" 35 | AUTERPARENTDIR="$(dirname "$AUTERDIR")" 36 | VERSION="$(grep "Version" "$AUTERDIR"/auter.spec | awk '{print $2}')" 37 | export TESTDIR AUTERDIR AUTERPARENTDIR VERSION 38 | --------------------------------------------------------------------------------