├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .travis.yml ├── COPYRIGHT ├── Gemfile ├── LICENSE ├── README.md ├── RELEASENOTES.md ├── Rakefile ├── TODO.md ├── lib ├── packer-config.rb └── packer │ ├── builder.rb │ ├── builders │ ├── all.rb │ ├── amazon.rb │ ├── docker.rb │ ├── null.rb │ ├── qemu.rb │ ├── virtualbox.rb │ ├── vmware_iso.rb │ └── vmware_vmx.rb │ ├── dataobject.rb │ ├── envvar.rb │ ├── macro.rb │ ├── postprocessor.rb │ ├── postprocessors │ ├── all.rb │ ├── compress.rb │ ├── docker.rb │ ├── manifest.rb │ ├── shell-local.rb │ └── vagrant.rb │ ├── provisioner.rb │ ├── provisioners │ ├── all.rb │ ├── ansible.rb │ ├── chef │ │ ├── client.rb │ │ └── solo.rb │ ├── file.rb │ ├── powershell.rb │ ├── puppet │ │ ├── masterless.rb │ │ └── server.rb │ ├── salt.rb │ ├── shell.rb │ ├── windows_restart.rb │ └── windows_shell.rb │ ├── runner.rb │ └── version.rb ├── packer-config.gemspec ├── spec ├── integration │ ├── README.md │ ├── builds │ │ └── .gitignore │ ├── centos_vagrant_spec.rb │ ├── packer_cache │ │ └── .gitignore │ └── scripts │ │ ├── chef.sh │ │ ├── cleanup.sh │ │ ├── fix-slow-dns.sh │ │ ├── hello.sh │ │ ├── kickstart │ │ └── centos-7-ks.cfg │ │ ├── minimize.sh │ │ ├── sshd.sh │ │ ├── vagrant.sh │ │ └── vmtools.sh ├── packer │ ├── builder_spec.rb │ ├── builders │ │ ├── amazon_spec.rb │ │ ├── docker_spec.rb │ │ ├── null_spec.rb │ │ ├── qemu_spec.rb │ │ ├── virtualbox_spec.rb │ │ ├── vmware_iso_spec.rb │ │ └── vmware_vmx_spec.rb │ ├── dataobject_spec.rb │ ├── envvar_spec.rb │ ├── macro_spec.rb │ ├── postprocessor_spec.rb │ ├── postprocessors │ │ ├── docker_import_spec.rb │ │ ├── docker_push_spec.rb │ │ ├── docker_save_spec.rb │ │ ├── docker_tag_spec.rb │ │ ├── manifest_spec.rb │ │ ├── shell_local_spec.rb │ │ └── vagrant_spec.rb │ ├── provisioner_spec.rb │ ├── provisioners │ │ ├── ansible_spec.rb │ │ ├── chef │ │ │ ├── client_spec.rb │ │ │ └── solo_spec.rb │ │ ├── file_spec.rb │ │ ├── powershell_spec.rb │ │ ├── puppet │ │ │ ├── masterless_spec.rb │ │ │ └── server_spec.rb │ │ ├── salt_spec.rb │ │ ├── shell_spec.rb │ │ ├── windows_restart_spec.rb │ │ └── windows_shell_spec.rb │ └── runner_spec.rb ├── packer_config_spec.rb ├── spec_helper.rb └── unit_helper.rb └── tasks ├── clean.rake ├── rspec.rake ├── rubocop.rake └── rubygems.rake /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.gem 3 | *.rbc 4 | /.config 5 | /coverage/ 6 | /InstalledFiles 7 | /pkg/ 8 | /spec/reports/ 9 | /spec/integration/*.json 10 | /spec/integration/centos-*-x86_64-virtualbox 11 | /test/tmp/ 12 | /test/version_tmp/ 13 | /tmp/ 14 | /.idea/ 15 | tags 16 | 17 | ## Specific to RubyMotion: 18 | .dat* 19 | .repl_history 20 | build/ 21 | 22 | ## Documentation cache and generated files: 23 | /.yardoc/ 24 | /_yardoc/ 25 | /doc/ 26 | /rdoc/ 27 | 28 | ## Environment normalisation: 29 | /.bundle/ 30 | /lib/bundler/man/ 31 | 32 | # for a library or gem, you might want to ignore these files since the code is 33 | # intended to run in multiple environments; otherwise, check them in: 34 | Gemfile.lock 35 | .ruby-version 36 | .ruby-gemset 37 | 38 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 39 | .rvmrc 40 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | --format documentation 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-performance 2 | inherit_from: .rubocop_todo.yml 3 | 4 | Documentation: 5 | Enabled: false 6 | 7 | LeadingCommentSpace: 8 | Enabled: false 9 | 10 | LineLength: 11 | Enabled: false 12 | 13 | SignalException: 14 | Enabled: false 15 | 16 | # Don't care enough about this one 17 | SpaceInsideHashLiteralBraces: 18 | Enabled: false 19 | 20 | # Using $CHILD_STATUS instead of $?, etc 21 | SpecialGlobalVars: 22 | Enabled: false 23 | 24 | StringLiterals: 25 | Description: Checks if uses of quotes match the configured preference. 26 | Enabled: false 27 | EnforcedStyle: single_quotes 28 | SupportedStyles: 29 | - single_quotes 30 | - double_quotes 31 | 32 | # disabling a bunch of style linters 33 | Layout/BlockAlignment: 34 | Enabled: false 35 | 36 | Layout/EndAlignment: 37 | Enabled: false 38 | 39 | Lint/Loop: 40 | Enabled: false 41 | 42 | Lint/UnusedMethodArgument: 43 | Exclude: 44 | - 'lib/packer/dataobject.rb' 45 | - 'lib/packer/envvar.rb' 46 | - 'lib/packer/macro.rb' 47 | - 'lib/packer/provisioners/powershell.rb' 48 | 49 | Lint/Void: 50 | Exclude: 51 | - dashboard/spec/**/* 52 | 53 | Lint/MissingSuper: 54 | Exclude: 55 | - 'lib/packer/envvar.rb' 56 | - 'lib/packer/macro.rb' 57 | 58 | Naming/FileName: 59 | Exclude: 60 | - 'lib/packer-config.rb' 61 | - 'lib/packer/macro.rb' 62 | 63 | Layout/AccessModifierIndentation: 64 | Enabled: false 65 | 66 | Naming/AccessorMethodName: 67 | Enabled: false 68 | 69 | Layout/ArrayAlignment: 70 | Enabled: false 71 | 72 | Layout/HashAlignment: 73 | Enabled: false 74 | 75 | Layout/ParameterAlignment: 76 | Enabled: false 77 | 78 | Metrics/BlockNesting: 79 | Enabled: false 80 | 81 | Metrics/AbcSize: 82 | Max: 40 83 | 84 | Metrics/ClassLength: 85 | Max: 150 86 | 87 | Metrics/MethodLength: 88 | Max: 50 89 | 90 | Metrics/CyclomaticComplexity: 91 | Max: 10 92 | 93 | Metrics/PerceivedComplexity: 94 | Max: 10 95 | 96 | Layout/CaseIndentation: 97 | Enabled: false 98 | 99 | Style/CollectionMethods: 100 | Enabled: false 101 | 102 | Style/CommentAnnotation: 103 | Enabled: false 104 | 105 | Layout/CommentIndentation: 106 | Enabled: false 107 | 108 | Style/DefWithParentheses: 109 | Enabled: false 110 | 111 | Layout/DotPosition: 112 | Enabled: false 113 | 114 | Style/EachWithObject: 115 | Enabled: false 116 | 117 | Layout/EmptyLineBetweenDefs: 118 | Enabled: false 119 | 120 | Layout/EmptyLines: 121 | Enabled: false 122 | 123 | Layout/EmptyLinesAroundAccessModifier: 124 | Enabled: false 125 | 126 | Style/FrozenStringLiteralComment: 127 | Enabled: false 128 | 129 | Style/GuardClause: 130 | Enabled: false 131 | 132 | Style/HashSyntax: 133 | Enabled: false 134 | 135 | Style/HashEachMethods: 136 | Enabled: false 137 | 138 | Style/HashTransformKeys: 139 | Enabled: true 140 | 141 | Style/HashTransformValues: 142 | Enabled: true 143 | 144 | Style/IfUnlessModifier: 145 | Enabled: false 146 | 147 | Layout/IndentationWidth: 148 | Enabled: false 149 | 150 | Style/Lambda: 151 | Enabled: false 152 | 153 | Style/MultilineBlockChain: 154 | Enabled: false 155 | 156 | Style/MethodCallWithoutArgsParentheses: 157 | Enabled: false 158 | 159 | Style/MultilineIfThen: 160 | Enabled: false 161 | 162 | Style/MultilineTernaryOperator: 163 | Enabled: false 164 | 165 | Style/NilComparison: 166 | Enabled: false 167 | 168 | Naming/PredicateName: 169 | Enabled: false 170 | 171 | Style/RaiseArgs: 172 | Enabled: false 173 | 174 | Style/RedundantBegin: 175 | Enabled: false 176 | 177 | Style/RedundantSelf: 178 | Enabled: false 179 | 180 | Style/RegexpLiteral: 181 | Enabled: false 182 | 183 | Style/SingleLineMethods: 184 | Enabled: false 185 | 186 | Layout/SpaceAfterComma: 187 | Enabled: false 188 | 189 | Layout/SpaceAfterNot: 190 | Enabled: false 191 | 192 | Layout/SpaceAroundEqualsInParameterDefault: 193 | Enabled: false 194 | 195 | Layout/SpaceInsideParens: 196 | Enabled: false 197 | 198 | Layout/TrailingEmptyLines: 199 | Enabled: false 200 | 201 | Style/TrivialAccessors: 202 | Enabled: false 203 | 204 | Style/WhileUntilDo: 205 | Enabled: false 206 | 207 | Style/WhileUntilModifier: 208 | Enabled: false 209 | 210 | Layout/FirstHashElementIndentation: 211 | Enabled: false 212 | 213 | # As of rubocop 0.21, it thinks kwargs are useless assignments 214 | UselessAssignment: 215 | Enabled: false 216 | 217 | WordArray: 218 | Enabled: false 219 | 220 | Style/Encoding: 221 | Enabled: false 222 | 223 | Metrics/BlockLength: 224 | Enabled: false 225 | 226 | Layout/EmptyLineAfterMagicComment: 227 | Enabled: false 228 | 229 | Layout/HeredocIndentation: 230 | Enabled: false 231 | 232 | Layout/ClosingHeredocIndentation: 233 | Enabled: false 234 | 235 | Naming/HeredocDelimiterNaming: 236 | Enabled: false 237 | 238 | Gemspec/RequiredRubyVersion: 239 | Enabled: false 240 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2016-01-27 13:13:41 +0000 using RuboCop version 0.36.0. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 31 10 | # Cop supports --auto-correct. 11 | Style/MutableConstant: 12 | Exclude: 13 | - 'lib/packer-config.rb' 14 | - 'lib/packer/builder.rb' 15 | - 'lib/packer/postprocessor.rb' 16 | - 'lib/packer/provisioner.rb' 17 | - 'lib/packer/version.rb' 18 | - 'spec/integration/centos_vagrant_spec.rb' 19 | - 'spec/packer/postprocessor_spec.rb' 20 | - 'spec/packer/provisioner_spec.rb' 21 | 22 | # Offense count: 34 23 | # Cop supports --auto-correct. 24 | # Configuration parameters: PreferredDelimiters. 25 | Style/PercentLiteralDelimiters: 26 | Enabled: false 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.4 4 | - 2.5 5 | - 2.6 6 | - ruby-head 7 | before_install: gem install bundler 8 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Unless specified otherwise, all content copyright the respective contributors. 2 | 3 | Including, but not limited to: 4 | 5 | [Ian Chesal](https://github.com/ianchesal) 6 | [Fraser Cobb](https://github.com/frasercobb) 7 | [Greg Poirier](https://github.com/grepory) 8 | [Greg Diamond](https://github.com/diamond29) 9 | [Enzo Rivello](https://github.com/enzor) 10 | [Edwin Biemond](https://github.com/biemond) 11 | [Andrei Shiniti Nakagawa](https://github.com/shinitiandrei) 12 | [Kevin Formsma](https://github.com/arothian) 13 | [sabishiii](https://github.com/sabishiii) 14 | 15 | For licensing information please see LICENSE. Copyright holders contributing to 16 | this project agree to have their contributions licensed under the terms of the 17 | LICENSE agreement. 18 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :development, :test do 4 | gem 'bundler' 5 | gem 'fakefs' 6 | gem 'rake' 7 | gem 'rspec' 8 | gem 'rspec-mocks' 9 | gem 'rubocop' 10 | gem 'rubocop-performance' 11 | gem 'rubygems-tasks' 12 | end 13 | 14 | gemspec 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # packer-config 2 | 3 | [![Gem Version](https://badge.fury.io/rb/packer-config.svg)](http://badge.fury.io/rb/packer-config) 4 | 5 | [![Build Status](https://travis-ci.org/ianchesal/packer-config.svg?branch=master)](https://travis-ci.org/ianchesal/packer-config) 6 | 7 | A Ruby model that lets you build [Packer](http://packer.io) configurations in Ruby. 8 | 9 | Building the Packer JSON configurations in raw JSON can be quite an adventure. 10 | There's limited facilities for variable expansion and absolutely no support for 11 | nice things like comments. I decided it would just be easier to have an object 12 | model to build the Packer configurations in that would easily write to the 13 | correct JSON format. It also saved me having to remember the esoteric Packer 14 | syntax for referencing variables and whatnot in the JSON. 15 | 16 | Bonus: you can really go to town with templates when it's all done in Ruby. 17 | 18 | ## Installation 19 | 20 | gem install packer-config 21 | 22 | ## Use 23 | 24 | require 'packer-config' 25 | 26 | ## Requires 27 | 28 | * [Packer](http://packer.io) version 1.0.0 or higher 29 | 30 | ### Builders 31 | 32 | The following [Packer builders](http://www.packer.io/docs/templates/builders.html) are currently implemented: 33 | 34 | * [amazon-ebs](http://www.packer.io/docs/builders/amazon-ebs.html) 35 | * [amazon-instance](http://www.packer.io/docs/builders/amazon-instance.html) 36 | * [docker](http://www.packer.io/docs/builders/docker.html) 37 | * [virtualbox-iso](http://www.packer.io/docs/builders/virtualbox-iso.html) 38 | * [vmware-vmx](https://www.packer.io/docs/builders/vmware-vmx) 39 | * [vmware-iso](https://www.packer.io/docs/builders/vmware-iso) 40 | * [qemu](https://www.packer.io/docs/builders/qemu.html) 41 | * [null](https://www.packer.io/docs/builders/null.html) 42 | 43 | [Communicators](https://www.packer.io/docs/templates/communicator.html) are supported as options on Builders in `packer-config`. The `none`, `ssh`, and `winrm` communicators are all available as is the `docker` communicator on the Docker-type builders. `packer-config` will raise an error if you try to use a Communicator type that isn't valid for the Builder. 44 | 45 | ### Provisioners 46 | 47 | The following [Packer provisioners](http://www.packer.io/docs/templates/provisioners.html) are currently implemented: 48 | 49 | * [file](http://www.packer.io/docs/provisioners/file.html) 50 | * [shell](http://www.packer.io/docs/provisioners/shell.html) 51 | * [shell-local](https://www.packer.io/docs/provisioners/shell-local.html) 52 | * [ansible](https://www.packer.io/docs/provisioners/ansible-local.html) 53 | * [chef-client](https://www.packer.io/docs/provisioners/chef-client.html) 54 | * [chef-solo](https://www.packer.io/docs/provisioners/chef-solo.html) 55 | * [salt](https://www.packer.io/docs/provisioners/salt-masterless.html) 56 | * [puppet server](https://www.packer.io/docs/provisioners/puppet-server.html) 57 | * [puppet masterless](https://www.packer.io/docs/provisioners/puppet-masterless.html) 58 | * [windows-restart](https://www.packer.io/docs/provisioners/windows-restart.html) 59 | 60 | ### Post-Processors 61 | 62 | The following [Packer post-processors](http://www.packer.io/docs/templates/post-processors.html) are currently implemented: 63 | 64 | * [docker-import](http://www.packer.io/docs/post-processors/docker-import.html) 65 | * [docker-push](http://www.packer.io/docs/post-processors/docker-push.html) 66 | * [docker-save](http://www.packer.io/docs/post-processors/docker-save.html) 67 | * [docker-tag](http://www.packer.io/docs/post-processors/docker-tag.html) 68 | * [vagrant](http://www.packer.io/docs/post-processors/vagrant.html) 69 | * [compress](https://www.packer.io/docs/post-processors/compress.html) 70 | * [manifest](https://www.packer.io/docs/post-processors/manifest.html) 71 | 72 | ## Examples 73 | 74 | ### Packing a Vagrant Basebox from a CentOS ISO Using VirtualBox 75 | 76 | This example is based on the integration test [spec/integration/centos_vagrant_spec.rb](spec/integration/centos_vagrant_spec.rb). It produces a Vagrant Basebox that's provisionable with [Chef](http://www.getchef.com/) and the packer config and provisioning is based on work done in the [Bento](https://github.com/opscode/bento) project from the OpsCode crew. 77 | 78 | CENTOS_VERSION = '7' 79 | 80 | pconfig = Packer::Config.new "centos-#{CENTOS_VERSION}-vagrant.json" 81 | pconfig.description "CentOS #{CENTOS_VERSION} VirtualBox Vagrant" 82 | pconfig.add_variable 'mirror', 'http://mirrors.sonic.net/centos' 83 | pconfig.add_variable 'mirror', 'http://ftp.pbone.net/pub/centos' 84 | pconfig.add_variable 'my_version', '0.0.1' 85 | pconfig.add_variable 'chef_version', 'latest' 86 | 87 | builder = pconfig.add_builder Packer::Builder::VIRTUALBOX_ISO 88 | builder.boot_command [" text ks=http://#{pconfig.macro.HTTPIP}:#{pconfig.macro.HTTPPort}/centos-#{CENTOS_VERSION}-ks.cfg"] 89 | builder.boot_wait '10s' 90 | builder.disk_size 40_960 91 | builder.guest_additions_path "VBoxGuestAdditions_#{pconfig.macro.Version}.iso" 92 | builder.guest_os_type "RedHat_64" 93 | builder.http_directory "scripts/kickstart" 94 | builder.iso_checksum '38d5d51d9d100fd73df031ffd6bd8b1297ce24660dc8c13a3b8b4534a4bd291c' 95 | builder.iso_checksum_type 'sha256' 96 | builder.iso_url "#{pconfig.variable 'mirror'}/#{CENTOS_VERSION}/isos/x86_64/CentOS-7-x86_64-Minimal-1810.iso" 97 | builder.output_directory "centos-#{CENTOS_VERSION}-x86_64-virtualbox" 98 | builder.shutdown_command "echo 'vagrant'|sudo -S /sbin/halt -h -p" 99 | builder.communicator "ssh" 100 | builder.ssh_password "vagrant" 101 | builder.ssh_port 22 102 | builder.ssh_username "vagrant" 103 | builder.ssh_timeout "10000s" 104 | builder.vboxmanage [ 105 | [ 106 | "modifyvm", 107 | pconfig.macro.Name, 108 | "--memory", 109 | "480" 110 | ], 111 | [ 112 | "modifyvm", 113 | pconfig.macro.Name, 114 | "--cpus", 115 | "1" 116 | ] 117 | ] 118 | builder.virtualbox_version_file ".vbox_version" 119 | builder.vm_name "packer-centos-#{CENTOS_VERSION}-x86_64" 120 | 121 | provisioner = pconfig.add_provisioner Packer::Provisioner::FILE 122 | provisioner.source 'scripts/hello.sh' 123 | provisioner.destination '/home/vagrant/hello.sh' 124 | 125 | provisioner = pconfig.add_provisioner Packer::Provisioner::SHELL 126 | provisioner.scripts [ 127 | 'scripts/sshd.sh', 128 | 'scripts/vagrant.sh' 129 | ] 130 | provisioner.environment_vars [ 131 | "CHEF_VERSION=#{pconfig.variable 'chef_version'}", 132 | "MY_CENTOS_VERSION=#{pconfig.variable 'my_version'}" 133 | ] 134 | provisioner.execute_command "echo 'vagrant' | #{pconfig.macro.Vars} sudo -S -E bash '#{pconfig.macro.Path}'" 135 | 136 | postprocessor = pconfig.add_postprocessor Packer::PostProcessor::VAGRANT 137 | postprocessor.output File.join('builds', pconfig.macro.Provider, "centos-#{CENTOS_VERSION}-x86_64-#{pconfig.variable 'my_version'}.box") 138 | 139 | pconfig.validate 140 | pconfig.build 141 | 142 | ## Development 143 | 144 | ### Continuous Integration 145 | 146 | I'm using Travis CI to build and test on every push to the public github repository. You can find the Travis CI page for this project here: https://travis-ci.org/ianchesal/packer-config/ 147 | 148 | ### Branching in Git 149 | 150 | We release off the `master` branch. Please open your pull requests against `develop`. We were using git-flow but I've fallen out of love with its style in favor of more ad hoc branching and just keeping `master` clean for releases. 151 | 152 | **PLEASE OPEN ALL PULL REQUESTS AGAINST `develop` NOT `master`!** 153 | 154 | ### TODO Work 155 | 156 | Please see [TODO.md](TODO.md) for the short list of big things I thought worth writing down. 157 | 158 | ## Contact Me 159 | 160 | Questions or comments about `packer-config`? Hit me up at ian.chesal@gmail.com. 161 | -------------------------------------------------------------------------------- /RELEASENOTES.md: -------------------------------------------------------------------------------- 1 | # packer-config Release Notes 2 | 3 | ## 1.6.5 4 | 5 | * Docker builder: Expose the `commit` and `changes` parameters. (via [sabishiii]) 6 | * Made the `commit` parameter mutually exclusive with the `export_path` one. (via [sabishiii]) 7 | * Unchained postprocessors: added a Packer::Config boolean switch to nest the array of postprocessors (via [sabishiii]) 8 | 9 | ## 1.6.4 10 | 11 | * Add support for the manifest post-provisioner (via [arothian]) 12 | * Fix broken test and development environments 13 | * Fix broken integration test, update to CentOS 7 14 | * Minimum Ruby version updated to 2.4.0 15 | * Minimum Packer version updated to 1.0.0 16 | 17 | ## 1.6.3 18 | 19 | * Add support for the shell-local provisioner (via [shinitiandrei]) 20 | * Add support for QEMU builder (via [biemond]) 21 | * Add support for the Compress post-processor (via [biemond]) 22 | * Add support for `-debug` in the packer command line call (via [diamond29]) 23 | 24 | ## 1.6.2 25 | 26 | * Updates for CVE-2017-8418 27 | * Lots of fixes for things that broke because of the updates for CVE-2017-8418 28 | * Updated integration spec to use to a CentOS version that exists 29 | 30 | ## 1.6.1 31 | 32 | * Added a verbose option to the `verify` method on `Packer::Config`. Closes issue #19. 33 | 34 | ## 1.6.0 35 | 36 | * Support for running Packer on Windows! 37 | 38 | ## 1.5.0 39 | 40 | * Adds json object support the the chef-solo provisioner (via [enzor][]). 41 | * Fixes Travis-CI setup so tests work with newer bundler 42 | 43 | ## 1.4.0 44 | 45 | * Adds support for Communicators (with help from [diamond29][]). 46 | * Validates the minimum version of Packer is met before running any Packer command line calls. Minimum version is set to 0.8.5 to support Communicators. 47 | * Bumps integration spec and working example in the README to CentOS 6.7. 48 | 49 | ## 1.3.1 50 | 51 | * Adds a fix for [issue #8](https://github.com/ianchesal/packer-config/issues/8). Removes psuedo-terminals from the runner library which were causing the problem. 52 | 53 | ## 1.3.0 54 | 55 | * Add support for vmware-vmx building (via [tduehr][]) 56 | 57 | ## 1.2.0 58 | 59 | * Add support for docker-save post-processor (via [frasercobb][]) 60 | * Add support for docker-tab post-processor (via [frasercobb][]) 61 | 62 | ## 1.1.0 63 | * Thanks to [grepory][] we have a `vmware-vmx` builder 64 | 65 | ## 1.0.0 66 | * A 1.0.0 release because...why not? You're either working or you're not and this is working so let's drop the `0.x.x` pretense and tango. 67 | * Thanks to [frasercobb][] we have `puppet-server` and `puppet-masterless` provisioner interfaces 68 | * There's also `ansible`, `chef-client`, `chef-solo` and `salt` provisioners for you to use now 69 | * Added the `null` builder because it seemed handy to have 70 | * Updated the example in the README (and the integration test) to CentOS 6.6 71 | * Moved the legalese in to COPYRIGHT and got it out of the all of the individual files 72 | 73 | ## 0.0.4 74 | 75 | * Added the ability to call `#build` with a quiet option. This option turns streaming to stdout off/on. With `quiet: false`, you will see the output of the Packer call on your screen while `#build` is executing it. This can be handy for view Packer status for long running Packer jobs. 76 | * Started writing release notes! 77 | 78 | 79 | [tduehr]: https://github.com/tduehr 80 | [frasercobb]: https://github.com/frasercobb 81 | [grepory]: https://github.com/grepory 82 | [ianchesal]: https://github.com/ianchesal 83 | [diamond29]: https://github.com/diamond29 84 | [enzor]: https://github.com/enzor 85 | [biemond]: https://github.com/biemond 86 | [shinitiandrei]: https://github.com/shinitiandrei 87 | [arothian]: https://github.com/arothian 88 | [sabishiii]: https://github.com/sabishiii 89 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | Dir.glob('tasks/**/*.rake').each(&method(:import)) 3 | task :default => [:lint, 'test:spec'] 4 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | * Add an option to Packer::Config#validate to run the configuration through packer's `validate` command 4 | * Add spec tests for every method on every sub-class. Found during integration testing that some methods on the sub-classes had typos in the `__*` method calls. Spec tests would have caught this. 5 | * Look in to something like VCR to drive the tests of the child classes -- there's a lot of repetitive testing that could be done on them. 6 | * Refactor the child classes. I get the feeling that these could be implemented in some better, templated, type of way 7 | -------------------------------------------------------------------------------- /lib/packer-config.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'json' 3 | require 'packer/runner' 4 | require 'packer/dataobject' 5 | require 'packer/builder' 6 | require 'packer/provisioner' 7 | require 'packer/postprocessor' 8 | require 'packer/macro' 9 | require 'packer/envvar' 10 | require 'packer/version' 11 | require 'rubygems/dependency' 12 | 13 | module Packer 14 | class Config < Packer::DataObject 15 | attr_accessor :builders, :postprocessors, :provisioners, :packer, :packer_options, :chained_postprocessors 16 | attr_reader :macro, :envvar, :output_file 17 | 18 | PACKER_VERSION = '1.0.0' 19 | 20 | def initialize(file) 21 | super() 22 | self.data['variables'] = {} 23 | self.output_file = file 24 | self.builders = [] 25 | self.provisioners = [] 26 | self.postprocessors = [] 27 | self.packer = 'packer' 28 | self.packer_options = [] 29 | self.macro = Macro.new 30 | self.envvar = EnvVar.new 31 | self.chained_postprocessors = true 32 | end 33 | 34 | def chained_postprocessors? 35 | self.chained_postprocessors ? true : false 36 | end 37 | 38 | def validate(verbose: false) 39 | super() 40 | verify_packer_version 41 | if self.builders.empty? 42 | raise DataValidationError.new("At least one builder is required") 43 | end 44 | 45 | self.builders.each(&:validate) 46 | self.provisioners.each(&:validate) 47 | self.postprocessors.each(&:validate) 48 | self.write 49 | stdout = nil 50 | Dir.chdir(File.dirname(self.output_file)) do 51 | begin 52 | stdout = Packer::Runner.run! self.packer, 'validate', File.basename(self.output_file), quiet: !verbose 53 | rescue Packer::Runner::CommandExecutionError => e 54 | raise PackerBuildError.new(e.to_s) 55 | end 56 | end 57 | self.delete 58 | stdout 59 | end 60 | 61 | class DumpError < StandardError 62 | end 63 | 64 | def dump(format='json', pretty: false) 65 | data_copy = self.deep_copy 66 | data_copy['builders'] = [] 67 | self.builders.each do |thing| 68 | data_copy['builders'].push(thing.deep_copy) 69 | end 70 | unless self.provisioners.empty? 71 | data_copy['provisioners'] = [] 72 | self.provisioners.each do |thing| 73 | data_copy['provisioners'].push(thing.deep_copy) 74 | end 75 | end 76 | unless self.postprocessors.empty? 77 | data_copy['post-processors'] = [] 78 | self.postprocessors.each do |thing| 79 | data_copy['post-processors'].push(thing.deep_copy) 80 | end 81 | unless self.chained_postprocessors? 82 | data_copy['post-processors'] = [data_copy['post-processors']] 83 | end 84 | end 85 | case format 86 | when 'json' 87 | if pretty 88 | JSON.pretty_generate(data_copy) 89 | else 90 | data_copy.to_json 91 | end 92 | else 93 | raise DumpError.new("Unrecognized format #{format} use one of ['json']") 94 | end 95 | end 96 | 97 | def write(format='json') 98 | File.write(self.output_file, self.dump(format)) 99 | end 100 | 101 | def delete 102 | File.delete(self.output_file) 103 | end 104 | 105 | class PackerBuildError < StandardError 106 | end 107 | 108 | def build(quiet: false) 109 | self.validate 110 | self.write 111 | stdout = nil 112 | Dir.chdir(File.dirname(self.output_file)) do 113 | begin 114 | stdout = Packer::Runner.run! self.packer, 'build', self.packer_options, File.basename(self.output_file), quiet: quiet 115 | rescue Packer::Runner::CommandExecutionError => e 116 | raise PackerBuildError.new(e.to_s) 117 | end 118 | end 119 | self.delete 120 | stdout 121 | end 122 | 123 | def description(description) 124 | self.__add_string('description', description) 125 | end 126 | 127 | def variables 128 | self.data['variables'] 129 | end 130 | 131 | def add_builder(type) 132 | builder = Packer::Builder.get_builder(type) 133 | self.builders.push(builder) 134 | builder 135 | end 136 | 137 | def add_provisioner(type) 138 | provisioner = Packer::Provisioner.get_provisioner(type) 139 | self.provisioners.push(provisioner) 140 | provisioner 141 | end 142 | 143 | def add_postprocessor(type) 144 | postprocessor = Packer::PostProcessor.get_postprocessor(type) 145 | self.postprocessors.push(postprocessor) 146 | postprocessor 147 | end 148 | 149 | def add_variable(name, value) 150 | variables_copy = Marshal.load(Marshal.dump(self.variables)) 151 | variables_copy[name.to_s] = value.to_s 152 | self.__add_hash('variables', variables_copy) 153 | end 154 | 155 | class UndefinedVariableError < StandardError 156 | end 157 | 158 | def variable(name) 159 | unless self.variables.key? name 160 | raise UndefinedVariableError.new("No variable named #{name} has been defined -- did you forget to call add_variable?") 161 | end 162 | 163 | "{{user `#{name}`}}" 164 | end 165 | 166 | def verify_packer_version 167 | min_packer_version PACKER_VERSION 168 | version = /Packer v(\d+\.\d+\.\d+)/.match( 169 | Packer::Runner.run!(packer, 'version', quiet: true) 170 | )[1] 171 | Gem::Dependency.new('', ">= #{PACKER_VERSION}").match?('', version) || raise("Packer version #{version} is not new enough (requires >= #{PACKER_VERSION})") 172 | end 173 | 174 | def min_packer_version(version) 175 | self.__add_string('min_packer_version', version) 176 | end 177 | 178 | private 179 | 180 | attr_writer :output_file, :macro, :envvar 181 | end 182 | end 183 | -------------------------------------------------------------------------------- /lib/packer/builder.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builders/all' 3 | 4 | module Packer 5 | class Builder < DataObject 6 | AMAZON_EBS = 'amazon-ebs' 7 | AMAZON_INSTANCE = 'amazon-instance' 8 | DOCKER = 'docker' 9 | VIRTUALBOX_ISO = 'virtualbox-iso' 10 | VMWARE_VMX = 'vmware-vmx' 11 | VMWARE_ISO = 'vmware-iso' 12 | QEMU = 'qemu' 13 | NULL = 'null' 14 | 15 | VALID_BUILDER_TYPES = [ 16 | AMAZON_EBS, 17 | AMAZON_INSTANCE, 18 | DOCKER, 19 | VIRTUALBOX_ISO, 20 | VMWARE_VMX, 21 | VMWARE_ISO, 22 | QEMU, 23 | NULL 24 | ] 25 | 26 | class UnrecognizedBuilderTypeError < StandardError 27 | end 28 | 29 | def self.get_builder(type) 30 | unless validate_type(type) 31 | raise UnrecognizedBuilderTypeError.new("Unrecognized builder type #{type}") 32 | end 33 | 34 | { 35 | AMAZON_EBS => Packer::Builder::Amazon::EBS, 36 | AMAZON_INSTANCE => Packer::Builder::Amazon::Instance, 37 | DOCKER => Packer::Builder::Docker, 38 | VIRTUALBOX_ISO => Packer::Builder::VirtualBoxISO, 39 | VMWARE_VMX => Packer::Builder::VMWareVMX, 40 | VMWARE_ISO => Packer::Builder::VMWareISO, 41 | QEMU => Packer::Builder::Qemu, 42 | NULL => Packer::Builder::Null 43 | }.fetch(type).new 44 | end 45 | 46 | attr_reader :communicators 47 | 48 | def self.types 49 | VALID_BUILDER_TYPES 50 | end 51 | 52 | def initialize 53 | super 54 | self.add_required('type') 55 | self.communicators = [] 56 | end 57 | 58 | def name(name) 59 | self.__add_string('name', name) 60 | end 61 | 62 | # @ianchesal: Communicators are technically Templates in Packer land but 63 | # they modify Builders. Weird. So we'll treat them as Builder attributes. 64 | # See: https://packer.io/docs/templates/communicator.html 65 | def communicator(comm) 66 | raise(DataValidationError, "unknown communicator protocol #{comm}") unless communicators.include? comm 67 | 68 | self.__add_string('communicator', comm) 69 | end 70 | 71 | # Technically these only apply if the communicator is ssh 72 | def ssh_host(host) 73 | self.__add_string('ssh_host', host) 74 | end 75 | 76 | def ssh_port(port) 77 | self.__add_integer('ssh_port', port) 78 | end 79 | 80 | def ssh_username(username) 81 | self.__add_string('ssh_username', username) 82 | end 83 | 84 | def ssh_password(password) 85 | self.__add_string('ssh_password', password) 86 | end 87 | 88 | def ssh_private_key_file(filename) 89 | self.__add_string('ssh_private_key_file', filename) 90 | end 91 | 92 | def ssh_pty(pty) 93 | self.__add_boolean('ssh_pty', pty) 94 | end 95 | 96 | def ssh_timeout(timeout) 97 | self.__add_string('ssh_timeout', timeout) 98 | end 99 | 100 | def ssh_handshake_attempts(attempts) 101 | self.__add_integer('ssh_handshake_attempts', attempts) 102 | end 103 | 104 | def ssh_disable_agent(disable) 105 | self.__add_boolean('ssh_disable_agent', disable) 106 | end 107 | 108 | def ssh_bastion_host(hostname) 109 | self.__add_string('ssh_bastion_host', hostname) 110 | end 111 | 112 | def ssh_bastion_username(username) 113 | self.__add_string('ssh_bastion_username', username) 114 | end 115 | 116 | def ssh_bastion_password(password) 117 | self.__add_string('ssh_bastion_password', password) 118 | end 119 | 120 | def ssh_bastion_private_key_file(filename) 121 | self.__add_string('ssh_bastion_private_key_file', filename) 122 | end 123 | 124 | # Technically these only apply if the communicator is winrm 125 | def winrm_host(host) 126 | self.__add_string('winrm_host', host) 127 | end 128 | 129 | def winrm_port(port) 130 | self.__add_string('winrm_port', port) 131 | end 132 | 133 | def winrm_username(username) 134 | self.__add_string('winrm_username', username) 135 | end 136 | 137 | def winrm_password(password) 138 | self.__add_string('winrm_password', password) 139 | end 140 | 141 | def winrm_timeout(timeout) 142 | self.__add_string('winrm_timeout', timeout) 143 | end 144 | 145 | private 146 | 147 | attr_writer :communicators 148 | 149 | def self.validate_type(type) 150 | VALID_BUILDER_TYPES.include? type 151 | end 152 | 153 | private_class_method :validate_type 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /lib/packer/builders/all.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builders/amazon' 3 | require 'packer/builders/docker' 4 | require 'packer/builders/virtualbox' 5 | require 'packer/builders/vmware_vmx' 6 | require 'packer/builders/vmware_iso' 7 | require 'packer/builders/qemu' 8 | require 'packer/builders/null' 9 | -------------------------------------------------------------------------------- /lib/packer/builders/amazon.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builder' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Builder < Packer::DataObject 7 | class Amazon < Builder 8 | def initialize 9 | super 10 | self.add_required( 11 | 'ami_name', 12 | 'instance_type', 13 | 'region', 14 | 'source_ami', 15 | 'communicator' 16 | ) 17 | self.communicators = %w(none ssh winrm) 18 | end 19 | 20 | def access_key(key) 21 | self.__add_string('access_key', key) 22 | end 23 | 24 | def ami_name(name) 25 | self.__add_string('ami_name', name) 26 | end 27 | 28 | def instance_type(type) 29 | self.__add_string('instance_type', type) 30 | end 31 | 32 | def region(name) 33 | self.__add_string('region', name) 34 | end 35 | 36 | def source_ami(name) 37 | self.__add_string('source_ami', name) 38 | end 39 | 40 | def secret_key(key) 41 | self.__add_string('secret_key', key) 42 | end 43 | 44 | def ami_block_device_mappings(mappings) 45 | self.__add_array_of_hashes('ami_block_device_mappings', mappings) 46 | end 47 | 48 | def ami_description(description) 49 | self.__add_string('ami_description', description) 50 | end 51 | 52 | def ami_groups(groups) 53 | self.__add_array_of_strings('ami_groups', groups) 54 | end 55 | 56 | def ami_product_codes(codes) 57 | self.__add_array_of_strings('ami_product_codes', codes) 58 | end 59 | 60 | def ami_regions(regions) 61 | self.__add_array_of_strings('ami_regions', regions) 62 | end 63 | 64 | def ami_users(users) 65 | self.__add_array_of_strings('ami_users', users) 66 | end 67 | 68 | def ami_virtualization_type(type) 69 | self.__add_string('ami_virtualization_type', type) 70 | end 71 | 72 | def associate_public_ip_address(bool) 73 | self.__add_boolean('associate_public_ip_address', bool) 74 | end 75 | 76 | def availability_zone(zone) 77 | self.__add_string('availability_zone', zone) 78 | end 79 | 80 | def enhanced_networking(bool) 81 | self.__add_boolean('enhanced_networking', bool) 82 | end 83 | 84 | def iam_instance_profile(profile) 85 | self.__add_string('iam_instance_profile', profile) 86 | end 87 | 88 | def launch_block_device_mappings(mappings) 89 | self.__add_array_of_hashes('launch_block_device_mappings', mappings) 90 | end 91 | 92 | def run_tags(tags) 93 | self.__add_hash('run_tags', tags) 94 | end 95 | 96 | def security_group_id(id) 97 | self.__add_string('security_group_id', id, %w[security_group_ids]) 98 | end 99 | 100 | def security_group_ids(ids) 101 | self.__add_array_of_strings('security_group_ids', ids, %w[security_group_id]) 102 | end 103 | 104 | def subnet_id(id) 105 | self.__add_string('subnet_id', id) 106 | end 107 | 108 | def tags(tags) 109 | self.__add_hash('tags', tags) 110 | end 111 | 112 | def temporary_key_pair_name(name) 113 | self.__add_string('temporary_key_pair_name', name) 114 | end 115 | 116 | def user_data(data) 117 | self.__add_string('user_data', data) 118 | end 119 | 120 | def user_data_file(file) 121 | self.__add_string('user_data_file', file) 122 | end 123 | 124 | def vpc_id(id) 125 | self.__add_string('vpc_id', id) 126 | end 127 | 128 | class EBS < Amazon 129 | def initialize 130 | super 131 | self.data['type'] = AMAZON_EBS 132 | end 133 | end 134 | 135 | class Instance < Amazon 136 | def initialize 137 | super 138 | self.data['type'] = AMAZON_INSTANCE 139 | self.add_required( 140 | 'account_id', 141 | 's3_bucket', 142 | 'x509_cert_path', 143 | 'x509_key_path' 144 | ) 145 | end 146 | 147 | def account_id(id) 148 | self.__add_string('account_id', id) 149 | end 150 | 151 | def s3_bucket(bucket) 152 | self.__add_string('s3_bucket', bucket) 153 | end 154 | 155 | def x509_cert_path(path) 156 | self.__add_string('x509_cert_path', path) 157 | end 158 | 159 | def x509_key_path(path) 160 | self.__add_string('x509_key_path', path) 161 | end 162 | 163 | def bundle_destination(directory) 164 | self.__add_string('bundle_destination', directory) 165 | end 166 | 167 | def bundle_prefix(prefix) 168 | self.__add_string('bundle_prefix', prefix) 169 | end 170 | 171 | def bundle_upload_command(command) 172 | self.__add_string('bundle_upload_command', command) 173 | end 174 | 175 | def bundle_vol_command(command) 176 | self.__add_string('bundle_vol_command', command) 177 | end 178 | 179 | def x509_upload_path(path) 180 | self.__add_string('x509_upload_path', path) 181 | end 182 | end 183 | end 184 | end 185 | end 186 | -------------------------------------------------------------------------------- /lib/packer/builders/docker.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builder' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Builder < Packer::DataObject 7 | class Docker < Builder 8 | def initialize 9 | super 10 | self.data['type'] = DOCKER 11 | self.add_required('image') 12 | end 13 | 14 | def export_path(path) 15 | self.__add_string('export_path', path, ['commit']) 16 | end 17 | 18 | def commit(bool) 19 | self.__add_boolean('commit', bool, ['export_path']) 20 | end 21 | 22 | def image(name) 23 | self.__add_string('image', name) 24 | end 25 | 26 | def pull(bool) 27 | self.__add_boolean('pull', bool) 28 | end 29 | 30 | def changes(changes) 31 | self.__add_array_of_strings('changes', changes) 32 | end 33 | 34 | def run_command(commands) 35 | self.__add_array_of_strings('run_command', commands) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/packer/builders/null.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builder' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Builder < Packer::DataObject 7 | class Null < Builder 8 | def initialize 9 | super 10 | self.data['type'] = NULL 11 | self.communicators = %w(none ssh winrm) 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/packer/builders/qemu.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builder' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Builder < Packer::DataObject 7 | class Qemu < Builder 8 | def initialize 9 | super 10 | self.data['type'] = QEMU 11 | self.add_required( 12 | 'iso_checksum', 13 | 'iso_checksum_type', 14 | 'iso_url', 15 | 'communicator' 16 | ) 17 | self.communicators = %w(none ssh winrm) 18 | end 19 | 20 | def iso_checksum(checksum) 21 | self.__add_string('iso_checksum', checksum) 22 | end 23 | 24 | def iso_checksum_type(type) 25 | self.__add_string('iso_checksum_type', type) 26 | end 27 | 28 | def iso_url(url) 29 | self.__add_string('iso_url', url, %w[iso_urls]) 30 | end 31 | 32 | def boot_command(commands) 33 | self.__add_array_of_strings('boot_command', commands) 34 | end 35 | 36 | def boot_wait(time) 37 | self.__add_string('boot_wait',time) 38 | end 39 | 40 | def disk_size(megabytes) 41 | self.__add_integer('disk_size', megabytes) 42 | end 43 | 44 | def accelerator(accelerator) 45 | self.__add_string('accelerator', accelerator) 46 | end 47 | 48 | def format(format) 49 | self.__add_string('format', format) 50 | end 51 | 52 | def net_device(net_device) 53 | self.__add_string('net_device', net_device) 54 | end 55 | 56 | def floppy_files(files) 57 | self.__add_array_of_strings('floppy_files', files) 58 | end 59 | 60 | def qemu_binary(qemu_binary) 61 | self.__add_string('qemu_binary', qemu_binary) 62 | end 63 | 64 | def disk_interface(disk_interface) 65 | self.__add_string('disk_interface', disk_interface) 66 | end 67 | 68 | def headless(bool) 69 | self.__add_boolean('headless', bool) 70 | end 71 | 72 | def http_directory(directory) 73 | self.__add_string('http_directory', directory) 74 | end 75 | 76 | def http_port_min(port_number) 77 | self.__add_integer('http_port_min', port_number) 78 | end 79 | 80 | def http_port_max(port_number) 81 | self.__add_integer('http_port_max', port_number) 82 | end 83 | 84 | def output_directory(directory) 85 | self.__add_string('output_directory', directory) 86 | end 87 | 88 | def shutdown_command(command) 89 | self.__add_string('shutdown_command', command) 90 | end 91 | 92 | def shutdown_timeout(time) 93 | self.__add_string('shutdown_timeout', time) 94 | end 95 | 96 | def qemuargs(array_of_commands) 97 | self.__add_array_of_array_of_strings('qemuargs', array_of_commands) 98 | end 99 | 100 | def vm_name(name) 101 | self.__add_string('vm_name', name) 102 | end 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/packer/builders/virtualbox.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builder' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Builder < Packer::DataObject 7 | class VirtualBoxISO < Builder 8 | def initialize 9 | super 10 | self.data['type'] = VIRTUALBOX_ISO 11 | self.add_required( 12 | 'iso_checksum', 13 | 'iso_checksum_type', 14 | 'iso_url', 15 | 'communicator' 16 | ) 17 | self.communicators = %w(none ssh winrm) 18 | end 19 | 20 | def iso_checksum(checksum) 21 | self.__add_string('iso_checksum', checksum) 22 | end 23 | 24 | def iso_checksum_type(type) 25 | self.__add_string('iso_checksum_type', type) 26 | end 27 | 28 | def iso_url(url) 29 | self.__add_string('iso_url', url, %w[iso_urls]) 30 | end 31 | 32 | def iso_urls(urls) 33 | self.__add_array_of_strings('iso_urls', urls, %[iso_url]) 34 | end 35 | 36 | def boot_command(commands) 37 | self.__add_array_of_strings('boot_command', commands) 38 | end 39 | 40 | def boot_wait(time) 41 | self.__add_string('boot_wait',time) 42 | end 43 | 44 | def disk_size(megabytes) 45 | self.__add_integer('disk_size', megabytes) 46 | end 47 | 48 | def export_opts(vboxmanage_export_options) 49 | self.__add_array_of_strings('export_opts', vboxmanage_export_options) 50 | end 51 | 52 | def floppy_files(files) 53 | self.__add_array_of_strings('floppy_files', files) 54 | end 55 | 56 | def format(format) 57 | self.__add_string('format', format) 58 | end 59 | 60 | def guest_additions_mode(mode) 61 | self.__add_string('guest_additions_mode', mode) 62 | end 63 | 64 | def guest_additions_path(path) 65 | self.__add_string('guest_additions_path', path) 66 | end 67 | 68 | def guest_additions_sha256(checksum) 69 | self.__add_string('guest_additions_sha256', checksum) 70 | end 71 | 72 | def guest_additions_url(url) 73 | self.__add_string('guest_additions_url', url) 74 | end 75 | 76 | def guest_os_type(ostype) 77 | self.__add_string('guest_os_type', ostype) 78 | end 79 | 80 | def hard_drive_interface(controllertype) 81 | self.__add_string('hard_drive_interface', controllertype) 82 | end 83 | 84 | def headless(bool) 85 | self.__add_boolean('headless', bool) 86 | end 87 | 88 | def http_directory(directory) 89 | self.__add_string('http_directory', directory) 90 | end 91 | 92 | def http_port_min(port_number) 93 | self.__add_integer('http_port_min', port_number) 94 | end 95 | 96 | def http_port_max(port_number) 97 | self.__add_integer('http_port_max', port_number) 98 | end 99 | 100 | def output_directory(directory) 101 | self.__add_string('output_directory', directory) 102 | end 103 | 104 | def shutdown_command(command) 105 | self.__add_string('shutdown_command', command) 106 | end 107 | 108 | def shutdown_timeout(time) 109 | self.__add_string('shutdown_timeout', time) 110 | end 111 | 112 | def vboxmanage(array_of_commands) 113 | self.__add_array_of_array_of_strings('vboxmanage', array_of_commands) 114 | end 115 | 116 | def vboxmanage_post(array_of_commands) 117 | self.__add_array_of_array_of_strings('vboxmanage_post', array_of_commands) 118 | end 119 | 120 | def virtualbox_version_file(file) 121 | self.__add_string('virtualbox_version_file', file) 122 | end 123 | 124 | def vm_name(name) 125 | self.__add_string('vm_name', name) 126 | end 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /lib/packer/builders/vmware_iso.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/builder' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Builder < Packer::DataObject 7 | class VMWareISO < Builder 8 | def initialize 9 | super 10 | self.data['type'] = VMWARE_ISO 11 | self.add_required( 12 | 'iso_checksum', 13 | 'iso_checksum_type', 14 | 'iso_url', 15 | 'communicator' 16 | ) 17 | self.communicators = %w(none ssh winrm) 18 | end 19 | 20 | def iso_checksum(checksum) 21 | self.__add_string('iso_checksum', checksum) 22 | end 23 | 24 | def iso_checksum_type(type) 25 | self.__add_string('iso_checksum_type', type) 26 | end 27 | 28 | def iso_url(url) 29 | self.__add_string('iso_url', url, %w[iso_urls]) 30 | end 31 | 32 | def iso_urls(urls) 33 | self.__add_array_of_strings('iso_urls', urls, %[iso_url]) 34 | end 35 | 36 | def boot_command(commands) 37 | self.__add_array_of_strings('boot_command', commands) 38 | end 39 | 40 | def boot_wait(time) 41 | self.__add_string('boot_wait',time) 42 | end 43 | 44 | def disk_size(megabytes) 45 | self.__add_integer('disk_size', megabytes) 46 | end 47 | 48 | # 0 - create a growable virtual disk contained in a single file (monolithic sparse). 49 | # 1 - create a growable virtual disk split into 2GB files (split sparse). 50 | # 2 - create a preallocated virtual disk contained in a single file (monolithic flat). 51 | # 3 - create a preallocated virtual disk split into 2GB files (split flat). 52 | # 4 - create a preallocated virtual disk compatible with ESX server (VMFS flat). 53 | # 5 - create a compressed disk optimized for streaming. 54 | def disk_type_id(tid) 55 | self.__add_string('disk_type_id',tid) 56 | end 57 | 58 | def floppy_files(files) 59 | self.__add_array_of_strings('floppy_files', files) 60 | end 61 | 62 | def fusion_app_path(app_path) 63 | self.__add_string('fusion_app_path',app_path) 64 | end 65 | 66 | def guest_os_type(ostype) 67 | self.__add_string('guest_os_type', ostype) 68 | end 69 | 70 | def headless(bool) 71 | self.__add_boolean('headless', bool) 72 | end 73 | 74 | def http_directory(directory) 75 | self.__add_string('http_directory', directory) 76 | end 77 | 78 | def http_port_min(port_number) 79 | self.__add_integer('http_port_min', port_number) 80 | end 81 | 82 | def http_port_max(port_number) 83 | self.__add_integer('http_port_max', port_number) 84 | end 85 | 86 | def output_directory(directory) 87 | self.__add_string('output_directory', directory) 88 | end 89 | 90 | def remote_cache_datastore(cache) 91 | self.__add_string('remote_cache_datastore', cache) 92 | end 93 | 94 | def remote_cache_directory(cache_directory) 95 | self.__add_string('remote_cache_directory', cache_directory) 96 | end 97 | 98 | def remote_datastore(datastore) 99 | self.__add_string('remote_datastore', datastore) 100 | end 101 | 102 | def remote_host(host) 103 | self.__add_string('remote_host', host) 104 | end 105 | 106 | def remote_password(passwd) 107 | self.__add_string('remote_password', passwd) 108 | end 109 | 110 | def remote_type(typ) 111 | self.__add_string('remote_type', typ) 112 | end 113 | 114 | def remote_username(username) 115 | self.__add_string('remote_username', username) 116 | end 117 | 118 | def shutdown_command(command) 119 | self.__add_string('shutdown_command', command) 120 | end 121 | 122 | def shutdown_timeout(time) 123 | self.__add_string('shutdown_timeout', time) 124 | end 125 | 126 | def skip_compaction(bool) 127 | self.__add_boolean('skip_compaction', bool) 128 | end 129 | 130 | def tools_upload_flavor(flavor) 131 | self.__add_string('tools_upload_flavor', flavor) 132 | end 133 | 134 | def tools_upload_path(path) 135 | self.__add_string('tools_upload_path', path) 136 | end 137 | 138 | def version(version) 139 | self.__add_string('version', version) 140 | end 141 | 142 | def vm_name(name) 143 | self.__add_string('vm_name', name) 144 | end 145 | 146 | def vmdk_name(name) 147 | self.__add_string('vmdk_name', name) 148 | end 149 | 150 | def vmx_data(data) 151 | self.__add_hash('vmx_data', data) 152 | end 153 | 154 | def vmx_data_post(data) 155 | self.__add_hash('vmx_data_post', data) 156 | end 157 | 158 | def vmx_template_path(path) 159 | self.__add_string('vmx_template_path', path) 160 | end 161 | 162 | def vnc_port_min(port) 163 | self.__add_integer('vnc_port_min', port) 164 | end 165 | 166 | def vnc_port_max(port) 167 | self.__add_integer('vnc_port_max', port) 168 | end 169 | end 170 | end 171 | end 172 | -------------------------------------------------------------------------------- /lib/packer/builders/vmware_vmx.rb: -------------------------------------------------------------------------------- 1 | module Packer 2 | class Builder 3 | class VMWareVMX < Builder 4 | def initialize 5 | super 6 | data['type'] = VMWARE_VMX 7 | add_required( 8 | 'source_path', 9 | 'communicator' 10 | ) 11 | self.communicators = %w(none ssh winrm) 12 | end 13 | 14 | def source_path(path) 15 | __add_string('source_path', path) 16 | end 17 | 18 | def boot_command(commands) 19 | __add_array_of_strings('boot_command', commands) 20 | end 21 | 22 | def boot_wait(wait) 23 | __add_string('boot_wait', wait) 24 | end 25 | 26 | def floppy_files(files) 27 | __add_array_of_strings('floppy_files', files) 28 | end 29 | 30 | def fusion_app_path(app_path) 31 | __add_string('fusion_app_path', app_path) 32 | end 33 | 34 | def headless(bool) 35 | __add_boolean('headless', bool) 36 | end 37 | 38 | def http_directory(path) 39 | __add_string('http_directory', path) 40 | end 41 | 42 | def http_port_min(port) 43 | __add_integer('http_port_min', port) 44 | end 45 | 46 | def http_port_max(port) 47 | __add_integer('http_port_max', port) 48 | end 49 | 50 | def output_directory(path) 51 | __add_string('output_directory', path) 52 | end 53 | 54 | def shutdown_command(command) 55 | __add_string('shutdown_command', command) 56 | end 57 | 58 | def shutdown_timeout(timeout) 59 | __add_string('shutdown_timeout', timeout) 60 | end 61 | 62 | def skip_compaction(bool) 63 | __add_boolean('skip_compaction', bool) 64 | end 65 | 66 | def vm_name(name) 67 | __add_string('vm_name', name) 68 | end 69 | 70 | def vmx_data(data_hash) 71 | __add_hash('vmx_data', data_hash) 72 | end 73 | 74 | def vmx_data_post(data_hash) 75 | __add_hash('vmx_data_post', data_hash) 76 | end 77 | 78 | def vnc_port_min(port) 79 | __add_integer('vnc_port_min', port) 80 | end 81 | 82 | def vnc_port_max(port) 83 | __add_integer('vnc_port_max', port) 84 | end 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/packer/dataobject.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | module Packer 3 | class DataObject 4 | attr_accessor :data, :required, :key_dependencies 5 | 6 | def initialize 7 | self.data = {} 8 | self.required = [] 9 | self.key_dependencies = {} 10 | end 11 | 12 | class DataValidationError < StandardError 13 | end 14 | 15 | def validate 16 | validate_required 17 | validate_key_dependencies 18 | 19 | # TODO(ianc) Also validate the data with the packer command? 20 | true 21 | end 22 | 23 | def validate_required 24 | self.required.each do |r| 25 | if (r.is_a? Array) && !r.empty? 26 | if (r.length - (r - self.data.keys).length).zero? 27 | raise DataValidationError.new("Missing one required setting from the set #{r}") 28 | end 29 | if r.length - (r - self.data.keys).length > 1 30 | raise DataValidationError.new("Found more than one exclusive setting in data from set #{r}") 31 | end 32 | elsif ! self.data.key? r 33 | raise DataValidationError.new("Missing required setting #{r}") 34 | end 35 | end 36 | end 37 | 38 | def validate_key_dependencies 39 | self.data.keys.each do |data_key| 40 | next unless self.key_dependencies.key? data_key 41 | unless (self.key_dependencies[data_key] - self.data.keys).empty? 42 | raise DataValidationError.new("Key #{data_key} requires that keys be defined: #{self.key_dependencies[data_key]}") 43 | end 44 | end 45 | end 46 | 47 | def add_required(*keys) 48 | keys.each do |k| 49 | self.required.push(k) 50 | end 51 | end 52 | 53 | def add_key_dependencies(key_deps) 54 | self.key_dependencies.merge!(key_deps) 55 | end 56 | 57 | def deep_copy 58 | Marshal.load(Marshal.dump(self.data)) 59 | end 60 | 61 | class ExclusiveKeyError < StandardError 62 | end 63 | 64 | def __exclusive_key_error(key, exclusives) 65 | exclusives.each do |e| 66 | if self.data.key? e 67 | raise ExclusiveKeyError.new("Only one of #{exclusives} can be used at a time") 68 | end 69 | end 70 | true 71 | end 72 | 73 | def __add_array_of_strings(key, values, exclusives = []) 74 | self.__exclusive_key_error(key, exclusives) 75 | raise TypeError.new() unless Array.try_convert(values) 76 | 77 | self.data[key.to_s] = values.to_ary.map(&:to_s) 78 | end 79 | 80 | def __add_array_of_array_of_strings(key, values, exclusives = []) 81 | self.__exclusive_key_error(key, exclusives) 82 | raise TypeError.new() unless Array.try_convert(values) 83 | 84 | self.data[key.to_s] = [] 85 | values.each do |v| 86 | raise TypeError.new() unless Array.try_convert(v) 87 | 88 | self.data[key.to_s].push(v.to_ary.map(&:to_s)) 89 | end 90 | end 91 | 92 | def __add_array_of_hashes(key, values, exclusives = []) 93 | self.__exclusive_key_error(key, exclusives) 94 | raise TypeError.new() unless Array.try_convert(values) 95 | 96 | self.data[key.to_s] = [] 97 | values.each do |v| 98 | raise TypeError.new() unless v.is_a?(Hash) 99 | 100 | self.data[key.to_s].push({}) 101 | v.keys.each do |k| 102 | self.data[key.to_s][-1][k] = v[k].to_s 103 | end 104 | end 105 | end 106 | 107 | def __add_array_of_ints(key, values, exclusives = []) 108 | self.__exclusive_key_error(key, exclusives) 109 | raise TypeError.new() unless Array.try_convert(values) 110 | 111 | self.data[key.to_s] = values.to_ary.map(&:to_i) 112 | end 113 | 114 | def __add_string(key, data, exclusives = []) 115 | self.__exclusive_key_error(key, exclusives) 116 | self.data[key.to_s] = data.to_s 117 | end 118 | 119 | def __add_integer(key, data, exclusives = []) 120 | self.__exclusive_key_error(key, exclusives) 121 | self.data[key.to_s] = data.to_i 122 | end 123 | 124 | 125 | def __add_boolean(key, bool, exclusives = []) 126 | self.__exclusive_key_error(key, exclusives) 127 | self.data[key.to_s] = bool ? true : false 128 | end 129 | 130 | def __add_hash(key, data, exclusives = []) 131 | self.__exclusive_key_error(key, exclusives) 132 | raise TypeError.new() unless data.is_a?(Hash) 133 | 134 | self.data[key.to_s] = {} 135 | data.keys.each do |k| 136 | self.data[key.to_s][k] = data[k].to_s 137 | end 138 | end 139 | 140 | 141 | def __add_json(key, data, exclusives = []) 142 | self.__exclusive_key_error(key, exclusives) 143 | raise TypeError.new() unless data.is_a?(Hash) 144 | 145 | self.data[key.to_s] = {} 146 | data.keys.each do |k| 147 | self.data[key.to_s][k] = data[k] 148 | end 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /lib/packer/envvar.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | 3 | module Packer 4 | class EnvVar 5 | def method_missing(method_name, *args) 6 | "{{env `#{method_name}`}}" 7 | end 8 | 9 | 10 | def respond_to_missing?(method_name, include_private: false) 11 | true 12 | end 13 | 14 | def respond_to?(symbol, include_private: false) 15 | # We literally respond to everything... 16 | true 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/packer/macro.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | 3 | module Packer 4 | class Macro 5 | def method_missing(method_name, *args) 6 | name = method_name.to_s.slice(0,1).capitalize + method_name.to_s.slice(1..-1) 7 | "{{ .#{name} }}" 8 | end 9 | 10 | 11 | def respond_to_missing?(method_name, include_private = false) 12 | true 13 | end 14 | 15 | def respond_to?(symbol, include_private: false) 16 | # We literally respond to everything... 17 | true 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/packer/postprocessor.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/postprocessors/all' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class PostProcessor < Packer::DataObject 7 | DOCKER_IMPORT = 'docker-import' 8 | DOCKER_PUSH = 'docker-push' 9 | DOCKER_SAVE = 'docker-save' 10 | DOCKER_TAG = 'docker-tag' 11 | VAGRANT = 'vagrant' 12 | COMPRESS = 'compress' 13 | SHELL_LOCAL = 'shell-local' 14 | MANIFEST = 'manifest' 15 | 16 | VALID_POST_PROCESSOR_TYPES = [ 17 | DOCKER_IMPORT, 18 | DOCKER_PUSH, 19 | DOCKER_SAVE, 20 | DOCKER_TAG, 21 | COMPRESS, 22 | VAGRANT, 23 | SHELL_LOCAL, 24 | MANIFEST 25 | ] 26 | 27 | class UnrecognizedPostProcessorTypeError < StandardError 28 | end 29 | 30 | def self.get_postprocessor(type) 31 | unless validate_type(type) 32 | raise UnrecognizedPostProcessorTypeError.new("Unrecognized post-processor type #{type}") 33 | end 34 | 35 | { 36 | DOCKER_IMPORT => Packer::PostProcessor::DockerImport, 37 | DOCKER_PUSH => Packer::PostProcessor::DockerPush, 38 | DOCKER_SAVE => Packer::PostProcessor::DockerSave, 39 | DOCKER_TAG => Packer::PostProcessor::DockerTag, 40 | COMPRESS => Packer::PostProcessor::Compress, 41 | SHELL_LOCAL => Packer::PostProcessor::ShellLocal, 42 | VAGRANT => Packer::PostProcessor::Vagrant, 43 | MANIFEST => Packer::PostProcessor::Manifest 44 | }.fetch(type).new 45 | end 46 | 47 | def self.types 48 | VALID_POST_PROCESSOR_TYPES 49 | end 50 | 51 | def initialize 52 | super 53 | self.add_required('type') 54 | end 55 | 56 | def only(buildname) 57 | unless self.data.key? 'only' 58 | self.data['only'] = [] 59 | end 60 | self.data['only'] << buildname.to_s 61 | end 62 | 63 | def except(buildname) 64 | unless self.data.key? 'except' 65 | self.data['except'] = [] 66 | end 67 | self.data['except'] << buildname.to_s 68 | end 69 | 70 | def keep_input_artifact(bool) 71 | self.__add_boolean('keep_input_artifact', bool) 72 | end 73 | 74 | def self.validate_type(type) 75 | VALID_POST_PROCESSOR_TYPES.include? type 76 | end 77 | 78 | private_class_method :validate_type 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/packer/postprocessors/all.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require_relative 'docker' 3 | require_relative 'vagrant' 4 | require_relative 'compress' 5 | require_relative 'shell-local' 6 | require_relative 'manifest' 7 | -------------------------------------------------------------------------------- /lib/packer/postprocessors/compress.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/postprocessor' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class PostProcessor < Packer::DataObject 7 | class Compress < PostProcessor 8 | def initialize 9 | super() 10 | self.data['type'] = COMPRESS 11 | end 12 | 13 | def compression_level(level) 14 | self.__add_integer('compression_level', level) 15 | end 16 | 17 | def keep_input_artifact(bool) 18 | self.__add_boolean('keep_input_artifact', bool) 19 | end 20 | 21 | def output(file) 22 | self.__add_string('output', file) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/packer/postprocessors/docker.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/postprocessor' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class PostProcessor < Packer::DataObject 7 | class DockerImport < PostProcessor 8 | def initialize 9 | super 10 | self.data['type'] = DOCKER_IMPORT 11 | self.add_required('repository') 12 | end 13 | 14 | def repository(repo) 15 | self.__add_string('repository', repo) 16 | end 17 | 18 | def tag(tag) 19 | self.__add_string('tag', tag) 20 | end 21 | end 22 | 23 | class DockerPush < PostProcessor 24 | def initialize 25 | super 26 | self.data['type'] = DOCKER_PUSH 27 | end 28 | 29 | def login(bool) 30 | self.__add_boolean('login', bool) 31 | end 32 | 33 | def login_email(email) 34 | self.__add_string('login_email', email) 35 | end 36 | 37 | def login_username(username) 38 | self.__add_string('login_username', username) 39 | end 40 | 41 | def login_password(password) 42 | self.__add_string('login_password', password) 43 | end 44 | 45 | def login_server(server) 46 | self.__add_string('login_server', server) 47 | end 48 | end 49 | 50 | class DockerSave < PostProcessor 51 | def initialize 52 | super 53 | self.data['type'] = DOCKER_SAVE 54 | self.add_required('path') 55 | end 56 | 57 | def path(path) 58 | self.__add_string('path', path) 59 | end 60 | end 61 | 62 | class DockerTag < PostProcessor 63 | def initialize 64 | super 65 | self.data['type'] = DOCKER_TAG 66 | self.add_required('repository') 67 | end 68 | 69 | def repository(repo) 70 | self.__add_string('repository', repo) 71 | end 72 | 73 | def tag(tag) 74 | self.__add_string('tag', tag) 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/packer/postprocessors/manifest.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/postprocessor' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class PostProcessor < Packer::DataObject 7 | class Manifest < PostProcessor 8 | def initialize 9 | super 10 | self.data['type'] = MANIFEST 11 | end 12 | 13 | def output(file) 14 | self.__add_string('output', file) 15 | end 16 | 17 | def strip_path(bool) 18 | self.__add_boolean('strip_path', bool) 19 | end 20 | 21 | def custom_data(data) 22 | self.__add_hash('custom_data', data) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/packer/postprocessors/shell-local.rb: -------------------------------------------------------------------------------- 1 | # rubocop:disable Naming/FileName 2 | # Encoding: utf-8 3 | require 'packer/postprocessor' 4 | require 'packer/dataobject' 5 | 6 | module Packer 7 | class PostProcessor < Packer::DataObject 8 | class ShellLocal < PostProcessor 9 | def initialize 10 | super 11 | self.data['type'] = SHELL_LOCAL 12 | self.add_required(['inline', 'script', 'scripts']) 13 | end 14 | def inline(commands) 15 | self.__add_array_of_strings('inline', commands, %w[script scripts]) 16 | end 17 | 18 | def script(filename) 19 | self.__add_string('script', filename, %w[inline scripts]) 20 | end 21 | 22 | def scripts(filenames) 23 | self.__add_array_of_strings('scripts', filenames, %w[inline script]) 24 | end 25 | 26 | def binary(bool) 27 | self.__add_boolean('binary', bool, []) 28 | end 29 | 30 | def environment_vars(envpairs) 31 | self.__add_array_of_strings('environment_vars', envpairs) 32 | end 33 | 34 | def execute_command(command) 35 | self.__add_string('execute_command', command) 36 | end 37 | 38 | def inline_shebang(command) 39 | self.__add_string('inline_shebang', command) 40 | end 41 | 42 | def remote_path(command) 43 | self.__add_string('remote_path', command) 44 | end 45 | end 46 | end 47 | end 48 | 49 | # rubocop:enable Naming/FileName 50 | -------------------------------------------------------------------------------- /lib/packer/postprocessors/vagrant.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/postprocessor' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class PostProcessor < Packer::DataObject 7 | class Vagrant < PostProcessor 8 | def initialize 9 | super() 10 | self.data['type'] = VAGRANT 11 | end 12 | 13 | def compression_level(level) 14 | self.__add_integer('compression_level', level) 15 | end 16 | 17 | def include(files) 18 | self.__add_array_of_strings('include', files) 19 | end 20 | 21 | def keep_input_artifact(bool) 22 | self.__add_boolean('keep_input_artifact', bool) 23 | end 24 | 25 | def output(file) 26 | self.__add_string('output', file) 27 | end 28 | 29 | def vagrantfile_template(file) 30 | self.__add_string('vagrantfile_template', file) 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/packer/provisioner.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioners/all' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | SHELL = 'shell' 8 | WINDOWS_SHELL = 'windows-shell' 9 | POWERSHELL = 'powershell' 10 | FILE = 'file' 11 | SALT = 'salt-masterless' 12 | ANSIBLE = 'ansible-local' 13 | CHEF_CLIENT = 'chef-client' 14 | CHEF_SOLO = 'chef-solo' 15 | PUPPET_MASTERLESS = 'puppet-masterless' 16 | PUPPET_SERVER = 'puppet-server' 17 | WINDOWS_RESTART = 'windows-restart' 18 | 19 | VALID_PROVISIONER_TYPES = [ 20 | SHELL, 21 | WINDOWS_SHELL, 22 | POWERSHELL, 23 | FILE, 24 | SALT, 25 | ANSIBLE, 26 | CHEF_CLIENT, 27 | CHEF_SOLO, 28 | PUPPET_MASTERLESS, 29 | PUPPET_SERVER, 30 | WINDOWS_RESTART 31 | ] 32 | 33 | class UnrecognizedProvisionerTypeError < StandardError 34 | end 35 | 36 | def self.get_provisioner(type) 37 | unless validate_type(type) 38 | raise UnrecognizedProvisionerTypeError.new("Unrecognized provisioner type #{type}") 39 | end 40 | 41 | { 42 | SHELL => Packer::Provisioner::Shell, 43 | WINDOWS_SHELL => Packer::Provisioner::WindowsShell, 44 | POWERSHELL => Packer::Provisioner::Powershell, 45 | FILE => Packer::Provisioner::File, 46 | SALT => Packer::Provisioner::Salt, 47 | ANSIBLE => Packer::Provisioner::Ansible, 48 | CHEF_CLIENT => Packer::Provisioner::Chef::Client, 49 | CHEF_SOLO => Packer::Provisioner::Chef::Solo, 50 | PUPPET_MASTERLESS => Packer::Provisioner::Puppet::Masterless, 51 | PUPPET_SERVER => Packer::Provisioner::Puppet::Server, 52 | WINDOWS_RESTART => Packer::Provisioner::WindowsRestart 53 | }.fetch(type).new 54 | end 55 | 56 | def self.types 57 | VALID_PROVISIONER_TYPES 58 | end 59 | 60 | def initialize 61 | super 62 | self.add_required('type') 63 | end 64 | 65 | def only(buildname) 66 | unless self.data.key? 'only' 67 | self.data['only'] = [] 68 | end 69 | self.data['only'] << buildname.to_s 70 | end 71 | 72 | def except(buildname) 73 | unless self.data.key? 'except' 74 | self.data['except'] = [] 75 | end 76 | self.data['except'] << buildname.to_s 77 | end 78 | 79 | def pause_before(duration) 80 | self.data["pause_before"] = duration.to_s 81 | end 82 | 83 | def override(builddefinition, values) 84 | raise TypeError.new() unless values.is_a?(Hash) 85 | 86 | unless self.data.key? 'override' 87 | self.data['override'] = {} 88 | end 89 | if self.data.key? @data['override'][builddefinition] 90 | self.data['override'][builddefinition].merge! values 91 | else 92 | self.data['override'][builddefinition] = values 93 | end 94 | end 95 | 96 | def self.validate_type(type) 97 | VALID_PROVISIONER_TYPES.include? type 98 | end 99 | 100 | private_class_method :validate_type 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/packer/provisioners/all.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require_relative 'file' 3 | require_relative 'shell' 4 | require_relative 'windows_shell' 5 | require_relative 'powershell' 6 | require_relative 'salt' 7 | require_relative 'ansible' 8 | require_relative 'chef/client' 9 | require_relative 'chef/solo' 10 | require_relative 'puppet/server' 11 | require_relative 'puppet/masterless' 12 | require_relative 'windows_restart' 13 | -------------------------------------------------------------------------------- /lib/packer/provisioners/ansible.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class Ansible < Provisioner 8 | def initialize 9 | super 10 | self.data['type'] = ANSIBLE 11 | self.add_required(['playbook_file']) 12 | end 13 | 14 | def playbook_file(filename) 15 | self.__add_string('playbook_file', filename) 16 | end 17 | 18 | def command(cmd) 19 | self.__add_string('command', cmd) 20 | end 21 | 22 | def extra_arguments(args) 23 | self.__add_array_of_strings('extra_arguments', args) 24 | end 25 | 26 | def inventory_file(filename) 27 | self.__add_string('inventory_file', filename) 28 | end 29 | 30 | def playbook_dir(dirname) 31 | self.__add_string('playbook_dir', dirname) 32 | end 33 | 34 | def playbook_paths(paths) 35 | self.__add_array_of_strings('playbook_paths', paths) 36 | end 37 | 38 | def group_vars(vars) 39 | self.__add_string('group_vars', vars) 40 | end 41 | 42 | def host_vars(vars) 43 | self.__add_string('host_vars', vars) 44 | end 45 | 46 | def role_paths(paths) 47 | self.__add_array_of_strings('role_paths', paths) 48 | end 49 | 50 | def staging_directory(dirname) 51 | self._add_string('staging_directory', dirname) 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/packer/provisioners/chef/client.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class Chef < Provisioner 8 | class Client < Chef 9 | def initialize 10 | super 11 | self.data['type'] = CHEF_CLIENT 12 | self.add_required(['server_url']) 13 | end 14 | 15 | def server_url(url) 16 | self.__add_string('server_url', url) 17 | end 18 | 19 | def chef_environment(env) 20 | self.__add_string('chef_environment', env) 21 | end 22 | 23 | def config_template(filename) 24 | self.__add_string('config_template', filename) 25 | end 26 | 27 | def execute_command(command) 28 | self.__add_string('execute_command', command) 29 | end 30 | 31 | def install_command(command) 32 | self.__add_string('install_command', command) 33 | end 34 | 35 | # TODO How to support json? 36 | 37 | def node_name(name) 38 | self.__add_string('node_name', name) 39 | end 40 | 41 | def prevent_sudo(bool) 42 | self.__add_boolean('prevent_sudo', bool) 43 | end 44 | 45 | def run_list(list) 46 | self.__add_array_of_strings('run_list', list) 47 | end 48 | 49 | def skip_clean_client(bool) 50 | self.__add_bool('skip_clean_client', bool) 51 | end 52 | 53 | def skip_clean_node(bool) 54 | self.__add_bool('skip_clean_node', bool) 55 | end 56 | 57 | def skip_install(bool) 58 | self.__add_bool('skip_install', bool) 59 | end 60 | 61 | def staging_directory(dirname) 62 | self.__add_string('staging_directory', dirname) 63 | end 64 | 65 | def validation_client_name(name) 66 | self.__add_string('validation_client_name', name) 67 | end 68 | 69 | def validation_key_path(path) 70 | self.__add_string('validation_key_path', path) 71 | end 72 | 73 | def client_key(keyname) 74 | self.__add_string('client_key', keyname) 75 | end 76 | end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/packer/provisioners/chef/solo.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class Chef < Provisioner 8 | class Solo < Chef 9 | def initialize 10 | super 11 | self.data['type'] = CHEF_SOLO 12 | #self.add_required([]) 13 | end 14 | 15 | def config_template(filename) 16 | self.__add_string('config_template', filename) 17 | end 18 | 19 | def cookbook_paths(paths) 20 | self.__add_array_of_strings('cookbook_paths', paths) 21 | end 22 | 23 | def data_bags_path(path) 24 | self.__add_string('data_bags_path', path) 25 | end 26 | 27 | def encrypted_data_bag_secret_path(path) 28 | self.__add_string('encrypted_data_bag_secret_path', path) 29 | end 30 | 31 | def execute_command(command) 32 | self.__add_string('execute_command', command) 33 | end 34 | 35 | def install_command(command) 36 | self.__add_string('install_command', command) 37 | end 38 | 39 | def json(hash) 40 | self.__add_json('json', hash) 41 | end 42 | 43 | def prevent_sudo(bool) 44 | self.__add_boolean('prevent_sudo', bool) 45 | end 46 | 47 | def remote_cookbook_paths(paths) 48 | self.__add_array_of_strings('remote_cookbook_paths', paths) 49 | end 50 | 51 | def roles_path(path) 52 | self.__add_string('roles_path', path) 53 | end 54 | 55 | def run_list(runlist) 56 | self.__add_array_of_strings('run_list', runlist) 57 | end 58 | 59 | def skip_install(bool) 60 | self.__add_boolean('skip_install', bool) 61 | end 62 | 63 | def staging_directory(dirname) 64 | self.__add_string('staging_directory', dirname) 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/packer/provisioners/file.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class File < Provisioner 8 | def initialize 9 | super 10 | self.data['type'] = FILE 11 | self.add_required('source', 'destination') 12 | end 13 | 14 | def source(filename) 15 | self.__add_string('source', filename) 16 | end 17 | 18 | def destination(filename) 19 | self.__add_string('destination', filename) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/packer/provisioners/powershell.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class Powershell < Provisioner 8 | def initialize 9 | super 10 | self.data['type'] = POWERSHELL 11 | self.add_required(['inline', 'script', 'scripts']) 12 | self.add_key_dependencies({ 13 | 'elevated_user' => ['elevated_password'], 14 | 'elevated_password' => ['elevated_user'] 15 | }) 16 | end 17 | 18 | def inline(commands) 19 | self.__add_array_of_strings('inline', commands, %w[script scripts]) 20 | end 21 | 22 | def script(filename) 23 | self.__add_string('script', filename, %w[inline scripts]) 24 | end 25 | 26 | def scripts(filenames) 27 | self.__add_array_of_strings('scripts', filenames, %w[inline script]) 28 | end 29 | 30 | def binary(bool) 31 | self.__add_boolean('binary', bool, []) 32 | end 33 | 34 | def environment_vars(envpairs) 35 | self.__add_array_of_strings('environment_vars', envpairs) 36 | end 37 | 38 | def execute_command(command) 39 | self.__add_string('execute_command', command) 40 | end 41 | 42 | def remote_path(command) 43 | self.__add_string('remote_path', command) 44 | end 45 | 46 | def start_retry_timeout(time) 47 | self.__add_string('start_retry_timeout', string) 48 | end 49 | 50 | def valid_exit_codes(codes) 51 | self.__add_array_of_ints('valid_exit_codes', codes) 52 | end 53 | 54 | def elevated_user(user) 55 | self.__add_string('elevated_user', user) 56 | end 57 | 58 | def elevated_password(password) 59 | self.__add_string('elevated_password', password) 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/packer/provisioners/puppet/masterless.rb: -------------------------------------------------------------------------------- 1 | require 'packer/provisioner' 2 | require 'packer/dataobject' 3 | 4 | module Packer 5 | class Provisioner < Packer::DataObject 6 | class Puppet < Provisioner 7 | class Masterless < Puppet 8 | def initialize 9 | super 10 | self.data['type'] = PUPPET_MASTERLESS 11 | self.add_required(['manifest_file']) 12 | end 13 | 14 | def manifest_file(filename) 15 | self.__add_string('manifest_file', filename) 16 | end 17 | 18 | def execute_command(command) 19 | self.__add_string('execute_command', command) 20 | end 21 | 22 | def facter(facts) 23 | self.__add_hash('facter', facts) 24 | end 25 | 26 | def hiera_config_path(path) 27 | self.__add_string('hiera_config_path', path) 28 | end 29 | 30 | def manifest_dir(path) 31 | self.__add_string('manifest_dir', path) 32 | end 33 | 34 | def module_paths(paths) 35 | self.__add_array_of_strings('module_paths',paths) 36 | end 37 | 38 | def prevent_sudo(flag) 39 | self.__add_boolean('prevent_sudo', flag) 40 | end 41 | 42 | def staging_directory(dirname) 43 | self.__add_string('staging_directory', dirname) 44 | end 45 | end 46 | end 47 | end 48 | end -------------------------------------------------------------------------------- /lib/packer/provisioners/puppet/server.rb: -------------------------------------------------------------------------------- 1 | require 'packer/provisioner' 2 | require 'packer/dataobject' 3 | 4 | module Packer 5 | class Provisioner < Packer::DataObject 6 | class Puppet < Provisioner 7 | class Server < Puppet 8 | def initialize 9 | super 10 | self.data['type'] = PUPPET_SERVER 11 | end 12 | 13 | def client_cert_path(path) 14 | self.__add_string('client_cert_path', path) 15 | end 16 | 17 | def client_private_key_path(path) 18 | self.__add_string('client_private_key_path', path) 19 | end 20 | 21 | def facter(facts) 22 | self.__add_hash('facter', facts) 23 | end 24 | 25 | def options(opts) 26 | self.__add_string('options', opts) 27 | end 28 | 29 | def prevent_sudo(flag) 30 | self.__add_boolean('prevent_sudo', flag) 31 | end 32 | 33 | def puppet_node(nodename) 34 | self.__add_string('puppet_node', nodename) 35 | end 36 | 37 | def puppet_server(servername) 38 | self.__add_string('puppet_server', servername) 39 | end 40 | 41 | def staging_directory(dirname) 42 | self.__add_string('staging_directory', dirname) 43 | end 44 | end 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /lib/packer/provisioners/salt.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class Salt < Provisioner 8 | def initialize 9 | super 10 | self.data['type'] = SALT 11 | self.add_required(['local_state_tree']) 12 | end 13 | 14 | def bootstrap_args(args) 15 | self.__add_string('bootstrap_args', args) 16 | end 17 | 18 | def local_pillar_roots(dirname) 19 | self.__add_string('local_pillar_roots', dirname) 20 | end 21 | 22 | def local_state_tree(dirname) 23 | self.__add_string('local_state_tree', dirname) 24 | end 25 | 26 | def minion_config(filename) 27 | self.__add_string('minion_config', filename) 28 | end 29 | 30 | def temp_config_dir(dirname) 31 | self.__add_string('temp_config_dir', dirname) 32 | end 33 | 34 | def skip_bootstrap(bool) 35 | self.__add_boolean('skip_bootstrap', bool) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/packer/provisioners/shell.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class Shell < Provisioner 8 | def initialize 9 | super 10 | self.data['type'] = SHELL 11 | self.add_required(['inline', 'script', 'scripts']) 12 | end 13 | 14 | def inline(commands) 15 | self.__add_array_of_strings('inline', commands, %w[script scripts]) 16 | end 17 | 18 | def script(filename) 19 | self.__add_string('script', filename, %w[inline scripts]) 20 | end 21 | 22 | def scripts(filenames) 23 | self.__add_array_of_strings('scripts', filenames, %w[inline script]) 24 | end 25 | 26 | def binary(bool) 27 | self.__add_boolean('binary', bool, []) 28 | end 29 | 30 | def environment_vars(envpairs) 31 | self.__add_array_of_strings('environment_vars', envpairs) 32 | end 33 | 34 | def execute_command(command) 35 | self.__add_string('execute_command', command) 36 | end 37 | 38 | def inline_shebang(command) 39 | self.__add_string('inline_shebang', command) 40 | end 41 | 42 | def remote_path(command) 43 | self.__add_string('remote_path', command) 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/packer/provisioners/windows_restart.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class WindowsRestart < Provisioner 8 | def initialize 9 | super 10 | self.data['type'] = WINDOWS_RESTART 11 | end 12 | 13 | def restart_command(command) 14 | self.__add_string('restart_command', command) 15 | end 16 | 17 | def restart_check_command(command) 18 | self.__add_string('restart_check_command', command) 19 | end 20 | 21 | def restart_timeout(timeout) 22 | self.__add_string('restart_timeout', timeout) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/packer/provisioners/windows_shell.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer/provisioner' 3 | require 'packer/dataobject' 4 | 5 | module Packer 6 | class Provisioner < Packer::DataObject 7 | class WindowsShell < Provisioner 8 | def initialize 9 | super 10 | self.data['type'] = WINDOWS_SHELL 11 | self.add_required(['inline', 'script', 'scripts']) 12 | end 13 | 14 | def inline(commands) 15 | self.__add_array_of_strings('inline', commands, %w[script scripts]) 16 | end 17 | 18 | def script(filename) 19 | self.__add_string('script', filename, %w[inline scripts]) 20 | end 21 | 22 | def scripts(filenames) 23 | self.__add_array_of_strings('scripts', filenames, %w[inline script]) 24 | end 25 | 26 | def binary(bool) 27 | self.__add_boolean('binary', bool, []) 28 | end 29 | 30 | def environment_vars(envpairs) 31 | self.__add_array_of_strings('environment_vars', envpairs) 32 | end 33 | 34 | def execute_command(command) 35 | self.__add_string('execute_command', command) 36 | end 37 | 38 | def remote_path(command) 39 | self.__add_string('remote_path', command) 40 | end 41 | 42 | def start_retry_timeout(time) 43 | self.__add_string('start_retry_timeout', time) 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/packer/runner.rb: -------------------------------------------------------------------------------- 1 | require 'open3' 2 | require 'shellwords' 3 | 4 | module Packer 5 | class Runner 6 | class CommandExecutionError < StandardError 7 | end 8 | 9 | def self.run!(*args, quiet: false) 10 | cmd = Shellwords.shelljoin(args.flatten) 11 | 12 | debug = cmd.include? '-debug' 13 | 14 | status = 0 15 | stdout = '' 16 | stderr = '' 17 | if quiet && !debug 18 | # Run without streaming std* to any screen 19 | stdout, stderr, status = Open3.capture3(cmd) 20 | else 21 | # Run but stream as well as capture stdout to the screen 22 | # see: http://stackoverflow.com/a/1162850/83386 23 | Open3.popen3(cmd) do |_std_in, std_out, std_err, thread| 24 | # read each stream from a new thread 25 | Thread.new do 26 | until (raw = std_out.getc).nil? do 27 | stdout << raw 28 | $stdout.write raw.to_s 29 | end 30 | end 31 | Thread.new do 32 | until (raw_line = std_err.gets).nil? do 33 | stderr << raw_line 34 | end 35 | end 36 | 37 | Thread.new do 38 | std_in.puts $stdin.gets while thread.alive? 39 | end 40 | 41 | thread.join # don't exit until the external process is done 42 | status = thread.value 43 | end 44 | end 45 | # rubocop:disable Style/NumericPredicate 46 | raise CommandExecutionError.new(stderr) unless status == 0 47 | 48 | stdout 49 | # rubocop:enable Style/NumericPredicate 50 | end 51 | 52 | def self.exec!(*args) 53 | cmd = Shellwords.shelljoin(args.flatten) 54 | logger.debug "Exec'ing: #{cmd}, in: #{Dir.pwd}" 55 | Kernel.exec cmd 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/packer/version.rb: -------------------------------------------------------------------------------- 1 | module Packer 2 | VERSION = '1.6.5'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /packer-config.gemspec: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | lib = File.expand_path('lib', __dir__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'packer/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'packer-config' 8 | spec.version = Packer::VERSION 9 | spec.authors = ["Ian Chesal", "Fraser Cobb", "Greg Poirier", "Matasano Security", "Greg Diamond", "Enzo Rivello", "Edwin Biemond", "Andrei Shiniti Nakagawa", "Kevin Formsma", "sabishiii"] 10 | spec.email = %w(ian.chesal@gmail.com) 11 | spec.summary = 'An object model to build packer.io configurations in Ruby.' 12 | spec.description = <<-END 13 | Building the Packer JSON configurations in raw JSON can be quite an adventure. 14 | There's limited facilities for variable expansion and absolutely no support for 15 | nice things like comments. I decided it would just be easier to have an object 16 | model to build the Packer configurations in that would easily write to the 17 | correct JSON format. It also saved me having to remember the esoteric Packer 18 | syntax for referencing variables and whatnot in the JSON. 19 | END 20 | spec.homepage = 'https://github.com/ianchesal/packer-config' 21 | spec.license = 'Apache-2.0' 22 | spec.required_ruby_version = '>= 2.4.0' 23 | 24 | spec.files = `git ls-files -z`.split("\x0") 25 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 26 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 27 | spec.require_paths = %w(lib) 28 | end 29 | -------------------------------------------------------------------------------- /spec/integration/README.md: -------------------------------------------------------------------------------- 1 | # Integration Testing packer-config 2 | 3 | You can run the integration tests via the Rakefile with: 4 | 5 | bundle exec rake test:integration 6 | 7 | The integration tests actually run Packer builds and can take some time to run. As of now I've only run them on an OS X system but, in theory, they should run on any system where the `packer` command and VirtualBox are available. 8 | 9 | The provisioning and kickstart scripts are largely taken from the [OpsCode Bento](https://github.com/opscode/bento) project which is most awesome and you should check it out. 10 | 11 | ## Requirements 12 | 13 | * [Packer.io](http://www.packer.io/downloads.html) 14 | * [VirtualBox](https://www.virtualbox.org/wiki/Downloads) -------------------------------------------------------------------------------- /spec/integration/builds/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /spec/integration/centos_vagrant_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | CENTOS_VERSION = '7' 5 | 6 | RSpec.describe Packer::Config do 7 | it 'can build a centos-7 Vagrant base box' do 8 | pconfig = Packer::Config.new "centos-#{CENTOS_VERSION}-vagrant.json" 9 | pconfig.description "CentOS #{CENTOS_VERSION} VirtualBox Vagrant" 10 | pconfig.add_variable 'mirror', 'http://mirrors.sonic.net/centos' 11 | pconfig.add_variable 'mirror', 'http://ftp.pbone.net/pub/centos' 12 | pconfig.add_variable 'my_version', '0.0.1' 13 | pconfig.add_variable 'chef_version', 'latest' 14 | 15 | builder = pconfig.add_builder Packer::Builder::VIRTUALBOX_ISO 16 | builder.boot_command [" text ks=http://#{pconfig.macro.HTTPIP}:#{pconfig.macro.HTTPPort}/centos-#{CENTOS_VERSION}-ks.cfg"] 17 | builder.boot_wait '10s' 18 | builder.disk_size 40_960 19 | builder.guest_additions_path "VBoxGuestAdditions_#{pconfig.macro.Version}.iso" 20 | builder.guest_os_type "RedHat_64" 21 | builder.http_directory "scripts/kickstart" 22 | builder.iso_checksum '38d5d51d9d100fd73df031ffd6bd8b1297ce24660dc8c13a3b8b4534a4bd291c' 23 | builder.iso_checksum_type 'sha256' 24 | builder.iso_url "#{pconfig.variable 'mirror'}/#{CENTOS_VERSION}/isos/x86_64/CentOS-7-x86_64-Minimal-1810.iso" 25 | builder.output_directory "centos-#{CENTOS_VERSION}-x86_64-virtualbox" 26 | builder.shutdown_command "echo 'vagrant'|sudo -S /sbin/halt -h -p" 27 | builder.communicator "ssh" 28 | builder.ssh_password "vagrant" 29 | builder.ssh_port 22 30 | builder.ssh_username "vagrant" 31 | builder.ssh_timeout "10000s" 32 | builder.vboxmanage [ 33 | [ 34 | "modifyvm", 35 | pconfig.macro.Name, 36 | "--memory", 37 | "480" 38 | ], 39 | [ 40 | "modifyvm", 41 | pconfig.macro.Name, 42 | "--cpus", 43 | "1" 44 | ] 45 | ] 46 | builder.virtualbox_version_file ".vbox_version" 47 | builder.vm_name "packer-centos-#{CENTOS_VERSION}-x86_64" 48 | 49 | provisioner = pconfig.add_provisioner Packer::Provisioner::FILE 50 | provisioner.source 'scripts/hello.sh' 51 | provisioner.destination '/home/vagrant/hello.sh' 52 | 53 | provisioner = pconfig.add_provisioner Packer::Provisioner::SHELL 54 | provisioner.scripts [ 55 | 'scripts/sshd.sh', 56 | 'scripts/vagrant.sh' 57 | ] 58 | provisioner.environment_vars [ 59 | "CHEF_VERSION=#{pconfig.variable 'chef_version'}", 60 | "MY_CENTOS_VERSION=#{pconfig.variable 'my_version'}" 61 | ] 62 | provisioner.execute_command "echo 'vagrant' | #{pconfig.macro.Vars} sudo -S -E bash '#{pconfig.macro.Path}'" 63 | 64 | postprocessor = pconfig.add_postprocessor Packer::PostProcessor::VAGRANT 65 | postprocessor.output File.join('builds', pconfig.macro.Provider, "centos-#{CENTOS_VERSION}-x86_64-#{pconfig.variable 'my_version'}.box") 66 | 67 | Dir.chdir('spec/integration') 68 | expect(pconfig.validate).to be_truthy 69 | expect(pconfig.build).to be_truthy 70 | Dir.chdir('../..') 71 | end 72 | end 73 | 74 | -------------------------------------------------------------------------------- /spec/integration/packer_cache/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /spec/integration/scripts/chef.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # WARNING: REQUIRES /bin/sh 3 | # 4 | # - must run on /bin/sh on solaris 9 5 | # - must run on /bin/sh on AIX 6.x 6 | # - if you think you are a bash wizard, you probably do not understand 7 | # this programming language. do not touch. 8 | # - if you are under 40, get peer review from your elders. 9 | # 10 | # Author:: Julian C. Dunn () 11 | # Cribbed Code From:: Lamont Granquist, Seth Chisamore, Stephen Delano & Tyler Cloke 12 | # Copyright:: Copyright (c) 2013, Chef Software, Inc. 13 | # License:: Apache License, Version 2.0 14 | # 15 | # Licensed under the Apache License, Version 2.0 (the "License"); 16 | # you may not use this file except in compliance with the License. 17 | # You may obtain a copy of the License at 18 | # 19 | # http://www.apache.org/licenses/LICENSE-2.0 20 | # 21 | # Unless required by applicable law or agreed to in writing, software 22 | # distributed under the License is distributed on an "AS IS" BASIS, 23 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | # See the License for the specific language governing permissions and 25 | # limitations under the License. 26 | # 27 | 28 | # Set $CHEF_VERSION inside Packer's template. Valid options are: 29 | # 'provisionerless' -- build a box without Chef 30 | # 'x.y.z' -- build a box with version x.y.z of Chef 31 | # 'latest' -- build a box with the latest version of Chef 32 | # 'prerelease' -- build a box with a prerelease version of Chef 33 | 34 | chef_installer="/tmp/install-chef.sh" 35 | chef_installer_url="https://www.getchef.com/chef/install.sh" 36 | 37 | # Check whether a command exists - returns 0 if it does, 1 if it does not 38 | exists() { 39 | if command -v $1 >/dev/null 2>&1 40 | then 41 | return 0 42 | else 43 | return 1 44 | fi 45 | } 46 | 47 | unable_to_retrieve_package() { 48 | echo "Unable to retrieve install.sh!" 49 | if test "x$stderr_results" != "x"; then 50 | echo "\nDEBUG OUTPUT FOLLOWS:\n$stderr_results" 51 | fi 52 | exit 1 53 | } 54 | 55 | capture_tmp_stderr() { 56 | # spool up /tmp/stderr from all the commands we called 57 | if test -f "/tmp/stderr"; then 58 | output=`cat /tmp/stderr` 59 | stderr_results="${stderr_results}\nSTDERR from $1:\n\n$output\n" 60 | rm /tmp/stderr 61 | fi 62 | } 63 | 64 | # do_wget URL FILENAME 65 | do_wget() { 66 | echo "trying wget..." 67 | wget -O "$2" "$1" 2>/tmp/stderr 68 | rc=$? 69 | # check for 404 70 | grep "ERROR 404" /tmp/stderr 2>&1 >/dev/null 71 | if test $? -eq 0; then 72 | echo "ERROR 404" 73 | unable_to_retrieve_package 74 | fi 75 | 76 | # check for bad return status or empty output 77 | if test $rc -ne 0 || test ! -s "$2"; then 78 | capture_tmp_stderr "wget" 79 | return 1 80 | fi 81 | 82 | return 0 83 | } 84 | 85 | # do_curl URL FILENAME 86 | do_curl() { 87 | echo "trying curl..." 88 | curl -sL -D /tmp/stderr "$1" > "$2" 89 | rc=$? 90 | # check for 404 91 | grep "404 Not Found" /tmp/stderr 2>&1 >/dev/null 92 | if test $? -eq 0; then 93 | echo "ERROR 404" 94 | unable_to_retrieve_package 95 | fi 96 | 97 | # check for bad return status or empty output 98 | if test $rc -ne 0 || test ! -s "$2"; then 99 | capture_tmp_stderr "curl" 100 | return 1 101 | fi 102 | 103 | return 0 104 | } 105 | 106 | # do_fetch URL FILENAME 107 | do_fetch() { 108 | echo "trying fetch..." 109 | fetch -o "$2" "$1" 2>/tmp/stderr 110 | # check for bad return status 111 | test $? -ne 0 && return 1 112 | return 0 113 | } 114 | 115 | # do_curl URL FILENAME 116 | do_perl() { 117 | echo "trying perl..." 118 | perl -e 'use LWP::Simple; getprint($ARGV[0]);' "$1" > "$2" 2>/tmp/stderr 119 | rc=$? 120 | # check for 404 121 | grep "404 Not Found" /tmp/stderr 2>&1 >/dev/null 122 | if test $? -eq 0; then 123 | echo "ERROR 404" 124 | unable_to_retrieve_package 125 | fi 126 | 127 | # check for bad return status or empty output 128 | if test $rc -ne 0 || test ! -s "$2"; then 129 | capture_tmp_stderr "perl" 130 | return 1 131 | fi 132 | 133 | return 0 134 | } 135 | 136 | # do_curl URL FILENAME 137 | do_python() { 138 | echo "trying python..." 139 | python -c "import sys,urllib2 ; sys.stdout.write(urllib2.urlopen(sys.argv[1]).read())" "$1" > "$2" 2>/tmp/stderr 140 | rc=$? 141 | # check for 404 142 | grep "HTTP Error 404" /tmp/stderr 2>&1 >/dev/null 143 | if test $? -eq 0; then 144 | echo "ERROR 404" 145 | unable_to_retrieve_package 146 | fi 147 | 148 | # check for bad return status or empty output 149 | if test $rc -ne 0 || test ! -s "$2"; then 150 | capture_tmp_stderr "python" 151 | return 1 152 | fi 153 | return 0 154 | } 155 | 156 | do_download() { 157 | echo "downloading $1" 158 | echo " to file $2" 159 | 160 | # we try all of these until we get success. 161 | # perl, in particular may be present but LWP::Simple may not be installed 162 | 163 | if exists wget; then 164 | do_wget $1 $2 && return 0 165 | fi 166 | 167 | if exists curl; then 168 | do_curl $1 $2 && return 0 169 | fi 170 | 171 | if exists fetch; then 172 | do_fetch $1 $2 && return 0 173 | fi 174 | 175 | if exists perl; then 176 | do_perl $1 $2 && return 0 177 | fi 178 | 179 | if exists python; then 180 | do_python $1 $2 && return 0 181 | fi 182 | 183 | unable_to_retrieve_package 184 | } 185 | 186 | if [ x$CHEF_VERSION != x'provisionerless' ]; then 187 | do_download "$chef_installer_url" "$chef_installer" 188 | chmod +x $chef_installer 189 | if [ x$CHEF_VERSION == x'latest' ]; then 190 | $chef_installer 191 | elif [ x$CHEF_VERSION == x'prerelease' ]; then 192 | $chef_installer -p 193 | else 194 | $chef_installer -v $CHEF_VERSION 195 | fi 196 | rm -f $chef_installer 197 | else 198 | echo "Building a box without Chef" 199 | fi 200 | -------------------------------------------------------------------------------- /spec/integration/scripts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | # From: https://github.com/opscode/bento 4 | 5 | # These were only needed for building VMware/Virtualbox extensions: 6 | # yum -y remove gcc cpp kernel-devel kernel-headers perl 7 | yum -y clean all 8 | rm -rf VBoxGuestAdditions_*.iso VBoxGuestAdditions_*.iso.? 9 | rm -f /tmp/chef*rpm 10 | 11 | # clean up redhat interface persistence 12 | rm -f /etc/udev/rules.d/70-persistent-net.rules 13 | if [ -r /etc/sysconfig/network-scripts/ifcfg-eth0 ]; then 14 | sed -i 's/^HWADDR.*$//' /etc/sysconfig/network-scripts/ifcfg-eth0 15 | sed -i 's/^UUID.*$//' /etc/sysconfig/network-scripts/ifcfg-eth0 16 | fi 17 | 18 | -------------------------------------------------------------------------------- /spec/integration/scripts/fix-slow-dns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | # From: https://github.com/opscode/bento 4 | 5 | if [[ "$PACKER_BUILDER_TYPE" == virtualbox* ]]; then 6 | ## https://access.redhat.com/site/solutions/58625 (subscription required) 7 | # add 'single-request-reopen' so it is included when /etc/resolv.conf is generated 8 | echo 'RES_OPTIONS="single-request-reopen"' >> /etc/sysconfig/network 9 | service network restart 10 | echo 'Slow DNS fix applied (single-request-reopen)' 11 | else 12 | echo 'Slow DNS fix not required for this platform, skipping' 13 | fi 14 | -------------------------------------------------------------------------------- /spec/integration/scripts/hello.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | echo 'Hello, world!' -------------------------------------------------------------------------------- /spec/integration/scripts/kickstart/centos-7-ks.cfg: -------------------------------------------------------------------------------- 1 | install 2 | cdrom 3 | lang en_US.UTF-8 4 | keyboard us 5 | network --bootproto=dhcp --onboot=on --device=eth0 6 | rootpw vagrant 7 | firewall --disabled 8 | selinux --permissive 9 | timezone UTC 10 | unsupported_hardware 11 | bootloader --location=mbr --append="net.ifnames=0 biosdevname=0" 12 | text 13 | skipx 14 | zerombr 15 | clearpart --all --initlabel 16 | autopart 17 | auth --enableshadow --passalgo=sha512 --kickstart 18 | firstboot --disabled 19 | reboot --eject 20 | user --name=vagrant --plaintext --password vagrant 21 | 22 | %packages --nobase --ignoremissing --excludedocs --instLangs=en_US.utf8 23 | # vagrant needs this to copy initial files via scp 24 | openssh-clients 25 | sudo 26 | kernel-headers 27 | kernel-devel 28 | gcc 29 | make 30 | perl 31 | selinux-policy-devel 32 | wget 33 | nfs-utils 34 | net-tools 35 | bzip2 36 | deltarpm 37 | -fprintd-pam 38 | -intltool 39 | 40 | # unnecessary firmware 41 | -aic94xx-firmware 42 | -alsa-firmware 43 | -alsa-tools-firmware 44 | -ivtv-firmware 45 | -iwl100-firmware 46 | -iwl105-firmware 47 | -iwl135-firmware 48 | -iwl1000-firmware 49 | -iwl2000-firmware 50 | -iwl2030-firmware 51 | -iwl3160-firmware 52 | -iwl3945-firmware 53 | -iwl4965-firmware 54 | -iwl5000-firmware 55 | -iwl5150-firmware 56 | -iwl6000-firmware 57 | -iwl6000g2a-firmware 58 | -iwl6000g2b-firmware 59 | -iwl6050-firmware 60 | -iwl7260-firmware 61 | -iwl7265-firmware 62 | %end 63 | 64 | %post 65 | # sudo 66 | echo "%vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vagrant 67 | chmod 0440 /etc/sudoers.d/vagrant 68 | 69 | #Enable hyper-v daemons only if using hyper-v virtualization 70 | if [ $(virt-what) == "hyperv" ]; then 71 | yum -y install hyperv-daemons cifs-utils 72 | systemctl enable hypervvssd 73 | systemctl enable hypervkvpd 74 | fi 75 | 76 | # Since we disable consistent network naming, we need to make sure the eth0 77 | # configuration file is in place so it will come up. 78 | # Delete other network configuration first because RHEL/C7 networking will not 79 | # restart successfully if there are configuration files for devices that do not 80 | # exist. 81 | rm -f /etc/sysconfig/network-scripts/ifcfg-e* 82 | cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << _EOF_ 83 | TYPE=Ethernet 84 | PROXY_METHOD=none 85 | BROWSER_ONLY=no 86 | BOOTPROTO=dhcp 87 | DEFROUTE=yes 88 | IPV4_FAILURE_FATAL=no 89 | IPV6INIT=yes 90 | IPV6_AUTOCONF=yes 91 | IPV6_DEFROUTE=yes 92 | IPV6_FAILURE_FATAL=no 93 | IPV6_ADDR_GEN_MODE=stable-privacy 94 | NAME=eth0 95 | DEVICE=eth0 96 | ONBOOT=yes 97 | _EOF_ 98 | %end 99 | -------------------------------------------------------------------------------- /spec/integration/scripts/minimize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | # From: https://github.com/opscode/bento 4 | 5 | case "$PACKER_BUILDER_TYPE" in 6 | qemu) exit 0 ;; 7 | esac 8 | 9 | # Whiteout root 10 | count=$(df --sync -kP / | tail -n1 | awk -F ' ' '{print $4}') 11 | count=$(($count-1)) 12 | dd if=/dev/zero of=/tmp/whitespace bs=1M count=$count || echo "dd exit code $? is suppressed"; 13 | rm /tmp/whitespace 14 | 15 | # Whiteout /boot 16 | count=$(df --sync -kP /boot | tail -n1 | awk -F ' ' '{print $4}') 17 | count=$(($count-1)) 18 | dd if=/dev/zero of=/boot/whitespace bs=1M count=$count || echo "dd exit code $? is suppressed"; 19 | rm /boot/whitespace 20 | 21 | set +e 22 | swapuuid="`/sbin/blkid -o value -l -s UUID -t TYPE=swap`"; 23 | case "$?" in 24 | 2|0) ;; 25 | *) exit 1 ;; 26 | esac 27 | set -e 28 | 29 | if [ "x${swapuuid}" != "x" ]; then 30 | # Whiteout the swap partition to reduce box size 31 | # Swap is disabled till reboot 32 | swappart="`readlink -f /dev/disk/by-uuid/$swapuuid`"; 33 | /sbin/swapoff "$swappart"; 34 | dd if=/dev/zero of="$swappart" bs=1M || echo "dd exit code $? is suppressed"; 35 | /sbin/mkswap -U "$swapuuid" "$swappart"; 36 | fi 37 | 38 | sync; 39 | -------------------------------------------------------------------------------- /spec/integration/scripts/sshd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | # From: https://github.com/opscode/bento 4 | 5 | SSHD_CONFIG="/etc/ssh/sshd_config" 6 | 7 | # ensure that there is a trailing newline before attempting to concatenate 8 | sed -i -e '$a\' "$SSHD_CONFIG" 9 | 10 | USEDNS="UseDNS no" 11 | if grep -q -E "^[[:space:]]*UseDNS" "$SSHD_CONFIG"; then 12 | sed -i "s/^\s*UseDNS.*/${USEDNS}/" "$SSHD_CONFIG" 13 | else 14 | echo "$USEDNS" >>"$SSHD_CONFIG" 15 | fi 16 | 17 | GSSAPI="GSSAPIAuthentication no" 18 | if grep -q -E "^[[:space:]]*GSSAPIAuthentication" "$SSHD_CONFIG"; then 19 | sed -i "s/^\s*GSSAPIAuthentication.*/${GSSAPI}/" "$SSHD_CONFIG" 20 | else 21 | echo "$GSSAPI" >>"$SSHD_CONFIG" 22 | fi 23 | -------------------------------------------------------------------------------- /spec/integration/scripts/vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | # From: https://github.com/opscode/bento 4 | 5 | # set a default HOME_DIR environment variable if not set 6 | HOME_DIR="${HOME_DIR:-/home/vagrant}"; 7 | 8 | pubkey_url="https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub"; 9 | mkdir -p $HOME_DIR/.ssh; 10 | if command -v wget >/dev/null 2>&1; then 11 | wget --no-check-certificate "$pubkey_url" -O $HOME_DIR/.ssh/authorized_keys; 12 | elif command -v curl >/dev/null 2>&1; then 13 | curl --insecure --location "$pubkey_url" > $HOME_DIR/.ssh/authorized_keys; 14 | elif command -v fetch >/dev/null 2>&1; then 15 | fetch -am -o $HOME_DIR/.ssh/authorized_keys "$pubkey_url"; 16 | else 17 | echo "Cannot download vagrant public key"; 18 | exit 1; 19 | fi 20 | chown -R vagrant $HOME_DIR/.ssh; 21 | chmod -R go-rwsx $HOME_DIR/.ssh; 22 | -------------------------------------------------------------------------------- /spec/integration/scripts/vmtools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | # From: https://github.com/opscode/bento 4 | 5 | # set a default HOME_DIR environment variable if not set 6 | HOME_DIR="${HOME_DIR:-/home/vagrant}"; 7 | 8 | case "$PACKER_BUILDER_TYPE" in 9 | virtualbox-iso|virtualbox-ovf) 10 | VER="`cat $HOME_DIR/.vbox_version`"; 11 | ISO="VBoxGuestAdditions_$VER.iso"; 12 | mkdir -p /tmp/vbox; 13 | mount -o loop $HOME_DIR/$ISO /tmp/vbox; 14 | sh /tmp/vbox/VBoxLinuxAdditions.run \ 15 | || echo "VBoxLinuxAdditions.run exited $? and is suppressed." \ 16 | "For more read https://www.virtualbox.org/ticket/12479"; 17 | umount /tmp/vbox; 18 | rm -rf /tmp/vbox; 19 | rm -f $HOME_DIR/*.iso; 20 | ;; 21 | esac 22 | -------------------------------------------------------------------------------- /spec/packer/builder_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | VALID_BUILDER_TYPE = Packer::Builder::VIRTUALBOX_ISO 5 | 6 | RSpec.describe Packer::Builder do 7 | let(:builder) { Packer::Builder.new } 8 | 9 | describe '.get_builder' do 10 | it 'returns a builder' do 11 | expect(Packer::Builder.get_builder(VALID_BUILDER_TYPE)).to be_a_kind_of(Packer::Builder) 12 | end 13 | 14 | it 'raises an error when the builder type is not recognized' do 15 | expect { Packer::Builder.get_builder('unknown-type') }.to raise_error 16 | end 17 | end 18 | 19 | describe '#name' do 20 | it 'lets you set a custom name on the builder instance' do 21 | builder.name('fancy name') 22 | expect(builder.data['name']).to eq('fancy name') 23 | builder.data.delete('name') 24 | end 25 | end 26 | 27 | describe '#communicator' do 28 | it 'raises an error if you try to set an invalid communicator' do 29 | expect { builder.communicator 'foo' }.to raise_error Packer::DataObject::DataValidationError 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/packer/builders/amazon_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Builder::Amazon do 5 | let(:builder) { Packer::Builder::Amazon.new } 6 | let(:block_device_mappings) do 7 | [{ 8 | "device_name" => "/dev/sdb", 9 | "virtual_name" => "ephemeral0" 10 | }, 11 | { 12 | "device_name" => "/dev/sdc", 13 | "virtual_name" => "ephemeral1" 14 | }] 15 | end 16 | 17 | let(:tags) do 18 | { 19 | "a" => '1', 20 | "b" => '2' 21 | } 22 | end 23 | 24 | describe '#ami_block_device_mappings' do 25 | it 'adds a block device mappings data structure' do 26 | builder.ami_block_device_mappings(block_device_mappings) 27 | expect(builder.data['ami_block_device_mappings']).to eq(block_device_mappings) 28 | builder.data.delete('ami_block_device_mappings') 29 | end 30 | end 31 | 32 | describe '#launch_block_device_mappings' do 33 | it 'adds a block device mappings data structure' do 34 | builder.launch_block_device_mappings(block_device_mappings) 35 | expect(builder.data['launch_block_device_mappings']).to eq(block_device_mappings) 36 | builder.data.delete('launch_block_device_mappings') 37 | end 38 | end 39 | 40 | describe '#tags' do 41 | it 'adds a hash of strings to the key' do 42 | builder.tags(tags) 43 | expect(builder.data['tags']).to eq(tags) 44 | builder.data.delete('tags') 45 | end 46 | end 47 | 48 | describe '#run_tags' do 49 | it 'adds a hash of strings to the key' do 50 | builder.run_tags(tags) 51 | expect(builder.data['run_tags']).to eq(tags) 52 | builder.data.delete('run_tags') 53 | end 54 | end 55 | end 56 | 57 | RSpec.describe Packer::Builder::Amazon::EBS do 58 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::AMAZON_EBS) } 59 | 60 | it 'has a type of amazon-ebs' do 61 | expect(builder.data['type']).to eq(Packer::Builder::AMAZON_EBS) 62 | end 63 | 64 | it 'requires ami_name, instance_type, region, source_ami, and communicator' do 65 | expect { builder.validate }.to raise_error(Packer::DataObject::DataValidationError) 66 | builder.ami_name 'foo' 67 | builder.instance_type 'foo' 68 | builder.region 'foo' 69 | builder.source_ami 'foo' 70 | builder.communicator 'ssh' 71 | expect { builder.validate }.not_to raise_error 72 | end 73 | end 74 | 75 | RSpec.describe Packer::Builder::Amazon::Instance do 76 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::AMAZON_INSTANCE) } 77 | 78 | it 'has a type of amazon-instance' do 79 | expect(builder.data['type']).to eq(Packer::Builder::AMAZON_INSTANCE) 80 | end 81 | 82 | it 'requires ami_name, instance_type, region, source_ami, account_id, s3_bucket, x509_cert_path, x509_key_path, and communicator' do 83 | expect { builder.validate }.to raise_error(Packer::DataObject::DataValidationError) 84 | builder.ami_name 'foo' 85 | builder.instance_type 'foo' 86 | builder.region 'foo' 87 | builder.source_ami 'foo' 88 | builder.account_id 'foo' 89 | builder.s3_bucket 'foo' 90 | builder.x509_cert_path 'foo' 91 | builder.x509_key_path 'foo' 92 | builder.communicator 'ssh' 93 | expect { builder.validate }.not_to raise_error 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /spec/packer/builders/docker_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Builder::Docker do 5 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::DOCKER) } 6 | 7 | describe '#initialize' do 8 | it 'has a type of docker' do 9 | expect(builder.data['type']).to eq(Packer::Builder::DOCKER) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/packer/builders/null_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Builder::Null do 5 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::NULL) } 6 | let(:some_string) { 'some string' } 7 | 8 | it 'has a type of null' do 9 | expect(builder.data['type']).to eq(Packer::Builder::NULL) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/packer/builders/qemu_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Builder::Qemu do 5 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::QEMU) } 6 | let(:in_commands_mixed) { [["command1", 1], ["command2", 2]] } 7 | let(:out_commands_strings) { [["command1", "1"], ["command2", "2"]] } 8 | 9 | it 'has a type of qemu' do 10 | expect(builder.data['type']).to eq(Packer::Builder::QEMU) 11 | end 12 | 13 | it 'requires iso_checksum, iso_checksum_type, iso_url, and communicator' do 14 | expect { builder.validate }.to raise_error(Packer::DataObject::DataValidationError) 15 | builder.iso_checksum '88197272b2a442402820fcc788a8cc7a' 16 | builder.iso_checksum_type "MD5" 17 | builder.iso_url 'path' 18 | builder.communicator 'ssh' 19 | expect { builder.validate }.not_to raise_error 20 | end 21 | 22 | describe '#qemuargs' do 23 | it 'builds an array of arrays of strings' do 24 | builder.qemuargs(in_commands_mixed) 25 | expect( builder.data['qemuargs'] ).to eq(out_commands_strings) 26 | builder.data.delete('qemuargs') 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/packer/builders/virtualbox_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Builder::VirtualBoxISO do 5 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::VIRTUALBOX_ISO) } 6 | let(:in_commands_strings) { [["command1", "1"], ["command2", "2"]] } 7 | let(:in_commands_mixed) { [["command1", 1], ["command2", 2]] } 8 | let(:out_commands_strings) { [["command1", "1"], ["command2", "2"]] } 9 | 10 | it 'has a type of virtualbox-iso' do 11 | expect(builder.data['type']).to eq(Packer::Builder::VIRTUALBOX_ISO) 12 | end 13 | 14 | it 'requires iso_checksum, iso_checksum_type, iso_url and communicator' do 15 | expect { builder.validate }.to raise_error(Packer::DataObject::DataValidationError) 16 | builder.iso_checksum '88197272b2a442402820fcc788a8cc7a' 17 | builder.iso_checksum_type "MD5" 18 | builder.iso_url 'path' 19 | builder.communicator 'ssh' 20 | expect { builder.validate }.not_to raise_error 21 | end 22 | 23 | describe '#vboxmanage' do 24 | it 'builds an array of arrays of strings' do 25 | builder.vboxmanage(in_commands_mixed) 26 | expect( builder.data['vboxmanage'] ).to eq(out_commands_strings) 27 | builder.data.delete('vboxmanage') 28 | end 29 | end 30 | 31 | describe '#vboxmanage_post' do 32 | it 'builds an array of arrays of strings' do 33 | builder.vboxmanage_post(in_commands_mixed) 34 | expect( builder.data['vboxmanage_post'] ).to eq(out_commands_strings) 35 | builder.data.delete('vboxmanage_post') 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/packer/builders/vmware_iso_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Builder::VMWareISO do 5 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::VMWARE_ISO) } 6 | 7 | it 'has a type of VMWare ISO' do 8 | expect(builder.data['type']).to eq(Packer::Builder::VMWARE_ISO) 9 | end 10 | 11 | it 'requires iso_checksum, iso_checksum_type, iso_url and communicator' do 12 | expect { builder.validate }.to raise_error(Packer::DataObject::DataValidationError) 13 | builder.iso_checksum '88197272b2a442402820fcc788a8cc7a' 14 | builder.iso_checksum_type "MD5" 15 | builder.iso_url 'path' 16 | builder.communicator 'ssh' 17 | expect { builder.validate }.not_to raise_error 18 | end 19 | 20 | describe '#vmx_data' do 21 | it 'adds a hash of arbitrary data' do 22 | builder.vmx_data( 23 | key1: 'value1', 24 | key2: 'value2' 25 | ) 26 | expect(builder.data['vmx_data'].keys.length).to eq(2) 27 | end 28 | end 29 | 30 | describe '#vmx_data_post' do 31 | it 'adds a hash of arbitrary data' do 32 | builder.vmx_data_post( 33 | key1: 'value1', 34 | key2: 'value2' 35 | ) 36 | expect(builder.data['vmx_data_post'].keys.length).to eq(2) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/packer/builders/vmware_vmx_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Builder::VMWareVMX do 5 | let(:builder) { Packer::Builder.get_builder(Packer::Builder::VMWARE_VMX) } 6 | 7 | it 'has a type of VMWare VMX' do 8 | expect(builder.data['type']).to eq(Packer::Builder::VMWARE_VMX) 9 | end 10 | 11 | it 'requires source_path and communicator' do 12 | expect { builder.validate }.to raise_error(Packer::DataObject::DataValidationError) 13 | builder.source_path 'path' 14 | builder.communicator 'ssh' 15 | expect { builder.validate }.not_to raise_error 16 | end 17 | 18 | describe '#vmx_data' do 19 | it 'adds a hash of arbitrary data' do 20 | builder.vmx_data( 21 | key1: 'value1', 22 | key2: 'value2' 23 | ) 24 | expect(builder.data['vmx_data'].keys.length).to eq(2) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/packer/dataobject_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::DataObject do 5 | let(:dataobject) { Packer::DataObject.new } 6 | let(:some_string) { 'some string' } 7 | let(:keys) { %w[key1 key2] } 8 | let(:some_array_of_strings) { %w[value1 value2 value3] } 9 | let(:some_array_of_ints) { [1, 2, 3] } 10 | let(:some_hash_of_mixed) { { 'a' => 1, 'b' => 2 } } 11 | let(:some_hash_of_strings) { { 'a' => '1', 'b' => '2' } } 12 | let(:in_commands_strings) { [["command1", "1"], ["command2", "2"]] } 13 | let(:in_commands_mixed) { [["command1", 1], ["command2", 2]] } 14 | let(:out_commands_strings) { [["command1", "1"], ["command2", "2"]] } 15 | 16 | describe "#initialize" do 17 | it 'has a data hash' do 18 | expect(dataobject.data).to eq({}) 19 | end 20 | end 21 | 22 | describe '#add_required' do 23 | it 'tracks required settings' do 24 | dataobject.add_required('a', 'b') 25 | dataobject.add_required(['c', 'd']) 26 | expect(dataobject.required).to eq(['a', 'b', ['c', 'd']]) 27 | end 28 | end 29 | 30 | describe '#add_key_dependency' do 31 | it 'tracks dependencies on a key' do 32 | dataobject.add_key_dependencies({ 33 | 'key' => ['a', 'b'], 34 | 'a' => ['b', 'c'] 35 | }) 36 | 37 | expect(dataobject.key_dependencies).to eq({ 38 | 'key' => ['a', 'b'], 39 | 'a' => ['b', 'c'] 40 | }) 41 | end 42 | end 43 | 44 | describe '#validate' do 45 | describe '#validate_required' do 46 | it 'returns true when all required settings are present' do 47 | dataobject.add_required('key') 48 | dataobject.__add_string('key', 'value') 49 | expect(dataobject.validate_required).to be_truthy 50 | dataobject.data = [] 51 | dataobject.required = [] 52 | end 53 | 54 | it 'raises an error when a required setting is missing' do 55 | dataobject.add_required('key') 56 | expect { dataobject.validate_required }.to raise_error 57 | dataobject.data = [] 58 | dataobject.required = [] 59 | end 60 | 61 | it 'returns true when exactly one setting from an exclusive set is preset' do 62 | dataobject.add_required(['key1', 'key2']) 63 | dataobject.__add_string('key1', 'value') 64 | expect(dataobject.validate_required).to be_truthy 65 | dataobject.data = [] 66 | dataobject.required = [] 67 | end 68 | 69 | it 'raises an error when no settings from an exclusive set are present' do 70 | dataobject.add_required(['key1', 'key2']) 71 | expect { dataobject.validate_required }.to raise_error 72 | dataobject.data = [] 73 | dataobject.required = [] 74 | end 75 | 76 | it 'raises an error when more than one setting from an exclusive set is present' do 77 | dataobject.add_required(['key1', 'key2']) 78 | dataobject.__add_string('key1', 'value') 79 | dataobject.__add_string('key2', 'value') 80 | expect { dataobject.validate_required }.to raise_error 81 | dataobject.data = [] 82 | dataobject.required = [] 83 | end 84 | 85 | it 'returns true when there are no required settings to validate_required' do 86 | expect(dataobject.validate_required).to be_truthy 87 | end 88 | end 89 | 90 | describe '#validate_key_dependencies' do 91 | # it 'raises an error when a key is missing a dependency' do 92 | # dataobject.add_required_dependency {} 93 | # end 94 | end 95 | end 96 | 97 | describe '#deep_copy' do 98 | it 'retuns a full copy of the data structure' do 99 | dataobject.__add_string('key', 'foo') 100 | copy = dataobject.deep_copy 101 | expect(copy).to eq(dataobject.data) 102 | expect(copy).not_to be(dataobject.data) 103 | copy['key'] << 'bar' 104 | expect(copy).not_to eq(dataobject.data) 105 | dataobject.data.delete('key') 106 | end 107 | end 108 | 109 | describe "#__exclusive_key_error" do 110 | it 'returns true when the key is exclusive' do 111 | dataobject.data[keys[0]] = 'value' 112 | expect(dataobject.__exclusive_key_error(keys[0], keys[1..-1])).to be_truthy 113 | dataobject.data.delete(keys[0]) 114 | end 115 | 116 | it 'raises an error when the key is not exclusive' do 117 | dataobject.data[keys[0]] = 'value' 118 | dataobject.data[keys[1]] = 'value' 119 | expect { dataobject.__exclusive_key_error(keys[0], keys[1..-1]) }.to raise_error 120 | dataobject.data.delete(keys) 121 | end 122 | end 123 | 124 | describe '#__add_array_of_strings' do 125 | it 'assigns an array of strings to key' do 126 | dataobject.__add_array_of_strings('key', some_array_of_strings) 127 | expect(dataobject.data['key']).to eq(some_array_of_strings) 128 | dataobject.data.delete('key') 129 | end 130 | 131 | it 'converts an array of non-strings to strings and assigns them to key' do 132 | dataobject.__add_array_of_strings('key', some_array_of_ints) 133 | expect(dataobject.data['key']).to eq(some_array_of_ints.map(&:to_s)) 134 | dataobject.data.delete('key') 135 | end 136 | 137 | it 'raises an error if the values cannot be turned in to an Array' do 138 | expect { dataobject.__add_array_of_strings('key', 'some string') }.to raise_error 139 | end 140 | end 141 | 142 | describe "#__add_array_of_array_of_strings" do 143 | it 'assigns an array of array of strings to key' do 144 | dataobject.__add_array_of_array_of_strings('key', in_commands_strings) 145 | expect(dataobject.data['key']).to eq(out_commands_strings) 146 | dataobject.data.delete('key') 147 | end 148 | 149 | it 'converts non-strings to strings in the sub-arrays during assignment to key' do 150 | dataobject.__add_array_of_array_of_strings('key', in_commands_mixed) 151 | expect(dataobject.data['key']).to eq(out_commands_strings) 152 | dataobject.data.delete('key') 153 | end 154 | 155 | it 'raises an error if the values argument is not an array' do 156 | expect { dataobject.__add_array_of_array_of_strings('key', 'some string') }.to raise_error 157 | end 158 | 159 | it 'raises an error if any element in the values argument is not an array' do 160 | expect { dataobject.__add_array_of_array_of_strings('key', [['legal'], 'illegal']) }.to raise_error 161 | end 162 | end 163 | 164 | describe '#__add_string' do 165 | it 'accepts a string' do 166 | dataobject.__add_string('key', some_string) 167 | expect(dataobject.data['key']).to eq(some_string) 168 | dataobject.data.delete('key') 169 | end 170 | 171 | it 'converts any argument passed to a string' do 172 | dataobject.__add_string('key', some_array_of_ints) 173 | expect(dataobject.data['key']).to eq(some_array_of_ints.to_s) 174 | dataobject.data.delete('key') 175 | end 176 | end 177 | 178 | describe '#__add_integer' do 179 | it 'accepts anything that can be converted to an integer with #to_i' do 180 | dataobject.__add_integer(keys[0], 1) 181 | dataobject.__add_integer(keys[1], "2") 182 | expect(dataobject.data[keys[0]]).to eq(1) 183 | expect(dataobject.data[keys[1]]).to eq(2) 184 | dataobject.data.delete(keys) 185 | end 186 | 187 | it 'raises an error if the value cannot be converted to an integer with #to_i' do 188 | expect { dataobject.__add_integer('key', StandardError.new("not convertable")) }.to raise_error 189 | end 190 | end 191 | 192 | describe '#__add_boolean' do 193 | it 'accepts any truthy value and converts it to true' do 194 | dataobject.__add_boolean('key', some_string) 195 | expect(dataobject.data['key']).to be_truthy 196 | dataobject.data.delete('key') 197 | end 198 | 199 | it 'accepts any non-truthy value and converts it to false' do 200 | dataobject.__add_boolean('key', false) 201 | expect(dataobject.data['key']).to be_falsey 202 | dataobject.data.delete('key') 203 | end 204 | end 205 | 206 | describe '#__add_json' do 207 | it 'assigns a hash to a key' do 208 | dataobject.__add_json('key', some_hash_of_mixed) 209 | expect(dataobject.data['key']).to eq(some_hash_of_mixed) 210 | dataobject.data.delete('key') 211 | end 212 | 213 | it 'import the hash as it is and assigns them to key' do 214 | dataobject.__add_json('key', some_hash_of_mixed) 215 | expect(dataobject.data['key']).to eq(some_hash_of_mixed) 216 | dataobject.data.delete('key') 217 | end 218 | 219 | it 'raises an error if the values argument is not a Hash' do 220 | expect { dataobject.__add_json('key', 'some string') }.to raise_error 221 | end 222 | end 223 | 224 | describe '#__add_hash' do 225 | it 'assigns a hash to a key' do 226 | dataobject.__add_hash('key', some_hash_of_strings) 227 | expect(dataobject.data['key']).to eq(some_hash_of_strings) 228 | dataobject.data.delete('key') 229 | end 230 | 231 | it 'converts a hash of non-strings to strings and assigns them to key' do 232 | dataobject.__add_hash('key', some_hash_of_mixed) 233 | expect(dataobject.data['key']).to eq(some_hash_of_strings) 234 | dataobject.data.delete('key') 235 | end 236 | 237 | it 'raises an error if the values argument is not a Hash' do 238 | expect { dataobject.__add_hash('key', 'some string') }.to raise_error 239 | end 240 | end 241 | 242 | describe '#__add_array_of_hashes' do 243 | it 'assigns an array of hashes to a key' do 244 | array = [some_hash_of_strings, some_hash_of_strings] 245 | dataobject.__add_array_of_hashes('key', array) 246 | expect(dataobject.data['key']).to eq(array) 247 | dataobject.data.delete('key') 248 | end 249 | 250 | it 'converts non-strings in the hashes to strings during assignment to key' do 251 | array = [some_hash_of_mixed, some_hash_of_mixed] 252 | dataobject.__add_array_of_hashes('key', array) 253 | expect(dataobject.data['key']).to eq([some_hash_of_strings, some_hash_of_strings]) 254 | dataobject.data.delete('key') 255 | end 256 | 257 | it 'raises an error if the values argument is not an array' do 258 | expect { dataobject.__add_array_of_hashes('key', 'some string') }.to raise_error 259 | end 260 | 261 | it 'raises an error if any element in the values argument is not a Hash' do 262 | expect { dataobject.__add_array_of_hashes('key', [some_hash_of_strings, 'illegal']) }.to raise_error 263 | end 264 | end 265 | 266 | describe '#__add_array_of_ints' do 267 | it 'assigns an array of ints to a key' do 268 | array = some_array_of_ints 269 | dataobject.__add_array_of_ints('key', array) 270 | expect(dataobject.data['key']).to eq(array) 271 | dataobject.data.delete('key') 272 | end 273 | 274 | it 'raises error if the values argument is not an array' do 275 | expect { dataobject.__add_array_of_ints('key', some_hash_of_strings) }.to raise_error 276 | end 277 | 278 | it 'raises error if the array contains non-integer strings' do 279 | expect { dataobject._add_array_of_ints('key', some_array_of_strings) }.to raise_error 280 | end 281 | end 282 | end 283 | -------------------------------------------------------------------------------- /spec/packer/envvar_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::EnvVar do 5 | let(:envvar) do 6 | Packer::EnvVar.new 7 | end 8 | 9 | it 'returns a packer.io envvar string for any method' do 10 | expect(envvar.FOO).to eq("{{env `FOO`}}") 11 | expect(envvar.BAR).to eq("{{env `BAR`}}") 12 | expect(envvar.MOO).to eq("{{env `MOO`}}") 13 | end 14 | 15 | it 'never changes the capitalization of the env var' do 16 | expect(envvar.foo).to eq("{{env `foo`}}") 17 | expect(envvar.Foo).to eq("{{env `Foo`}}") 18 | expect(envvar.fOo).to eq("{{env `fOo`}}") 19 | end 20 | 21 | it 'responds to anything' do 22 | expect(envvar.respond_to?('anything')).to be_truthy 23 | expect(envvar.respond_to?('anything_else')).to be_truthy 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/packer/macro_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Macro do 5 | let(:macro) do 6 | Packer::Macro.new 7 | end 8 | 9 | it 'returns a packer.io macro string for any method' do 10 | expect(macro.Foo).to eq("{{ .Foo }}") 11 | expect(macro.Bar).to eq("{{ .Bar }}") 12 | expect(macro.Moo).to eq("{{ .Moo }}") 13 | end 14 | 15 | it 'always capitalizes the first letter in the macro' do 16 | expect(macro.foo).to eq("{{ .Foo }}") 17 | expect(macro.Foo).to eq("{{ .Foo }}") 18 | expect(macro.fOo).to eq("{{ .FOo }}") 19 | end 20 | 21 | it 'responds to anything' do 22 | expect(macro.respond_to?('anything')).to be_truthy 23 | expect(macro.respond_to?('anything_else')).to be_truthy 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/packer/postprocessor_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | POSTPROCESSOR_TYPE = 'vagrant' 5 | 6 | RSpec.describe Packer::PostProcessor do 7 | let(:postprocessor) do 8 | Packer::PostProcessor.new 9 | end 10 | 11 | let(:overrides) do 12 | { 13 | "key1" => "value1", 14 | "key2" => "value2" 15 | } 16 | end 17 | 18 | describe '.get_postprocessor' do 19 | it 'returns a post-processor' do 20 | expect(Packer::PostProcessor.get_postprocessor(POSTPROCESSOR_TYPE)).to be_a_kind_of(Packer::PostProcessor) 21 | end 22 | 23 | it 'raises an error when the post-processor type is not recognized' do 24 | expect { Packer::PostProcessor.get_postprocessor('unknown-type') }.to raise_error 25 | end 26 | end 27 | 28 | describe '#only' do 29 | it 'adds an only exception' do 30 | postprocessor.only('thing1') 31 | expect(postprocessor.data['only']).to eq(%w[thing1]) 32 | postprocessor.only('thing2') 33 | expect(postprocessor.data['only']).to eq(%w[thing1 thing2]) 34 | end 35 | end 36 | 37 | describe '#except' do 38 | it 'adds an execpt exception' do 39 | postprocessor.except('thing3') 40 | expect(postprocessor.data['except']).to eq(%w[thing3]) 41 | postprocessor.except('thing4') 42 | expect(postprocessor.data['except']).to eq(%w[thing3 thing4]) 43 | end 44 | end 45 | 46 | describe '#keep_input_artifact' do 47 | it 'accepts any truthy value and converts it to true' do 48 | postprocessor.keep_input_artifact('this is true') 49 | expect(postprocessor.data['keep_input_artifact']).to be_truthy 50 | postprocessor.data.delete('keep_input_artifact') 51 | end 52 | 53 | it 'accepts any non-truthy value and converts it to false' do 54 | postprocessor.keep_input_artifact(false) 55 | expect(postprocessor.data['keep_input_artifact']).to be_falsey 56 | postprocessor.data.delete('keep_input_artifact') 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/packer/postprocessors/docker_import_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::PostProcessor::DockerImport do 5 | let(:postprocessor) do 6 | Packer::PostProcessor.get_postprocessor(Packer::PostProcessor::DOCKER_IMPORT) 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_ints) do 14 | [1, 2, 3] 15 | end 16 | 17 | describe '#initialize' do 18 | it 'has a type of shell' do 19 | expect(postprocessor.data['type']).to eq(Packer::PostProcessor::DOCKER_IMPORT) 20 | end 21 | end 22 | 23 | describe '#repository' do 24 | it 'accepts a string' do 25 | postprocessor.repository(some_string) 26 | expect(postprocessor.data['repository']).to eq(some_string) 27 | postprocessor.data.delete('repository') 28 | end 29 | 30 | it 'converts any argument passed to a string' do 31 | postprocessor.repository(some_array_of_ints) 32 | expect(postprocessor.data['repository']).to eq(some_array_of_ints.to_s) 33 | postprocessor.data.delete('repository') 34 | end 35 | end 36 | 37 | describe 'tag' do 38 | it 'accepts a string' do 39 | postprocessor.tag(some_string) 40 | expect(postprocessor.data['tag']).to eq(some_string) 41 | postprocessor.data.delete('tag') 42 | end 43 | 44 | it 'converts any argument passed to a string' do 45 | postprocessor.tag(some_array_of_ints) 46 | expect(postprocessor.data['tag']).to eq(some_array_of_ints.to_s) 47 | postprocessor.data.delete('tag') 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/packer/postprocessors/docker_push_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::PostProcessor::DockerPush do 5 | let(:postprocessor) do 6 | Packer::PostProcessor.get_postprocessor(Packer::PostProcessor::DOCKER_PUSH) 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_boolean) do 14 | true 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | describe '#initialize' do 22 | it 'has a type of shell' do 23 | expect(postprocessor.data['type']).to eq(Packer::PostProcessor::DOCKER_PUSH) 24 | end 25 | end 26 | 27 | describe '#login' do 28 | it 'accepts a boolean' do 29 | postprocessor.login(some_boolean) 30 | expect(postprocessor.data['login']).to be_truthy 31 | postprocessor.data.delete('login') 32 | end 33 | end 34 | 35 | describe '#login_email' do 36 | it 'accepts a string' do 37 | postprocessor.login_email(some_string) 38 | expect(postprocessor.data['login_email']).to eq(some_string) 39 | postprocessor.data.delete('login_email') 40 | end 41 | 42 | it 'converts any argument passed to a string' do 43 | postprocessor.login_email(some_array_of_ints) 44 | expect(postprocessor.data['login_email']).to eq(some_array_of_ints.to_s) 45 | postprocessor.data.delete('login_email') 46 | end 47 | end 48 | 49 | describe '#login_username' do 50 | it 'accepts a string' do 51 | postprocessor.login_username(some_string) 52 | expect(postprocessor.data['login_username']).to eq(some_string) 53 | postprocessor.data.delete('login_username') 54 | end 55 | 56 | it 'converts any argument passed to a string' do 57 | postprocessor.login_username(some_array_of_ints) 58 | expect(postprocessor.data['login_username']).to eq(some_array_of_ints.to_s) 59 | postprocessor.data.delete('login_username') 60 | end 61 | end 62 | 63 | describe '#login_password' do 64 | it 'accepts a string' do 65 | postprocessor.login_password(some_string) 66 | expect(postprocessor.data['login_password']).to eq(some_string) 67 | postprocessor.data.delete('login_password') 68 | end 69 | 70 | it 'converts any argument passed to a string' do 71 | postprocessor.login_password(some_array_of_ints) 72 | expect(postprocessor.data['login_password']).to eq(some_array_of_ints.to_s) 73 | postprocessor.data.delete('login_password') 74 | end 75 | end 76 | 77 | describe '#login_server' do 78 | it 'accepts a string' do 79 | postprocessor.login_server(some_string) 80 | expect(postprocessor.data['login_server']).to eq(some_string) 81 | postprocessor.data.delete('login_server') 82 | end 83 | 84 | it 'converts any argument passed to a string' do 85 | postprocessor.login_server(some_array_of_ints) 86 | expect(postprocessor.data['login_server']).to eq(some_array_of_ints.to_s) 87 | postprocessor.data.delete('login_server') 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /spec/packer/postprocessors/docker_save_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::PostProcessor::DockerSave do 5 | let(:postprocessor) do 6 | Packer::PostProcessor.get_postprocessor(Packer::PostProcessor::DOCKER_SAVE) 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_ints) do 14 | [1, 2, 3] 15 | end 16 | 17 | describe '#initialize' do 18 | it 'has a type of shell' do 19 | expect(postprocessor.data['type']).to eq(Packer::PostProcessor::DOCKER_SAVE) 20 | end 21 | end 22 | 23 | describe '#path' do 24 | it 'accepts a string' do 25 | postprocessor.path(some_string) 26 | expect(postprocessor.data['path']).to eq(some_string) 27 | postprocessor.data.delete('path') 28 | end 29 | 30 | it 'converts any argument passed to a string' do 31 | postprocessor.path(some_array_of_ints) 32 | expect(postprocessor.data['path']).to eq(some_array_of_ints.to_s) 33 | postprocessor.data.delete('path') 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/packer/postprocessors/docker_tag_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::PostProcessor::DockerTag do 5 | let(:postprocessor) do 6 | Packer::PostProcessor.get_postprocessor(Packer::PostProcessor::DOCKER_TAG) 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_ints) do 14 | [1, 2, 3] 15 | end 16 | 17 | describe '#initialize' do 18 | it 'has a type of shell' do 19 | expect(postprocessor.data['type']).to eq(Packer::PostProcessor::DOCKER_TAG) 20 | end 21 | end 22 | 23 | describe '#repository' do 24 | it 'accepts a string' do 25 | postprocessor.repository(some_string) 26 | expect(postprocessor.data['repository']).to eq(some_string) 27 | postprocessor.data.delete('repository') 28 | end 29 | 30 | it 'converts any argument passed to a string' do 31 | postprocessor.repository(some_array_of_ints) 32 | expect(postprocessor.data['repository']).to eq(some_array_of_ints.to_s) 33 | postprocessor.data.delete('repository') 34 | end 35 | end 36 | 37 | describe '#tag' do 38 | it 'accepts a string' do 39 | postprocessor.tag(some_string) 40 | expect(postprocessor.data['tag']).to eq(some_string) 41 | postprocessor.data.delete('tag') 42 | end 43 | 44 | it 'converts any argument passed to a string' do 45 | postprocessor.tag(some_array_of_ints) 46 | expect(postprocessor.data['tag']).to eq(some_array_of_ints.to_s) 47 | postprocessor.data.delete('tag') 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/packer/postprocessors/manifest_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::PostProcessor::Manifest do 5 | let(:postprocessor) do 6 | Packer::PostProcessor.get_postprocessor(Packer::PostProcessor::MANIFEST) 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_ints) do 14 | [1, 2, 3] 15 | end 16 | 17 | let(:hash_of_strings) do 18 | { 19 | my_custom_data: 'example', 20 | my_other_data: 'hidden' 21 | } 22 | end 23 | 24 | describe '#initialize' do 25 | it 'has a type of manifest' do 26 | expect(postprocessor.data['type']).to eq(Packer::PostProcessor::MANIFEST) 27 | end 28 | end 29 | 30 | describe '#output' do 31 | it 'accepts a string' do 32 | postprocessor.output(some_string) 33 | expect(postprocessor.data['output']).to eq(some_string) 34 | postprocessor.data.delete('output') 35 | end 36 | 37 | it 'converts any argument passed to a string' do 38 | postprocessor.output(some_array_of_ints) 39 | expect(postprocessor.data['output']).to eq(some_array_of_ints.to_s) 40 | postprocessor.data.delete('output') 41 | end 42 | end 43 | 44 | describe '#strip_path' do 45 | it 'accepts any truthy value and converts it to true' do 46 | postprocessor.strip_path('this is true') 47 | expect(postprocessor.data['strip_path']).to be_truthy 48 | postprocessor.data.delete('strip_path') 49 | end 50 | 51 | it 'accepts any non-truthy value and converts it to false' do 52 | postprocessor.strip_path(false) 53 | expect(postprocessor.data['strip_path']).to be_falsey 54 | postprocessor.data.delete('strip_path') 55 | end 56 | end 57 | 58 | describe '#custom_data' do 59 | it 'adds a hash of strings to the key' do 60 | postprocessor.custom_data(hash_of_strings) 61 | expect(postprocessor.data['custom_data']).to eq(hash_of_strings) 62 | postprocessor.data.delete('custom_data') 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/packer/postprocessors/shell_local_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::PostProcessor::ShellLocal do 5 | let(:postprocessor) do 6 | Packer::PostProcessor.get_postprocessor(Packer::PostProcessor::SHELL_LOCAL) 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[command1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | describe '#initialize' do 22 | it 'has a type of shell' do 23 | expect(postprocessor.data['type']).to eq(Packer::PostProcessor::SHELL_LOCAL) 24 | end 25 | end 26 | 27 | describe '#inline' do 28 | it 'accepts an array of commands' do 29 | postprocessor.inline(some_array_of_strings) 30 | expect(postprocessor.data['inline']).to eq(some_array_of_strings) 31 | postprocessor.data.delete('inline') 32 | end 33 | 34 | it 'raise an error if it is empty' do 35 | expect { postprocessor.inline('') }.to raise_error 36 | postprocessor.data.delete('inline') 37 | end 38 | 39 | it 'converts all commands to strings' do 40 | postprocessor.inline(some_array_of_ints) 41 | expect(postprocessor.data['inline']).to eq(some_array_of_ints.map(&:to_s)) 42 | postprocessor.data.delete('inline') 43 | end 44 | 45 | it 'raises an error if the commands argument cannot be made an Array' do 46 | expect { postprocessor.inline(some_string) }.to raise_error 47 | end 48 | 49 | it 'raises an error if #script or #scripts method was already called' do 50 | postprocessor.data['script'] = 1 51 | expect { postprocessor.inline(some_array_of_strings) }.to raise_error 52 | postprocessor.data.delete('script') 53 | postprocessor.data['scripts'] = 1 54 | expect { postprocessor.inline(some_array_of_strings) }.to raise_error 55 | postprocessor.data.delete('scripts') 56 | end 57 | end 58 | 59 | describe '#script' do 60 | it 'accepts a string' do 61 | postprocessor.script(some_string) 62 | expect(postprocessor.data['script']).to eq(some_string) 63 | postprocessor.data.delete('script') 64 | end 65 | 66 | it 'converts any argument passed to a string' do 67 | postprocessor.script(some_array_of_ints) 68 | expect(postprocessor.data['script']).to eq(some_array_of_ints.to_s) 69 | postprocessor.data.delete('script') 70 | end 71 | 72 | it 'raises an error if #inline or #scripts method was already called' do 73 | postprocessor.data['inline'] = 1 74 | expect { postprocessor.script(some_string) }.to raise_error 75 | postprocessor.data.delete('inline') 76 | postprocessor.data['scripts'] = 1 77 | expect { postprocessor.script(some_string) }.to raise_error 78 | postprocessor.data.delete('scripts') 79 | end 80 | end 81 | 82 | describe '#scripts' do 83 | it 'accepts an array of commands' do 84 | postprocessor.scripts(some_array_of_strings) 85 | expect(postprocessor.data['scripts']).to eq(some_array_of_strings) 86 | postprocessor.data.delete('scripts') 87 | end 88 | 89 | it 'converts all commands to strings' do 90 | postprocessor.scripts(some_array_of_ints) 91 | expect(postprocessor.data['scripts']).to eq(some_array_of_ints.map(&:to_s)) 92 | postprocessor.data.delete('scripts') 93 | end 94 | 95 | it 'raises an error if the commands argument cannot be made an Array' do 96 | expect { postprocessor.scripts(some_string) }.to raise_error 97 | end 98 | 99 | it 'raises an error if #inline or #script method was already called' do 100 | postprocessor.data['script'] = 1 101 | expect { postprocessor.scripts(some_array_of_strings) }.to raise_error 102 | postprocessor.data.delete('scripts') 103 | postprocessor.data['inline'] = 1 104 | expect { postprocessor.scripts(some_array_of_strings) }.to raise_error 105 | postprocessor.data.delete('scripts') 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /spec/packer/postprocessors/vagrant_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::PostProcessor::Vagrant do 5 | let(:postprocessor) do 6 | Packer::PostProcessor.get_postprocessor(Packer::PostProcessor::VAGRANT) 7 | end 8 | 9 | describe '#initialize' do 10 | it 'has a type of shell' do 11 | expect(postprocessor.data['type']).to eq(Packer::PostProcessor::VAGRANT) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/packer/provisioner_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | PROVISIONER_TYPE = 'shell' 5 | 6 | RSpec.describe Packer::Provisioner do 7 | let(:provisioner) do 8 | Packer::Provisioner.new 9 | end 10 | 11 | let(:overrides) do 12 | { 13 | "key1" => "value1", 14 | "key2" => "value2" 15 | } 16 | end 17 | 18 | describe '.get_provisioner' do 19 | it 'returns a provisioner' do 20 | expect(Packer::Provisioner.get_provisioner(PROVISIONER_TYPE)).to be_a_kind_of(Packer::Provisioner) 21 | end 22 | 23 | it 'raises an error when the provisioner type is not recognized' do 24 | expect { Packer::Provisioner.get_provisioner('unknown-type') }.to raise_error 25 | end 26 | end 27 | 28 | describe '#only' do 29 | it 'adds an only exception' do 30 | provisioner.only('thing1') 31 | expect(provisioner.data['only']).to eq(%w[thing1]) 32 | provisioner.only('thing2') 33 | expect(provisioner.data['only']).to eq(%w[thing1 thing2]) 34 | end 35 | end 36 | 37 | describe '#except' do 38 | it 'adds an except exception' do 39 | provisioner.except('thing3') 40 | expect(provisioner.data['except']).to eq(%w[thing3]) 41 | provisioner.except('thing4') 42 | expect(provisioner.data['except']).to eq(%w[thing3 thing4]) 43 | end 44 | end 45 | 46 | describe '#pause_before' do 47 | it 'adds a pause time' do 48 | provisioner.pause_before(10) 49 | expect(provisioner.data['pause_before']).to eq("10") 50 | provisioner.pause_before("10s") 51 | expect(provisioner.data['pause_before']).to eq("10s") 52 | end 53 | end 54 | 55 | describe '#override' do 56 | it 'adds a hash overridef override values to a list of overrides' do 57 | provisioner.override('build', overrides) 58 | expect(provisioner.data['override']['build']).to eq(overrides) 59 | end 60 | 61 | it 'raises a TypeError when the overrides are not a hash' do 62 | expect { provisioner.override('build', 10) }.to raise_error 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/packer/provisioners/ansible_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::Ansible do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('ansible-local') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[commmand1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | let(:some_hash_of_strings) do 22 | {a: 'foo', b: 'bar'} 23 | end 24 | 25 | it 'requires a playbook_file setting' do 26 | expect { provisioner.validate }.to raise_error 27 | end 28 | 29 | describe '#initialize' do 30 | it 'has a type of ansible-local' do 31 | expect(provisioner.data['type']).to eq('ansible-local') 32 | end 33 | end 34 | 35 | describe '#playbook_file' do 36 | it 'accepts a string' do 37 | provisioner.playbook_file some_string 38 | expect(provisioner.data['playbook_file']).to eq(some_string) 39 | provisioner.data.delete('playbook_file') 40 | end 41 | 42 | it 'converts any argument passed to a string' do 43 | provisioner.playbook_file some_array_of_ints 44 | expect(provisioner.data['playbook_file']).to eq(some_array_of_ints.to_s) 45 | provisioner.data.delete('playbook_file') 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/packer/provisioners/chef/client_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::Chef::Client do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('chef-client') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[commmand1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | let(:some_hash_of_strings) do 22 | {a: 'foo', b: 'bar'} 23 | end 24 | 25 | it 'requires a server_url setting' do 26 | expect { provisioner.validate }.to raise_error 27 | end 28 | 29 | describe '#initialize' do 30 | it 'has a type of chef-client' do 31 | expect(provisioner.data['type']).to eq('chef-client') 32 | end 33 | end 34 | 35 | describe '#server_url' do 36 | it 'accepts a string' do 37 | provisioner.server_url some_string 38 | expect(provisioner.data['server_url']).to eq(some_string) 39 | provisioner.data.delete('server_url') 40 | end 41 | 42 | it 'converts any argument passed to a string' do 43 | provisioner.server_url some_array_of_ints 44 | expect(provisioner.data['server_url']).to eq(some_array_of_ints.to_s) 45 | provisioner.data.delete('server_url') 46 | end 47 | end 48 | 49 | describe '#run_list' do 50 | it 'accepts an array of strings' do 51 | provisioner.run_list(some_array_of_strings) 52 | expect(provisioner.data['run_list']).to eq(some_array_of_strings) 53 | provisioner.data.delete('run_list') 54 | end 55 | 56 | it 'converts all entities to strings' do 57 | provisioner.run_list(some_array_of_ints) 58 | expect(provisioner.data['run_list']).to eq(some_array_of_ints.map(&:to_s)) 59 | provisioner.data.delete('run_list') 60 | end 61 | 62 | it 'raises an error if the argument cannot be made an Array' do 63 | expect { provisioner.run_list(some_string) }.to raise_error 64 | end 65 | end 66 | 67 | describe '#client_key' do 68 | it 'accepts a string' do 69 | provisioner.client_key some_string 70 | expect(provisioner.data['client_key']).to eq(some_string) 71 | provisioner.data.delete('client_key') 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /spec/packer/provisioners/chef/solo_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::Chef::Solo do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('chef-solo') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[commmand1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | let(:some_hash_of_strings) do 22 | {a: 'foo', b: 'bar'} 23 | end 24 | 25 | describe '#initialize' do 26 | it 'has a type of chef-solo' do 27 | expect(provisioner.data['type']).to eq('chef-solo') 28 | end 29 | end 30 | 31 | describe '#config_template`' do 32 | it 'accepts a string' do 33 | provisioner.config_template(some_string) 34 | expect(provisioner.data['config_template']).to eq(some_string) 35 | provisioner.data.delete('config_template') 36 | end 37 | 38 | it 'converts any argument passed to a string' do 39 | provisioner.config_template(some_array_of_ints) 40 | expect(provisioner.data['config_template']).to eq(some_array_of_ints.to_s) 41 | provisioner.data.delete('config_template') 42 | end 43 | end 44 | 45 | describe '#cookbook_paths' do 46 | it 'accepts an array of strings' do 47 | provisioner.cookbook_paths(some_array_of_strings) 48 | expect(provisioner.data['cookbook_paths']).to eq(some_array_of_strings) 49 | provisioner.data.delete('cookbook_paths') 50 | end 51 | 52 | it 'converts all entities to strings' do 53 | provisioner.cookbook_paths(some_array_of_ints) 54 | expect(provisioner.data['cookbook_paths']).to eq(some_array_of_ints.map(&:to_s)) 55 | provisioner.data.delete('cookbook_paths') 56 | end 57 | 58 | it 'raises an error if the argument cannot be made an Array' do 59 | expect { provisioner.cookbook_paths(some_string) }.to raise_error 60 | end 61 | end 62 | 63 | describe '#remote_cookbook_paths' do 64 | it 'accepts an array of strings' do 65 | provisioner.remote_cookbook_paths(some_array_of_strings) 66 | expect(provisioner.data['remote_cookbook_paths']).to eq(some_array_of_strings) 67 | provisioner.data.delete('remote_cookbook_paths') 68 | end 69 | 70 | it 'converts all entities to strings' do 71 | provisioner.remote_cookbook_paths(some_array_of_ints) 72 | expect(provisioner.data['remote_cookbook_paths']).to eq(some_array_of_ints.map(&:to_s)) 73 | provisioner.data.delete('remote_cookbook_paths') 74 | end 75 | 76 | it 'raises an error if the argument cannot be made an Array' do 77 | expect { provisioner.remote_cookbook_paths(some_string) }.to raise_error 78 | end 79 | end 80 | 81 | describe '#run_list' do 82 | it 'accepts an array of strings' do 83 | provisioner.run_list(some_array_of_strings) 84 | expect(provisioner.data['run_list']).to eq(some_array_of_strings) 85 | provisioner.data.delete('run_list') 86 | end 87 | 88 | it 'converts all entities to strings' do 89 | provisioner.run_list(some_array_of_ints) 90 | expect(provisioner.data['run_list']).to eq(some_array_of_ints.map(&:to_s)) 91 | provisioner.data.delete('run_list') 92 | end 93 | 94 | it 'raises an error if the argument cannot be made an Array' do 95 | expect { provisioner.run_list(some_string) }.to raise_error 96 | end 97 | end 98 | 99 | describe '#validate' do 100 | it 'works with no data' do 101 | expect { provisioner.validate }.to_not raise_error 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /spec/packer/provisioners/file_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::File do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('file') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_ints) do 14 | [1, 2, 3] 15 | end 16 | 17 | describe '#initialize' do 18 | it 'has a type of file' do 19 | expect(provisioner.data['type']).to eq('file') 20 | end 21 | end 22 | 23 | describe '#source' do 24 | it 'accepts a string' do 25 | provisioner.source(some_string) 26 | expect(provisioner.data['source']).to eq(some_string) 27 | provisioner.data.delete('source') 28 | end 29 | 30 | it 'converts any argument passed to a string' do 31 | provisioner.source(some_array_of_ints) 32 | expect(provisioner.data['source']).to eq(some_array_of_ints.to_s) 33 | provisioner.data.delete('source') 34 | end 35 | end 36 | 37 | describe '#destination' do 38 | it 'accepts a string' do 39 | provisioner.destination(some_string) 40 | expect(provisioner.data['destination']).to eq(some_string) 41 | provisioner.data.delete('destination') 42 | end 43 | 44 | it 'converts any argument passed to a string' do 45 | provisioner.destination(some_array_of_ints) 46 | expect(provisioner.data['destination']).to eq(some_array_of_ints.to_s) 47 | provisioner.data.delete('destination') 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/packer/provisioners/powershell_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::Powershell do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('powershell') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[command1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | describe '#initialize' do 22 | it 'has a type of powershell' do 23 | expect(provisioner.data['type']).to eq('powershell') 24 | end 25 | end 26 | 27 | describe '#inline' do 28 | it 'accepts an array of commands' do 29 | provisioner.inline(some_array_of_strings) 30 | expect(provisioner.data['inline']).to eq(some_array_of_strings) 31 | provisioner.data.delete('inline') 32 | end 33 | 34 | it 'converts all commands to strings' do 35 | provisioner.inline(some_array_of_ints) 36 | expect(provisioner.data['inline']).to eq(some_array_of_ints.map(&:to_s)) 37 | provisioner.data.delete('inline') 38 | end 39 | 40 | it 'raises an error if the commands argument cannot be made an Array' do 41 | expect { provisioner.inline(some_string) }.to raise_error 42 | end 43 | 44 | it 'raises an error if #script or #scripts method was already called' do 45 | provisioner.data['script'] = 1 46 | expect { provisioner.inline(some_array_of_strings) }.to raise_error 47 | provisioner.data.delete('script') 48 | provisioner.data['scripts'] = 1 49 | expect { provisioner.inline(some_array_of_strings) }.to raise_error 50 | provisioner.data.delete('scripts') 51 | end 52 | end 53 | 54 | describe '#script' do 55 | it 'accepts a string' do 56 | provisioner.script(some_string) 57 | expect(provisioner.data['script']).to eq(some_string) 58 | provisioner.data.delete('script') 59 | end 60 | 61 | it 'converts any argument passed to a string' do 62 | provisioner.script(some_array_of_ints) 63 | expect(provisioner.data['script']).to eq(some_array_of_ints.to_s) 64 | provisioner.data.delete('script') 65 | end 66 | 67 | it 'raises an error if #inline or #scripts method was already called' do 68 | provisioner.data['inline'] = 1 69 | expect { provisioner.script(some_string) }.to raise_error 70 | provisioner.data.delete('inline') 71 | provisioner.data['scripts'] = 1 72 | expect { provisioner.script(some_string) }.to raise_error 73 | provisioner.data.delete('scripts') 74 | end 75 | end 76 | 77 | describe '#scripts' do 78 | it 'accepts an array of commands' do 79 | provisioner.scripts(some_array_of_strings) 80 | expect(provisioner.data['scripts']).to eq(some_array_of_strings) 81 | provisioner.data.delete('scripts') 82 | end 83 | 84 | it 'converts all commands to strings' do 85 | provisioner.scripts(some_array_of_ints) 86 | expect(provisioner.data['scripts']).to eq(some_array_of_ints.map(&:to_s)) 87 | provisioner.data.delete('scripts') 88 | end 89 | 90 | it 'raises an error if the commands argument cannot be made an Array' do 91 | expect { provisioner.scripts(some_string) }.to raise_error 92 | end 93 | 94 | it 'raises an error if #inline or #script method was already called' do 95 | provisioner.data['script'] = 1 96 | expect { provisioner.scripts(some_array_of_strings) }.to raise_error 97 | provisioner.data.delete('scripts') 98 | provisioner.data['inline'] = 1 99 | expect { provisioner.scripts(some_array_of_strings) }.to raise_error 100 | provisioner.data.delete('scripts') 101 | end 102 | end 103 | 104 | describe '#valid_exit_codes' do 105 | it 'accepts an array of exit codes' do 106 | provisioner.valid_exit_codes(some_array_of_ints) 107 | expect(provisioner.data['valid_exit_codes']).to eq(some_array_of_ints) 108 | provisioner.data.delete('valid_exit_codes') 109 | end 110 | end 111 | 112 | describe '#validate' do 113 | it 'raises an error if elevated_user is defined and elevated_password is not' do 114 | provisioner.inline [some_string] 115 | provisioner.elevated_user some_string 116 | expect { provisioner.validate }.to raise_error Packer::DataObject::DataValidationError 117 | end 118 | 119 | it 'completes with no elevated_user or elevated_password' do 120 | provisioner.inline [some_string] 121 | expect(provisioner.validate).to be true 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /spec/packer/provisioners/puppet/masterless_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Packer::Provisioner do 4 | let(:provisioner) do 5 | Packer::Provisioner.get_provisioner('puppet-masterless') 6 | end 7 | 8 | let(:some_string) do 9 | 'some string' 10 | end 11 | 12 | let(:some_hash_of_strings) do 13 | { :foo => 'bar', :bar => 'foo' } 14 | end 15 | 16 | let(:some_array_of_strings) do 17 | %w[command1 command2] 18 | end 19 | 20 | let(:some_boolean) do 21 | true 22 | end 23 | 24 | let(:some_array_of_ints) do 25 | [1, 2, 3] 26 | end 27 | 28 | describe '#initialize' do 29 | it 'returns an instance' do 30 | expect(provisioner).to be_a_kind_of(Packer::Provisioner::Puppet::Masterless) 31 | end 32 | 33 | it 'has a type of masterless' do 34 | expect(provisioner.data['type']).to eq('puppet-masterless') 35 | end 36 | end 37 | 38 | describe '#manifest_file' do 39 | it 'accepts a string' do 40 | provisioner.manifest_file(some_string) 41 | expect(provisioner.data['manifest_file']).to eq(some_string) 42 | provisioner.data.delete('manifest_file') 43 | end 44 | 45 | it 'converts any argument passed to a string' do 46 | provisioner.manifest_file(some_array_of_ints) 47 | expect(provisioner.data['manifest_file']).to eq(some_array_of_ints.to_s) 48 | provisioner.data.delete('manifest_file') 49 | end 50 | end 51 | 52 | describe '#execute_command' do 53 | it 'accepts a string' do 54 | provisioner.execute_command(some_string) 55 | expect(provisioner.data['execute_command']).to eq(some_string) 56 | provisioner.data.delete('execute_command') 57 | end 58 | 59 | it 'converts any argument passed to a string' do 60 | provisioner.execute_command(some_array_of_ints) 61 | expect(provisioner.data['execute_command']).to eq(some_array_of_ints.to_s) 62 | provisioner.data.delete('execute_command') 63 | end 64 | end 65 | 66 | describe '#facter' do 67 | it 'accepts a hash' do 68 | provisioner.facter(some_hash_of_strings) 69 | expect(provisioner.data['facter']).to eq(some_hash_of_strings) 70 | provisioner.data.delete('facter') 71 | end 72 | 73 | it 'raises an error if arguments cannot be made into a hash' do 74 | expect { provisioner.facter(some_string) }.to raise_error 75 | end 76 | end 77 | 78 | describe '#hiera_config_path' do 79 | it 'accepts a string' do 80 | provisioner.hiera_config_path(some_string) 81 | expect(provisioner.data['hiera_config_path']).to eq(some_string) 82 | provisioner.data.delete('hiera_config_path') 83 | end 84 | 85 | it 'converts any argument passed to a string' do 86 | provisioner.hiera_config_path(some_array_of_ints) 87 | expect(provisioner.data['hiera_config_path']).to eq(some_array_of_ints.to_s) 88 | provisioner.data.delete('hiera_config_path') 89 | end 90 | end 91 | 92 | describe '#manifest_dir' do 93 | it 'accepts a string' do 94 | provisioner.manifest_dir(some_string) 95 | expect(provisioner.data['manifest_dir']).to eq(some_string) 96 | provisioner.data.delete('manifest_dir') 97 | end 98 | 99 | it 'converts any argument passed to a string' do 100 | provisioner.manifest_dir(some_array_of_ints) 101 | expect(provisioner.data['manifest_dir']).to eq(some_array_of_ints.to_s) 102 | provisioner.data.delete('manifest_dir') 103 | end 104 | end 105 | 106 | describe '#module_paths' do 107 | it 'accepts an array of strings' do 108 | provisioner.module_paths(some_array_of_strings) 109 | expect(provisioner.data['module_paths']).to eq(some_array_of_strings) 110 | provisioner.data.delete('module_paths') 111 | end 112 | 113 | it 'converts any argument passed to an array string' do 114 | provisioner.module_paths(some_array_of_ints) 115 | expect(provisioner.data['module_paths']).to eq(some_array_of_ints.map(&:to_s)) 116 | provisioner.data.delete('module_paths') 117 | end 118 | end 119 | 120 | describe '#prevent_sudo' do 121 | it 'accepts a boolean' do 122 | provisioner.prevent_sudo(some_boolean) 123 | expect(provisioner.data['prevent_sudo']).to be_truthy 124 | provisioner.data.delete('prevent_sudo') 125 | end 126 | end 127 | 128 | describe '#staging_directory' do 129 | it 'accepts a string' do 130 | provisioner.staging_directory(some_string) 131 | expect(provisioner.data['staging_directory']).to eq(some_string) 132 | provisioner.data.delete('staging_directory') 133 | end 134 | 135 | it 'converts any argument passed to a string' do 136 | provisioner.staging_directory(some_array_of_ints) 137 | expect(provisioner.data['staging_directory']).to eq(some_array_of_ints.to_s) 138 | provisioner.data.delete('staging_directory') 139 | end 140 | end 141 | end 142 | -------------------------------------------------------------------------------- /spec/packer/provisioners/puppet/server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Packer::Provisioner::Puppet::Server do 4 | let(:provisioner) do 5 | Packer::Provisioner.get_provisioner('puppet-server') 6 | end 7 | 8 | let(:some_string) do 9 | 'some string' 10 | end 11 | 12 | let(:some_array_of_strings) do 13 | %w[command1 command2] 14 | end 15 | 16 | let(:some_array_of_ints) do 17 | [1, 2, 3] 18 | end 19 | 20 | let(:some_hash_of_strings) do 21 | {a: 'foo', b: 'bar'} 22 | end 23 | 24 | describe '#initialize' do 25 | it 'returns an instance' do 26 | expect(provisioner).to be_a_kind_of(Packer::Provisioner::Puppet::Server) 27 | end 28 | 29 | it 'has a type of puppet-server' do 30 | expect(provisioner.data['type']).to eq('puppet-server') 31 | end 32 | end 33 | 34 | describe '#client_cert_path' do 35 | it 'accepts a string' do 36 | provisioner.client_cert_path(some_string) 37 | expect(provisioner.data['client_cert_path']).to eq(some_string) 38 | provisioner.data.delete('client_cert_path') 39 | end 40 | 41 | it 'converts any argument passed to a string' do 42 | provisioner.client_cert_path(some_array_of_ints) 43 | expect(provisioner.data['client_cert_path']).to eq(some_array_of_ints.to_s) 44 | provisioner.data.delete('client_cert_path') 45 | end 46 | end 47 | 48 | describe '#client_private_key_path' do 49 | it 'accepts a string' do 50 | provisioner.client_private_key_path(some_string) 51 | expect(provisioner.data['client_private_key_path']).to eq(some_string) 52 | provisioner.data.delete('client_private_key_path') 53 | end 54 | 55 | it 'converts any argument passed to a string' do 56 | provisioner.client_private_key_path(some_array_of_ints) 57 | expect(provisioner.data['client_private_key_path']).to eq(some_array_of_ints.to_s) 58 | provisioner.data.delete('client_private_key_path') 59 | end 60 | end 61 | 62 | describe '#facter' do 63 | it 'accepts a has' do 64 | provisioner.facter(some_hash_of_strings) 65 | expect(provisioner.data['facter']).to eq(some_hash_of_strings) 66 | provisioner.data.delete('facter') 67 | end 68 | 69 | it 'raises an error if argument passed is not a Hash' do 70 | expect { provisioner.facter(some_string) }.to raise_error 71 | end 72 | end 73 | 74 | describe '#options' do 75 | it 'accepts a string' do 76 | provisioner.options(some_string) 77 | expect(provisioner.data['options']).to eq(some_string) 78 | provisioner.data.delete('options') 79 | end 80 | 81 | it 'converts any argument passed to a string' do 82 | provisioner.options(some_array_of_ints) 83 | expect(provisioner.data['options']).to eq(some_array_of_ints.to_s) 84 | provisioner.data.delete('options') 85 | end 86 | end 87 | 88 | describe '#puppet_node' do 89 | it 'accepts a string' do 90 | provisioner.puppet_node(some_string) 91 | expect(provisioner.data['puppet_node']).to eq(some_string) 92 | provisioner.data.delete('puppet_node') 93 | end 94 | 95 | it 'converts any argument passed to a string' do 96 | provisioner.puppet_node(some_array_of_ints) 97 | expect(provisioner.data['puppet_node']).to eq(some_array_of_ints.to_s) 98 | provisioner.data.delete('puppet_node') 99 | end 100 | end 101 | 102 | describe '#puppet_server' do 103 | it 'accepts a string' do 104 | provisioner.puppet_server(some_string) 105 | expect(provisioner.data['puppet_server']).to eq(some_string) 106 | provisioner.data.delete('puppet_server') 107 | end 108 | 109 | it 'converts any argument passed to a string' do 110 | provisioner.puppet_server(some_array_of_ints) 111 | expect(provisioner.data['puppet_server']).to eq(some_array_of_ints.to_s) 112 | provisioner.data.delete('puppet_server') 113 | end 114 | end 115 | 116 | describe '#prevent_sudo' do 117 | it 'accepts a boolean' do 118 | provisioner.prevent_sudo(true) 119 | expect(provisioner.data['prevent_sudo']).to be_truthy 120 | provisioner.data.delete('prevent_sudo') 121 | end 122 | end 123 | 124 | describe '#staging_directory' do 125 | it 'accepts a string' do 126 | provisioner.staging_directory(some_string) 127 | expect(provisioner.data['staging_directory']).to eq(some_string) 128 | provisioner.data.delete('staging_directory') 129 | end 130 | 131 | it 'converts any argument passed to a string' do 132 | provisioner.staging_directory(some_array_of_ints) 133 | expect(provisioner.data['staging_directory']).to eq(some_array_of_ints.to_s) 134 | provisioner.data.delete('staging_directory') 135 | end 136 | end 137 | end -------------------------------------------------------------------------------- /spec/packer/provisioners/salt_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::Salt do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('salt-masterless') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[commmand1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | let(:some_hash_of_strings) do 22 | {a: 'foo', b: 'bar'} 23 | end 24 | 25 | it 'requires a local_state_tree setting' do 26 | expect { provisioner.validate }.to raise_error 27 | end 28 | 29 | describe '#initialize' do 30 | it 'has a type of salt-masterless' do 31 | expect(provisioner.data['type']).to eq('salt-masterless') 32 | end 33 | end 34 | 35 | describe '#bootstrap_args' do 36 | it 'accepts a string' do 37 | provisioner.bootstrap_args some_string 38 | expect(provisioner.data['bootstrap_args']).to eq(some_string) 39 | provisioner.data.delete('bootstrap_args') 40 | end 41 | 42 | it 'converts any argument passed to a string' do 43 | provisioner.bootstrap_args some_array_of_ints 44 | expect(provisioner.data['bootstrap_args']).to eq(some_array_of_ints.to_s) 45 | provisioner.data.delete('bootstrap_args') 46 | end 47 | end 48 | 49 | describe '#local_pillar_roots' do 50 | it 'accepts a string' do 51 | provisioner.local_pillar_roots some_string 52 | expect(provisioner.data['local_pillar_roots']).to eq(some_string) 53 | provisioner.data.delete('local_pillar_roots') 54 | end 55 | 56 | it 'converts any argument passed to a string' do 57 | provisioner.local_pillar_roots some_array_of_ints 58 | expect(provisioner.data['local_pillar_roots']).to eq(some_array_of_ints.to_s) 59 | provisioner.data.delete('local_pillar_roots') 60 | end 61 | end 62 | 63 | describe '#local_state_tree' do 64 | it 'accepts a string' do 65 | provisioner.local_state_tree some_string 66 | expect(provisioner.data['local_state_tree']).to eq(some_string) 67 | provisioner.data.delete('local_state_tree') 68 | end 69 | 70 | it 'converts any argument passed to a string' do 71 | provisioner.local_state_tree some_array_of_ints 72 | expect(provisioner.data['local_state_tree']).to eq(some_array_of_ints.to_s) 73 | provisioner.data.delete('local_state_tree') 74 | end 75 | end 76 | 77 | describe '#minion_config' do 78 | it 'accepts a string' do 79 | provisioner.minion_config some_string 80 | expect(provisioner.data['minion_config']).to eq(some_string) 81 | provisioner.data.delete('minion_config') 82 | end 83 | 84 | it 'converts any argument passed to a string' do 85 | provisioner.minion_config some_array_of_ints 86 | expect(provisioner.data['minion_config']).to eq(some_array_of_ints.to_s) 87 | provisioner.data.delete('minion_config') 88 | end 89 | end 90 | 91 | describe '#temp_config_dir' do 92 | it 'accepts a string' do 93 | provisioner.temp_config_dir some_string 94 | expect(provisioner.data['temp_config_dir']).to eq(some_string) 95 | provisioner.data.delete('temp_config_dir') 96 | end 97 | 98 | it 'converts any argument passed to a string' do 99 | provisioner.temp_config_dir some_array_of_ints 100 | expect(provisioner.data['temp_config_dir']).to eq(some_array_of_ints.to_s) 101 | provisioner.data.delete('temp_config_dir') 102 | end 103 | end 104 | 105 | describe '#skip_bootstrap' do 106 | it 'accepts a boolean' do 107 | provisioner.skip_bootstrap(true) 108 | expect(provisioner.data['skip_bootstrap']).to be_truthy 109 | provisioner.skip_bootstrap(false) 110 | expect(provisioner.data['skip_bootstrap']).to be_falsey 111 | provisioner.data.delete('skip_bootstrap') 112 | end 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /spec/packer/provisioners/shell_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::Shell do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('shell') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[command1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | describe '#initialize' do 22 | it 'has a type of shell' do 23 | expect(provisioner.data['type']).to eq('shell') 24 | end 25 | end 26 | 27 | describe '#inline' do 28 | it 'accepts an array of commands' do 29 | provisioner.inline(some_array_of_strings) 30 | expect(provisioner.data['inline']).to eq(some_array_of_strings) 31 | provisioner.data.delete('inline') 32 | end 33 | 34 | it 'converts all commands to strings' do 35 | provisioner.inline(some_array_of_ints) 36 | expect(provisioner.data['inline']).to eq(some_array_of_ints.map(&:to_s)) 37 | provisioner.data.delete('inline') 38 | end 39 | 40 | it 'raises an error if the commands argument cannot be made an Array' do 41 | expect { provisioner.inline(some_string) }.to raise_error 42 | end 43 | 44 | it 'raises an error if #script or #scripts method was already called' do 45 | provisioner.data['script'] = 1 46 | expect { provisioner.inline(some_array_of_strings) }.to raise_error 47 | provisioner.data.delete('script') 48 | provisioner.data['scripts'] = 1 49 | expect { provisioner.inline(some_array_of_strings) }.to raise_error 50 | provisioner.data.delete('scripts') 51 | end 52 | end 53 | 54 | describe '#script' do 55 | it 'accepts a string' do 56 | provisioner.script(some_string) 57 | expect(provisioner.data['script']).to eq(some_string) 58 | provisioner.data.delete('script') 59 | end 60 | 61 | it 'converts any argument passed to a string' do 62 | provisioner.script(some_array_of_ints) 63 | expect(provisioner.data['script']).to eq(some_array_of_ints.to_s) 64 | provisioner.data.delete('script') 65 | end 66 | 67 | it 'raises an error if #inline or #scripts method was already called' do 68 | provisioner.data['inline'] = 1 69 | expect { provisioner.script(some_string) }.to raise_error 70 | provisioner.data.delete('inline') 71 | provisioner.data['scripts'] = 1 72 | expect { provisioner.script(some_string) }.to raise_error 73 | provisioner.data.delete('scripts') 74 | end 75 | end 76 | 77 | describe '#scripts' do 78 | it 'accepts an array of commands' do 79 | provisioner.scripts(some_array_of_strings) 80 | expect(provisioner.data['scripts']).to eq(some_array_of_strings) 81 | provisioner.data.delete('scripts') 82 | end 83 | 84 | it 'converts all commands to strings' do 85 | provisioner.scripts(some_array_of_ints) 86 | expect(provisioner.data['scripts']).to eq(some_array_of_ints.map(&:to_s)) 87 | provisioner.data.delete('scripts') 88 | end 89 | 90 | it 'raises an error if the commands argument cannot be made an Array' do 91 | expect { provisioner.scripts(some_string) }.to raise_error 92 | end 93 | 94 | it 'raises an error if #inline or #script method was already called' do 95 | provisioner.data['script'] = 1 96 | expect { provisioner.scripts(some_array_of_strings) }.to raise_error 97 | provisioner.data.delete('scripts') 98 | provisioner.data['inline'] = 1 99 | expect { provisioner.scripts(some_array_of_strings) }.to raise_error 100 | provisioner.data.delete('scripts') 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /spec/packer/provisioners/windows_restart_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::WindowsRestart do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('windows-restart') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_ints) do 14 | [1, 2, 3] 15 | end 16 | 17 | describe '#initialize' do 18 | it 'has a type of windows-restart' do 19 | expect(provisioner.data['type']).to eq('windows-restart') 20 | end 21 | end 22 | 23 | shared_examples 'test string parameter' do |key| 24 | it 'accepts a string' do 25 | provisioner.send(key.to_sym, some_string) 26 | expect(provisioner.data[key]).to eq(some_string) 27 | provisioner.data.delete(key) 28 | end 29 | 30 | it 'converts any argument passed to a string' do 31 | provisioner.send(key.to_sym, some_array_of_ints) 32 | expect(provisioner.data[key]).to eq(some_array_of_ints.to_s) 33 | provisioner.data.delete(key) 34 | end 35 | end 36 | 37 | describe '#restart_command' do 38 | include_examples 'test string parameter', 'restart_command' 39 | end 40 | 41 | describe '#restart_check_command' do 42 | include_examples 'test string parameter', 'restart_check_command' 43 | end 44 | 45 | describe '#restart_timeout' do 46 | include_examples 'test string parameter', 'restart_timeout' 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/packer/provisioners/windows_shell_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | 4 | RSpec.describe Packer::Provisioner::WindowsShell do 5 | let(:provisioner) do 6 | Packer::Provisioner.get_provisioner('windows-shell') 7 | end 8 | 9 | let(:some_string) do 10 | 'some string' 11 | end 12 | 13 | let(:some_array_of_strings) do 14 | %w[command1 command2] 15 | end 16 | 17 | let(:some_array_of_ints) do 18 | [1, 2, 3] 19 | end 20 | 21 | describe '#initialize' do 22 | it 'has a type of windows-shell' do 23 | expect(provisioner.data['type']).to eq('windows-shell') 24 | end 25 | end 26 | 27 | describe '#inline' do 28 | it 'accepts an array of commands' do 29 | provisioner.inline(some_array_of_strings) 30 | expect(provisioner.data['inline']).to eq(some_array_of_strings) 31 | provisioner.data.delete('inline') 32 | end 33 | 34 | it 'converts all commands to strings' do 35 | provisioner.inline(some_array_of_ints) 36 | expect(provisioner.data['inline']).to eq(some_array_of_ints.map(&:to_s)) 37 | provisioner.data.delete('inline') 38 | end 39 | 40 | it 'raises an error if the commands argument cannot be made an Array' do 41 | expect { provisioner.inline(some_string) }.to raise_error 42 | end 43 | 44 | it 'raises an error if #script or #scripts method was already called' do 45 | provisioner.data['script'] = 1 46 | expect { provisioner.inline(some_array_of_strings) }.to raise_error 47 | provisioner.data.delete('script') 48 | provisioner.data['scripts'] = 1 49 | expect { provisioner.inline(some_array_of_strings) }.to raise_error 50 | provisioner.data.delete('scripts') 51 | end 52 | end 53 | 54 | describe '#script' do 55 | it 'accepts a string' do 56 | provisioner.script(some_string) 57 | expect(provisioner.data['script']).to eq(some_string) 58 | provisioner.data.delete('script') 59 | end 60 | 61 | it 'converts any argument passed to a string' do 62 | provisioner.script(some_array_of_ints) 63 | expect(provisioner.data['script']).to eq(some_array_of_ints.to_s) 64 | provisioner.data.delete('script') 65 | end 66 | 67 | it 'raises an error if #inline or #scripts method was already called' do 68 | provisioner.data['inline'] = 1 69 | expect { provisioner.script(some_string) }.to raise_error 70 | provisioner.data.delete('inline') 71 | provisioner.data['scripts'] = 1 72 | expect { provisioner.script(some_string) }.to raise_error 73 | provisioner.data.delete('scripts') 74 | end 75 | end 76 | 77 | describe '#scripts' do 78 | it 'accepts an array of commands' do 79 | provisioner.scripts(some_array_of_strings) 80 | expect(provisioner.data['scripts']).to eq(some_array_of_strings) 81 | provisioner.data.delete('scripts') 82 | end 83 | 84 | it 'converts all commands to strings' do 85 | provisioner.scripts(some_array_of_ints) 86 | expect(provisioner.data['scripts']).to eq(some_array_of_ints.map(&:to_s)) 87 | provisioner.data.delete('scripts') 88 | end 89 | 90 | it 'raises an error if the commands argument cannot be made an Array' do 91 | expect { provisioner.scripts(some_string) }.to raise_error 92 | end 93 | 94 | it 'raises an error if #inline or #script method was already called' do 95 | provisioner.data['script'] = 1 96 | expect { provisioner.scripts(some_array_of_strings) }.to raise_error 97 | provisioner.data.delete('scripts') 98 | provisioner.data['inline'] = 1 99 | expect { provisioner.scripts(some_array_of_strings) }.to raise_error 100 | provisioner.data.delete('scripts') 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /spec/packer/runner_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | require 'fakefs/spec_helpers' 4 | 5 | RSpec.describe Packer::Runner do 6 | describe '#run!' do 7 | it 'returns stdout on success' do 8 | open3 = class_double("Open3").as_stubbed_const(:transfer_nested_constants => true) 9 | expect(open3).to receive(:capture3).and_return(['output', 'error', 0]) 10 | expect(described_class.run!('true', quiet: true)).to eq('output') 11 | end 12 | 13 | it 'raises an error on failure' do 14 | open3 = class_double("Open3").as_stubbed_const(:transfer_nested_constants => true) 15 | expect(open3).to receive(:capture3).and_return(['output', 'error', 1]) 16 | expect { described_class.run!('false', quiet: true) }.to raise_error 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/packer_config_spec.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | require 'fakefs/spec_helpers' 4 | 5 | RSpec.describe Packer::Config do 6 | let(:config_file) { 'config.json' } 7 | let(:packer) { Packer::Config.new(config_file) } 8 | let(:builder_type) { Packer::Builder::VIRTUALBOX_ISO } 9 | let(:provisioner_type) { Packer::Provisioner::FILE } 10 | let(:postprocessor_type) { Packer::PostProcessor::VAGRANT } 11 | let(:json_representation) do 12 | '{"variables":{"foo":"bar"},"builders":[{"type":"virtualbox-iso"}],"provisioners":[{"type":"file"}],"post-processors":[{"type":"vagrant"}]}' 13 | end 14 | 15 | describe '#initialize' do 16 | it 'returns an instance' do 17 | expect(packer).to be_a_kind_of(Packer::Config) 18 | end 19 | end 20 | 21 | describe "#validate" do 22 | it 'returns true for a valid instance' do 23 | expect(packer.builders).to receive(:empty?).and_return(false) 24 | expect(Packer::Runner).to receive(:run!).and_return('') 25 | expect(packer).to receive(:verify_packer_version).and_return(true) 26 | FakeFS do 27 | expect(packer.validate).to be_truthy 28 | end 29 | end 30 | end 31 | 32 | describe '#dump' do 33 | it 'dumps a JSON-formatted configuration' do 34 | packer.add_builder builder_type 35 | packer.add_provisioner provisioner_type 36 | packer.add_postprocessor postprocessor_type 37 | packer.add_variable 'foo', 'bar' 38 | expect(packer.dump).to eq(json_representation) 39 | end 40 | 41 | it 'raises an error if the format is not recognized' do 42 | expect { packer.dump 'invalid-format' }.to raise_error 43 | end 44 | end 45 | 46 | describe '#write' do 47 | it 'writes a JSON-formatted configuration file to disk' do 48 | packer.add_builder builder_type 49 | packer.add_provisioner provisioner_type 50 | packer.add_postprocessor postprocessor_type 51 | packer.add_variable 'foo', 'bar' 52 | FakeFS do 53 | packer.write 54 | expect(File.read(config_file)).to eq(json_representation) 55 | end 56 | end 57 | 58 | it 'raises an error if the format is not recognized' do 59 | FakeFS do 60 | expect { packer.dump 'invalid-format' }.to raise_error 61 | end 62 | end 63 | end 64 | 65 | describe "#build" do 66 | it 'returns successfully if the build command returns a successful exit code' do 67 | expect(packer).to receive(:validate).and_return(true) 68 | expect(packer).to receive(:write).and_return(true) 69 | expect(Packer::Runner).to receive(:run!).and_return('') 70 | FakeFS do 71 | expect(packer.build).to be_truthy 72 | end 73 | end 74 | 75 | it 'raises an error if the data is not valid' do 76 | expect(packer).to receive(:validate).and_raise(Packer::DataObject::DataValidationError) 77 | FakeFS do 78 | expect { packer.build }.to raise_error 79 | end 80 | end 81 | 82 | it 'raises an error if the config cannot be written to disk' do 83 | expect(packer).to receive(:validate).and_return(true) 84 | expect(packer).to receive(:write).and_raise(StandardError) 85 | expect { packer.build }.to raise_error 86 | end 87 | 88 | it 'raises an error if the build command returns an unsuccessful exit code' do 89 | expect(packer).to receive(:validate).and_return(true) 90 | expect(packer).to receive(:write).and_return(true) 91 | expect(Packer::Runner).to receive(:run!).and_raise(Packer::Runner::CommandExecutionError) 92 | FakeFS do 93 | expect { packer.build }.to raise_error 94 | end 95 | end 96 | end 97 | 98 | describe "#add_builder" do 99 | it 'returns a builder for a valid type' do 100 | expect(packer.add_builder(builder_type)).to be_a_kind_of(Packer::Builder) 101 | expect(packer.builders.length).to eq(1) 102 | packer.builders = [] 103 | end 104 | 105 | it 'raises an error for an invalid type' do 106 | expect { packer.add_builder('invalid') }.to raise_error 107 | expect(packer.builders.length).to eq(0) 108 | end 109 | end 110 | 111 | describe "#add_provisioner" do 112 | it 'returns a provisioner for a valid type' do 113 | expect(packer.add_provisioner(provisioner_type)).to be_a_kind_of(Packer::Provisioner) 114 | expect(packer.provisioners.length).to eq(1) 115 | packer.provisioners = [] 116 | end 117 | 118 | it 'raises an error for an invalid type' do 119 | expect { packer.add_provisioner('invalid') }.to raise_error 120 | expect(packer.provisioners.length).to eq(0) 121 | end 122 | end 123 | 124 | describe "#add_postprocessor" do 125 | it 'returns a post-processor for a valid type' do 126 | expect(packer.add_postprocessor(postprocessor_type)).to be_a_kind_of(Packer::PostProcessor) 127 | expect(packer.postprocessors.length).to eq(1) 128 | packer.postprocessors = [] 129 | end 130 | 131 | it 'raises an error for an invalid type' do 132 | expect { packer.add_postprocessor 'invalid' }.to raise_error 133 | expect(packer.postprocessors.length).to eq(0) 134 | end 135 | end 136 | 137 | describe '#add_variable' do 138 | it 'adds a new variable key/value pair' do 139 | expect(packer.variables).to eq({}) 140 | packer.add_variable('key1', 'value1') 141 | expect(packer.variables).to eq({'key1' => 'value1'}) 142 | packer.add_variable('key2', 'value2') 143 | expect(packer.variables).to eq({'key1' => 'value1', 'key2' => 'value2'}) 144 | packer.data['variables'] = {} 145 | end 146 | end 147 | 148 | describe '#variable' do 149 | it 'creates a packer reference to a variable in the configuration' do 150 | expect(packer.variables).to eq({}) 151 | packer.add_variable('key1', 'value1') 152 | expect(packer.variable('key1')).to eq('{{user `key1`}}') 153 | packer.data['variables'] = {} 154 | end 155 | 156 | it 'raises an error when the variable has not been defined in the configuration' do 157 | expect(packer.variables).to eq({}) 158 | expect { packer.variable 'key1' }.to raise_error 159 | packer.data['variables'] = {} 160 | end 161 | end 162 | 163 | describe '#envvar' do 164 | it 'creates a packer reference to an environment variable' do 165 | expect(packer.envvar.TEST).to eq('{{env `TEST`}}') 166 | end 167 | end 168 | 169 | describe '#macro' do 170 | it 'creates a packer macro reference for any method call' do 171 | expect(packer.macro.var).to eq('{{ .Var }}') 172 | end 173 | end 174 | 175 | describe '#verify_packer_version' do 176 | it 'validates Packer is a high enough version' do 177 | expect(Packer::Runner).to receive(:run!).and_return('Packer v1.0.0') 178 | expect(packer.verify_packer_version).to be_truthy 179 | end 180 | 181 | it 'raises an error if the version of Packer is not high enough' do 182 | expect(Packer::Runner).to receive(:run!).and_return('Packer v0.0.1') 183 | expect { packer.verify_packer_version }.to raise_error 184 | end 185 | end 186 | end 187 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'packer-config' 3 | 4 | RSpec::Expectations.configuration.warn_about_potential_false_positives = false 5 | # RSpec.configure do |c| 6 | # c.treat_symbols_as_metadata_keys_with_true_values = true 7 | # end 8 | -------------------------------------------------------------------------------- /spec/unit_helper.rb: -------------------------------------------------------------------------------- 1 | # Encoding: utf-8 2 | require 'spec_helper' 3 | -------------------------------------------------------------------------------- /tasks/clean.rake: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | CLEAN = FileList['deployment_dir/**/*'].exclude('*.txt') 4 | 5 | task :clean do 6 | FileList['spec/integration/packer_cache/*'].each do |f| 7 | FileUtils.rm_f(f) 8 | end 9 | Dir.glob('spec/integration/builds/*').select { |f| File.directory? f }.each do |d| 10 | FileUtils.rm_rf(d) 11 | end 12 | Dir.glob('spec/integration/*.json').select { |f| File.file? f }.each do |d| 13 | FileUtils.rm_f(d) 14 | end 15 | end 16 | 17 | -------------------------------------------------------------------------------- /tasks/rspec.rake: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | namespace :test do 4 | RSpec::Core::RakeTask.new(:spec) do |t| 5 | t.pattern = Dir['spec/**/*_spec.rb'].reject { |f| f['/integration'] } 6 | end 7 | 8 | RSpec::Core::RakeTask.new(:integration) do |t| 9 | t.pattern = "spec/integration/**/*_spec.rb" 10 | end 11 | end 12 | 13 | -------------------------------------------------------------------------------- /tasks/rubocop.rake: -------------------------------------------------------------------------------- 1 | require 'rubocop/rake_task' 2 | 3 | RuboCop::RakeTask.new(:lint) do |tsk| 4 | tsk.options = ['-D'] # display cop name 5 | end 6 | -------------------------------------------------------------------------------- /tasks/rubygems.rake: -------------------------------------------------------------------------------- 1 | require 'rubygems/tasks' 2 | Gem::Tasks.new 3 | --------------------------------------------------------------------------------